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块里的语句

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

 

posted on 2019-09-21 14:30  活着的虫子  阅读(2729)  评论(0编辑  收藏  举报

导航