comparison druntime/src/compiler/dmd/deh.c @ 1458:e0b2d67cfe7c

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