/*** *frame.cxx - The frame handler and everything associated with it. * * Copyright (c) 1993-2001, Microsoft Corporation. All rights reserved. * *Purpose: * The frame handler and everything associated with it. * * Entry points: * _CxxFrameHandler - the frame handler. * * Open issues: * Handling re-throw from dynamicly nested scope. * Fault-tolerance (checking for data structure validity). * *Revision History: * 05-20-93 BS Module created * 03-03-94 TL Added Mips specific code * 06-19-94 AD Added Alpha specific code (Al Dosser) * 10-17-94 BWT Disable code for PPC. * 11-23-94 JWM Removed obsolete 'hash' check in TypeMatch(). * 11-29-94 JWM AdjustPointer() now adds in pdisp, not vdisp. * 01-13-95 JWM Added _NLG_Destination struct; dwCode set for catch * blocks & local destructors. * 02-09-95 JWM Mac merge. * 02-10-95 JWM UnhandledExceptionFilter() now called if exception * raised during stack unwind. * 03-22-95 PML Add const for read-only compiler-gen'd structs * 04-14-95 JWM Re-fix EH/SEH exception handling. * 04-17-95 JWM FrameUnwindFilter() must be #ifdef _WIN32. * 04-21-95 JWM _NLG_Destination moved to exsup3.asm (_M_X86 only). * 04-21-95 TGL Added Mips fixes. * 04-27-95 JWM EH_ABORT_FRAME_UNWIND_PART now #ifdef * ALLOW_UNWIND_ABORT. * 05-19-95 DAK Don't initialize the kernel handler * 06-07-95 JWM Various NLG additions. * 06-14-95 JWM Unneeded LastError calls removed. * 06-19-95 JWM NLG no longer uses per-thread data (X86 only). * 09-26-95 AMP PowerMac avoids re-throws to same catch clause * 08-06-95 JWM Typo fixed (Orion #6509); Alpha-specific. * 04-18-97 JWM In __InternalCxxFrameHandler(), 'recursive' changed to * BOOLEAN. * 06-01-97 TGL Added P7 specific code * 08-22-97 TGL More P7 fixes * 11-14-98 JWM Merge with P7 sources. * 02-11-99 TGL EH: correct catch in exe calling dll. * 05-17-99 PML Remove all Macintosh support. * 07-12-99 RDL Image relative fixes under CC_P7_SOFT25. * 10-17-99 PML Update EH state before each unwind action, instead of * once at end (vs7#5419) * 10-19-99 TGL More P7/Win64 fixes * 10-22-99 PML Add EHTRACE support * 12-10-99 GB Add Uncaught exception Support by adding a new function * __uncaught_exception(); * 02-15-99 PML Can't put __try/__finally around call to * _UnwindNestedFrames (vs7#79460) * 03-03-00 GB made __DestructExceptionObject export from dll. * 03-21-00 KBF Check for C++ exception in __CxxExceptionFilter * 03-22-00 PML Remove CC_P7_SOFT25, which is now on permanently. * 03-28-00 GB Check for no buildobj in __CxxExceptionFilter. * 04-06-00 GB Added more functions for com+ eh support. * 04-19-00 GB ComPlus EH bug fixes. * 05-23-00 GB Don't catch BreakPoint generated Exceptions. * 05-30-00 GB ComPlus EH bug fixes. * 06-08-00 RDL VS#111429: IA64 workaround for AV while handling throw. * 06-21-00 GB Fix the difference in order of destruction and * construction depending on inlining. * 07-26-00 GB Fixed multiple destruction problem in COM+ eh. * 08-23-00 GB Fixed problem in BuildCatchObject when called from * __CxxExceptionFilter. * 02-23-01 PML Add __CxxCallUnwindDtor COM+ wrapper (vs7#217108) * 04-09-01 GB Add uncaught_exception support for COM+ C++ App. * 04-13-01 GB Fixed problems with Seh and catch(...). (vc7#236286) * 04-26-01 GB Fixed a problem with a rethrow without a throw * and catch(...) * 06-05-01 GB AMD64 Eh support Added. * 07-03-01 GB Added __CxxCallUnwindVecDtor for COM+. * 07-13-01 GB Rewrite of C++Eh for IA64 and AMD64 * 07-15-01 PML Remove all ALPHA, MIPS, and PPC code * 09-18-01 GB Support for exception specification (Provided by Arturl). * 09-20-01 PML Buildfix: EH_MAGIC_NUMBER1_OLD -> EH_MAGIC_NUMBER1, * EH_MAGIC_NUMBER1 -> EH_MAGIC_NUMBER2. * 09-23-01 GB Rewrite of C++Eh for IA64 * ****/ #include #include #include #include #include #include #include // CRT internal header file #include // This project's versions of standard assert macros #include // Declarations of all types used for EH #include // Declarations of state management stuff #include // User-visible routines for eh #include // Declarations of hook variables and callbacks #include // Routines to handle transfer of control (trnsctrl.asm) #if defined(_M_IA64) /*IFSTRIP=IGN*/ #include #include #include #endif #include // We define CRTIMP2 to remove dependency on msvcprt.dll. #if defined(CRTIMP2) #undef CRTIMP2 #endif #define CRTIMP2 #include // we need to get unexpected, and std::bad_exception from there #pragma hdrstop // PCH is created from here //////////////////////////////////////////////////////////////////////////////// // // Intel x86-specific definitions // #if defined(_M_IX86) #define __GetRangeOfTrysToCheck(a, b, c, d, e, f, g) \ _GetRangeOfTrysToCheck(b, c, d, e, f) #define __FrameUnwindToEmptyState(a, b, c) \ __FrameUnwindToState(a, b, c, EH_EMPTY_STATE); #define __CallSETranslator(a, b, c, d, e, f, g, h) \ _CallSETranslator(a, b, c, d, e, f, g) #define __GetUnwindState(a, b, c) \ GetCurrentState(a, b, c) #define __OffsetToAddress(a, b, c) \ OffsetToAddress(a, b) #define __GetAddress(a, b) \ (a) #define REAL_FP(a, b) \ (a) #define __ResetException(a) //////////////////////////////////////////////////////////////////////////////// // // MIPS-specific definitions // #elif defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ #define __GetRangeOfTrysToCheck(a, b, c, d, e, f, g) \ _GetRangeOfTrysToCheck(a, b, c, d, e, f, g) #define __CallSETranslator(a, b, c, d, e, f, g, h) \ _CallSETranslator(a, b, c, d, e, f, g) #define __GetUnwindState(a, b, c) \ GetCurrentState(a, b, c) #define __OffsetToAddress(a, b, c) \ OffsetToAddress(a, b) #define __GetAddress(a, b) \ (void*)(a) #define REAL_FP(a, b) \ (a) #define __ResetException(a) #ifdef _MT #define pExitContext (*((CONTEXT **)&(_getptd()->_pExitContext))) #define _pForeignExcept (*((EHExceptionRecord **)&(_getptd()->_pForeignException))) #else static CONTEXT *pExitContext = NULL; // context to assist the return to the continuation point EHExceptionRecord *_pForeignExcept = NULL; #endif // _MT // The throw site #undef CT_PTD #define CT_PTD(ct) (CT_PTD_IB(ct, _GetThrowImageBase())) #undef CT_COPYFUNC #define CT_COPYFUNC(ct) ((ct).copyFunction? CT_COPYFUNC_IB(ct, _GetThrowImageBase()):NULL) #undef THROW_FORWARDCOMPAT #define THROW_FORWARDCOMPAT(ti) ((ti).pForwardCompat? THROW_FORWARDCOMPAT_IB(ti, _GetThrowImageBase()):NULL) #undef THROW_COUNT #define THROW_COUNT(ti) THROW_COUNT_IB(ti, _GetThrowImageBase()) #undef THROW_CTLIST #define THROW_CTLIST(ti) THROW_CTLIST_IB(ti, _GetThrowImageBase()) // The catch site #undef HT_HANDLER #define HT_HANDLER(ht) (HT_HANDLER_IB(ht, _GetImageBase())) #undef UWE_ACTION #define UWE_ACTION(uwe) ((uwe).action? UWE_ACTION_IB(uwe, _GetImageBase()):NULL) #undef FUNC_UNWIND #define FUNC_UNWIND(fi,st) (FUNC_PUNWINDMAP(fi,_GetImageBase())[st]) #undef TBME_CATCH #define TBME_CATCH(hm,n) (TBME_PLIST(hm,_GetImageBase())[n]) #undef TBME_PCATCH #define TBME_PCATCH(hm,n) (&(TBME_PLIST(hm,_GetImageBase())[n])) #undef HT_PTD #define HT_PTD(ht) ((TypeDescriptor*)((ht).dispType? HT_PTD_IB(ht,_GetImageBase()):NULL)) #undef abnormal_termination #define abnormal_termination() FALSE #else #error Unrecognized platform #endif extern "C" { typedef struct { unsigned long dwSig; unsigned long uoffDestination; unsigned long dwCode; unsigned long uoffFramePointer; } _NLG_INFO; extern _NLG_INFO _NLG_Destination; } //////////////////////////////////////////////////////////////////////////////// // // Forward declaration of local functions: // // M00TODO: all these parameters should be declared const // The local unwinder must be external (see __CxxLongjmpUnwind in trnsctrl.cpp) extern "C" void __FrameUnwindToState( EHRegistrationNode *, DispatcherContext *, FuncInfo *, __ehstate_t ); static void FindHandler( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, DispatcherContext *, FuncInfo *, BOOLEAN, int, EHRegistrationNode* ); static void CatchIt( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, DispatcherContext *, FuncInfo *, HandlerType *, CatchableType *, TryBlockMapEntry *, int, EHRegistrationNode *, BOOLEAN #if defined (_M_AMD64) || defined (_M_IA64) , BOOLEAN #endif ); static void * CallCatchBlock( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, FuncInfo *, void *, int, unsigned long ); static void BuildCatchObject( EHExceptionRecord *, void *, HandlerType *, CatchableType * ); static __inline int TypeMatch( HandlerType *, CatchableType *, ThrowInfo * ); static void * AdjustPointer( void *, const PMD& ); static void FindHandlerForForeignException( EHExceptionRecord *, EHRegistrationNode *, CONTEXT *, DispatcherContext *, FuncInfo *, __ehstate_t, int, EHRegistrationNode * ); static int FrameUnwindFilter( EXCEPTION_POINTERS * ); static int ExFilterRethrow( EXCEPTION_POINTERS * #if defined(_M_AMD64) || defined(_M_IA64) ,EHExceptionRecord *, int * #endif ); extern "C" void _CRTIMP __DestructExceptionObject( EHExceptionRecord *, BOOLEAN ); static BOOLEAN IsInExceptionSpec( EHExceptionRecord *pExcept, // Information for this (logical) // exception ESTypeList *pFuncInfo // Static information for subject frame ); static void CallUnexpected(ESTypeList* pESTypeList); static BOOLEAN Is_bad_exception_allowed(ESTypeList *pExceptionSpec); // // Make sure the terminate wrapper is dragged in: // static void *pMyUnhandledExceptionFilter = &__CxxUnhandledExceptionFilter; // // This describes the most recently handled exception, in case of a rethrow: // #ifdef _MT #define _pCurrentException (*((EHExceptionRecord **)&(_getptd()->_curexception))) #define _pCurrentExContext (*((CONTEXT **)&(_getptd()->_curcontext))) #define __ProcessingThrow _getptd()->_ProcessingThrow #define _pCurrentFuncInfo (*((ESTypeList **)&(_getptd()->_curexcspec))) #else EHExceptionRecord *_pCurrentException = NULL; CONTEXT *_pCurrentExContext = NULL; int __ProcessingThrow = 0; ESTypeList *_pCurrentFuncInfo = NULL; #endif //////////////////////////////////////////////////////////////////////////////// // // __InternalCxxFrameHandler - the frame handler for all functions with C++ EH // information. // // If exception is handled, this doesn't return; otherwise, it returns // ExceptionContinueSearch. // // Note that this is called three ways: // From __CxxFrameHandler: primary usage, called to inspect whole function. // CatchDepth == 0, pMarkerRN == NULL // From CatchGuardHandler: If an exception occurred within a catch, this is // called to check for try blocks within that catch only, and does not // handle unwinds. // From TranslatorGuardHandler: Called to handle the translation of a // non-C++ EH exception. Context considered is that of parent. 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); DASSERT(FUNC_MAGICNUM(*pFuncInfo) == EH_MAGIC_NUMBER1 || FUNC_MAGICNUM(*pFuncInfo) == EH_MAGIC_NUMBER2); 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_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ if (IS_TARGET_UNWIND(PER_FLAGS(pExcept)) && PER_CODE(pExcept) == STATUS_LONGJUMP) { __ehstate_t target_state = __StateFromIp(pFuncInfo, pDC, #if defined(_M_IA64) pContext->StIIP #elif defined(_M_AMD64) pContext->Rip #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_IA64) if (!(PER_FLAGS(pExcept) & 0x40)) { __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 ) { // 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_MAGICNUM(pExcept) > EH_MAGIC_NUMBER2 && (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) 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) } 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 //////////////////////////////////////////////////////////////////////////////// // // 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 iff exception was not handled. // // Assumptions: // Only called if there are handlers in this function. 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_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ __ehstate_t curState = __StateFromControlPc(pFuncInfo, pDC); EHRegistrationNode EstablisherFrame; _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 defined(_M_IA64) || defined(_M_AMD64)/*IFSTRIP=IGN*/ _SetThrowImageBase((unsigned __int64)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 defined(_M_IA64) || defined(_M_AMD64) __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 defined(_M_IA64) || defined(_M_AMD64) 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_IA64) || defined (_M_AMD64) , recursive #endif ); goto NextTryBlock; } // Scan posible conversions } // Scan catch clauses NextTryBlock: ; } // Scan try blocks } // if FUNC_NTRYBLOCKS( pFuncInfo ) > 0 #if defined(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 (recursive) { // A translation was provided, but this frame didn't catch it. // Destruct the translated object before returning; if destruction // raises an exception, issue _inconsistency. __DestructExceptionObject(pExcept, TRUE); } // // 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_IA64) || defined (_M_AMD64)/*IFSTRIP=IGN*/ EHRegistrationNode *pEstablisher = pRN; EHRegistrationNode EstablisherFramePointers; pEstablisher = _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFramePointers); PVOID pExceptionObjectDestroyed = NULL; CONTEXT ExitContext; pExitContext = &ExitContext; _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 } } } // 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. We're outa here! // M00REVIEW: Two choices here actually: we could let the new // exception take over. terminate(); } } } // It wasn't our exception DASSERT( _pCurrentFuncInfo == NULL ); // never leave it initialized with something EHTRACE_EXIT; } //////////////////////////////////////////////////////////////////////////////// // // FindHandlerForForeignException - We've got an exception which wasn't ours. // Try to translate it into C++ EH, and also check for match with ellipsis. // // Description: // If an SE-to-EH translator has been installed, call it. The translator // must throw the appropriate typed exception or return. If the translator // throws, we invoke FindHandler again as the exception filter. // // Returns: // Returns if exception was not fully handled. // No return value. // // Assumptions: // Only called if there are handlers in this function. static void FindHandlerForForeignException( 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 __ehstate_t curState, // Current state int CatchDepth, // Level of nested catch that is being // checked EHRegistrationNode *pMarkerRN // Extra marker RN for nested catch // handling ) { EHTRACE_ENTER; unsigned curTry; unsigned end; TryBlockMapEntry *pEntry; // We don't want to touch BreakPoint generated Exception. if (PER_CODE(pExcept) == STATUS_BREAKPOINT) { EHTRACE_EXIT; return; } if (__pSETranslator != NULL) { // Call the translator. If the translator knows what to // make of it, it will throw an appropriate C++ exception. // We intercept it and use it (recursively) for this // frame. Don't recurse more than once. if (__CallSETranslator(pExcept, pRN, pContext, pDC, pFuncInfo, CatchDepth, pMarkerRN, TDTransOffset)) { EHTRACE_EXIT; return; } } DASSERT( FUNC_NTRYBLOCKS(*pFuncInfo) != 0 ); // Didn't have a translator, or the translator returned normally (i.e. // didn't translate it). Still need to check for match with ellipsis: pEntry = __GetRangeOfTrysToCheck(pRN, pFuncInfo, CatchDepth, curState, &curTry, &end, pDC); // Scan the try blocks in the function: for (; curTry < end; curTry++, pEntry++) { // If the try-block was in scope *and* the last catch in that try is an // ellipsis (no other can be) if (curState < TBME_LOW(*pEntry) || curState > TBME_HIGH(*pEntry) || !HT_IS_TYPE_ELLIPSIS(TBME_CATCH(*pEntry, TBME_NCATCHES(*pEntry) - 1))) { continue; } // Found an ellipsis. Handle exception. CatchIt(pExcept, pRN, pContext, pDC, pFuncInfo, TBME_PCATCH(*pEntry, TBME_NCATCHES(*pEntry) - 1), NULL, pEntry, CatchDepth, pMarkerRN, TRUE #if defined(_M_IA64) || defined(_M_AMD64) ,FALSE #endif ); // If it returns, handler re-threw. Keep searching. } // Search for try EHTRACE_EXIT; // If we got here, that means we didn't have anything to do with the // exception. Continue search. } //////////////////////////////////////////////////////////////////////////////// // // 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_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ && (!THROW_ISUNALIGNED(*pThrow) || HT_ISUNALIGNED(*pCatch)) #endif && (!THROW_ISVOLATILE(*pThrow) || HT_ISVOLATILE(*pCatch)); } //////////////////////////////////////////////////////////////////////////////// // // FrameUnwindFilter - Allows possibility of continuing through SEH during // unwind. // static int FrameUnwindFilter( EXCEPTION_POINTERS *pExPtrs ) { EHTRACE_ENTER; EHExceptionRecord *pExcept = (EHExceptionRecord *)pExPtrs->ExceptionRecord; switch (PER_CODE(pExcept)) { case EH_EXCEPTION_NUMBER: __ProcessingThrow = 0; terminate(); #ifdef ALLOW_UNWIND_ABORT case EH_ABORT_FRAME_UNWIND_PART: EHTRACE_EXIT; return EXCEPTION_EXECUTE_HANDLER; #endif default: EHTRACE_EXIT; 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 DispatcherContext *pDC, // Context within subject frame FuncInfo *pFuncInfo, // Static information for subject // function __ehstate_t targetState // State to unwind to ) { EHTRACE_ENTER; #if defined(_M_AMD64) || defined(_M_IA64) __ehstate_t curState = __GetCurrentState(pRN, pDC, pFuncInfo); #else __ehstate_t curState = __GetUnwindState(pRN, pDC, pFuncInfo); #endif __ProcessingThrow++; __try { #if defined(_M_IA64) || defined(_M_AMD64) while (curState != EH_EMPTY_STATE && curState > targetState) #else while (curState != targetState) #endif { DASSERT((curState > EH_EMPTY_STATE) && (curState < FUNC_MAXSTATE(*pFuncInfo))); // Get state after next unwind action __ehstate_t nxtState = UWE_TOSTATE(FUNC_UNWIND(*pFuncInfo, curState)); __try { // Call the unwind action (if one exists): if (UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)) != NULL) { // Before calling unwind action, adjust state as if it were // already completed: #if defined(_M_AMD64) || defined (_M_IA64) __SetState(pRN, pDC, pFuncInfo, nxtState); #else SetState(pRN, pDC, pFuncInfo, nxtState); #endif EHTRACE_FMT2("Unwind from state %d to state %d", curState, nxtState); _CallSettingFrame(__GetAddress(UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)), pDC), REAL_FP(pRN, pFuncInfo), 0x103); } } __except(EHTRACE_EXCEPT(FrameUnwindFilter(exception_info()))) { } curState = nxtState; } } __finally { if (__ProcessingThrow > 0) { __ProcessingThrow--; } } // Now that we're done, set the frame to reflect the final state. #if defined(_M_IA64) || defined(_M_AMD64) DASSERT(curState == EH_EMPTY_STATE || curState <= targetState); #else DASSERT(curState == targetState); #endif EHTRACE_FMT2("Move from state %d to state %d", __GetUnwindState(pRN, pDC, pFuncInfo), curState); #if defined(_M_IA64) || defined(_M_AMD64) __SetState(pRN, pDC, pFuncInfo, curState); #else SetState(pRN, pDC, pFuncInfo, curState); #endif EHTRACE_EXIT; } //////////////////////////////////////////////////////////////////////////////// // // 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. // // M00REVIEW: It is still 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_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ , BOOLEAN recursive #endif // defined(_POWERPC) ) { EHTRACE_ENTER_FMT1("Catching object @ 0x%p", PER_PEXCEPTOBJ(pExcept)); EHRegistrationNode *pEstablisher = pRN; #if defined(_M_IA64) || defined(_M_AMD64) /*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_IA64) || defined(_M_AMD64)/*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, recursive ); #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 } #if defined(_M_AMD64) || defined(_M_IA64) static void __RethrowException(EHExceptionRecord *pThisException) { RaiseException( pThisException->ExceptionCode, pThisException->ExceptionFlags, pThisException->NumberParameters, (PULONG_PTR)&(pThisException->params) ); } extern "C" void *__CxxCallCatchBlock( EXCEPTION_RECORD *pExcept ) { int rethrow = 0, TranslatedCatch = 0; void *handlerAddress; void *continuationAddress = NULL; FuncInfo *pFuncInfo; FRAMEINFO FrameInfo; CONTEXT *pContext, *pSaveContext; EHRegistrationNode *pEstablisherFrame; EHExceptionRecord *pThisException, *pSaveException, *pForeignException = NULL; pSaveContext = _pCurrentExContext; pSaveException = _pCurrentException; // Copy Necessary Information which is passed from UnwindNestedFrames. pThisException = (EHExceptionRecord *) pExcept->ExceptionInformation[6]; pFuncInfo = (FuncInfo *) pExcept->ExceptionInformation[5]; pContext = (CONTEXT *) pExcept->ExceptionInformation[4]; handlerAddress = (void *) pExcept->ExceptionInformation[2]; pEstablisherFrame = (EHRegistrationNode *)pExcept->ExceptionInformation[1]; _pCurrentException = pThisException; _pCurrentExContext = pContext; FRAMEINFO *pFrameInfo = _CreateFrameInfo(&FrameInfo, (void *)PER_PEXCEPTOBJ(_pCurrentException)); if (pExcept->ExceptionInformation[7]) { // Stuff for SehTralation book keeping // This Exception Object is Translation of Froigen Exception And should // be destroyed in any case. If there is rethrow, throw ForeignException. // IMPRORTANT: One one else can rethrow this Exception Object. TranslatedCatch = 1; pForeignException = _pForeignExcept; _pCurrentException = pForeignException; // End Translation Stuff } __try { __try{ continuationAddress = _CallSettingFrame(handlerAddress, pEstablisherFrame, 0x100); } __except(ExFilterRethrow(exception_info(), pThisException, &rethrow)) { rethrow = 1; if (TranslatedCatch) { // Note in case of rethrow, no one else can convert rethrow to // pThisException. This means only this except will deal with conversion // of rethrow to pThisException. Instead of pThisException, we // will throw original Foreign Exception. Also we will need to // destroy Exception Object of before Raising Foreign Exception. __DestructExceptionObject(pThisException, TRUE); __RethrowException(pForeignException); } else { __RethrowException(pThisException); } } } __finally { _FindAndUnlinkFrame(pFrameInfo); if ( !rethrow && PER_IS_MSVC_EH(pThisException) && _IsExceptionObjectToBeDestroyed(PER_PEXCEPTOBJ(pThisException))) { __DestructExceptionObject(pThisException, TRUE); } _pCurrentException = pSaveException; _pCurrentExContext = pSaveContext; } #if defined(_M_AMD64) UNWINDHELP(*pEstablisherFrame, FUNC_DISPUNWINDHELP(*pFuncInfo)) = -2; #elif defined(_M_IA64) UNWINDHELP(pEstablisherFrame->MemoryStackFp, FUNC_DISPUNWINDHELP(*pFuncInfo)) = -2; #endif return continuationAddress; } //////////////////////////////////////////////////////////////////////////////// // // ExFilterRethrow - Exception filter for re-throw exceptions. // // Returns: // EXCEPTION_EXECUTE_HANDLER - exception was a re-throw // EXCEPTION_CONTINUE_SEARCH - anything else // // Side-effects: sets rethrow = TRUE if exception objects of the two Exception matches static int ExFilterRethrow( EXCEPTION_POINTERS *pExPtrs, EHExceptionRecord *pOldExcept, int *rethrow ) { // Get the exception record thrown (don't care about other info) EHExceptionRecord *pExcept = (EHExceptionRecord *)pExPtrs->ExceptionRecord; *rethrow = 0; if (PER_IS_MSVC_EH(pExcept) && PER_PEXCEPTOBJ(pExcept) == PER_PEXCEPTOBJ(pOldExcept)) *rethrow = 1; // Check if it's ours and it's has no exception information. if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) { *rethrow = 1; return EXCEPTION_EXECUTE_HANDLER; } return EXCEPTION_CONTINUE_SEARCH; } #elif defined(_M_IX86) //////////////////////////////////////////////////////////////////////////////// // // 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()))) { // 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; } //////////////////////////////////////////////////////////////////////////////// // // ExFilterRethrow - Exception filter for re-throw exceptions. // // Returns: // EXCEPTION_EXECUTE_HANDLER - exception was a re-throw // EXCEPTION_CONTINUE_SEARCH - anything else // // Side-effects: NONE. static int ExFilterRethrow( EXCEPTION_POINTERS *pExPtrs ) { // Get the exception record thrown (don't care about other info) EHExceptionRecord *pExcept = (EHExceptionRecord *)pExPtrs->ExceptionRecord; // Check if it's ours and it's has no exception information. if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) { return EXCEPTION_EXECUTE_HANDLER; } else { return EXCEPTION_CONTINUE_SEARCH; } } #endif //////////////////////////////////////////////////////////////////////////////// // // 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 void *pRN, // This is a pointer to the object // that we want to build while doing // COM+ eh. If we are in our own eh, // then this is a Registration node of // catching function HandlerType *pCatch, // The catch clause that got it CatchableType *pConv // The conversion to use ) { EHTRACE_ENTER; // 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) && !HT_ISCOMPLUSEH(*pCatch))) { EHTRACE_EXIT; return; } void **pCatchBuffer; if ( HT_ISCOMPLUSEH(*pCatch)) { pCatchBuffer = (void **)pRN; } else { #if defined(_M_IA64) /*IFSTRIP=IGN*/ pCatchBuffer = (void **)__OffsetToAddress( HT_DISPCATCH(*pCatch), ((EHRegistrationNode *)pRN)->MemoryStackFp, HT_FRAMENEST(*pCatch) ); #elif defined(_M_AMD64) pCatchBuffer = (void **)__OffsetToAddress( HT_DISPCATCH(*pCatch), *((EHRegistrationNode *)pRN), HT_FRAMENEST(*pCatch) ); #else pCatchBuffer = (void **)__OffsetToAddress( HT_DISPCATCH(*pCatch), (EHRegistrationNode *)pRN, HT_FRAMENEST(*pCatch) ); #endif } __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. if (_ValidateRead(PER_PEXCEPTOBJ(pExcept)) && _ValidateWrite(pCatchBuffer)) { *pCatchBuffer = PER_PEXCEPTOBJ(pExcept); *pCatchBuffer = AdjustPointer(*pCatchBuffer, CT_THISDISP(*pConv)); } else { _inconsistency(); // Does not return; TKB } } 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. if (_ValidateRead(PER_PEXCEPTOBJ(pExcept)) && _ValidateWrite(pCatchBuffer)) { memmove(pCatchBuffer, PER_PEXCEPTOBJ(pExcept), CT_SIZE(*pConv)); if (CT_SIZE(*pConv) == sizeof(void*) && *pCatchBuffer != NULL) { *pCatchBuffer = AdjustPointer(*pCatchBuffer, CT_THISDISP(*pConv)); } } else { _inconsistency(); // Does not return; TKB } } else { // Object thrown is UDT. if (CT_COPYFUNC(*pConv) == NULL) { // The UDT had a simple ctor. Adjust in the thrown object, // then copy n bytes. if (_ValidateRead(PER_PEXCEPTOBJ(pExcept)) && _ValidateWrite(pCatchBuffer)) { memmove(pCatchBuffer, AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv)), CT_SIZE(*pConv)); } else { _inconsistency(); // Does not return; TKB } } else { // It's a UDT: make a copy using copy ctor #pragma warning(disable:4191) if (_ValidateRead(PER_PEXCEPTOBJ(pExcept)) && _ValidateWrite(pCatchBuffer) && _ValidateExecute((FARPROC)CT_COPYFUNC(*pConv))) { #pragma warning(default:4191) if (CT_HASVB(*pConv)) { _CallMemberFunction2((char *)pCatchBuffer, CT_COPYFUNC(*pConv), AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv)), 1); } else { _CallMemberFunction1((char *)pCatchBuffer, CT_COPYFUNC(*pConv), AdjustPointer(PER_PEXCEPTOBJ(pExcept), CT_THISDISP(*pConv))); } } else { _inconsistency(); // Does not return; TKB } } } } __except(EHTRACE_EXCEPT(EXCEPTION_EXECUTE_HANDLER)) { // Something went wrong when building the catch object. terminate(); } EHTRACE_EXIT; } //////////////////////////////////////////////////////////////////////////////// // // __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. extern "C" void _CRTIMP __DestructExceptionObject( EHExceptionRecord *pExcept, // The original exception record BOOLEAN fThrowNotAllowed // TRUE if destructor not allowed to // throw ) { EHTRACE_ENTER_FMT1("Destroying object @ 0x%p", PER_PEXCEPTOBJ(pExcept)); if (pExcept != NULL && THROW_UNWINDFUNC(*PER_PTHROW(pExcept)) != NULL) { __try { // M00REVIEW: A destructor has additional hidden arguments, doesn't // it? #if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/ _CallMemberFunction0(PER_PEXCEPTOBJ(pExcept), THROW_UNWINDFUNC_IB(*PER_PTHROW(pExcept),(unsigned __int64)PER_PTHROWIB(pExcept))); #else _CallMemberFunction0(PER_PEXCEPTOBJ(pExcept), THROW_UNWINDFUNC(*PER_PTHROW(pExcept))); #endif __ResetException(pExcept); } __except(EHTRACE_EXCEPT(fThrowNotAllowed ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)) { // Can't have new exceptions when we're unwinding due to another // exception. terminate(); } } EHTRACE_EXIT; } //////////////////////////////////////////////////////////////////////////////// // // 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 += *(__int32 *)((char *)*(ptrdiff_t *)((char *)pThis + pmd.pdisp) #if defined(_WIN64) + (unsigned _int64)pmd.vdisp); #else + pmd.vdisp); #endif pRet += pmd.pdisp; } return pRet; } /////////////////////////////////////////////////////////////////////////////// // // __uncaught_exception() - Returns true after completing of a throw-expression // untils completing initialization of the // exception-declaration in the matching handler. // bool __uncaught_exception() { return (__ProcessingThrow != 0); } #if !defined(_M_AMD64) // Enable&fix for AMD64 when COM+ C++ EH support available there //////////////////////////////////////////////////////////////////////////////// // Model of C++ eh in COM+ // // void func() // { // try { // TryBody(); // } catch (cpp_object o) // { // CatchOBody(); // } catch (...) // { // CatchAllBody(); // } // } // // Turns into this: // // // void func() // { // int rethrow; // // One per try block // int isCxxException; // // One per catch(...) // __try { // TryBody(); // } // __except(__CxxExceptionFilter(exception, // typeinfo(cpp_object), // flags, // &o)) // // This is how it's done already // { // // Begin catch(object) prefix // char *storage = _alloca(__CxxQueryExceptionSize()); // rethrow = false; // __CxxRegisterExceptionObject(exception, // storage); // __try { // __try { // // End catch(object) prefix // CatchOBody(); // // Begin catch(object) suffix // } __except(rethrow = __CxxDetectRethrow(exception), // EXCEPTION_CONTINUE_SEARCH) // {} // } // __finally // { // __CxxUnregisterExceptionObject(storage, // rethrow); // } // // End catch(object) suffix // } // __except(1) // { // // Begin catch(...) prefix // char *storage = _alloca(__CxxQueryExceptionSize()); // rethrow = false; // isCxxException = __CxxRegisterExceptionObject(exception, // storage); // __try // { // __try // { // // End catch(...) prefix // CatchAllBody(); // // Begin catch(...) suffix // } __except(rethrow = __CxxDetectRethrow(exception), // EXCEPTION_CONTINUE_SEARCH) // {} // } __finally // { // if (isCxxException) // __CxxUnregisterExceptionObject(storage, rethrow); // } // // End catch(...) suffix // } // } // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // __CxxExceptionFilter() - Returns EXCEPTION_EXECUTE_HANDLER when the pType // matches with the objects we can catch. Returns // EXCEPTION_CONTINUE_SEARCH when pType is not one of // the catchable type for the thrown object. This // function is made for use with COM+ EH, where they // attempt to do C++ EH as well. // extern "C" int __cdecl __CxxExceptionFilter( void *ppExcept, // Information for this (logical) // exception void *pType, // Info about the datatype. int adjectives, // Extra Info about the datatype. void *pBuildObj // Pointer to datatype. ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there struct _s_HandlerType pCatch; #if defined(_M_IA64) || defined(_M_AMD64) __int32 const *ppCatchable; #elif defined(_WIN64) CatchableType * UNALIGNED const *ppCatchable; #else CatchableType * const *ppCatchable; #endif CatchableType *pCatchable; int catchables; EHExceptionRecord *pExcept; if (!ppExcept) { return EXCEPTION_CONTINUE_SEARCH; } pExcept = *(EHExceptionRecord **)ppExcept; // If catch all, always return EXCEPTION_EXECUTE_HANDLER if ( TD_IS_TYPE_ELLIPSIS((TypeDescriptor *)pType)) { if (PER_IS_MSVC_EH(pExcept)) { if ( PER_PTHROW(pExcept) == NULL) { if ( _pCurrentException == NULL) { return EXCEPTION_CONTINUE_SEARCH; } } } __ProcessingThrow++; return EXCEPTION_EXECUTE_HANDLER; } if (PER_IS_MSVC_EH(pExcept)) { if ( PER_PTHROW(pExcept) == NULL) { if (_pCurrentException == NULL) return EXCEPTION_CONTINUE_SEARCH; pExcept = _pCurrentException; } pCatch.pType = (TypeDescriptor *)pType; pCatch.adjectives = adjectives; SET_HT_ISCOMPLUSEH(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 defined(_M_IA64) || defined(_M_AMD64) pCatchable = (CatchableType *)(_GetThrowImageBase() + *ppCatchable); #else pCatchable = *ppCatchable; #endif if (TypeMatch(&pCatch, pCatchable, PER_PTHROW(pExcept))) { // SucessFull. Now build the object. __ProcessingThrow++; if (pBuildObj != NULL) BuildCatchObject(pExcept, pBuildObj, &pCatch, pCatchable); return EXCEPTION_EXECUTE_HANDLER; } } // Scan posible conversions } #endif // ndef _M_IA64 return EXCEPTION_CONTINUE_SEARCH; } //////////////////////////////////////////////////////////////////////////////// // // __CxxRgisterExceptionObject() - Registers Exception Object and saves it to // This is same as first part of // CallCatchBlock. // extern "C" int __cdecl __CxxRegisterExceptionObject( void *ppExcept, void *pStorage ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there // This function is only called for C++ EH. EHExceptionRecord *pExcept = NULL; FRAMEINFO *pFrameInfo = (FRAMEINFO *)pStorage; EHExceptionRecord **ppSaveException; CONTEXT **ppSaveExContext; ppSaveException = (EHExceptionRecord **)(&pFrameInfo[1]); ppSaveExContext = (CONTEXT **)(&ppSaveException[1]); if (ppExcept != NULL && (*(void **)ppExcept) != NULL) { pExcept = *(EHExceptionRecord **)ppExcept; if (PER_IS_MSVC_EH(pExcept)) { if ( PER_PTHROW(pExcept) == NULL) { // was a rethrow pExcept = _pCurrentException; } } pFrameInfo = _CreateFrameInfo(pFrameInfo, PER_PEXCEPTOBJ(pExcept)); *ppSaveException = _pCurrentException; *ppSaveExContext = _pCurrentExContext; _pCurrentException = pExcept; } else { *ppSaveException = (EHExceptionRecord *)-1; *ppSaveExContext = (CONTEXT *)-1; } __ProcessingThrow--; if ( __ProcessingThrow < 0) __ProcessingThrow = 0; #endif // ndef _M_IA64 return 1; } //////////////////////////////////////////////////////////////////////////////// // // __CxxDetectRethrow() - Looks at the Exception and returns true if rethrow, // false if not a rethrow. This is then used for // destructing the exception object in // __CxxUnregisterExceptionObject(). // extern "C" int __cdecl __CxxDetectRethrow( void *ppExcept ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there EHExceptionRecord *pExcept; if (!ppExcept) return EXCEPTION_CONTINUE_SEARCH; pExcept = *(EHExceptionRecord **)ppExcept; if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) { return EXCEPTION_EXECUTE_HANDLER; } #endif // ndef _M_IA64 return EXCEPTION_CONTINUE_SEARCH; } //////////////////////////////////////////////////////////////////////////////// // // __CxxUnregisterExceptionObject - Destructs Exception Objects if rethrow == // true. Also set __pCurrentException and // __pCurrentExContext() to current value. // extern "C" void __cdecl __CxxUnregisterExceptionObject( void *pStorage, int rethrow ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there FRAMEINFO *pFrameInfo = (FRAMEINFO *)pStorage; EHExceptionRecord **ppSaveException; CONTEXT **ppSaveExContext; ppSaveException = (EHExceptionRecord **)(&pFrameInfo[1]); ppSaveExContext = (CONTEXT **)(&ppSaveException[1]); if (*ppSaveException != (void *)-1) { _FindAndUnlinkFrame(pFrameInfo); if ( !rethrow && PER_IS_MSVC_EH(_pCurrentException) && IsExceptionObjectToBeDestroyed(PER_PEXCEPTOBJ(_pCurrentException))) { __DestructExceptionObject(_pCurrentException, TRUE); } _pCurrentException = *ppSaveException; _pCurrentExContext = *ppSaveExContext; } #endif // ndef _M_IA64 } //////////////////////////////////////////////////////////////////////////////// // // __CxxQueryExceptionSize - returns the value of Storage needed to save // FrameInfo + two pointers. // extern "C" int __cdecl __CxxQueryExceptionSize( void ) { return sizeof(FRAMEINFO) + sizeof(void *) + sizeof(void *); } //////////////////////////////////////////////////////////////////////////////// // // __CxxCallUnwindDtor - Calls a destructor during unwind. For COM+, the dtor // call needs to be wrapped inside a __try/__except to // get correct terminate() behavior when an exception // occurs during the dtor call. // extern "C" void __cdecl __CxxCallUnwindDtor( #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there void (__thiscall * pDtor)(void*), #else void (* pDtor)(void *), #endif // ndef _M_IA64 void *pThis ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there __try { (*pDtor)(pThis); } __except(FrameUnwindFilter(exception_info())) { } #endif // ndef _M_IA64 } ////////////////////////////////////////////////////////////////////////////////// // __CxxCallUnwindVecDtor - Calls a vector destructor during vector unwind. // For COM+, the dtor call needs to be wrapped inside // a __try/__except to get correct terminate() behavior // when an exception occurs during the dtor call. // extern "C" void __cdecl __CxxCallUnwindVecDtor( #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there void (__stdcall * pVecDtor)(void*, size_t, int, void (__thiscall *)(void*)), #else void (* pVecDtor)(void*, size_t, int, void (*)(void*)), #endif // ndef _M_IA64 void* ptr, size_t size, int count, #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there void (__thiscall * pDtor)(void*) #else void (* pDtor)(void *) #endif // ndef _M_IA64 ) { #ifndef _M_IA64 // Enable&fix for IA64 when COM+ C++ EH support available there __try { (*pVecDtor)(ptr, size, count, pDtor); } __except(FrameUnwindFilter(exception_info())) { } #endif // ndef _M_IA64 } #endif // ndef _M_AMD64 ////////////////////////////////////////////////////////////////////////////////// // IsInExceptionSpec - Checks if the exception matches the exception specification // list. Returns TRUE if it does, otherwise FALSE // BOOLEAN IsInExceptionSpec( EHExceptionRecord *pExcept, // Information for this (logical) // exception ESTypeList *pESTypeList // Static information for subject frame ) { DASSERT( pESTypeList != NULL ); BOOLEAN bFoundMatchingTypeInES = FALSE; #if defined(_M_IA64) || defined(_M_AMD64) __int32 const *ppCatchable; #elif defined(_WIN64) CatchableType * UNALIGNED const *ppCatchable; #else CatchableType * const *ppCatchable; #endif CatchableType *pCatchable; int catchables; // for every type in the exception spec... for (int i=0; i 0; catchables--, ppCatchable++) { #if defined(_M_IA64) || defined(_M_AMD64) pCatchable = (CatchableType *)(_GetThrowImageBase() + *ppCatchable); #else pCatchable = *ppCatchable; #endif if (TypeMatch(EST_ARRAY(pESTypeList,i), pCatchable, PER_PTHROW(pExcept))) { bFoundMatchingTypeInES = TRUE; break; } } } return bFoundMatchingTypeInES; } ////////////////////////////////////////////////////////////////////////////////// // CallUnexpected - Calls unexpected and handles all exceptions // thrown by it // // The unexpected() function shall not return, but it can throw (or re-throw) an // exception. // // If it throws a new exception which is allowed by the exception // specification which previously was violated, then // { // the search for another handler will continue at the call of the function // whose exception specification was violated. // } // else /* If it throws or rethrows an exception that the exception-specification does not // allow */ then the following happens: // { // If the exception-specification does not include the class std::bad_exception // (lib.bad.exception) then // { // the function terminate() is called, // } // otherwise // { // the thrown exception is replaced by an implementation-defined // object of the type std::bad_exception and the search for another handler // will continue at the call of the function whose exception-specification // was violated. // } // } // // Simple, isn't it? // void CallUnexpected( ESTypeList* pESTypeList ) { DASSERT( _pCurrentFuncInfo == NULL ); try { unexpected(); } catch(...) { _pCurrentFuncInfo = pESTypeList; throw; // rethrow -- we will catch it in the FrameHandler } terminate(); } ////////////////////////////////////////////////////////////////////////////////// // Is_bad_exception_allowed - checks if std::bad_exception belongs to the list // BOOLEAN Is_bad_exception_allowed(ESTypeList *pESTypeList) { for (int i=0; i