能直接定位到源码行,前提是程序和动态库都带 -g 编译且未被 -O2 以上优化破坏行号映射;否则 bt 只显示函数名或地址。若崩溃栈全为 ??,需先用 file 检查是否 stripped,再用 readelf 验证动态库调试节,确认未启用过高优化,并处理 systemd-coredump 压缩、路径映射及 addr2line 地址解析等环节。

能直接定位到源码行,前提是程序和动态库都带 -g 编译,且没被 -O2 以上优化破坏行号映射。否则 bt 只能显示函数名或地址,看不到具体哪一行。
崩溃栈里只有地址,没有文件行号怎么办
这是最常遇到的情况:运行 gdb ./prog -c core 后执行 bt,输出全是类似 #0 0x00007f8a12345678 in ?? () 的内容,完全没源码线索。
- 先确认程序是否真含调试信息:
file ./prog输出必须包含with debug_info;若显示stripped,说明符号已被剥离,gdb无法还原源码 - 检查动态库是否也带
-g:崩溃若发生在libxxx.so内,仅主程序有调试信息没用;用readelf -S libxxx.so | grep debug看是否存在.debug_*节 - 避免
-O2或更高优化:优化会内联、删变量、重排指令,导致地址与源码行严重错位;调试阶段务必用-g -O0 - 如果用的是 systemd-coredump(如 CentOS/RHEL/Ubuntu 22.04+),core 文件默认是
.lz4压缩的,需先解压:zcat /var/lib/systemd/coredump/core.prog.*.lz4 > core.uncompressed,再用gdb ./prog -c core.uncompressed
bt full 显示了函数但没行号,怎么补
有时 bt 能显示函数名(如 #2 0x00007f8a12345678 in my_func ()),但不显示 my_func 在哪个文件哪一行 —— 这通常是因为调试信息存在,但 GDB 没找到对应源码路径。
- 用
info sources查看 GDB 当前已知的所有源文件路径;如果列表为空或缺失关键文件,说明编译时记录的路径和当前实际路径不一致 - 用
set substitute-path /old/build/path /current/src/path告诉 GDB 把旧路径映射到新位置(比如你是在容器里编译的,现在在宿主机调试) - 临时加载源码:如果知道函数名,直接
list my_func,GDB 会尝试按函数名反查并显示源码;失败则说明符号表没关联源码,或函数被内联了 - 确认当前工作目录是否为源码根目录:
pwd和info sources对不上时,cd到正确路径再试list
从崩溃地址反查源码行号(无符号表时的兜底法)
当连函数名都看不到(全是 ??),只剩一个崩溃地址(比如 segfault at 0 ip 0000000000401136),就得靠地址硬查。
- 用
objdump -d ./prog | grep "401136"找到该地址对应的汇编指令,再往上翻几行,看它属于哪个函数(找最近的<func_name>:标签) - 用
addr2line -e ./prog -C -f 0x401136直接解析地址 → 函数名 + 文件行号(-C支持 C++ 符号解码) - 若
addr2line输出仍是问号,说明该地址不在可执行段,可能是栈或堆上的非法跳转;此时重点看info registers中的rip和rsp,结合x/10i $rsp查栈顶指令 - 对动态库地址(如
0x00007f8a12345678),先用info sharedlibrary确认该库加载基址,再用objdump -d /path/to/lib.so查偏移量,最后用addr2line -e lib.so -C -f 0x12345678(注意减去基址)
真正卡住的地方往往不是命令不会输,而是调试信息从编译那一刻就断了链:没加 -g、用了 -O2、动态库没调试符号、路径变了没 substitute-path、core 是压缩的却忘了解压——这些环节漏掉任意一个,bt 就只能给你地址,而不是行号。


















