ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/sigsegv.cpp
(Generate patch)

Comparing BasiliskII/src/Unix/sigsegv.cpp (file contents):
Revision 1.9 by gbeauche, 2002-03-16T21:36:12Z vs.
Revision 1.10 by gbeauche, 2002-05-12T11:10:50Z

# Line 40 | Line 40
40   // Type of the system signal handler
41   typedef RETSIGTYPE (*signal_handler)(int);
42  
43 + // Is the fault to be ignored?
44 + static bool sigsegv_ignore_fault = false;
45 +
46   // User's SIGSEGV handler
47   static sigsegv_handler_t sigsegv_user_handler = 0;
48  
49 + // Function called to dump state if we can't handle the fault
50 + static sigsegv_handler_t sigsegv_dump_state = 0;
51 +
52   // Actual SIGSEGV handler installer
53   static bool sigsegv_do_install_handler(int sig);
54  
# Line 64 | Line 70 | static bool sigsegv_do_install_handler(i
70   #if (defined(i386) || defined(__i386__))
71   #include <sys/ucontext.h>
72   #define SIGSEGV_FAULT_INSTRUCTION               (((ucontext_t *)scp)->uc_mcontext.gregs[14]) /* should use REG_EIP instead */
73 + #define SIGSEGV_REGISTER_FILE                   (unsigned long *)(((ucontext_t *)scp)->uc_mcontext.gregs)
74 + #define SIGSEGV_SKIP_INSTRUCTION                ix86_skip_instruction
75   #endif
76   #if (defined(ia64) || defined(__ia64__))
77   #define SIGSEGV_FAULT_INSTRUCTION               (((struct sigcontext *)scp)->sc_ip & ~0x3ULL) /* slot number is in bits 0 and 1 */
# Line 84 | Line 92 | static bool sigsegv_do_install_handler(i
92   #define SIGSEGV_FAULT_HANDLER_ARGLIST   int sig, struct sigcontext scs
93   #define SIGSEGV_FAULT_ADDRESS                   scs.cr2
94   #define SIGSEGV_FAULT_INSTRUCTION               scs.eip
95 + #define SIGSEGV_REGISTER_FILE                   (unsigned long *)(&scs)
96 + #define SIGSEGV_SKIP_INSTRUCTION                ix86_skip_instruction
97   #endif
98   #if (defined(sparc) || defined(__sparc__))
99   #include <asm/sigcontext.h>
# Line 274 | Line 284 | static sigsegv_address_t get_fault_addre
284   #endif
285   #endif
286  
287 + #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
288 + // Decode and skip X86 instruction
289 + #if (defined(i386) || defined(__i386__))
290 + #if defined(__linux__)
291 + enum {
292 +        X86_REG_EIP = 14,
293 +        X86_REG_EAX = 11,
294 +        X86_REG_ECX = 10,
295 +        X86_REG_EDX = 9,
296 +        X86_REG_EBX = 8,
297 +        X86_REG_ESP = 7,
298 +        X86_REG_EBP = 6,
299 +        X86_REG_ESI = 5,
300 +        X86_REG_EDI = 4
301 + };
302 + #endif
303 + // FIXME: this is partly redundant with the instruction decoding phase
304 + // to discover transfer type and register number
305 + static inline int ix86_step_over_modrm(unsigned char * p)
306 + {
307 +        int mod = (p[0] >> 6) & 3;
308 +        int rm = p[0] & 7;
309 +        int offset = 0;
310 +
311 +        // ModR/M Byte
312 +        switch (mod) {
313 +        case 0: // [reg]
314 +                if (rm == 5) return 4; // disp32
315 +                break;
316 +        case 1: // disp8[reg]
317 +                offset = 1;
318 +                break;
319 +        case 2: // disp32[reg]
320 +                offset = 4;
321 +                break;
322 +        case 3: // register
323 +                return 0;
324 +        }
325 +        
326 +        // SIB Byte
327 +        if (rm == 4) {
328 +                if (mod == 0 && (p[1] & 7) == 5)
329 +                        offset = 5; // disp32[index]
330 +                else
331 +                        offset++;
332 +        }
333 +
334 +        return offset;
335 + }
336 +
337 + static bool ix86_skip_instruction(sigsegv_address_t fault_instruction, unsigned long * regs)
338 + {
339 +        unsigned char * eip = (unsigned char *)fault_instruction;
340 +
341 +        if (eip == 0)
342 +                return false;
343 +        
344 +        // Transfer type
345 +        enum {
346 +                TYPE_UNKNOWN,
347 +                TYPE_LOAD,
348 +                TYPE_STORE
349 +        } transfer_type = TYPE_UNKNOWN;
350 +        
351 +        // Transfer size
352 +        enum {
353 +                SIZE_BYTE,
354 +                SIZE_WORD,
355 +                SIZE_LONG
356 +        } transfer_size = SIZE_LONG;
357 +        
358 +        int reg = -1;
359 +        int len = 0;
360 +        
361 +        // Operand size prefix
362 +        if (*eip == 0x66) {
363 +                eip++;
364 +                len++;
365 +                transfer_size = SIZE_WORD;
366 +        }
367 +
368 +        // Decode instruction
369 +        switch (eip[0]) {
370 +        case 0x8a: // MOV r8, r/m8
371 +                transfer_size = SIZE_BYTE;
372 +        case 0x8b: // MOV r32, r/m32 (or 16-bit operation)
373 +                switch (eip[1] & 0xc0) {
374 +                case 0x80:
375 +                        reg = (eip[1] >> 3) & 7;
376 +                        transfer_type = TYPE_LOAD;
377 +                        break;
378 +                case 0x40:
379 +                        reg = (eip[1] >> 3) & 7;
380 +                        transfer_type = TYPE_LOAD;
381 +                        break;
382 +                case 0x00:
383 +                        reg = (eip[1] >> 3) & 7;
384 +                        transfer_type = TYPE_LOAD;
385 +                        break;
386 +                }
387 +                len += 2 + ix86_step_over_modrm(eip + 1);
388 +                break;
389 +        case 0x88: // MOV r/m8, r8
390 +                transfer_size = SIZE_BYTE;
391 +        case 0x89: // MOV r/m32, r32 (or 16-bit operation)
392 +                switch (eip[1] & 0xc0) {
393 +                case 0x80:
394 +                        reg = (eip[1] >> 3) & 7;
395 +                        transfer_type = TYPE_STORE;
396 +                        break;
397 +                case 0x40:
398 +                        reg = (eip[1] >> 3) & 7;
399 +                        transfer_type = TYPE_STORE;
400 +                        break;
401 +                case 0x00:
402 +                        reg = (eip[1] >> 3) & 7;
403 +                        transfer_type = TYPE_STORE;
404 +                        break;
405 +                }
406 +                len += 2 + ix86_step_over_modrm(eip + 1);
407 +                break;
408 +        }
409 +
410 +        if (transfer_type == TYPE_UNKNOWN) {
411 +                // Unknown machine code, let it crash. Then patch the decoder
412 +                return false;
413 +        }
414 +
415 +        if (transfer_type == TYPE_LOAD && reg != -1) {
416 +                static const int x86_reg_map[8] = {
417 +                        X86_REG_EAX, X86_REG_ECX, X86_REG_EDX, X86_REG_EBX,
418 +                        X86_REG_ESP, X86_REG_EBP, X86_REG_ESI, X86_REG_EDI
419 +                };
420 +                
421 +                if (reg < 0 || reg >= 8)
422 +                        return false;
423 +
424 +                int rloc = x86_reg_map[reg];
425 +                switch (transfer_size) {
426 +                case SIZE_BYTE:
427 +                        regs[rloc] = (regs[rloc] & ~0xff);
428 +                        break;
429 +                case SIZE_WORD:
430 +                        regs[rloc] = (regs[rloc] & ~0xffff);
431 +                        break;
432 +                case SIZE_LONG:
433 +                        regs[rloc] = 0;
434 +                        break;
435 +                }
436 +        }
437 +
438 + #if DEBUG
439 +        printf("%08x: %s %s access", regs[X86_REG_EIP],
440 +                   transfer_size == SIZE_BYTE ? "byte" : transfer_size == SIZE_WORD ? "word" : "long",
441 +                   transfer_type == TYPE_LOAD ? "read" : "write");
442 +        
443 +        if (reg != -1) {
444 +                static const char * x86_reg_str_map[8] = {
445 +                        "eax", "ecx", "edx", "ebx",
446 +                        "esp", "ebp", "esi", "edi"
447 +                };
448 +                printf(" %s register %%%s", transfer_type == TYPE_LOAD ? "to" : "from", x86_reg_str_map[reg]);
449 +        }
450 +        printf(", %d bytes instruction\n", len);
451 + #endif
452 +        
453 +        regs[X86_REG_EIP] += len;
454 +        return true;
455 + }
456 + #endif
457 + #endif
458 +
459   // Fallbacks
460   #ifndef SIGSEGV_FAULT_INSTRUCTION
461   #define SIGSEGV_FAULT_INSTRUCTION               SIGSEGV_INVALID_PC
# Line 292 | Line 474 | static sigsegv_address_t get_fault_addre
474   #ifdef HAVE_SIGSEGV_RECOVERY
475   static void sigsegv_handler(SIGSEGV_FAULT_HANDLER_ARGLIST)
476   {
477 +        sigsegv_address_t fault_address = (sigsegv_address_t)SIGSEGV_FAULT_ADDRESS;
478 +        sigsegv_address_t fault_instruction = (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION;
479 +        bool fault_recovered = false;
480 +        
481          // Call user's handler and reinstall the global handler, if required
482 <        if (sigsegv_user_handler((sigsegv_address_t)SIGSEGV_FAULT_ADDRESS, (sigsegv_address_t)SIGSEGV_FAULT_INSTRUCTION)) {
482 >        if (sigsegv_user_handler(fault_address, fault_instruction)) {
483   #if (defined(HAVE_SIGACTION) ? defined(SIGACTION_NEED_REINSTALL) : defined(SIGNAL_NEED_REINSTALL))
484                  sigsegv_do_install_handler(sig);
485   #endif
486 +                fault_recovered = true;
487          }
488 <        else {
488 > #if HAVE_SIGSEGV_SKIP_INSTRUCTION
489 >        else if (sigsegv_ignore_fault) {
490 >                // Call the instruction skipper with the register file available
491 >                if (SIGSEGV_SKIP_INSTRUCTION(fault_instruction, SIGSEGV_REGISTER_FILE))
492 >                        fault_recovered = true;
493 >        }
494 > #endif
495 >
496 >        if (!fault_recovered) {
497                  // FAIL: reinstall default handler for "safe" crash
498   #define FAULT_HANDLER(sig) signal(sig, SIG_DFL);
499                  SIGSEGV_ALL_SIGNALS
500   #undef FAULT_HANDLER
501 +                
502 +                // We can't do anything with the fault_address, dump state?
503 +                if (sigsegv_dump_state != 0)
504 +                        sigsegv_dump_state(fault_address, fault_instruction);
505          }
506   }
507   #endif
# Line 379 | Line 578 | void sigsegv_deinstall_handler(void)
578   #endif
579   }
580  
581 +
582 + /*
583 + *  SIGSEGV ignore state modifier
584 + */
585 +
586 + void sigsegv_set_ignore_state(bool ignore_fault)
587 + {
588 +        sigsegv_ignore_fault = ignore_fault;
589 + }
590 +
591 +
592 + /*
593 + *  Set callback function when we cannot handle the fault
594 + */
595 +
596 + void sigsegv_set_dump_state(sigsegv_handler_t handler)
597 + {
598 +        sigsegv_dump_state = handler;
599 + }
600 +
601 +
602   /*
603   *  Test program used for configure/test
604   */
# Line 404 | Line 624 | static bool sigsegv_test_handler(sigsegv
624          return true;
625   }
626  
627 + #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
628 + static bool sigsegv_insn_handler(sigsegv_address_t fault_address, sigsegv_address_t instruction_address)
629 + {
630 +        return false;
631 + }
632 + #endif
633 +
634   int main(void)
635   {
636          if (vm_init() < 0)
# Line 425 | Line 652 | int main(void)
652          if (handler_called != 1)
653                  return 1;
654  
655 + #ifdef HAVE_SIGSEGV_SKIP_INSTRUCTION
656 +        if (!sigsegv_install_handler(sigsegv_insn_handler))
657 +                return 1;
658 +        
659 +        if (vm_protect((char *)page, page_size, VM_PAGE_WRITE) < 0)
660 +                return 1;
661 +        
662 +        for (int i = 0; i < page_size; i++)
663 +                page[i] = (i + 1) % page_size;
664 +        
665 +        if (vm_protect((char *)page, page_size, VM_PAGE_NOACCESS) < 0)
666 +                return 1;
667 +        
668 +        sigsegv_set_ignore_state(true);
669 +
670 + #define TEST_SKIP_INSTRUCTION(TYPE) do {                                \
671 +                const unsigned int TAG = 0x12345678;                    \
672 +                TYPE data = *((TYPE *)(page + sizeof(TYPE)));   \
673 +                volatile unsigned int effect = data + TAG;              \
674 +                if (effect != TAG)                                                              \
675 +                        return 1;                                                                       \
676 +        } while (0)
677 +        
678 +        TEST_SKIP_INSTRUCTION(unsigned char);
679 +        TEST_SKIP_INSTRUCTION(unsigned short);
680 +        TEST_SKIP_INSTRUCTION(unsigned int);
681 + #endif
682 +
683          vm_exit();
684          return 0;
685   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines