如何在GDB中提前加载外部Linux内核模块的调试符号以调试其初始化过程
没问题,我刚好折腾过这个场景,给你一步步说怎么搞定这个需求:
首先得确保你的内核模块编译时带了调试符号——在模块的Makefile里加上CFLAGS_MODULE += -g,不然.ko文件里没调试信息,再怎么折腾GDB也认不出来。
接下来按这个流程走:
启动带调试暂停的QEMU
启动QEMU的时候加上-S和-s参数:qemu-system-x86_64 -kernel vmlinux -initrd rootfs.cpio -S -s-S会让内核启动后立刻暂停在入口点,不会直接跑起来;-s是默认开启1234端口的GDB调试服务,方便后续连接。用GDB连接QEMU并加载基础符号
打开另一个终端,启动GDB并加载内核镜像的符号:gdb vmlinux进入GDB后,连接到QEMU的调试端口:
target remote :1234提前加载模块的调试符号
这时候内核还没开始运行,直接加载你的模块符号就行,用add-symbol-file命令,虽然现在不知道模块实际的加载地址,暂时填0就行:add-symbol-file mymodule.ko 0执行后GDB会弹出一堆section地址的提示,问你要不要确认,直接输入
y回车就行——它会把模块里的所有符号都记下来,等模块实际加载时自动匹配地址。设置初始化函数的断点
现在就可以直接给模块的初始化函数设断点了,比如你的模块初始化函数叫mymodule_init:b mymodule_init这时候GDB可能会提示“函数未定义,是否设置待决断点”,输入
y确认就行。启动内核并触发断点
最后让内核继续运行:c等你在QEMU的系统里执行
insmod mymodule.ko加载模块时,GDB会自动把之前设的待决断点关联到模块实际的内存地址,初始化函数一运行就会立刻触发断点,刚好能抓到初始化的全过程。
另外补充个小技巧:如果担心模块加载时的断点没触发,也可以先给sys_init_module设个断点——这是内核处理模块加载的系统调用入口,触发后再手动确认符号加载情况,不过前面的方法已经足够覆盖你的需求了。
备注:内容来源于stack exchange,提问作者Kirill




