This is the mail archive of the
gdb@sourceware.org
mailing list for the GDB project.
debugging a program that uses SIGTRAP
- From: Michael FIG <michael-dated-1177034790 dot 7f6941 at fig dot org>
- To: gdb at sourceware dot org
- Date: Tue, 20 Mar 2007 20:06:30 -0600
- Subject: debugging a program that uses SIGTRAP
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/ \//\/
/* 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:
*/