Linux内核模块反汇编中callq指令为何总是调用下一条指令
问题原因解释
你观察到的现象完全是正常的,本质是Linux内核模块的编译和加载机制导致的,分为两类情况:
1. 函数开头的callq 下一条指令是ftrace的占位符
你看到的所有函数开头第一行的e8 00 00 00 00指令,是内核编译选项CONFIG_FUNCTION_TRACER开启后默认生成的ftrace动态跟踪占位区域,刚好占5字节长度:
- 编译阶段预留该位置,默认填充相对偏移为0的call指令,表现就是调用当前call的下一条指令
- 模块加载时如果没有启用ftrace功能,这5字节会被替换为
nop(空操作)指令,不影响函数正常执行 - 如果启用ftrace,该位置会被替换为跳转到ftrace处理逻辑的指令,用于实现函数调用跟踪、性能分析等功能
2. 函数内部调用其他函数的callq占位是重定位机制的要求
你在init_module、cleanup_module里看到的其他callq占位,属于可重定位目标文件的标准特性:
.ko文件属于可重定位目标文件,不是已经完成地址绑定的最终可执行代码。编译阶段编译器不知道模块最终会被加载到内核虚拟地址空间的哪个位置,也不知道内核导出函数(比如register_chrdev_region、cdev_init等)的实际地址,因此会在这些callq的位置预留4字节的偏移空位,暂时填0,同时在重定位表中记录对应的重定位条目- 你可以执行
readelf -r driver.ko查看模块的重定位表,所有这些callq的偏移位置都会对应一条重定位记录,标明该位置需要回填的目标函数名称 - 当你用
insmod加载模块时,内核的模块加载器会完成重定位流程:先将模块的代码段、数据段映射到内核地址空间,再遍历所有重定位条目,计算出目标函数的实际地址和相对偏移,回填到callq指令的偏移位,此时callq就会指向正确的目标地址了
额外说明:你示例中_read函数没有调用get的原因
你写的_read函数中调用get()获取的字符串没有被后续代码使用,编译器开启优化后直接将该调用优化删除了,所以你在反汇编中看不到对应get的callq指令。如果要保留该调用,可以给get函数加上__attribute__((used))修饰,或者实际使用返回的字符串。
内容的提问来源于stack exchange,提问作者Pierre
相关产品推荐
相关产品推荐

