// // Copyright (c) Microsoft Corporation. All rights reserved. // // // This source code is licensed under Microsoft Shared Source License // Version 1.0 for Windows CE. // For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223. // /*** *frame.cpp - The frame handler and everything associated with it. * * *Purpose: * The frame handler and everything associated with it. * * Entry points: * _CxxFrameHandler - the frame handler. * ****/ #include "kernel.h" #include "excpt.h" #include "corecrtstorage.h" #include "corecrt.h" #include // CRT internal header file #include // Declarations of all types used for EH #include // Routines to handle transfer of control #include // Declarations of state management stuff #include // User-visible routines for eh #include // Declarations of hook variables and callbacks #include // EH global variables #include // Define debug zone information #pragma hdrstop // PCH is created from here // define CC_OUTERTRY for all platforms that use smd directory #define CC_OUTERTRY #if defined(_M_IX86) #define __OffsetToAddress(a, b, c) OffsetToAddress(a, b) #define DC_CONTEXTRECORD(pDC) (NULL) #else #define __OffsetToAddress(a, b, c) _OffsetToAddress(a, b, c) #define DC_CONTEXTRECORD(pDC) (pDC->ContextRecord) #endif //////////////////////////////////////////////////////////////////////////////// // // Forward declaration of local functions: // // M00TODO: all these parameters should be declared const extern "C" void __FrameUnwindToState( EHRegistrationNode *, PDISPATCHER_CONTEXT, FuncInfo *, __ehstate_t ); static BOOLEAN FindHandler( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, PDISPATCHER_CONTEXT, FuncInfo *, int ); static void * CallCatchBlock( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, FuncInfo *, void *, int, unsigned long ); static void BuildCatchObject( EHExceptionRecord *, EHRegistrationNode *, HandlerType *, CatchableType * ); static __inline int TypeMatch( HandlerType *, CatchableType *, ThrowInfo * ); static void * AdjustPointer( void *, const PMD& ); static int FrameUnwindFilter( EXCEPTION_POINTERS * ); static BOOLEAN CanThisExceptionObjectBeDestructed( EHExceptionRecord *pExcept, BOOLEAN abnormalTermination ); //////////////////////////////////////////////////////////////////////////////// // // __InternalCxxFrameHandler - the frame handler for all functions with C++ EH // information. // // If exception is handled, this doesn't return; otherwise, it returns // ExceptionContinueSearch. // // extern "C" EXCEPTION_DISPOSITION __cdecl __InternalCxxFrameHandler( EHExceptionRecord *pExcept, // Information for this exception EHRegistrationNode *pRN, // Dynamic information for this frame CONTEXT *pContext, // Context info PDISPATCHER_CONTEXT pDC, // Context within subject frame FuncInfo *pFuncInfo, // Static information for this frame int CatchDepth // How deeply nested are we? ) { DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: ENTER\r\n"))); DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: pExcept=%08x pFuncInfo=%08x pRN=%08x CatchDepth=%d\r\n"), pExcept, pFuncInfo, pRN, CatchDepth)); DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: ExceptionCode=%08x ExceptionFlags=%08x\r\n"), pExcept->ExceptionCode, pExcept->ExceptionFlags)); DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: pDC->ControlPc=%08x\r\n"), pDC->ControlPc)); DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: pExceptionObject=%08x pThrowInfo= %08x\r\n"), PER_PEXCEPTOBJ(pExcept), PER_PTHROW(pExcept))); if ((PER_CODE(pExcept) != EH_EXCEPTION_NUMBER) && (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. DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: unwinding ...\r\n"))); if ( IS_TARGET_UNWIND(PER_FLAGS(pExcept) )){ DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: IS_TARGET_UNWIND\r\n"))); __ehstate_t targetState = TBME_LOW(*TargetEntry); DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: targetState=%d from TargetEntry=%08x\r\n"), targetState, TargetEntry)); __FrameUnwindToState(pRN, pDC, pFuncInfo, targetState); #if defined(_M_IX86) SetState(pRN, TBME_HIGH(*TargetEntry)+1); #endif // _M_IX86 DEBUGMSG(DBGEH,(TEXT("__InternalCxxFrameHandler: after unwind, call CallCatchBlock\r\n"))); void * continuationAddress = CallCatchBlock(pExcept, pRN, pContext, pFuncInfo, (void *)CONTEXT_TO_PROGRAM_COUNTER(pContext), CatchDepth, 0x100); DEBUGMSG(DBGEH,(TEXT("from CallCatchBlock: continuationAddress %08x\r\n"), continuationAddress)); CONTEXT_TO_PROGRAM_COUNTER(pContext) = (ULONG)continuationAddress; } else if ( #ifdef _M_IX86 FUNC_MAXSTATE(*pFuncInfo) != 0 && CatchDepth == 0 #else FUNC_MAXSTATE(*pFuncInfo) != 0 #endif // _M_IX86 ) { // Only unwind if there's something to unwind DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler: Unwind to empty state\n"))); __FrameUnwindToState(pRN, pDC, pFuncInfo, EH_EMPTY_STATE); } } else if (FUNC_NTRYBLOCKS(*pFuncInfo) != 0) { // // If try blocks are present, search for a handler: // DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler: NTRYBLOCKS=%d\r\n"), FUNC_NTRYBLOCKS(*pFuncInfo))); DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler: call FindHandler\r\n"))); if ( FindHandler(pExcept, pRN, pContext, pDC, pFuncInfo, CatchDepth) ) { DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler: EXIT with pExcept(%08x): Handler Found %08x\n"), pExcept, pDC->ControlPc)); return ExceptionExecuteHandler; } // If it returned FALSE, we didn't have any matches. DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler: No Handler Found\r\n"))); } // We had nothing to do with it or it was rethrown. Keep searching. DEBUGMSG(DBGEH,(TEXT("InternalCxxFrameHandler EXIT with pExcept(%08x): ExceptionContinueSearch\r\n"), pExcept)); return ExceptionContinueSearch; } // InternalCxxFrameHandler #ifndef _M_IX86 // // __CxxFrameHandler - Real entry point to the runtime // extern "C" _CRTIMP EXCEPTION_DISPOSITION __CxxFrameHandler3( EHExceptionRecord *pExcept, // Information for this exception EHRegistrationNode *pRN, // Dynamic information for this frame CONTEXT *pContext, // Context info PDISPATCHER_CONTEXT pDC // More dynamic info for this frame ) { FuncInfo *pFuncInfo; EXCEPTION_DISPOSITION result; pFuncInfo = (FuncInfo*)pDC->FunctionEntry->HandlerData; result = __InternalCxxFrameHandler(pExcept, pRN, pContext, pDC, pFuncInfo, 0); return result; } // __CxxFrameHandler #endif // ifndef _M_IX86 //////////////////////////////////////////////////////////////////////////////// // // FindHandler - find a matching handler on this frame, using all means // available. // // Description: // If the exception thrown was an MSC++ EH, search handlers for match. // Otherwise, if we haven't already recursed, try to translate. // If we have recursed (ie we're handling the translator's exception), and // it isn't a typed exception, call _inconsistency. // // Returns: // Returns TRUE if a handler was found // // Assumptions: // Only called if there are handlers in this function. static BOOLEAN FindHandler( EHExceptionRecord *pExcept, // Information for this (logical) exception EHRegistrationNode *pRN, // Dynamic information for subject frame CONTEXT *pContext, // Context info PDISPATCHER_CONTEXT pDC, // Context within subject frame FuncInfo *pFuncInfo, // Static information for subject frame int CatchDepth // Level of nested catch that is being checked ) { BOOLEAN Found = FALSE; EHExceptionRecord *pNewExcept = pExcept ; EHExceptionRecord *pRethrowException = NULL ; // Get the current state (machine-dependent) __ehstate_t curState = GetCurrentState(pRN, pDC, pFuncInfo); DEBUGMSG(DBGEH,(TEXT("FindHandler: curState=GetCurrentState=%d\r\n"), curState)); DEBUGCHK(curState >= EH_EMPTY_STATE && curState < FUNC_MAXSTATE(*pFuncInfo)); // Check if it's a re-throw ( ie. no new object to throw ). // Use the exception we stashed away if it is. if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) { DEBUGMSG(DBGEH,(TEXT("FindHandler: pExcept(0x%08x) is a rethrow of _pCurrentException(0x%08x)\r\n"), pExcept, _pCurrentException)); if (_pCurrentException == NULL) { // Oops! User re-threw a non-existant exception! Let it propogate. return FALSE; } if ( PER_PTHROW(_pCurrentException) == NULL) { DEBUGMSG(DBGEH,(TEXT("FindHandler: ThrowInfo is NULL\n"))) ; #ifdef DBGEH _exit (3) ; #endif } pExcept = _pCurrentException; pContext = _pCurrentExContext; DEBUGMSG(DBGEH,(TEXT("FindHandler: reset pExcept=%08x pContext=%08x from _pCurrentException\r\n"), pExcept, pContext)); pRethrowException = pExcept; DEBUGMSG(DBGEH,(TEXT("FindHandler: rethrow: setting pRethrowException=%08x\r\n"), pRethrowException)); DEBUGCHK(!PER_IS_MSVC_EH(pExcept) || PER_PTHROW(pExcept) != NULL); } #if !defined(_M_IX86) FRAMEINFO *pFrameInfo; if (pFrameInfo = FindFrameInfo(pRN)) { // // This frame called a catch block (after unwinding). reset curState // to the state reached when the unwind occurred. NestLevel is the // nesting level of the called catch block. This frame's nest level // is one less. // curState = pFrameInfo->state; CatchDepth = pFrameInfo->NestLevel - 1; DEBUGMSG(DBGEH,(TEXT("FindHandler: from FindFrameInfo : curState: %d CatchDepth: %d "), curState, CatchDepth)); DEBUGMSG(DBGEH,(TEXT(" FindHandler: pFrameInfo->pRN : %8.8x pFrameInfo->pCatchRN : %8.8lx\r\n"), pFrameInfo->pRN, pFrameInfo->pCatchRN)); } else if ( pFrameInfo = FindCatchFrameInfo(pRN)) { // // This frame is a catch block which called a nested catch block. // Dispatch based on the nested block's level. // CatchDepth = pFrameInfo->NestLevel; DEBUGMSG(DBGEH,(TEXT("FindHandler: from FindCatchFrameInfo: curState: %d CatchDepth: %d "), curState, CatchDepth)); DEBUGMSG(DBGEH,(TEXT("FindHandler: pFrameInfo->pRN : %8.8x pFrameInfo->pCatchRN : %8.8lx\r\n"), pFrameInfo->pRN, pFrameInfo->pCatchRN)); } else { DEBUGMSG(DBGEH,(TEXT("FindHandler: ====Couldn't find FrameInfo/CatchFrameInfo from pFrameInfoChain(%08x)\r\n"), pFrameInfoChain)); DEBUGMSG(DBGEH,(TEXT("FindHandler: with pRN=%8.8x CatchDepth=%d curState=%d===\r\n"), pRN, CatchDepth, curState)); } #endif // Determine range of try blocks to consider: // Only try blocks which are at the current catch depth are of interest. unsigned int curTryBlockIndex; // the index of current try block. unsigned int endTryBlockIndex; // the index of try block to end search, itself excluded TryBlockMapEntry *pEntry = _GetRangeOfTrysToCheck(pFuncInfo, CatchDepth, curState, &curTryBlockIndex, &endTryBlockIndex); DEBUGMSG(DBGEH,(TEXT("FindHandler: TBME=%08x curTryBlockIndex=%d endTryBlockIndex=%d curState=%d\r\n"), pEntry, curTryBlockIndex, endTryBlockIndex, curState)); // Scan the try blocks in the function: for (; curTryBlockIndex < endTryBlockIndex; curTryBlockIndex++, pEntry++) { DEBUGMSG(DBGEH,(TEXT("FindHandler: Scanning Try Block: pEntry=%08x curTryBlockIndex=%d curState=%d\r\n"), pEntry, curTryBlockIndex, curState)); DEBUGMSG(DBGEH,(TEXT("FindHandler: TBME_LOW=%d TBME_HIGH=%d\r\n"), TBME_LOW(*pEntry), TBME_HIGH(*pEntry))); if (TBME_LOW(*pEntry) > curState || curState > TBME_HIGH(*pEntry)) { DEBUGMSG(DBGEH,(TEXT("FindHandler: curState(%d) not in range. Skipping Try Block(%d)\r\n"), curState, curTryBlockIndex)); continue; } #if !defined(_M_IX86) // // Only consider catch blocks at a higher nesting level: // DEBUGMSG(DBGEH,(TEXT("FindHandler: CatchDepth=%d HT_FRAMENEST=%d\r\n"), CatchDepth, HT_FRAMENEST(TBME_CATCH(*pEntry, 0)))); if ( CatchDepth >= (int)HT_FRAMENEST(TBME_CATCH(*pEntry, 0)) ){ DEBUGMSG(DBGEH,(TEXT("FindHandler: Skipping Enclosing Catch Block. \r\n"))); continue; } #endif // !defined(_M_IX86) DEBUGMSG(DBGEH,(TEXT("FindHandler: In Try block scope, TBME_NCATCHES=%d\r\n"), TBME_NCATCHES(*pEntry))); // Try block was in scope for current state. Scan catches for this try: for (int catchIndex = 0; catchIndex < TBME_NCATCHES(*pEntry); catchIndex++){ HandlerType *pCatch; CatchableType * const *ppCatchable; // get pCatch with catchIndex pCatch = TBME_PCATCH(*pEntry, catchIndex); DEBUGMSG(DBGEH,(TEXT("FindHandler: catchIndex(%d) pCatch=%08x is trying to catch\r\n"), catchIndex, pCatch)); if (!PER_IS_MSVC_EH(pExcept)){ // // Non EH exception, check for an ellipsis handler // DEBUGMSG(DBGEH,(TEXT("FindHandler: Non Eh\r\n"))); Found = (HT_IS_TYPE_ELLIPSIS(TBME_CATCH(*pEntry, catchIndex)) && (!HT_IS_STD_DOTDOT(TBME_CATCH(*pEntry, catchIndex)))); ppCatchable = NULL; } else { // // Scan all types that thrown object can be converted to: // DEBUGMSG(DBGEH,(TEXT("FindHandler: to scan %d thrown objects ...\r\n"), THROW_COUNT(*PER_PTHROW(pExcept)))); ppCatchable = THROW_CTLIST(*PER_PTHROW(pExcept)); for (int catchableIndex = 0; catchableIndex < THROW_COUNT(*PER_PTHROW(pExcept)); catchableIndex++, ppCatchable++) { DEBUGMSG(DBGEH,(TEXT("FindHandler: catchableIndex(%d) ppCatchable=%08x properties=%08x pCatch=%08x\r\n"), catchableIndex, ppCatchable, CT_PROPERTIES(**ppCatchable), pCatch)); if (TypeMatch(pCatch, *ppCatchable, PER_PTHROW(pExcept))) { DEBUGMSG(DBGEH,(TEXT("FindHandler: Found: *ppCatchable=%08x TypeMatched pCatch=%08x\r\n"), *ppCatchable, pCatch)); Found = TRUE; break; } } // Scan possible conversions } // MSVC_EH exception if ( Found ) { DEBUGMSG(DBGEH,(TEXT("FindHandler: pNewExcept(0x%08x) obj(0x%08x) pthrow(0x%08x)\r\n"), pNewExcept, PER_PEXCEPTOBJ(pNewExcept), PER_PTHROW(pNewExcept))); _pCurrExceptRethrow = pRethrowException; if (pRethrowException) { DEBUGMSG(DBGEH,(TEXT("FindHandler: set pNewExcept(0x%08x) obj|pthrow to be pExcept(0x%08x)\r\n"), pNewExcept, pExcept)); PER_PEXCEPTOBJ(pNewExcept) = PER_PEXCEPTOBJ(pExcept); PER_PTHROW(pNewExcept) = PER_PTHROW(pExcept); DEBUGMSG(DBGEH,(TEXT("FindHandler: pNewExcept(0x%08x) obj(0x%08x) pthrow(0x%08x)\r\n"), pNewExcept, PER_PEXCEPTOBJ(pNewExcept), PER_PTHROW(pNewExcept))); } // // A handler was found. Save current state and build a // catch object. // TargetEntry = pEntry; DEBUGMSG(DBGEH,(TEXT("FindHandler: Catch Block Found: Handler=%08x TargetState=%d\r\n"), HT_HANDLER(*pCatch), TBME_LOW(*pEntry))); DEBUGMSG(DBGEH,(TEXT("FindHandler: set TargetEntry=pEntry=%08x\r\n"), pEntry)); // // Set the Unwind continuation address to the address of // the catch handler. // pDC->ControlPc = (ULONG)HT_HANDLER(*pCatch); if (ppCatchable && *ppCatchable != NULL) { #if defined(_M_ARM) pRN = AdjustFrameForCatch(pRN, pFuncInfo); #endif DEBUGMSG(DBGEH,(TEXT("FindHandler: Call BuildCatchObject\r\n"))); BuildCatchObject(pExcept, pRN, pCatch, *ppCatchable); } DEBUGMSG(DBGEH,(TEXT("FindHandler: EXIT handler Found\r\n"))); return TRUE; } } // Scan catch clauses } // Scan try blocks DEBUGMSG(DBGEH,(TEXT("FindHandler: EXIT return search again\r\n"))); return FALSE; } // FindHandler //////////////////////////////////////////////////////////////////////////////// // // TypeMatch - Check if the catch type matches the given throw conversion. // // Returns: // TRUE if the catch can catch using this throw conversion, FALSE otherwise. static __inline int TypeMatch( HandlerType *pCatch, // Type of the 'catch' clause CatchableType *pCatchable, // Type conversion under consideration ThrowInfo *pThrow // General information about the thrown type. ) { // First, check for match with ellipsis: if (HT_IS_TYPE_ELLIPSIS(*pCatch)) { return TRUE; } // Not ellipsis; the basic types match if it's the same record *or* the // names are identical. if (HT_PTD(*pCatch) != CT_PTD(*pCatchable) && strcmp(HT_NAME(*pCatch), CT_NAME(*pCatchable)) != 0) { return FALSE; } // Basic types match. The actual conversion is valid if: // caught by ref if ref required *and* // the qualifiers are compatible *and* // the alignments match *and* // the volatility matches return (!CT_BYREFONLY(*pCatchable) || HT_ISREFERENCE(*pCatch)) && (!THROW_ISCONST(*pThrow) || HT_ISCONST(*pCatch)) #if !defined(_M_IX86) && (!THROW_ISUNALIGNED(*pThrow) || HT_ISUNALIGNED(*pCatch)) #endif && (!THROW_ISVOLATILE(*pThrow) || HT_ISVOLATILE(*pCatch)); } // TypeMatch //////////////////////////////////////////////////////////////////////////////// // // FrameUnwindFilter - Allows possibility of continuing through SEH during // unwind. // static int FrameUnwindFilter( EXCEPTION_POINTERS *pExPtrs ) { EHExceptionRecord *pExcept = (EHExceptionRecord *)pExPtrs->ExceptionRecord; switch (PER_CODE(pExcept)) { case EH_EXCEPTION_NUMBER: __ProcessingThrow = 0; std::terminate(); #ifdef ALLOW_UNWIND_ABORT case EH_ABORT_FRAME_UNWIND_PART: return EXCEPTION_EXECUTE_HANDLER; #endif default: return EXCEPTION_CONTINUE_SEARCH; } } //////////////////////////////////////////////////////////////////////////////// // // __FrameUnwindToState - Unwind this frame until specified state is reached. // // Returns: // No return value. // // Side Effects: // All objects on frame which go out of scope as a result of the unwind are // destructed. // Registration node is updated to reflect new state. // // Usage: // This function is called both to do full-frame unwind during the unwind // phase (targetState = -1), and to do partial unwinding when the current // frame has an appropriate catch. extern "C" void __FrameUnwindToState ( EHRegistrationNode *pRN, // Registration node for subject function PDISPATCHER_CONTEXT pDC, // Context within subject frame FuncInfo *pFuncInfo, // Static information for subject function __ehstate_t targetState // State to unwind to ) { DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: ENTER: pRN=%08x pDC->ControlPc=%08x targetState=%d\r\n"), pRN, pDC->ControlPc, targetState)); __ehstate_t curState = GetCurrentState(pRN, pDC, pFuncInfo); __ProcessingThrow++; DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: curState=GetCurrentState=%d\r\n"), curState)); #if !defined(_M_IX86) // // If unwinding from a catch block which threw, use the unwind state from // the catch block. (CatchExceptRN is set in CallCatchBlock) // DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: CatchExceptRN=%08x CatchExceptState=%d\r\n"), CatchExceptRN, CatchExceptState)); if ( pRN == CatchExceptRN ) { curState = CatchExceptState; DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: pRN=CatchExceptRN=%08x\r\n"), pRN)); DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: reset curState=%d\r\n"), curState)); } CatchExceptRN = 0; #endif // !defined(_M_IX86) __try { while (curState != targetState) { // The unwind-map may have a shortcut by using EH_EMPTY_STATE if ( curState == EH_EMPTY_STATE ){ break; } DEBUGCHK((curState > EH_EMPTY_STATE) && (curState < FUNC_MAXSTATE(*pFuncInfo))); DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: curState=%d\r\n"), curState)); __try { // Call the unwind action (if one exists): if (UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)) != NULL) { #if defined(_M_ARM) pRN = FindFrameForUnwind (pRN, pFuncInfo); DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: ARM adjusted pRN=%08x\r\n"), UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)), pRN)); // pass correct frame pointer for unwind funclet. In ARM // the frame pointer is not stored in stack when catch // block is called. so we need to pass the correct frame // to access the locals #endif // _M_ARM DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: to call _CallSettingFrame with action @%08x pRN=%08x\r\n"), UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)), pRN)); _CallSettingFrame(UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)), pRN, DC_CONTEXTRECORD(pDC), NULL); } else { DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: no action for curState=%d\r\n"), curState)); } } __except(FrameUnwindFilter(exception_info())) { } // Adjust the state: curState = UWE_TOSTATE(FUNC_UNWIND(*pFuncInfo, curState)); DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: adjust curState=%d\r\n"), curState)); } } __finally { if (__ProcessingThrow > 0) { __ProcessingThrow--; } } DEBUGCHK(curState == targetState); DEBUGMSG(DBGEH,(TEXT("__FrameUnwindToState: EXIT curState=targetState=%d\r\n"), curState)); } // __FrameUnwindToState #define IS_RETHROW(pNewExcept, pExcept) \ ( pNewExcept && pExcept && pNewExcept!=pExcept \ && PER_PEXCEPTOBJ(pNewExcept)==PER_PEXCEPTOBJ(pExcept) \ && PER_PTHROW(pNewExcept)==PER_PTHROW(pExcept)) //////////////////////////////////////////////////////////////////////////////// // // CanThisExceptionObjectBeDestructed // // This function is invoked to determine that exception object can be destroyed // or not: // // return TRUE means that the object can be destructed; FALSE otherwise. // // Note: // The object we are trying to delete is from the throw line, constructed by // copy constructor. // Example: throw a0; the copy of a0 is the exception object that // we are talking about here. // // _pCurrentException is actually the previous exception besides pExcept // static BOOLEAN CanThisExceptionObjectBeDestructed(EHExceptionRecord *pExcept, BOOLEAN abnormalTermination) { BOOLEAN ret = FALSE; DEBUGMSG(DBGEH,(TEXT("Checking: pExcept(%08x), obj(%08x), abnormalTermination(%d)\r\n"), pExcept, PER_PEXCEPTOBJ(pExcept), abnormalTermination)); DEBUGMSG(DBGEH,(TEXT("Checking: _pCurrExceptRethrow(%08x), _pCurrentException(%08x)\r\n"), _pCurrExceptRethrow, _pCurrentException)); if ( abnormalTermination ) { // as long as pExcept is not the same as _pCurrExceptRethrow if ( pExcept!=_pCurrExceptRethrow ) { ret = TRUE; } } else { // Important point: // Just because we found normal exit from a catch does not imply that we can destroy // the source of the local exception object (original exception object). This is // because a catch may be nested inside another. After exiting a nested catch, we // may find a rethrow of the original exception object. For this reason, we can only // destroy the original exception object once we have left the scope of the catch which // caught it. // // For normal termination we destroy pExcept if any of the following are true: // 1) No pCurrExceptRethrow at all. // 2) pExcept is the same of pCurrExceptRethrow, and pExcept itself // is not a rethrow of _pCurrentException. // 3) Exception is rethrow and _pCurrExceptRethrow is not the previous recorded exception // because another rethrow might be thrown. if ( !_pCurrExceptRethrow || ( pExcept==_pCurrExceptRethrow && !IS_RETHROW(pExcept, _pCurrentException) ) || ( IS_RETHROW((EHExceptionRecord *)_pCurrExceptRethrow, pExcept) && _pCurrExceptRethrow!=_pCurrentException ) ) { ret = TRUE; } } DEBUGMSG(DBGEH,(TEXT("Checking: EXIT ret:%08x\r\n"), ret)); return ret; } //////////////////////////////////////////////////////////////////////////////// // // CallCatchBlock // // This function is called after the shx\target frame has been unwound (all unwind // actions have been performed. The catch block is now invoked. // // 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 unsigned long NLGCode // NLG destination code ) { // 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; #if !defined(_M_IX86) FRAMEINFO NewFrame; #if defined(_M_ARM) CATCHFRAMEINFO NewCatchFrame; #endif // _M_ARM #endif // !defined(_M_IX86) DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: ENTER, to invoke handler @%08x pRN=%08x\r\n"), handlerAddress, pRN)); // 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 ; CONTEXT *pSaveExContext ; // Save the current rethrow exception. This is because rethrows can be nested. To be // restored when the catch exits. EHExceptionRecord *pSaveRethrowException ; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: save _pCurrentException=%08x _pCurrentExContext=%08x\r\n"), _pCurrentException, _pCurrentExContext)); pSaveException = _pCurrentException; pSaveExContext = _pCurrentExContext; pSaveRethrowException = (EHExceptionRecord *)_pCurrExceptRethrow; _pCurrentException = pExcept; _pCurrentExContext = pContext; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: put pExcept=%08x into _pCurrentException\r\n"), pExcept)); DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: put pContext=%08x into _pCurrentExContext\r\n"), pContext)); __try { #if defined(_M_IX86) // The current stack pointer is stored just ahead of the // registration node. We need to add it to the context // structure (for the continuation) before calling the catch // block because it will be changed in the catch block if // there's a nested try/catch block. pContext->Esp = *((unsigned long*)pRN - 1); continuationAddress = _CallCatchBlock2(pRN, pFuncInfo, handlerAddress, CatchDepth, 0); // Get frame pointer by looking past the exception // registration node on the stack per __CallSettingFrame pContext->Ebp = (unsigned long) pRN + sizeof(EHRegistrationNode); #else DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: Fill NewFrame fields all but pCatchRN...\r\n"))); // this will be set in CallSetttingFrame NewFrame.pCatchRN = NULL; NewFrame.pRN = pRN; NewFrame.state = (FUNC_UNWIND(*pFuncInfo, TBME_LOW(*TargetEntry))).toState; NewFrame.NestLevel = HT_FRAMENEST(TBME_CATCH(*TargetEntry, 0)); NewFrame.next = pFrameInfoChain; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: NewFrame=%08x pRN=%08x state=%d NestLevel=%d next=%08x\r\n"), &NewFrame, NewFrame.pRN, NewFrame.state, NewFrame.NestLevel, NewFrame.next)); pFrameInfoChain = &NewFrame; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: insert NewFrame=%08x into pFrameInfoChain\r\n"), &NewFrame)); #if defined(_M_ARM) NewCatchFrame.pRN=pRN; NewCatchFrame.NestLevel = HT_FRAMENEST(TBME_CATCH(*TargetEntry, 0)); NewCatchFrame.pFuncInfo = pFuncInfo ; NewCatchFrame.next = pCatchChain; pCatchChain = &NewCatchFrame; pRN=FindCatchFrame (pRN, HT_FRAMENEST(TBME_CATCH(*TargetEntry, 0))); #endif // _M_ARM continuationAddress = _CallSettingFrame(handlerAddress, pRN, pContext, &NewFrame ); DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: from _CallSettingFrame: pCatchRN=%08x\r\n"), NewFrame.pCatchRN)); #endif // _M_IX86 DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: from _CallSettingFrame continuationAddress %08x\r\n"), continuationAddress)); } __finally { // DEBUGCHK(pExcept==_pCurrentException); DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: Restore the _pCurrentException\r\n"))); _pCurrentException = pSaveException; _pCurrentExContext = pSaveExContext; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: _pCurrentException=%08x _pCurrentExContext=%08x\r\n"), _pCurrentException, _pCurrentExContext)); // Destroy the original exception object if we're not exiting on a // re-throw. Note that the catch handles destruction of its parameter. DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: pExcept(0x%08x), PER_PTHROW(pExcept):%08x, PER_PEXCEPTOBJ(pExcept):%08x _pCurrentException=%08x\r\n"), pExcept, PER_PTHROW(pExcept), PER_PEXCEPTOBJ(pExcept), _pCurrentException)); #if !defined(_M_IX86) DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: abnormal_termination(%d) _pCurrExceptRethrow(0x%08x)\r\n"), abnormal_termination(), _pCurrExceptRethrow)); #endif // !defined(_M_IX86) if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) != NULL && CanThisExceptionObjectBeDestructed(pExcept, abnormal_termination())) { DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: calling DestructExceptionObject pExcept=%08x, obj(%08x)\r\n"), pExcept, PER_PEXCEPTOBJ(pExcept))); _DestructExceptionObject(pExcept, abnormal_termination()); } _pCurrExceptRethrow = pSaveRethrowException; #if !defined(_M_IX86) // // If the catch block raised an exception, save the pRN and state // for unwinding through the associated try block. The lifetime of // these values is from here until the next call to FrameUnwindToState // if (abnormal_termination()) { DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: abnormal_termination. Catch Block Raised an exception "))); CatchExceptRN = NewFrame.pRN; CatchExceptState = NewFrame.state; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: save CatchExceptRN=%08x CatchExceptState=%d from NewFrame\r\n"), CatchExceptRN, CatchExceptState)); } #if defined(_M_ARM) // // Remove current catch frame from list // CATCHFRAMEINFO *lcurFrame = pCatchChain; CATCHFRAMEINFO **lnextFrame = &pCatchChain; while ( lcurFrame != NULL ){ if (lcurFrame == &NewCatchFrame ) { *lnextFrame = lcurFrame->next; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: Removing %08x: pCatchChain=%08x\r\n"), lcurFrame, pCatchChain)); } lnextFrame = &lcurFrame->next; lcurFrame = lcurFrame->next; } #endif // _M_ARM #endif // !defined(_M_IX86) } // finally #ifndef _M_IX86 // to update pFrameInfoChain here. // we remove not only NewFrame when normal_termination occurs, but also residing FrameInfo // which had been created by previous CallCatchBlock while abnormal_termination happened. // If the state of pFrameInfo on the pFrameInfoChain is bigger than NewFrame's state, // we are sure that pFrameInfo is enclosed by NewFrame's state, given that try/catch // can not be overlapped. { FRAMEINFO *prevFrame = NULL; FRAMEINFO *curFrame = pFrameInfoChain; DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: normal_termination. update pFrameChainInfo "))); DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: remove NewFrame(%08x) from pFrameInfoChain(%08x)\r\n"), &NewFrame, pFrameInfoChain)); // iterate the pFrameInfoChain to look for NewFrame to remove // Assumption: stack grows while memory decreases. while ( curFrame != NULL ) { if ( curFrame->pRN <= NewFrame.pRN ) { // curFrame's establisher frame was established after NewFrame's. // curFrame can be safely removed from the chain. DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: Removing curFrame=%08x from pFrameInfoChain=%08x\r\n"), curFrame, pFrameInfoChain)); if ( curFrame == pFrameInfoChain ) { pFrameInfoChain = curFrame->next; } else { prevFrame->next = curFrame->next; } DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: curFrame: pRN=%08x CatchRN=%08x state=%d NestLevel=%d\r\n"), curFrame->pRN, curFrame->pCatchRN, curFrame->state, curFrame->NestLevel)); } prevFrame = curFrame; curFrame = curFrame->next; } DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: After removal NewFrame(%08x), pFrameInfoChain(%08x)\r\n"), &NewFrame, pFrameInfoChain)); } #endif // _M_IX86 DEBUGMSG(DBGEH,(TEXT("CallCatchBlock: EXIT pExcept(%08x) with continuationAddress=%08x\r\n"), pExcept, continuationAddress)); return continuationAddress; } // CallCatchBlock //////////////////////////////////////////////////////////////////////////////// // // BuildCatchObject - Copy or construct the catch object from the object thrown. // // Returns: // nothing. // // Side-effects: // A buffer in the subject function's frame is initialized. // // Open issues: // What happens if the constructor throws? (or faults?) static void BuildCatchObject( EHExceptionRecord *pExcept, // Original exception thrown EHRegistrationNode *pRN, // Registration node of catching function HandlerType *pCatch, // The catch clause that got it CatchableType *pConv // The conversion to use ) { // If the catch is by ellipsis, then there is no object to construct. // If the catch is by type(No Catch Object), then leave too! if (HT_IS_TYPE_ELLIPSIS(*pCatch) || !HT_DISPCATCH(*pCatch)) { return; } void **pCatchBuffer = (void **)__OffsetToAddress(HT_DISPCATCH(*pCatch), pRN, HT_FRAMENEST(*pCatch)); #ifndef _M_IX86 DEBUGMSG(DBGEH,(TEXT("BuildCatchObject pRN=%08x Level=%d\r\n"),pRN, HT_FRAMENEST(*pCatch))); #endif // _M_IX86 __try { if (HT_ISREFERENCE(*pCatch)) { // The catch is of form 'reference to T'. At the throw point we // treat both 'T' and 'reference to T' the same, i.e. // pExceptionObject is a (machine) pointer to T. Adjust as // required. DEBUGMSG(DBGEH,(TEXT("Object thrown is HT_ISREFERENCE\r\n"))); *pCatchBuffer = PER_PEXCEPTOBJ(pExcept); *pCatchBuffer = AdjustPointer(*pCatchBuffer, CT_THISDISP(*pConv)); } else if (CT_ISSIMPLETYPE(*pConv)) { // Object thrown is of simple type (this including pointers) copy // specified number of bytes. Adjust the pointer as required. If // the thing is not a pointer, then this should be safe since all // the entries in the THISDISP are 0. DEBUGMSG(DBGEH,(TEXT("Object thrown is CT_ISSIMPLETYPE\r\n"))); memmove(pCatchBuffer, PER_PEXCEPTOBJ(pExcept), CT_SIZE(*pConv)); if (CT_SIZE(*pConv) == sizeof(void*) && *pCatchBuffer != NULL) { *pCatchBuffer = AdjustPointer(*pCatchBuffer, CT_THISDISP(*pConv)); } } else { DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: Object thrown is UDT\r\n"))); if (CT_COPYFUNC(*pConv) == NULL) { // The UDT had a simple ctor. Adjust in the thrown object, // then copy n bytes. DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: No copy ctor is provided\r\n"))); memmove(pCatchBuffer, AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv)), CT_SIZE(*pConv)); } else { // It's a UDT: make a copy using copy ctor DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: The copy ctor (0x%08x) provided \r\n"), CT_COPYFUNC(*pConv))); if (CT_HASVB(*pConv)) { DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: The class has virtual base 0x%08x\r\n"), CT_HASVB(*pConv))); _CallMemberFunction2((char *)pCatchBuffer, CT_COPYFUNC(*pConv), AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv)), 1); } else { DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: The class has No virtual base CT_HASVB\r\n"))); _CallMemberFunction1((char *)pCatchBuffer, CT_COPYFUNC(*pConv), AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv))); } } } } __except(EXCEPTION_EXECUTE_HANDLER) { DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: Something went wrong when building the catch object.\r\n"))); std::terminate(); } DEBUGMSG(DBGEH,(TEXT("BuildCatchObject: EXIT\r\n"))); } // BuildCatchObject //////////////////////////////////////////////////////////////////////////////// // // _DestructExceptionObject - Call the destructor (if any) of the original // exception object. // // Returns: None. // // Side-effects: // Original exception object is destructed. // // Notes: // If destruction throws any exception, and we are destructing the exception // object as a result of a new exception, we give up. If the destruction // throws otherwise, we let it be. #define __ResetException(a) void _DestructExceptionObject( EHExceptionRecord *pExcept, // The original exception record BOOLEAN fThrowNotAllowed // TRUE if destructor not allowed to throw ) { DEBUGMSG(DBGEH,(TEXT("_DestructExceptionObject: Func=%08x pExcept=%08x\r\n"), THROW_UNWINDFUNC(*PER_PTHROW(pExcept)), pExcept)); if (pExcept != NULL && THROW_UNWINDFUNC(*PER_PTHROW(pExcept)) != NULL) { __try { // M00REVIEW: A destructor has additional hidden arguments, doesn't it? DEBUGMSG(DBGEH,(TEXT("_DestructExceptionObject Func=%08x Obj=%08x\r\n"), THROW_UNWINDFUNC(*PER_PTHROW(pExcept)), PER_PEXCEPTOBJ(pExcept))); #pragma warning(disable:4191) _CallMemberFunction0(PER_PEXCEPTOBJ(pExcept), THROW_UNWINDFUNC(*PER_PTHROW(pExcept))); __ResetException(pExcept); } __except(fThrowNotAllowed ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { // Can't have new exceptions when we're unwinding due to another // exception. std::terminate(); } #pragma warning(default:4191) } } //////////////////////////////////////////////////////////////////////////////// // // AdjustPointer - Adjust the pointer to the exception object to a pointer to a // base instance. // // Output: // The address point of the base. // // Side-effects: // NONE. static void *AdjustPointer( void *pThis, // Address point of exception object const PMD& pmd // Generalized pointer-to-member descriptor ) { char *pRet = (char *)pThis + pmd.mdisp; if (pmd.pdisp >= 0) { pRet += *(ptrdiff_t *)((char *)*(ptrdiff_t *)((char *)pThis + pmd.pdisp) + pmd.vdisp); pRet += pmd.pdisp; } return pRet; } #ifndef _WIN32_WCE /////////////////////////////////////////////////////////////////////////////// // // std::uncaught_exception() - Returns true after completing evaluation of a // throw-expression until either completing // initialization of the exception-declaration in // the matching handler. // bool __cdecl std::uncaught_exception() { return (__ProcessingThrow != 0); } #endif // _WIN32_WCE #if !defined(_M_IX86) // // Used by BuildCatchObject to copy the thrown object. Since catch-handlers are // nested functions, and access variables with up-level addressing, we have // to find the frame of the outer-most parent. // PVOID _OffsetToAddress( ptrdiff_t offset, PULONG pBase, ULONG nesting_level ) { #if defined(_M_ARM) CATCHFRAMEINFO *pCatchFrame = pCatchChain; #endif while( nesting_level > 1 ) { #if defined(_M_ARM) pBase = (PULONG)pCatchFrame->pRN ; DEBUGMSG(DBGEH,(TEXT("pBASE %08x Nest Level %d\r\n"), pBase, pCatchFrame->NestLevel)) ; pCatchFrame = pCatchFrame->next; #elif defined(_MIPS64) // MIPS64 in this context means 64-bit registers pBase = (PULONG)(*(PULONG)((char*)pBase - 8)); #else pBase = (PULONG)(*(PULONG)((char*)pBase - 4)); #endif nesting_level--; } return (PVOID)(((char*)pBase) + (int)offset); } // // This routine returns a corresponding frame info structure if we are executing // from within a catch. Otherwise, NULL is returned. // FRAMEINFO * FindFrameInfo( EHRegistrationNode *pRN ) { FRAMEINFO *pFrameInfo = pFrameInfoChain; while ( pFrameInfo != NULL ){ if ( pFrameInfo->pRN == pRN ) { break; } pFrameInfo = pFrameInfo->next; } return pFrameInfo; } FRAMEINFO * FindCatchFrameInfo( EHRegistrationNode *pRN ) { FRAMEINFO *pFrameInfo = pFrameInfoChain; while ( pFrameInfo != NULL ){ if ( pFrameInfo->pCatchRN == pRN ) { break; } pFrameInfo = pFrameInfo->next; } return pFrameInfo; } #ifdef CC_OUTERTRY // _GetRangeOfTrysToCheck - determine which try blocks are of interest, given // the current catch block nesting depth. We only check the trys at a single // depth. // // Returns: // Address of first try block of interest is returned // pStart and pEnd get the indices of the range in question // This is the version of _GetRangeOfTrysToCheck that would work if // CC_OUTERTRY is defined in the BE. To be compatible with EH format // output by the OLD MIPS compiler, CC_OUTERTRY causes the MIPS-UTC compiler // to emit the outer try index in the try block table instead of the // "high catch state". TryBlockMapEntry* _GetRangeOfTrysToCheck( FuncInfo *pFuncInfo, int CatchDepth, __ehstate_t curState, unsigned *pStart, unsigned *pEnd ) { TryBlockMapEntry *pEntry; unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo); DEBUGCHK( num_of_try_blocks > 0 ); for( unsigned int index = 0; index < num_of_try_blocks; index++ ) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, index); if(curState >= TBME_LOW(*pEntry) && curState <= TBME_HIGH(*pEntry) ) { *pStart = index; // // It would be better to return the end-index itself, but I don't // want to change the caller's code. // *pEnd = TBME_CATCHHIGH(*pEntry) + 1; DEBUGCHK( *pEnd <= num_of_try_blocks && *pStart < *pEnd ); return pEntry; } } *pStart = *pEnd = 0; return NULL; } #else // CC_OUTERTRY // _GetRangeOfTrysToCheck - determine which try blocks are of interest, given // the current catch block nesting depth. We only check the trys at a single // depth. // // Returns: // Address of first try block of interest is returned // pStart and pEnd get the indices of the range in question TryBlockMapEntry *_GetRangeOfTrysToCheck( FuncInfo *pFuncInfo, int CatchDepth, __ehstate_t curState, unsigned *pStart, unsigned *pEnd ) { TryBlockMapEntry *pEntry = FUNC_PTRYBLOCK(*pFuncInfo, 0); int start; int end = FUNC_NTRYBLOCKS(*pFuncInfo) - 1; // // First, find the innermost try block that contains this state. We // must always check this try block. // for (start = 0; start <= end; start++) { if (TBME_LOW(pEntry[start]) <= curState && TBME_HIGH(pEntry[start]) >= curState) { break; } } // We may not check try block if we are already in an associated catch. // We know our current catch depth and that the try blocks are sorted // innermost to outermost. Therefore, we start with the outermost try // block and remove it from list of try blocks to check, if we are in // its catch. We continue working inward until we have accounted for // our current catch depth. if(CatchDepth > 0 && start < end) { for (; CatchDepth > 0 && start < end; end--) { if (TBME_HIGH(pEntry[end]) < curState && curState <= TBME_CATCHHIGH(pEntry[end])) { --CatchDepth; } } end++; } *pStart = start; *pEnd = end + 1; DEBUGCHK(*pEnd >= *pStart && *pEnd <= FUNC_NTRYBLOCKS(*pFuncInfo)); return &(pEntry[start]); } #endif // CC_OUTERTRY #endif // !_M_IX86