summaryrefslogtreecommitdiff
path: root/src/vm/exceptionhandling.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/exceptionhandling.cpp')
-rw-r--r--src/vm/exceptionhandling.cpp389
1 files changed, 226 insertions, 163 deletions
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index 262d3526eb..31b85bdb0a 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -17,6 +17,48 @@
#include "eventtrace.h"
#include "virtualcallstub.h"
+#if defined(_TARGET_X86_)
+#define USE_CURRENT_CONTEXT_IN_FILTER
+#endif // _TARGET_X86_
+
+#if defined(_TARGET_ARM_) || defined(_TARGET_X86_)
+#define VSD_STUB_CAN_THROW_AV
+#endif // _TARGET_ARM_ || _TARGET_X86_
+
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_)
+#define ADJUST_PC_UNWOUND_TO_CALL
+#define STACK_RANGE_BOUNDS_ARE_CALLER_SP
+#define USE_FUNCLET_CALL_HELPER
+#define USE_CALLER_SP_IN_FUNCLET
+// For ARM/ARM64, EstablisherFrame is Caller-SP (SP just before executing call instruction).
+// This has been confirmed by AaronGi from the kernel team for Windows.
+//
+// For x86/Linux, RtlVirtualUnwind sets EstablisherFrame as Caller-SP.
+#define ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
+#endif // _TARGET_ARM_ || _TARGET_ARM64_ || _TARGET_X86_
+
+#ifdef USE_CURRENT_CONTEXT_IN_FILTER
+inline void CaptureNonvolatileRegisters(PKNONVOLATILE_CONTEXT pNonvolatileContext, PCONTEXT pContext)
+{
+#define CALLEE_SAVED_REGISTER(reg) pNonvolatileContext->reg = pContext->reg;
+ ENUM_CALLEE_SAVED_REGISTERS();
+#undef CALLEE_SAVED_REGISTER
+}
+
+inline void RestoreNonvolatileRegisters(PCONTEXT pContext, PKNONVOLATILE_CONTEXT pNonvolatileContext)
+{
+#define CALLEE_SAVED_REGISTER(reg) pContext->reg = pNonvolatileContext->reg;
+ ENUM_CALLEE_SAVED_REGISTERS();
+#undef CALLEE_SAVED_REGISTER
+}
+
+inline void RestoreNonvolatileRegisterPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pContextPointers, PKNONVOLATILE_CONTEXT pNonvolatileContext)
+{
+#define CALLEE_SAVED_REGISTER(reg) pContextPointers->reg = &pNonvolatileContext->reg;
+ ENUM_CALLEE_SAVED_REGISTERS();
+#undef CALLEE_SAVED_REGISTER
+}
+#endif
#ifndef DACCESS_COMPILE
// o Functions and funclets are tightly associated. In fact, they are laid out in contiguous memory.
@@ -75,18 +117,30 @@ static void DoEHLog(DWORD lvl, __in_z const char *fmt, ...);
TrackerAllocator g_theTrackerAllocator;
-void __declspec(noinline)
-ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord,
- UINT_PTR ReturnValue,
- UINT_PTR TargetIP,
- UINT_PTR TargetFrameSp);
-
bool FixNonvolatileRegisters(UINT_PTR uOriginalSP,
Thread* pThread,
CONTEXT* pContextRecord,
bool fAborting
);
+void FixContext(PCONTEXT pContextRecord)
+{
+#define FIXUPREG(reg, value) \
+ do { \
+ STRESS_LOG2(LF_GCROOTS, LL_INFO100, "Updating " #reg " %p to %p\n", \
+ pContextRecord->reg, \
+ (value)); \
+ pContextRecord->reg = (value); \
+ } while (0)
+
+#ifdef _TARGET_X86_
+ size_t resumeSp = EECodeManager::GetResumeSp(pContextRecord);
+ FIXUPREG(ResumeEsp, resumeSp);
+#endif // _TARGET_X86_
+
+#undef FIXUPREG
+}
+
MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut);
#ifdef FEATURE_PAL
@@ -405,6 +459,7 @@ void ExceptionTracker::UpdateNonvolatileRegisters(CONTEXT *pContextRecord, REGDI
} \
} while (0)
+
#if defined(_TARGET_X86_)
UPDATEREG(Ebx);
@@ -448,14 +503,7 @@ void ExceptionTracker::UpdateNonvolatileRegisters(CONTEXT *pContextRecord, REGDI
UPDATEREG(X26);
UPDATEREG(X27);
UPDATEREG(X28);
- // Obtain value of Fp from CurrentContext instead of from CurrentContextPointers
- // It should not matter. CurrentContextPointers does not have value of FP as this will
- // require changes in MachState to also store pointer of FP which it does not do currently.
- pContextRecord->Fp = pRegDisplay->pCurrentContext->Fp;
- if (pAbortContext)
- {
- pAbortContext->Fp = pContextRecord->Fp;
- }
+ UPDATEREG(Fp);
#else
PORTABILITY_ASSERT("ExceptionTracker::UpdateNonvolatileRegisters");
@@ -583,7 +631,7 @@ UINT_PTR ExceptionTracker::CallCatchHandler(CONTEXT* pContextRecord, bool* pfAbo
if (!fIntercepted)
{
_ASSERTE(m_uCatchToCallPC != 0 && m_pClauseForCatchToken != NULL);
- uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
+ uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch X86_ARG(pContextRecord) ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
}
else
{
@@ -966,7 +1014,6 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord
}
#endif // FEATURE_CORRUPTING_EXCEPTIONS
-#ifdef FEATURE_CORECLR
{
// Switch to COOP mode since we are going to work
// with throwable
@@ -1009,7 +1056,6 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord
}
}
}
-#endif // FEATURE_CORECLR
#ifndef FEATURE_PAL // Watson is on Windows only
// Setup bucketing details for nested exceptions (rethrow and non-rethrow) only if we are in the first pass
@@ -1154,6 +1200,8 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord
pThread->SetFrame(pLimitFrame);
+ FixContext(pContextRecord);
+
SetIP(pContextRecord, (PCODE)uResumePC);
}
@@ -1290,9 +1338,14 @@ void ExceptionTracker::InitializeCurrentContextForCrawlFrame(CrawlFrame* pcfThis
{
REGDISPLAY *pRD = pcfThisFrame->pRD;
+#ifndef USE_CURRENT_CONTEXT_IN_FILTER
INDEBUG(memset(pRD->pCurrentContext, 0xCC, sizeof(*(pRD->pCurrentContext))));
// Ensure that clients can tell the current context isn't valid.
SetIP(pRD->pCurrentContext, 0);
+#else // !USE_CURRENT_CONTEXT_IN_FILTER
+ RestoreNonvolatileRegisters(pRD->pCurrentContext, pDispatcherContext->CurrentNonVolatileContextRecord);
+ RestoreNonvolatileRegisterPointers(pRD->pCurrentContextPointers, pDispatcherContext->CurrentNonVolatileContextRecord);
+#endif // USE_CURRENT_CONTEXT_IN_FILTER
*(pRD->pCallerContext) = *(pDispatcherContext->ContextRecord);
pRD->IsCallerContextValid = TRUE;
@@ -1300,12 +1353,12 @@ void ExceptionTracker::InitializeCurrentContextForCrawlFrame(CrawlFrame* pcfThis
pRD->SP = sfEstablisherFrame.SP;
pRD->ControlPC = pDispatcherContext->ControlPc;
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
pcfThisFrame->pRD->IsCallerSPValid = TRUE;
// Assert our first pass assumptions for the Arm/Arm64
_ASSERTE(sfEstablisherFrame.SP == GetSP(pDispatcherContext->ContextRecord));
-#endif // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#endif // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
}
@@ -1399,19 +1452,23 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
{
pCurrentTracker->InitializeCurrentContextForCrawlFrame(pcfThisFrame, pDispatcherContext, sf);
}
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
else
{
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
// See the comment above the call to InitRegDisplay for this assertion.
_ASSERTE(pDispatcherContext->ControlPc == GetIP(pDispatcherContext->ContextRecord));
+#endif // _TARGET_ARM_ || _TARGET_ARM64_
+#ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
// Simply setup the callerSP during the second pass in the caller context.
// This is used in setting up the "EnclosingClauseCallerSP" in ExceptionTracker::ProcessManagedCallFrame
// when the termination handlers are invoked.
::SetSP(pcfThisFrame->pRD->pCallerContext, sf.SP);
pcfThisFrame->pRD->IsCallerSPValid = TRUE;
+#endif // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
}
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
// Further below, we will adjust the ControlPC based upon whether we are at a callsite or not.
// We need to do this for "RegDisplay.ControlPC" field as well so that when data structures like
// EECodeInfo initialize themselves using this field, they will have the correct absolute value
@@ -1437,7 +1494,7 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
#endif
#endif // _TARGET_ARM_ || _TARGET_ARM64_
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_)
+#ifdef ADJUST_PC_UNWOUND_TO_CALL
// If the OS indicated that the IP is a callsite, then adjust the ControlPC by decrementing it
// by two. This is done because unwinding at callsite will make ControlPC point to the
// instruction post the callsite. If a protected region ends "at" the callsite, then
@@ -1460,7 +1517,7 @@ void ExceptionTracker::InitializeCrawlFrame(CrawlFrame* pcfThisFrame, Thread* pT
pcfThisFrame->isIPadjusted = true;
}
}
-#endif // _TARGET_ARM_ || _TARGET_ARM64_ || _TARGET_X86_
+#endif // ADJUST_PC_UNWOUND_TO_CALL
pcfThisFrame->codeInfo.Init(ControlPCForEHSearch);
@@ -1617,17 +1674,11 @@ CLRUnwindStatus ExceptionTracker::ProcessOSExceptionNotification(
ExceptionTracker::InitializeCrawlFrame(&cfThisFrame, pThread, sf, &regdisp, pDispatcherContext, ControlPc, &uMethodStartPC, this);
-#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
+#ifndef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
uCallerSP = EECodeManager::GetCallerSp(cfThisFrame.pRD);
-#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
- // On ARM & ARM64, the EstablisherFrame is the value of SP at the time a function was called and before it's prolog
- // executed. Effectively, it is the SP of the caller. This has been confirmed by AaronGi from the kernel
- // team.
+#else // !ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
uCallerSP = sf.SP;
-#else
- PORTABILITY_ASSERT("ExceptionTracker::ProcessOSExceptionNotification");
- uCallerSP = NULL;
-#endif // _TARGET_AMD64_
+#endif // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
EH_LOG((LL_INFO100, "ProcessCrawlFrame: PSP: " FMT_ADDR " EstablisherFrame: " FMT_ADDR "\n", DBG_ADDR(uCallerSP), DBG_ADDR(sf.SP)));
@@ -1928,19 +1979,16 @@ lExit:
// in ExceptionTracker::ProcessManagedCallFrame.
if (m_fResetEnclosingClauseSPForCatchFunclet)
{
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
// DispatcherContext->EstablisherFrame's value
// represents the CallerSP of the current frame.
UINT_PTR EnclosingClauseCallerSP = (UINT_PTR)pDispatcherContext->EstablisherFrame;
-#elif defined(_TARGET_AMD64_)
- // Extract the CallerSP from RegDisplay on AMD64
+#else // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
+ // Extract the CallerSP from RegDisplay
REGDISPLAY *pRD = cfThisFrame.GetRegisterSet();
_ASSERTE(pRD->IsCallerContextValid || pRD->IsCallerSPValid);
UINT_PTR EnclosingClauseCallerSP = (UINT_PTR)GetSP(pRD->pCallerContext);
-#else // !_ARM_ && !_AMD64_ && !_ARM64_
- PORTABILITY_ASSERT("ExceptionTracker::ProcessOSExceptionNotification");
- UINT_PTR EnclosingClauseCallerSP = NULL;
-#endif // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#endif // !ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
m_EnclosingClauseInfo = EnclosingClauseInfo(false, cfThisFrame.GetRelOffset(), EnclosingClauseCallerSP);
}
m_fResetEnclosingClauseSPForCatchFunclet = FALSE;
@@ -2213,13 +2261,11 @@ CLRUnwindStatus ExceptionTracker::ProcessExplicitFrame(
#if defined(DEBUGGING_SUPPORTED)
if (ExceptionTracker::NotifyDebuggerOfStub(pThread, sf, pFrame))
{
-#ifdef FEATURE_EXCEPTION_NOTIFICATIONS
// Deliver the FirstChanceNotification after the debugger, if not already delivered.
if (!this->DeliveredFirstChanceNotification())
{
ExceptionNotifications::DeliverFirstChanceNotification();
}
-#endif // FEATURE_EXCEPTION_NOTIFICATIONS
}
#endif // DEBUGGING_SUPPORTED
@@ -2243,7 +2289,6 @@ CLRUnwindStatus ExceptionTracker::HandleFunclets(bool* pfProcessThisFrame, bool
}
CONTRACTL_END;
-#ifdef WIN64EXCEPTIONS // funclets
BOOL fUnwindingToFindResumeFrame = m_ExceptionFlags.UnwindingToFindResumeFrame();
//
@@ -2316,7 +2361,6 @@ CLRUnwindStatus ExceptionTracker::HandleFunclets(bool* pfProcessThisFrame, bool
}
}
}
-#endif // WIN64EXCEPTIONS
return UnwindPending;
}
@@ -2356,14 +2400,9 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
EH_LOG((LL_INFO100, " [ ProcessManagedCallFrame this=%p, %s PASS ]\n", this, (fIsFirstPass ? "FIRST" : "SECOND")));
-#ifdef WIN64EXCEPTIONS // funclets
EH_LOG((LL_INFO100, " [ method: %s%s, %s ]\n",
(fIsFunclet ? "FUNCLET of " : ""),
pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName));
-#else // !WIN64EXCEPTIONS
- EH_LOG((LL_INFO100, " [ method: %s, %s ]\n",
- pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName));
-#endif // WIN64EXCEPTIONS
Thread *pThread = GetThread();
_ASSERTE (pThread);
@@ -2553,14 +2592,12 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
}
#endif // DEBUGGING_SUPPORTED
-#ifdef FEATURE_EXCEPTION_NOTIFICATIONS
// Attempt to deliver the first chance notification to the AD only *AFTER* the debugger
// has done that, provided we have not already delivered it.
if (!this->DeliveredFirstChanceNotification())
{
ExceptionNotifications::DeliverFirstChanceNotification();
}
-#endif // FEATURE_EXCEPTION_NOTIFICATIONS
}
else
{
@@ -2686,7 +2723,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
EE_ILEXCEPTION_CLAUSE EHClause;
PTR_EXCEPTION_CLAUSE_TOKEN pEHClauseToken = pJitMan->GetNextEHClause(&EnumState, &EHClause);
- EH_LOG((LL_INFO100, " considering %s clause [%x,%x], ControlPc is %s clause (offset %x)",
+ EH_LOG((LL_INFO100, " considering %s clause [%x,%x), ControlPc is %s clause (offset %x)",
(IsFault(&EHClause) ? "fault" :
(IsFinally(&EHClause) ? "finally" :
(IsFilterHandler(&EHClause) ? "filter" :
@@ -2846,13 +2883,13 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
// executes.
m_dwIndexClauseForCatch = i + 1;
m_sfEstablisherOfActualHandlerFrame = sfEstablisherFrame;
-#ifdef _TARGET_AMD64_
+#ifndef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
m_sfCallerOfActualHandlerFrame = EECodeManager::GetCallerSp(pcfThisFrame->pRD);
-#else
+#else // !ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
// On ARM & ARM64, the EstablisherFrame is the value of SP at the time a function was called and before it's prolog
// executed. Effectively, it is the SP of the caller.
m_sfCallerOfActualHandlerFrame = sfEstablisherFrame.SP;
-#endif
+#endif // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
ReturnStatus = FirstPassComplete;
}
@@ -2894,7 +2931,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
SetEnclosingClauseInfo(fIsFunclet,
pcfThisFrame->GetRelOffset(),
GetSP(pcfThisFrame->GetRegisterSet()->pCallerContext));
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_FUNCLET_CALL_HELPER
// On ARM & ARM64, the OS passes us the CallerSP for the frame for which personality routine has been invoked.
// Since IL filters are invoked in the first pass, we pass this CallerSP to the filter funclet which will
// then lookup the actual frame pointer value using it since we dont have a frame pointer to pass to it
@@ -2903,17 +2940,25 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
// Assert our invariants (we had set them up in InitializeCrawlFrame):
REGDISPLAY *pCurRegDisplay = pcfThisFrame->GetRegisterSet();
+ CONTEXT *pContext = NULL;
+#ifndef USE_CURRENT_CONTEXT_IN_FILTER
// 1) In first pass, we dont have a valid current context IP
- _ASSERTE(GetIP(pCurRegDisplay->pCurrentContext) == 0);
+ _ASSERTE(GetIP(pCurRegDisplay->pCurrentContext) == 0);
+ pContext = pCurRegDisplay->pCallerContext;
+#else
+ pContext = pCurRegDisplay->pCurrentContext;
+#endif // !USE_CURRENT_CONTEXT_IN_FILTER
+#ifdef USE_CALLER_SP_IN_FUNCLET
// 2) Our caller context and caller SP are valid
_ASSERTE(pCurRegDisplay->IsCallerContextValid && pCurRegDisplay->IsCallerSPValid);
// 3) CallerSP is intact
_ASSERTE(GetSP(pCurRegDisplay->pCallerContext) == GetRegdisplaySP(pCurRegDisplay));
-#endif // _TARGET_ARM_ || _TARGET_ARM64_
+#endif // USE_CALLER_SP_IN_FUNCLET
+#endif // USE_FUNCLET_CALL_HELPER
{
// CallHandler expects to be in COOP mode.
GCX_COOP();
- dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter ARM_ARG(pCurRegDisplay->pCallerContext) ARM64_ARG(pCurRegDisplay->pCallerContext));
+ dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter X86_ARG(pContext) ARM_ARG(pContext) ARM64_ARG(pContext));
}
}
EX_CATCH
@@ -3112,13 +3157,11 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
m_dwIndexClauseForCatch = i + 1;
m_sfEstablisherOfActualHandlerFrame = sfEstablisherFrame;
-#ifdef _TARGET_AMD64_
+#ifndef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
m_sfCallerOfActualHandlerFrame = EECodeManager::GetCallerSp(pcfThisFrame->pRD);
-#else
- // On ARM & ARM64, the EstablisherFrame is the value of SP at the time a function was called and before it's prolog
- // executed. Effectively, it is the SP of the caller.
+#else // !ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
m_sfCallerOfActualHandlerFrame = sfEstablisherFrame.SP;
-#endif
+#endif // ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
//
// END resume frame code
//
@@ -3146,7 +3189,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
// Since we also forbid GC during second pass, disable it now since
// invocation of managed code can result in a GC.
ENDFORBIDGC();
- dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));
+ dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally X86_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));
// Once we return from a funclet, forbid GC again (refer to comment before start of the loop for details)
BEGINFORBIDGC();
@@ -3214,20 +3257,57 @@ lExit:
#define OPTIONAL_SO_CLEANUP_UNWIND(pThread, pFrame) if (pThread->GetFrame() < pFrame) { UnwindFrameChain(pThread, pFrame); }
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_FUNCLET_CALL_HELPER
// This is an assembly helper that enables us to call into EH funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP);
// This is an assembly helper that enables us to call into EH filter funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP);
-#endif // _TARGET_ARM_ || _TARGET_ARM64_
+static inline UINT_PTR CastHandlerFn(HandlerFn *pfnHandler)
+{
+#ifdef _TARGET_ARM_
+ return DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler);
+#else
+ return (UINT_PTR)pfnHandler;
+#endif
+}
+
+static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextRecord)
+{
+#if defined(_TARGET_ARM_)
+ return (UINT_PTR*)&(pContextRecord->R4);
+#elif defined(_TARGET_ARM64_)
+ return (UINT_PTR*)&(pContextRecord->X19);
+#elif defined(_TARGET_X86_)
+ return (UINT_PTR*)&(pContextRecord->Edi);
+#else
+ PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress");
+ return NULL;
+#endif
+}
+
+static inline TADDR GetFrameRestoreBase(PCONTEXT pContextRecord)
+{
+#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+ return GetSP(pContextRecord);
+#elif defined(_TARGET_X86_)
+ return pContextRecord->Ebp;
+#else
+ PORTABILITY_ASSERT("GetFrameRestoreBase");
+ return NULL;
+#endif
+}
+
+#endif // USE_FUNCLET_CALL_HELPER
+
DWORD_PTR ExceptionTracker::CallHandler(
UINT_PTR uHandlerStartPC,
StackFrame sf,
EE_ILEXCEPTION_CLAUSE* pEHClause,
MethodDesc* pMD,
EHFuncletType funcletType
+ X86_ARG(PCONTEXT pContextRecord)
ARM_ARG(PCONTEXT pContextRecord)
ARM64_ARG(PCONTEXT pContextRecord)
)
@@ -3282,7 +3362,7 @@ DWORD_PTR ExceptionTracker::CallHandler(
break;
}
-#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#ifdef USE_FUNCLET_CALL_HELPER
// Invoke the funclet. We pass throwable only when invoking the catch block.
// Since the actual caller of the funclet is the assembly helper, pass the reference
// to the CallerStackFrame instance so that it can be updated.
@@ -3291,14 +3371,9 @@ DWORD_PTR ExceptionTracker::CallHandler(
if (funcletType != EHFuncletType::Filter)
{
dwResumePC = CallEHFunclet((funcletType == EHFuncletType::Catch)?OBJECTREFToObject(throwable):(Object *)NULL,
-#ifdef _TARGET_ARM_
- DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
- (UINT_PTR*)&(pContextRecord->R4),
-#else
- (UINT_PTR)pfnHandler,
- &(pContextRecord->X19),
-#endif // _TARGET_ARM_
- pFuncletCallerSP);
+ CastHandlerFn(pfnHandler),
+ GetFirstNonVolatileRegisterAddress(pContextRecord),
+ pFuncletCallerSP);
}
else
{
@@ -3306,20 +3381,16 @@ DWORD_PTR ExceptionTracker::CallHandler(
// it will retrieve the framepointer for accessing the locals in the parent
// method.
dwResumePC = CallEHFilterFunclet(OBJECTREFToObject(throwable),
- GetSP(pContextRecord),
-#ifdef _TARGET_ARM_
- DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
-#else
- (UINT_PTR)pfnHandler,
-#endif // _TARGET_ARM_
- pFuncletCallerSP);
+ GetFrameRestoreBase(pContextRecord),
+ CastHandlerFn(pfnHandler),
+ pFuncletCallerSP);
}
-#else // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#else // USE_FUNCLET_CALL_HELPER
//
// Invoke the funclet.
//
dwResumePC = pfnHandler(sf.SP, OBJECTREFToObject(throwable));
-#endif // _TARGET_ARM_
+#endif // !USE_FUNCLET_CALL_HELPER
switch(funcletType)
{
@@ -4128,6 +4199,7 @@ void ExceptionTracker::MakeCallbacksRelatedToHandler(
}
}
+#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
//---------------------------------------------------------------------------------------
//
// This function is called by DefaultCatchHandler() to intercept an exception and start an unwind.
@@ -4164,6 +4236,7 @@ EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_RE
UNREACHABLE();
}
+#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
#endif // DEBUGGING_SUPPORTED
#ifdef _DEBUG
@@ -4337,7 +4410,7 @@ VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartConte
dispatcherContext.FunctionEntry = codeInfo.GetFunctionEntry();
dispatcherContext.ControlPc = controlPc;
dispatcherContext.ImageBase = codeInfo.GetModuleBase();
-#if defined(_TARGET_ARM_) || defined(_TARGET_X86_)
+#ifdef ADJUST_PC_UNWOUND_TO_CALL
dispatcherContext.ControlPcIsUnwound = !!(currentFrameContext->ContextFlags & CONTEXT_UNWOUND_TO_CALL);
#endif
// Check whether we have a function table entry for the current controlPC.
@@ -4488,7 +4561,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
dispatcherContext.FunctionEntry = codeInfo.GetFunctionEntry();
dispatcherContext.ControlPc = controlPc;
dispatcherContext.ImageBase = codeInfo.GetModuleBase();
-#if defined(_TARGET_ARM_) || defined(_TARGET_X86_)
+#ifdef ADJUST_PC_UNWOUND_TO_CALL
dispatcherContext.ControlPcIsUnwound = !!(frameContext->ContextFlags & CONTEXT_UNWOUND_TO_CALL);
#endif
@@ -4497,6 +4570,11 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
// and then check whether an exception handler exists for the frame.
if (dispatcherContext.FunctionEntry != NULL)
{
+#ifdef USE_CURRENT_CONTEXT_IN_FILTER
+ KNONVOLATILE_CONTEXT currentNonVolatileContext;
+ CaptureNonvolatileRegisters(&currentNonVolatileContext, frameContext);
+#endif // USE_CURRENT_CONTEXT_IN_FILTER
+
RtlVirtualUnwind(UNW_FLAG_EHANDLER,
dispatcherContext.ImageBase,
dispatcherContext.ControlPc,
@@ -4515,6 +4593,9 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
}
dispatcherContext.EstablisherFrame = establisherFrame;
+#ifdef USE_CURRENT_CONTEXT_IN_FILTER
+ dispatcherContext.CurrentNonVolatileContextRecord = &currentNonVolatileContext;
+#endif // USE_CURRENT_CONTEXT_IN_FILTER
dispatcherContext.ContextRecord = frameContext;
// Find exception handler in the current frame
@@ -4704,29 +4785,7 @@ Return value :
--*/
VOID* GetRegisterAddressByIndex(PCONTEXT pContext, UINT index)
{
-#if defined(_TARGET_AMD64_)
- _ASSERTE(index < 16);
- return &((&pContext->Rax)[index]);
-#elif defined(_TARGET_X86_)
- _ASSERTE(index < 8);
-
- static const SIZE_T OFFSET_OF_REGISTERS[] =
- {
- offsetof(CONTEXT, Eax),
- offsetof(CONTEXT, Ecx),
- offsetof(CONTEXT, Edx),
- offsetof(CONTEXT, Ebx),
- offsetof(CONTEXT, Esp),
- offsetof(CONTEXT, Ebp),
- offsetof(CONTEXT, Esi),
- offsetof(CONTEXT, Edi),
- };
-
- return (VOID*)(PBYTE(pContext) + OFFSET_OF_REGISTERS[index]);
-#else
- PORTABILITY_ASSERT("GetRegisterAddressByIndex");
- return NULL;
-#endif
+ return getRegAddr(index, pContext);
}
/*++
@@ -4858,10 +4917,15 @@ DWORD64 GetModRMOperandValue(BYTE rex, BYTE* ip, PCONTEXT pContext, bool is8Bit,
// Get the value we need from the register.
//
- // Check for RIP-relative addressing mode.
+ // Check for RIP-relative addressing mode for AMD64
+ // Check for Displacement only addressing mode for x86
if ((mod == 0) && (rm == 5))
{
+#if defined(_TARGET_AMD64_)
result = (DWORD64)ip + sizeof(INT32) + *(INT32*)ip;
+#else
+ result = (DWORD64)(*(DWORD*)ip);
+#endif // _TARGET_AMD64_
}
else
{
@@ -5048,6 +5112,39 @@ BOOL IsSafeToCallExecutionManager()
GCStress<cfg_instr_ngen>::IsEnabled();
}
+#ifdef VSD_STUB_CAN_THROW_AV
+//Return TRUE if pContext->Pc is in VirtualStub
+static BOOL IsIPinVirtualStub(PCODE f_IP)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Thread * pThread = GetThread();
+
+ // We may not have a managed thread object. Example is an AV on the helper thread.
+ // (perhaps during StubManager::IsStub)
+ if (pThread == NULL)
+ {
+ return FALSE;
+ }
+
+ VirtualCallStubManager::StubKind sk;
+ VirtualCallStubManager::FindStubManager(f_IP, &sk);
+
+ if (sk == VirtualCallStubManager::SK_DISPATCH)
+ {
+ return TRUE;
+ }
+ else if (sk == VirtualCallStubManager::SK_RESOLVE)
+ {
+ return TRUE;
+ }
+
+ else {
+ return FALSE;
+ }
+}
+#endif // VSD_STUB_CAN_THROW_AV
+
BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord)
{
PCODE controlPc = GetIP(contextRecord);
@@ -5055,9 +5152,9 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e
exceptionRecord->ExceptionCode == STATUS_BREAKPOINT ||
exceptionRecord->ExceptionCode == STATUS_SINGLE_STEP ||
(IsSafeToCallExecutionManager() && ExecutionManager::IsManagedCode(controlPc)) ||
-#ifdef _TARGET_ARM_
+#ifdef VSD_STUB_CAN_THROW_AV
IsIPinVirtualStub(controlPc) || // access violation comes from DispatchStub of Interface call
-#endif
+#endif // VSD_STUB_CAN_THROW_AV
IsIPInMarkedJitHelper(controlPc));
}
@@ -5094,9 +5191,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
// Create frame necessary for the exception handling
FrameWithCookie<FaultingExceptionFrame> fef;
-#if defined(WIN64EXCEPTIONS)
*((&fef)->GetGSCookiePtr()) = GetProcessGSCookie();
-#endif // WIN64EXCEPTIONS
{
GCX_COOP(); // Must be cooperative to modify frame chain.
if (IsIPInMarkedJitHelper(controlPc))
@@ -5108,12 +5203,12 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
PAL_VirtualUnwind(ex->GetContextRecord(), NULL);
ex->GetExceptionRecord()->ExceptionAddress = (PVOID)GetIP(ex->GetContextRecord());
}
-#ifdef _TARGET_ARM_
+#ifdef VSD_STUB_CAN_THROW_AV
else if (IsIPinVirtualStub(controlPc))
{
AdjustContextForVirtualStub(ex->GetExceptionRecord(), ex->GetContextRecord());
}
-#endif
+#endif // VSD_STUB_CAN_THROW_AV
fef.InitAndLink(ex->GetContextRecord());
}
@@ -5151,9 +5246,9 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
#endif // FEATURE_PAL
+#ifndef FEATURE_PAL
void ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR ReturnValue, UINT_PTR TargetIP, UINT_PTR TargetFrameSp)
{
-#ifndef FEATURE_PAL
PVOID TargetFrame = (PVOID)TargetFrameSp;
CONTEXT ctx;
@@ -5164,13 +5259,10 @@ void ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR ReturnValue, UINT_
&ctx,
NULL); // HistoryTable
-#else // !FEATURE_PAL
- PORTABILITY_ASSERT("UNIXTODO: Implement unwinding for PAL");
-#endif // !FEATURE_PAL
-
// doesn't return
UNREACHABLE();
}
+#endif // !FEATURE_PAL
void TrackerAllocator::Init()
{
@@ -5302,6 +5394,7 @@ void TrackerAllocator::FreeTrackerMemory(ExceptionTracker* pTracker)
FastInterlockExchangePointer(&(pTracker->m_pThread), NULL);
}
+#ifndef FEATURE_PAL
// This is Windows specific implementation as it is based upon the notion of collided unwind that is specific
// to Windows 64bit.
//
@@ -5326,7 +5419,6 @@ void TrackerAllocator::FreeTrackerMemory(ExceptionTracker* pTracker)
// nt\base\ntos\rtl\{ia64|amd64}\exdsptch.c for RtlUnwindEx().
void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pContext, LPVOID originalControlPC, PEXCEPTION_ROUTINE pUnwindPersonalityRoutine)
{
-#ifndef FEATURE_PAL
if (pContext)
{
STRESS_LOG1(LF_EH, LL_INFO10, "FDC: pContext: %p\n", pContext);
@@ -5447,9 +5539,6 @@ void FixupDispatcherContext(DISPATCHER_CONTEXT* pDispatcherContext, CONTEXT* pCo
}
_ASSERTE(pDispatcherContext->LanguageHandler != NULL);
-#else // !FEATURE_PAL
- PORTABILITY_ASSERT("UNIXTODO: Implement the fixup for PAL");
-#endif // !FEATURE_PAL
}
@@ -5595,6 +5684,7 @@ FixContextHandler(IN PEXCEPTION_RECORD pExceptionRecord
// (which was broken when we whacked the IP to get control over the thread)
return ExceptionCollidedUnwind;
}
+#endif // !FEATURE_PAL
#ifdef _DEBUG
// IsSafeToUnwindFrameChain:
@@ -5806,30 +5896,6 @@ NOT_WIN64_ARG(IN ULONG MemoryStackFp),
}
#endif // _DEBUG
- // We need to ReverseLeaveRuntime if we are unwinding (since there is no
- // frame to do this for us...
- if (IS_UNWINDING(pExceptionRecord->ExceptionFlags))
- {
- BYTE bFlag;
-
-#ifdef _TARGET_AMD64_
- bFlag = *(BYTE*)(pDispatcherContext->ContextRecord->Rbp + UMTHUNKSTUB_HOST_NOTIFY_FLAG_RBPOFFSET);
-#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
- // On ARM, we do not need to do anything here. If required, ReverseEnterRuntime should happen
- // in the VM in UMThunkStubWorker via a holder so that during an exceptional case, we will
- // automatically perform the ReverseLeaveRuntime.
- bFlag = 0;
-#else
- bFlag = 0;
- PORTABILITY_ASSERT("NYI -- UMThunkStubUnwindFrameChainHandler notify host of ReverseLeaveRuntime");
-#endif // _TARGET_AMD64_
-
- if (0 != bFlag)
- {
- GetThread()->ReverseLeaveRuntime();
- }
- }
-
EXCEPTION_DISPOSITION disposition = UMThunkUnwindFrameChainHandler(
pExceptionRecord,
MemoryStackFp,
@@ -5915,6 +5981,7 @@ ReverseComUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRecord
}
#endif // FEATURE_COMINTEROP
+#ifndef FEATURE_PAL
EXTERN_C EXCEPTION_DISPOSITION
FixRedirectContextHandler(
IN PEXCEPTION_RECORD pExceptionRecord
@@ -5951,7 +6018,7 @@ FixRedirectContextHandler(
// (which was broken when we whacked the IP to get control over the thread)
return ExceptionCollidedUnwind;
}
-
+#endif // !FEATURE_PAL
#endif // DACCESS_COMPILE
void ExceptionTracker::StackRange::Reset()
@@ -6198,11 +6265,11 @@ bool ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(CrawlFrame * p
// Remember that sfLowerBound and sfUpperBound are in the "OS format".
// Refer to the comment for CallerStackFrame for more information.
-#if defined(_TARGET_AMD64_)
+#ifndef STACK_RANGE_BOUNDS_ARE_CALLER_SP
if ((sfLowerBound < csfToCheck) && (csfToCheck <= sfUpperBound))
-#else // _TARGET_ARM_ || _TARGET_ARM64_
+#else // !STACK_RANGE_BOUNDS_ARE_CALLER_SP
if ((sfLowerBound <= csfToCheck) && (csfToCheck < sfUpperBound))
-#endif // _TARGET_AMD64_
+#endif // STACK_RANGE_BOUNDS_ARE_CALLER_SP
{
return true;
}
@@ -6290,13 +6357,11 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF)
// Refer to the detailed comment in ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException on the nature
// of this check.
//
-#if defined(_TARGET_AMD64_)
+#ifndef STACK_RANGE_BOUNDS_ARE_CALLER_SP
if ((sfLowerBound < csfToCheck) && (csfToCheck <= sfUpperBound))
-#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
+#else // !STACK_RANGE_BOUNDS_ARE_CALLER_SP
if ((sfLowerBound <= csfToCheck) && (csfToCheck < sfUpperBound))
-#else
- PORTABILITY_ASSERT("ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException");
-#endif // _TARGET_AMD64_
+#endif // STACK_RANGE_BOUNDS_ARE_CALLER_SP
{
fHasFrameBeenUnwound = true;
break;
@@ -6319,7 +6384,7 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF)
// This is applicable to managed frames only.
if (fFrameless)
{
-#if defined(_TARGET_AMD64_)
+#ifndef STACK_RANGE_BOUNDS_ARE_CALLER_SP
// On X64, if the SP of the managed frame indicates that the frame is forming the upper bound,
// then:
//
@@ -6344,7 +6409,7 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF)
break;
}
}
-#else // _TARGET_ARM_ || _TARGET_ARM64_
+#else // !STACK_RANGE_BOUNDS_ARE_CALLER_SP
// On ARM, if the callerSP of the managed frame is the same as upper bound, then:
//
// For case (1), sfCurrentEstablisherFrame will be above the callerSP of the managed frame (since EstbalisherFrame is the caller SP for a given frame on ARM)
@@ -6364,7 +6429,7 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF)
break;
}
}
-#endif // _TARGET_AMD64_
+#endif // STACK_RANGE_BOUNDS_ARE_CALLER_SP
}
// The frame in question does not appear in the current tracker's scanned stack range (of managed frames).
@@ -7049,10 +7114,8 @@ void ExceptionTracker::ResetThreadAbortStatus(PTR_Thread pThread, CrawlFrame *pC
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(pThread != NULL);
-#ifdef WIN64EXCEPTIONS
PRECONDITION(pCf != NULL);
PRECONDITION(!sfCurrentStackFrame.IsNull());
-#endif // WIN64EXCEPTIONS
}
CONTRACTL_END;
@@ -7063,5 +7126,5 @@ void ExceptionTracker::ResetThreadAbortStatus(PTR_Thread pThread, CrawlFrame *pC
}
#endif //!DACCESS_COMPILE
-#endif // _WIN64
+#endif // WIN64EXCEPTIONS