没有源代码时 gdb 回溯的含义

Meaning of a gdb backtrace when there is not source code

我有一个崩溃进程的 gdb 回溯,但我看不到发生崩溃的具体行,因为源代码不在那个时刻。 我不明白提到的回溯给出的一些信息。

回溯由如下几行组成:

<path_to_binary_file>(_Z12someFunction+0x18)[0x804a378]

请注意 _Z12someFunctionint someFunction(double ) 的错位名称。

我的问题是:

+0x18 是否表示产生崩溃的汇编指令的偏移量,从 _Z12someFunction 地址开始?

如果前面的问题是肯定的,并且考虑到我正在使用 32 位架构,那么 +0x18 是否表示 0x18 * 4 字节?

如果以上是肯定的,我假设地址0x804a378就是_Z12someFunction加上0x18,对吗?

编辑:

该错误发生在生产机器(未启用内核),而且似乎是时间相关的错误,因此不容易重现。那是因为我要的信息在这个场合对我很重要。

运行 man gcc,在那里你应该看到 -g 选项,它使你有可能将调试信息添加到二进制目标文件中,因此当发生崩溃并且核心被转储时,gdb 可以检测到确切的行,其中和为什么会发生崩溃,或者您可以 运行 使用 gdb 的进程或附加到它并直接查看跟踪,而无需搜索核心文件。

你的大部分假设都是正确的。 +0x18 确实意味着可执行文件的偏移量(以字节为单位,无论体系结构如何)。

0x804a378是实际发生错误的地址。

话虽如此,重要的是要了解您可以对此做些什么。

首先,用-g编译会产生调试符号。你,理所当然地,为你的生产构建剥离那些,但一切都没有丢失。如果您使用原始可执行文件(即 - 在对其进行条带化之前),您可以 运行: addr2line -e executable

然后您可以将 gdb 给您的地址 (0x804a378) 输入标准输入,addr2line 将为您提供该地址指向的精确文件和行。

如果你有一个核心文件,你也可以用未分割的可执行文件加载这个核心文件,并获得完整的调试信息。它仍然会有些损坏,因为您可能正在使用优化进行构建,但仍然应该可以访问一些变量。

使用调试符号构建并在发布前剥离是最佳选择。但是,即使您没有这样做,如果您在同一环境中使用相同的构建工具并使用相同的构建选项再次构建相同的源代码,您 应该 获得相同的二进制文件符号位置。如果bug真的很难重现,也许值得一试。

编辑添加

另外两个重要的工具是 c++filt。您向它提供一个损坏的符号,并生成实际源符号的 C++ 路径。它用作过滤器,因此您只需复制回溯并将其粘贴到 c++filt 中,它会为您提供相同的回溯,只是更具可读性。

第二个工具是gdb远程调试。这允许您在具有带调试符号的可执行文件的机器上 运行 gdb,但 运行 生产机器上的实际代码。这允许在生产中进行实时调试(包括附加到已经 运行ning 进程)。

你糊涂了。您看到的是 glibc's backtrace 函数的回溯输出,而不是 gdb 的回溯。

but I can't see the specific line in which the crash occurred because the source code was not in that moment

现在您可以在 gdb 中加载可执行文件并检查地址 0x804a378 以获得行号。您可以使用 list *0x804a378info symbol 0x804a378。参见 Convert a libc backtrace to a source line number and How to use addr2line command in linux