| | 163 | |
|---|
| | 164 | /* Windows Kernel function to initiate a system unwind. |
|---|
| | 165 | |
|---|
| | 166 | Documentation for this function is severely lacking. |
|---|
| | 167 | http://www.nynaeve.net/?p=99 states that the MSDN documentation is incorrect, |
|---|
| | 168 | and gives a corrected form, but it's for x86_64 only. |
|---|
| | 169 | http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was |
|---|
| | 170 | undocumented in 1997. |
|---|
| | 171 | Presumably DMC reverse-engineered it, because it got the signature of this |
|---|
| | 172 | function wrong!! |
|---|
| | 173 | The pExceptRec is what will be passed to the language specific handler. |
|---|
| | 174 | According to MSJ, the targetIp value is unused on Win32. |
|---|
| | 175 | The 'valueForEAX' parameter should always be 0. |
|---|
| | 176 | */ |
|---|
| | 177 | extern(Windows) |
|---|
| | 178 | void RtlUnwind(void *targetFrame, void *targetIp, EXCEPTION_RECORD *pExceptRec, void *valueForEAX); |
|---|
| | 481 | /+ |
|---|
| | 482 | These next two functions are necessary for dealing with collided exceptions: |
|---|
| | 483 | when an exception has been thrown during unwinding. This happens for example |
|---|
| | 484 | when a throw statement was encountered inside a finally clause. |
|---|
| | 485 | |
|---|
| | 486 | A description of the equivalent situation for x86_64 is given at |
|---|
| | 487 | http://www.nynaeve.net/?p=107, which states: |
|---|
| | 488 | This, in effect, requires that the compiler stop the current unwind operation |
|---|
| | 489 | and transfer control to the target location (which is usually within the |
|---|
| | 490 | function that contained the __finally that the programmer jumped out of). |
|---|
| | 491 | Nevertheless, the compiler still has to deal with the fact that it has been |
|---|
| | 492 | called in the context of an unwind operation, and as such it needs a way |
|---|
| | 493 | to 'break out' of the unwind call stack. This is done by executing a |
|---|
| | 494 | 'local unwind', or an unwind to a location within the current function. |
|---|
| | 495 | In order to do this, the compiler calls a small, runtime-supplied helper |
|---|
| | 496 | function known as local_unwind. This function is described below, and is essentially |
|---|
| | 497 | an extremely thin wrapper around RtlUnwindEx that, in practice, adds no value other |
|---|
| | 498 | than providing some default argument values (and scratch space on the stack |
|---|
| | 499 | for RtlUnwindEx to use to store a CONTEXT structure). |
|---|
| | 500 | [snip brief asm code] |
|---|
| | 501 | When the compiler calls local_unwind as a result of the programmer |
|---|
| | 502 | breaking out of a __finally block in some fashion, then execution will |
|---|
| | 503 | eventually end up in RtlUnwindEx. From there, RtlUnwindEx eventually |
|---|
| | 504 | detects the operation as a collided unwind, once it unwinds past the |
|---|
| | 505 | original call to the original unwind handler that started the new unwind |
|---|
| | 506 | operation via local_unwind. |
|---|
| | 507 | |
|---|
| | 508 | +/ |
|---|
| | 509 | |
|---|
| | 510 | extern(C) |
|---|
| | 511 | EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( |
|---|
| | 512 | EXCEPTION_RECORD *ExceptionRecord, |
|---|
| | 513 | DEstablisherFrame *frame, |
|---|
| | 514 | CONTEXT *context, |
|---|
| | 515 | void *dispatcherContext) |
|---|
| | 516 | { |
|---|
| | 517 | if (!(ExceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) |
|---|
| | 518 | return EXCEPTION_DISPOSITION.ExceptionContinueSearch; |
|---|
| | 519 | // An exception has been thrown during unwinding (eg, a throw statement |
|---|
| | 520 | // was encountered inside a finally clause). |
|---|
| | 521 | // The target for unwinding needs to change. |
|---|
| | 522 | // Based on the code for RtlUnwind in http://www.microsoft.com/msj/0197/exception/exception.aspx, |
|---|
| | 523 | // the dispatcherContext is used to set the EXCEPTION_REGISTRATION to be used from now on. |
|---|
| | 524 | *(cast(DEstablisherFrame **)dispatcherContext) = frame; |
|---|
| | 525 | return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; |
|---|
| | 526 | } |
|---|
| | 527 | |
|---|
| | 579 | /+ According to http://www.microsoft.com/msj/0197/exception/exception.aspx, |
|---|
| | 580 | global unwind is just a thin wrapper around RtlUnwind. |
|---|
| | 581 | __global_unwind(void * pRegistFrame) |
|---|
| | 582 | { |
|---|
| | 583 | _RtlUnwind( pRegistFrame, |
|---|
| | 584 | &__ret_label, |
|---|
| | 585 | 0, 0 ); |
|---|
| | 586 | __ret_label: |
|---|
| | 587 | |
|---|
| | 588 | This code seems to be calling RtlUnwind( pFrame, &__retlabel, eRecord, 0); |
|---|
| | 589 | Apparently Win32 doesn't use the return address anyway. |
|---|
| | 590 | } |
|---|
| | 591 | |
|---|
| | 592 | +/ |
|---|
| | 593 | extern(C) |
|---|
| | 594 | int _d_global_unwind(DEstablisherFrame *pFrame, EXCEPTION_RECORD *eRecord) |
|---|
| | 595 | { |
|---|
| | 596 | asm { |
|---|
| | 597 | naked; |
|---|
| | 598 | push EBP; |
|---|
| | 599 | mov EBP,ESP; |
|---|
| | 600 | push ECX; |
|---|
| | 601 | push EBX; |
|---|
| | 602 | push ESI; |
|---|
| | 603 | push EDI; |
|---|
| | 604 | push EBP; |
|---|
| | 605 | push 0; |
|---|
| | 606 | push dword ptr 12[EBP]; //eRecord |
|---|
| | 607 | call __system_unwind; |
|---|
| | 608 | jmp __unwind_exit; |
|---|
| | 609 | __system_unwind: |
|---|
| | 610 | push dword ptr 8[EBP]; // pFrame |
|---|
| | 611 | call RtlUnwind; |
|---|
| | 612 | __unwind_exit: |
|---|
| | 613 | pop EBP; |
|---|
| | 614 | pop EDI; |
|---|
| | 615 | pop ESI; |
|---|
| | 616 | pop EBX; |
|---|
| | 617 | pop ECX; |
|---|
| | 618 | mov ESP,EBP; |
|---|
| | 619 | pop EBP; |
|---|
| | 620 | ret; |
|---|
| | 621 | } |
|---|
| | 622 | } |
|---|
| | 623 | |
|---|