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

Changeset 459

Show
Ignore:
Timestamp:
12/28/10 13:22:12 (14 years ago)
Author:
Don Clugston
Message:

Remove dependence on the C stdlib for exception handling. This is a necessary step towards fixing bug 1513.

Files:

Legend:

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

    r458 r459  
    152152    DWORD[EXCEPTION_MAXIMUM_PARAMETERS] ExceptionInformation; 
    153153} 
    154154alias EXCEPTION_RECORD* PEXCEPTION_RECORD, LPEXCEPTION_RECORD; 
    155155 
    156156struct EXCEPTION_POINTERS { 
    157157    PEXCEPTION_RECORD ExceptionRecord; 
    158158    PCONTEXT          ContextRecord; 
    159159} 
    160160alias EXCEPTION_POINTERS* PEXCEPTION_POINTERS, LPEXCEPTION_POINTERS; 
    161161 
    162  
    163 extern(C) 
    164 { 
    165 /*** From Digital Mars C runtime library ***/ 
    166 EXCEPTION_DISPOSITION _local_except_handler (EXCEPTION_RECORD *ExceptionRecord, 
    167     void* EstablisherFrame, 
    168         void *ContextRecord, 
    169         void *DispatcherContext 
    170         ); 
    171 void _global_unwind(void *frame,EXCEPTION_RECORD *eRecord); 
    172 } 
    173162enum EXCEPTION_UNWIND = 6;  // Flag to indicate if the system is unwinding 
     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 */ 
     177extern(Windows) 
     178void RtlUnwind(void *targetFrame, void *targetIp, EXCEPTION_RECORD *pExceptRec, void *valueForEAX); 
    174179 
    175180alias int function() fp_t; // function pointer in ambient memory model 
    176181 
    177182extern(C) 
    178183{  
    179184extern __gshared DWORD _except_list; 
    180185} 
    181186 
    182187extern(C) 
    183188{ 
     
    303308 
    304309                        pti = _d_translate_se_to_d_exception(exception_record); 
    305310 
    306311                        // Initialize catch variable 
    307312                        regebp = cast(int)&frame.ebp;              // EBP for this frame 
    308313                        *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 
    309314 
    310315                        _d_setUnhandled(pti); 
    311316 
    312317                        // Have system call all finally blocks in intervening frames 
    313                         _global_unwind(frame, exception_record); 
     318                        _d_global_unwind(frame, exception_record); 
    314319 
    315320                        // Call all the finally blocks skipped in this frame 
    316321                        _d_local_unwind(handler_table, frame, ndx); 
    317322 
    318323                        _d_setUnhandled(null); 
    319324 
    320325                        frame.table_index = prev_ndx;  // we are out of this handler 
    321326 
    322327                        // Jump to catch block. Does not return. 
    323328                        { 
     
    466471*/ 
    467472        // convert all other exception codes into a Win32Exception 
    468473        default: 
    469474            pti = new Error("Win32 Exception"); 
    470475            break; 
    471476    } 
    472477    _d_createTrace(pti); 
    473478    return pti; 
    474479} 
    475480 
     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 
     510extern(C) 
     511EXCEPTION_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 
    476528/************************************** 
    477529 * Call finally blocks in the current stack frame until stop_index. 
    478530 * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c 
    479531 */ 
    480532extern(C) 
    481533void _d_local_unwind(DHandlerTable *handler_table, 
    482534        DEstablisherFrame *frame, int stop_index) 
    483535{ 
    484536    DHandlerInfo *phi; 
    485537    DCatchInfo *pci; 
    486538    int i; 
    487539    // Set up a special exception handler to catch double-fault exceptions. 
    488540    asm 
    489541    { 
    490542        push    dword ptr -1; 
    491543        push    dword ptr 0; 
    492         push    offset _local_except_handler;    // defined in src\win32\ehsup.c 
     544        push    offset unwindCollisionExceptionHandler; 
    493545        push    dword ptr FS:_except_list; 
    494546        mov     FS:_except_list,ESP; 
    495547    } 
    496548    for (i = frame.table_index; i != -1 && i != stop_index; i = phi.prev_index) 
    497549    { 
    498550        phi = &handler_table.handler_info[i]; 
    499551        if (phi.finally_code) 
    500552        { 
    501553            // Note that it is unnecessary to adjust the ESP, as the finally block 
    502554            // accesses all items on the stack as relative to EBP. 
     
    517569        } 
    518570    } 
    519571 
    520572    asm 
    521573    { 
    522574        pop     FS:_except_list; 
    523575        add     ESP,12; 
    524576    } 
    525577} 
    526578 
     579/+ According to http://www.microsoft.com/msj/0197/exception/exception.aspx,  
     580global 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 
     588This code seems to be calling RtlUnwind( pFrame, &__retlabel, eRecord, 0); 
     589Apparently Win32 doesn't use the return address anyway. 
     590} 
     591 
     592+/ 
     593extern(C) 
     594int _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 
    527624/*********************************** 
    528625 * external version of the unwinder 
    529626 */ 
    530627extern(C) 
    531628void _d_local_unwind2() 
    532629{ 
    533630    asm 
    534631    { 
    535632        naked; 
    536633        jmp     _d_local_unwind;