调试没有符号的核心文件

Posted

技术标签:

【中文标题】调试没有符号的核心文件【英文标题】:Debug core file with no symbols 【发布时间】:2009-06-26 18:15:54 【问题描述】:

我有一个已部署到客户站点的 C 应用程序。它是在 HP-UX 上编译和运行的。用户报告了崩溃,我们获得了核心转储。到目前为止,我一直无法复制内部的崩溃。

正如您所怀疑的,核心文件/部署的可执行文件完全没有任何类型的符号。当我在 gdb 中加载它并执行 bt 时,我得到的最好的结果是:

(gdb) bt
#0  0xc0199470 in ?? ()

我可以对文件执行“字符串核心”,但我的理解是,我得到的只是可执行文件中的所有字符串,因此在其中追踪任何内容似乎是半不可能的。

我确实有可执行文件的调试版本(使用 -g 编译),不幸的是,它比发布版本更新了几个月。如果我尝试使用该集线器启动 gdb,我会看到:

warning: exec file is newer than core file.
Core was generated by `program_name'.
Program terminated with signal 11, Segmentation fault.
__dld_list is not valid according to __dld_flags.

#0  0xc0199470 in ?? ()
(gdb) bt
#0  0xc0199470 in ?? ()

虽然编译调试版本并将其部署在客户站点然后等待下一次崩溃是可行的,但由于多种原因,这相对困难且不可取。

我对代码非常熟悉,并且根据客户的错误报告对代码崩溃的位置有一个相对较好的了解。

有什么方法可以从这个核心转储中收集更多信息吗?通过字符串或其他调试器或任何东西?谢谢。

【问题讨论】:

【参考方案1】:

来自 gdb 的这种类型的响应:

(gdb) bt
#0  0xc0199470 in ?? ()

也可能发生在堆栈被缓冲区溢出破坏的情况下,其中返回地址在内存中被覆盖,因此程序计数器被设置为看似随机的区域。

这是即使使用相应符号数据库的构建也可能导致符号查找错误(或奇怪的回溯)的方式之一。如果您在获得符号表后仍然出现此问题,则您的问题很可能是您的客户数据导致您的代码出现问题。

【讨论】:

这个答案对我来说似乎很荒谬。我一定会仔细查看代码以查找可能超出范围的区域。 如果使用“重复”副本进行调试没有显示任何内容,那么是时候开始查看寄存器和堆栈转储,以尝试推断您是如何陷入困境的。它也可能是函数指针损坏(或未初始化)、分配溢出,或者可能是缓冲区大小不正确或“错误”输入导致缓冲区损坏(使用 sprintf()/sscanf 和不受控制的输入等)。 我从来没有在这里弄清楚任何事情,但我接受了这一点,因为它似乎仍然是最有可能发生的。【参考方案2】:

为了未来:

    确保您始终使用外部符号数据库进行构建(这不是调试构建 - 它是发布构建,但您单独存储符号表) 为您部署的版本保留它

对于这种情况:

您知道一般领域,因此要查看您是否正确,请转到堆栈跟踪并找到汇编代码 - 关注它并查看您是否认为它与您的源代码匹配(如果您知道什么更容易源生成了这个程序集)。如果它看起来正确,那么你对你的假设有一些验证。您也许可以通过查看堆栈来找出局部变量的值(因为您知道传入和声明的内容)。

【讨论】:

如何找到汇编代码和/或访问堆栈跟踪?到目前为止我看到的所有堆栈跟踪都粘贴在上面... 命令是'disassemble' -- 见unknownroad.com/rtfm/gdbtut/gdbadvanced.html 我这样做并得到:(gdb) disassemble 没有函数包含所选帧的程序计数器。在我看来,这似乎有利于下面 Sufian 所建议的粉碎堆栈。【参考方案3】:

在 gdb 下,“信息寄存器”应该在崩溃时为您提供足够的执行状态,以便与可执行文件和相关共享库的反汇编一起使用。我通常使用 objdump 进行反汇编,将输出重定向到一个文件,然后在我最喜欢的编辑器中调出该文件——这对于在弄清楚事情时记笔记很有用。此外,gdb 的“信息目标”和“信息共享库”对于确定共享库的加载位置很有用。

有了寄存器状态、堆栈内容和反汇编以及一点点运气,重建调用堆栈应该很简单(如果乏味的话)(当然,除非堆栈已被缓冲区溢出或类似灾难破坏...在这种情况下可能需要一个占卜板或水晶球。)

您还可以将使用 -g 构建的较新版本的反汇编与剥离版本的反汇编相关联。

【讨论】:

【参考方案4】:
    始终使用源代码控制 (CVS/GIT/Subversion/etc),即使对于测试版本也是如此 标记所有版本 考虑(将来)使用调试 (-g) 进行构建,并在发布前剥离可执行文件。注意:不要使用和不使用 -g 进行两次构建;它们很可能不匹配,因为 -g 即使在相同的优化级别有时也会导致生成不同的代码。在超级性能关键代码中,您可以放弃关键文件的 -g - 大多数情况下它不会产生任何影响。 如果你真的卡住了,转储堆栈并将堆的相关部分转储为十六进制并手动查看;也许获取一个检测副本并在生成的代码和堆栈中寻找类似的“签名”。这是真正的“老派”调试... :-)

【讨论】:

绝对可靠的建议。我们在这里几乎执行了第 1-3 步,但无论如何,它们由与我自己完全不同的一组人处理(我们在这里有一个负责这些事情的团队)。【参考方案5】:

您是否拥有用于编译旧版本的确切 源代码(例如,通过源代码树中的标记或类似的东西)?也许您可以使用它进行重建,并可能深入了解崩溃发生的位置?

【讨论】:

我确实有确切的来源,但这段特定的代码从那时到现在并没有太大变化(如果有的话)。【参考方案6】:

尝试对核心文件运行“pmap”(如果 hp/ux 有此工具)。这应该报告核心文件中所有模块的起始地址。有了这些信息,您应该能够获取故障位置的地址并找出哪个库崩溃了。崩溃地址和库中已知函数的地址之间的进一步地址比较(“nm”对库应该可以得到)可以帮助您确定哪个函数崩溃了。

即使您确实设法识别了堆栈顶部的函数,该函数也不太可能是问题的根源......希望它实际上已经在您的代码中崩溃了,而不是说,标准 C 字符串库。重建堆栈跟踪是当时最好的事情。

【讨论】:

【参考方案7】:

这里没有太多信息。二进制文件已被剥离。但是查看分段错误...您应该寻找有可能覆盖一段内存的地方。

这只是一个建议。可能会有很多问题。

顺便说一句,如果您无法在本地机器上重现,那么客户的数据量可能是个问题。

【讨论】:

【参考方案8】:

我认为核心文件不应该包含符号。您需要能够构建与您交付给客户的程序完全相同的程序版本,但使用 -g。如果您剥离您的调试可执行文件,它应该与交付的版本相同。只有这样,gdb 才能为您提供任何有用的东西。

【讨论】:

以上是关于调试没有符号的核心文件的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 在混淆时上传调试符号没有意义吗?

在发布模式下调试符号

是否可以调试由没有 gdb 标志编译的可执行文件生成的核心文件?

如何调试从执行发布文件生成的核心转储文件?

如何下载 Win7 MFC 调试符号

VS2017调试代码显示“当前无法命中断点,还没有为该文档加载任何符号”