编译驱动产生 xxx.mod.c 和 Module.symvers 文件分析
xxx.mod.c文件源码
xxx.mod.c是编译驱动的时候自动产生的,假设我们编译module.ko,当编译时就会产生一个module.mod.c文件
#include <linux/module.h> #include <linux/vermagic.h> #include <linux/compiler.h> MODULE_INFO(vermagic, VERMAGIC_STRING); struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif .arch = MODULE_ARCH_INIT, }; static const char __module_depends[] __used __attribute__((section(".modinfo"))) = "depends=";
文件里定义了struct module类型的变量__this_module,__this_module变量初始化的信息就是用来描述驱动,这个结构体会被链接进ko文件中,将来在加载ko驱动时,内核中会有代码将这个结构体从ko文件中解析出来,并根据这个结构体来加载驱动。
init_module:这个函数是驱动代码中用module_init宏声明的驱动加载函数,init_module是驱动加载函数的统一别名
cleanup_module:这个函数是驱动代码中用module_exit宏声明的驱动卸载函数,cleanup_module是驱动卸载函数的统一别名
Module.symvers文件
在编译过的驱动代码目录中会产生Module.symvers文件,该文件记录了驱动中导出到内核的符号
//驱动代码中 EXPORT_SYMBOL(test_chrdev_write); //终端命令行 root@ubuntu:/root/windows_share/driver/char_dev/code/3.CharDevSenior/5.3.11# ls Makefile modules.order Module.symvers module_test.c module_test.ko module_test.mod.c module_test.mod.o module_test.o
root@ubuntu:/root/windows_share/driver/char_dev/code/3.CharDevSenior/5.3.11# cat Module.symvers 0x00000000 test_chrdev_write /root/windows_share/driver/char_dev/code/3.CharDevSenior/5.3.11/module_test EXPORT_SYMBOL
上面的Module.symvers文件中导出了test_chrdev_write符号,是因为在驱动代码中用 EXPORT_SYMBOL 宏导出了test_chrdev_write符号
如果其他的驱动模块在编译时依赖该驱动的test_chrdev_write符号,则将Module.symvers文件拷贝到用到的驱动代码目录下或者使用 KBUILD_EXTRA_SYMBOLS,再进行编译,这样就不会报找不到符号的错误
驱动的 Makefile 添加: KBUILD_EXTRA_SYMBOLS += /path/to/another/Module.symvers 注意:KBUILD_EXTRA_SYMBOLS 需要填写绝对路径
CONFIG_MODVERSIONS
CONFIG_MODVERSIONS 用来决定是否开启版本控制,该选项防止将接口定义和当前内核版本不再匹配的废弃模块载入内核。
在编译 vmlinux 时,最后会利用 modpost 来解析 vmlinux.o 对象文件,并将基本内核导出的所有符号都记录到文件 Module.symvers 中去。如果没有打开 CONFIG_MODVERSIONS 选项,那么你看到的 Module.symvers 文件中第一列符号 CRC 校验值都是 0x00000000 。如果打开该选项,那么第一列就会显示出各个符号的 CRC 校验值,如:
0x2d6c0f40 put_mnt_ns vmlinux EXPORT_SYMBOL 0x6c386a8c clear_bdi_congested vmlinux EXPORT_SYMBOL ... ...
一旦设置过这个配置选项,就意味着打开了内核的 Module versioning功能。Module versioning 功能应用在我们使用模块的场合。如果 Module versioning 功能被打开的话,它会以每个导出符号的 C 原型声明作为输入,计算出对应的CRC校验值,保存在文件 Module.symvers 中。如此一来,内核在后面要加载使用模块的时候,会两相比较模块中的CRC值和保存下来的CRC值,如果发现不相等,内核就拒绝加载这个模块。