diff options
Diffstat (limited to 'src/vm/exceptionhandling.cpp')
-rw-r--r-- | src/vm/exceptionhandling.cpp | 389 |
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, ®disp, 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(¤tNonVolatileContext, 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 = ¤tNonVolatileContext; +#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 |