// // Copyright (c) 1999-2003 by Digital Mars, www.digitalmars.com // All Rights Reserved // Written by Walter Bright // Exception handling support #include #include #include #include /* ======================== Win32 =============================== */ #if _WIN32 #include #include //#include "\sc\src\include\ehsup.h" /*** From Digital Mars C runtime library ***/ EXCEPTION_DISPOSITION __cdecl _local_except_handler (EXCEPTION_RECORD *ExceptionRecord, void* EstablisherFrame, void *ContextRecord, void *DispatcherContext ); void __cdecl _global_unwind(void *frame,EXCEPTION_RECORD *eRecord); #define EXCEPTION_UNWIND 6 // Flag to indicate if the system is unwinding extern DWORD _except_list; /*** ***/ #include "mars.h" extern ClassInfo _Class_9Exception; typedef int (__pascal *fp_t)(); // function pointer in ambient memory model // The layout of DEstablisherFrame is the same for C++ struct DEstablisherFrame { void *prev; // pointer to previous exception list void *handler; // pointer to routine for exception handler DWORD table_index; // current index into handler_info[] DWORD ebp; // this is EBP of routine }; struct DHandlerInfo { int prev_index; // previous table index unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch) void *finally_code; // pointer to finally code to execute // (!=0 if try-finally) }; // Address of DHandlerTable is passed in EAX to _d_framehandler() struct DHandlerTable { void *fptr; // pointer to start of function unsigned espoffset; // offset of ESP from EBP unsigned retoffset; // offset from start of function to return code struct DHandlerInfo handler_info[1]; }; struct DCatchBlock { ClassInfo *type; // catch type unsigned bpoffset; // EBP offset of catch var void *code; // catch handler code }; // Create one of these for each try-catch struct DCatchInfo { unsigned ncatches; // number of catch blocks struct DCatchBlock catch_block[1]; // data for each catch block }; // Macro to make our own exception code #define MAKE_EXCEPTION_CODE(severity, facility, exception) \ (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception)) #define STATUS_DIGITAL_MARS_D_EXCEPTION MAKE_EXCEPTION_CODE(3,'D',1) Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record); void __cdecl _d_local_unwind(struct DHandlerTable *handler_table, struct DEstablisherFrame *frame, int stop_index); /*********************************** * The frame handler, this is called for each frame that has been registered * in the OS except_list. * Input: * EAX the handler table for the frame */ EXCEPTION_DISPOSITION _d_framehandler( EXCEPTION_RECORD *exception_record, struct DEstablisherFrame *frame, CONTEXT context, void *dispatcher_context) { struct DHandlerTable *handler_table; __asm { mov handler_table,EAX } if (exception_record->ExceptionFlags & EXCEPTION_UNWIND) { // Call all the finally blocks in this frame _d_local_unwind(handler_table, frame, -1); } else { // Jump to catch block if matching one is found int ndx,prev_ndx,i; struct DHandlerInfo *phi; struct DCatchInfo *pci; struct DCatchBlock *pcb; unsigned ncatches; // number of catches in the current handler Object *pti; ClassInfo *ci; ci = NULL; // only compute it if we need it // walk through handler table, checking each handler // with an index smaller than the current table_index for (ndx = frame->table_index; ndx != -1; ndx = prev_ndx) { phi = &handler_table->handler_info[ndx]; prev_ndx = phi->prev_index; if (phi->cioffset) { // this is a catch handler (no finally) pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset); ncatches = pci->ncatches; for (i = 0; i < ncatches; i++) { pcb = &pci->catch_block[i]; if (!ci) { // This code must match the translation code if (exception_record->ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) { //printf("ei[0] = %p\n", exception_record->ExceptionInformation[0]); ci = **(ClassInfo ***)(exception_record->ExceptionInformation[0]); } else ci = &_Class_9Exception; } if (_d_isbaseof(ci, pcb->type)) { // Matched the catch type, so we've found the handler. int regebp; pti = _d_translate_se_to_d_exception(exception_record); // Initialize catch variable regebp = (int)&frame->ebp; // EBP for this frame *(void **)(regebp + (pcb->bpoffset)) = pti; // Have system call all finally blocks in intervening frames _global_unwind(frame, exception_record); // Call all the finally blocks skipped in this frame _d_local_unwind(handler_table, frame, ndx); frame->table_index = prev_ndx; // we are out of this handler // Jump to catch block. Does not return. { unsigned catch_esp; fp_t catch_addr; catch_addr = (fp_t)(pcb->code); catch_esp = regebp - handler_table->espoffset - sizeof(fp_t); _asm { mov EAX,catch_esp mov ECX,catch_addr mov [EAX],ECX mov EBP,regebp mov ESP,EAX // reset stack ret // jump to catch block } } } } } } } return ExceptionContinueSearch; } /*********************************** * Exception filter for use in __try..__except block * surrounding call to Dmain() */ int _d_exception_filter(struct _EXCEPTION_POINTERS *eptrs, int retval, Object **exception_object) { *exception_object = _d_translate_se_to_d_exception(eptrs->ExceptionRecord); return retval; } /*********************************** * Throw a D object. */ void __stdcall _d_throw(Object *h) { //printf("_d_throw(h = %p, &h = %p)\n", h, &h); //printf("\tvptr = %p\n", *(void **)h); RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, EXCEPTION_NONCONTINUABLE, 1, (DWORD *)&h); } /*********************************** * Create an exception object */ Object *_d_create_exception_object(ClassInfo *ci, char *msg) { Exception *exc; exc = (Exception *)_d_newclass(ci); // BUG: what if _d_newclass() throws an out of memory exception? if (msg) { exc->msglen = strlen(msg); exc->msg = msg; } return (Object *)exc; } /*********************************** * Converts a Windows Structured Exception code to a D Exception Object. */ Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record) { Object *pti; switch (exception_record->ExceptionCode) { case STATUS_DIGITAL_MARS_D_EXCEPTION: // Generated D exception pti = (Object *)(exception_record->ExceptionInformation[0]); break; case STATUS_INTEGER_DIVIDE_BY_ZERO: pti = _d_create_exception_object(&_Class_9Exception, "Integer Divide by Zero"); break; case STATUS_FLOAT_DIVIDE_BY_ZERO: pti = _d_create_exception_object(&_Class_9Exception, "Float Divide by Zero"); break; case STATUS_ACCESS_VIOLATION: pti = _d_create_exception_object(&_Class_9Exception, "Access Violation"); break; case STATUS_STACK_OVERFLOW: pti = _d_create_exception_object(&_Class_9Exception, "Stack Overflow"); break; // convert all other exception codes into a Win32Exception default: pti = _d_create_exception_object(&_Class_9Exception, "Win32 Exception"); break; } return pti; } /************************************** * Call finally blocks in the current stack frame until stop_index. * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c */ void __cdecl _d_local_unwind(struct DHandlerTable *handler_table, struct DEstablisherFrame *frame, int stop_index) { struct DHandlerInfo *phi; struct DCatchInfo *pci; int i; // Set up a special exception handler to catch double-fault exceptions. __asm { push dword ptr -1 push dword ptr 0 push offset _local_except_handler // defined in src\win32\ehsup.c push dword ptr fs:_except_list mov FS:_except_list,ESP } for (i = frame->table_index; i != -1 && i != stop_index; i = phi->prev_index) { phi = &handler_table->handler_info[i]; if (phi->finally_code) { // Note that it is unnecessary to adjust the ESP, as the finally block // accesses all items on the stack as relative to EBP. DWORD *catch_ebp = &frame->ebp; void *blockaddr = phi->finally_code; _asm { push EBX mov EBX,blockaddr push EBP mov EBP,catch_ebp call EBX pop EBP pop EBX } } } _asm { pop FS:_except_list add ESP,12 } } /*********************************** * external version of the unwinder */ __declspec(naked) void __cdecl _d_local_unwind2() { __asm { jmp _d_local_unwind } } /*********************************** * The frame handler, this is called for each frame that has been registered * in the OS except_list. * Input: * EAX the handler table for the frame */ EXCEPTION_DISPOSITION _d_monitor_handler( EXCEPTION_RECORD *exception_record, struct DEstablisherFrame *frame, CONTEXT context, void *dispatcher_context) { if (exception_record->ExceptionFlags & EXCEPTION_UNWIND) { _d_monitorexit((Object *)frame->table_index); } else { } return ExceptionContinueSearch; } /*********************************** */ void _d_monitor_prolog(void *x, void *y, Object *h) { __asm { push EAX } //printf("_d_monitor_prolog(x=%p, y=%p, h=%p)\n", x, y, h); _d_monitorenter(h); __asm { pop EAX } } /*********************************** */ void _d_monitor_epilog(void *x, void *y, Object *h) { //printf("_d_monitor_epilog(x=%p, y=%p, h=%p)\n", x, y, h); __asm { push EAX push EDX } _d_monitorexit(h); __asm { pop EDX pop EAX } } #endif /* ======================== linux =============================== */ #if linux #include "mars.h" extern ClassInfo _Class_9Exception; typedef int (*fp_t)(); // function pointer in ambient memory model struct DHandlerInfo { unsigned offset; // offset from function address to start of guarded section int prev_index; // previous table index unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch) void *finally_code; // pointer to finally code to execute // (!=0 if try-finally) }; // Address of DHandlerTable, searched for by eh_finddata() struct DHandlerTable { void *fptr; // pointer to start of function unsigned espoffset; // offset of ESP from EBP unsigned retoffset; // offset from start of function to return code unsigned nhandlers; // dimension of handler_info[] struct DHandlerInfo handler_info[1]; }; struct DCatchBlock { ClassInfo *type; // catch type unsigned bpoffset; // EBP offset of catch var void *code; // catch handler code }; // Create one of these for each try-catch struct DCatchInfo { unsigned ncatches; // number of catch blocks struct DCatchBlock catch_block[1]; // data for each catch block }; // One of these is generated for each function with try-catch or try-finally struct FuncTable { void *fptr; // pointer to start of function struct DHandlerTable *handlertable; // eh data for this function unsigned size; // size of function in bytes }; extern struct FuncTable *table_start; extern struct FuncTable *table_end; void terminate() { // _asm // { // hlt // } } /******************************************* * Given address that is inside a function, * figure out which function it is in. * Return DHandlerTable if there is one, NULL if not. */ struct DHandlerTable *__eh_finddata(void *address) { struct FuncTable *ft; for (ft = (struct FuncTable *)table_start; ft < (struct FuncTable *)table_end; ft++) { if (ft->fptr <= address && address < (void *)((char *)ft->fptr + ft->size)) { return ft->handlertable; } } return NULL; } /****************************** * Given EBP, find return address to caller, and caller's EBP. * Input: * regbp Value of EBP for current function * *pretaddr Return address * Output: * *pretaddr return address to caller * Returns: * caller's EBP */ unsigned __eh_find_caller(unsigned regbp, unsigned *pretaddr) { unsigned bp = *(unsigned *)regbp; if (bp) // if not end of call chain { // Perform sanity checks on new EBP. // If it is screwed up, terminate() hopefully before we do more damage. if (bp <= regbp) // stack should grow to smaller values terminate(); *pretaddr = *(unsigned *)(regbp + sizeof(int)); } return bp; } /*********************************** * Throw a D object. */ void __stdcall _d_throw(Object *h) { unsigned regebp; //printf("_d_throw(h = %p, &h = %p)\n", h, &h); //printf("\tvptr = %p\n", *(void **)h); regebp = _EBP; while (1) // for each function on the stack { struct DHandlerTable *handler_table; struct FuncTable *pfunc; struct DHandlerInfo *phi; unsigned retaddr; unsigned funcoffset; unsigned spoff; unsigned retoffset; int index; int dim; int ndx; int prev_ndx; regebp = __eh_find_caller(regebp,&retaddr); if (!regebp) // if end of call chain break; handler_table = __eh_finddata((void *)retaddr); // find static data associated with function if (!handler_table) // if no static data { continue; } funcoffset = (unsigned)handler_table->fptr; spoff = handler_table->espoffset; retoffset = handler_table->retoffset; #ifdef DEBUG printf("retaddr = x%x\n",(unsigned)retaddr); printf("regebp=x%04x, funcoffset=x%04x, spoff=x%x, retoffset=x%x\n", regebp,funcoffset,spoff,retoffset); #endif // Find start index for retaddr in static data dim = handler_table->nhandlers; index = -1; for (int i = 0; i < dim; i++) { phi = &handler_table->handler_info[i]; if ((unsigned)retaddr >= funcoffset + phi->offset) index = i; } // walk through handler table, checking each handler // with an index smaller than the current table_index for (ndx = index; ndx != -1; ndx = prev_ndx) { phi = &handler_table->handler_info[ndx]; prev_ndx = phi->prev_index; if (phi->cioffset) { // this is a catch handler (no finally) struct DCatchInfo *pci; int ncatches; int i; pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset); ncatches = pci->ncatches; for (i = 0; i < ncatches; i++) { struct DCatchBlock *pcb; ClassInfo *ci = **(ClassInfo ***)h; pcb = &pci->catch_block[i]; if (_d_isbaseof(ci, pcb->type)) { // Matched the catch type, so we've found the handler. // Initialize catch variable *(void **)(regebp + (pcb->bpoffset)) = h; // Jump to catch block. Does not return. { unsigned catch_esp; fp_t catch_addr; catch_addr = (fp_t)(pcb->code); catch_esp = regebp - handler_table->espoffset - sizeof(fp_t); _asm { mov EAX,catch_esp mov ECX,catch_addr mov [EAX],ECX mov EBP,regebp mov ESP,EAX // reset stack ret // jump to catch block } } } } } else if (phi->finally_code) { // Call finally block // Note that it is unnecessary to adjust the ESP, as the finally block // accesses all items on the stack as relative to EBP. void *blockaddr = phi->finally_code; _asm { push EBX mov EBX,blockaddr push EBP mov EBP,regebp call EBX pop EBP pop EBX } } } } } #endif