zoukankan      html  css  js  c++  java
  • C++ EH Exception(0xe06d7363)---捕获过程

    书接上文《C++ EH Exception(0xe06d7363)----抛出过程》,下面我们讲下,VC++是如何catch到异常且处理的。

    我们知道,在VC++里,C++异常实现的底层机制还是SEH,所以,我们将程序跑起来观察

    上图红框框起来的部分就是编译器安装了异常处理链,且将其设置位最后一个节点,也就如下结构

    struct VC_EXCEPTION_REGISTRATION
    {
         VC_EXCEPTION_REGISTRATION* prev;    //前一个结构体的指针
         FARPROC                    handler; //永远指向_exception_handler4回调函数
         scopetable_entry*          scopetable;//指向scpoetable数组的指针
         int                        _index; //有的书上也叫tryLevel。scopetable项的当前项
         DWORD                      _ebp;   //当前ebp值,用于访问各成员
    }
    

    那我们就知道,编译器通过push        0C852F8h 给我们注册了异常处理回调,我们看看 0C852F8处是个什么东西

    可以看到,0x0C852F8处是函数__ehhandler$_main,那么__ehhandler$_main干了些什么呢:

    首先,是将FuncInfo信息提指针赋值给eax,这个结构如下

    这个结构表示的是当前处于的函数的信息,收集发生异常时必须要完成的操作的所有信息。异常处理从funcinfo结构里知道了catchblock的参数,也就是catch块的地址。对于每个函数,VC还收集一份它里面的try语句的位置信息,catch语句能catch住的object type信息,以及每个可能发生异常的情况下要完成的object destructor调用的信息。这些信息构成了function info。

    然后jmp 到___CxxFrameHandler3 函数,这个函数如下:

    extern "C" _CRTIMP __declspec(naked) EXCEPTION_DISPOSITION __cdecl __CxxFrameHandler3(
    /*
        EAX=FuncInfo   *pFuncInfo,          // Static information for this frame
    */
        EHExceptionRecord  *pExcept,        // Information for this exception
        EHRegistrationNode *pRN,            // Dynamic information for this frame
        void               *pContext,       // Context info (we don't care what's in it)
        DispatcherContext  *pDC             // More dynamic info for this frame (ignored on Intel)
    ) {
        FuncInfo   *pFuncInfo;
        EXCEPTION_DISPOSITION result;
    
        __asm {
            //
            // Standard function prolog
            //
            push    ebp
            mov     ebp, esp
            sub     esp, __LOCAL_SIZE
            push    ebx
            push    esi
            push    edi
            cld             // A bit of paranoia -- Our code-gen assumes this
    
            //
            // Save the extra parameter
            //
            mov     pFuncInfo, eax
            }
    
        EHTRACE_ENTER_FMT1("pRN = 0x%p", pRN);
    
        result = __InternalCxxFrameHandler( pExcept, pRN, pContext, pDC, pFuncInfo, 0, NULL, FALSE );
    
        EHTRACE_HANDLER_EXIT(result);
    
        __asm {
            pop     edi
            pop     esi
            pop     ebx
            mov     eax, result
            mov     esp, ebp
            pop     ebp
            ret     0
            }
    }

    在这个函数里,通过mov pFuncInfo, eax将funcinfo赋值给本地变量pFuncInfo,传递给__InternalCxxFrameHandler函数。

    函数__InternalCxxFrameHandler的代码如下

    extern "C" EXCEPTION_DISPOSITION __cdecl __InternalCxxFrameHandler(
        EHExceptionRecord  *pExcept,        // Information for this exception
        EHRegistrationNode *pRN,            // Dynamic information for this frame
        CONTEXT *pContext,                  // Context info
        DispatcherContext *pDC,             // Context within subject frame
        FuncInfo *pFuncInfo,                // Static information for this frame
        int CatchDepth,                     // How deeply nested are we?
        EHRegistrationNode *pMarkerRN,      // Marker node for when checking inside
                                            //  catch block
        BOOLEAN recursive                   // Are we handling a translation?
    ) {
        EHTRACE_ENTER_FMT2("%s, pRN = 0x%p",
                           IS_UNWINDING(PER_FLAGS(pExcept)) ? "Unwinding" : "Searching",
                           pRN);
    
        if ((cxxReThrow == false) && (PER_CODE(pExcept) != EH_EXCEPTION_NUMBER) &&
    #if defined(_M_X64) || defined(_M_ARM) /*IFSTRIP=IGN*/
            /* On the 64 bit/ARM platforms, ExceptionCode maybe set to STATUS_UNWIND_CONSOLIDATE
                when called from _UnwindNestedFrames during Logical Unwind. _UnwindNestedFrames
                will also set EH_MAGIC_NUMBER1 in the 8 element */
            (!((PER_CODE(pExcept) == STATUS_UNWIND_CONSOLIDATE) && (PER_NPARAMS(pExcept) == 15) && (PER_EXCEPTINFO(pExcept)[8] == EH_MAGIC_NUMBER1))) &&
    #endif
            (PER_CODE(pExcept) != STATUS_LONGJUMP) &&
            (FUNC_MAGICNUM(*pFuncInfo) >= EH_MAGIC_NUMBER3) &&
            ((FUNC_FLAGS(*pFuncInfo) & FI_EHS_FLAG) != 0))
        {
            /*
             * This function was compiled /EHs so we don't need to do anything in
             * this handler.
             */
            return ExceptionContinueSearch;
        }
    
        if (IS_UNWINDING(PER_FLAGS(pExcept)))
        {
            // We're at the unwinding stage of things.  Don't care about the
            // exception itself.  (Check this first because it's easier)
    
            if (FUNC_MAXSTATE(*pFuncInfo) != 0 && CatchDepth == 0)
            {
                // Only unwind if there's something to unwind
                // AND we're being called through the primary RN.
    
    #if defined(_M_X64) || defined(_M_ARM) /*IFSTRIP=IGN*/
    
                if (IS_TARGET_UNWIND(PER_FLAGS(pExcept)) && PER_CODE(pExcept) == STATUS_LONGJUMP) {
                        __ehstate_t target_state = __StateFromIp(pFuncInfo,
                                                                 pDC,
    #if defined(_M_X64)
                                                                 pContext->Rip
    #elif defined(_M_ARM)
                                                                 pContext->Pc
    #endif
                                                                 );
    
                        DASSERT(target_state >= EH_EMPTY_STATE
                                && target_state < FUNC_MAXSTATE(*pFuncInfo));
    
                        __FrameUnwindToState(pRN, pDC, pFuncInfo, target_state);
                        EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
                        return ExceptionContinueSearch;
                } else if(IS_TARGET_UNWIND(PER_FLAGS(pExcept)) &&
                          PER_CODE(pExcept) == STATUS_UNWIND_CONSOLIDATE)
                {
                    PEXCEPTION_RECORD pSehExcept = (PEXCEPTION_RECORD)pExcept;
                    __ehstate_t target_state = (__ehstate_t)pSehExcept->ExceptionInformation[3];
    
                    DASSERT(target_state >= EH_EMPTY_STATE
                            && target_state < FUNC_MAXSTATE(*pFuncInfo));
                    __FrameUnwindToState((EHRegistrationNode *)pSehExcept->ExceptionInformation[1],
                                         pDC,
                                         pFuncInfo,
                                         target_state);
                    EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
                    return ExceptionContinueSearch;
                }
    #endif // defined(_M_X64) || defined(_M_ARM)
                __FrameUnwindToEmptyState(pRN, pDC, pFuncInfo);
            }
            EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
            return ExceptionContinueSearch;     // I don't think this value matters
    
        } else if (FUNC_NTRYBLOCKS(*pFuncInfo) != 0
            //
            // If the function has no try block, we still want to call the
            // frame handler if there is an exception specification
            //
            || (FUNC_MAGICNUM(*pFuncInfo) >= EH_MAGIC_NUMBER2 && FUNC_PESTYPES(pFuncInfo) != NULL)) {
    
            // NT is looking for handlers.  We've got handlers.
            // Let's check this puppy out.  Do we recognize it?
    
            int (__cdecl *pfn)(...);
    
            if (PER_CODE(pExcept) == EH_EXCEPTION_NUMBER
              && PER_NPARAMS(pExcept) >= 3
              && PER_MAGICNUM(pExcept) > EH_MAGIC_NUMBER3
              && (pfn = THROW_FORWARDCOMPAT(*PER_PTHROW(pExcept))) != NULL) {
    
                // Forward compatibility:  The thrown object appears to have been
                // created by a newer version of our compiler.  Let that version's
                // frame handler do the work (if one was specified).
    
    #if defined(_DEBUG) || defined(_SYSCRT_DEBUG)
                if (_ValidateExecute((FARPROC)pfn)) {
    #endif
                    EXCEPTION_DISPOSITION result =
                        (EXCEPTION_DISPOSITION)pfn(pExcept, pRN, pContext, pDC,
                                                   pFuncInfo, CatchDepth,
                                                   pMarkerRN, recursive);
                    EHTRACE_HANDLER_EXIT(result);
                    return result;
    #if defined(_DEBUG) || defined(_SYSCRT_DEBUG)
                } else {
                    _inconsistency(); // Does not return; TKB
                }
    #endif
    
            } else {
    
                // Anything else: we'll handle it here.
                FindHandler(pExcept, pRN, pContext, pDC, pFuncInfo, recursive,
                  CatchDepth, pMarkerRN);
            }
    
            // If it returned, we didn't have any matches.
    
            } // NT was looking for a handler
    
        // We had nothing to do with it or it was rethrown.  Keep searching.
        EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
        return ExceptionContinueSearch;
    
    } // InternalCxxFrameHandler

    而__InternalCxxFrameHandler里调用FindHandler来寻找最终的catch块,FindHandler函数代码如下:

    static void FindHandler(
        EHExceptionRecord *pExcept,         // Information for this (logical)
                                            //   exception
        EHRegistrationNode *pRN,            // Dynamic information for subject frame
        CONTEXT *pContext,                  // Context info
        DispatcherContext *pDC,             // Context within subject frame
        FuncInfo *pFuncInfo,                // Static information for subject frame
        BOOLEAN recursive,                  // TRUE if we're handling the
                                            //   translation
        int CatchDepth,                     // Level of nested catch that is being
                                            //   checked
        EHRegistrationNode *pMarkerRN       // Extra marker RN for nested catch
                                            //   handling
    )
    {
        EHTRACE_ENTER;
    
        BOOLEAN IsRethrow = FALSE;
        BOOLEAN gotMatch = FALSE;
    
        // Get the current state (machine-dependent)
    #if defined(_M_X64) || defined(_M_ARM_NT) /*IFSTRIP=IGN*/
        __ehstate_t curState = __StateFromControlPc(pFuncInfo, pDC);
        EHRegistrationNode EstablisherFrame;
        /*
         * Here We find what is the actual State of current function. The way we
         * do this is first get State from ControlPc.
         *
         * Remember we have __GetUnwindTryBlock to remember the last State for which
         * Exception was handled and __GetCurrentState for retriving the current
         * state of the function. Please Note that __GetCurrentState is used
         * primarily for unwinding purpose.
         *
         * Also remember that all the catch blocks act as funclets. This means that
         * ControlPc for all the catch blocks are different from ControlPc of parent
         * catch block or function.
         *
         * take a look at this example
         * try {
         *   // STATE1 = 1
         *   try {
         *     // STATE2
         *     // THROW
         *   } catch (...) { // CatchB1
         *     // STATE3
         *     // RETHROW OR NEW THROW
         *   }
         * } catch (...) { // CatchB2
         * }
         *
         * If we have an exception comming from STATE3, the FindHandler will be
         * called for CatchB1, at this point we do the test which State is our
         * real state, curState from ControlPc or state from __GetUnwindTryBlock.
         * Since curState from ControlPc is greater, we know that real State is
         * curState from ControlPc and thus we update the UnwindTryBlockState.
         *
         * On further examination, we found out that there is no handler within
         * this catch block, we return without handling the exception. For more
         * info on how we determine if we have handler, have a look at
         * __GetRangeOfTrysToCheck.
         *
         * Now FindHandler will again be called for parent function. Here again
         * we test which is real State, state from ControlPc or State from
         * __GetUnwindTryBlock. This time state from __GetUnwindTryBlock is correct.
         *
         * Also look at code in __CxxCallCatchBlock, you will se that as soon as we get
         * out of last catch block, we reset __GetUnwindTryBlock state to -1.
         */
    
        _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFrame);
        if (curState > __GetUnwindTryBlock(pRN, pDC, pFuncInfo)) {
            __SetState(&EstablisherFrame, pDC, pFuncInfo, curState);
            __SetUnwindTryBlock(pRN, pDC, pFuncInfo, /*curTry*/ curState);
        } else {
            curState = __GetUnwindTryBlock(pRN, pDC, pFuncInfo);
        }
    #else
        __ehstate_t curState = GetCurrentState(pRN, pDC, pFuncInfo);
    #endif
        DASSERT(curState >= EH_EMPTY_STATE && curState < FUNC_MAXSTATE(*pFuncInfo));
    
        // Check if it's a re-throw.  Use the exception we stashed away if it is.
        if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) {
    
            if (_pCurrentException == NULL) {
                // Oops!  User re-threw a non-existant exception!  Let it propogate.
                EHTRACE_EXIT;
                return;
            }
    
            pExcept = _pCurrentException;
            pContext = _pCurrentExContext;
            IsRethrow = TRUE;
    #if _EH_RELATIVE_OFFSETS /*IFSTRIP=IGN*/
            _SetThrowImageBase((ptrdiff_t)pExcept->params.pThrowImageBase);
    #endif
    
            DASSERT(_ValidateRead(pExcept));
            DASSERT(!PER_IS_MSVC_EH(pExcept) || PER_PTHROW(pExcept) != NULL);
    
            //
            // We know it is a rethrow -- did we come here as a result of an
            // exception re-thrown from CallUnexpected() ?
            //
            if( _pCurrentFuncInfo != NULL )
            {
                ESTypeList* pCurrentFuncInfo = _pCurrentFuncInfo;   // remember it in a local variable
                _pCurrentFuncInfo = NULL;   // and reset it immediately -- so we don't forget to do it later
    
                // Does the exception thrown by CallUnexpected belong to the exception specification?
    
                if( IsInExceptionSpec(pExcept, pCurrentFuncInfo) )
                {
                    // Yes it does -- so "continue the search for another handler at the call of the function
                    // whose exception-specification was violated"
                    ;
                }
                else
                {
                    // Nope, it does not. Is std::bad_exception allowed by the spec?
    
                    if( Is_bad_exception_allowed(pCurrentFuncInfo) )
                    {
                        // yup -- so according to the standard, we need to replace the thrown
                        // exception by an implementation-defined object of the type std::bad_exception
                        // and continue the search for another handler at the call of the function
                        // whose exception-specification was violated.
    
                        // Just throw bad_exception -- we will then come into FindHandler for the third time --
                        // but make sure we will not get here again
    
                        __DestructExceptionObject(pExcept, TRUE);   // destroy the original object
    
                        throw std::bad_exception();
                    }
                    else
                    {
                        terminate();
                    }
                }
            }
        }
    
        if (PER_IS_MSVC_EH(pExcept)) {
            // Looks like it's ours.  Let's see if we have a match:
            //
            // First, determine range of try blocks to consider:
            // Only try blocks which are at the current catch depth are of interest.
    
            unsigned curTry;
            unsigned end;
    
            if( FUNC_NTRYBLOCKS(*pFuncInfo) > 0 )
            {
                TryBlockMapEntry *pEntry = __GetRangeOfTrysToCheck(pRN,
                                                                pFuncInfo,
                                                                CatchDepth,
                                                                curState,
                                                                &curTry,
                                                                &end,
                                                                pDC);
    
                // Scan the try blocks in the function:
                for (; curTry < end; curTry++, pEntry++) {
                    HandlerType *pCatch;
    #if _EH_RELATIVE_OFFSETS
                    __int32 const *ppCatchable;
    #else
                    CatchableType * const *ppCatchable;
    #endif
                    CatchableType *pCatchable;
                    int catches;
                    int catchables;
    
                    if (TBME_LOW(*pEntry) > curState || curState > TBME_HIGH(*pEntry)) {
                        continue;
                    }
    
                    // Try block was in scope for current state.  Scan catches for this
                    // try:
                    pCatch  = TBME_PCATCH(*pEntry, 0);
                    for (catches = TBME_NCATCHES(*pEntry); catches > 0; catches--,
                    pCatch++) {
    
                        // Scan all types that thrown object can be converted to:
                        ppCatchable = THROW_CTLIST(*PER_PTHROW(pExcept));
                        for (catchables = THROW_COUNT(*PER_PTHROW(pExcept));
                        catchables > 0; catchables--, ppCatchable++) {
    
    #if _EH_RELATIVE_OFFSETS
                            pCatchable = (CatchableType *)(_GetThrowImageBase() + *ppCatchable);
    #else
                            pCatchable = *ppCatchable;
    #endif
    
                            if (!__TypeMatch(pCatch, pCatchable, PER_PTHROW(pExcept))) {
                                continue;
                            }
    
                            // OK.  We finally found a match.  Activate the catch.  If
                            // control gets back here, the catch did a re-throw, so
                            // keep searching.
    
                            gotMatch = TRUE;
    
                            CatchIt(pExcept,
                                    pRN,
                                    pContext,
                                    pDC,
                                    pFuncInfo,
                                    pCatch,
                                    pCatchable,
                                    pEntry,
                                    CatchDepth,
                                    pMarkerRN,
                                    IsRethrow
    #if defined (_M_X64) || defined(_M_ARM_NT)
                                    , recursive
    #endif
                                    );
                            goto NextTryBlock;
    
                        } // Scan posible conversions
                    } // Scan catch clauses
        NextTryBlock: ;
                } // Scan try blocks
            } // if FUNC_NTRYBLOCKS( pFuncInfo ) > 0
    #if defined(_DEBUG) || defined(_SYSCRT_DEBUG)
            else
            {
                //
                // This can only happen if the function has an exception specification
                // but no try/catch blocks
                //
                DASSERT( FUNC_MAGICNUM(*pFuncInfo) >= EH_MAGIC_NUMBER2 );
                DASSERT( FUNC_PESTYPES(pFuncInfo) != NULL );
            }
    #endif
    
    #if defined(_M_IX86)
            if (recursive) {
                //
                // A translation was provided, but this frame didn't catch it.
                // Destruct the translated object before returning; if destruction
                // raises an exception, terminate.
                //
                // This is not done for Win64 platforms.  On those, the translated
                // object is destructed in __CxxCallCatchBlock.
                //
                __DestructExceptionObject(pExcept, TRUE);
            }
    #endif
    
    #if (!defined(_M_ARM) || defined(_M_ARM_NT))
            //
            // We haven't found the match -- let's look at the exception spec and see if our try
            // matches one of the listed types.
            //
            if( !gotMatch && FUNC_MAGICNUM(*pFuncInfo) >= EH_MAGIC_HAS_ES && FUNC_PESTYPES(pFuncInfo) != NULL )
            {
                if( !IsInExceptionSpec(pExcept, FUNC_PESTYPES(pFuncInfo)) )
                {
                    // Nope, it does not. Call unexpected
    
                    //
                    // We must unwind the stack before calling unexpected -- this makes it work
                    // as if it were inside catch(...) clause
                    //
    #if defined (_M_X64) || defined(_M_ARM_NT) /*IFSTRIP=IGN*/
                    EHRegistrationNode *pEstablisher = pRN;
                    EHRegistrationNode EstablisherFramePointers;
                    pEstablisher = _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFramePointers);
                    PVOID pExceptionObjectDestroyed = NULL;
                    _UnwindNestedFrames(pRN,
                                        pExcept,
                                        pContext,
                                        pEstablisher,
                                        NULL,
                                        -1,
                                        pFuncInfo,
                                        pDC,
                                        recursive
                                        );
    #else
                    EHExceptionRecord *pSaveException = _pCurrentException;
                    CONTEXT *pSaveExContext = _pCurrentExContext;
    
                    _pCurrentException = pExcept;
                    _pCurrentExContext = pContext;
    
                    if (pMarkerRN == NULL) {
                        _UnwindNestedFrames(pRN, pExcept);
                    } else {
                        _UnwindNestedFrames(pMarkerRN, pExcept);
                    }
                    __FrameUnwindToEmptyState(pRN, pDC, pFuncInfo);
    
                    CallUnexpected(FUNC_PESTYPES(pFuncInfo));
                    _pCurrentException = pExcept;
                    _pCurrentExContext = pContext;
    #endif
                }
            }
    #endif // !defined(_M_ARM)
    
        } // It was a C++ EH exception
        else {
            // Not ours.  But maybe someone told us how to make it ours.
            if( FUNC_NTRYBLOCKS(*pFuncInfo) > 0 ) {
                if (!recursive) {
                    FindHandlerForForeignException(pExcept, pRN, pContext, pDC,
                    pFuncInfo, curState, CatchDepth, pMarkerRN);
                } else {
                    // We're recursive, and the exception wasn't a C++ EH!
                    // Translator threw something uninteligable.
    
                    // Two choices here: we could let the new exception take over, or we could abort. We abort.
                    terminate();
                }
            }
        } // It wasn't our exception
    
        DASSERT( _pCurrentFuncInfo == NULL );   // never leave it initialized with something
    
        EHTRACE_EXIT;
    }

    FindHandler函数里找到合适的catch块后,就调用CatchIt函数,代码如下:

    ////////////////////////////////////////////////////////////////////////////////
    //
    // CatchIt - A handler has been found for the thrown type.  Do the work to
    //   transfer control.
    //
    // Description:
    //     Builds the catch object
    //     Unwinds the stack to the point of the try
    //     Calls the address of the handler (funclet) with the frame set up for that
    //       function but without resetting the stack.
    //     Handler funclet returns address to continue execution, or NULL if the
    //       handler re-threw ("throw;" lexically in handler)
    //     If the handler throws an EH exception whose exception info is NULL, then
    //       it's a re-throw from a dynamicly enclosed scope.
    //
    // It is an open question whether the catch object is built before or after the local unwind.
    //
    // Returns:
    //     No return value.  Returns iff handler re-throws.
    static void CatchIt(
        EHExceptionRecord *pExcept,         // The exception thrown
        EHRegistrationNode *pRN,            // Dynamic info of function with catch
        CONTEXT *pContext,                  // Context info
        DispatcherContext *pDC,             // Context within subject frame
        FuncInfo *pFuncInfo,                // Static info of function with catch
        HandlerType *pCatch,                // The catch clause selected
        CatchableType *pConv,               // The rules for making the conversion
        TryBlockMapEntry *pEntry,           // Description of the try block
        int CatchDepth,                     // How many catches are we nested in?
        EHRegistrationNode *pMarkerRN,      // Special node if nested in catch
        BOOLEAN IsRethrow                   // Is this a rethrow ?
    #if defined(_M_X64) || defined(_M_ARM_NT) /*IFSTRIP=IGN*/
        , BOOLEAN recursive
    #endif // defined(_POWERPC)
    ) {
        EHTRACE_ENTER_FMT1("Catching object @ 0x%p", PER_PEXCEPTOBJ(pExcept));
    
        EHRegistrationNode *pEstablisher = pRN;
    
    #if defined(_M_X64) || defined(_M_ARM)  /*IFSTRIP=IGN*/
        EHRegistrationNode EstablisherFramePointers;
        pEstablisher = _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFramePointers);
    #else
        void *continuationAddress;
    #endif // defined(_POWERPC)
    
        // Copy the thrown object into a buffer in the handler's stack frame,
        // unless the catch was by elipsis (no conversion) OR the catch was by
        // type without an actual 'catch object'.
    
        if (pConv != NULL) {
            __BuildCatchObject(pExcept, pEstablisher, pCatch, pConv);
        }
    
        // Unwind stack objects to the entry of the try that caught this exception.
    
    #if defined(_M_X64) || defined(_M_ARM)  /*IFSTRIP=IGN*/
        // This call will never return. This call will end up calling CxxCallCatchBlock
        // through RtlUnwind (STATUS_CONSULIDATE_FRAMES) mechanism.
        _UnwindNestedFrames(pRN,
                            pExcept,
                            pContext,
                            pEstablisher,
                            __GetAddress(HT_HANDLER(*pCatch), pDC),
                            TBME_LOW(*pEntry),
                            pFuncInfo,
                            pDC,
    #if defined(_M_ARM) && !defined(_M_ARM_NT)
                            FALSE
    #else
                            recursive
    #endif
                            );
    #else
    
        if (pMarkerRN == NULL) {
            _UnwindNestedFrames(pRN, pExcept);
        } else {
            _UnwindNestedFrames(pMarkerRN, pExcept);
        }
    
    
        __FrameUnwindToState(pEstablisher, pDC, pFuncInfo, TBME_LOW(*pEntry));
    
        // Call the catch.  Separated out because it introduces a new registration
        // node.
    
        EHTRACE_FMT2("Move from state %d to state %d", __GetUnwindState(pRN, pDC, pFuncInfo), TBME_HIGH(*pEntry) + 1);
        SetState(pRN, pDC, pFuncInfo, TBME_HIGH(*pEntry) + 1);
    
        continuationAddress = CallCatchBlock(pExcept,
                                             pEstablisher,
                                             pContext,
                                             pFuncInfo,
                                             __GetAddress(HT_HANDLER(*pCatch), pDC),
                                             CatchDepth,
                                             0x100);
    
        // Transfer control to the continuation address.  If no continuation then
        // it's a re-throw, so return.
    
        if (continuationAddress != NULL) {
    
            _JumpToContinuation(continuationAddress, pRN);
            // No return.
    
        }
        EHTRACE_EXIT;
    #endif
    }

    CatchIt函数里调用了CallCatchBlock函数,代码如下:

    ////////////////////////////////////////////////////////////////////////////////
    //
    // CallCatchBlock - continuation of CatchIt.
    //
    // This is seperated from CatchIt because it needs to introduce an SEH/EH frame
    //   in case the catch block throws.  This frame cannot be added until unwind of
    //   nested frames has been completed (otherwise this frame would be the first
    //   to go).
    
    static void *CallCatchBlock(
        EHExceptionRecord *pExcept,         // The exception thrown
        EHRegistrationNode *pRN,            // Dynamic info of function with catch
        CONTEXT *pContext,                  // Context info
        FuncInfo *pFuncInfo,                // Static info of function with catch
        void *handlerAddress,               // Code address of handler
        int CatchDepth,                     // How deeply nested in catch blocks
                                            //   are we?
        unsigned long NLGCode               // NLG destination code
    ) {
        EHTRACE_ENTER;
    
        // Address where execution resumes after exception handling completed.
        // Initialized to non-NULL (value doesn't matter) to distinguish from
        // re-throw in __finally.
        void *continuationAddress = handlerAddress;
    
        BOOL ExceptionObjectDestroyed = FALSE;
    
        // The stack pointer at entry to the try must be saved, in case there is
        // another try inside this catch.  We'll restore it on our way out.
        void *saveESP = PRN_STACK(pRN);
    
        // Push this catch block's frame info on a linked list
        FRAMEINFO FrameInfo;
        FRAMEINFO *pFrameInfo = _CreateFrameInfo(&FrameInfo, PER_PEXCEPTOBJ(pExcept));
    
        // Save the current exception in case of a rethrow.  Save the previous value
        // on the stack, to be restored when the catch exits.
        EHExceptionRecord *pSaveException = _pCurrentException;
        CONTEXT *pSaveExContext = _pCurrentExContext;
    
        _pCurrentException = pExcept;
        _pCurrentExContext = pContext;
    
        __try {
            __try {
                // Execute the handler as a funclet, whose return value is the
                // address to resume execution.
    
                continuationAddress = _CallCatchBlock2(pRN, pFuncInfo,
                  handlerAddress, CatchDepth, NLGCode);
            } __except(EHTRACE_EXCEPT(ExFilterRethrow(exception_info()))) {
                cxxReThrow=false;
                // Here we are exiting the catch block on rethrow out of this
                // catch block. To keep the order of destruction and construction
                // same when the the rethrow was from function or was inline, here
                // we unwind to the parent state for this catch.
                UnwindMapEntry *pUnwindMap = pFuncInfo->pUnwindMap;
                int cState = GetCurrentState(pRN, handlerAddress, pFuncInfo);
                TryBlockMapEntry *pTryBlockMap = pFuncInfo->pTryBlockMap;
                unsigned int i;
                for (i = 0; i < pFuncInfo->nTryBlocks; i++) {
                    if (cState > pTryBlockMap[i].tryHigh &&
                        cState <= pTryBlockMap[i].catchHigh) {
                        cState = pTryBlockMap[i].tryHigh +1;
                        cState = pUnwindMap[cState].toState;
                        break;
                    }
                }
                __FrameUnwindToState(pRN, NULL, pFuncInfo, cState);
                // If the handler threw a typed exception without exception info or
                // exception object, then it's a re-throw, so return.  Otherwise
                // it's a new exception, which takes precedence over this one.
                continuationAddress = NULL;
            }
        } __finally {
            EHTRACE_SAVE_LEVEL;
            EHTRACE_FMT1("Executing __finally, %snormal termination", _abnormal_termination() ? "ab" : "");
    
            // Restore the saved stack pointer, so the stack can be reset when
            // we're done.
            PRN_STACK(pRN) = saveESP;
    
            // Pop this catch block's frame info
            _FindAndUnlinkFrame(pFrameInfo);
    
            // Restore the 'current exception' for a possibly enclosing catch
            _pCurrentException = pSaveException;
            _pCurrentExContext = pSaveExContext;
    
            // Destroy the original exception object if we're not exiting on a
            // re-throw and the object isn't also in use by a more deeply nested
            // catch.  Note that the catch handles destruction of its parameter.
    
            if (PER_IS_MSVC_EH(pExcept) && !ExceptionObjectDestroyed
              && continuationAddress != NULL
                && _IsExceptionObjectToBeDestroyed(PER_PEXCEPTOBJ(pExcept))
                ) {
                __DestructExceptionObject(pExcept, abnormal_termination());
            }
    
            EHTRACE_RESTORE_LEVEL(!!_abnormal_termination());
        }
        EHTRACE_EXIT;
        return continuationAddress;
    }

    在CallCatchBlock又调用_CallCatchBlock2函数,代码如下:

    void *_CallCatchBlock2(
        EHRegistrationNode *pRN,            // Dynamic info of function with catch
        FuncInfo           *pFuncInfo,      // Static info of function with catch
        void               *handlerAddress, // Code address of handler
        int                CatchDepth,      // How deeply nested in catch blocks are we?
        unsigned long      NLGCode
    ) {
        EHTRACE_ENTER;
    
        //
        // First, create and link in our special guard node:
        //
        CatchGuardRN CGRN = { NULL,
                              (void*)CatchGuardHandler,
                              __security_cookie ^ (UINT_PTR)&CGRN,
                              pFuncInfo,
                              pRN,
                              CatchDepth + 1
    #if defined(ENABLE_EHTRACE)
                              , __ehtrace_level
    #endif
        };
    
        __asm {
            mov     eax, FS:[0]     // Fetch frame list head
            mov     CGRN.pNext, eax // Link this node in
            lea     eax, CGRN       // Put this node at the head
            mov     FS:[0], eax
            }
    
        //
        // Call the catch
        //
        void *continuationAddress = _CallSettingFrame( handlerAddress, pRN, NLGCode );
    
        //
        // Unlink our registration node
        //
        __asm {
            mov     eax, CGRN.pNext // Get parent node
            mov     FS:[0], eax     // Put it at the head
            }
    
        EHTRACE_EXIT;
    
        return continuationAddress;
        }

    _CallCatchBlock2函数调用_CallSettingFrame函数来实现代码如下

    _CallSettingFrame proc stdcall, funclet:IWORD, pRN:IWORD, dwInCode:DWORD
        ; FPO = 0 dwords locals allocated in prolog
        ;       3 dword parameters
        ;       8 bytes in prolog
        ;       4 registers saved (includes locals to work around debugger bug)
        ;       1 EBP is used
        ;       0 frame type = FPO
        .FPO    (0,3,8,4,1,0)
    
        sub    esp,4
        push    ebx
        push    ecx
        mov    eax,pRN
        add    eax,0Ch            ; sizeof(EHRegistrationNode) -- assumed to equal 0Ch
        mov    dword ptr [ebp-4],eax
        mov    eax,funclet
        push    ebp            ; Save our frame pointer
            push    dwInCode
        mov    ecx,dwInCode
        mov    ebp,dword ptr [ebp-4]    ; Load target frame pointer
        call    _NLG_Notify1        ; Notify debugger
        push    esi
        push    edi
        call    eax            ; Call the funclet
    _NLG_Return::
        pop    edi
        pop    esi
        mov    ebx,ebp
        pop    ebp
            mov     ecx,dwInCode
        push    ebp
        mov    ebp,ebx
        cmp    ecx, 0100h
        jne    _NLG_Continue
            mov     ecx, 02h
    _NLG_Continue:
            push    ecx
        call    _NLG_Notify1        ; Notify debugger yet again
        pop    ebp            ; Restore our frame pointer
        pop    ecx
        pop    ebx
        ret    0Ch
    _CallSettingFrame ENDP
    
        END

    最终在_CallSettingFrame函数里通过 call eax 执行了catch块里的语句

     整个捕获过程的调用链如下:

  • 相关阅读:
    Linux使用locate命令定位文件
    【iOS开发-54】案例学习:通过UIScrollView的缩放图片功能练习代理模式的详细实现
    数据结构—单链表(类C语言描写叙述)
    怎样訪问pcie整个4k的配置空间
    [Swift]LeetCode988. 从叶结点开始的最小字符串 | Smallest String Starting From Leaf
    [Swift]LeetCode985. 查询后的偶数和 | Sum of Even Numbers After Queries
    [Swift]LeetCode494. 目标和 | Target Sum
    [Swift]LeetCode493. 翻转对 | Reverse Pairs
    [Swift]LeetCode491. 递增子序列 | Increasing Subsequences
    [Swift]LeetCode488. 祖玛游戏 | Zuma Game
  • 原文地址:https://www.cnblogs.com/yilang/p/11551377.html
Copyright © 2011-2022 走看看