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