Linux module(4) - module version

  • 了解linux module 版本控制

1.为什么需要版本控制?

  Linux 的迅速发展致使相邻版本的内核之间亦存在较大的差异,即在版本补丁号(Patch Level,即内核版本号的第四位数)相邻的内核之间。为此 Linux 的开发者为了保证内核的稳定,Linux 在加载模块到内核时对模块采用了版本校验机制。

装载模块失败:
# insmod ./hello/hello.ko 
insmod: error inserting './hello/hello.ko': -1 Invalid module format 

  模块 hello.ko 构建时的环境与当前系统不一致,导致工具 insmod 在尝试装载模块 hello.ko 到内核时失败。

  module版本控制主要用来解决内核模块和内核之间的接口一致性问题。所谓内核模块和内核之间的接口,指由内核导出并被内核模块调用的那些符号。产生这种问题的根源在于内核模块和内核作为独立实体各自分开编译。

2.如何解决版本问题?

  使用CRC校验码,根据函数的参数生成一个大小为4字节的CRC校验码,当双方校验码相等视为相同接口,否则为不同接口。

  为了确保机制正常工作,内核必须首先启用CONFIG_MODVERSIONS,在此基础上内核模块在编译时也必须启用CONFIG_MODVERSIONS,否则模块将会因为出现无法解决的未定义符号错误导致加载失败。

  当编译内核时配置了CONFIG_MODVERSIONS选项, 会根据内在编译的MODPOST阶段根据内核源码为每个模块的导出函数生成一个CRC校验。 全部的模块的函数的这个CRC版本信息在make modules后会保存在源码的根目录的Module.symvers文件里面。单独编译其他模块依赖其他模块的符号时会使用这个文件,加入版本信息,如果找不到Module.symvers 编译时会打印警告的。

Module.symvers file 的格式是这样:
		<CRC>	    <Symbol>	       <module>   

  内核加载模块是会检查这个CRC版本信息,如果两个模块的导出方和导入方的版本不一致,也就提示内核接口变化了,会拒绝加载,打印 “ disagrees about version of symbol”。 如果编译时缺Module.symvers 导致编译出来的模块里面没有携带版本信息也是会报格式错误的。

2.1.CONFIG_MODVERSIONS

  In essence, what it is meant to achieve is that if you have a module you can attempt to load that module into any kernel, safe in the knowledge that it will fail to load if any of the kernel data structures, types or functions that the module uses have changed.

  If your kernel is not compiled with CONFIG_MODVERSIONS enabled you will only be able to load modules that were compiled specifically for that kernel version and that were also compiled without MODVERSIONS enabled.

  However, if your kernel is compiled with CONFIG_MODVERSIONS enabled you will be able to load a module that was compiled for the same kernel version with MODVERSIONS turned off. But - here’s the important part folks - you will also be able to load any modules compiled with MDOVERSIONS turned on, as long as the kernel API that the module uses hasn’t changed.

2.2.CRC生成工具genksyms

  在编译内核模块时,会生成*.mod.c文件,该文件中包含了模块中各个符号的校验码。校验码的生成,由scripts/genksyms/genksyms计算生成。

  注意:scripts/genksyms/genksyms文件是在内核源码目录或内核开发包目录中:

scripts/genksyms/genksyms.c

scripts/Makefile:
subdir-$(CONFIG_MODVERSIONS) += genksyms 

  如果一个独立编译的内核模块,比如driver.ko,引用到了内核导出的符号,比如printk,那么在driver.ko的编译链接过程中,工具链会到内核源码所在目录下查找Module.symvers文件,将得到的printk的CRC校验码记录到driver.ko的"__versions" section 中,即工具链不会在使用导出符号的地方重新为之生成一个CRC校验码。查看driver.mod.c文件,发现printk的校验码和内核源码中的Module.symvers完全一样。

#include <linux/build-salt.h>
#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

BUILD_SALT;

MODULE_INFO(vermagic, VERMAGIC_STRING);
MODULE_INFO(name, KBUILD_MODNAME);

__visible 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,
};

#ifdef CONFIG_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif

static const struct modversion_info ____versions[]
__used
__attribute__((section("__versions"))) = {
	{ 0xd4ad3bef, "module_layout" },
	{ 0x7c32d0f0, "printk" },
	{ 0xbdfb6dbb, "__fentry__" },
};

static const char __module_depends[]
__used
__attribute__((section(".modinfo"))) =
"depends=";

MODULE_INFO(srcversion, "4009E511290461B3E70E78C");

2.3.模块的vermagic

  When loading a module, the strings in the vermagic value are checked if they match. If they don’t match you will get an error and the kernel refuses to load the module. You can overcome that by using the --force flag of modprobe. Naturally, these checks are there for your protection, so using this option is dangerous.

~/Documents/work/code/albert/linux$ modinfo driver1.ko
filename:       /home/zhaoxiao/Documents/work/code/albert/linux/driver1.ko
srcversion:     0861A358105BCD5D61D3857
depends:        
retpoline:      Y
name:           driver1
vermagic:       4.19.37 SMP mod_unload 

3.内核模块版本控制使能与关闭

  常遇到内核提示“disagrees about version of symbol struct_module”,而导致模块无法加载的情况。

3.1.内核中关闭/使能

  若选择关闭内核的模块版本控制功能,则会避免出现这种情况。模块版本控制选项在内核源码配置文件.config中,注释掉CONFIG_MODVERSIONS就取消了模块版本控制。重新编译内核,重启即可。

3.2.模块中关闭/使能版本控制

  若去掉模块版本控制后,加载驱动导致系统死机。建议解决办法:使用待运行内核的.config配置文件覆盖模块编译指向的内核开发包(源码).config文件。

  .config配置文件的获取:

  • 可以拷贝/proc/config.gz,然后解压缩,拷贝为.config;
  • 若/proc/config.gz不存在,可以使用/boot/目录下对应的内核配置文件;
  • 向内核提供者获取.config配置文件。

  此时修改内核开发包中的模块版本控制选项,修改文件.config(在内核源码或开发包根目录下),注释掉或删除CONFIG_MODVERSIONS选项,重新编译模块即可去除模块的版本控制。

CONFIG_MODULES=y
CONFIG_OBSOLETE_MODPARM=y
#CONFIG_MODVERSIONS=y
CONFIG_MODULE_SIG=y
   25 static const struct modversion_info ____versions[]
   26 __used
   27 __attribute__((section("__versions"))) = {
   28     { 0xd4ad3bef, "module_layout" },
   29     { 0x7c32d0f0, "printk" },                                                                                                                      
   30     { 0xbdfb6dbb, "__fentry__" },
   31 };

参考:
http://www.skynet.ie/~mark/home/kernel/symbols.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值