/* Copyright (C) 1994, 1995 Charles Sandmann (sandmann@clio.rice.edu) * Copyright (C) 1996-1998 Ilya Ryzhenkov (orangy@inetlab.com) * Exception handling and basis for signal support for DLM * This software may be freely distributed, no warranty. */ /* Update for use with DLMs (C) 1996 Ilya P. Ryzhenkov */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For DS base/limit info */ #include #define err(x) _write(STDERR_FILENO, x, sizeof(x)-1) extern unsigned end __asm__ ("end"); static unsigned char old_video_mode = 3; static int cbrk_vect = 0x1b; /* May be 0x06 for PC98 */ extern unsigned _stklen; extern unsigned __djgpp_stack_limit; /* These are all defined in exceptn.S and only used here */ extern int __djgpp_exception_table; extern int __djgpp_npx_hdlr; extern int __djgpp_kbd_hdlr; extern int __djgpp_kbd_hdlr_pc98; extern int __djgpp_iret, __djgpp_i24; extern void __djgpp_cbrk_hdlr(void); extern int __djgpp_hw_lock_start, __djgpp_hw_lock_end; extern __dpmi_paddr __djgpp_old_kbd; extern unsigned djgpp_exception_stack __asm__("exception_stack"); static void itox(int v, int len) { static char buf[9]; /* startup default to all NULLs */ char *bp = buf+7; while (len) { int d = v & 15; v >>= 4; *bp = d + '0'; if (d > 9) *bp += 'a' - '9' - 1; bp--; len--; } _write(STDERR_FILENO, bp+1, 7-(bp-buf)); } static int except_to_sig(int excep) { switch(excep) { case 5: case 8: case 9: case 11: case 12: case 13: case 14: return SIGSEGV; case 0: case 4: case 16: return SIGFPE; case 1: case 3: return SIGTRAP; case 7: return SIGNOFP; default: if(excep == 0x75) /* HW int to fake exception values hardcoded in exceptn.S */ return SIGFPE; else if(excep == 0x78) return SIGTIMR; else if(excep == 0x79 || excep == 0x1b) return SIGINT; else if(excep == 0x7a) return SIGQUIT; else return SIGILL; } } void show_call_frame(void); static const char *exception_names[] = { "Division by Zero", "Debug", "NMI", "Breakpoint", "Overflow", "Bounds Check", "Invalid Opcode", "Coprocessor not available", "Double Fault", "Coprocessor overrun", "Invalid TSS", "Segment Not Present", "Stack Fault", "General Protection Fault", "Page fault", 0, "Coprocessor Error", "Alignment Check" }; static char has_error[] = {0,0,0,0,0,0,0,0 ,1,0,1,1,1,1,1,0 ,0,1 }; #define EXCEPTION_COUNT (sizeof(exception_names)/sizeof(exception_names[0])) static void dump_selector(const char *name, int sel) { unsigned long base; unsigned limit; write(STDERR_FILENO, name, 2); err(": sel="); itox(sel, 4); if (sel) { if (__dpmi_get_segment_base_address(sel, &base)) { err(" invalid"); } else { err(" base="); itox(base, 8); limit = __dpmi_get_segment_limit(sel); err(" limit="); itox(limit, 8); } } err("\r\n"); } static void do_faulting_finish_message(int fake_exception) { const char *en; unsigned long signum = __djgpp_exception_state->__signum; unsigned excpt_stack_addr = (unsigned)&djgpp_exception_stack; int i; /* check video mode for original here and reset (not if PC98) */ if(ScreenPrimary != 0xa0000 && _farpeekb(_dos_ds, 0x449) != old_video_mode) { asm("pusha;movzbl _old_video_mode,%eax; int $0x10;popa;nop"); } en = (signum >= EXCEPTION_COUNT) ? 0 : exception_names[signum]; if (signum == 0x75) en = "Floating Point exception"; else if (signum == 0x1b) en = "Control-Break Pressed"; else if (signum == 0x79) en = "INTR key Pressed"; else if (signum == 0x7a) en = "QUIT key Pressed"; if (en == 0) { if (fake_exception) err("Raised"); else { err("Exception "); itox(signum, 2); } } else _write(STDERR_FILENO, en, strlen(en)); err(" at eip="); /* For fake exceptions like SIGABRT report where `raise' was called. */ if (fake_exception && __djgpp_exception_state->__cs == _my_cs() && (unsigned)__djgpp_exception_state->__ebp >= __djgpp_exception_state->__esp && (unsigned *)__djgpp_exception_state->__ebp >= &end && (unsigned *)__djgpp_exception_state->__ebp < (unsigned *)__djgpp_selector_limit) itox(*((unsigned *)__djgpp_exception_state->__ebp + 1), 8); else itox(__djgpp_exception_state->__eip, 8); if (signum == 0x79) { err("\r\n"); exit(-1); /* note: `exit', not `_exit' as for the rest of signals! */ } if (signum <= EXCEPTION_COUNT && has_error[signum]) { unsigned int errorcode = __djgpp_exception_state->__sigmask & 0xffff; if(errorcode) { err(", error="); itox(errorcode, 4); } } if (except_to_sig(signum) == SIGFPE) { err(", x87 status="); itox(_status87(), 4); } err("\r\neax="); itox(__djgpp_exception_state->__eax, 8); err(" ebx="); itox(__djgpp_exception_state->__ebx, 8); err(" ecx="); itox(__djgpp_exception_state->__ecx, 8); err(" edx="); itox(__djgpp_exception_state->__edx, 8); err(" esi="); itox(__djgpp_exception_state->__esi, 8); err(" edi="); itox(__djgpp_exception_state->__edi, 8); err("\r\nebp="); itox(__djgpp_exception_state->__ebp, 8); err(" esp="); itox(__djgpp_exception_state->__esp, 8); err(" program="); for (i=0; __dos_argv0[i]; i++); write(STDERR_FILENO, __dos_argv0, i); err("\r\n"); dump_selector("cs", __djgpp_exception_state->__cs); dump_selector("ds", __djgpp_exception_state->__ds); dump_selector("es", __djgpp_exception_state->__es); dump_selector("fs", __djgpp_exception_state->__fs); dump_selector("gs", __djgpp_exception_state->__gs); dump_selector("ss", __djgpp_exception_state->__ss); err("App stack: ["); itox(__djgpp_stack_limit+_stklen, 8); err(".."); itox(__djgpp_stack_limit, 8); err("] Exceptn stack: ["); itox(excpt_stack_addr+8000, 8); err(".."); itox(excpt_stack_addr, 8); err("]\r\n"); err("\r\n"); if (__djgpp_exception_state->__cs == _my_cs()) show_call_frame(); _exit(-1); } typedef void (*SignalHandler)(int); static SignalHandler signal_list[SIGMAX]; /* SIG_DFL = 0 */ SignalHandler signal(int sig, SignalHandler func) { SignalHandler temp; if(sig <= 0 || sig > SIGMAX || sig == SIGKILL) { errno = EINVAL; return SIG_ERR; } temp = signal_list[sig - 1]; signal_list[sig - 1] = func; return temp; } static const char signames[] = "ABRTFPE ILL SEGVTERMALRMHUP INT KILLPIPEQUITUSR1USR2NOFPTRAP"; void __djgpp_traceback_exit(int); void __djgpp_traceback_exit(int sig) { jmp_buf fake_exception; if (sig >= SIGABRT && sig <= SIGTRAP) { if (!__djgpp_exception_state_ptr) { /* This is a software signal, like SIGABRT or SIGKILL. Fill the exception structure, so we get the traceback. */ __djgpp_exception_state_ptr = &fake_exception; if (setjmp(__djgpp_exception_state)) { err("Bad longjmp to __djgpp_exception_state--aborting\r\n"); do_faulting_finish_message(0); /* does not return */ } else /* Fake the exception number. 7Ah is the last one hardwired inside exceptn.S, for SIGQUIT. */ __djgpp_exception_state->__signum = 0x7a + 1 + sig - SIGABRT; } err("Exiting due to signal SIG"); _write(STDERR_FILENO, signames+(sig-SIGABRT)*4, 4); } else { err("Exiting due to signal 0x"); itox(sig, 4); } err("\r\n"); if(__djgpp_exception_state_ptr) /* This exits, does not return. */ do_faulting_finish_message(__djgpp_exception_state == fake_exception); _exit(-1); } int raise(int sig) { SignalHandler temp; if(sig <= 0) return -1; if(sig > SIGMAX) return -1; temp = signal_list[sig - 1]; if(temp == (SignalHandler)SIG_IGN || (sig == SIGQUIT && temp == (SignalHandler)SIG_DFL)) return 0; /* Ignore it */ if(temp == (SignalHandler)SIG_DFL) __djgpp_traceback_exit(sig); /* this does not return */ else if((unsigned)temp < 4096 || temp > (SignalHandler)&end) { err("Bad signal handler, "); __djgpp_traceback_exit(sig); /* does not return */ } else temp(sig); return 0; } /* This routine must call exit() or jump changing stacks. This routine is the basis for traceback generation, core creation, signal handling. */ void __djgpp_exception_processor(void) { int sig; sig = except_to_sig(__djgpp_exception_state->__signum); raise(sig); if(__djgpp_exception_state->__signum >= EXCEPTION_COUNT) /* Not exception so continue OK */ longjmp(__djgpp_exception_state, __djgpp_exception_state->__eax); /* User handler did not exit or longjmp, we must exit */ err("Cannot continue from exception, exiting due to signal "); itox(sig, 4); err("\r\n"); //do_faulting_finish_message(0); } static __dpmi_paddr except_ori[EXCEPTION_COUNT]; static __dpmi_paddr kbd_ori; static __dpmi_paddr npx_ori; static __dpmi_raddr cbrk_ori,cbrk_rmcb; static char cbrk_hooked = 0; static __dpmi_regs cbrk_regs; /* Routine toggles ALL the exceptions. Used around system calls, at exit. */ void __djgpp_exception_toggle(void) { __dpmi_paddr except; int i; for(i=0; i < EXCEPTION_COUNT; i++) { __dpmi_get_processor_exception_handler_vector(i, &except); if(i != 2 || (_crt0_startup_flags & _CRT0_FLAG_NMI_SIGNAL)) __dpmi_set_processor_exception_handler_vector(i, &except_ori[i]); except_ori[i] = except; } __dpmi_get_protected_mode_interrupt_vector(0x75, &except); __dpmi_set_protected_mode_interrupt_vector(0x75, &npx_ori); npx_ori = except; __dpmi_get_protected_mode_interrupt_vector(9, &except); __dpmi_set_protected_mode_interrupt_vector(9, &kbd_ori); kbd_ori = except; if(cbrk_hooked) { __dpmi_set_real_mode_interrupt_vector(cbrk_vect, &cbrk_ori); __dpmi_free_real_mode_callback(&cbrk_rmcb); cbrk_hooked = 0; } else { __dpmi_get_real_mode_interrupt_vector(cbrk_vect, &cbrk_ori); __dpmi_allocate_real_mode_callback(__djgpp_cbrk_hdlr, &cbrk_regs, &cbrk_rmcb); __dpmi_set_real_mode_interrupt_vector(cbrk_vect, &cbrk_rmcb); cbrk_hooked = 1; } } #define RSHIFT 1 #define LSHIFT 2 #define CTRL 4 #define ALT 8 #define SHIFT (RSHIFT | LSHIFT) #define DEFAULT_SIGINT 0x042e /* Ctrl-C: scan code 2Eh, kb status 04h */ #define DEFAULT_SIGQUIT 0x042b /* Ctrl-\: scan code 2Bh, kb status 04h */ /* Make it so the key NEW_KEY will generate the signal SIG. NEW_KEY must include the keyboard status byte in bits 8-15 and the scan code in bits 0-7. */ static int set_signal_key(int sig, int new_key) { int old_key; unsigned short *mask; unsigned short *key; unsigned short kb_status; if (sig == SIGINT) { mask = &__djgpp_sigint_mask; key = &__djgpp_sigint_key; } else if (sig == SIGQUIT) { mask = &__djgpp_sigquit_mask; key = &__djgpp_sigquit_key; } else return -1; old_key = *key; *key = new_key & 0xffff; kb_status = *key >> 8; *mask = 0x000f; /* Alt, Ctrl and Shift bits only */ /* Mask off the RShift bit unless they explicitly asked for it. Our keyboard handler pretends that LShift is pressed when they press RShift. */ if ((kb_status & RSHIFT) == 0) *mask &= ~RSHIFT; /* Mask off the LShift bit if any of the Ctrl or Alt are set, since Shift doesn't matter when Ctrl and/or Alt are pressed. */ if (kb_status & (CTRL | ALT)) *mask &= ~LSHIFT; return old_key; } int __djgpp_set_sigint_key(int new_key) { return set_signal_key(SIGINT, new_key); } int __djgpp_set_sigquit_key(int new_key) { return set_signal_key(SIGQUIT, new_key); } void __djgpp_exception_setup(void) { __dpmi_paddr except; __dpmi_meminfo lockmem; int i; __djgpp_set_sigint_key(DEFAULT_SIGINT); __djgpp_set_sigquit_key(DEFAULT_SIGQUIT); for (i = 0; i < SIGMAX; i++) signal_list[i] = (SignalHandler)SIG_DFL; /* app_DS only used when converting HW interrupts to exceptions */ asm("mov %ds,___djgpp_app_DS"); asm("mov %ds,___djgpp_our_DS"); __djgpp_dos_sel = _dos_ds; /* lock addresses which may see HW interrupts */ lockmem.address = __djgpp_base_address + (unsigned) &__djgpp_hw_lock_start; lockmem.size = ((unsigned) &__djgpp_hw_lock_end - (unsigned) &__djgpp_hw_lock_start); __dpmi_lock_linear_region(&lockmem); asm("mov %%cs,%0" : "=g" (except.selector) ); except.offset32 = (unsigned) &__djgpp_exception_table; for(i=0; i < EXCEPTION_COUNT; i++) { except_ori[i] = except; /* New value to set */ except.offset32 += 4; /* This is the size of push n, jmp */ } kbd_ori.selector = npx_ori.selector = except.selector; npx_ori.offset32 = (unsigned) &__djgpp_npx_hdlr; if(ScreenPrimary != 0xa0000) kbd_ori.offset32 = (unsigned) &__djgpp_kbd_hdlr; else { kbd_ori.offset32 = (unsigned) &__djgpp_kbd_hdlr_pc98; cbrk_vect = 0x06; except.offset32 = (unsigned) &__djgpp_iret; /* TDPMI98 bug */ __dpmi_set_protected_mode_interrupt_vector(0x23, &except); } except.offset32 = (unsigned) &__djgpp_i24; __dpmi_set_protected_mode_interrupt_vector(0x24, &except); __dpmi_get_protected_mode_interrupt_vector(9, &__djgpp_old_kbd); __djgpp_exception_toggle(); /* Set new values & save old values */ /* get original video mode and save */ old_video_mode = _farpeekb(_dos_ds, 0x449); } int __djgpp_set_ctrl_c(int enable) { int oldenable = !(__djgpp_hwint_flags & 1); if(enable) __djgpp_hwint_flags &= ~1; else __djgpp_hwint_flags |= 1; return oldenable; } void __attribute__((noreturn)) _exit(int status) { /* We need to restore hardware interrupt handlers even if somebody calls `_exit' directly, or else we crash the machine in nested programs. We only toggle the handlers if the original keyboard handler is intact (otherwise, they might have already toggled them). */ if (__djgpp_old_kbd.offset32 == kbd_ori.offset32 && __djgpp_old_kbd.selector == kbd_ori.selector) __djgpp_exception_toggle (); __exit (status); }