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 }