Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make GDB stop at SIGTRAP only at breakpoints?

Tags:

gdb

I'm trying to debug a program that often causes GDB to stop and display SIGTRAP when it is not at a breakpoint. It happens when loading dynamic libraries and other ordinary stuff. There are like 1,000 of these occurring before my breakpoint is finally hit, so it's non-feasible for me to manually "continue" all these irrelevant SIGTRAPs. But if I use the command handle SIGTRAP nostop noprint, then GDB will not stop at my breakpoint.

It seems like there must be a way to educate GDB so that it understands which SIGTRAP is good for stopping, and which is no good for stopping. Clearly GDB knows whether it is at a breakpoint, because the output is very reliably different: at a breakpoint, it mentions "breakpoint" and shows the breakpoint number--but at any other SIGTRAP, it just says "SIGTRAP". So instead of printing the message about a SIGTRAP, I'd really like GDB to just say to itself, "wow, this is a SIGTRAP and there is no breakpoint here--look at me, I'm about to stop and print a useless SIGTRAP message that completely ruins the debug session! How about I just continue quietly?" Please let me know if someone has a way to do this.

like image 944
Byron Hawkins Avatar asked Mar 27 '13 19:03

Byron Hawkins


1 Answers

You can set catchpoints to catch the SIGTRAP signal and add commands to decide whether to continue or to stop. In this handler, you can inspect convenience variables such as $_siginfo for the reason for the signal.

Of particular interest is $_siginfo.si_code, its value depends on the signal that is delivered. The sigaction(2) Linux manual page describes the exact relations. You can find the numeric values for those SI_USER, SI_KERNEL, etc. codes by compiling a program or looking in headers (on my system it uses the header /usr/include/bits/siginfo.h). Some values I have encountered are:

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

With this information in hand, here is an example that catches SIGTRAP and prints the reason, then continues:

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

Now consider this test program that sleeps 5 seconds, then triggers a debug trap on x86(-64):

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

This program keeps stopping gdb at the int3 line because the signal is caught (si_code happens to be 0x80, SI_KERNEL), but then the instruction is repeated again. So to skip this instruction, the program counter ($pc) must be incremented. After doing so, I learned this information about SIGTRAP and si_code:

  • Breakpoints trigger SIGTRAP with code 128 (SI_KERNEL).
  • After continuing the breakpoing, a SIGTRAP with code 2 (TRAP_TRACE) is received (because of the catchpoint for SIGTRAP).
  • The int3 instruction triggers SIGTRAP with code 128. Thus you needs something to differentiate the instructions.

Here are the final GDB commands that skip the int3 traps and still keep the breakpoints functional:

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

A final note: SIGTRAP is used internally by the debugger, it is possible that the above catches too much. This was tested with GDB 7.10 on Arch Linux.

like image 173
Lekensteyn Avatar answered Jan 02 '23 19:01

Lekensteyn