【问题标题】:Gdb cannot find assertion failure positions after recompilinggdb重新编译后找不到断言失败位置
【发布时间】:2018-02-02 14:38:51
【问题描述】:

在我重新编译我的代码后,gdb 似乎无法找到断言失败的代码位置。更准确地说,相对于断言失败,我预计信号提升的位置是

0x00007ffff7a5ff00 in raise () from /lib64/libc.so.`6

而我却得到

0x00007ffff7a5ff00 in ?? ()

例如,考虑以下代码

#include <assert.h>

int main()
{
  assert(0);
  return 0;
}

使用调试符号编译并使用 gdb 进行调试。

> gcc -g main.c
> gdb a.out

gdb第一次运行,找到了位置,回溯报错:

GNU gdb (Gentoo 8.0.1 p1) 8.0.1 
...
(gdb) r
Starting program: /home/myself/a.out 
a.out: main.c:5: main: Assertion `0' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff7a5ff00 in raise () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7a5ff00 in raise () from /lib64/libc.so.6
#1  0x00007ffff7a61baa in abort () from /lib64/libc.so.6
#2  0x00007ffff7a57cb7 in ?? () from /lib64/libc.so.6
#3  0x00007ffff7a57d72 in __assert_fail () from /lib64/libc.so.6
#4  0x00005555555546b3 in main () at main.c:5
(gdb)

当我重新编译代码时,问题就来了。重新编译后,我在同一个 gdb 实例中发出运行命令。 gdb重新读取符号,从头开始启动程序,但没有找到正确的位置:

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
`/home/myself/a.out' has changed; re-reading symbols.
Starting program: /home/myself/a.out 
a.out: main.c:5: main: Assertion `0' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff7a5ff00 in ?? ()
(gdb) bt
#0  0x00007ffff7a5ff00 in ?? ()
#1  0x0000000000000000 in ?? ()
(gdb) up
Initial frame selected; you cannot go up.
(gdb) n
Cannot find bounds of current function

此时调试器无法使用。上不去,上前一步。 作为一种解决方法,我可以手动重新加载文件,然后再次找到位置。

(gdb) file a.out
Load new symbol table from "a.out"? (y or n) y
Reading symbols from a.out...done.
(gdb) r
Starting program: /home/myself/a.out 
a.out: main.c:5: main: Assertion `0' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff7a5ff00 in raise () from /lib64/libc.so.6
(gdb) 

不幸的是,以这种方式重新加载文件后,gdb 无法重置断点。

ERRATA CORRIGE:我在使用 gdb 7.12.1 重置断点时遇到了失败。升级到 8.0.1 后问题消失了。据推测,这与错误修复 https://sourceware.org/bugzilla/show_bug.cgi?id=21555 有关。但是,仍然无法正确找到断言失败的代码位置。

有人知道这里发生了什么吗?

这在系统更新后开始发生。系统更新将包括 glibc 在内的所有系统库重新编译为与位置无关的代码,即使用 -fPIC 编译。

另外,我使用的 gcc 版本是 6.4.0

【问题讨论】:

  • 你调试了一个不同的文件,你怎么能使用为旧文件定义的断点?
  • 使用 IDE。它们足够聪明,可以在一行上附加断点,并在调试器启动时告诉它。它们也足够聪明,可以知道当您在断点之前添加一行时断点位置会发生变化。非常有用。
  • 我不知道它是如何在 gdb 中实现的。我认为在重新编译后能够重新运行您的代码并重用您的断点是可取的。我可以预期,也许行断点不合适,但不需要每次都重新启动 gdb
  • file a.out 不会重新启动 gdb。它加载正确的二进制文件。并且可以说因为添加或删除了一行而让所有断点都有错误的位置听起来没有用。
  • 我同意。事实上,我希望找到函数的断点。但这里的问题是 gdb 找不到任何位置(例如断言失败的位置),无论断点如何。

标签: gdb fpic


【解决方案1】:

这是一种解决方法。由于file 正确地重新读取符号,而run 没有,我们可以为命令run 定义一个钩子,以便在之前执行file

define hook-run
  pi gdb.execute("file %s" % gdb.current_progspace().filename)
end

【讨论】:

    【解决方案2】:

    在您更改源文件并重新编译后,您将生成与加载到 GDB 的文件不同的文件。

    您需要停止正在运行的调试任务并重新加载文件。

    您不能将文件中先前定义的断点和观察点保存到更改的源中,因为 gdb 实际上是在您的源中插入额外的代码来支持断点和注册器处理程序。

    如果您更改源,则行为未定义,您需要重置这些断点。

    您可以参考 gdb 手册,了解将文件中的断点保存为 Mark Plotnick 建议,但如果您更改文件,它将不起作用(根据我的经验) https://sourceware.org/gdb/onlinedocs/gdb/Save-Breakpoints.html

    【讨论】:

    • 确实,我正在重新加载符号。重新编译后,我再次发出“运行”命令,返回消息“/home/myself/a.out”已更改;重读符号。但是 gdb 未能找到断言失败的正确位置。
    • 我不知道如何“保存”断点。当您重新加载文件时,您需要重置它们。我认为这是因为您尝试检查的表达式的虚拟地址已更改(更改文件后)并且先前设置的断点与它无关
    • 如果有一种方法可以指定 gdb 行号断点,并在该行及其周围使用源代码的一些 sn-p,就像索引源代码的每一行的 ctags。然后您的断点列表可以在较小的代码编辑中幸存下来。也许这就是 IDE 可以做的事情。
    • 确实如此。然而,我遇到的问题比这更微妙。它与运行命令后重置断点失败有关。这是 PIE 可执行文件的错误,已在版本 8.0.1 中修复。我相应地更新了帖子
    猜你喜欢
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多