PageRenderTime 77ms CodeModel.GetById 35ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/wad-0-2-1/SWIG/Tools/WAD/Wad/signal.c

#
C | 520 lines | 313 code | 85 blank | 122 comment | 61 complexity | 6cd16ad447ddb6b1ed34fd1a874ea3af MD5 | raw file
Possible License(s): LGPL-2.1, Cube, GPL-3.0, 0BSD, GPL-2.0
  1/* ----------------------------------------------------------------------------- 
  2 * signal.c
  3 *
  4 *     WAD signal handler. 
  5 * 
  6 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
  7 *
  8 * Copyright (C) 2000.  The University of Chicago. 
  9 *
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation; either
 13 * version 2.1 of the License, or (at your option) any later version.
 14 *
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 23 * 
 24 * See the file COPYING for a complete copy of the LGPL.
 25 * ----------------------------------------------------------------------------- */
 26
 27#include "wad.h"
 28
 29static char cvs[] = "$Header$";
 30
 31extern void wad_stab_debug();
 32
 33/* For some odd reason, certain linux distributions do not seem to define the
 34   register constants in a way that is easily accessible to us.  This is a hack */
 35
 36#ifdef WAD_LINUX
 37#ifndef ESP
 38#define ESP      7
 39#endif
 40#ifndef EBP
 41#define EBP      6
 42#endif
 43#ifndef EIP
 44#define EIP      14
 45#endif
 46#ifndef ESI
 47#define ESI      5
 48#endif
 49#ifndef EDI
 50#define EDI      4
 51#endif
 52#ifndef EBX
 53#define EBX      8
 54#endif
 55
 56#endif
 57
 58/* Signal handling stack */
 59#define STACK_SIZE 4*SIGSTKSZ
 60char wad_sig_stack[STACK_SIZE];
 61
 62/* This variable is set if the signal handler thinks that the
 63   heap has overflowed */
 64
 65int wad_heap_overflow = 0;
 66
 67static void (*sig_callback)(int signo, WadFrame *data, char *ret) = 0;
 68
 69void wad_set_callback(void (*s)(int,WadFrame *,char *ret)) {
 70  sig_callback = s;
 71}
 72
 73/* This bit of nastiness is used to make a non-local return from the
 74   signal handler to a configurable location on the call stack. In a nutshell,
 75   this works by repeatedly calling "restore" to roll back the 
 76   register windows and stack pointer.  Then we fake a return value and
 77   return to the caller as if the function had actually completed
 78   normally. */
 79
 80int            wad_nlr_levels = 0;
 81static volatile int  *volatile nlr_p = &wad_nlr_levels;
 82long           wad_nlr_value = 0;
 83void          (*wad_nlr_func)(void) = 0;
 84
 85/* Set the return value from another module */
 86void wad_set_return_value(long value) {
 87  wad_nlr_value = value;
 88}
 89
 90/* Set the return function */
 91void wad_set_return_func(void(*f)(void)) {
 92  wad_nlr_func = f;
 93}
 94
 95#ifdef WAD_SOLARIS
 96static void nonlocalret() {
 97  long a;
 98  
 99  a = wad_nlr_value;
100  /* We never call this procedure as a function.  This code
101     causes an immediate return if someone does this */
102
103  asm("jmp %i7 + 8");
104  asm("restore");
105
106  /* This is the real entry point */
107  /*  asm(".globl _returnsignal");*/
108  asm(".type   _returnsignal,2");
109  asm("_returnsignal:");
110
111  while (*nlr_p > 0) {
112    (*nlr_p)--;
113    asm("restore");
114  }
115
116  asm("sethi %hi(wad_nlr_value), %o0");
117  asm("or %o0, %lo(wad_nlr_value), %o0");
118  asm("ld [%o0], %i0");
119
120  /* If there is a non-local return function.  We're going to go ahead
121     and transfer control to it */
122  
123  if (wad_nlr_func) 
124    (*wad_nlr_func)();
125
126  asm("jmp %i7 + 8");
127  asm("restore");
128  asm(".size	_returnsignal,(.-_returnsignal)");
129}
130#endif
131
132#ifdef WAD_LINUX
133
134/* Saved values of the machine registers */
135
136long   wad_saved_esi = 0;
137long   wad_saved_edi = 0;
138long   wad_saved_ebx = 0;
139
140static void nonlocalret() {
141  asm("_returnsignal:");
142  while (*nlr_p > 0) {
143    (*nlr_p)--;
144    asm("leave");
145  }
146
147  if (wad_nlr_func) 
148    (*wad_nlr_func)();
149
150  /* Restore the registers */
151  asm("movl wad_saved_esi, %esi");
152  asm("movl wad_saved_edi, %edi");
153  asm("movl wad_saved_ebx, %ebx");
154  asm("movl wad_nlr_value, %eax");
155  asm("leave");
156  asm("ret");
157}
158
159/* This function uses a heuristic to restore the callee-save registers on i386.
160   According to the Linux Assembly HOWTO, the %esi, %edi, %ebx, and %ebp registers
161   are callee-saved.  All others are caller saved.    To restore the callee-save
162   registers, we use the fact that the C compiler saves the callee-save registers
163   (if any) at the beginning of function execution.   Therefore, we can scan the
164   instructions at the start of each function in the stack trace to try and find
165   where they are. 
166
167   The following heuristic is used:
168
169   1. Each function starts with a preamble like this which saves the %ebp 
170      register:
171
172          55 89 e5       --->   push %ebp
173                                mov  %esp, %ebp
174
175   2. Next, space is allocated for local variables, using one of two schemes:
176 
177          83 ec xx       --->  Less than 256 bytes of local storage
178                ^^^
179                length
180
181          81 ec xx xx xx xx  --> More than 256 bytes of local storage
182                ^^^^^^^^^^^
183                   length
184
185   3. After this, a collection of 1-byte stack push op codes might appear
186          
187          56      = pushl %esi
188          57      = pushl %edi
189          53      = pushl %ebx
190
191
192   Based on the size of local variable storage and the order in which 
193   the %esi, %edi, and %ebx registers are pushed on the stack, we can
194   determine where in memory the registers are saved and restore them to
195   their proper values.
196*/
197
198void wad_restore_i386_registers(WadFrame *f, int nlevels) {
199  WadFrame *lastf = f;
200  int       localsize = 0;
201  unsigned char     *pc;
202  unsigned long     *saved;
203  int i, j;
204  int pci;
205  for (i = 0; i <= nlevels; i++, f=f->next) {
206
207    /* This gets the starting instruction for the stack frame */
208    pc = (unsigned char *) f->sym_base;
209    /*    printf("pc = %x, base = %x, %s\n", f->pc, f->sym_base, SYMBOL(f)); */
210    if (!pc) continue;
211
212    /* Look for the standard prologue 0x55 0x89 0xe5 */
213    if ((pc[0] == 0x55) && (pc[1] == 0x89) && (pc[2] == 0xe5)) {
214      /* Determine the size */
215      pci = 3;
216      if ((pc[3] == 0x83) && (pc[4] == 0xec)) {
217	/*	printf("8-bit size\n");*/
218	localsize = (int) pc[5];
219	pci = 6;
220      } 
221      if ((pc[3] == 0x81) && (pc[4] == 0xec)) {
222	/*	printf("32-bit size\n"); */
223	localsize = (int) *((long *) (pc+5));
224	pci = 10;
225      }
226      saved = (long *) (f->fp - localsize - sizeof(long));
227      /*      printf("saved = %x, fp = %x\n", saved, f->fp);
228      printf("localsize = %d\n", localsize);
229      */
230      for (j = 0; j < 3; j++, saved--, pci++) {
231	if (pc[pci] == 0x57) {
232	  wad_saved_edi = *saved;
233	  /*	  printf("restored edi = %x\n", wad_saved_edi); */
234	}
235	else if (pc[pci] == 0x56) {
236	  wad_saved_esi = *saved;
237	  /*	  printf("restored esi = %x\n", wad_saved_esi); */
238	}
239	else if (pc[pci] == 0x53) {
240	  wad_saved_ebx = *saved;
241	  /*	  printf("restored ebx = %x\n", wad_saved_ebx); */
242	}
243	else break;
244      }
245    }
246  }
247}
248
249#endif
250
251void wad_signalhandler(int sig, siginfo_t *si, void *vcontext) {
252  greg_t  *pc;
253  greg_t  *npc;
254  greg_t  *sp;
255  greg_t  *fp;
256#ifdef WAD_LINUX
257  greg_t  *esi;
258  greg_t  *edi;
259  greg_t  *ebx;
260#endif
261
262  unsigned long   addr;
263  ucontext_t      *context;
264  unsigned long   p_sp;        /* process stack pointer   */
265  unsigned long   p_pc;        /* Process program counter */
266  unsigned long   p_fp;        /* Process frame pointer   */
267  int      nlevels = 0;
268  int      found = 0;
269  void     _returnsignal();
270  WadFrame  *frame, *origframe;
271  char      *framedata;
272  char      *retname = 0;
273  unsigned long current_brk;
274
275  /* Reset all of the signals while running WAD */
276  wad_signal_clear();
277
278  wad_nlr_func = 0;
279
280  context = (ucontext_t *) vcontext;
281
282  wad_printf("WAD: Collecting debugging information...\n");
283
284  /* Read the segments */
285  if (wad_segment_read() < 0) {
286    wad_printf("WAD: Unable to read segment map\n");
287    return;
288  }
289 
290  if (wad_debug_mode & DEBUG_SIGNAL) {
291    wad_printf("WAD: siginfo = %x, context = %x\n", si, vcontext);
292  }
293  
294  current_brk = (long) sbrk(0);
295
296  /* Get some information about the current context */
297
298#ifdef WAD_SOLARIS
299  pc = &((context->uc_mcontext).gregs[REG_PC]);
300  npc = &((context->uc_mcontext).gregs[REG_nPC]);
301  sp = &((context->uc_mcontext).gregs[REG_SP]);
302#endif
303
304#ifdef WAD_LINUX
305  sp = &((context->uc_mcontext).gregs[ESP]);        /* Top of stack */
306  fp = &((context->uc_mcontext).gregs[EBP]);        /* Stack base - frame pointer */
307  pc = &((context->uc_mcontext).gregs[EIP]);        /* Current instruction */
308  esi = &((context->uc_mcontext).gregs[ESI]);       
309  edi = &((context->uc_mcontext).gregs[EDI]);       
310  ebx = &((context->uc_mcontext).gregs[EBX]);       
311  
312  wad_saved_esi = (unsigned long) (*esi);
313  wad_saved_edi = (unsigned long) (*edi);
314  wad_saved_ebx = (unsigned long) (*ebx);
315
316  /*  printf("esi = %x, edi = %x, ebx = %x\n", wad_saved_esi, wad_saved_edi, wad_saved_ebx); */
317
318  /*   printf("&sp = %x, &pc = %x\n", sp, pc); */
319#endif
320  
321  /* Get some information out of the signal handler stack */
322  addr = (unsigned long) si->si_addr;
323
324  /* See if this might be a stack overflow */
325
326  p_pc = (unsigned long) (*pc);
327  p_sp = (unsigned long) (*sp);
328#ifdef WAD_LINUX
329  p_fp = (unsigned long) (*fp);
330#endif
331#ifdef WAD_SOLARIS
332  p_fp = (unsigned long) *(((long *) p_sp) + 14);
333#endif
334  
335  if (wad_debug_mode & DEBUG_SIGNAL) {
336    wad_printf("fault at address %x, pc = %x, sp = %x, fp = %x\n", addr, p_pc, p_sp, p_fp);
337  }
338  frame = wad_stack_trace(p_pc, p_sp, p_fp);
339
340  if (!frame) {
341    /* We're really hosed.  Not possible to generate a stack trace */
342    wad_printf("WAD: Unable to generate stack trace.\n");
343    wad_printf("WAD: Maybe the call stack has been corrupted by buffer overflow.\n");
344    wad_signal_clear();
345    return;
346  }
347
348  {
349    WadFrame *f = frame;
350    while (f) {
351      wad_find_object(f);
352      wad_find_symbol(f);
353      f = f->next;
354    }
355    f = frame;
356    while (f) {
357      wad_find_debug(f);
358      wad_build_vars(f);
359      f = f->next;
360    }
361  }
362  wad_heap_overflow = 0;
363  if (sig == SIGSEGV) {
364    if (addr >= current_brk) wad_heap_overflow = 1;
365  }
366
367  wad_stack_debug(frame);
368
369  /* Generate debugging strings */
370  wad_debug_make_strings(frame);
371  
372  wad_stab_debug();
373
374  /* Walk the exception frames and try to find a return point */
375  origframe = frame;
376  while (frame) {
377    WadReturnFunc *wr = wad_check_return(frame->sym_name);
378    if (wr) {
379      found = 1;
380      wad_nlr_value = wr->value;
381      retname = wr->name;
382    }
383    if (found) {
384      frame->last = 1;   /* Cut off top of the stack trace */
385      break;
386    }
387    frame = frame->next;
388    nlevels++;
389  }
390  
391
392  if (found) {
393    wad_nlr_levels = nlevels - 1;
394#ifdef WAD_LINUX
395    wad_restore_i386_registers(origframe, wad_nlr_levels);
396#endif
397  } else {
398    wad_nlr_levels = -1;
399  }
400
401  wad_string_debug();
402  wad_memory_debug();
403
404  /* Before we do anything with callbacks, we are going
405     to attempt to dump a wad-core */
406  
407  {
408    int fd;
409    static int already = 0;
410    fd = open("wadtrace",O_WRONLY | O_CREAT | (already*O_APPEND) | ((already==0)*O_TRUNC),0666);
411    if (fd > 0) {
412      wad_dump_trace(fd,sig,origframe,retname);
413      close(fd);
414      already=1;
415    }
416  }
417
418  if (sig_callback) {
419    (*sig_callback)(sig,origframe,retname);
420  } else {
421    /* No signal handler defined.  Go invoke the default */
422
423    wad_default_callback(sig, origframe,retname);
424  }
425
426  if (wad_debug_mode & DEBUG_HOLD) while(1);
427
428  /* If we found a function to which we should return, we jump to
429     an alternative piece of code that unwinds the stack and 
430     initiates a non-local return. */
431
432  if (wad_nlr_levels >= 0) {
433    *(pc) = (greg_t) _returnsignal;
434#ifdef WAD_SOLARIS
435    *(npc) = *(pc) + 4;
436#endif
437    if (!(wad_debug_mode & DEBUG_ONESHOT)) {
438      wad_signal_init();
439    }
440    return;
441  }
442  exit(1);
443}
444
445
446/* -----------------------------------------------------------------------------
447 * wad_signal_init()
448 *
449 * Resets the signal handler.
450 * ----------------------------------------------------------------------------- */
451
452void wad_signal_init() {
453  struct sigaction newvec;
454  static stack_t  sigstk;
455  static int      initstack = 0;
456
457  if (wad_debug_mode & DEBUG_INIT) {
458    wad_printf("WAD: Initializing signal handler.\n");
459  }
460  /* This is buggy in Linux and threads.  disabled by default */
461
462#ifndef WAD_LINUX
463
464  if (!initstack) {
465    /* Set up an alternative stack */
466    
467    sigstk.ss_sp = (char *) wad_sig_stack;
468    sigstk.ss_size = STACK_SIZE;
469    sigstk.ss_flags = 0;
470    if (!(wad_debug_mode & DEBUG_NOSTACK)) {
471      if (sigaltstack(&sigstk, (stack_t*)0) < 0) {
472	perror("sigaltstack");
473      }
474    }
475    initstack=1;
476  }
477#endif
478
479  sigemptyset(&newvec.sa_mask);
480  sigaddset(&newvec.sa_mask, SIGSEGV);
481  sigaddset(&newvec.sa_mask, SIGBUS);
482  sigaddset(&newvec.sa_mask, SIGABRT);
483  sigaddset(&newvec.sa_mask, SIGILL);
484  sigaddset(&newvec.sa_mask, SIGFPE);
485  newvec.sa_flags = SA_SIGINFO;
486
487  if (wad_debug_mode & DEBUG_ONESHOT) {
488    newvec.sa_flags |= SA_RESETHAND;
489  }
490#ifndef WAD_LINUX
491  if (!(wad_debug_mode & DEBUG_NOSTACK)) {
492    newvec.sa_flags |= SA_ONSTACK;
493  } 
494#endif
495  newvec.sa_sigaction = ((void (*)(int,siginfo_t *, void *)) wad_signalhandler);
496  if (sigaction(SIGSEGV, &newvec, NULL) < 0) goto werror;
497  if (sigaction(SIGBUS, &newvec, NULL) < 0) goto werror;
498  if (sigaction(SIGABRT, &newvec, NULL) < 0) goto werror;
499  if (sigaction(SIGFPE, &newvec, NULL) < 0) goto werror;
500  if (sigaction(SIGILL, &newvec, NULL) < 0) goto werror;
501  
502  return;
503 werror:
504  wad_printf("WAD: Couldn't install signal handler!\n");
505}
506
507/* -----------------------------------------------------------------------------
508 * clear signals 
509 * ----------------------------------------------------------------------------- */
510
511void wad_signal_clear() {
512  signal(SIGSEGV, SIG_DFL);
513  signal(SIGBUS, SIG_DFL);
514  signal(SIGILL, SIG_DFL);
515  signal(SIGFPE, SIG_DFL);
516  signal(SIGABRT, SIG_DFL);  
517}
518
519
520