Linux 内核模块加载过程
0x0
项目开发过程中,尝试加载一个内核模块时出错,查看系统日志,有以下错误信息:
iscsi_sfnet: disagrees about version of symbol struct_module
经过调试发现,是因为这个内核模块与当前 Linux kernel 版本不一致,所以无法加载。更新版本后问题解决。
0x1
以下内容是在问题解决后对于内核模块加载过程的一些学习记录。
- 使用 insmod 或者 modprobe 加载内核模块(需要 root 权限),比如:
insmod hello.ko, 或者 modprobe hello (需要通过 depmod 更新模块的依赖关系)
- 用户工具通过
sys_init_module()
这个系统调用陷入内核,来完成将模块加载到内核里去的动作- 首先,会对这个模块进行各种检查,以确保对它的加载不会影响内核的运行,包括
- 确认文件的大小,比如最大不能超过 6MB、最小不能小于 ELF 头(大概几十个字节,与 CPU 位数有关)
- Application Binary Interface (ABI) 一致性检查
- vermagic 检查
- 确认是否为 rel 类型的文件,而不是 exe、so 或者其他
- 确认是否与当前运行的硬件类型匹配,比如 i386、arm 等等
- 确认 ELF 文件结构是否完整,比如 ELFMAG 是否匹配、等等
- 检查通过后开始加载,此处不关心这个过程,从略 …
- 首先,会对这个模块进行各种检查,以确保对它的加载不会影响内核的运行,包括
- 浏览了 sys_init_module() 的实现,上述错误的原因是 ABI 一致性检查未通过
- 通常内核模块里有一个名为 __versions 的段,它的内容很简单,是编译模块时所用内核导出的部分符号以及其对应的 CRC 校验值
13579bdf struct_module 2468ace0 printk
- 这个校验值是在一个特定的内核版本里计算出来的,用于区别不同版本的内核
- 安装内核源码包(*.rpm,从 kernel-*.spec 里可以看到由 Module.kabi 生成了 Module.symvers)或者内核编译后会在源码目录下生成 Module.symvers 文件,里面记录了所有导出符号及 CRC 校验值
- 编译内核模块时会将对应源码目录下的 struct_module、printk 或者其他一些导出符号及其 CRC 校验值链接到内核模块的 __versions 段里去
- 加载内核模块时,内核会将其与运行内核里的相应字段作比较,以此来保证 ABI 一致
- modprobe 有一个选项,
--force-modversion
,告诉内核忽略可能出现的 ABI 不一致,当然这样做很危险,可能发生内核崩溃 - 使用 objdump 查看 __versions 这个段里的内容,格式简单、可读,比如:
$ objdump -s ext3.ko Contents of section __versions: 0000 b2294749 00000000 73747275 63745f6d .)GI....struct_m 0010 6f64756c 65000000 00000000 00000000 odule........... 0020 00000000 00000000 00000000 00000000 ................ 0030 00000000 00000000 00000000 00000000 ................
- 对比需要加载的内核模块和内核中任一可加载的内核模块的差异,也可以从系统日志里查看加载失败的原因
- 通常内核模块里有一个名为 __versions 的段,它的内容很简单,是编译模块时所用内核导出的部分符号以及其对应的 CRC 校验值
- vermagic 检查
- 加载内核模块时,也会检查 vermagic,它是一个可读的、用于表示内核版本的字符串
- 内核源码里有一个宏 VERMAGIC_STRING 里,表示了这份内核头文件的版本,比如 2.6.18-el5 SMP mod_unload gcc-4.1,其含义非常直观
- 内核模块里的 .modinfo 段,其中 vermagic 字段里记录了编译模块时所用内核源码的版本和编译器的信息;可以通过
objdump -s
的方式获取,也可以直接使用 modinfo 工具来查看,比如:$ modinfo ext3.ko filename: ext3.ko license: GPL description: Second Extended Filesystem with journaling extensions author: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others srcversion: C06AB23BDA1C57EFB6501CF depends: jbd vermagic: 2.6.18-el5 SMP mod_unload gcc-4.1
- 加载内核模块时,内核会将其与自己的版本作比较,保证内核版本的匹配
- modprobe 有一个选项,
--force-vermagic
,告诉内核忽略 vermagic 的不一致,当然这样做很危险
- 加载内核模块时,也会检查 vermagic,它是一个可读的、用于表示内核版本的字符串
- 加载内核模块时有许多 ELF 格式的完整性检查,以此来过滤掉格式错误的文件、被 Hack 过的文件、等等