Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1995 lines
71 KiB

/***
*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.
*
****/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#if defined(_NTSUBSET_)
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntstatus.h> // STATUS_UNHANDLED_EXCEPTION
#include <ntos.h>
#include <ex.h> // ExRaiseException
}
#endif // defined(_NTSUBSET_)
#include <windows.h>
#include <internal.h>
#include <mtdll.h> // CRT internal header file
#include <ehassert.h> // This project's versions of standard assert macros
#include <ehdata.h> // Declarations of all types used for EH
#include <ehstate.h> // Declarations of state management stuff
#include <eh.h> // User-visible routines for eh
#include <ehhooks.h> // Declarations of hook variables and callbacks
#include <trnsctrl.h> // Routines to handle transfer of control (trnsctrl.asm)
#if defined(_M_IA64) /*IFSTRIP=IGN*/
#include <kxia64.h>
#include <ia64inst.h>
#include <cvconst.h>
#endif
#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_MRX000)
#define __GetRangeOfTrysToCheck(a, b, c, d, e, f, g) \
_GetRangeOfTrysToCheck(a, 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, h)
#define __GetUnwindState(a, b, c) \
GetCurrentState(a, b, c)
#define __OffsetToAddress(a, b, c) \
_OffsetToAddress(a, b, c)
#define __GetAddress(a, b) \
(a)
#define REAL_FP(a, b) \
(a)
#define SetState(a, b, c, d)
#define __ResetException(a)
#define VER41_C2(p) (HT_ADJECTIVES(TBME_CATCH(FUNC_TRYBLOCK(*p, 0), 0)) & 0x20)
#ifdef _MT
#define pExitContext (*((CONTEXT **)&(_getptd()->_pExitContext)))
#else
static CONTEXT *pExitContext = NULL; // context to assist the return to the continuation point
#endif // _MT
////////////////////////////////////////////////////////////////////////////////
//
// P7 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)))
#else
static CONTEXT *pExitContext = NULL; // context to assist the return to the continuation point
#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
////////////////////////////////////////////////////////////////////////////////
//
// DEC Alpha-specific definitions
//
#elif defined(_M_ALPHA)
#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 __ResetException(a)
////////////////////////////////////////////////////////////////////////////////
//
// PowerPC for Windows/NT-specific definitions
//
#elif defined(_M_PPC)
#define _POWERPC
#define __GetRangeOfTrysToCheck(a, b, c, d, e, f, g) \
_GetRangeOfTrysToCheck(b, c, d, e, f)
#define _MoveContext(a, b) \
RtlMoveMemory(a, b, sizeof(CONTEXT));
#define __CallSETranslator(a, b, c, d, e, f, g, h) \
_CallSETranslator(a, b, c, d, e, f, g)
#define __GetUnwindState(a, b, c) \
GetUnwindState(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)
extern "C" CONTEXT * _GetUnwindContext(VOID);
#ifdef _MT
#define pExitContext (*((CONTEXT **)&(_getptd()->_pExitContext)))
#else
static CONTEXT *pExitContext = NULL; // context to assist the return to the continuation point
#endif // _MT
////////////////////////////////////////////////////////////////////////////////
//
// Unknown platform
//
#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
);
static void * CallCatchBlock(
EHExceptionRecord *,
EHRegistrationNode *,
CONTEXT *,
FuncInfo *,
void *,
int,
unsigned long
#if defined(_M_IA64) || defined(_M_AMD64)
,FRAMEINFO *
#endif
);
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 *
);
extern "C" void _CRTIMP __DestructExceptionObject(
EHExceptionRecord *,
BOOLEAN
);
//
// Make sure the terminate wrapper is dragged in:
//
static void *pMyUnhandledExceptionFilter =
#if defined(_NTSUBSET_)
0;
#else
&__CxxUnhandledExceptionFilter;
#endif
//
// 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
#else
EHExceptionRecord *_pCurrentException = NULL;
CONTEXT *_pCurrentExContext = NULL;
int __ProcessingThrow = 0;
#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);
#if defined(_M_ALPHA)
// On Alpha - these values synthesized from DispatcherContext
pFuncInfo = (FuncInfo *)(pDC ->FunctionEntry->HandlerData);
pRN = (EHRegistrationNode *)VIRTUAL_FP(pDC);
#endif // defined(_M_ALPHA)
DASSERT(FUNC_MAGICNUM(*pFuncInfo) == EH_MAGIC_NUMBER1);
#if defined(_M_ALPHA)
// A special case to support gotos out of nested catch handlers
// and setjmp/longjmp.
//
// See __CxxEHGoto in alpha\trnsctrl.cxx for details.
//
if (IS_TARGET_UNWIND(PER_FLAGS(pExcept))) {
if (PER_CODE(pExcept) == STATUS_UNWIND && PER_NPARAMS(pExcept) == 1) {
__ehstate_t target_state =
(__ehstate_t)((PEXCEPTION_RECORD)(pExcept))->ExceptionInformation[0];
DASSERT(target_state >= EH_EMPTY_STATE
&& target_state < FUNC_MAXSTATE(*pFuncInfo));
__FrameUnwindToState(pRN, pDC, pFuncInfo, target_state);
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch;
} else {
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch;
}
}
#endif // defined(_M_ALPHA)
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 defined(_POWERPC)
if (FUNC_MAXSTATE(*pFuncInfo) != 0)
#else
if (FUNC_MAXSTATE(*pFuncInfo) != 0 && CatchDepth == 0)
#endif
{
// Only unwind if there's something to unwind
// AND we're being called through the primary RN.
#if defined(_M_MRX000) || defined(_POWERPC) || defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
// If we are exiting to the continuation point, we don't want to
// use the unwind map again. Unwinding continues until the
// dispatcher finds the target frame, at which point the dispatcher
// will jump to the continuation point
//
// Don't unwind the target frame if the unwind was initiated by
// UnwindNestedFrames
if (_GetUnwindContext() != NULL
&& IS_TARGET_UNWIND(PER_FLAGS(pExcept))) {
#if defined (_POWERPC)
// Virtually unwind the target frame to recover the value of r2.
// We must take care to not unwind a glue sequence that may have
// been used to reach the target frame. This is done by giving
// stack limit values that will regard any stack pointer as bad.
CONTEXT TocContext;
PRUNTIME_FUNCTION FunctionEntry;
BOOLEAN InFunction;
ULONG EstablisherFrame;
_MoveContext(&TocContext, pContext);
FunctionEntry = RtlLookupFunctionEntry(pDC->ControlPc);
RtlVirtualUnwind(pDC->ControlPc, FunctionEntry, &TocContext,
&InFunction, &EstablisherFrame, NULL, 0xffffffff, 0);
pContext->Gpr2 = TocContext.Gpr2;
#endif // defined (_POWERPC)
// Save the target context to be used in 'CatchIt' to jump to
// the continuation point.
DASSERT(pExitContext != NULL);
_MoveContext(pExitContext, pContext);
// This is how we give control back to _UnwindNestedFrames
_MoveContext(pContext, _GetUnwindContext());
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch;
}
#endif // defined(_M_MRX000) || defined(_POWERPC) || defined(_M_IA64)
#if defined(_M_IA64)
else if (IS_TARGET_UNWIND(PER_FLAGS(pExcept)) && PER_CODE(pExcept) == STATUS_LONGJUMP) {
__ehstate_t target_state = _StateFromIp(pFuncInfo, pDC, pContext->StIIP);
DASSERT(target_state >= EH_EMPTY_STATE
&& target_state < FUNC_MAXSTATE(*pFuncInfo));
__FrameUnwindToState(pRN, pDC, pFuncInfo, target_state);
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch;
}
#elif defined(_M_AMD64)
else if (IS_TARGET_UNWIND(PER_FLAGS(pExcept)) && PER_CODE(pExcept) == STATUS_LONGJUMP) {
__ehstate_t target_state = _StateFromIp(pFuncInfo, pDC, pContext->Rip);
DASSERT(target_state >= EH_EMPTY_STATE
&& target_state < FUNC_MAXSTATE(*pFuncInfo));
__FrameUnwindToState(pRN, pDC, pFuncInfo, target_state);
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch;
}
#endif // defined(_M_IA64)
__FrameUnwindToEmptyState(pRN, pDC, pFuncInfo);
}
EHTRACE_HANDLER_EXIT(ExceptionContinueSearch);
return ExceptionContinueSearch; // I don't think this value matters
} else if (FUNC_NTRYBLOCKS(*pFuncInfo) != 0) {
// 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_NUMBER1
&& (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;
// Get the current state (machine-dependent)
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
__ehstate_t curState = _StateFromControlPc(pFuncInfo, pDC);
#if defined(_M_AMD64) // Will be used when unwinding.
EHRegistrationNode EstablisherFrame;
_GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFrame);
if (curState > GetUnwindTryBlock(pRN, pDC, pFuncInfo)) {
SetState(&EstablisherFrame, pDC, pFuncInfo, curState);
}
#endif
#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);
}
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 defined(_M_MRX000)
DASSERT(VER41_C2(pFuncInfo));
#endif
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;
#elif defined(_WIN64)
CatchableType * UNALIGNED 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.
#if defined(_M_MRX000)
PVOID pRealFrame = _OffsetToAddress(0, pRN, FUNC_FRAMENEST(*pFuncInfo));
UNWINDHELP(pRealFrame, FUNC_DISPUNWINDHELP(*pFuncInfo),0) = curTry;
#elif defined(_M_IA64) || defined(_M_AMD64)/*IFSTRIP=IGN*/
SetUnwindTryBlock(pRN, pDC, pFuncInfo, /*curTry*/ curState);
#endif // defined(_M_MRX000)
CatchIt(pExcept, pRN, pContext, pDC, pFuncInfo, pCatch,
pCatchable, pEntry, CatchDepth, pMarkerRN, IsRethrow);
#if defined(_M_IA64) /*IFSTRIP=IGN*/
goto EndOfTryScan;
#else
goto NextTryBlock;
#endif
} // Scan posible conversions
} // Scan catch clauses
#if !defined(_M_IA64) /*IFSTRIP=IGN*/
NextTryBlock: ;
#endif
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
// if( GetUnwindTryBlock(pRN, pDC, pFuncInfo) >= 0 )
// curTry = GetUnwindTryBlock(pRN, pDC, pFuncInfo);
#endif
} // Scan try blocks
#if defined(_M_IA64) /*IFSTRIP=IGN*/
EndOfTryScan:
#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);
}
} // It was a C++ EH exception
else {
// Not ours. But maybe someone told us how to make it ours.
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
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 defined(_M_MRX000)
ULONG TDTransOffset = 0;
#ifdef _MT
struct _tiddata DummyStruct;
TDTransOffset = (char*)&DummyStruct._translator - (char*)&DummyStruct;
#endif
#endif // defined(_M_MRX000)
if (__CallSETranslator(pExcept, pRN, pContext, pDC, pFuncInfo,
CatchDepth, pMarkerRN, TDTransOffset)) {
EHTRACE_EXIT;
return;
}
}
// 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.
#if defined(_M_MRX000)
DASSERT(VER41_C2(pFuncInfo));
PVOID pRealFrame = _OffsetToAddress(0, pRN, FUNC_FRAMENEST(*pFuncInfo));
UNWINDHELP(pRealFrame, FUNC_DISPUNWINDHELP(*pFuncInfo), 0) = curTry;
#elif defined(_M_IA64) || defined(_M_AMD64)/*IFSTRIP=IGN*/
SetUnwindTryBlock(pRN, pDC, pFuncInfo, /*curTry*/ curState);
#endif // defined(_M_MRX000)
CatchIt(pExcept, pRN, pContext, pDC, pFuncInfo,
TBME_PCATCH(*pEntry, TBME_NCATCHES(*pEntry) - 1), NULL, pEntry,
CatchDepth, pMarkerRN, TRUE);
// 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_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || 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;
__ehstate_t curState = __GetUnwindState(pRN, pDC, pFuncInfo);
__ProcessingThrow++;
__try {
#if defined(_M_MRX000)
// The MIPS unwind-map may have a shortcut by using EH_EMPTY_STATE
while (curState != EH_EMPTY_STATE && curState != targetState)
#elif 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 defined(_M_MRX000)
// If this is nested function (catch block), the real frame where
// all locals are stored is the outermost function's frame.
PVOID pRealFrame =
_OffsetToAddress(0, pRN, FUNC_FRAMENEST(*pFuncInfo));
if (UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)) != NULL
&& !UNWINDHELP(pRealFrame, FUNC_DISPUNWINDHELP(*pFuncInfo),
curState)) {
UNWINDHELP(pRealFrame, FUNC_DISPUNWINDHELP(*pFuncInfo),
curState) = TRUE;
#else // !defined(_M_MRX000)
if (UWE_ACTION(FUNC_UNWIND(*pFuncInfo, curState)) != NULL) {
#endif
// Before calling unwind action, adjust state as if it were
// already completed:
SetState(pRN, pDC, pFuncInfo, nxtState);
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_MRX000)
DASSERT(curState == EH_EMPTY_STATE || curState == targetState);
#elif 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);
SetState(pRN, pDC, pFuncInfo, curState);
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 ?
) {
EHTRACE_ENTER_FMT1("Catching object @ 0x%p", PER_PEXCEPTOBJ(pExcept));
void *continuationAddress;
EHRegistrationNode *pEstablisher = pRN;
#if defined(_M_MRX000) || defined(_POWERPC) || defined(_M_IA64) || defined(_M_AMD64)/*IFSTRIP=IGN*/
FRAMEINFO FrameInfo;
FRAMEINFO *pFrameInfo;
CONTEXT ExitContext;
#endif // defined(_M_MRX000) || defined(_POWERPC) || defined(_M_IA64) || defined(_M_AMD64)
#if defined(_POWERPC)
int dummy;
pEstablisher = _GetEstablisherFrame(pDC, &dummy);
#elif defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
PVOID pExceptionObjectDestroyed = NULL;
EHRegistrationNode EstablisherFramePointers;
pEstablisher = _GetEstablisherFrame(pRN, pDC, pFuncInfo, &EstablisherFramePointers);
#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_MRX000) || defined(_POWERPC) || defined(_M_IA64) || defined(_M_AMD64)/*IFSTRIP=IGN*/
pExitContext = &ExitContext;
_UnwindNestedFrames(pRN,
pExcept,
pContext
#ifdef _M_AMD64
, pDC
#endif
);
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
if( _pCurrentException != NULL && _ExecutionInCatch(pDC, pFuncInfo) && ! IsRethrow) {
__DestructExceptionObject(_pCurrentException, TRUE);
pExceptionObjectDestroyed = PER_PEXCEPTOBJ(_pCurrentException);
}
#endif
#elif defined(_M_ALPHA)
// Alpha specific - this calls a special version of RtlUnwind which
// walks the stack performing unwind actions but which does NOT
// restore the context to the target frame's routine. Instead it
// just returns.
_UnwindNestedFrames(pRN, pExcept);
#else
if (pMarkerRN == NULL) {
_UnwindNestedFrames(pRN, pExcept);
} else {
_UnwindNestedFrames(pMarkerRN, pExcept);
}
#endif
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
// Create FrameInfo before we attempt to unwind with __FrameUnwindToState()
// pExitContext must be setup in advance just in case a DTOR throws a new exception. VS7:#202440
pFrameInfo = _CreateFrameInfo(&FrameInfo, pDC, pExitContext, -2, pExceptionObjectDestroyed, pExcept);
#endif
__FrameUnwindToState(pEstablisher, pDC, pFuncInfo, TBME_LOW(*pEntry));
// Call the catch. Separated out because it introduces a new registration
// node.
#if defined(_M_MRX000)
pFrameInfo = _CreateFrameInfo(&FrameInfo, pDC, pRN, pExitContext);
#elif defined(_M_IA64) /*IFSTRIP=IGN*/
UNWINDSTATE(pEstablisher->MemoryStackFp, FUNC_DISPUNWINDHELP(*pFuncInfo)) = GetCurrentState(pEstablisher,pDC,pFuncInfo);
if(IsRethrow) {
pFrameInfo->isRethrow = TRUE;
}
else if( pExcept != NULL && pExceptionObjectDestroyed == NULL ) {
pFrameInfo->pExceptionObjectToBeDestroyed = PER_PEXCEPTOBJ(pExcept);
}
#elif defined(_M_AMD64)
if(IsRethrow) {
pFrameInfo->isRethrow = TRUE;
}
else if( pExcept != NULL && pExceptionObjectDestroyed == NULL ) {
pFrameInfo->pExceptionObjectToBeDestroyed = PER_PEXCEPTOBJ(pExcept);
}
#elif defined(_POWERPC)
pFrameInfo = _CreateFrameInfo(&FrameInfo, pDC, pEstablisher, pExitContext,
(FUNC_UNWIND(*pFuncInfo, TBME_LOW(*pEntry))).toState);
#elif defined(_M_ALPHA)
SetState(pRN, pDC, pFuncInfo,
(FUNC_UNWIND(*pFuncInfo, TBME_LOW(*pEntry))).toState);
#else
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);
#endif
continuationAddress = CallCatchBlock(pExcept, pEstablisher, pContext,
pFuncInfo, __GetAddress(HT_HANDLER(*pCatch), pDC), CatchDepth, 0x100
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
, pFrameInfo
#endif
);
// Transfer control to the continuation address. If no continuation then
// it's a re-throw, so return.
if (continuationAddress != NULL) {
#if defined(_M_MRX000) || defined(_POWERPC) || defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
// Exit gracefully to the continuation adddress.
//
// We are done, but we have to blow away the stack below the frame where
// the try-block resides. In addition, we have to resore a bunch of
// other registers besides SP as well. Leave that task up to
// _JumpToContinuation.
//
// The code that is commented out below worked well until we hit the
// scenario of:
//
// void foo()
// {
// try {
// bar();
// }
// catch(int) {
// }
// }
//
// void bar()
// {
// __try {
// throw 1;
// }
// __finally {
// }
// }
//
// In the above example, RtlUnwind would call the __finally's handler
// twice. Once for the original unwind through the call to
// _UnwindNestedFrames, then here where all we want is to get to the
// continuation point.
//
// ExitContext was saved during the original unwind when we detected the
// target of the unwind in __InternalCxxFrameHandler.
#if defined(_M_MRX000)
DASSERT(VER41_C2(pFuncInfo));
PVOID pRealFrame = _OffsetToAddress(0, pRN, FUNC_FRAMENEST(*pFuncInfo));
UNWINDHELP(pRealFrame, FUNC_DISPUNWINDHELP(*pFuncInfo), 0) = -1;
#elif defined(_M_AMD64)
UNWINDHELP(*pEstablisher, FUNC_DISPUNWINDHELP(*pFuncInfo)) = -2;
FRAMEINFO * pContFrameInfo = _FindFrameInfo(continuationAddress, pFrameInfo);
if( pContFrameInfo != NULL && !pContFrameInfo->isRethrow
&& pContFrameInfo->pExceptionObjectToBeDestroyed
&& !_IsExceptionObjectDestroyed(pContFrameInfo->pExceptionObjectToBeDestroyed,pFrameInfo)
) {
__DestructExceptionObject(pContFrameInfo->pExcept, TRUE);
_MarkExceptionObjectDestroyed(pContFrameInfo->pExcept);
}
#elif defined(_M_IA64) /*IFSTRIP=IGN*/
UNWINDHELP(pEstablisher->MemoryStackFp, FUNC_DISPUNWINDHELP(*pFuncInfo)) = -2;
FRAMEINFO * pContFrameInfo = _FindFrameInfo(continuationAddress, pFrameInfo);
if( pContFrameInfo != NULL && !pContFrameInfo->isRethrow
&& pContFrameInfo->pExceptionObjectToBeDestroyed
&& !_IsExceptionObjectDestroyed(pContFrameInfo->pExceptionObjectToBeDestroyed,pFrameInfo)
) {
__DestructExceptionObject(pContFrameInfo->pExcept, TRUE);
}
else if( pFrameInfo != NULL && pFrameInfo != pContFrameInfo
&& !_IsExceptionObjectDestroyed(PER_PEXCEPTOBJ(pExcept),pFrameInfo)
) {
__DestructExceptionObject(pExcept, TRUE);
}
#endif // defined(_M_MRX000)
__ResetException(pExcept);
pExitContext = NULL;
#if defined(_WIN64)
_JumpToContinuation((unsigned __int64)continuationAddress,
#else
_JumpToContinuation((ULONG)continuationAddress,
#endif
_FindAndUnlinkFrame(continuationAddress, pFrameInfo)
#if defined(_M_IA64) || defined(_M_AMD64)
,pExcept
#endif
);
#else // !(defined(_M_MRX000) || defined(_POWERPC)) || defined(_M_IA64) || defined(_M_AMD64)
_JumpToContinuation(continuationAddress, pRN);
#endif
// No return.
#if defined(_POWERPC)
} else {
_FindAndUnlinkFrame(NULL, pFrameInfo);
#elif defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
} else {
_UnlinkFrame(pFrameInfo);
#endif
}
EHTRACE_EXIT;
}
////////////////////////////////////////////////////////////////////////////////
//
// 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
#if defined(_M_IA64) || defined(_M_AMD64) /*IFSTRIP=IGN*/
,
FRAMEINFO *pFrameInfo
#endif
) {
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;
#if defined(_M_IX86)
// 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));
#endif
// 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.
#if defined(_M_IX86)
continuationAddress = _CallCatchBlock2(pRN, pFuncInfo,
handlerAddress, CatchDepth, NLGCode);
#else
continuationAddress = _CallSettingFrame(handlerAddress,
REAL_FP(pRN, pFuncInfo), NLGCode);
#endif
} __except(EHTRACE_EXCEPT(ExFilterRethrow(exception_info()))) {
#if defined(_M_IX86)
// 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);
#endif
// 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" : "");
#if defined(_M_IX86)
// 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);
#endif
// 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
#if defined(_M_IA64) || defined(_M_AMD64)
&& !_IsExceptionObjectDestroyed(PER_PEXCEPTOBJ(pExcept),pFrameInfo)
&& !pFrameInfo->isRethrow
#elif defined(_M_IX86)
&& IsExceptionObjectToBeDestroyed(PER_PEXCEPTOBJ(pExcept))
#endif
) {
#if defined(_M_IA64) || defined(_M_AMD64)
pFrameInfo->dtorThrowFlag = TRUE;
#endif
__DestructExceptionObject(pExcept, abnormal_termination());
#if defined(_M_MRX000)
ExceptionObjectDestroyed = TRUE;
#endif
}
EHTRACE_RESTORE_LEVEL(!!_abnormal_termination());
}
EHTRACE_EXIT;
#if defined(_M_IA64) || defined(_M_AMD64)
pFrameInfo->dtorThrowFlag = FALSE;
#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: 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;
}
}
////////////////////////////////////////////////////////////////////////////////
//
// 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*/
_MarkExceptionObjectDestroyed(pExcept);
_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_IA64) && !defined(_M_AMD64) // Enable&fix for IA64 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.
)
{
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)
*(EHExceptionRecord **)ppExcept = _pCurrentException;
else
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);
// We set the current exception.
if ( PER_PTHROW(*(EHExceptionRecord **)ppExcept) == NULL)
*(EHExceptionRecord **)ppExcept = _pCurrentException;
return EXCEPTION_EXECUTE_HANDLER;
}
} // Scan posible conversions
}
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
)
{
// This function is only called for C++ EH.
EHExceptionRecord *pExcept;
FRAMEINFO *pFrameInfo = (FRAMEINFO *)pStorage;
EHExceptionRecord **ppSaveException;
CONTEXT **ppSaveExContext;
ppSaveException = (EHExceptionRecord **)(&pFrameInfo[1]);
ppSaveExContext = (CONTEXT **)(&ppSaveException[1]);
pExcept = *(EHExceptionRecord **)ppExcept;
pFrameInfo = _CreateFrameInfo(pFrameInfo, PER_PEXCEPTOBJ(pExcept));
*ppSaveException = _pCurrentException;
*ppSaveExContext = _pCurrentExContext;
_pCurrentException = pExcept;
__ProcessingThrow--;
if ( __ProcessingThrow < 0)
__ProcessingThrow = 0;
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
)
{
EHExceptionRecord *pExcept;
if (!ppExcept)
return 0;
pExcept = *(EHExceptionRecord **)ppExcept;
if (PER_IS_MSVC_EH(pExcept) && PER_PTHROW(pExcept) == NULL) {
*(EHExceptionRecord **)ppExcept = _pCurrentException;
return 1;
} else if (*(EHExceptionRecord **)ppExcept == _pCurrentException)
return 1;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//
// __CxxUnregisterExceptionObject - Destructs Exception Objects if rethrow ==
// true. Also set __pCurrentException and
// __pCurrentExContext() to current value.
//
extern "C" void __cdecl __CxxUnregisterExceptionObject(
void *pStorage,
int rethrow
)
{
FRAMEINFO *pFrameInfo = (FRAMEINFO *)pStorage;
EHExceptionRecord **ppSaveException;
CONTEXT **ppSaveExContext;
ppSaveException = (EHExceptionRecord **)(&pFrameInfo[1]);
ppSaveExContext = (CONTEXT **)(&ppSaveException[1]);
_FindAndUnlinkFrame(pFrameInfo);
if ( !rethrow && PER_IS_MSVC_EH(_pCurrentException) && IsExceptionObjectToBeDestroyed(PER_PEXCEPTOBJ(_pCurrentException))) {
__DestructExceptionObject(_pCurrentException, TRUE);
}
_pCurrentException = *ppSaveException;
_pCurrentExContext = *ppSaveExContext;
}
////////////////////////////////////////////////////////////////////////////////
//
// __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(
void (__thiscall * pDtor)(void*),
void *pThis
)
{
__try
{
(*pDtor)(pThis);
}
__except(FrameUnwindFilter(exception_info()))
{
}
}
#endif // ndef _M_IA64