1 /**
2  * Implementation of exception handling support routines for Win32.
3  *
4  * Copyright: Copyright Digital Mars 1999 - 2013.
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Authors:   Walter Bright
9  * Source: $(DRUNTIMESRC rt/deh_win32.d)
10  */
11 
12 module rt.deh_win32;
13 
14 version (Win32):
15 
16 import core.sys.windows.basetsd /+: ULONG_PTR+/;
17 import core.sys.windows.windef /+: BOOL, BYTE, DWORD+/;
18 import core.sys.windows.winnt /+: PVOID+/;
19 import rt.monitor_;
20 //import core.stdc.stdio;
21 
22 version (D_InlineAsm_X86)
23 {
24     version = AsmX86;
25 }
26 else version (D_InlineAsm_X86_64)
27 {
28     version = AsmX86;
29 }
30 
31 enum EXCEPTION_DISPOSITION {
32     ExceptionContinueExecution,
33     ExceptionContinueSearch,
34     ExceptionNestedException,
35     ExceptionCollidedUnwind
36 }
37 
38 /+
39 enum {
40     EXCEPTION_EXECUTE_HANDLER    = 1,
41     EXCEPTION_CONTINUE_SEARCH    = 0,
42     EXCEPTION_CONTINUE_EXECUTION = -1
43 }
44 +/
45 
46 extern(Windows)
47 {
48 void RaiseException(DWORD, DWORD, DWORD, void *);
49 }
50 
51 // used in EXCEPTION_RECORD
52 enum : DWORD {
53         STATUS_WAIT_0                      = 0,
54         STATUS_ABANDONED_WAIT_0            = 0x00000080,
55         STATUS_USER_APC                    = 0x000000C0,
56         STATUS_TIMEOUT                     = 0x00000102,
57         STATUS_PENDING                     = 0x00000103,
58 
59         STATUS_SEGMENT_NOTIFICATION        = 0x40000005,
60         STATUS_GUARD_PAGE_VIOLATION        = 0x80000001,
61         STATUS_DATATYPE_MISALIGNMENT       = 0x80000002,
62         STATUS_BREAKPOINT                  = 0x80000003,
63         STATUS_SINGLE_STEP                 = 0x80000004,
64 
65         STATUS_ACCESS_VIOLATION            = 0xC0000005,
66         STATUS_IN_PAGE_ERROR               = 0xC0000006,
67         STATUS_INVALID_HANDLE              = 0xC0000008,
68 
69         STATUS_NO_MEMORY                   = 0xC0000017,
70         STATUS_ILLEGAL_INSTRUCTION         = 0xC000001D,
71         STATUS_NONCONTINUABLE_EXCEPTION    = 0xC0000025,
72         STATUS_INVALID_DISPOSITION         = 0xC0000026,
73         STATUS_ARRAY_BOUNDS_EXCEEDED       = 0xC000008C,
74         STATUS_FLOAT_DENORMAL_OPERAND      = 0xC000008D,
75         STATUS_FLOAT_DIVIDE_BY_ZERO        = 0xC000008E,
76         STATUS_FLOAT_INEXACT_RESULT        = 0xC000008F,
77         STATUS_FLOAT_INVALID_OPERATION     = 0xC0000090,
78         STATUS_FLOAT_OVERFLOW              = 0xC0000091,
79         STATUS_FLOAT_STACK_CHECK           = 0xC0000092,
80         STATUS_FLOAT_UNDERFLOW             = 0xC0000093,
81         STATUS_INTEGER_DIVIDE_BY_ZERO      = 0xC0000094,
82         STATUS_INTEGER_OVERFLOW            = 0xC0000095,
83         STATUS_PRIVILEGED_INSTRUCTION      = 0xC0000096,
84         STATUS_STACK_OVERFLOW              = 0xC00000FD,
85         STATUS_CONTROL_C_EXIT              = 0xC000013A,
86         STATUS_DLL_INIT_FAILED             = 0xC0000142,
87         STATUS_DLL_INIT_FAILED_LOGOFF      = 0xC000026B,
88 
89         CONTROL_C_EXIT                     = STATUS_CONTROL_C_EXIT,
90 
91         EXCEPTION_ACCESS_VIOLATION         = STATUS_ACCESS_VIOLATION,
92         EXCEPTION_DATATYPE_MISALIGNMENT    = STATUS_DATATYPE_MISALIGNMENT,
93         EXCEPTION_BREAKPOINT               = STATUS_BREAKPOINT,
94         EXCEPTION_SINGLE_STEP              = STATUS_SINGLE_STEP,
95         EXCEPTION_ARRAY_BOUNDS_EXCEEDED    = STATUS_ARRAY_BOUNDS_EXCEEDED,
96         EXCEPTION_FLT_DENORMAL_OPERAND     = STATUS_FLOAT_DENORMAL_OPERAND,
97         EXCEPTION_FLT_DIVIDE_BY_ZERO       = STATUS_FLOAT_DIVIDE_BY_ZERO,
98         EXCEPTION_FLT_INEXACT_RESULT       = STATUS_FLOAT_INEXACT_RESULT,
99         EXCEPTION_FLT_INVALID_OPERATION    = STATUS_FLOAT_INVALID_OPERATION,
100         EXCEPTION_FLT_OVERFLOW             = STATUS_FLOAT_OVERFLOW,
101         EXCEPTION_FLT_STACK_CHECK          = STATUS_FLOAT_STACK_CHECK,
102         EXCEPTION_FLT_UNDERFLOW            = STATUS_FLOAT_UNDERFLOW,
103         EXCEPTION_INT_DIVIDE_BY_ZERO       = STATUS_INTEGER_DIVIDE_BY_ZERO,
104         EXCEPTION_INT_OVERFLOW             = STATUS_INTEGER_OVERFLOW,
105         EXCEPTION_PRIV_INSTRUCTION         = STATUS_PRIVILEGED_INSTRUCTION,
106         EXCEPTION_IN_PAGE_ERROR            = STATUS_IN_PAGE_ERROR,
107         EXCEPTION_ILLEGAL_INSTRUCTION      = STATUS_ILLEGAL_INSTRUCTION,
108         EXCEPTION_NONCONTINUABLE_EXCEPTION = STATUS_NONCONTINUABLE_EXCEPTION,
109         EXCEPTION_STACK_OVERFLOW           = STATUS_STACK_OVERFLOW,
110         EXCEPTION_INVALID_DISPOSITION      = STATUS_INVALID_DISPOSITION,
111         EXCEPTION_GUARD_PAGE               = STATUS_GUARD_PAGE_VIOLATION,
112         EXCEPTION_INVALID_HANDLE           = STATUS_INVALID_HANDLE
113 }
114 
115 enum MAXIMUM_SUPPORTED_EXTENSION = 512;
116 enum size_t EXCEPTION_MAXIMUM_PARAMETERS = 15;
117 enum DWORD EXCEPTION_NONCONTINUABLE      =  1;
118 
119 struct FLOATING_SAVE_AREA {
120     DWORD    ControlWord;
121     DWORD    StatusWord;
122     DWORD    TagWord;
123     DWORD    ErrorOffset;
124     DWORD    ErrorSelector;
125     DWORD    DataOffset;
126     DWORD    DataSelector;
127     BYTE[80] RegisterArea;
128     DWORD    Cr0NpxState;
129 }
130 
131 struct CONTEXT {
132     DWORD ContextFlags;
133     DWORD Dr0;
134     DWORD Dr1;
135     DWORD Dr2;
136     DWORD Dr3;
137     DWORD Dr6;
138     DWORD Dr7;
139     FLOATING_SAVE_AREA FloatSave;
140     DWORD SegGs;
141     DWORD SegFs;
142     DWORD SegEs;
143     DWORD SegDs;
144     DWORD Edi;
145     DWORD Esi;
146     DWORD Ebx;
147     DWORD Edx;
148     DWORD Ecx;
149     DWORD Eax;
150     DWORD Ebp;
151     DWORD Eip;
152     DWORD SegCs;
153     DWORD EFlags;
154     DWORD Esp;
155     DWORD SegSs;
156     BYTE[MAXIMUM_SUPPORTED_EXTENSION] ExtendedRegisters;
157 }
158 
159 alias CONTEXT* PCONTEXT, LPCONTEXT;
160 
161 struct EXCEPTION_RECORD {
162         DWORD ExceptionCode;
163         DWORD ExceptionFlags;
164         EXCEPTION_RECORD* ExceptionRecord;
165         PVOID ExceptionAddress;
166         DWORD NumberParameters;
167         ULONG_PTR[EXCEPTION_MAXIMUM_PARAMETERS] ExceptionInformation;
168 }
169 alias EXCEPTION_RECORD* PEXCEPTION_RECORD, LPEXCEPTION_RECORD;
170 
171 struct EXCEPTION_POINTERS {
172         PEXCEPTION_RECORD ExceptionRecord;
173         PCONTEXT          ContextRecord;
174 }
175 alias EXCEPTION_POINTERS* PEXCEPTION_POINTERS, LPEXCEPTION_POINTERS;
176 
177 enum EXCEPTION_UNWIND = 6;  // Flag to indicate if the system is unwinding
178 /+ Values used by Microsoft for Itanium and Win64 are:
179 #define EXCEPTION_NONCONTINUABLE   0x0001
180 #define EXCEPTION_UNWINDING        0x0002
181 #define EXCEPTION_EXIT_UNWIND      0x0004
182 #define EXCEPTION_STACK_INVALID    0x0008
183 #define EXCEPTION_NESTED_CALL      0x0010
184 #define EXCEPTION_TARGET_UNWIND    0x0020
185 #define EXCEPTION_COLLIDED_UNWIND  0x0040
186 #define EXCEPTION_UNWIND           0x0066
187 
188 @@@ BUG @@@
189 We don't have any guarantee that this bit will remain available. Unfortunately,
190 it seems impossible to implement exception handling at all, without relying on
191 undocumented behaviour in several places.
192 +/
193 enum EXCEPTION_COLLATERAL = 0x100; // Flag used to implement TDPL exception chaining
194 
195 /* Windows Kernel function to initiate a system unwind.
196 
197   Documentation for this function is severely lacking.
198   http://www.nynaeve.net/?p=99 states that the MSDN documentation is incorrect,
199     and gives a corrected form, but it's for x86_64 only.
200   http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was
201     undocumented in 1997.
202   The pExceptRec is what will be passed to the language specific handler.
203   According to MSJ, the targetIp value is unused on Win32.
204   The 'valueForEAX' parameter should always be 0.
205  */
206 extern(Windows)
207 void RtlUnwind(void *targetFrame, void *targetIp, EXCEPTION_RECORD *pExceptRec, void *valueForEAX);
208 
209 extern(C)
210 {
211 extern __gshared DWORD _except_list; // This is just FS:[0]
212 }
213 
214 extern(C)
215 {
216     int _d_isbaseof(ClassInfo b, ClassInfo c);
217     Throwable.TraceInfo _d_traceContext(void* ptr = null);
218     void _d_createTrace(Throwable o, void* context);
219 }
220 
221 
222 /+
223 
224 Implementation of Structured Exception Handling in DMD-Windows
225 
226 Every function which uses exception handling (a 'frame') has a thunk created
227 for it. This thunk is the 'language-specific handler'.
228 The thunks are created in the DMD backend, in nteh_framehandler() in nteh.c.
229 These thunks are of the form:
230       MOV     EAX, &scope_table
231       JMP     __d_framehandler
232 FS:[0] contains a singly linked list of all active handlers (they'll all be
233 thunks). The list is created on the stack.
234 At the end of this list is _except_handler3, a function in the DMC library.
235 It may be unnecessary. I think it is included for compatibility with MSVC
236 exceptions? The function below is useful for debugging.
237 
238 extern(C)
239 EXCEPTION_DISPOSITION _except_handler3(EXCEPTION_RECORD *eRecord,
240     DEstablisherFrame * frame,CONTEXT *context,void *dispatchercontext);
241 
242 // Walk the exception handler chain
243 void printHandlerChain()
244 {
245     DEstablisherFrame *head;
246     asm
247     {
248         mov EAX, FS:[0];
249         mov head, EAX;
250     }
251     while (head && head != cast(DEstablisherFrame *)~0)
252     {
253         printf("%p %p ", head, head.handler);
254         if (head.handler == &unwindCollisionExceptionHandler)
255              printf("UnwindCollisionHandler\n");
256         else if (head.handler == &_except_handler3)
257             printf("excepthandler3\n");
258         else
259         {
260             ubyte *hnd = cast(ubyte *)head.handler;
261             if (hnd[0] == 0xB8 && hnd[5]==0xE9) // mov EAX, xxx; jmp yyy;
262             {
263                 int adr = *cast(int *)(hnd+6);
264                 printf("thunk: frametable=%x adr=%x ", *cast(int *)(hnd+1), hnd + adr+10);
265                 if (cast(void *)(hnd + adr + 10) == &_d_framehandler)
266                     printf("dframehandler\n");
267                 else printf("\n");
268             } else printf("(unknown)\n");
269         }
270         head = head.prev;
271     }
272 }
273 
274 Documentation of Windows SEH is hard to find. Here is a brief explanation:
275 
276 When an exception is raised, the OS calls each handler in the FS:[0] list in
277 turn, looking for a catch block. It continues moving down the list, as long as
278 each handler indicates that it has not caught the exception. When a handler is
279 ready to catch the exception, it calls the OS function RtlUnwind.
280 This calls each function in the FS:[0] list again, this time indicating that it
281 is a 'unwind' call. All of the intervening finally blocks are run at this time.
282 The complicated case is a CollidedException, which happens when a finally block
283 throws an exception. The new exception needs to either replace the old one, or
284 be chained to the old one.
285 
286 The other complexity comes from the fact that a single function may have
287 multiple try/catch/finally blocks. Hence, there's a 'handler table' created for
288 each function which uses exceptions.
289 +/
290 
291 extern(C)
292 {
293     alias
294     EXCEPTION_DISPOSITION function (
295             EXCEPTION_RECORD *exceptionRecord,
296             DEstablisherFrame *frame,
297             CONTEXT *context,
298             void *dispatcherContext) LanguageSpecificHandler;
299 }
300 
301 
302 // The layout of DEstablisherFrame is the same for C++
303 
304 struct DEstablisherFrame
305 {
306     DEstablisherFrame *prev;         // pointer to previous exception list
307     LanguageSpecificHandler handler; // pointer to routine for exception handler
308     DWORD table_index;               // current index into handler_info[]
309     DWORD ebp;                       // this is EBP of routine
310 }
311 
312 struct DHandlerInfo
313 {
314     int prev_index;             // previous table index
315     uint cioffset;              // offset to DCatchInfo data from start of table (!=0 if try-catch)
316     void *finally_code;         // pointer to finally code to execute
317                                 // (!=0 if try-finally)
318 }
319 
320 // Address of DHandlerTable is passed in EAX to _d_framehandler()
321 
322 struct DHandlerTable
323 {
324     void *fptr;                 // pointer to start of function
325     uint espoffset;         // offset of ESP from EBP
326     uint retoffset;         // offset from start of function to return code
327     DHandlerInfo[1] handler_info;
328 }
329 
330 struct DCatchBlock
331 {
332     ClassInfo type;         // catch type
333     uint bpoffset;          // EBP offset of catch var
334     void *code;             // catch handler code
335 }
336 
337 // One of these is created for each try-catch
338 struct DCatchInfo
339 {
340     uint ncatches;                  // number of catch blocks
341     DCatchBlock[1] catch_block;  // data for each catch block
342 }
343 
344 // Macro to make our own exception code
345 template MAKE_EXCEPTION_CODE(int severity, int facility, int exception)
346 {
347     enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception));
348 }
349 enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1);
350 
351 /* Head of a linked list of all exceptions which are in flight.
352  * This is used to implement exception chaining as described in TDPL.
353  * Central to making chaining work correctly is that chaining must only occur
354  * when a collision occurs (not merely when two exceptions are in flight,
355  * because one may be caught before it has any effect on the other).
356  *
357  * The 'ExceptionRecord' member of the EXCEPTION_RECORD struct is used to
358  * store a link to the earlier member on the list.
359  * All exceptions which have found their catch handler are linked into this
360  * list. The exceptions which collided are marked by setting a bit in the
361  * ExceptionFlags. I've called this bit EXCEPTION_COLLATERAL. It has never
362  * been used by Microsoft.
363  *
364  * Every member of the list will either eventually collide with the next earlier
365  * exception, having its EXCEPTION_COLLATERAL bit set, or else will be caught.
366  * If it is caught, a D exception object is created, containing all of the
367  * collateral exceptions.
368  *
369  * There are many subtleties in this design:
370  * (1) The exception records are all on the stack, so it's not possible to
371  * modify them very much. In particular, we have very little choice about how
372  * unwinding works, so we have to leave all the exception records essentially
373  * intact.
374  * (2) The length of an exception record is not constant. System exceptions
375  * are shorter than D exceptions, for example.
376  * (3) System exceptions don't have any space for a pointer to a D object.
377  * So we cannot store the collision information in the exception record.
378  * (4) it's important that this list is fiber-local.
379  */
380 
381 EXCEPTION_RECORD * inflightExceptionList = null;
382 
383 /***********************************
384  * Switch out inflightExceptionList on fiber context switches.
385  */
386 extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc
387 {
388     auto old = inflightExceptionList;
389     inflightExceptionList = cast(EXCEPTION_RECORD*)newContext;
390     return old;
391 }
392 
393 
394 /***********************************
395  * Find the first non-collateral exception in the list. If the last
396  * entry in the list has the EXCEPTION_COLLATERAL bit set, it means
397  * that this fragment will collide with the top exception in the
398  * inflightException list.
399  */
400 EXCEPTION_RECORD *skipCollateralExceptions(EXCEPTION_RECORD *n)
401 {
402     while ( n.ExceptionRecord && n.ExceptionFlags & EXCEPTION_COLLATERAL )
403     {
404         n = n.ExceptionRecord;
405     }
406     return n;
407 }
408 
409 
410 /***********************************
411  * The frame handler, this is called for each frame that has been registered
412  * in the OS except_list.
413  * Input:
414  *      EAX     the handler table for the frame
415  */
416 extern(C)
417 EXCEPTION_DISPOSITION _d_framehandler(
418             EXCEPTION_RECORD *exceptionRecord,
419             DEstablisherFrame *frame,
420             CONTEXT *context,
421             void *dispatcherContext)
422 {
423     DHandlerTable *handlerTable;
424 
425     asm { mov handlerTable,EAX; }
426 
427     if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)
428     {
429          // Call all the finally blocks in this frame
430          _d_local_unwind(handlerTable, frame, -1, &unwindCollisionExceptionHandler);
431     }
432     else
433     {
434         // Jump to catch block if matching one is found
435         int ndx,prev_ndx;
436         DHandlerInfo *phi;
437         DCatchInfo *pci;
438         DCatchBlock *pcb;
439         uint ncatches;              // number of catches in the current handler
440 
441         /* The Master or Boss exception controls which catch () clause will
442          * catch the exception. If all collateral exceptions are derived from
443          * Exception, the boss is the first exception thrown. Otherwise,
444          * the first Error is the boss.
445          * But, if an Error (or non-Exception Throwable) is thrown as a collateral
446          * exception, it will take priority over an Exception.
447          */
448         EXCEPTION_RECORD * master = null; // The Master exception.
449         ClassInfo masterClassInfo;       // Class info of the Master exception.
450 
451         masterClassInfo = null;           // only compute it if we need it
452 
453         // walk through handler table, checking each handler
454         // with an index smaller than the current table_index
455         for (ndx = frame.table_index; ndx != -1; ndx = prev_ndx)
456         {
457             phi = &handlerTable.handler_info.ptr[ndx];
458             prev_ndx = phi.prev_index;
459             if (phi.cioffset)
460             {
461                 // this is a catch handler (no finally)
462                 pci = cast(DCatchInfo *)(cast(ubyte *)handlerTable + phi.cioffset);
463                 ncatches = pci.ncatches;
464 
465                 foreach (i; 0..ncatches)
466                 {
467                     pcb = &pci.catch_block.ptr[i];
468                     int match = 0;
469                     EXCEPTION_RECORD * er = exceptionRecord;
470                     // We need to check all the collateral exceptions.
471                     for (;;)
472                     {
473                         if (er.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION)
474                         {
475                             // printf("ei[0] = %p\n", er.ExceptionInformation[0]);
476                             ClassInfo ci = (**(cast(ClassInfo **)(er.ExceptionInformation[0])));
477                             // If we've reached the oldest exception without
478                             // finding an Error, this one must be the master.
479                             if (!master && !(er.ExceptionFlags & EXCEPTION_COLLATERAL))
480                             {
481                                 master = er;
482                                 masterClassInfo = ci;
483                                 break;
484                             }
485                             if (_d_isbaseof(ci, typeid(Error)))
486                             {   // It's derived from Error. This _may_ be the master.
487                                 master = er;
488                                 masterClassInfo = ci;
489                             } // Else it's a collateral Exception
490                         }
491                         else
492                         {   // Non-D exception. It will become an Error.
493                             masterClassInfo = typeid(Error);
494                             master = er;
495                         }
496                         // End the loop if this was the original exception
497                         if (! (er.ExceptionFlags & EXCEPTION_COLLATERAL))
498                             break;
499 
500                         // Now get the next collateral exception.
501                         if (er.ExceptionRecord)
502                             er = er.ExceptionRecord;
503                         else // It is collateral for an existing exception chain
504                              // for which we've already found the catch{}. It is
505                              // possible that the new collateral makes the old catch
506                              // invalid.
507                             er = inflightExceptionList;
508                     }
509                     if (_d_isbaseof(masterClassInfo, pcb.type))
510                     {
511                         // Matched the catch type, so we've found the catch
512                         // handler for this exception.
513                         // BEWARE: We don't yet know if the catch handler will
514                         // actually be executed. If there's an unwind collision,
515                         // this call may be abandoned: the calls to
516                         // _global_unwind and _local_unwind may never return,
517                         // and the contents of the local variables will be lost.
518 
519                         // We need to add this exception to the list of in-flight
520                         // exceptions, in case something collides with it.
521                         EXCEPTION_RECORD * originalException = skipCollateralExceptions(exceptionRecord);
522                         if (originalException.ExceptionRecord is null
523                             && !(exceptionRecord is inflightExceptionList))
524                         {
525                             originalException.ExceptionRecord = inflightExceptionList;
526                         }
527                         inflightExceptionList = exceptionRecord;
528 
529                         // Have system call all finally blocks in intervening frames
530                         _d_global_unwind(frame, exceptionRecord);
531 
532                         // Call all the finally blocks skipped in this frame
533                         _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler);
534 
535 
536                         frame.table_index = prev_ndx;  // we are out of this handler
537 
538                         // Now create the D exception from the SEH exception record chain.
539                         EXCEPTION_RECORD * z = exceptionRecord;
540                         Throwable prev = null;
541                         Error masterError = null;
542                         Throwable pti;
543 
544                         for (;;)
545                         {
546                             Throwable w = _d_translate_se_to_d_exception(z, context);
547                             if (z == master && (z.ExceptionFlags & EXCEPTION_COLLATERAL))
548                             {   // if it is a short-circuit master, save it
549                                 masterError = cast(Error)w;
550                             }
551                             prev = Throwable.chainTogether(w, prev);
552                             if (!(z.ExceptionFlags & EXCEPTION_COLLATERAL))
553                                 break;
554                             z = z.ExceptionRecord;
555                         }
556                         // Reached the end. Now add the Master, if any.
557                         if (masterError)
558                         {
559                             masterError.bypassedException = prev;
560                             pti = masterError;
561                         }
562                         else
563                         {
564                             pti = prev;
565                         }
566                         // Pop the exception from the list of in-flight exceptions
567                         inflightExceptionList = z.ExceptionRecord;
568 
569                         int regebp;
570                         // Initialize catch variable
571                         regebp = cast(int)&frame.ebp;              // EBP for this frame
572                         *cast(Object *)(regebp + (pcb.bpoffset)) = pti;
573 
574                         // Jump to catch block. Does not return.
575                         {
576                             uint catch_esp;
577                             alias void function() fp_t; // generic function pointer
578                             fp_t catch_addr = cast(fp_t)(pcb.code);
579                             catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof;
580                             asm
581                             {
582                                 mov     EAX,catch_esp;
583                                 mov     ECX,catch_addr;
584                                 mov     [EAX],ECX;
585                                 mov     EBP,regebp;
586                                 mov     ESP,EAX;         // reset stack
587                                 ret;                     // jump to catch block
588                             }
589                         }
590                     }
591                 }
592             }
593         }
594     }
595     return EXCEPTION_DISPOSITION.ExceptionContinueSearch;
596 }
597 
598 /***********************************
599  * Exception filter for use in __try..__except block
600  * surrounding call to Dmain()
601  */
602 
603 int _d_exception_filter(EXCEPTION_POINTERS *eptrs,
604                         int retval,
605                         Object *exceptionObject)
606 {
607     *exceptionObject = _d_translate_se_to_d_exception(eptrs.ExceptionRecord, eptrs.ContextRecord);
608     return retval;
609 }
610 
611 /***********************************
612  * Throw a D instance of Throwable.
613  */
614 
615 private void throwImpl(Throwable h)
616 {
617     //printf("_d_throw(h = %p, &h = %p)\n", h, &h);
618     //printf("\tvptr = %p\n", *(void **)h);
619 
620     /* Increment reference count if `h` is a refcounted Throwable
621      */
622     auto refcount = h.refcount();
623     if (refcount)       // non-zero means it's refcounted
624         h.refcount() = refcount + 1;
625 
626     _d_createTrace(h, null);
627     RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION,
628                    EXCEPTION_NONCONTINUABLE,
629                    1, cast(void *)&h);
630 }
631 
632 /***************************************
633  * The compiler converts:
634  *      throw h;
635  * into a call to:
636  *      _d_throwc(h);
637  */
638 extern(C) void _d_throwc(Throwable h)
639 {
640     // set up a stack frame for trace unwinding
641     version (AsmX86)
642     {
643         asm
644         {
645             naked;
646             enter 0, 0;
647         }
648         version (D_InlineAsm_X86)
649             asm { mov EAX, [EBP+8]; }
650         asm
651         {
652             call throwImpl;
653             leave;
654             ret;
655         }
656     }
657     else
658     {
659         throwImpl(h);
660     }
661 }
662 
663 /***********************************
664  * Converts a Windows Structured Exception code to a D Throwable Object.
665  */
666 
667 Throwable _d_translate_se_to_d_exception(EXCEPTION_RECORD *exceptionRecord, CONTEXT* context)
668 {
669     Throwable pti;
670    // BUG: what if _d_newclass() throws an out of memory exception?
671 
672     switch (exceptionRecord.ExceptionCode) {
673         case STATUS_DIGITAL_MARS_D_EXCEPTION:
674             // Generated D exception
675             pti = cast(Throwable)cast(void *)(exceptionRecord.ExceptionInformation[0]);
676             break;
677 
678         case STATUS_INTEGER_DIVIDE_BY_ZERO:
679             pti = new Error("Integer Divide by Zero");
680             break;
681 
682         case STATUS_INTEGER_OVERFLOW: // eg, int.min % -1
683             pti = new Error("Integer Overflow");
684             break;
685 
686         case STATUS_FLOAT_DIVIDE_BY_ZERO:
687             pti = new Error("Float Divide by Zero");
688             break;
689 
690         case STATUS_ACCESS_VIOLATION:
691             pti = new Error("Access Violation");
692             break;
693 
694         case STATUS_STACK_OVERFLOW:
695             pti = new Error("Stack Overflow");
696             break;
697 
698         case STATUS_DATATYPE_MISALIGNMENT:
699             pti = new Error("Datatype Misalignment");
700             break;
701 
702         case STATUS_ARRAY_BOUNDS_EXCEEDED:
703             pti = new Error("Array Bounds Exceeded");
704             break;
705 
706         case STATUS_FLOAT_INVALID_OPERATION:
707             pti = new Error("Invalid Floating Point Operation");
708             break;
709 
710         case STATUS_FLOAT_DENORMAL_OPERAND:
711             pti = new Error("Floating Point Denormal Operand");
712             break;
713 
714         case STATUS_FLOAT_INEXACT_RESULT:
715             pti = new Error("Floating Point Inexact Result");
716             break;
717 
718         case STATUS_FLOAT_OVERFLOW:
719             pti = new Error("Floating Point Overflow");
720             break;
721 
722         case STATUS_FLOAT_UNDERFLOW:
723             pti = new Error("Floating Point Underflow");
724             break;
725 
726         case STATUS_FLOAT_STACK_CHECK:
727             pti = new Error("Floating Point Stack Check");
728             break;
729 
730         case STATUS_PRIVILEGED_INSTRUCTION:
731             if (*(cast(ubyte *)(exceptionRecord.ExceptionAddress))==0xF4) { // HLT
732                 pti = new Error("assert(0) or HLT instruction");
733             } else {
734                 pti = new Error("Privileged Instruction");
735             }
736             break;
737 
738         case STATUS_ILLEGAL_INSTRUCTION:
739             pti = new Error("Illegal Instruction");
740             break;
741 
742         case STATUS_BREAKPOINT:
743             pti = new Error("Breakpoint");
744             break;
745 
746         case STATUS_IN_PAGE_ERROR:
747             pti = new Error("Win32 In Page Exception");
748             break;
749 /*
750         case STATUS_INVALID_DISPOSITION:
751         case STATUS_NONCONTINUABLE_EXCEPTION:
752         case STATUS_SINGLE_STEP:
753                 case DBG_CONTROL_C: // only when a debugger is attached
754         // In DMC, but not in Microsoft docs
755         case STATUS_GUARD_PAGE_VIOLATION:
756         case STATUS_INVALID_HANDLE:
757 */
758         // convert all other exception codes into a Win32Exception
759         default:
760             pti = new Error("Win32 Exception");
761             break;
762     }
763     _d_createTrace(pti, context);
764     return pti;
765 }
766 
767 /*
768 These next two functions are necessary for dealing with collided exceptions:
769 when an exception has been thrown during unwinding. This happens for example
770 when a throw statement was encountered inside a finally clause.
771 
772 'frame' is the stack pointer giving the state we were in, when we made
773 the call to RtlUnwind.
774 When we return ExceptionCollidedUnwind, the OS initiates a new SEARCH
775 phase, using the new exception, and it begins this search from the frame we
776 provide in the 'dispatcherContext' output parameter.
777 We change the target frame pointer, by changing dispatcherContext. After this, we'll be
778 back at the start of a SEARCH phase, so we need cancel all existing operations.
779 There are two types of possible collisions.
780 (1) collision during a local unwind. That is, localunwind was called during the
781 SEARCH phase (without going through an additional call to RtlUnwind).
782 We need to cancel the original search pass, so we'll restart from 'frame'.
783 (2) collision during a global unwind. That is, localunwind was called from the UNWIND phase.
784 We need to cancel the unwind pass, AND we need to cancel the search pass that initiated it.
785 So, we need to restart from 'frame.prev'.
786 */
787 
788 extern (C)
789 EXCEPTION_DISPOSITION searchCollisionExceptionHandler(
790             EXCEPTION_RECORD *exceptionRecord,
791             DEstablisherFrame *frame,
792             CONTEXT *context,
793             void *dispatcherContext)
794 {
795     if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))
796     {
797         // Mark this as a collateral exception
798         EXCEPTION_RECORD * n = skipCollateralExceptions(exceptionRecord);
799         n.ExceptionFlags |= EXCEPTION_COLLATERAL;
800 
801         return EXCEPTION_DISPOSITION.ExceptionContinueSearch;
802     }
803 
804     // An exception has been thrown during unwinding.
805     // It happened during the SEARCH phase.
806     // We need to cancel the original search pass, so we'll restart from 'frame'.
807     *(cast(void **)dispatcherContext) = frame;
808     return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind;
809 }
810 
811 extern(C)
812 EXCEPTION_DISPOSITION unwindCollisionExceptionHandler(
813             EXCEPTION_RECORD *exceptionRecord,
814             DEstablisherFrame *frame,
815             CONTEXT *context,
816             void *dispatcherContext)
817 {
818     if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))
819     {
820         // Mark this as a collateral exception
821         EXCEPTION_RECORD * n = skipCollateralExceptions(exceptionRecord);
822         n.ExceptionFlags |= EXCEPTION_COLLATERAL;
823         return EXCEPTION_DISPOSITION.ExceptionContinueSearch;
824     }
825     // An exception has been thrown during unwinding.
826     // It happened during the UNWIND phase.
827     // We need to cancel the unwind pass, AND we need to cancel the search
828     // pass that initiated the unwind. So, we need to restart from 'frame.prev'.
829     *(cast(void **)dispatcherContext) = frame.prev;
830     return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind;
831 }
832 
833 /**************************************
834  * Call finally blocks in the current stack frame until stop_index.
835  * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c
836  */
837 extern(C)
838 void _d_local_unwind(DHandlerTable *handler_table,
839         DEstablisherFrame *frame, int stop_index, LanguageSpecificHandler collisionHandler)
840 {
841     DHandlerInfo *phi;
842     DCatchInfo *pci;
843     int i;
844     // Set up a special exception handler to catch double-fault exceptions.
845     asm
846     {
847         push    dword ptr -1;
848         push    dword ptr 0;
849         push    collisionHandler;
850         push    dword ptr FS:_except_list;
851         mov     FS:_except_list,ESP;
852     }
853     for (i = frame.table_index; i != -1 && i != stop_index; i = phi.prev_index)
854     {
855         phi = &handler_table.handler_info.ptr[i];
856         if (phi.finally_code)
857         {
858             // Note that it is unnecessary to adjust the ESP, as the finally block
859             // accesses all items on the stack as relative to EBP.
860 
861             DWORD *catch_ebp = &frame.ebp;
862             void *blockaddr = phi.finally_code;
863 
864             asm
865             {
866                 push    EBX;
867                 mov     EBX,blockaddr;
868                 push    EBP;
869                 mov     EBP,catch_ebp;
870                 call    EBX;
871                 pop     EBP;
872                 pop     EBX;
873             }
874         }
875     }
876 
877     asm
878     {
879         pop     FS:_except_list;
880         add     ESP,12;
881     }
882 }
883 
884 /+ According to http://www.microsoft.com/msj/0197/exception/exception.aspx,
885 global unwind is just a thin wrapper around RtlUnwind.
886 __global_unwind(void * pRegistFrame)
887  {
888      _RtlUnwind( pRegistFrame,
889                  &__ret_label,
890                  0, 0 );
891      __ret_label:
892   }
893 Apparently Win32 doesn't use the return address anyway.
894 
895 This code seems to be calling RtlUnwind( pFrame, &__retlabel, eRecord, 0);
896 +/
897 extern(C)
898 int _d_global_unwind(DEstablisherFrame *pFrame, EXCEPTION_RECORD *eRecord)
899 {
900     asm {
901         naked;
902         push EBP;
903         mov EBP,ESP;
904         push ECX;
905         push EBX;
906         push ESI;
907         push EDI;
908         push EBP;
909         push 0;
910         push dword ptr 12[EBP]; //eRecord
911         call __system_unwind;
912         jmp __unwind_exit;
913  __system_unwind:
914         push dword ptr 8[EBP]; // pFrame
915         call RtlUnwind;
916  __unwind_exit:
917         pop EBP;
918         pop     EDI;
919         pop     ESI;
920         pop     EBX;
921         pop ECX;
922         mov     ESP,EBP;
923         pop EBP;
924         ret;
925     }
926 }
927 
928 /***********************************
929  * external version of the unwinder
930  * This is used for 'goto' or 'return', to run any finally blocks
931  * which were skipped.
932  */
933 extern(C)
934 void _d_local_unwind2()
935 {
936     asm
937     {
938         naked;
939         jmp     _d_localUnwindForGoto;
940     }
941 }
942 
943 extern(C)
944 void _d_localUnwindForGoto(DHandlerTable *handler_table,
945         DEstablisherFrame *frame, int stop_index)
946 {
947     _d_local_unwind(handler_table, frame, stop_index, &searchCollisionExceptionHandler);
948 }
949 
950 /***********************************
951  * The frame handler, this is called for each frame that has been registered
952  * in the OS except_list.
953  * Input:
954  *      EAX     the handler table for the frame
955  */
956 
957 extern(C)
958 EXCEPTION_DISPOSITION _d_monitor_handler(
959             EXCEPTION_RECORD *exceptionRecord,
960             DEstablisherFrame *frame,
961             CONTEXT *context,
962             void *dispatcherContext)
963 {
964     if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)
965     {
966         _d_monitorexit(cast(Object)cast(void *)frame.table_index);
967     }
968     else
969     {
970     }
971     return EXCEPTION_DISPOSITION.ExceptionContinueSearch;
972 }
973 
974 /***********************************
975  */
976 extern(C)
977 void _d_monitor_prolog(void *x, void *y, Object h)
978 {
979     asm
980     {
981         push    EAX;
982     }
983     //printf("_d_monitor_prolog(x=%p, y=%p, h=%p)\n", x, y, h);
984     _d_monitorenter(h);
985     asm
986     {
987         pop     EAX;
988     }
989 }
990 
991 /***********************************
992  */
993 extern(C)
994 void _d_monitor_epilog(void *x, void *y, Object h)
995 {
996     //printf("_d_monitor_epilog(x=%p, y=%p, h=%p)\n", x, y, h);
997     asm
998     {
999         push    EAX;
1000         push    EDX;
1001     }
1002     _d_monitorexit(h);
1003     asm
1004     {
1005         pop     EDX;
1006         pop     EAX;
1007     }
1008 }