带调试信息的ELF文件调试core dump时GDB返回无效回溯的原因咨询(stripped ELF调试正常)
首先明确一个关键事实:strip命令不会对符号或代码进行重定位——它仅仅是移除ELF文件中的调试信息(.debug*段)、局部符号表等冗余数据,完全保留程序运行必需的代码、数据段以及动态链接依赖的符号。你两次strip后的文件完全一致,也验证了原始ELF和stripped版本的可执行逻辑是完全相同的。
回到你的问题,两种调试结果差异巨大,核心原因是你使用的旧版本GDB(7.12)和binutils(2.28)在处理**位置无关可执行(PIE)**程序的core dump时存在兼容性bug,具体来说:
1. 旧GDB对PIE程序调试信息的解析错误
在旧Debian发行版中,默认编译的程序大多是PIE(位置无关可执行),这类程序运行时会被加载到随机的内存地址。而GDB 7.12在处理带有完整调试信息的原始ELF时,会错误地直接使用编译时的静态地址(调试信息中记录的地址)去匹配core dump中的运行时地址,没有正确计算PIE加载的地址偏移,导致所有栈帧的地址都无法对应到有效的符号,最终出现全是??()的无效回溯。
而stripped版本的ELF没有调试信息,GDB只能依赖动态链接库(比如libc、libstdc++)的符号表和程序的动态符号表,这时候它会直接使用core dump中的实际运行地址去查询,反而能正确解析系统库的函数调用栈,只是自己程序的函数因为符号被移除而显示??()。
2. 调试信息与core dump的地址空间不匹配
如果你的程序启用了ASLR(地址空间随机化),旧版本GDB在关联调试信息和core dump时,也可能无法正确处理地址的随机偏移。stripped版本因为没有调试信息,GDB不会尝试去匹配静态调试符号,而是直接使用core dump中的真实地址,所以能正常解析系统库的栈帧。
解决方法
针对这个问题,你可以尝试以下几种方案:
分离调试信息(推荐):不要直接strip原始ELF,而是用
objcopy把调试信息分离到单独文件,同时保留stripped程序的调试链接:# 提取调试信息到单独文件 objcopy --only-keep-debug myelf myelf.debug # 生成stripped版本的程序 strip myelf -o myelf.strip # 给stripped程序添加调试信息链接 objcopy --add-gnu-debuglink=myelf.debug myelf.strip之后用
gdb myelf.strip coredump调试,GDB会自动加载myelf.debug中的调试信息,既能看到自己程序的函数栈帧,又不会出现地址解析错误。升级GDB和binutils:如果环境允许,升级到GDB 8.0以上、binutils 2.30以上版本,新版本修复了大量旧版本的PIE和core dump解析bug。
禁用PIE编译:如果你的程序不需要位置无关特性,可以在编译时添加
-no-pie选项,生成固定地址的可执行文件,旧版本GDB就能正确关联调试信息和core dump了。
内容的提问来源于stack exchange,提问作者user1244932

