Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 482

Show
Ignore:
Timestamp:
01/07/11 22:11:15 (14 years ago)
Author:
Don Clugston
Message:

1513 try/catch/finally misbehavior on windows

This one's for Brad.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/rt/deh.d

    r475 r482  
    170170    and gives a corrected form, but it's for x86_64 only. 
    171171  http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was 
    172172    undocumented in 1997. 
    173173  The pExceptRec is what will be passed to the language specific handler. 
    174174  According to MSJ, the targetIp value is unused on Win32. 
    175175  The 'valueForEAX' parameter should always be 0. 
    176176 */ 
    177177extern(Windows) 
    178178void RtlUnwind(void *targetFrame, void *targetIp, EXCEPTION_RECORD *pExceptRec, void *valueForEAX); 
    179179 
    180 alias int function() fp_t; // function pointer in ambient memory model 
    181  
    182180extern(C) 
    183181{  
    184182extern __gshared DWORD _except_list; // This is just FS:[0] 
    185183} 
    186184 
    187185extern(C) 
    188186{ 
    189187void _d_setUnhandled(Object); 
    190188void _d_createTrace(Object); 
    191189int _d_isbaseof(ClassInfo b, ClassInfo c); 
     
    223221is a 'unwind' call. All of the intervening finally blocks are run at this time. 
    224222The complicated case is a CollidedException, which happens when a finally block 
    225223throws an exception. The new exception needs to either replace the old one, or 
    226224be chained to the old one. 
    227225 
    228226The other complexity comes from the fact that a single function may have 
    229227multiple try/catch/finally blocks. Hence, there's a 'handler table' created for 
    230228each function which uses exceptions. 
    231229+/ 
    232230 
     231extern(C) 
     232{ 
     233    alias  
     234    EXCEPTION_DISPOSITION function ( 
     235            EXCEPTION_RECORD *exceptionRecord, 
     236            DEstablisherFrame *frame, 
     237            CONTEXT *context, 
     238            void *dispatcherContext) LanguageSpecificHandler; 
     239} 
     240 
     241 
    233242// The layout of DEstablisherFrame is the same for C++ 
    234243 
    235244struct DEstablisherFrame 
    236245{ 
    237     void *prev;                 // pointer to previous exception list 
    238     void *handler;              // pointer to routine for exception handler 
    239     DWORD table_index;          // current index into handler_info[] 
    240     DWORD ebp;                  // this is EBP of routine 
     246    DEstablisherFrame *prev;         // pointer to previous exception list 
     247    LanguageSpecificHandler handler; // pointer to routine for exception handler 
     248    DWORD table_index;               // current index into handler_info[] 
     249    DWORD ebp;                       // this is EBP of routine 
    241250}; 
    242251 
    243252struct DHandlerInfo 
    244253{ 
    245254    int prev_index;             // previous table index 
    246255    uint cioffset;              // offset to DCatchInfo data from start of table (!=0 if try-catch) 
    247256    void *finally_code;         // pointer to finally code to execute 
    248257                                // (!=0 if try-finally) 
    249258}; 
    250259 
     
    294301            CONTEXT *context, 
    295302            void *dispatcherContext) 
    296303{ 
    297304    DHandlerTable *handlerTable; 
    298305 
    299306    asm { mov handlerTable,EAX; } 
    300307 
    301308    if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 
    302309    { 
    303310         // Call all the finally blocks in this frame 
    304          _d_local_unwind(handlerTable, frame, -1); 
     311         _d_local_unwind(handlerTable, frame, -1, &unwindCollisionExceptionHandler); 
    305312    } 
    306313    else 
    307314    { 
    308315        // Jump to catch block if matching one is found 
    309316        int ndx,prev_ndx,i; 
    310317        DHandlerInfo *phi; 
    311318        DCatchInfo *pci; 
    312319        DCatchBlock *pcb; 
    313320        uint ncatches;              // number of catches in the current handler 
    314321        Object pti; 
     
    352359                        // Initialize catch variable 
    353360                        regebp = cast(int)&frame.ebp;              // EBP for this frame 
    354361                        *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 
    355362 
    356363                        _d_setUnhandled(pti); 
    357364 
    358365                        // Have system call all finally blocks in intervening frames 
    359366                        _d_global_unwind(frame, exceptionRecord); 
    360367 
    361368                        // Call all the finally blocks skipped in this frame 
    362                         _d_local_unwind(handlerTable, frame, ndx); 
     369                        _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 
    363370 
    364371                        _d_setUnhandled(null); 
    365372 
    366373                        frame.table_index = prev_ndx;  // we are out of this handler 
    367374 
    368375                        // Jump to catch block. Does not return. 
    369376                        { 
    370377                            uint catch_esp; 
    371                             fp_t catch_addr; 
    372  
    373                             catch_addr = cast(fp_t)(pcb.code); 
     378                            alias void function() fp_t; // generic function pointer 
     379                            fp_t catch_addr = cast(fp_t)(pcb.code); 
    374380                            catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof; 
    375381                            asm 
    376382                            { 
    377383                                mov     EAX,catch_esp; 
    378384                                mov     ECX,catch_addr; 
    379385                                mov     [EAX],ECX; 
    380386                                mov     EBP,regebp; 
    381387                                mov     ESP,EAX;         // reset stack 
    382388                                ret;                     // jump to catch block 
    383389                            } 
     
    512518*/ 
    513519        // convert all other exception codes into a Win32Exception 
    514520        default: 
    515521            pti = new Error("Win32 Exception"); 
    516522            break; 
    517523    } 
    518524    _d_createTrace(pti); 
    519525    return pti; 
    520526} 
    521527 
    522 /+ 
    523   These next two functions are necessary for dealing with collided exceptions: 
    524   when an exception has been thrown during unwinding. This happens for example 
    525   when a throw statement was encountered inside a finally clause. 
    526 +/ 
     528/* 
     529These next two functions are necessary for dealing with collided exceptions: 
     530when an exception has been thrown during unwinding. This happens for example 
     531when a throw statement was encountered inside a finally clause. 
     532 
     533'frame' is the stack pointer giving the state we were in, when we made 
     534the call to RtlUnwind. 
     535When we return ExceptionCollidedUnwind, the OS initiates a new SEARCH 
     536phase, using the new exception, and it begins this search from the frame we 
     537provide in the 'dispatcherContext' output parameter. 
     538We change the target frame pointer, by changing dispatcherContext. After this, we'll be 
     539back at the start of a SEARCH phase, so we need cancel all existing operations. 
     540There are two types of possible collisions. 
     541(1) collision during a local unwind. That is, localunwind was called during the 
     542SEARCH phase (without going through an additional call to RtlUnwind). 
     543We need to cancel the original search pass, so we'll restart from 'frame'. 
     544(2) collision during a global unwind. That is, localunwind was called from the UNWIND phase. 
     545We need to cancel the unwind pass, AND we need to cancel the search pass that initiated it. 
     546So, we need to restart from 'frame.prev'. 
     547*/ 
     548 
     549extern (C) 
     550EXCEPTION_DISPOSITION searchCollisionExceptionHandler( 
     551            EXCEPTION_RECORD *exceptionRecord, 
     552            DEstablisherFrame *frame, 
     553            CONTEXT *context, 
     554            void *dispatcherContext) 
     555
     556    if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))  
     557        return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
     558     
     559    // An exception has been thrown during unwinding. 
     560    // It happened during the SEARCH phase. 
     561    // We need to cancel the original search pass, so we'll restart from 'frame'. 
     562    *(cast(void **)dispatcherContext) = frame; 
     563    return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 
     564
    527565 
    528566extern(C) 
    529567EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( 
    530568            EXCEPTION_RECORD *exceptionRecord, 
    531569            DEstablisherFrame *frame, 
    532570            CONTEXT *context, 
    533571            void *dispatcherContext) 
    534572{ 
    535573    if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))  
    536574        return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
    537     // An exception has been thrown during unwinding (eg, a throw statement 
    538     //   was encountered inside a finally clause)
    539     // The target for unwinding needs to change.  
    540     // Based on the code for RtlUnwind in http://www.microsoft.com/msj/0197/exception/exception.aspx, 
    541     // the dispatcherContext is used to set the EXCEPTION_REGISTRATION to be used from now on
    542     *(cast(DEstablisherFrame **)dispatcherContext) = frame
     575     
     576    // An exception has been thrown during unwinding
     577    // It happened during the UNWIND phase. 
     578    // We need to cancel the unwind pass, AND we need to cancel the search 
     579    // pass that initiated the unwind. So, we need to restart from 'frame.prev'
     580    *(cast(void **)dispatcherContext) = frame.prev
    543581    return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 
    544582} 
    545583 
    546584/************************************** 
    547585 * Call finally blocks in the current stack frame until stop_index. 
    548586 * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c 
    549587 */ 
    550588extern(C) 
    551589void _d_local_unwind(DHandlerTable *handler_table, 
    552         DEstablisherFrame *frame, int stop_index
     590        DEstablisherFrame *frame, int stop_index, LanguageSpecificHandler collisionHandler
    553591{ 
    554592    DHandlerInfo *phi; 
    555593    DCatchInfo *pci; 
    556594    int i; 
    557595    // Set up a special exception handler to catch double-fault exceptions. 
    558596    asm 
    559597    { 
    560598        push    dword ptr -1; 
    561599        push    dword ptr 0; 
    562         push    offset unwindCollisionExceptionHandler; 
     600        push    collisionHandler; 
    563601        push    dword ptr FS:_except_list; 
    564602        mov     FS:_except_list,ESP; 
    565603    } 
    566604    for (i = frame.table_index; i != -1 && i != stop_index; i = phi.prev_index) 
    567605    { 
    568606        phi = &handler_table.handler_info[i]; 
    569607        if (phi.finally_code) 
    570608        { 
    571609            // Note that it is unnecessary to adjust the ESP, as the finally block 
    572610            // accesses all items on the stack as relative to EBP.