x86架构Linux内核中如何通过内存地址定位故障内核模块名称
基于故障地址识别Linux内核模块名称的实现方案
核心思路
你不需要依赖kernel/module/internal.h(该文件是内核内部私有头文件,外部模块无法引用),直接利用公开的内核接口和全局变量就能实现需求:
- 用
modules全局链表遍历所有已加载模块 - 持有
module_mutex锁避免遍历过程中模块动态加载/卸载引发的竞态 - 检查地址是否落在模块的核心段(core)或初始化段(init)范围内
完整实现代码
#include <linux/module.h> #include <linux/list.h> #include <linux/mutex.h> void get_mod_name(unsigned long addr) { struct module *mod; // 锁定模块链表,防止遍历期间模块被加载/卸载 mutex_lock(&module_mutex); // 遍历所有已加载模块 list_for_each_entry(mod, &modules, list) { bool addr_in_mod = false; // 适配不同内核版本的地址范围检查逻辑 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) // 5.x及以上内核:使用mem数组存储核心/初始化段信息 // 检查核心段 if (addr >= (unsigned long)mod->mem[MODULE_CORE].base && addr < (unsigned long)mod->mem[MODULE_CORE].base + mod->mem[MODULE_CORE].size) { addr_in_mod = true; } // 检查初始化段(仅当该段未被释放时) if (!addr_in_mod && mod->mem[MODULE_INIT].size > 0 && addr >= (unsigned long)mod->mem[MODULE_INIT].base && addr < (unsigned long)mod->mem[MODULE_INIT].base + mod->mem[MODULE_INIT].size) { addr_in_mod = true; } #else // 5.x以下内核:使用core_addr/core_size、init_addr/init_size字段 if (addr >= (unsigned long)mod->core_addr && addr < (unsigned long)mod->core_addr + mod->core_size) { addr_in_mod = true; } if (!addr_in_mod && mod->init_size > 0 && addr >= (unsigned long)mod->init_addr && addr < (unsigned long)mod->init_addr + mod->init_size) { addr_in_mod = true; } #endif if (addr_in_mod) { printk(KERN_WARNING "Module's name is: %s", mod->name); // 找到目标模块后立即解锁返回,避免无效遍历 mutex_unlock(&module_mutex); return; } } mutex_unlock(&module_mutex); printk(KERN_WARNING "No module found for address 0x%lx", addr); }
关键注意事项
- 必须加锁:
module_mutex是内核保护模块链表的全局互斥锁,遍历前必须锁定,否则会因链表动态修改引发内核崩溃。 - 版本适配:不同内核版本中
struct module的地址存储字段不同,通过LINUX_VERSION_CODE宏做分支处理,保证兼容性。 - 初始化段判断:模块加载完成后,初始化段(init)可能被内核释放,因此需要先判断其大小是否大于0再检查地址范围。
- 避免私有头文件:
kernel/module/internal.h是内核内部代码专用,外部模块包含会导致编译错误,所有需要的公开定义都在<linux/module.h>中。
内容的提问来源于stack exchange,提问作者Vinicius
相关产品推荐
相关产品推荐

