ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sigsegv.cpp
Revision: 1.9
Committed: 2002-03-16T21:36:12Z (22 years, 8 months ago) by gbeauche
Branch: MAIN
Changes since 1.8: +4 -0 lines
Log Message:
- Add fault instruction recovery for Linux/ppc and siginfo_t handlers

File Contents

# Content
1 /*
2 * sigsegv.cpp - SIGSEGV signals support
3 *
4 * Derived from Bruno Haible's work on his SIGSEGV library for clisp
5 * <http://clisp.sourceforge.net/>
6 *
7 * Basilisk II (C) 1997-2002 Christian Bauer
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <signal.h>
33 #include "sigsegv.h"
34
35 // Return value type of a signal handler (standard type if not defined)
36 #ifndef RETSIGTYPE
37 #define RETSIGTYPE void
38 #endif
39
40 // Type of the system signal handler
41 typedef RETSIGTYPE (*signal_handler)(int);
42
43 // User's SIGSEGV handler
44 static sigsegv_handler_t sigsegv_user_handler = 0;
45
46 // Actual SIGSEGV handler installer
47 static bool sigsegv_do_install_handler(int sig);
48
49
50 /*
51 * OS-dependant SIGSEGV signals support section
52 */
53
54 #if HAVE_SIGINFO_T
55 // Generic extended signal handler
56 #if defined(__NetBSD__) || defined(__FreeBSD__)
57 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS)
58 #else
59 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
60 #endif
61 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, siginfo_t *sip, void *scp
62 #define SIGSEGV_FAULT_ADDRESS sip->si_addr
63 #if defined(__linux__)
64 #if (defined(i386) || defined(__i386__))
65 #include <sys/ucontext.h>
66 #define SIGSEGV_FAULT_INSTRUCTION (((ucontext_t *)scp)->uc_mcontext.gregs[14]) /* should use REG_EIP instead */
67 #endif
68 #if (defined(ia64) || defined(__ia64__))
69 #define SIGSEGV_FAULT_INSTRUCTION (((struct sigcontext *)scp)->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */
70 #endif
71 #if (defined(powerpc) || defined(__powerpc__))
72 #include <sys/ucontext.h>
73 #define SIGSEGV_FAULT_INSTRUCTION (((ucontext_t *)scp)->uc_mcontext.regs->nip)
74 #endif
75 #endif
76 #endif
77
78 #if HAVE_SIGCONTEXT_SUBTERFUGE
79 // Linux kernels prior to 2.4 ?
80 #if defined(__linux__)
81 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
82 #if (defined(i386) || defined(__i386__))
83 #include <asm/sigcontext.h>
84 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext scs
85 #define SIGSEGV_FAULT_ADDRESS scs.cr2
86 #define SIGSEGV_FAULT_INSTRUCTION scs.eip
87 #endif
88 #if (defined(sparc) || defined(__sparc__))
89 #include <asm/sigcontext.h>
90 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp, char *addr
91 #define SIGSEGV_FAULT_ADDRESS addr
92 #endif
93 #if (defined(powerpc) || defined(__powerpc__))
94 #include <asm/sigcontext.h>
95 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, struct sigcontext *scp
96 #define SIGSEGV_FAULT_ADDRESS scp->regs->dar
97 #define SIGSEGV_FAULT_INSTRUCTION scp->regs->nip
98 #endif
99 #if (defined(alpha) || defined(__alpha__))
100 #include <asm/sigcontext.h>
101 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
102 #define SIGSEGV_FAULT_ADDRESS get_fault_address(scp)
103 #define SIGSEGV_FAULT_INSTRUCTION scp->sc_pc
104
105 // From Boehm's GC 6.0alpha8
106 static sigsegv_address_t get_fault_address(struct sigcontext *scp)
107 {
108 unsigned int instruction = *((unsigned int *)(scp->sc_pc));
109 unsigned long fault_address = scp->sc_regs[(instruction >> 16) & 0x1f];
110 fault_address += (signed long)(signed short)(instruction & 0xffff);
111 return (sigsegv_address_t)fault_address;
112 }
113 #endif
114 #endif
115
116 // Irix 5 or 6 on MIPS
117 #if (defined(sgi) || defined(__sgi)) && (defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4))
118 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
119 #define SIGSEGV_FAULT_ADDRESS scp->sc_badvaddr
120 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
121 #endif
122
123 // OSF/1 on Alpha
124 #if defined(__osf__)
125 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
126 #define SIGSEGV_FAULT_ADDRESS scp->sc_traparg_a0
127 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
128 #endif
129
130 // AIX
131 #if defined(_AIX)
132 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
133 #define SIGSEGV_FAULT_ADDRESS scp->sc_jmpbuf.jmp_context.o_vaddr
134 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
135 #endif
136
137 // NetBSD or FreeBSD
138 #if defined(__NetBSD__) || defined(__FreeBSD__)
139 #if (defined(m68k) || defined(__m68k__))
140 #include <m68k/frame.h>
141 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
142 #define SIGSEGV_FAULT_ADDRESS ({ \
143 struct sigstate { \
144 int ss_flags; \
145 struct frame ss_frame; \
146 }; \
147 struct sigstate *state = (struct sigstate *)scp->sc_ap; \
148 char *fault_addr; \
149 switch (state->ss_frame.f_format) { \
150 case 7: /* 68040 access error */ \
151 /* "code" is sometimes unreliable (i.e. contains NULL or a bogus address), reason unknown */ \
152 fault_addr = state->ss_frame.f_fmt7.f_fa; \
153 break; \
154 default: \
155 fault_addr = (char *)code; \
156 break; \
157 } \
158 fault_addr; \
159 })
160 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGSEGV)
161 #else
162 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, void *scp, char *addr
163 #define SIGSEGV_FAULT_ADDRESS addr
164 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS)
165 #endif
166 #endif
167
168 // MacOS X
169 #if defined(__APPLE__) && defined(__MACH__)
170 #if (defined(ppc) || defined(__ppc__))
171 #define SIGSEGV_FAULT_HANDLER_ARGLIST int sig, int code, struct sigcontext *scp
172 #define SIGSEGV_FAULT_ADDRESS get_fault_address(scp)
173 #define SIGSEGV_FAULT_INSTRUCTION scp->sc_ir
174 #define SIGSEGV_ALL_SIGNALS FAULT_HANDLER(SIGBUS)
175
176 // From Boehm's GC 6.0alpha8
177 #define EXTRACT_OP1(iw) (((iw) & 0xFC000000) >> 26)
178 #define EXTRACT_OP2(iw) (((iw) & 0x000007FE) >> 1)
179 #define EXTRACT_REGA(iw) (((iw) & 0x001F0000) >> 16)
180 #define EXTRACT_REGB(iw) (((iw) & 0x03E00000) >> 21)
181 #define EXTRACT_REGC(iw) (((iw) & 0x0000F800) >> 11)
182 #define EXTRACT_DISP(iw) ((short *) &(iw))[1]
183
184 static sigsegv_address_t get_fault_address(struct sigcontext *scp)
185 {
186 unsigned int instr = *((unsigned int *) scp->sc_ir);
187 unsigned int * regs = &((unsigned int *) scp->sc_regs)[2];
188 int disp = 0, tmp;
189 unsigned int baseA = 0, baseB = 0;
190 unsigned int addr, alignmask = 0xFFFFFFFF;
191
192 switch(EXTRACT_OP1(instr)) {
193 case 38: /* stb */
194 case 39: /* stbu */
195 case 54: /* stfd */
196 case 55: /* stfdu */
197 case 52: /* stfs */
198 case 53: /* stfsu */
199 case 44: /* sth */
200 case 45: /* sthu */
201 case 47: /* stmw */
202 case 36: /* stw */
203 case 37: /* stwu */
204 tmp = EXTRACT_REGA(instr);
205 if(tmp > 0)
206 baseA = regs[tmp];
207 disp = EXTRACT_DISP(instr);
208 break;
209 case 31:
210 switch(EXTRACT_OP2(instr)) {
211 case 86: /* dcbf */
212 case 54: /* dcbst */
213 case 1014: /* dcbz */
214 case 247: /* stbux */
215 case 215: /* stbx */
216 case 759: /* stfdux */
217 case 727: /* stfdx */
218 case 983: /* stfiwx */
219 case 695: /* stfsux */
220 case 663: /* stfsx */
221 case 918: /* sthbrx */
222 case 439: /* sthux */
223 case 407: /* sthx */
224 case 661: /* stswx */
225 case 662: /* stwbrx */
226 case 150: /* stwcx. */
227 case 183: /* stwux */
228 case 151: /* stwx */
229 case 135: /* stvebx */
230 case 167: /* stvehx */
231 case 199: /* stvewx */
232 case 231: /* stvx */
233 case 487: /* stvxl */
234 tmp = EXTRACT_REGA(instr);
235 if(tmp > 0)
236 baseA = regs[tmp];
237 baseB = regs[EXTRACT_REGC(instr)];
238 /* determine Altivec alignment mask */
239 switch(EXTRACT_OP2(instr)) {
240 case 167: /* stvehx */
241 alignmask = 0xFFFFFFFE;
242 break;
243 case 199: /* stvewx */
244 alignmask = 0xFFFFFFFC;
245 break;
246 case 231: /* stvx */
247 alignmask = 0xFFFFFFF0;
248 break;
249 case 487: /* stvxl */
250 alignmask = 0xFFFFFFF0;
251 break;
252 }
253 break;
254 case 725: /* stswi */
255 tmp = EXTRACT_REGA(instr);
256 if(tmp > 0)
257 baseA = regs[tmp];
258 break;
259 default: /* ignore instruction */
260 return 0;
261 break;
262 }
263 break;
264 default: /* ignore instruction */
265 return 0;
266 break;
267 }
268
269 addr = (baseA + baseB) + disp;
270 addr &= alignmask;
271 return (sigsegv_address_t)addr;
272 }
273 #endif
274 #endif
275 #endif
276
277 // Fallbacks
278 #ifndef SIGSEGV_FAULT_INSTRUCTION
279 #define SIGSEGV_FAULT_INSTRUCTION SIGSEGV_INVALID_PC
280 #endif
281
282 // SIGSEGV recovery supported ?
283 #if defined(SIGSEGV_ALL_SIGNALS) && defined(SIGSEGV_FAULT_HANDLER_ARGLIST) && defined(SIGSEGV_FAULT_ADDRESS)
284 #define HAVE_SIGSEGV_RECOVERY
285 #endif
286
287
288 /*
289 * SIGSEGV global handler
290 */
291
292 #ifdef HAVE_SIGSEGV_RECOVERY
293 static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST)
294 {
295 // Call user's handler and reinstall the global handler, if required
296 if (sigsegv_user_handler((sigsegv_address_t)SIGSEGV_FAULT_ADDRESS, (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION)) {
297 #if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL))
298 sigsegv_do_install_handler(sig);
299 #endif
300 }
301 else {
302 // FAIL: reinstall default handler for "safe" crash
303 #define FAULT_HANDLER(sig) signal(sig, SIG_DFL);
304 SIGSEGV_ALL_SIGNALS
305 #undef FAULT_HANDLER
306 }
307 }
308 #endif
309
310
311 /*
312 * SIGSEGV handler initialization
313 */
314
315 #if defined(HAVE_SIGINFO_T)
316 static bool sigsegv_do_install_handler(int sig)
317 {
318 // Setup SIGSEGV handler to process writes to frame buffer
319 #ifdef HAVE_SIGACTION
320 struct sigaction vosf_sa;
321 sigemptyset(&vosf_sa.sa_mask);
322 vosf_sa.sa_sigaction = sigsegv_handler;
323 vosf_sa.sa_flags = SA_SIGINFO;
324 return (sigaction(sig, &vosf_sa, 0) == 0);
325 #else
326 return (signal(sig, (signal_handler)sigsegv_handler) != SIG_ERR);
327 #endif
328 }
329 #endif
330
331 #if defined(HAVE_SIGCONTEXT_SUBTERFUGE)
332 static bool sigsegv_do_install_handler(int sig)
333 {
334 // Setup SIGSEGV handler to process writes to frame buffer
335 #ifdef HAVE_SIGACTION
336 struct sigaction vosf_sa;
337 sigemptyset(&vosf_sa.sa_mask);
338 vosf_sa.sa_handler = (signal_handler)sigsegv_handler;
339 #if !EMULATED_68K && defined(__NetBSD__)
340 sigaddset(&vosf_sa.sa_mask, SIGALRM);
341 vosf_sa.sa_flags = SA_ONSTACK;
342 #else
343 vosf_sa.sa_flags = 0;
344 #endif
345 return (sigaction(sig, &vosf_sa, 0) == 0);
346 #else
347 return (signal(sig, (signal_handler)sigsegv_handler) != SIG_ERR);
348 #endif
349 }
350 #endif
351
352 bool sigsegv_install_handler(sigsegv_handler_t handler)
353 {
354 #ifdef HAVE_SIGSEGV_RECOVERY
355 sigsegv_user_handler = handler;
356 bool success = true;
357 #define FAULT_HANDLER(sig) success = success && sigsegv_do_install_handler(sig);
358 SIGSEGV_ALL_SIGNALS
359 #undef FAULT_HANDLER
360 return success;
361 #else
362 // FAIL: no siginfo_t nor sigcontext subterfuge is available
363 return false;
364 #endif
365 }
366
367
368 /*
369 * SIGSEGV handler deinitialization
370 */
371
372 void sigsegv_deinstall_handler(void)
373 {
374 #ifdef HAVE_SIGSEGV_RECOVERY
375 sigsegv_user_handler = 0;
376 #define FAULT_HANDLER(sig) signal(sig, SIG_DFL);
377 SIGSEGV_ALL_SIGNALS
378 #undef FAULT_HANDLER
379 #endif
380 }
381
382 /*
383 * Test program used for configure/test
384 */
385
386 #ifdef CONFIGURE_TEST_SIGSEGV_RECOVERY
387 #include <stdio.h>
388 #include <stdlib.h>
389 #include <fcntl.h>
390 #include <sys/mman.h>
391 #include "vm_alloc.h"
392
393 static int page_size;
394 static volatile char * page = 0;
395 static volatile int handler_called = 0;
396
397 static bool sigsegv_test_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
398 {
399 handler_called++;
400 if ((fault_address - 123) != page)
401 exit(1);
402 if (vm_protect((char *)((unsigned long)fault_address & -page_size), page_size, VM_PAGE_READ | VM_PAGE_WRITE) != 0)
403 exit(1);
404 return true;
405 }
406
407 int main(void)
408 {
409 if (vm_init() < 0)
410 return 1;
411
412 page_size = getpagesize();
413 if ((page = (char *)vm_acquire(page_size)) == VM_MAP_FAILED)
414 return 1;
415
416 if (vm_protect((char *)page, page_size, VM_PAGE_READ) < 0)
417 return 1;
418
419 if (!sigsegv_install_handler(sigsegv_test_handler))
420 return 1;
421
422 page[123] = 45;
423 page[123] = 45;
424
425 if (handler_called != 1)
426 return 1;
427
428 vm_exit();
429 return 0;
430 }
431 #endif