如何才能在断点处使GDB在SIGTRAP处停止?

时间:2013-03-27 19:31:59

标签: gdb

我正在尝试调试一个程序,该程序经常导致GDB停止并在不处于断点时显示SIGTRAP。加载动态库和其他普通的东西时会发生这种情况。在我的断点最终被击中之前,有大约1000个发生,所以我手动“继续”所有这些无关的SIGTRAP是不可行的。但是如果我使用命令handle SIGTRAP nostop noprint,那么GDB将不会停在我的断点处。

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

1 个答案:

答案 0 :(得分:4)

您可以设置catchpoints以捕获SIGTRAP信号并添加命令以决定是继续还是停止。在此处理程序中,您可以检查convenience variables,例如$_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)。完成后,我了解了有关SIGTRAPsi_code的信息:

  • 断点使用代码128(SI_KERNEL)触发SIGTRAP。
  • 继续破解后,收到代码为2(TRAP_TRACE)的SIGTRAP(由于SIGTRAP的抓点)。
  • int3指令使用代码128触发SIGTRAP。因此,您需要一些区分指令。

以下是跳过int3陷阱但仍保持断点正常的最终GDB命令:

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进行测试的。