addr2line必须配合带调试信息的ELF文件使用,仅提供裸地址无效;需用-e指定vmlinux、可执行文件或模块.o,且文件须-g编译未strip,否则输出??。

addr2line 不能直接用裸地址,得配对 ELF 文件
你拿到一个崩溃地址(比如 0x809008e0),想查它对应哪一行代码,addr2line 是最直接的工具,但它**不会自己猜符号在哪**。必须明确指定带调试信息的 ELF 文件(如 vmlinux 或编译后的 ./a.out),否则输出全是问号或乱码。
-
addr2line -e vmlinux 0x809008e0才有效;只写addr2line 0x809008e0会报错或返回?? - 内核模块要单独用其 .o 文件(如
sched/fair.o),不能塞进vmlinux里查——模块加载后地址已重定位,得用原始未加载的 .o - 用户态程序若 strip 过,
-e指向的文件必须是带-g编译且未 strip 的版本
gdb 里用 info symbol + x/i 组合定位更灵活
当只有运行中进程或 core dump,没有源码环境时,gdb 本身就能反查:先用 info symbol 看地址属于哪个符号,再用 x/i 反汇编附近指令,交叉验证调用上下文。
-
info symbol 0x809008e0返回类似start_kernel in section .text of /path/vmlinux,确认符号归属 -
x/5i 0x809008e0查看该地址起始的 5 条汇编指令,观察是否为函数入口、call/jmp 目标等 - 若地址落在函数中间(非入口),
info line *0x809008e0可尝试映射到源码行——但前提是该 ELF 有完整 DWARF 行号信息
core dump 里地址是虚拟地址,得确保符号表没被丢
从 core 文件还原调用现场时,常见错误是:程序编译没加 -g,或者发布时 strip 了二进制,导致 gdb ./a.out core 启动后 bt 只显示 #0 ??。
- 验证方式:
readelf -S ./a.out | grep debug应能看到.debug_*节区;若为空,addr2line和gdb都无法回溯 - strip 命令默认删掉所有调试节区;保留调试信息可用
strip --strip-unneeded,它只删符号表不碰.debug_* - 嵌入式场景下,
vmlinux必须和实际运行的 kernel 镜像完全匹配,哪怕只差一个 config 选项,行号都可能偏移
内核 panic 地址要过 System.map,别信 dmesg 里的 raw offset
内核日志(dmesg)常打印类似 IP: start_kernel+0x12c/0x4a0 的格式,这里的 +0x12c 是相对于符号的偏移,不是绝对地址。直接拿它喂 addr2line 会错。
- 正确做法:先从
System.map查出start_kernel的基地址(如809007b4),再算809007b4 + 12c = 809008e0,最后用这个结果查 -
System.map必须和当前运行的 kernel 严格对应;不同 build 生成的 map 文件不可混用 - 如果连
System.map都没了,只能靠gdb vmlinux后用info address start_kernel手动读基址


















