From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 31665 invoked by alias); 21 Mar 2007 02:06:42 -0000 Received: (qmail 31523 invoked by uid 22791); 21 Mar 2007 02:06:39 -0000 X-Spam-Check-By: sourceware.org Received: from misav08.sasknet.sk.ca (HELO misav08.sasknet.sk.ca) (142.165.20.172) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 21 Mar 2007 02:06:34 +0000 Received: from bgmpomr2.sasknet.sk.ca ([142.165.72.23]) by misav08 with InterScan Messaging Security Suite; Tue, 20 Mar 2007 20:06:31 -0600 Received: from babe.fig.org ([216.197.228.51]) by bgmpomr2.sasknet.sk.ca (SaskTel eMessaging Service) with ESMTPA id <0JF8009PADUV0DB0@bgmpomr2.sasknet.sk.ca> for gdb@sourceware.org; Tue, 20 Mar 2007 20:06:31 -0600 (CST) Received: by babe.fig.org (Postfix, from userid 1017) id EBCDB9C8013; Tue, 20 Mar 2007 20:06:30 -0600 (CST) Received: by babe.fig.org (tmda-sendmail, from uid 1017); Tue, 20 Mar 2007 20:06:30 -0600 (CST) Date: Wed, 21 Mar 2007 02:06:00 -0000 From: Michael FIG Subject: debugging a program that uses SIGTRAP To: gdb@sourceware.org Message-id: <7qy7lruz89.fsf@babe.fig.org> MIME-version: 1.0 Content-type: multipart/mixed; boundary="Boundary_(ID_r2mwFWOSJoS+iF3okTv8Jg)" X-Delivery-Agent: TMDA/1.0.3 (Seattle Slew) Mailing-List: contact gdb-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-owner@sourceware.org X-SW-Source: 2007-03/txt/msg00238.txt.bz2 --Boundary_(ID_r2mwFWOSJoS+iF3okTv8Jg) Content-type: TEXT/PLAIN Content-transfer-encoding: 7BIT Content-length: 2042 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 /\//\ http://fig.org/michael/ \//\/ --Boundary_(ID_r2mwFWOSJoS+iF3okTv8Jg) Content-type: text/x-csrc; NAME=t.c Content-transfer-encoding: 7BIT Content-disposition: inline; filename=t.c Content-description: Evil self-modifying program Content-length: 4700 /* libstackless prototype * Copyright (C) 2007 Michael FIG * * Michael FIG , 2007-03-19 */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include /* 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: */ --Boundary_(ID_r2mwFWOSJoS+iF3okTv8Jg)--