书接上文《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, , NULL, FALSE ); EHTRACE_HANDLER_EXIT(result); __asm {
pop edi
pop esi
pop ebx
mov eax, result
mov esp, ebp
pop ebp
ret
}
}

在这个函数里,通过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) == ) && (PER_EXCEPTINFO(pExcept)[] == EH_MAGIC_NUMBER1))) &&
#endif
(PER_CODE(pExcept) != STATUS_LONGJUMP) &&
(FUNC_MAGICNUM(*pFuncInfo) >= EH_MAGIC_NUMBER3) &&
((FUNC_FLAGS(*pFuncInfo) & FI_EHS_FLAG) != ))
{
/*
* 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) != && CatchDepth == )
{
// 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[]; DASSERT(target_state >= EH_EMPTY_STATE
&& target_state < FUNC_MAXSTATE(*pFuncInfo));
__FrameUnwindToState((EHRegistrationNode *)pSehExcept->ExceptionInformation[],
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) !=
//
// 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) >=
&& 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) > )
{
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, );
for (catches = TBME_NCATCHES(*pEntry); catches > ; 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 > ; 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,
-,
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) > ) {
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) + );
SetState(pRN, pDC, pFuncInfo, TBME_HIGH(*pEntry) + ); 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 = ; i < pFuncInfo->nTryBlocks; i++) {
if (cState > pTryBlockMap[i].tryHigh &&
cState <= pTryBlockMap[i].catchHigh) {
cState = pTryBlockMap[i].tryHigh +;
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 +
#if defined(ENABLE_EHTRACE)
, __ehtrace_level
#endif
}; __asm {
mov eax, FS:[] // Fetch frame list head
mov CGRN.pNext, eax // Link this node in
lea eax, CGRN // Put this node at the head
mov FS:[], eax
} //
// Call the catch
//
void *continuationAddress = _CallSettingFrame( handlerAddress, pRN, NLGCode ); //
// Unlink our registration node
//
__asm {
mov eax, CGRN.pNext // Get parent node
mov FS:[], 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 (,,,,,) sub esp,
push ebx
push ecx
mov eax,pRN
add eax,0Ch ; sizeof(EHRegistrationNode) -- assumed to equal 0Ch
mov dword ptr [ebp-],eax
mov eax,funclet
push ebp ; Save our frame pointer
push dwInCode
mov ecx,dwInCode
mov ebp,dword ptr [ebp-] ; 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块里的语句

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

C++ EH Exception(0xe06d7363)---捕获过程的更多相关文章

  1. C++ EH Exception(0xe06d7363)----抛出过程

    C++ EH Exception是Windows系统VC++里对c++语言的throw的分类和定义,它的代码就是0xe06d7363.在VC++里其本质也是SEH结构化异常机制.在我们分析用户崩溃的例 ...

  2. DOM事件: DOM事件级别、DOM事件流、DOM事件模型、DOM事件捕获过程、自定义事件

    前端面试中只要问到事件,就肯定会有DOM事件:如果回答出来了,就会一直向下延申,其实这些东西都很简单,但我第一次被问到的时候,也是懵的: DOM事件级别: DOM0 element.onclick = ...

  3. Java基础 try...catch...catch 使用Exception,去捕获其子类异常

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  4. 用Exception类捕获所有异常的技术是怎么用的?

    3.用Exception类捕获所有异常  马克-to-win:注意,一个事实是:Exception类是所有其他异常类的父类,所以Exception类能捕获所有的异常.马克-to-win:问题是用Exc ...

  5. Java处理Exception无法捕获的异常

    场景: 使用try...catch(Exception e){}来捕获异常,执行过程中线程中断或阻塞了,但是catch块中却并没有捕获到异常信息. try{ // 此处可能是调用一个远程的接口,或是调 ...

  6. .NET 基础 一步步 一幕幕[数组、集合、异常捕获]

    数组.集合.异常捕获 数组: 一次性存储多个相同类型的变量. 一维数组: 语法: 数组类型[] 数组名=new 数组类型[数组长度]; 声明数组的语法: A.数据类型 [] 数组名称= new 数据类 ...

  7. JAVA 线程中的异常捕获

    在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked e ...

  8. oracle PL/SQL(procedure language/SQL)程序设计之异常(exception)

    什么是异常?在PL/SQL中的一个标识.在程序运行期间被触发的错误.异常是怎样被触发的?产生一个Oracle错误.用户显示触发.怎样处理异常?用异常处理句柄捕获异常.传播异常到调用环境. 捕获异常 E ...

  9. Android UncaughtExceptionHandler,捕获错误

    最近在做个项目,需要在程序出现运行时异常和错误导致程序crash时进行一些操作,找到一个方法 Thread.setDefaultUncaughtExceptionHandler(new Uncaugh ...

随机推荐

  1. HBase 系列(三)—— HBase 基本环境搭建

    一.安装前置条件说明 1.1 JDK版本说明 HBase 需要依赖 JDK 环境,同时 HBase 2.0+ 以上版本不再支持 JDK 1.7 ,需要安装 JDK 1.8+ .JDK 安装方式见本仓库 ...

  2. .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?

    原文:.NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用? 都知道可以在任务管理器中查看进程的 CPU 和内存占用,那么如何通过 .NET 编写代码的方式来获取到 ...

  3. 2019 斗鱼java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.斗鱼等公司offer,岗位是Java后端开发,因为发展原因最终选择去了斗鱼,入职一年时间了,之前面试了很多家公 ...

  4. LocalDateTime&LocalDate&LocalTime

    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");LocalDateTime ti ...

  5. 关于创建Web图像时应记住的五个要素

    1. 格式与下载速度 当前,Web上用的最广泛的三种格式是GIF.PNG和JPEG.我们的目标是选择质量最高,同时文件最小的格式. WebP图像格式 谷歌建立了另一种图像格式,名为WebP. 这种格式 ...

  6. Excel工作表密码保护的破解

    操作步骤:打开Visual Basic编辑器,单击“插入-->模块“,将以下代码粘贴到模块中即可. Sub DelPassword() ActiveSheet.Protect DrawingOb ...

  7. WinRAR捆绑木马

    准备好木马文件 server.exe 准备一个小游戏 趣味数学计算 压缩 创建自解压格式压缩文件 自解压选项设置 解压路径设置 设置程序 模式设置 压缩完成 使用 开始玩游戏

  8. 【故障解决】enq: PS - contention

    [故障解决]enq: PS - contention 一.1  BLOG文档结构图       一.2  前言部分   一.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能, ...

  9. GDI双缓冲

    GDI双缓冲 翻译自Double buffering,原作者Dim_Yimma_H 语言:C (原文写的是C++,实际上是纯C) 推荐知识: 构建程序 函数 结构体 变量和条件语句 switch语句 ...

  10. MySQL用户与权限

    用户连接到mysql,并做各种查询,在用户和服务器中间分为两个阶段: 1:用户是否有权连接上来 2:用户是否有权执行此操作(如select,update等等) 先看第一个阶段:服务器如何判断用户是否有 ...