EXPORT_SYMBOL
是Linux内核中一个常见的工具,其作用是讲一个”Symbol”(函数或者变量)导出到内核空间,使得内核的所有代码都可以使用。我们用下面的例子来说明其使用方法。注意:在这篇文章中我假设你已经知道了内核模块开发的基本套路。
假设我们创建了两个内核模块mod1和mod2,其中mod1中有一个方法func_exported
需要在mod2内调用。
Mod1
Mod1中的源代码为
// mod1.c
#include <linux/init.h>
#include <linux/module.h>
static int func_exported(void)
{
printk(KERN_INFO "func_exported in mod1\n");
return 1;
}
EXPORT_SYMBOL(func_exported);
static int __init mod1_init(void)
{
printk(KERN_DEBUG "mod1 loaded\n");
return 0;
}
static void __exit mod1_exit(void)
{
printk(KERN_INFO "mod1 unloaded\n");
}
module_init(mod1_init);
module_exit(mod1_exit);
MODULE_LICENSE("Dual BSD/GPL");
其中func_exported
函数即为我们想要导出的函数。在完成正常的定义之后, 我们显式的调用EXPORT_SYMBOL
来导出。其余部分代码为标准的内核模块写法。
Mod2
Mod2中的源码为
// mod2.c
#include <linux/init.h>
#include <linux/module.h>
extern int func_exported(void);
static int __init mod2_init(void)
{
printk(KERN_DEBUG "mod2 loaded\n");
func_exported();
return 0;
}
static void __exit mod2_exit(void)
{
printk(KERN_INFO "mod2 unloaded\n");
}
module_init(mod2_init);
module_exit(mod2_exit);
MODULE_LICENSE("Dual BSD/GPL");
要使用Mod1中的func_exported
函数时,首先需要带extern
关键字对其进行生命。内核会在载入模块时将其连接到正确的函数中。
使用
使用insmod
指令载入Mod1之后,通过
cat /proc/kallsyms | grep func_exported
可以看到已经导出的Symbol信息
0000000000000000 T func_exported [mod1]
0000000000000000 r __kstrtab_func_exported [mod1]
0000000000000000 r __kcrctab_func_exported [mod1]
0000000000000000 r __ksymtab_func_exported [mod1]
然后通过insmod
载入Mod2,藉由dmesg
可以查看到系统日志输出,可以发现Mod2成功调用了Mod1中的函数。
Trouble Shooting
如果Mod1和Mod2都是你自己定义的外部载入模块,那么在sudo insmod mod2.ko
时,可能出现Unknown sysmbol
之类的错误。这是因为编译器无法找到mod1的Module.symvers
文件导致的(这个和编译后的mod1.c在同一目录下)。解决这个问题的办法是在编译Mod2时指定KBUILD_EXTRA_SYMBOLS=/path/to/mod1/Module.symvers