git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@1218 626c5289-ae23-0410-ae9c-e8d60b6d4f22
520 lines
14 KiB
C
520 lines
14 KiB
C
/* -----------------------------------------------------------------------------
|
|
* signal.c
|
|
*
|
|
* WAD signal handler.
|
|
*
|
|
* Author(s) : David Beazley (beazley@cs.uchicago.edu)
|
|
*
|
|
* Copyright (C) 2000. The University of Chicago.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* See the file COPYING for a complete copy of the LGPL.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
#include "wad.h"
|
|
|
|
static char cvs[] = "$Header$";
|
|
|
|
extern void wad_stab_debug();
|
|
|
|
/* For some odd reason, certain linux distributions do not seem to define the
|
|
register constants in a way that is easily accessible to us. This is a hack */
|
|
|
|
#ifdef WAD_LINUX
|
|
#ifndef ESP
|
|
#define ESP 7
|
|
#endif
|
|
#ifndef EBP
|
|
#define EBP 6
|
|
#endif
|
|
#ifndef EIP
|
|
#define EIP 14
|
|
#endif
|
|
#ifndef ESI
|
|
#define ESI 5
|
|
#endif
|
|
#ifndef EDI
|
|
#define EDI 4
|
|
#endif
|
|
#ifndef EBX
|
|
#define EBX 8
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/* Signal handling stack */
|
|
#define STACK_SIZE 4*SIGSTKSZ
|
|
char wad_sig_stack[STACK_SIZE];
|
|
|
|
/* This variable is set if the signal handler thinks that the
|
|
heap has overflowed */
|
|
|
|
int wad_heap_overflow = 0;
|
|
|
|
static void (*sig_callback)(int signo, WadFrame *data, char *ret) = 0;
|
|
|
|
void wad_set_callback(void (*s)(int,WadFrame *,char *ret)) {
|
|
sig_callback = s;
|
|
}
|
|
|
|
/* This bit of nastiness is used to make a non-local return from the
|
|
signal handler to a configurable location on the call stack. In a nutshell,
|
|
this works by repeatedly calling "restore" to roll back the
|
|
register windows and stack pointer. Then we fake a return value and
|
|
return to the caller as if the function had actually completed
|
|
normally. */
|
|
|
|
int wad_nlr_levels = 0;
|
|
static volatile int *volatile nlr_p = &wad_nlr_levels;
|
|
long wad_nlr_value = 0;
|
|
void (*wad_nlr_func)(void) = 0;
|
|
|
|
/* Set the return value from another module */
|
|
void wad_set_return_value(long value) {
|
|
wad_nlr_value = value;
|
|
}
|
|
|
|
/* Set the return function */
|
|
void wad_set_return_func(void(*f)(void)) {
|
|
wad_nlr_func = f;
|
|
}
|
|
|
|
#ifdef WAD_SOLARIS
|
|
static void nonlocalret() {
|
|
long a;
|
|
|
|
a = wad_nlr_value;
|
|
/* We never call this procedure as a function. This code
|
|
causes an immediate return if someone does this */
|
|
|
|
asm("jmp %i7 + 8");
|
|
asm("restore");
|
|
|
|
/* This is the real entry point */
|
|
/* asm(".globl _returnsignal");*/
|
|
asm(".type _returnsignal,2");
|
|
asm("_returnsignal:");
|
|
|
|
while (*nlr_p > 0) {
|
|
(*nlr_p)--;
|
|
asm("restore");
|
|
}
|
|
|
|
asm("sethi %hi(wad_nlr_value), %o0");
|
|
asm("or %o0, %lo(wad_nlr_value), %o0");
|
|
asm("ld [%o0], %i0");
|
|
|
|
/* If there is a non-local return function. We're going to go ahead
|
|
and transfer control to it */
|
|
|
|
if (wad_nlr_func)
|
|
(*wad_nlr_func)();
|
|
|
|
asm("jmp %i7 + 8");
|
|
asm("restore");
|
|
asm(".size _returnsignal,(.-_returnsignal)");
|
|
}
|
|
#endif
|
|
|
|
#ifdef WAD_LINUX
|
|
|
|
/* Saved values of the machine registers */
|
|
|
|
long wad_saved_esi = 0;
|
|
long wad_saved_edi = 0;
|
|
long wad_saved_ebx = 0;
|
|
|
|
static void nonlocalret() {
|
|
asm("_returnsignal:");
|
|
while (*nlr_p > 0) {
|
|
(*nlr_p)--;
|
|
asm("leave");
|
|
}
|
|
|
|
if (wad_nlr_func)
|
|
(*wad_nlr_func)();
|
|
|
|
/* Restore the registers */
|
|
asm("movl wad_saved_esi, %esi");
|
|
asm("movl wad_saved_edi, %edi");
|
|
asm("movl wad_saved_ebx, %ebx");
|
|
asm("movl wad_nlr_value, %eax");
|
|
asm("leave");
|
|
asm("ret");
|
|
}
|
|
|
|
/* This function uses a heuristic to restore the callee-save registers on i386.
|
|
According to the Linux Assembly HOWTO, the %esi, %edi, %ebx, and %ebp registers
|
|
are callee-saved. All others are caller saved. To restore the callee-save
|
|
registers, we use the fact that the C compiler saves the callee-save registers
|
|
(if any) at the beginning of function execution. Therefore, we can scan the
|
|
instructions at the start of each function in the stack trace to try and find
|
|
where they are.
|
|
|
|
The following heuristic is used:
|
|
|
|
1. Each function starts with a preamble like this which saves the %ebp
|
|
register:
|
|
|
|
55 89 e5 ---> push %ebp
|
|
mov %esp, %ebp
|
|
|
|
2. Next, space is allocated for local variables, using one of two schemes:
|
|
|
|
83 ec xx ---> Less than 256 bytes of local storage
|
|
^^^
|
|
length
|
|
|
|
81 ec xx xx xx xx --> More than 256 bytes of local storage
|
|
^^^^^^^^^^^
|
|
length
|
|
|
|
3. After this, a collection of 1-byte stack push op codes might appear
|
|
|
|
56 = pushl %esi
|
|
57 = pushl %edi
|
|
53 = pushl %ebx
|
|
|
|
|
|
Based on the size of local variable storage and the order in which
|
|
the %esi, %edi, and %ebx registers are pushed on the stack, we can
|
|
determine where in memory the registers are saved and restore them to
|
|
their proper values.
|
|
*/
|
|
|
|
void wad_restore_i386_registers(WadFrame *f, int nlevels) {
|
|
WadFrame *lastf = f;
|
|
int localsize = 0;
|
|
unsigned char *pc;
|
|
unsigned long *saved;
|
|
int i, j;
|
|
int pci;
|
|
for (i = 0; i <= nlevels; i++, f=f->next) {
|
|
|
|
/* This gets the starting instruction for the stack frame */
|
|
pc = (unsigned char *) f->sym_base;
|
|
/* printf("pc = %x, base = %x, %s\n", f->pc, f->sym_base, SYMBOL(f)); */
|
|
if (!pc) continue;
|
|
|
|
/* Look for the standard prologue 0x55 0x89 0xe5 */
|
|
if ((pc[0] == 0x55) && (pc[1] == 0x89) && (pc[2] == 0xe5)) {
|
|
/* Determine the size */
|
|
pci = 3;
|
|
if ((pc[3] == 0x83) && (pc[4] == 0xec)) {
|
|
/* printf("8-bit size\n");*/
|
|
localsize = (int) pc[5];
|
|
pci = 6;
|
|
}
|
|
if ((pc[3] == 0x81) && (pc[4] == 0xec)) {
|
|
/* printf("32-bit size\n"); */
|
|
localsize = (int) *((long *) (pc+5));
|
|
pci = 10;
|
|
}
|
|
saved = (long *) (f->fp - localsize - sizeof(long));
|
|
/* printf("saved = %x, fp = %x\n", saved, f->fp);
|
|
printf("localsize = %d\n", localsize);
|
|
*/
|
|
for (j = 0; j < 3; j++, saved--, pci++) {
|
|
if (pc[pci] == 0x57) {
|
|
wad_saved_edi = *saved;
|
|
/* printf("restored edi = %x\n", wad_saved_edi); */
|
|
}
|
|
else if (pc[pci] == 0x56) {
|
|
wad_saved_esi = *saved;
|
|
/* printf("restored esi = %x\n", wad_saved_esi); */
|
|
}
|
|
else if (pc[pci] == 0x53) {
|
|
wad_saved_ebx = *saved;
|
|
/* printf("restored ebx = %x\n", wad_saved_ebx); */
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void wad_signalhandler(int sig, siginfo_t *si, void *vcontext) {
|
|
greg_t *pc;
|
|
greg_t *npc;
|
|
greg_t *sp;
|
|
greg_t *fp;
|
|
#ifdef WAD_LINUX
|
|
greg_t *esi;
|
|
greg_t *edi;
|
|
greg_t *ebx;
|
|
#endif
|
|
|
|
unsigned long addr;
|
|
ucontext_t *context;
|
|
unsigned long p_sp; /* process stack pointer */
|
|
unsigned long p_pc; /* Process program counter */
|
|
unsigned long p_fp; /* Process frame pointer */
|
|
int nlevels = 0;
|
|
int found = 0;
|
|
void _returnsignal();
|
|
WadFrame *frame, *origframe;
|
|
char *framedata;
|
|
char *retname = 0;
|
|
unsigned long current_brk;
|
|
|
|
/* Reset all of the signals while running WAD */
|
|
wad_signal_clear();
|
|
|
|
wad_nlr_func = 0;
|
|
|
|
context = (ucontext_t *) vcontext;
|
|
|
|
wad_printf("WAD: Collecting debugging information...\n");
|
|
|
|
/* Read the segments */
|
|
if (wad_segment_read() < 0) {
|
|
wad_printf("WAD: Unable to read segment map\n");
|
|
return;
|
|
}
|
|
|
|
if (wad_debug_mode & DEBUG_SIGNAL) {
|
|
wad_printf("WAD: siginfo = %x, context = %x\n", si, vcontext);
|
|
}
|
|
|
|
current_brk = (long) sbrk(0);
|
|
|
|
/* Get some information about the current context */
|
|
|
|
#ifdef WAD_SOLARIS
|
|
pc = &((context->uc_mcontext).gregs[REG_PC]);
|
|
npc = &((context->uc_mcontext).gregs[REG_nPC]);
|
|
sp = &((context->uc_mcontext).gregs[REG_SP]);
|
|
#endif
|
|
|
|
#ifdef WAD_LINUX
|
|
sp = &((context->uc_mcontext).gregs[ESP]); /* Top of stack */
|
|
fp = &((context->uc_mcontext).gregs[EBP]); /* Stack base - frame pointer */
|
|
pc = &((context->uc_mcontext).gregs[EIP]); /* Current instruction */
|
|
esi = &((context->uc_mcontext).gregs[ESI]);
|
|
edi = &((context->uc_mcontext).gregs[EDI]);
|
|
ebx = &((context->uc_mcontext).gregs[EBX]);
|
|
|
|
wad_saved_esi = (unsigned long) (*esi);
|
|
wad_saved_edi = (unsigned long) (*edi);
|
|
wad_saved_ebx = (unsigned long) (*ebx);
|
|
|
|
/* printf("esi = %x, edi = %x, ebx = %x\n", wad_saved_esi, wad_saved_edi, wad_saved_ebx); */
|
|
|
|
/* printf("&sp = %x, &pc = %x\n", sp, pc); */
|
|
#endif
|
|
|
|
/* Get some information out of the signal handler stack */
|
|
addr = (unsigned long) si->si_addr;
|
|
|
|
/* See if this might be a stack overflow */
|
|
|
|
p_pc = (unsigned long) (*pc);
|
|
p_sp = (unsigned long) (*sp);
|
|
#ifdef WAD_LINUX
|
|
p_fp = (unsigned long) (*fp);
|
|
#endif
|
|
#ifdef WAD_SOLARIS
|
|
p_fp = (unsigned long) *(((long *) p_sp) + 14);
|
|
#endif
|
|
|
|
if (wad_debug_mode & DEBUG_SIGNAL) {
|
|
wad_printf("fault at address %x, pc = %x, sp = %x, fp = %x\n", addr, p_pc, p_sp, p_fp);
|
|
}
|
|
frame = wad_stack_trace(p_pc, p_sp, p_fp);
|
|
|
|
if (!frame) {
|
|
/* We're really hosed. Not possible to generate a stack trace */
|
|
wad_printf("WAD: Unable to generate stack trace.\n");
|
|
wad_printf("WAD: Maybe the call stack has been corrupted by buffer overflow.\n");
|
|
wad_signal_clear();
|
|
return;
|
|
}
|
|
|
|
{
|
|
WadFrame *f = frame;
|
|
while (f) {
|
|
wad_find_object(f);
|
|
wad_find_symbol(f);
|
|
f = f->next;
|
|
}
|
|
f = frame;
|
|
while (f) {
|
|
wad_find_debug(f);
|
|
wad_build_vars(f);
|
|
f = f->next;
|
|
}
|
|
}
|
|
wad_heap_overflow = 0;
|
|
if (sig == SIGSEGV) {
|
|
if (addr >= current_brk) wad_heap_overflow = 1;
|
|
}
|
|
|
|
wad_stack_debug(frame);
|
|
|
|
/* Generate debugging strings */
|
|
wad_debug_make_strings(frame);
|
|
|
|
wad_stab_debug();
|
|
|
|
/* Walk the exception frames and try to find a return point */
|
|
origframe = frame;
|
|
while (frame) {
|
|
WadReturnFunc *wr = wad_check_return(frame->sym_name);
|
|
if (wr) {
|
|
found = 1;
|
|
wad_nlr_value = wr->value;
|
|
retname = wr->name;
|
|
}
|
|
if (found) {
|
|
frame->last = 1; /* Cut off top of the stack trace */
|
|
break;
|
|
}
|
|
frame = frame->next;
|
|
nlevels++;
|
|
}
|
|
|
|
|
|
if (found) {
|
|
wad_nlr_levels = nlevels - 1;
|
|
#ifdef WAD_LINUX
|
|
wad_restore_i386_registers(origframe, wad_nlr_levels);
|
|
#endif
|
|
} else {
|
|
wad_nlr_levels = -1;
|
|
}
|
|
|
|
wad_string_debug();
|
|
wad_memory_debug();
|
|
|
|
/* Before we do anything with callbacks, we are going
|
|
to attempt to dump a wad-core */
|
|
|
|
{
|
|
int fd;
|
|
static int already = 0;
|
|
fd = open("wadtrace",O_WRONLY | O_CREAT | (already*O_APPEND) | ((already==0)*O_TRUNC),0666);
|
|
if (fd > 0) {
|
|
wad_dump_trace(fd,sig,origframe,retname);
|
|
close(fd);
|
|
already=1;
|
|
}
|
|
}
|
|
|
|
if (sig_callback) {
|
|
(*sig_callback)(sig,origframe,retname);
|
|
} else {
|
|
/* No signal handler defined. Go invoke the default */
|
|
|
|
wad_default_callback(sig, origframe,retname);
|
|
}
|
|
|
|
if (wad_debug_mode & DEBUG_HOLD) while(1);
|
|
|
|
/* If we found a function to which we should return, we jump to
|
|
an alternative piece of code that unwinds the stack and
|
|
initiates a non-local return. */
|
|
|
|
if (wad_nlr_levels >= 0) {
|
|
*(pc) = (greg_t) _returnsignal;
|
|
#ifdef WAD_SOLARIS
|
|
*(npc) = *(pc) + 4;
|
|
#endif
|
|
if (!(wad_debug_mode & DEBUG_ONESHOT)) {
|
|
wad_signal_init();
|
|
}
|
|
return;
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* wad_signal_init()
|
|
*
|
|
* Resets the signal handler.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void wad_signal_init() {
|
|
struct sigaction newvec;
|
|
static stack_t sigstk;
|
|
static int initstack = 0;
|
|
|
|
if (wad_debug_mode & DEBUG_INIT) {
|
|
wad_printf("WAD: Initializing signal handler.\n");
|
|
}
|
|
/* This is buggy in Linux and threads. disabled by default */
|
|
|
|
#ifndef WAD_LINUX
|
|
|
|
if (!initstack) {
|
|
/* Set up an alternative stack */
|
|
|
|
sigstk.ss_sp = (char *) wad_sig_stack;
|
|
sigstk.ss_size = STACK_SIZE;
|
|
sigstk.ss_flags = 0;
|
|
if (!(wad_debug_mode & DEBUG_NOSTACK)) {
|
|
if (sigaltstack(&sigstk, (stack_t*)0) < 0) {
|
|
perror("sigaltstack");
|
|
}
|
|
}
|
|
initstack=1;
|
|
}
|
|
#endif
|
|
|
|
sigemptyset(&newvec.sa_mask);
|
|
sigaddset(&newvec.sa_mask, SIGSEGV);
|
|
sigaddset(&newvec.sa_mask, SIGBUS);
|
|
sigaddset(&newvec.sa_mask, SIGABRT);
|
|
sigaddset(&newvec.sa_mask, SIGILL);
|
|
sigaddset(&newvec.sa_mask, SIGFPE);
|
|
newvec.sa_flags = SA_SIGINFO;
|
|
|
|
if (wad_debug_mode & DEBUG_ONESHOT) {
|
|
newvec.sa_flags |= SA_RESETHAND;
|
|
}
|
|
#ifndef WAD_LINUX
|
|
if (!(wad_debug_mode & DEBUG_NOSTACK)) {
|
|
newvec.sa_flags |= SA_ONSTACK;
|
|
}
|
|
#endif
|
|
newvec.sa_sigaction = ((void (*)(int,siginfo_t *, void *)) wad_signalhandler);
|
|
if (sigaction(SIGSEGV, &newvec, NULL) < 0) goto werror;
|
|
if (sigaction(SIGBUS, &newvec, NULL) < 0) goto werror;
|
|
if (sigaction(SIGABRT, &newvec, NULL) < 0) goto werror;
|
|
if (sigaction(SIGFPE, &newvec, NULL) < 0) goto werror;
|
|
if (sigaction(SIGILL, &newvec, NULL) < 0) goto werror;
|
|
|
|
return;
|
|
werror:
|
|
wad_printf("WAD: Couldn't install signal handler!\n");
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* clear signals
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
void wad_signal_clear() {
|
|
signal(SIGSEGV, SIG_DFL);
|
|
signal(SIGBUS, SIG_DFL);
|
|
signal(SIGILL, SIG_DFL);
|
|
signal(SIGFPE, SIG_DFL);
|
|
signal(SIGABRT, SIG_DFL);
|
|
}
|
|
|
|
|
|
|