编写C程序时,如果需要使用某个外部的函数,通常的做法是 #include 包含该函数原型(prototype)的头文件,然后在程序中进行调用。经过编译链接后,程序就能顺利调用该函数。但是对于内核模块来说,这种方法并不适用,因此Linux内核提供了一种机制——内核模块符号表机制。即使用 EXPORT_SYMBOL 标签将模块中的函数对整个内核公开,因此导出的函数不用修改内核代码就可以被其他内核模块所调用。
  
  也就是说,使用 EXPORT_SYMBOL 可以将一个函数以符号的方式导出给其他模块使用。下面以一个简单的例子来说明如何使用 EXPORT_SYMBOL。
  
  我们以导出符号 test_add 为例,在适当的位置创建 add.c 和 Makefile 文件,内容如下:
  
【add.c】

#include <linux/module.h>

int test_add(int x, int y)
{
    return x+y;
}

EXPORT_SYMBOL(test_add);

MODULE_LICENSE("Dual BSD/GPL");

【Makefile】

obj-m += add.o

KERN_VER := $(shell uname -r)
KERN_DIR  = /lib/modules/$(KERN_VER)/build

modules:
    $(MAKE) -C $(KERN_DIR) M=`pwd` modules
clean:
    $(MAKE) -C $(KERN_DIR) M=`pwd` modules clean

  执行 make 命令后,生成 add.ko,执行命令 insmod add.ko 加载模块。
  确认 add 模块加载成功:

命令:
    cat /proc/modules | grep add
输出:
    add 12465 0 - Live 0xf88f9000 (O)

  确认 test_add 符号导出成功:

命令:
    cat /proc/kallsyms | grep test_add
输出:
    f88fa024 r __ksymtab_test_add   [add]
    f88fa030 r __kstrtab_test_add   [add]
    f88fa02c r __kcrctab_test_add   [add]
    f88f9000 T test_add [add]

  接下来,在适当的位置创建 test.c 和 Makefile 文件进行测试,内容如下:
【test.c】

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

/* 导出的符号可以被其他模块使用,不过使用之前一定要声明一下 */
int test_add(int x, int y);

static int __init test_init(void)
{
    printk(KERN_ALERT "test_init\n");
    printk(KERN_ALERT "%d\n", test_add(12, 13));
    return 0;
}

static void __exit test_exit(void)
{
    printk(KERN_ALERT "test_exit\n");
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("Dual BSD/GPL");

【Makefile】

obj-m += test.o

KERN_VER := $(shell uname -r)
KERN_DIR  = /lib/modules/$(KERN_VER)/build

modules:
    $(MAKE) -C $(KERN_DIR) M=`pwd` modules
clean:
    $(MAKE) -C $(KERN_DIR) M=`pwd` modules clean

  执行 make 命令,发现如下警告:

WARNING: "test_add" [/home/walle/workspace/export_symbol/test/test.ko] undefined!

  如果我们忽略警告,直接执行 insmod test.ko,发现模块加载失败!

[806556.012242] test: no symbol version for test_add
[806556.012258] test: Unknown symbol test_add (err -22)

  所以我们必须处理这个警告,而出现这样警告的原因是 test 模块不知道 test_add 的符号信息,解决办法有两个:

  • ① 把 add 模块的 Module.symvers 放到 test 模块所在目录下,重新编译 test 模块,test_add 的符号信息就会链接进去。

  • ② 在 test 模块的 Makefile 中使用 KBUILD_EXTRA_SYMBOLS 指定 add 模块的 Module.symvers。

KBUILD_EXTRA_SYMBOLS = /home/walle/workspace/export_symbol/add/Module.symvers

  重新 make ,再次执行 insmod test.ko 加载模块。

[806582.562028] test_init
[806582.562074] 25

  可以看到,test 模块已经成功调用 add 模块的 test_add 函数了!(输出结果25)


另外,也可以使用 EXPORT_SYMBOL_GPL 进行符号导出,但只适用于包含GPL许可权的模块。(它们定义在 include/linux/export.h)

Logo

更多推荐