c++oding="utf-8" ?>
崩溃点在std::terminate或abort是异常未捕获的表象,真正异常抛出位置需通过info registers查$rdi中exception对象地址、结合x/10gx与disassemble反推,而非依赖失真的bt栈回溯。

直接看 bt 很可能找不到真正出问题的代码行——C++ 异常未捕获导致的 core,崩溃点通常在 std::terminate 或 abort,真正的异常抛出处被栈帧“吃掉”了。
为什么 bt 显示的是 __gnu_cxx::__verbose_terminate_handler?
因为未捕获的 C++ 异常最终会调用 std::terminate,而默认 terminate handler 会调用 abort(),触发 SIGABRT。此时 GDB 停在 libc 的 abort 入口,不是异常源头。
-
SIGABRT不代表堆损坏,也可能是纯 C++ 异常未处理(比如std::vector::at越界、std::throw后没catch) - 调用栈里看不到
thread_func或main中的业务代码,是因为异常传播过程中的栈帧在terminate时已被部分销毁 - 若编译时用了
-fno-exceptions或链接了不带 unwind 信息的库,栈回溯会进一步失真
怎么定位到真正抛异常的那行?
靠 info registers 和 disassemble 结合异常对象地址反推,而不是只信 bt。
- 先执行
info registers,重点关注rdi(x86_64 下第一个参数寄存器),它往往存着正在被处理的std::exception对象地址 - 用
x/10gx $rdi查看该地址附近内存,找疑似std::string、what()字符串或type_info指针 - 执行
disassemble $rip-32,$rip+32,看abort前最后几条指令,常能发现call __cxa_throw或call std::vector::at的残留调用痕迹 - 如果程序带调试符号且未 strip,尝试
frame 3或frame 4(跳过 terminate 相关帧),有时能意外看到上层业务帧
编译和运行阶段必须做的准备
没有这些,GDB 基本只能猜:异常在哪抛的,根本没法确定。
- 编译必须加
-g -O0或至少-g -O1;-O2+会内联at()等函数,丢失源码行号 - 不要 strip 可执行文件:
strip ./a.out会删掉.eh_frame段,导致 GDB 无法做栈回溯 - 确保链接了完整的 libstdc++ debug info(如 Ubuntu 上装
libstdc++6-12-dbg),否则__cxa_throw等符号不可见 - 多线程场景下,用
thread apply all bt挨个查,异常大概率发生在非主线程中,但bt默认只显示当前线程
最容易被忽略的细节
异常对象本身可能已被析构,但它的 what() 字符串常驻堆上,只要没被后续分配覆盖,就能捞出来。
-
print *(char**)$rdi+1有时能直接打出异常 message(std::out_of_range的提示文字) - 若
$rdi是空或非法地址,说明异常对象已销毁,这时要转去查__cxa_current_primary_exception()(需 libstdc++ debug info 支持) - docker 容器里分析 core,必须挂载宿主机的
/usr/lib/debug或提前apt install -y libstdc++6-12-dbg,否则所有 C++ 标准库符号都是??
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











