debugging a program that uses SIGTRAP

Michael FIG
2007-03-21T02:06:42Z

[-- 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:
*/