能直接定位到出错的源码行和变量状态,前提是二进制文件带-g编译符号且core文件真实存在并匹配;若bt显示问号,常见原因是可执行文件被strip、版本不匹配或缺失调试信息,需用readelf和file验证,并在gdb中执行info registers、bt full等命令确认上下文。

能直接定位到出错的源码行和变量状态,前提是二进制文件带 -g 编译符号,且 core 文件真实存在并匹配。
确认 core 文件和可执行文件是否配对
很多问题其实卡在这一步:gdb 能启动,但 bt 显示一堆问号或只有系统库地址。常见原因有:
- core 文件对应的是 stripped 版本的可执行文件(没调试信息),
file myapp输出里不含with debug_info - 程序运行时动态链接了不同版本的
libc,而你本地gdb加载的是另一套符号——此时info sharedlibrary会显示No symbol table或Cannot find bounds of current function - core 文件名被重命名过,但实际生成时用的是
/proc/sys/kernel/core_pattern定义的路径,比如/var/core/core-myapp-12345-1725790860,而你误用了当前目录下的core
验证方法:readelf -n /path/to/core | grep -A2 "NT_PRSTATUS" 可看到崩溃时的 pid 和 signal(通常是 SIGSEGV);再用 file /path/to/myapp 确认是否含调试段。
进 gdb 后必敲的三行命令
不要一上来就 bt,先确保上下文可靠:
-
info proc mappings:检查崩溃时内存布局是否和当前myapp的编译地址一致(尤其在 PIE 开启时,load address偏移必须匹配) -
info registers:看rip(x86_64)或pc(ARM)是否落在合理代码段;rdi/rsi/rdx等寄存器值能暴露空指针或越界地址(比如rdi = 0x0就大概率是空解引用) -
bt full:比bt多打印局部变量值,关键变量如ptr、buf、len的值往往直接揭示越界长度或非法地址来源
如果 bt full 报 No symbol table is loaded,说明调试信息缺失,得回退重编译:用 gcc -g -O0(关优化)重新构建,再复现崩溃。
识别典型段错误模式
从寄存器和调用栈里快速判断常见成因:
-
rip指向__memcpy_ssse3、strcpy、strlen等 libc 函数内部,且rdi或rsi是0x0→ 空指针传参(如strcpy(NULL, "abc")) -
rip在你的函数内某行(如src/server.c:356),rdi = 0x7f8e4c0008c0,但info proc mappings显示该地址不在任何rw-段 → 内存已释放(use-after-free)或根本未分配 -
bt最顶层是__stack_chk_fail→ 栈溢出(buffer overflow 或递归过深),不是传统段错误,但同样触发SIGSEGV
注意:bt 中若出现 ?? 且无法展开,别硬啃——优先检查 myapp 是否真带调试符号、是否和 core 同一构建批次、是否在容器里跑了不同 glibc 版本。
容器或嵌入式环境的特殊处理
在 Docker 或轻量级 rootfs 里,gdb 常找不到 libc 符号,导致 bt 不完整:
- 宿主机调试容器 core:把容器里的
/lib64/libc.so.6拷出来,用set sysroot /path/to/container/lib64告诉gdb去哪找符号 - 嵌入式设备无
gdb:用gdb-multiarch+ 对应target(如arm-linux-gnueabihf-gdb),并确保myapp是strip前的版本 - core 文件权限问题:容器默认
/proc/sys/fs/suid_dumpable = 0,SUID 程序崩溃不生成 core,需echo 2 | sudo tee /proc/sys/fs/suid_dumpable
最易忽略的一点:core_pattern 配置写入的是相对路径(如 core.%e.%p),它会落到进程当时的 cwd,而不是你预期的 /tmp ——务必用 find / -name "core-*" 2>/dev/null 全盘搜,别只盯当前目录。











