]> git.llucax.com Git - software/druntime.git/blob - src/compiler/dmd/deh.c
First attempt at support for dynamic library loading and unloading. Currently, only...
[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 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