addr2line 和 gdb 定位内核模块 Oops 源码行
内核模块崩溃后,dmesg 里经常看到类似这样的回溯:
[ 123.456] EIP: [<f8a01234>] xxx_main+0x1a2a/0x2000 [xxx]拿到符号加偏移xxx_main+0x1a2a,就能直接映射到 C 源码行。前提是模块编译时带了调试信息(-g,或内核开了CONFIG_DEBUG_INFO),且.ko文件没有被strip。
1. addr2line(最快)
addr2line-e./xxx.ko xxx_main+0x1a2a输出:
/path/to/source.c:123如果模块代码移动了位置,加-f可同时打印函数名:
addr2line-f-e./xxx.ko xxx_main+0x1a2a2. gdb(直接看上下文)
gdb ./xxx.ko(gdb)list *xxx_main+0x1a2agdb 会列出崩溃行附近的源代码。如果源码路径不对,用directory命令指向正确目录。
也可以只精确显示一行:
(gdb) info line *xxx_main+0x1a2a3. 用 objdump 做源码级反汇编(备用)
这个方法要手动计算绝对地址,一般不优先用,但适合想看清到底哪条指令炸了。
先找函数在.ko中的段内偏移:
objdump-txxx.ko|grep' xxx_main$'00000500 g F .text 00001000 xxx_main# 示例计算目标地址 = 0x500 + 0x1a2a = 0x1f2a,然后反汇编并穿插源码:
objdump-d-S--start-address=0x1f2a xxx.ko注意事项
- 如果崩溃日志只给了绝对地址(如
0xf8a01234)而没给符号+偏移,先拿到模块加载基址,再算偏移:偏移 = 绝对地址 - 模块基址。然后用addr2line -e xxx.ko 偏移不可行,因为.ko是重定位文件,地址从 0 起算。此时仍建议用 gdb,gdb ./xxx.ko后info symbol 偏移能还原出函数和偏移,再用上面的方法定位行。 - 务必保留编译时的
.ko文件,且不能strip。如果用modpost等工具处理过,调试信息可能已丢失。 - 对
vmlinux同样适用,文件替换即可。
