* debugging a program that uses SIGTRAP
@ 2007-03-21 2:06 Michael FIG
2007-03-21 2:18 ` Daniel Jacobowitz
0 siblings, 1 reply; 7+ messages in thread
From: Michael FIG @ 2007-03-21 2:06 UTC (permalink / raw)
To: gdb
[-- Attachment #1: Type: TEXT/PLAIN, Size: 2042 bytes --]
Hi, all...
How do you folks debug GDB under GDB?
I have a truly evil (mwahahah) library that inserts breakpoints into
the program that calls it. The intent is to self-modify the code to
conform to a different ABI than the original, as if adding
before-advice and after-advice to functions in a binary executable. I
also hope to have an offline translator, but the just-in-time
translator has more information at its disposal, which is why I'm
focusing on it as a proof-of-concept.
So, my problem is that I need to install a handler for SIGTRAP, which
interacts badly with GDB 6.4.90-debian on my Ubuntu i386 system.
It works fine if I manually resend the SIGTRAP:
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
`/fig/home/michael/sw/libstackless/t' has changed; re-reading symbols.
Breakpoint 1 at 0x8048891: file t.c, line 48.
Starting program: /fig/home/michael/sw/libstackless/t
Program received signal SIGTRAP, Trace/breakpoint trap.
0x080485d1 in stackless_premain (argc=-1075263868, argv=0xbfe8c4b4) at t.c:117
(gdb) signal SIGTRAP
Continuing with signal SIGTRAP.
from breakpoint at 0x080485d1
Breakpoint 1, stackless_trap_activation (signum=5, si=0x804bc9c, uctx=0x804bd1c) at t.c:50
(gdb) c
Continuing.
Hello, world!
Program exited normally.
(gdb)
However, when I change the handling of SIGTRAP to do this
automatically, my program dies:
(gdb) handle SIGTRAP pass
SIGTRAP is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal Stop Print Pass to program Description
SIGTRAP Yes Yes Yes Trace/breakpoint trap
(gdb) r
Starting program: /fig/home/michael/sw/libstackless/t
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
The program no longer exists.
(gdb)
The program is attached below.
Is there a better way to do this that I'm missing, or is this a GDB
bug that can be fixed (or has already been fixed in a newer version)?
Thanks,
--
Michael FIG <michael@fig.org> /\//\
http://fig.org/michael/ \//\/
[-- Attachment #2: Evil self-modifying program --]
[-- Type: text/x-csrc, Size: 4700 bytes --]
/* libstackless prototype
* Copyright (C) 2007 Michael FIG
*
* Michael FIG <michael@fig.org>, 2007-03-19
*/
#define _GNU_SOURCE 1
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <limits.h> /* PAGESIZE */
#ifndef PAGESIZE
# define PAGESIZE 4096
#endif
#define STACKLESS_MAX_SITES 256
/* This is a user-supplied function to start the application. */
int stackless_main(int argc, char **argv);
/* Put breakpoints on the return instructions, and intercept all the
call or jump instructions in FUNC. Return the maximum size of
FUNC's activation record. */
size_t stackless_infiltrate(void *func);
/* Mess with page protections to allow ADDR to be modified. */
void stackless_reprotect(void *addr);
/* This function does nothing more than provide a call site for
bootstrapping the activation trapper. */
int stackless_premain(int argc, char **argv);
static char *unprotected_page;
static char *site_address[STACKLESS_MAX_SITES];
static char site_contents[STACKLESS_MAX_SITES];
static int num_sites = 0;
/* Intercept a call to a child function, or a return to a parent
function. */
void
stackless_trap_activation(int signum, siginfo_t *si, void *uctx)
{
int i;
ucontext_t *uc = uctx;
int *eip = &uc->uc_mcontext.gregs[REG_EIP];
printf("from breakpoint at 0x%08x\n", *eip);
/* Restore the current sites to their defaults. */
for (i = 0; i < num_sites; i ++) {
stackless_reprotect(site_address[i]);
*site_address[i] = site_contents[i];
}
/* Try to protect ourselves the most again. */
stackless_reprotect(NULL);
/* Resume where we left off. */
(*eip) --;
}
/* Put breakpoints on all the call and return instructions in FUNC.
Return the maximum size of FUNC's activation record. */
size_t
stackless_infiltrate(void *func)
{
/* Do not infiltrate any return instructions for our premain. */
int do_rets = (func != stackless_premain);
/* FIXME: Use the disassembler. */
site_address[num_sites] = func;
site_contents[num_sites] = *site_address[num_sites];
stackless_reprotect(site_address[num_sites]);
*site_address[num_sites] = 0xcc; /* Instruction for debug trap. */
/* Advance to the next site. */
num_sites ++;
/* Try to reprotect our page. */
stackless_reprotect(NULL);
/* FIXME: Return the number of bytes we need. */
return 0;
}
/* Mess with page protections to allow ADDR to be modified. */
void
stackless_reprotect(void *addr)
{
char *to_unprotect = ((char *)addr) - ((long) addr) % PAGESIZE;
if (unprotected_page == to_unprotect) {
return;
}
if (unprotected_page &&
mprotect(unprotected_page, PAGESIZE, PROT_READ|PROT_EXEC)) {
fprintf(stderr, "cannot reprotect %p: %s\n",
unprotected_page, strerror(errno));
}
if (to_unprotect &&
mprotect(to_unprotect, PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC)) {
fprintf(stderr, "cannot unprotect %p: %s\n",
to_unprotect, strerror(errno));
}
else {
unprotected_page = to_unprotect;
}
}
/* This function does nothing more than provide a call site for
bootstrapping the activation trapper. */
int
stackless_premain(int argc, char **argv)
{
return stackless_main(argc, argv);
}
/* Stackless takes control over the main function; you must define
your application's main in a function called "stackless_main". */
int
main(int argc, char **argv)
{
/* The exit code from the user's stackless_main. */
int ret;
struct sigaction act, oldact;
stack_t altstack;
/* Use a temporary stack for our signal handler. */
memset(&altstack, 0, sizeof(altstack));
altstack.ss_size = SIGSTKSZ;
altstack.ss_sp = malloc(altstack.ss_size);
if (sigaltstack(&altstack, NULL)) {
fprintf(stderr, "%s: cannot set up Stackless signal stack: %s\n",
argv[0], strerror(errno));
exit(1);
}
/* Install our call/return trapper. */
memset(&act, 0, sizeof(act));
act.sa_sigaction = stackless_trap_activation;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction(SIGTRAP, &act, &oldact)) {
fprintf(stderr, "%s: cannot install Stackless signal handler: %s\n",
argv[0], strerror(errno));
exit(1);
}
/* Select stackless_premain as the first callsite. */
stackless_infiltrate(stackless_premain);
/* Run the user's main. */
ret = stackless_premain(argc, argv);
/* Restore the state of the call trapper. */
sigaction(SIGTRAP, &oldact, NULL);
return ret;
}
int
stackless_main(int argc, char **argv)
{
// FIXME: Do something useful.
printf("Hello, world!\n");
return 0;
}
/*
* Local variables:
* compile-command:"gcc -Wall -g -O2 t.c -o t && ./t"
* End:
*/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 2:06 debugging a program that uses SIGTRAP Michael FIG
@ 2007-03-21 2:18 ` Daniel Jacobowitz
2007-03-21 2:34 ` Michael FIG
0 siblings, 1 reply; 7+ messages in thread
From: Daniel Jacobowitz @ 2007-03-21 2:18 UTC (permalink / raw)
To: Michael FIG; +Cc: gdb
On Tue, Mar 20, 2007 at 08:06:30PM -0600, Michael FIG wrote:
> Hi, all...
>
> How do you folks debug GDB under GDB?
It's quite easy. GDB never generates SIGTRAPs in itself, only in the
program it is debugging. The outer GDB is only looking at the inner
GDB, not at the program the inner GDB is controlling.
> However, when I change the handling of SIGTRAP to do this
> automatically, my program dies:
>
> (gdb) handle SIGTRAP pass
> SIGTRAP is used by the debugger.
> Are you sure you want to change it? (y or n) y
> Signal Stop Print Pass to program Description
> SIGTRAP Yes Yes Yes Trace/breakpoint trap
> (gdb) r
> Starting program: /fig/home/michael/sw/libstackless/t
>
> Program terminated with signal SIGTRAP, Trace/breakpoint trap.
> The program no longer exists.
> (gdb)
>
> The program is attached below.
When you do this, it's not just resending SIGTRAPs that it would
display, but also all the internal ones. In this particular case it's
probably something from the shared library initialization breakpoint,
before main and before you have a SIGTRAP handler. Even if you get
past that point, your handler will now get called every time GDB hits
a breakpoint or single steps - single stepping will probably be
broken.
I don't see any good way to solve this. You've got two sets of
breakpoints and they're both going to stop GDB - it doesn't know which
ones you want and which you don't.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 2:18 ` Daniel Jacobowitz
@ 2007-03-21 2:34 ` Michael FIG
2007-03-21 11:14 ` Daniel Jacobowitz
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Michael FIG @ 2007-03-21 2:34 UTC (permalink / raw)
To: gdb
Thanks for the swift reply!
Daniel Jacobowitz <drow@false.org> writes:
> I don't see any good way to solve this. You've got two sets of
> breakpoints and they're both going to stop GDB - it doesn't know which
> ones you want and which you don't.
Okay. I thought somehow GDB would pass SIGTRAP iff it knows it has
not set a breakpoint on the current instruction pointer by scanning
its list of breakpoints.
> Even if you get past that point, your handler will now get called
> every time GDB hits a breakpoint or single steps - single stepping
> will probably be broken.
Would the above suggestion be reasonable? I think it would behave
nicer than what I have now, especially for my circumstance, since
there isn't any overlap between the code I'm trying to debug and the
code _it's_ trying to debug.
If it does seem reasonable, I can look into creating a patch. I
haven't gotten my hands dirty in GDB before. :)
--
Michael FIG <michael@fig.org> /\//\
http://fig.org/michael/ \//\/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 2:34 ` Michael FIG
@ 2007-03-21 11:14 ` Daniel Jacobowitz
2007-03-23 4:30 ` Michael FIG
2007-03-21 14:14 ` Joel Brobecker
2007-03-21 18:51 ` Michael Snyder
2 siblings, 1 reply; 7+ messages in thread
From: Daniel Jacobowitz @ 2007-03-21 11:14 UTC (permalink / raw)
To: Michael FIG; +Cc: gdb
On Tue, Mar 20, 2007 at 08:34:31PM -0600, Michael FIG wrote:
> > I don't see any good way to solve this. You've got two sets of
> > breakpoints and they're both going to stop GDB - it doesn't know which
> > ones you want and which you don't.
>
> Okay. I thought somehow GDB would pass SIGTRAP iff it knows it has
> not set a breakpoint on the current instruction pointer by scanning
> its list of breakpoints.
You've asked it to pass all SIGTRAPs and it's doing what you said -
I think you're stuck. However, it might be possible to make it do
this. The logic is in infrun.c. Be careful; that's one of the
touchiest pieces of GDB.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 2:34 ` Michael FIG
2007-03-21 11:14 ` Daniel Jacobowitz
@ 2007-03-21 14:14 ` Joel Brobecker
2007-03-21 18:51 ` Michael Snyder
2 siblings, 0 replies; 7+ messages in thread
From: Joel Brobecker @ 2007-03-21 14:14 UTC (permalink / raw)
To: Michael FIG; +Cc: gdb
> Okay. I thought somehow GDB would pass SIGTRAP iff it knows it has
> not set a breakpoint on the current instruction pointer by scanning
> its list of breakpoints.
>
> > Even if you get past that point, your handler will now get called
> > every time GDB hits a breakpoint or single steps - single stepping
> > will probably be broken.
>
> Would the above suggestion be reasonable? I think it would behave
> nicer than what I have now, especially for my circumstance, since
> there isn't any overlap between the code I'm trying to debug and the
> code _it's_ trying to debug.
As Daniel said, this is one of the trickiest parts of GDB. At first
glance, I think your suggestion above will not work, because of single
stepping. Each single-step operation ends with a SIGTRAP at the location
where the single-step finished, and chances are you have no breakpoint
at that location. This SIGTRAP is not to be passed back to the inferior.
You may think of adding some extra logic that guesses whether the SIGTRAP
comes from your single-step or from another source, but I think the logic
will be very fragile. Normally, all you are guarantied is that the program
will stop once it gets outside a given code range. But in fact, in can
stop anytime, even when inside that range. Try "set debug infrun 1" and
then do a few single steps; have a look at the output on x86-linux.
The way to achieve what you are suggesting, I think, is to use "software
single-step", which means single-stepping achieved by way of breakpoints
rather than using the kernel single-step. I don't think this is
implemented on x86, however.
Sorry, but I think you're pretty much stuck in your case. Can you not
use anything other than SIGTRAP?
--
Joel
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 2:34 ` Michael FIG
2007-03-21 11:14 ` Daniel Jacobowitz
2007-03-21 14:14 ` Joel Brobecker
@ 2007-03-21 18:51 ` Michael Snyder
2 siblings, 0 replies; 7+ messages in thread
From: Michael Snyder @ 2007-03-21 18:51 UTC (permalink / raw)
To: Michael FIG; +Cc: gdb
On Tue, 2007-03-20 at 20:34 -0600, Michael FIG wrote:
> Thanks for the swift reply!
>
> Daniel Jacobowitz <drow@false.org> writes:
>
> > I don't see any good way to solve this. You've got two sets of
> > breakpoints and they're both going to stop GDB - it doesn't know which
> > ones you want and which you don't.
>
> Okay. I thought somehow GDB would pass SIGTRAP iff it knows it has
> not set a breakpoint on the current instruction pointer by scanning
> its list of breakpoints.
>
> > Even if you get past that point, your handler will now get called
> > every time GDB hits a breakpoint or single steps - single stepping
> > will probably be broken.
>
> Would the above suggestion be reasonable? I think it would behave
> nicer than what I have now, especially for my circumstance, since
> there isn't any overlap between the code I'm trying to debug and the
> code _it's_ trying to debug.
>
> If it does seem reasonable, I can look into creating a patch. I
> haven't gotten my hands dirty in GDB before. :)
Michael, rather than telling gdb to always pass SIGTRAP
(handle SIGTRAP pass), or modifying gdb, you might be able
to debug at least a simple case (say, where there are only
a few non-breakpoint traps) by explicitly telling gdb to
pass the SIGTRAP on a case by case basis:
Program received signal SIGTRAP, Trace/breakpoint trap.
(gdb) signal SIGTRAP
Continuing with signal SIGTRAP.
If you expect a lot of events, there is a little-known
feature called a hook. You can put a hook on the "stop"
event, and have it do something to recognize your events.
Perhaps you can maintain your own list of locations.
(gdb) define hook-stop
> if in-my-list
> signal SIGTRAP
> end
> end
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: debugging a program that uses SIGTRAP
2007-03-21 11:14 ` Daniel Jacobowitz
@ 2007-03-23 4:30 ` Michael FIG
0 siblings, 0 replies; 7+ messages in thread
From: Michael FIG @ 2007-03-23 4:30 UTC (permalink / raw)
To: gdb
Thanks, all for your advice. I'll post again if I something
interesting comes out of it.
--
Michael FIG <michael@fig.org> /\//\
http://fig.org/michael/ \//\/
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2007-03-23 4:30 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-21 2:06 debugging a program that uses SIGTRAP Michael FIG
2007-03-21 2:18 ` Daniel Jacobowitz
2007-03-21 2:34 ` Michael FIG
2007-03-21 11:14 ` Daniel Jacobowitz
2007-03-23 4:30 ` Michael FIG
2007-03-21 14:14 ` Joel Brobecker
2007-03-21 18:51 ` Michael Snyder
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox