Mercurial > projects > ldc
comparison druntime/src/compiler/dmd/deh.c @ 759:d3eb054172f9
Added copy of druntime from DMD 2.020 modified for LDC.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 01:52:37 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
758:f04dde6e882c | 759:d3eb054172f9 |
---|---|
1 // | |
2 // Copyright (c) 1999-2003 by Digital Mars, www.digitalmars.com | |
3 // All Rights Reserved | |
4 // Written by Walter Bright | |
5 | |
6 // Exception handling support | |
7 | |
8 #include <stdio.h> | |
9 #include <string.h> | |
10 #include <assert.h> | |
11 #include <stdlib.h> | |
12 | |
13 /* ======================== Win32 =============================== */ | |
14 | |
15 #if _WIN32 | |
16 | |
17 #include <excpt.h> | |
18 #include <windows.h> | |
19 | |
20 //#include "\sc\src\include\ehsup.h" | |
21 | |
22 /*** From Digital Mars C runtime library ***/ | |
23 EXCEPTION_DISPOSITION __cdecl _local_except_handler (EXCEPTION_RECORD *ExceptionRecord, | |
24 void* EstablisherFrame, | |
25 void *ContextRecord, | |
26 void *DispatcherContext | |
27 ); | |
28 void __cdecl _global_unwind(void *frame,EXCEPTION_RECORD *eRecord); | |
29 #define EXCEPTION_UNWIND 6 // Flag to indicate if the system is unwinding | |
30 | |
31 extern DWORD _except_list; | |
32 /*** ***/ | |
33 | |
34 #include "mars.h" | |
35 | |
36 extern ClassInfo D6object9Throwable7__ClassZ; | |
37 #define _Class_9Throwable D6object9Throwable7__ClassZ; | |
38 | |
39 extern ClassInfo D6object5Error7__ClassZ; | |
40 #define _Class_5Error D6object5Error7__ClassZ | |
41 | |
42 typedef int (__pascal *fp_t)(); // function pointer in ambient memory model | |
43 | |
44 // The layout of DEstablisherFrame is the same for C++ | |
45 | |
46 struct DEstablisherFrame | |
47 { | |
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 | |
52 }; | |
53 | |
54 struct DHandlerInfo | |
55 { | |
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) | |
60 }; | |
61 | |
62 // Address of DHandlerTable is passed in EAX to _d_framehandler() | |
63 | |
64 struct DHandlerTable | |
65 { | |
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]; | |
70 }; | |
71 | |
72 struct DCatchBlock | |
73 { | |
74 ClassInfo *type; // catch type | |
75 unsigned bpoffset; // EBP offset of catch var | |
76 void *code; // catch handler code | |
77 }; | |
78 | |
79 // Create one of these for each try-catch | |
80 struct DCatchInfo | |
81 { | |
82 unsigned ncatches; // number of catch blocks | |
83 struct DCatchBlock catch_block[1]; // data for each catch block | |
84 }; | |
85 | |
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)) | |
89 | |
90 #define STATUS_DIGITAL_MARS_D_EXCEPTION MAKE_EXCEPTION_CODE(3,'D',1) | |
91 | |
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); | |
94 | |
95 | |
96 /*********************************** | |
97 * The frame handler, this is called for each frame that has been registered | |
98 * in the OS except_list. | |
99 * Input: | |
100 * EAX the handler table for the frame | |
101 */ | |
102 | |
103 EXCEPTION_DISPOSITION _d_framehandler( | |
104 EXCEPTION_RECORD *exception_record, | |
105 struct DEstablisherFrame *frame, | |
106 CONTEXT context, | |
107 void *dispatcher_context) | |
108 { | |
109 struct DHandlerTable *handler_table; | |
110 | |
111 __asm { mov handler_table,EAX } | |
112 | |
113 if (exception_record->ExceptionFlags & EXCEPTION_UNWIND) | |
114 { | |
115 // Call all the finally blocks in this frame | |
116 _d_local_unwind(handler_table, frame, -1); | |
117 } | |
118 else | |
119 { | |
120 // Jump to catch block if matching one is found | |
121 | |
122 int ndx,prev_ndx,i; | |
123 struct DHandlerInfo *phi; | |
124 struct DCatchInfo *pci; | |
125 struct DCatchBlock *pcb; | |
126 unsigned ncatches; // number of catches in the current handler | |
127 Object *pti; | |
128 ClassInfo *ci; | |
129 | |
130 ci = NULL; // only compute it if we need it | |
131 | |
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) | |
135 { | |
136 phi = &handler_table->handler_info[ndx]; | |
137 prev_ndx = phi->prev_index; | |
138 if (phi->cioffset) | |
139 { | |
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++) | |
144 { | |
145 pcb = &pci->catch_block[i]; | |
146 | |
147 if (!ci) | |
148 { | |
149 // This code must match the translation code | |
150 if (exception_record->ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) | |
151 { | |
152 //printf("ei[0] = %p\n", exception_record->ExceptionInformation[0]); | |
153 ci = **(ClassInfo ***)(exception_record->ExceptionInformation[0]); | |
154 } | |
155 else | |
156 ci = &_Class_9Throwable; | |
157 } | |
158 | |
159 if (_d_isbaseof(ci, pcb->type)) | |
160 { | |
161 // Matched the catch type, so we've found the handler. | |
162 int regebp; | |
163 | |
164 pti = _d_translate_se_to_d_exception(exception_record); | |
165 | |
166 // Initialize catch variable | |
167 regebp = (int)&frame->ebp; // EBP for this frame | |
168 *(void **)(regebp + (pcb->bpoffset)) = pti; | |
169 | |
170 // Have system call all finally blocks in intervening frames | |
171 _global_unwind(frame, exception_record); | |
172 | |
173 // Call all the finally blocks skipped in this frame | |
174 _d_local_unwind(handler_table, frame, ndx); | |
175 | |
176 frame->table_index = prev_ndx; // we are out of this handler | |
177 | |
178 // Jump to catch block. Does not return. | |
179 { | |
180 unsigned catch_esp; | |
181 fp_t catch_addr; | |
182 | |
183 catch_addr = (fp_t)(pcb->code); | |
184 catch_esp = regebp - handler_table->espoffset - sizeof(fp_t); | |
185 _asm | |
186 { | |
187 mov EAX,catch_esp | |
188 mov ECX,catch_addr | |
189 mov [EAX],ECX | |
190 mov EBP,regebp | |
191 mov ESP,EAX // reset stack | |
192 ret // jump to catch block | |
193 } | |
194 } | |
195 } | |
196 } | |
197 } | |
198 } | |
199 } | |
200 return ExceptionContinueSearch; | |
201 } | |
202 | |
203 /*********************************** | |
204 * Exception filter for use in __try..__except block | |
205 * surrounding call to Dmain() | |
206 */ | |
207 | |
208 int _d_exception_filter(struct _EXCEPTION_POINTERS *eptrs, | |
209 int retval, | |
210 Object **exception_object) | |
211 { | |
212 *exception_object = _d_translate_se_to_d_exception(eptrs->ExceptionRecord); | |
213 return retval; | |
214 } | |
215 | |
216 /*********************************** | |
217 * Throw a D object. | |
218 */ | |
219 | |
220 void __stdcall _d_throw(Object *h) | |
221 { | |
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, | |
226 1, (DWORD *)&h); | |
227 } | |
228 | |
229 /*********************************** | |
230 * Create an exception object | |
231 */ | |
232 | |
233 Object *_d_create_exception_object(ClassInfo *ci, char *msg) | |
234 { | |
235 Throwable *exc; | |
236 | |
237 exc = (Throwable *)_d_newclass(ci); | |
238 // BUG: what if _d_newclass() throws an out of memory exception? | |
239 | |
240 if (msg) | |
241 { | |
242 exc->msglen = strlen(msg); | |
243 exc->msg = msg; | |
244 } | |
245 return (Object *)exc; | |
246 } | |
247 | |
248 /*********************************** | |
249 * Converts a Windows Structured Exception code to a D Exception Object. | |
250 */ | |
251 | |
252 Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record) | |
253 { | |
254 Object *pti; | |
255 | |
256 switch (exception_record->ExceptionCode) { | |
257 case STATUS_DIGITAL_MARS_D_EXCEPTION: | |
258 // Generated D exception | |
259 pti = (Object *)(exception_record->ExceptionInformation[0]); | |
260 break; | |
261 | |
262 case STATUS_INTEGER_DIVIDE_BY_ZERO: | |
263 pti = _d_create_exception_object(&_Class_5Error, "Integer Divide by Zero"); | |
264 break; | |
265 | |
266 case STATUS_FLOAT_DIVIDE_BY_ZERO: | |
267 pti = _d_create_exception_object(&_Class_5Error, "Float Divide by Zero"); | |
268 break; | |
269 | |
270 case STATUS_ACCESS_VIOLATION: | |
271 pti = _d_create_exception_object(&_Class_5Error, "Access Violation"); | |
272 break; | |
273 | |
274 case STATUS_STACK_OVERFLOW: | |
275 pti = _d_create_exception_object(&_Class_5Error, "Stack Overflow"); | |
276 break; | |
277 | |
278 // convert all other exception codes into a Win32Exception | |
279 default: | |
280 pti = _d_create_exception_object(&_Class_5Error, "Win32 Exception"); | |
281 break; | |
282 } | |
283 | |
284 return pti; | |
285 } | |
286 | |
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 | |
290 */ | |
291 | |
292 void __cdecl _d_local_unwind(struct DHandlerTable *handler_table, | |
293 struct DEstablisherFrame *frame, int stop_index) | |
294 { | |
295 struct DHandlerInfo *phi; | |
296 struct DCatchInfo *pci; | |
297 int i; | |
298 | |
299 // Set up a special exception handler to catch double-fault exceptions. | |
300 __asm | |
301 { | |
302 push dword ptr -1 | |
303 push dword ptr 0 | |
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 | |
307 } | |
308 | |
309 for (i = frame->table_index; i != -1 && i != stop_index; i = phi->prev_index) | |
310 { | |
311 phi = &handler_table->handler_info[i]; | |
312 if (phi->finally_code) | |
313 { | |
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. | |
316 | |
317 DWORD *catch_ebp = &frame->ebp; | |
318 void *blockaddr = phi->finally_code; | |
319 | |
320 _asm | |
321 { | |
322 push EBX | |
323 mov EBX,blockaddr | |
324 push EBP | |
325 mov EBP,catch_ebp | |
326 call EBX | |
327 pop EBP | |
328 pop EBX | |
329 } | |
330 } | |
331 } | |
332 | |
333 _asm | |
334 { | |
335 pop FS:_except_list | |
336 add ESP,12 | |
337 } | |
338 } | |
339 | |
340 /*********************************** | |
341 * external version of the unwinder | |
342 */ | |
343 | |
344 __declspec(naked) void __cdecl _d_local_unwind2() | |
345 { | |
346 __asm | |
347 { | |
348 jmp _d_local_unwind | |
349 } | |
350 } | |
351 | |
352 /*********************************** | |
353 * The frame handler, this is called for each frame that has been registered | |
354 * in the OS except_list. | |
355 * Input: | |
356 * EAX the handler table for the frame | |
357 */ | |
358 | |
359 EXCEPTION_DISPOSITION _d_monitor_handler( | |
360 EXCEPTION_RECORD *exception_record, | |
361 struct DEstablisherFrame *frame, | |
362 CONTEXT context, | |
363 void *dispatcher_context) | |
364 { | |
365 if (exception_record->ExceptionFlags & EXCEPTION_UNWIND) | |
366 { | |
367 _d_monitorexit((Object *)frame->table_index); | |
368 } | |
369 else | |
370 { | |
371 } | |
372 return ExceptionContinueSearch; | |
373 } | |
374 | |
375 /*********************************** | |
376 */ | |
377 | |
378 void _d_monitor_prolog(void *x, void *y, Object *h) | |
379 { | |
380 __asm | |
381 { | |
382 push EAX | |
383 } | |
384 //printf("_d_monitor_prolog(x=%p, y=%p, h=%p)\n", x, y, h); | |
385 _d_monitorenter(h); | |
386 __asm | |
387 { | |
388 pop EAX | |
389 } | |
390 } | |
391 | |
392 /*********************************** | |
393 */ | |
394 | |
395 void _d_monitor_epilog(void *x, void *y, Object *h) | |
396 { | |
397 //printf("_d_monitor_epilog(x=%p, y=%p, h=%p)\n", x, y, h); | |
398 __asm | |
399 { | |
400 push EAX | |
401 push EDX | |
402 } | |
403 _d_monitorexit(h); | |
404 __asm | |
405 { | |
406 pop EDX | |
407 pop EAX | |
408 } | |
409 } | |
410 | |
411 #endif | |
412 | |
413 /* ======================== linux =============================== */ | |
414 | |
415 #if linux | |
416 | |
417 #include "mars.h" | |
418 | |
419 extern ClassInfo D6object9Throwable7__ClassZ; | |
420 #define _Class_9Throwable D6object9Throwable7__ClassZ; | |
421 | |
422 extern ClassInfo D6object5Error7__ClassZ; | |
423 #define _Class_5Error D6object5Error7__ClassZ | |
424 | |
425 typedef int (*fp_t)(); // function pointer in ambient memory model | |
426 | |
427 struct DHandlerInfo | |
428 { | |
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) | |
434 }; | |
435 | |
436 // Address of DHandlerTable, searched for by eh_finddata() | |
437 | |
438 struct DHandlerTable | |
439 { | |
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]; | |
445 }; | |
446 | |
447 struct DCatchBlock | |
448 { | |
449 ClassInfo *type; // catch type | |
450 unsigned bpoffset; // EBP offset of catch var | |
451 void *code; // catch handler code | |
452 }; | |
453 | |
454 // Create one of these for each try-catch | |
455 struct DCatchInfo | |
456 { | |
457 unsigned ncatches; // number of catch blocks | |
458 struct DCatchBlock catch_block[1]; // data for each catch block | |
459 }; | |
460 | |
461 // One of these is generated for each function with try-catch or try-finally | |
462 | |
463 struct FuncTable | |
464 { | |
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 | |
468 }; | |
469 | |
470 extern struct FuncTable *table_start; | |
471 extern struct FuncTable *table_end; | |
472 | |
473 void terminate() | |
474 { | |
475 // _asm | |
476 // { | |
477 // hlt | |
478 // } | |
479 } | |
480 | |
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. | |
485 */ | |
486 | |
487 struct DHandlerTable *__eh_finddata(void *address) | |
488 { | |
489 struct FuncTable *ft; | |
490 | |
491 for (ft = (struct FuncTable *)table_start; | |
492 ft < (struct FuncTable *)table_end; | |
493 ft++) | |
494 { | |
495 if (ft->fptr <= address && | |
496 address < (void *)((char *)ft->fptr + ft->size)) | |
497 { | |
498 return ft->handlertable; | |
499 } | |
500 } | |
501 return NULL; | |
502 } | |
503 | |
504 | |
505 /****************************** | |
506 * Given EBP, find return address to caller, and caller's EBP. | |
507 * Input: | |
508 * regbp Value of EBP for current function | |
509 * *pretaddr Return address | |
510 * Output: | |
511 * *pretaddr return address to caller | |
512 * Returns: | |
513 * caller's EBP | |
514 */ | |
515 | |
516 unsigned __eh_find_caller(unsigned regbp, unsigned *pretaddr) | |
517 { | |
518 unsigned bp = *(unsigned *)regbp; | |
519 | |
520 if (bp) // if not end of call chain | |
521 { | |
522 // Perform sanity checks on new EBP. | |
523 // If it is screwed up, terminate() hopefully before we do more damage. | |
524 if (bp <= regbp) | |
525 // stack should grow to smaller values | |
526 terminate(); | |
527 | |
528 *pretaddr = *(unsigned *)(regbp + sizeof(int)); | |
529 } | |
530 return bp; | |
531 } | |
532 | |
533 /*********************************** | |
534 * Throw a D object. | |
535 */ | |
536 | |
537 void __stdcall _d_throw(Object *h) | |
538 { | |
539 unsigned regebp; | |
540 | |
541 //printf("_d_throw(h = %p, &h = %p)\n", h, &h); | |
542 //printf("\tvptr = %p\n", *(void **)h); | |
543 | |
544 regebp = _EBP; | |
545 | |
546 while (1) // for each function on the stack | |
547 { | |
548 struct DHandlerTable *handler_table; | |
549 struct FuncTable *pfunc; | |
550 struct DHandlerInfo *phi; | |
551 unsigned retaddr; | |
552 unsigned funcoffset; | |
553 unsigned spoff; | |
554 unsigned retoffset; | |
555 int index; | |
556 int dim; | |
557 int ndx; | |
558 int prev_ndx; | |
559 | |
560 regebp = __eh_find_caller(regebp,&retaddr); | |
561 if (!regebp) | |
562 // if end of call chain | |
563 break; | |
564 | |
565 handler_table = __eh_finddata((void *)retaddr); // find static data associated with function | |
566 if (!handler_table) // if no static data | |
567 { | |
568 continue; | |
569 } | |
570 funcoffset = (unsigned)handler_table->fptr; | |
571 spoff = handler_table->espoffset; | |
572 retoffset = handler_table->retoffset; | |
573 | |
574 #ifdef DEBUG | |
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); | |
578 #endif | |
579 | |
580 // Find start index for retaddr in static data | |
581 dim = handler_table->nhandlers; | |
582 index = -1; | |
583 for (int i = 0; i < dim; i++) | |
584 { | |
585 phi = &handler_table->handler_info[i]; | |
586 | |
587 if ((unsigned)retaddr >= funcoffset + phi->offset) | |
588 index = i; | |
589 } | |
590 | |
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) | |
594 { | |
595 phi = &handler_table->handler_info[ndx]; | |
596 prev_ndx = phi->prev_index; | |
597 if (phi->cioffset) | |
598 { | |
599 // this is a catch handler (no finally) | |
600 struct DCatchInfo *pci; | |
601 int ncatches; | |
602 int i; | |
603 | |
604 pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset); | |
605 ncatches = pci->ncatches; | |
606 for (i = 0; i < ncatches; i++) | |
607 { | |
608 struct DCatchBlock *pcb; | |
609 ClassInfo *ci = **(ClassInfo ***)h; | |
610 | |
611 pcb = &pci->catch_block[i]; | |
612 | |
613 if (_d_isbaseof(ci, pcb->type)) | |
614 { // Matched the catch type, so we've found the handler. | |
615 | |
616 // Initialize catch variable | |
617 *(void **)(regebp + (pcb->bpoffset)) = h; | |
618 | |
619 // Jump to catch block. Does not return. | |
620 { | |
621 unsigned catch_esp; | |
622 fp_t catch_addr; | |
623 | |
624 catch_addr = (fp_t)(pcb->code); | |
625 catch_esp = regebp - handler_table->espoffset - sizeof(fp_t); | |
626 _asm | |
627 { | |
628 mov EAX,catch_esp | |
629 mov ECX,catch_addr | |
630 mov [EAX],ECX | |
631 mov EBP,regebp | |
632 mov ESP,EAX // reset stack | |
633 ret // jump to catch block | |
634 } | |
635 } | |
636 } | |
637 } | |
638 } | |
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. | |
643 | |
644 void *blockaddr = phi->finally_code; | |
645 | |
646 _asm | |
647 { | |
648 push EBX | |
649 mov EBX,blockaddr | |
650 push EBP | |
651 mov EBP,regebp | |
652 call EBX | |
653 pop EBP | |
654 pop EBX | |
655 } | |
656 } | |
657 } | |
658 } | |
659 } | |
660 | |
661 | |
662 #endif |