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