问题

    linux内核模块,装载的时候经常遇到 “could not insert module *.ko: Invalid module format”情况,此时通过dmesg查看会报错误信息,“disagrees about version of symbol module_layout”。

   在某业务环境遇到内核版本号为3.10.0.x86_64,随即编译3.10版本对应内核,使用新编译内核重新编译内核驱动模块,安装时依然报了以上错误。

排查

一、原因

    编译时使用的内核源码版本与装载环境内核源码版本不一致,或者配置文件不一致。究其根本原因是因为驱动装载时 linux内核会进行vermagic和symbol crc两处校验。

  • vermagic校验

    编译好的驱动文件,有.modinfo段,该段里面有vermagic关键字,eg: vermagic=3.10.0.x86_64 SMP mode_unload gcc-4.1,通过vim打开 -> (底部命令模式下)%!xxd 查看。装载驱动的时候内核会进行该关键字的验证,提取3.10.0.x86_64之后的magic参数与内核vermagic(在include/linux/vermagic.h中命名)全局变量之后的magic参数部分进行比较。

    vermagic全局变量是在编译内核源码时,由在linux/vermagic.h头文件中命名,其中的VERMAGIC_STRING宏定义既vermagic全局变量的值,在编译时可自行进行修改。

  • symbol crc校验

    驱动文件有一个__versions段,内核会提取驱动文件所使用的symbol的crc值,与内核自己的symbol的crc值进行比较,比较一致才可以进行安装。内核使用了scripts/genksyms/genksyms工具生成的crc值,crc值的生成与symbol的名字、返回值、参数类型和参数名有关。

    编写并编译一个驱动模块A,编译之后一般会看到 “A.mod.c”命名的文件,打开该文件我们可以看到A模块调用的内核symbol信息和所依赖的外部symbol接口以及他们的crc值。当编译环境与内核环境不一致时,既所涉及到的这些symbol的名字、返回值、参数类型和参数名有一点变化,均会导致crc值的不同,驱动也就安装不上了。

 

二、问题追踪

    有时候通过uname -r无法准确的识别当前内核的信息,因为uname -r的信息是编译升级内核时可人为修改的。

  • 我们可以通过查看 /lib/modules/内核版本号/build 或者 /lib/modules/内核版本号/SOURCE指向的内核源码目录中的Module.symers文件与编译环境下的Module.symers文件进行对比,该文件存储的是symbol的crc值。
  • 还可以通过/boot目录下面的symvers-内核版本号.gz文件,与编译环境下的Module.symers文件进行对比。symvers-内核号.gz是gzip压缩文件,可通过gunzip或者zcat进行解压查看。

    因此在驱动编译的时候,尽量要让编译环境和线上环境安全一致(除了代码,配置也尽量一致),至少所使用到的symbol需要一样。控制在小版本号内的同一个平台的内核升级,比如从内核2.6.18-8.el5升级到2.6.18-8.1.20.el5,只要是驱动中所使用的symbol没有发生变化,是可以公用的。

 

 

 

 

 

 

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐