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