8

我正在尝试调试一个经常导致 GDB 在不在断点处时停止并显示 SIGTRAP 的程序。它发生在加载动态库和其他普通东西时。在我的断点最终被击中之前,大约有 1,000 次此类事件发生,因此我无法手动“继续”所有这些不相关的 SIGTRAP。但是如果我使用 command handle SIGTRAP nostop noprint,那么 GDB 将不会在我的断点处停止。

似乎必须有一种方法来教育 GDB,以便它了解哪些 SIGTRAP 有利于停止,哪些不利于停止。显然 GDB 知道它是否在断点处,因为输出非常可靠地不同:在断点处,它提到“断点”并显示断点编号——但在任何其他 SIGTRAP 处,它只显示“SIGTRAP”。因此,与其打印关于 SIGTRAP 的消息,我真的希望 GDB 对自己说:“哇,这是一个 SIGTRAP,这里没有断点——看我,我要停下来打印一个完全破坏调试会话的无用 SIGTRAP 消息!我只是安静地继续怎么样?如果有人有办法做到这一点,请告诉我。

4

1 回答 1

6

您可以设置捕获点来捕获 SIGTRAP 信号并添加命令来决定是继续还是停止。在此处理程序中,您可以检查便利变量,例如$_siginfo信号的原因。

特别有趣的是$_siginfo.si_code,它的值取决于传递的信号。sigaction(2) Linux 手册页描述了确切的关系。您可以通过编译程序或查看标头(在我的系统上使用标头)来找到这些SI_USER,等代码的数值。我遇到的一些价值观是:SI_KERNEL/usr/include/bits/siginfo.h

  • 0x00 (0):SI_USER
  • 0x80 (128):SI_KERNEL
  • 0x02 (2):TRAP_TRACE

有了这些信息,下面是一个捕获 SIGTRAP 并打印原因的示例,然后继续:

catch signal SIGTRAP
commands
 p $_siginfo.si_code
 c
end
# Set another breakpoint for testing
break sleep

现在考虑这个休眠 5 秒的测试程序,然后在 x86(-64) 上触发调试陷阱:

#include <unistd.h>
int main(void) {
    for (;;) {
        sleep(5);
        asm("int3");
    }
    return 0;
}

该程序一直停gdb在该int3行,因为信号被捕获(si_code恰好是 0x80, SI_KERNEL),但随后再次重复该指令。因此,要跳过这条指令,程序计数器 ( $pc) 必须递增。这样做之后,我了解了有关SIGTRAPand的信息si_code

  • 断点使用代码 128 ( ) 触发 SIGTRAP SI_KERNEL
  • 继续断点后,将TRAP_TRACE收到代码为 2 ( ) 的 SIGTRAP(因为 的捕获点SIGTRAP)。
  • int3指令使用代码 128 触发 SIGTRAP。因此,您需要一些东西来区分指令。

以下是最终的 GDB 命令,它们跳过了int3陷阱并仍然保持断点功能:

catch signal SIGTRAP
commands
 silent # do not print catchpoint hits
 # ignore the int3 instruction (this address was looked up at
 # the tracepoint using print $pc)
 if $pc == 0x400568
  set $pc++ # skip int3
  c
 end
 # Ignore TRAP_TRACE that is used for breakpoints
 if $_siginfo.si_code == 2
  c
 end
end

最后一点: SIGTRAP 由调试器内部使用,上述内容可能会捕获太多。这是在 Arch Linux 上使用 GDB 7.10 测试的。

于 2015-11-03T12:11:22.337 回答