2 // Copyright (c) 1999-2003 by Digital Mars, www.digitalmars.com
4 // Written by Walter Bright
6 // Exception handling support
13 /* ======================== Win32 =============================== */
20 //#include "\sc\src\include\ehsup.h"
22 /*** From Digital Mars C runtime library ***/
23 EXCEPTION_DISPOSITION __cdecl _local_except_handler (EXCEPTION_RECORD *ExceptionRecord,
24 void* EstablisherFrame,
26 void *DispatcherContext
28 void __cdecl _global_unwind(void *frame,EXCEPTION_RECORD *eRecord);
29 #define EXCEPTION_UNWIND 6 // Flag to indicate if the system is unwinding
31 extern DWORD _except_list;
36 extern ClassInfo D6object9Throwable7__ClassZ;
37 #define _Class_9Throwable D6object9Throwable7__ClassZ;
39 extern ClassInfo D6object5Error7__ClassZ;
40 #define _Class_5Error D6object5Error7__ClassZ
42 typedef int (__pascal *fp_t)(); // function pointer in ambient memory model
44 // The layout of DEstablisherFrame is the same for C++
46 struct DEstablisherFrame
48 void *prev; // pointer to previous exception list
49 void *handler; // pointer to routine for exception handler
50 DWORD table_index; // current index into handler_info[]
51 DWORD ebp; // this is EBP of routine
56 int prev_index; // previous table index
57 unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch)
58 void *finally_code; // pointer to finally code to execute
59 // (!=0 if try-finally)
62 // Address of DHandlerTable is passed in EAX to _d_framehandler()
66 void *fptr; // pointer to start of function
67 unsigned espoffset; // offset of ESP from EBP
68 unsigned retoffset; // offset from start of function to return code
69 struct DHandlerInfo handler_info[1];
74 ClassInfo *type; // catch type
75 unsigned bpoffset; // EBP offset of catch var
76 void *code; // catch handler code
79 // Create one of these for each try-catch
82 unsigned ncatches; // number of catch blocks
83 struct DCatchBlock catch_block[1]; // data for each catch block
86 // Macro to make our own exception code
87 #define MAKE_EXCEPTION_CODE(severity, facility, exception) \
88 (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception))
90 #define STATUS_DIGITAL_MARS_D_EXCEPTION MAKE_EXCEPTION_CODE(3,'D',1)
92 Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record);
93 void __cdecl _d_local_unwind(struct DHandlerTable *handler_table, struct DEstablisherFrame *frame, int stop_index);
96 /***********************************
97 * The frame handler, this is called for each frame that has been registered
98 * in the OS except_list.
100 * EAX the handler table for the frame
103 EXCEPTION_DISPOSITION _d_framehandler(
104 EXCEPTION_RECORD *exception_record,
105 struct DEstablisherFrame *frame,
107 void *dispatcher_context)
109 struct DHandlerTable *handler_table;
111 __asm { mov handler_table,EAX }
113 if (exception_record->ExceptionFlags & EXCEPTION_UNWIND)
115 // Call all the finally blocks in this frame
116 _d_local_unwind(handler_table, frame, -1);
120 // Jump to catch block if matching one is found
123 struct DHandlerInfo *phi;
124 struct DCatchInfo *pci;
125 struct DCatchBlock *pcb;
126 unsigned ncatches; // number of catches in the current handler
130 ci = NULL; // only compute it if we need it
132 // walk through handler table, checking each handler
133 // with an index smaller than the current table_index
134 for (ndx = frame->table_index; ndx != -1; ndx = prev_ndx)
136 phi = &handler_table->handler_info[ndx];
137 prev_ndx = phi->prev_index;
140 // this is a catch handler (no finally)
141 pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset);
142 ncatches = pci->ncatches;
143 for (i = 0; i < ncatches; i++)
145 pcb = &pci->catch_block[i];
149 // This code must match the translation code
150 if (exception_record->ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION)
152 //printf("ei[0] = %p\n", exception_record->ExceptionInformation[0]);
153 ci = **(ClassInfo ***)(exception_record->ExceptionInformation[0]);
156 ci = &_Class_9Throwable;
159 if (_d_isbaseof(ci, pcb->type))
161 // Matched the catch type, so we've found the handler.
164 pti = _d_translate_se_to_d_exception(exception_record);
166 // Initialize catch variable
167 regebp = (int)&frame->ebp; // EBP for this frame
168 *(void **)(regebp + (pcb->bpoffset)) = pti;
170 // Have system call all finally blocks in intervening frames
171 _global_unwind(frame, exception_record);
173 // Call all the finally blocks skipped in this frame
174 _d_local_unwind(handler_table, frame, ndx);
176 frame->table_index = prev_ndx; // we are out of this handler
178 // Jump to catch block. Does not return.
183 catch_addr = (fp_t)(pcb->code);
184 catch_esp = regebp - handler_table->espoffset - sizeof(fp_t);
191 mov ESP,EAX // reset stack
192 ret // jump to catch block
200 return ExceptionContinueSearch;
203 /***********************************
204 * Exception filter for use in __try..__except block
205 * surrounding call to Dmain()
208 int _d_exception_filter(struct _EXCEPTION_POINTERS *eptrs,
210 Object **exception_object)
212 *exception_object = _d_translate_se_to_d_exception(eptrs->ExceptionRecord);
216 /***********************************
220 void __stdcall _d_throw(Object *h)
222 //printf("_d_throw(h = %p, &h = %p)\n", h, &h);
223 //printf("\tvptr = %p\n", *(void **)h);
224 RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION,
225 EXCEPTION_NONCONTINUABLE,
229 /***********************************
230 * Create an exception object
233 Object *_d_create_exception_object(ClassInfo *ci, char *msg)
237 exc = (Throwable *)_d_newclass(ci);
238 // BUG: what if _d_newclass() throws an out of memory exception?
242 exc->msglen = strlen(msg);
245 return (Object *)exc;
248 /***********************************
249 * Converts a Windows Structured Exception code to a D Exception Object.
252 Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record)
256 switch (exception_record->ExceptionCode) {
257 case STATUS_DIGITAL_MARS_D_EXCEPTION:
258 // Generated D exception
259 pti = (Object *)(exception_record->ExceptionInformation[0]);
262 case STATUS_INTEGER_DIVIDE_BY_ZERO:
263 pti = _d_create_exception_object(&_Class_5Error, "Integer Divide by Zero");
266 case STATUS_FLOAT_DIVIDE_BY_ZERO:
267 pti = _d_create_exception_object(&_Class_5Error, "Float Divide by Zero");
270 case STATUS_ACCESS_VIOLATION:
271 pti = _d_create_exception_object(&_Class_5Error, "Access Violation");
274 case STATUS_STACK_OVERFLOW:
275 pti = _d_create_exception_object(&_Class_5Error, "Stack Overflow");
278 // convert all other exception codes into a Win32Exception
280 pti = _d_create_exception_object(&_Class_5Error, "Win32 Exception");
287 /**************************************
288 * Call finally blocks in the current stack frame until stop_index.
289 * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c
292 void __cdecl _d_local_unwind(struct DHandlerTable *handler_table,
293 struct DEstablisherFrame *frame, int stop_index)
295 struct DHandlerInfo *phi;
296 struct DCatchInfo *pci;
299 // Set up a special exception handler to catch double-fault exceptions.
304 push offset _local_except_handler // defined in src\win32\ehsup.c
305 push dword ptr fs:_except_list
306 mov FS:_except_list,ESP
309 for (i = frame->table_index; i != -1 && i != stop_index; i = phi->prev_index)
311 phi = &handler_table->handler_info[i];
312 if (phi->finally_code)
314 // Note that it is unnecessary to adjust the ESP, as the finally block
315 // accesses all items on the stack as relative to EBP.
317 DWORD *catch_ebp = &frame->ebp;
318 void *blockaddr = phi->finally_code;
340 /***********************************
341 * external version of the unwinder
344 __declspec(naked) void __cdecl _d_local_unwind2()
352 /***********************************
353 * The frame handler, this is called for each frame that has been registered
354 * in the OS except_list.
356 * EAX the handler table for the frame
359 EXCEPTION_DISPOSITION _d_monitor_handler(
360 EXCEPTION_RECORD *exception_record,
361 struct DEstablisherFrame *frame,
363 void *dispatcher_context)
365 if (exception_record->ExceptionFlags & EXCEPTION_UNWIND)
367 _d_monitorexit((Object *)frame->table_index);
372 return ExceptionContinueSearch;
375 /***********************************
378 void _d_monitor_prolog(void *x, void *y, Object *h)
384 //printf("_d_monitor_prolog(x=%p, y=%p, h=%p)\n", x, y, h);
392 /***********************************
395 void _d_monitor_epilog(void *x, void *y, Object *h)
397 //printf("_d_monitor_epilog(x=%p, y=%p, h=%p)\n", x, y, h);
413 /* ======================== linux =============================== */
419 extern ClassInfo D6object9Throwable7__ClassZ;
420 #define _Class_9Throwable D6object9Throwable7__ClassZ;
422 extern ClassInfo D6object5Error7__ClassZ;
423 #define _Class_5Error D6object5Error7__ClassZ
425 typedef int (*fp_t)(); // function pointer in ambient memory model
429 unsigned offset; // offset from function address to start of guarded section
430 int prev_index; // previous table index
431 unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch)
432 void *finally_code; // pointer to finally code to execute
433 // (!=0 if try-finally)
436 // Address of DHandlerTable, searched for by eh_finddata()
440 void *fptr; // pointer to start of function
441 unsigned espoffset; // offset of ESP from EBP
442 unsigned retoffset; // offset from start of function to return code
443 unsigned nhandlers; // dimension of handler_info[]
444 struct DHandlerInfo handler_info[1];
449 ClassInfo *type; // catch type
450 unsigned bpoffset; // EBP offset of catch var
451 void *code; // catch handler code
454 // Create one of these for each try-catch
457 unsigned ncatches; // number of catch blocks
458 struct DCatchBlock catch_block[1]; // data for each catch block
461 // One of these is generated for each function with try-catch or try-finally
465 void *fptr; // pointer to start of function
466 struct DHandlerTable *handlertable; // eh data for this function
467 unsigned size; // size of function in bytes
470 extern struct FuncTable *table_start;
471 extern struct FuncTable *table_end;
481 /*******************************************
482 * Given address that is inside a function,
483 * figure out which function it is in.
484 * Return DHandlerTable if there is one, NULL if not.
487 struct DHandlerTable *__eh_finddata(void *address)
489 struct FuncTable *ft;
491 for (ft = (struct FuncTable *)table_start;
492 ft < (struct FuncTable *)table_end;
495 if (ft->fptr <= address &&
496 address < (void *)((char *)ft->fptr + ft->size))
498 return ft->handlertable;
505 /******************************
506 * Given EBP, find return address to caller, and caller's EBP.
508 * regbp Value of EBP for current function
509 * *pretaddr Return address
511 * *pretaddr return address to caller
516 unsigned __eh_find_caller(unsigned regbp, unsigned *pretaddr)
518 unsigned bp = *(unsigned *)regbp;
520 if (bp) // if not end of call chain
522 // Perform sanity checks on new EBP.
523 // If it is screwed up, terminate() hopefully before we do more damage.
525 // stack should grow to smaller values
528 *pretaddr = *(unsigned *)(regbp + sizeof(int));
533 /***********************************
537 void __stdcall _d_throw(Object *h)
541 //printf("_d_throw(h = %p, &h = %p)\n", h, &h);
542 //printf("\tvptr = %p\n", *(void **)h);
546 while (1) // for each function on the stack
548 struct DHandlerTable *handler_table;
549 struct FuncTable *pfunc;
550 struct DHandlerInfo *phi;
560 regebp = __eh_find_caller(regebp,&retaddr);
562 // if end of call chain
565 handler_table = __eh_finddata((void *)retaddr); // find static data associated with function
566 if (!handler_table) // if no static data
570 funcoffset = (unsigned)handler_table->fptr;
571 spoff = handler_table->espoffset;
572 retoffset = handler_table->retoffset;
575 printf("retaddr = x%x\n",(unsigned)retaddr);
576 printf("regebp=x%04x, funcoffset=x%04x, spoff=x%x, retoffset=x%x\n",
577 regebp,funcoffset,spoff,retoffset);
580 // Find start index for retaddr in static data
581 dim = handler_table->nhandlers;
583 for (int i = 0; i < dim; i++)
585 phi = &handler_table->handler_info[i];
587 if ((unsigned)retaddr >= funcoffset + phi->offset)
591 // walk through handler table, checking each handler
592 // with an index smaller than the current table_index
593 for (ndx = index; ndx != -1; ndx = prev_ndx)
595 phi = &handler_table->handler_info[ndx];
596 prev_ndx = phi->prev_index;
599 // this is a catch handler (no finally)
600 struct DCatchInfo *pci;
604 pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset);
605 ncatches = pci->ncatches;
606 for (i = 0; i < ncatches; i++)
608 struct DCatchBlock *pcb;
609 ClassInfo *ci = **(ClassInfo ***)h;
611 pcb = &pci->catch_block[i];
613 if (_d_isbaseof(ci, pcb->type))
614 { // Matched the catch type, so we've found the handler.
616 // Initialize catch variable
617 *(void **)(regebp + (pcb->bpoffset)) = h;
619 // Jump to catch block. Does not return.
624 catch_addr = (fp_t)(pcb->code);
625 catch_esp = regebp - handler_table->espoffset - sizeof(fp_t);
632 mov ESP,EAX // reset stack
633 ret // jump to catch block
639 else if (phi->finally_code)
640 { // Call finally block
641 // Note that it is unnecessary to adjust the ESP, as the finally block
642 // accesses all items on the stack as relative to EBP.
644 void *blockaddr = phi->finally_code;