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

Changeset 475

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

Added a brief explanation of Windows SEH, which is incredibly poorly documented by Microsoft, together with an explanation of the DMD/DMC implementation. The code is practically incomprehensible without this.
Also cleaned up variable naming scheme. No change to generated code.

Files:

Legend:

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

    r459 r475  
    1515import core.sys.windows.windows; 
    1616//import core.stdc.stdio; 
    1717 
    1818enum EXCEPTION_DISPOSITION { 
    1919    ExceptionContinueExecution, 
    2020    ExceptionContinueSearch, 
    2121    ExceptionNestedException, 
    2222    ExceptionCollidedUnwind 
    2323} 
    2424 
     25/+ 
    2526enum { 
    2627    EXCEPTION_EXECUTE_HANDLER    = 1, 
    2728    EXCEPTION_CONTINUE_SEARCH    = 0, 
    2829    EXCEPTION_CONTINUE_EXECUTION = -1 
    2930} 
     31+/ 
    3032 
    3133extern(Windows) 
    3234{ 
    3335void RaiseException(DWORD, DWORD, DWORD, void *); 
    3436} 
    3537 
    3638// used in EXCEPTION_RECORD 
    3739enum : DWORD { 
    3840    STATUS_WAIT_0                      = 0, 
    3941    STATUS_ABANDONED_WAIT_0            = 0x00000080, 
     
    161163 
    162164enum EXCEPTION_UNWIND = 6;  // Flag to indicate if the system is unwinding 
    163165 
    164166/* Windows Kernel function to initiate a system unwind. 
    165167 
    166168  Documentation for this function is severely lacking. 
    167169  http://www.nynaeve.net/?p=99 states that the MSDN documentation is incorrect, 
    168170    and gives a corrected form, but it's for x86_64 only. 
    169171  http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was 
    170172    undocumented in 1997. 
    171   Presumably DMC reverse-engineered it, because it got the signature of this  
    172     function wrong!! 
    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 
    180180alias int function() fp_t; // function pointer in ambient memory model 
    181181 
    182182extern(C) 
    183183{  
    184 extern __gshared DWORD _except_list; 
     184extern __gshared DWORD _except_list; // This is just FS:[0] 
    185185} 
    186186 
    187187extern(C) 
    188188{ 
    189189void _d_setUnhandled(Object); 
    190190void _d_createTrace(Object); 
    191191int _d_isbaseof(ClassInfo b, ClassInfo c); 
    192192} 
     193 
     194/+ 
     195 
     196Implementation of Structured Exception Handling in DMD-Windows 
     197 
     198Every function which uses exception handling (a 'frame') has a thunk created 
     199for it. This thunk is the 'language-specific handler'. 
     200The thunks are created in the DMD backend, in nteh_framehandler() in nteh.c. 
     201These thunks are of the form: 
     202      MOV     EAX,&scope_table 
     203      JMP     __d_framehandler 
     204FS:[0] contains a singly linked list of all active handlers (they'll all be 
     205thunks). The list is created on the stack.  
     206At the end of this list is _except_handler3, a function in the DMC library. 
     207Its signature is: 
     208 
     209extern(C) 
     210EXCEPTION_DISPOSITION _except_handler3(EXCEPTION_RECORD *eRecord, 
     211    DEstablisherFrame * frame,CONTEXT *context,void *dispatchercontext); 
     212 
     213It may be unnecessary. I think it is included for compatibility with MSVC 
     214exceptions? 
     215 
     216Documentation of Windows SEH is hard to find. Here is a brief explanation: 
     217 
     218When an exception is raised, the OS calls each handler in the FS:[0] list in 
     219turn, looking for a catch block. It continues moving down the list, as long as 
     220each handler indicates that it has not caught the exception. When a handler is 
     221ready to catch the exception, it calls the OS function RtlUnwind. 
     222This calls each function in the FS:[0] list again, this time indicating that it 
     223is a 'unwind' call. All of the intervening finally blocks are run at this time. 
     224The complicated case is a CollidedException, which happens when a finally block 
     225throws an exception. The new exception needs to either replace the old one, or 
     226be chained to the old one. 
     227 
     228The other complexity comes from the fact that a single function may have 
     229multiple try/catch/finally blocks. Hence, there's a 'handler table' created for 
     230each function which uses exceptions. 
     231+/ 
    193232 
    194233// The layout of DEstablisherFrame is the same for C++ 
    195234 
    196235struct DEstablisherFrame 
    197236{ 
    198237    void *prev;                 // pointer to previous exception list 
    199238    void *handler;              // pointer to routine for exception handler 
    200239    DWORD table_index;          // current index into handler_info[] 
    201240    DWORD ebp;                  // this is EBP of routine 
    202241}; 
     
    227266}; 
    228267 
    229268// Create one of these for each try-catch 
    230269struct DCatchInfo 
    231270{ 
    232271    uint ncatches;                  // number of catch blocks 
    233272    DCatchBlock catch_block[1];  // data for each catch block 
    234273}; 
    235274 
    236275// Macro to make our own exception code 
    237 template MAKE_EXCEPTION_CODE(int severity, int facility, int exception){ 
     276template MAKE_EXCEPTION_CODE(int severity, int facility, int exception) 
     277
    238278        enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception)); 
    239279} 
    240280enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1); 
    241281 
    242282 
    243283/*********************************** 
    244284 * The frame handler, this is called for each frame that has been registered 
    245285 * in the OS except_list. 
    246286 * Input: 
    247287 *      EAX     the handler table for the frame 
    248288 */ 
    249289extern(C) 
    250290EXCEPTION_DISPOSITION _d_framehandler( 
    251             EXCEPTION_RECORD *exception_record, 
     292            EXCEPTION_RECORD *exceptionRecord, 
    252293            DEstablisherFrame *frame, 
    253294            CONTEXT *context, 
    254             void *dispatcher_context) 
    255 { 
    256     DHandlerTable *handler_table; 
    257  
    258     asm { mov handler_table,EAX; } 
    259  
    260     if (exception_record.ExceptionFlags & EXCEPTION_UNWIND) 
     295            void *dispatcherContext) 
     296{ 
     297    DHandlerTable *handlerTable; 
     298 
     299    asm { mov handlerTable,EAX; } 
     300 
     301    if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 
    261302    { 
    262303         // Call all the finally blocks in this frame 
    263          _d_local_unwind(handler_table, frame, -1); 
     304         _d_local_unwind(handlerTable, frame, -1); 
    264305    } 
    265306    else 
    266307    { 
    267308        // Jump to catch block if matching one is found 
    268309        int ndx,prev_ndx,i; 
    269310        DHandlerInfo *phi; 
    270311        DCatchInfo *pci; 
    271312        DCatchBlock *pcb; 
    272313        uint ncatches;              // number of catches in the current handler 
    273314        Object pti; 
    274315        ClassInfo ci; 
    275316 
    276317        ci = null;                      // only compute it if we need it 
    277318 
    278319        // walk through handler table, checking each handler 
    279320        // with an index smaller than the current table_index 
    280321        for (ndx = frame.table_index; ndx != -1; ndx = prev_ndx) 
    281322        { 
    282             phi = &handler_table.handler_info[ndx]; 
     323            phi = &handlerTable.handler_info[ndx]; 
    283324            prev_ndx = phi.prev_index; 
    284325            if (phi.cioffset) 
    285326            { 
    286327                // this is a catch handler (no finally) 
    287                 pci = cast(DCatchInfo *)(cast(ubyte *)handler_table + phi.cioffset); 
     328                pci = cast(DCatchInfo *)(cast(ubyte *)handlerTable + phi.cioffset); 
    288329                ncatches = pci.ncatches; 
    289330                for (i = 0; i < ncatches; i++) 
    290331                { 
    291332                    pcb = &pci.catch_block[i]; 
    292333 
    293334                    if (!ci) 
    294335                    { 
    295336                        // This code must match the translation code 
    296                         if (exception_record.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) 
     337                        if (exceptionRecord.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) 
    297338                        { 
    298                             // printf("ei[0] = %p\n", exception_record.ExceptionInformation[0]); 
    299                             ci = (**(cast(ClassInfo **)(exception_record.ExceptionInformation[0]))); 
     339                            // printf("ei[0] = %p\n", exceptionRecord.ExceptionInformation[0]); 
     340                            ci = (**(cast(ClassInfo **)(exceptionRecord.ExceptionInformation[0]))); 
    300341                        } 
    301342                        else 
    302343                            ci = Throwable.typeinfo; 
    303344                    } 
    304345                    if (_d_isbaseof(ci, pcb.type)) 
    305346                    { 
    306347                        // Matched the catch type, so we've found the handler. 
    307348                        int regebp; 
    308349 
    309                         pti = _d_translate_se_to_d_exception(exception_record); 
     350                        pti = _d_translate_se_to_d_exception(exceptionRecord); 
    310351 
    311352                        // Initialize catch variable 
    312353                        regebp = cast(int)&frame.ebp;              // EBP for this frame 
    313354                        *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 
    314355 
    315356                        _d_setUnhandled(pti); 
    316357 
    317358                        // Have system call all finally blocks in intervening frames 
    318                         _d_global_unwind(frame, exception_record); 
     359                        _d_global_unwind(frame, exceptionRecord); 
    319360 
    320361                        // Call all the finally blocks skipped in this frame 
    321                         _d_local_unwind(handler_table, frame, ndx); 
     362                        _d_local_unwind(handlerTable, frame, ndx); 
    322363 
    323364                        _d_setUnhandled(null); 
    324365 
    325366                        frame.table_index = prev_ndx;  // we are out of this handler 
    326367 
    327368                        // Jump to catch block. Does not return. 
    328369                        { 
    329370                            uint catch_esp; 
    330371                            fp_t catch_addr; 
    331372 
    332373                            catch_addr = cast(fp_t)(pcb.code); 
    333                             catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; 
     374                            catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof; 
    334375                            asm 
    335376                            { 
    336377                                mov     EAX,catch_esp; 
    337378                                mov     ECX,catch_addr; 
    338379                                mov     [EAX],ECX; 
    339380                                mov     EBP,regebp; 
    340381                                mov     ESP,EAX;         // reset stack 
    341382                                ret;                     // jump to catch block 
    342383                            } 
    343384                        } 
     
    349390    return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
    350391} 
    351392 
    352393/*********************************** 
    353394 * Exception filter for use in __try..__except block 
    354395 * surrounding call to Dmain() 
    355396 */ 
    356397 
    357398int _d_exception_filter(EXCEPTION_POINTERS *eptrs, 
    358399                        int retval, 
    359                         Object *exception_object) 
    360 { 
    361     *exception_object = _d_translate_se_to_d_exception(eptrs.ExceptionRecord); 
     400                        Object *exceptionObject) 
     401{ 
     402    *exceptionObject = _d_translate_se_to_d_exception(eptrs.ExceptionRecord); 
    362403    return retval; 
    363404} 
    364405 
    365406/*********************************** 
    366407 * Throw a D object. 
    367408 */ 
    368409extern(C) 
    369410void _d_throwc(Object h) 
    370411{ 
    371412    //printf("_d_throw(h = %p, &h = %p)\n", h, &h); 
     
    374415    //_d_setUnhandled(h); 
    375416    RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, 
    376417                   EXCEPTION_NONCONTINUABLE, 
    377418                   1, cast(void *)&h); 
    378419} 
    379420 
    380421/*********************************** 
    381422 * Converts a Windows Structured Exception code to a D Exception Object. 
    382423 */ 
    383424 
    384 Object _d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record) 
     425Object _d_translate_se_to_d_exception(EXCEPTION_RECORD *exceptionRecord) 
    385426{ 
    386427    Object pti; 
    387428   // BUG: what if _d_newclass() throws an out of memory exception? 
    388429 
    389     switch (exception_record.ExceptionCode) { 
     430    switch (exceptionRecord.ExceptionCode) { 
    390431        case STATUS_DIGITAL_MARS_D_EXCEPTION: 
    391432            // Generated D exception 
    392             pti = cast(Object)cast(void *)(exception_record.ExceptionInformation[0]); 
     433            pti = cast(Object)cast(void *)(exceptionRecord.ExceptionInformation[0]); 
    393434            break; 
    394435 
    395436        case STATUS_INTEGER_DIVIDE_BY_ZERO: 
    396437            pti = new Error("Integer Divide by Zero"); 
    397438            break; 
    398439 
    399440        case STATUS_FLOAT_DIVIDE_BY_ZERO: 
    400441            pti = new Error("Float Divide by Zero"); 
    401442            break; 
    402443 
     
    434475 
    435476        case STATUS_FLOAT_UNDERFLOW: 
    436477            pti = new Error("Floating Point Underflow"); 
    437478            break; 
    438479 
    439480        case STATUS_FLOAT_STACK_CHECK: 
    440481            pti = new Error("Floating Point Stack Check"); 
    441482            break; 
    442483 
    443484        case STATUS_PRIVILEGED_INSTRUCTION: 
    444             if (*(cast(ubyte *)(exception_record.ExceptionAddress))==0xF4) { // HLT 
     485            if (*(cast(ubyte *)(exceptionRecord.ExceptionAddress))==0xF4) { // HLT 
    445486                pti = new Error("assert(0) or HLT instruction"); 
    446487            } else { 
    447488                pti = new Error("Privileged Instruction"); 
    448489            } 
    449490            break; 
    450491 
    451492        case STATUS_ILLEGAL_INSTRUCTION: 
    452493            pti = new Error("Illegal Instruction"); 
    453494            break; 
    454495 
     
    475516            break; 
    476517    } 
    477518    _d_createTrace(pti); 
    478519    return pti; 
    479520} 
    480521 
    481522/+ 
    482523  These next two functions are necessary for dealing with collided exceptions: 
    483524  when an exception has been thrown during unwinding. This happens for example 
    484525  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  
    508526+/ 
    509527 
    510528extern(C) 
    511529EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( 
    512530            EXCEPTION_RECORD *ExceptionRecord, 
    513531            DEstablisherFrame *frame, 
    514532            CONTEXT *context, 
    515533            void *dispatcherContext) 
    516534{ 
    517535    if (!(ExceptionRecord.ExceptionFlags & EXCEPTION_UNWIND))  
     
    577595} 
    578596 
    579597/+ According to http://www.microsoft.com/msj/0197/exception/exception.aspx,  
    580598global unwind is just a thin wrapper around RtlUnwind. 
    581599__global_unwind(void * pRegistFrame) 
    582600 { 
    583601     _RtlUnwind( pRegistFrame, 
    584602                 &__ret_label, 
    585603                 0, 0 ); 
    586604     __ret_label: 
     605  } 
     606Apparently Win32 doesn't use the return address anyway. 
    587607 
    588608This code seems to be calling RtlUnwind( pFrame, &__retlabel, eRecord, 0); 
    589 Apparently Win32 doesn't use the return address anyway. 
    590 } 
    591  
    592609+/ 
    593610extern(C) 
    594611int _d_global_unwind(DEstablisherFrame *pFrame, EXCEPTION_RECORD *eRecord) 
    595612{ 
    596613    asm { 
    597614        naked; 
    598615        push EBP; 
    599616        mov EBP,ESP; 
    600617        push ECX; 
    601618        push EBX; 
     
    636653 
    637654/*********************************** 
    638655 * The frame handler, this is called for each frame that has been registered 
    639656 * in the OS except_list. 
    640657 * Input: 
    641658 *      EAX     the handler table for the frame 
    642659 */ 
    643660 
    644661extern(C) 
    645662EXCEPTION_DISPOSITION _d_monitor_handler( 
    646             EXCEPTION_RECORD *exception_record, 
     663            EXCEPTION_RECORD *exceptionRecord, 
    647664            DEstablisherFrame *frame, 
    648665            CONTEXT *context, 
    649             void *dispatcher_context) 
    650 { 
    651     if (exception_record.ExceptionFlags & EXCEPTION_UNWIND) 
     666            void *dispatcherContext) 
     667{ 
     668    if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 
    652669    { 
    653670        _d_monitorexit(cast(Object)cast(void *)frame.table_index); 
    654671    } 
    655672    else 
    656673    { 
    657674    } 
    658675    return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 
    659676} 
    660677 
    661678/***********************************