1 /*
2  * mpatrol
3  * A library for controlling and tracing dynamic memory allocations.
4  * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307, USA.
20  */
21 
22 
23 /*
24  * Signal-handling.  The setting of signal-handlers and their actions
25  * is controlled from this module.
26  */
27 
28 
29 #include "signals.h"
30 #include "diag.h"
31 #if MP_THREADS_SUPPORT
32 #include "mutex.h"
33 #endif /* MP_THREADS_SUPPORT */
34 #include "inter.h"
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #if TARGET == TARGET_UNIX
39 #if MP_SIGINFO_SUPPORT
40 #include <siginfo.h>
41 #endif /* MP_SIGINFO_SUPPORT */
42 #elif TARGET == TARGET_WINDOWS
43 #include <windows.h>
44 #endif /* TARGET */
45 
46 
47 #if MP_IDENT_SUPPORT
48 #ident "$Id: signals.c,v 1.26 2002/01/08 20:13:59 graeme Exp $"
49 #else /* MP_IDENT_SUPPORT */
50 static MP_CONST MP_VOLATILE char *signals_id = "$Id: signals.c,v 1.26 2002/01/08 20:13:59 graeme Exp $";
51 #endif /* MP_IDENT_SUPPORT */
52 
53 
54 #ifdef __cplusplus
55 extern "C"
56 {
57 #endif /* __cplusplus */
58 
59 
60 /* The type of the parameter that is passed to the signal() function.
61  */
62 
63 typedef void (*handlerfunction)(int);
64 
65 
66 #if TARGET == TARGET_UNIX || TARGET == TARGET_WINDOWS
67 /* Handles any signals that result from illegal memory accesses.
68  */
69 
70 #if TARGET == TARGET_UNIX
71 #if MP_SIGINFO_SUPPORT
72 static
73 void
signalhandler(int s,siginfo_t * n,void * p)74 signalhandler(int s, siginfo_t *n, void *p)
75 #else /* MP_SIGINFO_SUPPORT */
76 #if SYSTEM == SYSTEM_AIX || SYSTEM == SYSTEM_FREEBSD || \
77     SYSTEM == SYSTEM_IRIX || SYSTEM == SYSTEM_LINUX || \
78     SYSTEM == SYSTEM_LYNXOS || SYSTEM == SYSTEM_NETBSD || \
79     SYSTEM == SYSTEM_OPENBSD || SYSTEM == SYSTEM_SUNOS
80 #if SYSTEM == SYSTEM_FREEBSD || SYSTEM == SYSTEM_NETBSD || \
81     SYSTEM == SYSTEM_OPENBSD || SYSTEM == SYSTEM_SUNOS
82 static
83 void
84 signalhandler(int s, int c, struct sigcontext *n, void *f)
85 #elif SYSTEM == SYSTEM_LINUX && ARCH == ARCH_IX86
86 static
87 void
88 signalhandler(int s, struct sigcontext n)
89 #else /* SYSTEM && ARCH */
90 static
91 void
92 signalhandler(int s, int c, struct sigcontext *n)
93 #endif /* SYSTEM && ARCH */
94 #else /* SYSTEM */
95 static
96 void
97 signalhandler(int s)
98 #endif /* SYSTEM */
99 #endif /* MP_SIGINFO_SUPPORT */
100 #elif TARGET == TARGET_WINDOWS
101 static
102 long __stdcall
103 signalhandler(EXCEPTION_POINTERS *e)
104 #endif /* TARGET */
105 {
106     infohead *h;
107     allocnode *t;
108     void *a;
109 #if TARGET == TARGET_WINDOWS
110     EXCEPTION_RECORD *r;
111 #endif /* TARGET */
112     stackinfo i;
113 
114 #if TARGET == TARGET_WINDOWS
115     /* This exception handler will catch all kinds of exceptions, but the
116      * only one we are interested in is an access violation, so we return
117      * control to the system to decide what to do in all other cases.
118      */
119     r = e->ExceptionRecord;
120     if (r->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
121         return EXCEPTION_CONTINUE_SEARCH;
122 #endif /* TARGET */
123 #if MP_THREADS_SUPPORT
124     /* Attempt to lock the library data structures if we are thread-safe.
125      * Hopefully this will never result in a deadlock since we should block
126      * until all other threads have finished manipulating memory allocations.
127      */
128     __mp_lockmutex(MT_MAIN);
129 #endif /* MP_THREADS_SUPPORT */
130     h = __mp_memhead();
131     h->recur++;
132     __mp_printsummary(h);
133 #if TARGET == TARGET_UNIX
134 #if MP_SIGINFO_SUPPORT && MP_WATCH_SUPPORT
135     /* If we received a TRAP signal then we should only say that it is an
136      * illegal memory access if it is a watch point error.  Otherwise we
137      * just do the default and abort the process.
138      */
139     if ((s == SIGTRAP) && (n != NULL) && (n->si_code != TRAP_RWATCH) &&
140         (n->si_code != TRAP_WWATCH) && (n->si_code != TRAP_XWATCH))
141     {
142         h->fini = 1;
143         __mp_abort();
144     }
145 #endif /* MP_SIGINFO_SUPPORT && MP_WATCH_SUPPORT */
146 #endif /* TARGET */
147     __mp_diag("\n");
148 #if TARGET == TARGET_UNIX
149 #if MP_SIGINFO_SUPPORT || SYSTEM == SYSTEM_AIX || SYSTEM == SYSTEM_FREEBSD || \
150     SYSTEM == SYSTEM_IRIX || SYSTEM == SYSTEM_LINUX || \
151     SYSTEM == SYSTEM_LYNXOS || SYSTEM == SYSTEM_NETBSD || \
152     SYSTEM == SYSTEM_OPENBSD || SYSTEM == SYSTEM_SUNOS
153 #if MP_SIGINFO_SUPPORT
154     if ((n != NULL) && (n->si_code > 0))
155     {
156         /* With the siginfo_t structure we can determine which address
157          * caused the illegal memory access.  This is not always guaranteed
158          * to be the exact byte location, but it is at least guaranteed that
159          * it will be on the same page which will suffice for our purposes.
160          */
161         a = (void *) n->si_addr;
162 #else /* MP_SIGINFO_SUPPORT */
163 #if SYSTEM == SYSTEM_LINUX && ARCH == ARCH_IX86
164     if (&n != NULL)
165 #else /* SYSTEM && ARCH */
166     if (n != NULL)
167 #endif /* SYSTEM && ARCH */
168     {
169         /* With systems that do not contain support for passing the siginfo_t
170          * structure to a signal handler we need to use some other form of
171          * trickery.  Luckily, on most UNIX systems a sigcontext parameter
172          * is passed on the stack to all signal handlers which we can then
173          * use to decode the address that caused the illegal memory access.
174          */
175         a = NULL;
176 #if SYSTEM == SYSTEM_AIX
177         a = (void *) n->sc_jmpbuf.jmp_context.o_vaddr;
178 #elif SYSTEM == SYSTEM_FREEBSD || SYSTEM == SYSTEM_NETBSD || \
179       SYSTEM == SYSTEM_OPENBSD || SYSTEM == SYSTEM_SUNOS
180         a = f;
181 #elif SYSTEM == SYSTEM_IRIX
182         a = (void *) ((long) n->sc_badvaddr);
183 #elif SYSTEM == SYSTEM_LINUX
184 #if ARCH == ARCH_ALPHA
185         a = (char *) n->sc_regs[(*((unsigned int *) n->sc_pc) >> 16) & 0x1F] +
186             ((*((unsigned int *) n->sc_pc) << 16) >> 16);
187 #elif ARCH == ARCH_IX86
188         a = (void *) n.cr2;
189 #elif ARCH == ARCH_M68K
190         switch ((n->sc_formatvec >> 12) & 0x0F)
191         {
192           case 4:
193             /* 68060 */
194             a = (void *) ((unsigned long *) (n + 1))[0];
195             break;
196           case 7:
197             /* 68040 */
198             a = (void *) ((unsigned long *) (n + 1))[3];
199             break;
200           case 10:
201           case 11:
202           default:
203             /* 68020/68030 */
204             a = (void *) ((unsigned long *) (n + 1))[2];
205             break;
206         }
207 #endif /* ARCH */
208 #elif SYSTEM == SYSTEM_LYNXOS
209 #if ARCH == ARCH_IX86
210         a = (void *) n->eax;
211 #elif ARCH == ARCH_POWER || ARCH == ARCH_POWERPC
212         if (n->fpscr < 0x4000)
213             a = (void *) n->fpscr;
214         else
215             a = (void *) (n->fpscr - ((n->fpscr & 7) << 1) + 7);
216 #endif /* ARCH */
217 #endif /* SYSTEM */
218 #endif /* MP_SIGINFO_SUPPORT */
219         __mp_error(ET_ILLMEM, AT_MAX, NULL, 0, "illegal memory access at "
220                    "address " MP_POINTER, a);
221         if (t = __mp_findnode(&h->alloc, a, 1))
222             if (t->info != NULL)
223                 __mp_printalloc(&h->syms, t);
224             else
225             {
226                 __mp_diag("    within free block " MP_POINTER " (", t->block);
227                 __mp_printsize(t->size);
228                 __mp_diag(")\n");
229             }
230         else
231             __mp_diag("    " MP_POINTER " not in heap\n", a);
232     }
233     else
234 #endif /* MP_SIGINFO_SUPPORT && SYSTEM */
235         __mp_error(ET_ILLMEM, AT_MAX, NULL, 0, "illegal memory access");
236     /* Obtain the call stack so that we can tell where the illegal memory
237      * access came from.  This relies on the assumption that the stack area
238      * for the signal handler is the same as the stack area for the main
239      * process.
240      */
241     __mp_newframe(&i, NULL);
242     if (__mp_getframe(&i) && __mp_getframe(&i) && __mp_getframe(&i))
243     {
244         __mp_diag("\n    call stack\n");
245         __mp_printstack(&h->syms, &i);
246     }
247 #elif TARGET == TARGET_WINDOWS
248     /* The exception information provided for access violations allows us to
249      * determine the address of the violation and whether an attempt was made
250      * to read to or write from that address.
251      */
252     a = (void *) r->ExceptionInformation[1];
253     if (r->ExceptionInformation[0])
254         __mp_error(ET_ILLMEM, AT_MAX, NULL, 0, "illegal memory write at "
255                    "address " MP_POINTER, a);
256     else
257         __mp_error(ET_ILLMEM, AT_MAX, NULL, 0, "illegal memory read at "
258                    "address " MP_POINTER, a);
259     if (t = __mp_findnode(&h->alloc, a, 1))
260         if (t->info != NULL)
261             __mp_printalloc(&h->syms, t);
262         else
263         {
264             __mp_diag("    within free block " MP_POINTER " (", t->block);
265             __mp_printsize(t->size);
266             __mp_diag(")\n");
267         }
268     else
269         __mp_diag("    " MP_POINTER " not in heap\n", a);
270     /* Unfortunately, the call stack where the access violation came from
271      * is not linked to the stack frame of this function.  Therefore, we
272      * need to invoke the __mp_newframe() function with the proper frame
273      * pointer and explicitly display the first frame as well.
274      */
275 #if !MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT
276     __mp_newframe(&i, (void *) e->ContextRecord);
277 #else /* MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT */
278     __mp_newframe(&i, (void *) e->ContextRecord->Ebp);
279 #endif /* MP_BUILTINSTACK_SUPPORT && MP_LIBRARYSTACK_SUPPORT */
280     if (__mp_getframe(&i))
281     {
282         a = (void *) e->ContextRecord->Eip;
283         __mp_diag("\n    call stack\n");
284         __mp_diag("\t" MP_POINTER " ", a);
285         __mp_printsymbol(&h->syms, a);
286         __mp_diag("\n");
287         __mp_printstack(&h->syms, &i);
288     }
289 #endif /* TARGET */
290     h->fini = 1;
291     __mp_abort();
292 #if TARGET == TARGET_WINDOWS
293     /* This line of code will not be executed due to the call to __mp_abort()
294      * on the previous line, but exists to suppress compiler warnings.
295      */
296     return EXCEPTION_CONTINUE_SEARCH;
297 #endif /* TARGET */
298 }
299 #endif /* TARGET */
300 
301 
302 /* Initialise a sighead structure.
303  */
304 
305 MP_GLOBAL
306 void
307 __mp_initsignals(sighead *s)
308 {
309 #if TARGET == TARGET_UNIX
310 #if MP_SIGINFO_SUPPORT
311     struct sigaction i;
312 #endif /* MP_SIGINFO_SUPPORT */
313 #endif /* TARGET */
314 
315 #if TARGET == TARGET_UNIX
316 #if MP_SIGINFO_SUPPORT
317     i.sa_flags = SA_SIGINFO;
318     (void *) i.sa_handler = (void *) signalhandler;
319     sigfillset(&i.sa_mask);
320     sigaction(SIGBUS, &i, NULL);
321     sigaction(SIGSEGV, &i, NULL);
322 #if MP_WATCH_SUPPORT
323     sigaction(SIGTRAP, &i, NULL);
324 #endif /* MP_WATCH_SUPPORT */
325 #else /* MP_SIGINFO_SUPPORT */
326     signal(SIGBUS, (handlerfunction) signalhandler);
327     signal(SIGSEGV, (handlerfunction) signalhandler);
328 #if MP_WATCH_SUPPORT
329     signal(SIGTRAP, (handlerfunction) signalhandler);
330 #endif /* MP_WATCH_SUPPORT */
331 #endif /* MP_SIGINFO_SUPPORT */
332 #elif TARGET == TARGET_WINDOWS
333     SetUnhandledExceptionFilter(signalhandler);
334 #endif /* TARGET */
335     s->sigint = s->sigterm = NULL;
336     s->saved = 0;
337 }
338 
339 
340 /* Save the current signal handlers and set them to ignore.
341  */
342 
343 MP_GLOBAL
344 void
345 __mp_savesignals(sighead *s)
346 {
347     s->sigint = signal(SIGINT, SIG_IGN);
348     s->sigterm = signal(SIGTERM, SIG_IGN);
349     s->saved = 1;
350 }
351 
352 
353 /* Restore the previous signal handlers.
354  */
355 
356 MP_GLOBAL
357 void
358 __mp_restoresignals(sighead *s)
359 {
360     if (s->saved)
361     {
362         signal(SIGINT, s->sigint);
363         signal(SIGTERM, s->sigterm);
364         s->saved = 0;
365     }
366 }
367 
368 
369 /* Send the current process an ABORT signal.
370  */
371 
372 MP_GLOBAL
373 void
374 __mp_abort(void)
375 {
376 #if TARGET == TARGET_UNIX || TARGET == TARGET_WINDOWS
377     /* Send the current process an ABORT signal for use in a debugger.
378      * Used on systems where this is supported.
379      */
380     fflush(NULL);
381     abort();
382 #else /* TARGET */
383     /* Terminate the current process with a failure exit code.
384      * Used on systems where memory will not be automatically returned
385      * back to the system on process termination.
386      */
387     exit(EXIT_FAILURE);
388 #endif /* TARGET */
389 }
390 
391 
392 #ifdef __cplusplus
393 }
394 #endif /* __cplusplus */
395