diff --git a/src/coreclr/debug/ee/debugger.inl b/src/coreclr/debug/ee/debugger.inl index 61b44c9466e584..8b7a973f48efff 100644 --- a/src/coreclr/debug/ee/debugger.inl +++ b/src/coreclr/debug/ee/debugger.inl @@ -213,7 +213,7 @@ inline TADDR FuncEvalFrame::GetReturnAddressPtr() // // This updates the register display for a FuncEvalFrame. // -inline void FuncEvalFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +inline void FuncEvalFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { SUPPORTS_DAC; DebuggerEval * pDE = GetDebuggerEval(); diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h index 83afbbbba7d327..1b32ce21b146b9 100644 --- a/src/coreclr/debug/inc/amd64/primitives.h +++ b/src/coreclr/debug/inc/amd64/primitives.h @@ -80,7 +80,11 @@ constexpr CorDebugRegister g_JITToCorDbgReg[] = inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum reg) { _ASSERTE(reg >= 0); - _ASSERTE(static_cast(reg) < ARRAY_SIZE(g_JITToCorDbgReg)); + //_ASSERTE(static_cast(reg) < ARRAY_SIZE(g_JITToCorDbgReg)); + if (static_cast(reg) >= ARRAY_SIZE(g_JITToCorDbgReg)) + { + reg = ICorDebugInfo::REGNUM_RAX; + } return g_JITToCorDbgReg[reg]; } diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 12a1e706602cc5..657c8f16914c13 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -259,7 +259,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("le CONFIG_DWORD_INFO(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableNewExceptionHandling, W("EnableNewExceptionHandling"), 0, "Enable new exception handling."); +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableNewExceptionHandling, W("EnableNewExceptionHandling"), 1, "Enable new exception handling."); /// diff --git a/src/coreclr/inc/vptr_list.h b/src/coreclr/inc/vptr_list.h index 0070c5867aa358..671d3497fa3a3c 100644 --- a/src/coreclr/inc/vptr_list.h +++ b/src/coreclr/inc/vptr_list.h @@ -58,6 +58,7 @@ VPTR_CLASS(DebuggerSecurityCodeMarkFrame) VPTR_CLASS(DebuggerExitFrame) VPTR_CLASS(DebuggerU2MCatchHandlerFrame) VPTR_CLASS(FaultingExceptionFrame) +VPTR_CLASS(NativeToManagedExceptionFrame) VPTR_CLASS(FuncEvalFrame) VPTR_CLASS(HelperMethodFrame) VPTR_CLASS(HelperMethodFrame_1OBJ) diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs index c29e68d1c050a8..c8d9a0a74c2645 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -1084,27 +1084,21 @@ private static bool ShouldTypedClauseCatchThisException(object exception, Method return TypeCast.IsInstanceOfException(pClauseType, exception); #else - bool retry = false; - do + if (tryUnwrapException && exception is RuntimeWrappedException ex) { - MethodTable* mt = RuntimeHelpers.GetMethodTable(exception); - while (mt != null) - { - if (pClauseType == mt) - { - return true; - } - - mt = mt->ParentMethodTable; - } + exception = ex.WrappedException; + } - if (tryUnwrapException && exception is RuntimeWrappedException ex) + MethodTable* mt = RuntimeHelpers.GetMethodTable(exception); + while (mt != null) + { + if (pClauseType == mt) { - exception = ex.WrappedException; - retry = true; + return true; } + + mt = mt->ParentMethodTable; } - while (retry); return false; #endif diff --git a/src/coreclr/pal/src/arch/arm/context2.S b/src/coreclr/pal/src/arch/arm/context2.S index 32983c196969fb..e292ca26fe2adc 100644 --- a/src/coreclr/pal/src/arch/arm/context2.S +++ b/src/coreclr/pal/src/arch/arm/context2.S @@ -18,9 +18,8 @@ #define CONTEXT_CONTROL 1 // Sp, Lr, Pc, Cpsr #define CONTEXT_INTEGER 2 // R0-R12 -#define CONTEXT_SEGMENTS 4 // -#define CONTEXT_FLOATING_POINT 8 -#define CONTEXT_DEBUG_REGISTERS 16 // +#define CONTEXT_FLOATING_POINT 4 +#define CONTEXT_DEBUG_REGISTERS 8 // #define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 26c22810260668..942bb493b46786 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -58,10 +58,18 @@ void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD) pContextPointers->R11 = NULL; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, GetSP()); + _ASSERTE(pRD->pCurrentContext->Rip == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -76,7 +84,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -97,6 +105,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, *(DWORD64 *)&m_pCallSiteSP); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -117,7 +132,7 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -129,6 +144,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, m_MachState.m_Rsp); + _ASSERTE(pRD->pCurrentContext->Rip == m_MachState.m_Rip); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -196,7 +219,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) ClearRegDisplayArgumentAndScratchRegisters(pRD); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -233,7 +256,7 @@ TADDR ResumableFrame::GetReturnAddressPtr() return dac_cast(m_Regs) + offsetof(CONTEXT, Rip); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -273,7 +296,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } // The HijackFrame has to know the registers that are pushed by OnHijackTripThread -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 91e18dc81faf21..27a44b62c119b3 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -127,7 +127,8 @@ LOCAL_LABEL(LReturnDone): EPILOG_STACK_RESTORE_OFFSET r7, #8 EPILOG_POP "{r4,r5,r7,pc}" -PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset +CallDescrWorkerInternalReturnAddressOffset: + .global CallDescrWorkerInternalReturnAddressOffset .word LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) NESTED_END CallDescrWorkerInternal,_TEXT diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 771d2440967022..65e5f2ea1ab6d1 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -629,7 +629,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = true; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -640,6 +640,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, m_MachState._sp); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -1500,8 +1508,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pRD->pCurrentContextPointers->Lr = NULL; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, GetSP()); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -1529,7 +1545,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -1555,7 +1571,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -1581,6 +1597,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, *(DWORD *)&m_pCallSiteSP); + } +#endif // DACCESS_COMPILE + // reset pContext; it's only valid for active (top-most) frame pRD->pContext = NULL; @@ -1613,7 +1636,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -1649,7 +1672,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index cc5dbf7d66b8d5..499fd895f05e7e 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -426,7 +426,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -437,6 +437,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, m_MachState._sp); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -601,8 +609,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, GetSP()); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -626,7 +642,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -659,7 +675,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -680,6 +696,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, *(DWORD64 *)&m_pCallSiteSP); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -722,7 +745,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -762,7 +785,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index fc6e7a5019e725..bb5bed368de9fe 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -1245,7 +1245,7 @@ void DebugStackTrace::DebugStackTraceElement::InitPass2() bRes = g_pDebugInterface->GetILOffsetFromNative( pFunc, (LPCBYTE)this->ip, - fAdjustOffset ? this->dwOffset - 1 : this->dwOffset, + fAdjustOffset ? this->dwOffset - STACKWALK_CONTROLPC_ADJUST_OFFSET : this->dwOffset, &this->dwILOffset); } diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 245909f7a72f70..e723e087d57f4c 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -3383,7 +3383,7 @@ BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT } else if (!pCf->HasFaulted() && pStackTraceElem->ip != 0) { - pStackTraceElem->ip -= 1; + pStackTraceElem->ip -= STACKWALK_CONTROLPC_ADJUST_OFFSET; pStackTraceElem->flags |= STEF_IP_ADJUSTED; } @@ -6563,7 +6563,6 @@ void HandleManagedFaultNew(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext #if defined(FEATURE_EH_FUNCLETS) *frame->GetGSCookiePtr() = GetProcessGSCookie(); #endif // FEATURE_EH_FUNCLETS - pContext->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; frame->InitAndLink(pContext); Thread *pThread = GetThread(); @@ -7103,12 +7102,14 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExcepti // // On 64-bit, some additional work is required.. #ifdef FEATURE_EH_FUNCLETS + pContext->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE; return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION; #endif // defined(FEATURE_EH_FUNCLETS) } else if (AdjustContextForVirtualStub(pExceptionRecord, pContext)) { #ifdef FEATURE_EH_FUNCLETS + pContext->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE; return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION; #endif } @@ -7445,6 +7446,10 @@ LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo) return EXCEPTION_CONTINUE_SEARCH; } +#ifdef FEATURE_EH_FUNCLETS + pExceptionInfo->ContextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; +#endif // FEATURE_EH_FUNCLETS + // WARNING // // We must preserve this so that GCStress=4 eh processing doesnt kill last error. diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 4d28ed16a5e71c..d698a9b8fbe389 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -866,6 +866,24 @@ UINT_PTR ExceptionTracker::FinishSecondPass( void CleanUpForSecondPass(Thread* pThread, bool fIsSO, LPVOID MemoryStackFpForFrameChain, LPVOID MemoryStackFp); +static void PopExplicitFrames(Thread *pThread, void *targetSp) +{ + Frame* pFrame = pThread->GetFrame(); + while (pFrame < targetSp) + { + pFrame->ExceptionUnwind(); + pFrame->Pop(pThread); + pFrame = pThread->GetFrame(); + } + + GCFrame* pGCFrame = pThread->GetGCFrame(); + while (pGCFrame && pGCFrame < targetSp) + { + pGCFrame->Pop(); + pGCFrame = pThread->GetGCFrame(); + } +} + EXTERN_C EXCEPTION_DISPOSITION ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, IN PVOID pEstablisherFrame, @@ -881,6 +899,19 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_THROWS; + Thread* pThread = GetThread(); + + if (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException)) + { + if ((pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) + { + GCX_COOP(); + PopExplicitFrames(pThread, (void*)GetSP(pContextRecord)); + ExInfo::PopExInfos(pThread, (void*)GetSP(pContextRecord)); + } + return ExceptionContinueSearch; + } + #ifndef HOST_UNIX if (!(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) { @@ -890,7 +921,6 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, EEPOLICY_HANDLE_FATAL_ERROR(pExceptionRecord->ExceptionCode); } - Thread* pThread = GetThread(); ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, INVALID_RESUME_ADDRESS, @@ -899,15 +929,15 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, else { GCX_COOP(); - FrameWithCookie frameWithCookie; - FaultingExceptionFrame *frame = &frameWithCookie; - #if defined(FEATURE_EH_FUNCLETS) - *frame->GetGSCookiePtr() = GetProcessGSCookie(); - #endif // FEATURE_EH_FUNCLETS - frame->InitAndLink(pContextRecord); + // FrameWithCookie frameWithCookie; + // NativeToManagedExceptionFrame *frame = &frameWithCookie; + // #if defined(FEATURE_EH_FUNCLETS) + // *frame->GetGSCookiePtr() = GetProcessGSCookie(); + // #endif // FEATURE_EH_FUNCLETS + // frame->InitAndLink(pContextRecord); OBJECTREF oref = ExceptionTracker::CreateThrowable(pExceptionRecord, FALSE); - DispatchManagedException(oref); + DispatchManagedException(oref, pContextRecord); } #endif // !HOST_UNIX EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, _T("SEH exception leaked into managed code")); @@ -931,8 +961,8 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, if (g_isNewExceptionHandlingEnabled) { - ProcessCLRExceptionNew(pExceptionRecord, pEstablisherFrame, pContextRecord, pDispatcherContext); - UNREACHABLE(); + return ProcessCLRExceptionNew(pExceptionRecord, pEstablisherFrame, pContextRecord, pDispatcherContext); + //UNREACHABLE(); } // We must preserve this so that GCStress=4 eh processing doesnt kill last error. @@ -3567,6 +3597,7 @@ void ExceptionTracker::PopTrackerIfEscaping( } CONTRACTL_END; + _ASSERTE(!g_isNewExceptionHandlingEnabled); Thread* pThread = GetThread(); ThreadExceptionState* pExState = pThread->GetExceptionState(); ExceptionTracker* pTracker = (ExceptionTracker*)pExState->m_pCurrentTracker; @@ -3615,6 +3646,11 @@ void ExceptionTracker::PopTrackers( } CONTRACTL_END; + if (g_isNewExceptionHandlingEnabled) + { + return; + } + Thread* pThread = GetThreadNULLOk(); ExceptionTracker* pTracker = (pThread ? (ExceptionTracker*)pThread->GetExceptionState()->m_pCurrentTracker : NULL); @@ -4930,9 +4966,13 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar { if (g_isNewExceptionHandlingEnabled) { + if (!isHardwareException) + { + RtlCaptureContext(ex.GetContextRecord()); + } GCX_COOP(); OBJECTREF throwable = ExceptionTracker::CreateThrowable(ex.GetExceptionRecord(), FALSE); - DispatchManagedException(throwable); + DispatchManagedException(throwable, ex.GetContextRecord()); } do @@ -5544,7 +5584,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) #endif // TARGET_UNIX -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace) +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pExceptionContext, bool preserveStackTrace) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -5554,13 +5594,15 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser _ASSERTE(IsException(throwable->GetMethodTable())); + Thread *pThread = GetThread(); + if (preserveStackTrace) { + pThread->IncPreventAbort(); ExceptionPreserveStackTrace(throwable); + pThread->DecPreventAbort(); } - Thread *pThread = GetThread(); - ULONG_PTR hr = GetHRFromThrowable(throwable); EXCEPTION_RECORD exceptionRecord; @@ -5570,10 +5612,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser exceptionRecord.NumberParameters = MarkAsThrownByUs(exceptionRecord.ExceptionInformation, hr); exceptionRecord.ExceptionRecord = NULL; - CONTEXT exceptionContext; - RtlCaptureContext(&exceptionContext); - - ExInfo exInfo(pThread, &exceptionRecord, &exceptionContext, ExKind::Throw); + ExInfo exInfo(pThread, &exceptionRecord, pExceptionContext, ExKind::Throw); if (pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&throwable)) { @@ -5605,6 +5644,28 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser UNREACHABLE(); } +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + CONTEXT exceptionContext; + RtlCaptureContext(&exceptionContext); +// Thread::VirtualUnwindToFirstManagedCallFrame(&exceptionContext); + +// GCX_COOP(); +// FrameWithCookie frameWithCookie; +// NativeToManagedExceptionFrame *frame = &frameWithCookie; +// #if defined(FEATURE_EH_FUNCLETS) +// *frame->GetGSCookiePtr() = GetProcessGSCookie(); +// #endif // FEATURE_EH_FUNCLETS +// frame->InitAndLink(&exceptionContext); + + DispatchManagedException(throwable, &exceptionContext, preserveStackTrace); + UNREACHABLE(); +} + VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind) { STATIC_CONTRACT_THROWS; @@ -6076,7 +6137,7 @@ void CleanUpForSecondPass(Thread* pThread, bool fIsSO, LPVOID MemoryStackFpForFr // Instead, we rely on the END_SO_TOLERANT_CODE macro to call ClearExceptionStateAfterSO(). Of course, // we may leak in the UMThunkStubCommon() case where we don't have this macro lower on the stack // (stack grows up). - if (!fIsSO) + if (!fIsSO && !g_isNewExceptionHandlingEnabled) { ExceptionTracker::PopTrackerIfEscaping(MemoryStackFp); } @@ -7519,24 +7580,6 @@ extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack e END_QCALL; } -static void PopExplicitFrames(Thread *pThread, void *targetSp) -{ - Frame* pFrame = pThread->GetFrame(); - while (pFrame < targetSp) - { - pFrame->ExceptionUnwind(); - pFrame->Pop(pThread); - pFrame = pThread->GetFrame(); - } - - GCFrame* pGCFrame = pThread->GetGCFrame(); - while (pGCFrame && pGCFrame < targetSp) - { - pGCFrame->Pop(); - pGCFrame = pThread->GetGCFrame(); - } -} - UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { #ifdef HOST_AMD64 @@ -7777,8 +7820,6 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) BOOL fIntercepted = pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo(); _ASSERTE(fIntercepted); - ExInfo::PopExInfos(pThread, (void*)targetSp); - // retrieve the interception information MethodDesc *pInterceptMD = NULL; StackFrame sfInterceptStackFrame; @@ -7787,6 +7828,8 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) pThread->GetExceptionState()->GetDebuggerState()->GetDebuggerInterceptInfo(&pInterceptMD, NULL, (PBYTE*)&(sfInterceptStackFrame.SP), &ulRelOffset, NULL); + ExInfo::PopExInfos(pThread, (void*)targetSp); + PCODE pStartAddress = pInterceptMD->GetNativeCode(); EECodeInfo codeInfo(pStartAddress); @@ -8041,7 +8084,6 @@ static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThrea GCX_COOP(); pThread->SafeSetThrowables(pExInfo->m_exception); EEToProfilerExceptionInterfaceWrapper::ExceptionThrown(pThread); - UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); } else // pExInfo->m_passNumber == 2 { @@ -8127,6 +8169,64 @@ static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExIn pExInfo->m_pMDToReportFunctionLeave = pMD; } +static void UnwindPastFrame(REGDISPLAY* pRD, Frame* pFrame) +{ + return; + + if (pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()) + { + if (InlinedCallFrame::FrameHasActiveCall(pFrame)) + { + InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; + while (GetRegdisplaySP(pRD) < (TADDR)pInlinedCallFrame->GetCallSiteSP()) + { +#ifdef TARGET_UNIX + PAL_VirtualUnwind(pRD->pCurrentContext, NULL); + SyncRegDisplayToCurrentContext(pRD); +#else + Thread::VirtualUnwindCallFrame(pRD); +#endif + } + } + } + else + { + while (GetRegdisplaySP(pRD) < (TADDR)pFrame) + { +#ifdef TARGET_UNIX + PAL_VirtualUnwind(pRD->pCurrentContext, NULL); + SyncRegDisplayToCurrentContext(pRD); +#else + Thread::VirtualUnwindCallFrame(pRD); +#endif + } + if ((pFrame->GetFrameAttribs() & Frame::FRAME_ATTR_CAPTURE_DEPTH_2) != 0) + { +#ifdef TARGET_UNIX + PAL_VirtualUnwind(pRD->pCurrentContext, NULL); + SyncRegDisplayToCurrentContext(pRD); +#else + Thread::VirtualUnwindCallFrame(pRD); +#endif + } + } + +#ifdef TARGET_UNIX + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. +#endif + + // EECodeManager::EnsureCallerContextIsValid(pRD, NULL); + // while (GetSP(pRD->pCallerContext) < (TADDR)pFrame) + // { + // // TODO: reuse EEInfo?? + // Thread::VirtualUnwindCallFrame(pRD); + // EECodeManager::EnsureCallerContextIsValid(pRD, NULL); + // } + // TODO: see if we can get rid of this. Maybe adding a reset to cached codeInfo in the stack frame iterator would do + //EECodeManager::EnsureCallerContextIsValid(pRD, NULL); +} + extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault, bool* pfIsExceptionIntercepted) { QCALL_CONTRACT; @@ -8155,7 +8255,7 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk LONG disposition = InternalUnhandledExceptionFilter_Worker((EXCEPTION_POINTERS *)&pExInfo->m_ptrs); #ifdef HOST_WINDOWS CreateCrashDumpIfEnabled(/* fSOException */ FALSE); - RaiseFailFastException(pExInfo->m_ptrs.ExceptionRecord, pExInfo->m_ptrs.ContextRecord, 0); + RaiseFailFastException(pExInfo->m_ptrs.ExceptionRecord, NULL, 0); #else CrashDumpAndTerminateProcess(pExInfo->m_ExceptionCode); #endif @@ -8165,7 +8265,14 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk pThread->FillRegDisplay(pRD, pStackwalkCtx); new (pThis) StackFrameIterator(); - result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE) != FALSE; + result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE | UNWIND_FLOATS) != FALSE; + + if (result && (pExInfo->m_passNumber == 1)) + { + // TODO: is this coop mode needed? + GCX_COOP(); + UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); + } // Walk the stack until it finds the first managed method while (result && pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD) @@ -8197,6 +8304,9 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk } } } + + UnwindPastFrame(pThis->m_crawl.GetRegisterSet(), pFrame); + StackWalkAction retVal = pThis->Next(); result = (retVal != SWA_FAILED); } @@ -8214,7 +8324,7 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk if (result) { TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; - if (!pThis->m_crawl.HasFaulted()) + if (!pThis->m_crawl.HasFaulted() && !pThis->m_crawl.IsIPadjusted()) { controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; } @@ -8233,6 +8343,11 @@ static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrame do { + if (pStackFrameIterator->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION || pStackFrameIterator->GetFrameState() == StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION) + { + Frame* pFrame = pStackFrameIterator->m_crawl.GetFrame(); + UnwindPastFrame(pStackFrameIterator->m_crawl.GetRegisterSet(), pFrame); + } retVal = pStackFrameIterator->Next(); if (retVal == SWA_FAILED) { @@ -8337,7 +8452,9 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla else { #ifdef HOST_WINDOWS - RaiseFailFastException(pTopExInfo->m_ptrs.ExceptionRecord, pTopExInfo->m_ptrs.ContextRecord, 0); + //RaiseFailFastException(pTopExInfo->m_ptrs.ExceptionRecord, NULL, 0); + GetThread()->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException); + RaiseException(pTopExInfo->m_ExceptionCode, EXCEPTION_NONCONTINUABLE_EXCEPTION, pTopExInfo->m_ptrs.ExceptionRecord->NumberParameters, pTopExInfo->m_ptrs.ExceptionRecord->ExceptionInformation); #else CrashDumpAndTerminateProcess(pTopExInfo->m_ExceptionCode); #endif @@ -8375,6 +8492,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla { // Detect collided unwind pFrame = pThis->m_crawl.GetFrame(); + UnwindPastFrame(pThis->m_crawl.GetRegisterSet(), pFrame); if (InlinedCallFrame::FrameHasActiveCall(pFrame)) { @@ -8411,7 +8529,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla { retVal = MoveToNextNonSkippedFrame(pThis); } - while ((retVal == SWA_CONTINUE) && pThis->m_crawl.GetRegisterSet()->SP != pPrevExInfo->m_regDisplay.SP); + while ((retVal == SWA_CONTINUE) && !(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD && pThis->m_crawl.GetRegisterSet()->SP == pPrevExInfo->m_regDisplay.SP)); _ASSERTE(retVal != SWA_FAILED); pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); @@ -8457,7 +8575,7 @@ Exit:; if (retVal != SWA_FAILED) { TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; - if (!pThis->m_crawl.HasFaulted()) + if (!pThis->m_crawl.HasFaulted() && !pThis->m_crawl.IsIPadjusted()) { controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 74818b9485b6ca..7be99adfd2021f 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -22,6 +22,7 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN OUT PT_CONTEXT pContextRecord, IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, bool preserveStackTrace = true); VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace = true); VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); @@ -50,8 +51,13 @@ typedef DPTR(ExInfo) PTR_ExInfo; // InlinedCallFrame::m_Datum field for details). enum class InlinedCallFrameMarker { +#ifdef HOST_64BIT ExceptionHandlingHelper = 2, SecondPassFuncletCaller = 4, +#else // HOST_64BIT + ExceptionHandlingHelper = 1, + SecondPassFuncletCaller = 2, +#endif // HOST_64BIT Mask = ExceptionHandlingHelper | SecondPassFuncletCaller }; diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 741bae687d81da..4ee3b03345452e 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -326,12 +326,17 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx m_propagateExceptionContext(NULL), #endif // HOST_UNIX m_CurrentClause({}), - m_pMDToReportFunctionLeave(NULL), - m_exContext({}) + m_pMDToReportFunctionLeave(NULL) { m_StackTraceInfo.AllocateStackTrace(); pThread->GetExceptionState()->m_pCurrentTracker = this; - m_exContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + memcpy(&m_exContext, pExceptionContext, sizeof(CONTEXT)); + m_exContext.ContextFlags = m_exContext.ContextFlags & (CONTEXT_FULL | CONTEXT_EXCEPTION_ACTIVE); + if (exceptionKind == ExKind::HardwareFault) + { + // Hardware exception handling needs to start on the FaultingExceptionFrame + SetIP(&m_exContext, 0); + } } #if defined(TARGET_UNIX) diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 90c21e54aa813b..ce6234b8024351 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -464,6 +464,22 @@ void Frame::PopIfChained() } #endif // TARGET_UNIX && !DACCESS_COMPILE +#if !defined(TARGET_X86) || defined(TARGET_UNIX) +void Frame::UpdateFloatingPointRegisters(const PREGDISPLAY pRD, TADDR targetSP) +{ + _ASSERTE(!ExecutionManager::IsManagedCode(::GetIP(pRD->pCurrentContext))); + //while (GetSP(pRD->pCurrentContext) != targetSP) + while (!ExecutionManager::IsManagedCode(::GetIP(pRD->pCurrentContext))) + { +#ifdef TARGET_UNIX + PAL_VirtualUnwind(pRD->pCurrentContext, NULL); +#else + Thread::VirtualUnwindCallFrame(pRD); +#endif + } +} +#endif // !TARGET_X86 || TARGET_UNIX + //----------------------------------------------------------------------- #endif // #ifndef DACCESS_COMPILE //--------------------------------------------------------------- diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 907cc2e0e3eb74..9ccd744efd1636 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -190,6 +190,7 @@ FRAME_TYPE_NAME(ResumableFrame) FRAME_TYPE_NAME(RedirectedThreadFrame) #endif // FEATURE_HIJACK FRAME_TYPE_NAME(FaultingExceptionFrame) +FRAME_TYPE_NAME(NativeToManagedExceptionFrame) #ifdef DEBUGGING_SUPPORTED FRAME_TYPE_NAME(FuncEvalFrame) #endif // DEBUGGING_SUPPORTED @@ -512,7 +513,7 @@ class Frame : public FrameBase // UpdateRegDisplay is generally used to fill out the REGDISPLAY parameter, some // overrides (e.g., code:ResumableFrame::UpdateRegDisplay) will actually READ what // you pass in. So be sure to pass in a valid or zeroed out REGDISPLAY. - virtual void UpdateRegDisplay(const PREGDISPLAY) + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false) { LIMITED_METHOD_DAC_CONTRACT; return; @@ -754,6 +755,12 @@ class Frame : public FrameBase LIMITED_METHOD_CONTRACT; } +#ifndef DACCESS_COMPILE +#if !defined(TARGET_X86) || defined(TARGET_UNIX) + void UpdateFloatingPointRegisters(const PREGDISPLAY pRD, TADDR targetSP); +#endif // !TARGET_X86 || TARGET_UNIX +#endif // DACCESS_COMPILE + #if defined(TARGET_UNIX) && !defined(DACCESS_COMPILE) virtual ~Frame() { LIMITED_METHOD_CONTRACT; } @@ -795,7 +802,7 @@ class ResumableFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); virtual unsigned GetFrameAttribs() { LIMITED_METHOD_DAC_CONTRACT; @@ -1000,7 +1007,7 @@ class TransitionFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); #ifdef TARGET_X86 void UpdateRegDisplayHelper(const PREGDISPLAY, UINT cbStackPop); #endif @@ -1052,7 +1059,7 @@ class FaultingExceptionFrame : public Frame TADDR m_ReturnAddress; T_CONTEXT m_ctx; #endif // !FEATURE_EH_FUNCLETS - + VPTR_VTABLE_CLASS(FaultingExceptionFrame, Frame) public: @@ -1080,7 +1087,11 @@ class FaultingExceptionFrame : public Frame unsigned GetFrameAttribs() { LIMITED_METHOD_DAC_CONTRACT; +#ifdef FEATURE_EH_FUNCLETS + return FRAME_ATTR_EXCEPTION | (!!(m_ctx.ContextFlags & CONTEXT_EXCEPTION_ACTIVE) ? FRAME_ATTR_FAULTED : 0); +#else return FRAME_ATTR_EXCEPTION | FRAME_ATTR_FAULTED; +#endif } #ifndef FEATURE_EH_FUNCLETS @@ -1107,6 +1118,7 @@ class FaultingExceptionFrame : public Frame LIMITED_METHOD_CONTRACT; return &m_fFilterExecuted; } + #endif // FEATURE_EH_FUNCLETS virtual BOOL NeedsUpdateRegDisplay() @@ -1114,12 +1126,32 @@ class FaultingExceptionFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); // Keep as last entry in class DEFINE_VTABLE_GETTER_AND_DTOR(FaultingExceptionFrame) }; +class NativeToManagedExceptionFrame : public FaultingExceptionFrame +{ + VPTR_VTABLE_CLASS(NativeToManagedExceptionFrame, FaultingExceptionFrame) +public: +#ifndef DACCESS_COMPILE + NativeToManagedExceptionFrame() { + LIMITED_METHOD_CONTRACT; + } +#endif + + unsigned GetFrameAttribs() + { + LIMITED_METHOD_DAC_CONTRACT; + return FRAME_ATTR_EXCEPTION; + } + + // Keep as last entry in class + DEFINE_VTABLE_GETTER_AND_DTOR(NativeToManagedExceptionFrame) +}; + //----------------------------------------------------------------------- // Frame for debugger function evaluation // @@ -1176,7 +1208,7 @@ class FuncEvalFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual DebuggerEval * GetDebuggerEval(); @@ -1263,7 +1295,7 @@ class HelperMethodFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual Interception GetInterception() { @@ -2037,7 +2069,7 @@ class PInvokeCalliFrame : public FramedMethodFrame } #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); #endif // TARGET_X86 BOOL TraceFrame(Thread *thread, BOOL fromPatch, @@ -2081,7 +2113,7 @@ class HijackFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual void GcScanRoots(promote_func *fn, ScanContext* sc); // HijackFrames are created by trip functions. See OnHijackTripThread() @@ -2176,7 +2208,7 @@ class StubDispatchFrame : public FramedMethodFrame PTR_BYTE GetGCRefMap(); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); virtual PCODE GetReturnAddress(); #endif // TARGET_X86 @@ -2319,7 +2351,7 @@ class ExternalMethodFrame : public FramedMethodFrame Interception GetInterception(); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); #endif // Keep as last entry in class @@ -2341,7 +2373,7 @@ class DynamicHelperFrame : public FramedMethodFrame virtual void GcScanRoots(promote_func *fn, ScanContext* sc); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); #endif virtual ETransitionType GetTransitionType() @@ -2800,7 +2832,8 @@ class InlinedCallFrame : public Frame { WRAPPER_NO_CONTRACT; if (FrameHasActiveCall(this) && HasFunction()) - return PTR_MethodDesc(m_Datum); + // Mask off marker bits + return PTR_MethodDesc((dac_cast(m_Datum) & ~(sizeof(TADDR) - 1))); else return NULL; } @@ -2811,7 +2844,7 @@ class InlinedCallFrame : public Frame #ifdef HOST_64BIT // See code:GenericPInvokeCalliHelper - return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x3)); + return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x1)); #else // HOST_64BIT return ((dac_cast(m_Datum) & ~0xffff) != 0); #endif // HOST_64BIT @@ -2868,7 +2901,7 @@ class InlinedCallFrame : public Frame #endif // defined(TARGET_X86) || defined(TARGET_ARM) } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); // m_Datum contains MethodDesc ptr or // - on 64 bit host: CALLI target address (if lowest bit is set) @@ -3034,7 +3067,7 @@ class TailCallFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); private: // Keep as last entry in class diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index 496c7c3f34366b..9bbc1046e806a2 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -139,7 +139,7 @@ void EHContext::UpdateFrame(PREGDISPLAY regs) } #endif // FEATURE_EH_FUNCLETS -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -211,7 +211,7 @@ void TransitionFrame::UpdateRegDisplayHelper(const PREGDISPLAY pRD, UINT cbStack RETURN; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -391,7 +391,7 @@ EXTERN_C MachState* STDCALL HelperMethodFrameConfirmState(HelperMethodFrame* fra } #endif -void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -411,7 +411,7 @@ void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } -void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -468,7 +468,7 @@ PCODE StubDispatchFrame::GetReturnAddress() return retAddress; } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -521,7 +521,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -615,7 +615,7 @@ TADDR ResumableFrame::GetReturnAddressPtr() return dac_cast(m_Regs) + offsetof(CONTEXT, Eip); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -696,7 +696,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) // The HijackFrame has to know the registers that are pushed by OnHijackTripThread // -> HijackFrame::UpdateRegDisplay should restore all the registers pushed by OnHijackTripThread -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; @@ -753,7 +753,7 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) #endif // FEATURE_HIJACK -void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -774,7 +774,7 @@ void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } #ifndef UNIX_X86_ABI -void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -822,7 +822,7 @@ void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) #endif // !UNIX_X86_ABI #ifdef FEATURE_READYTORUN -void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { WRAPPER_NO_CONTRACT; UpdateRegDisplayHelper(pRD, 0); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index d41861f6c3567e..657d9f72f4769f 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -4305,7 +4305,11 @@ void RethrowNew() ExInfo *pActiveExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker(); - ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pActiveExInfo->m_ptrs.ContextRecord, ExKind::None); + CONTEXT exceptionContext; + RtlCaptureContext(&exceptionContext); + //Thread::VirtualUnwindToFirstManagedCallFrame(&exceptionContext); + + ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, &exceptionContext, ExKind::None); GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index 5fe3599d0dc507..9f00012812fdc2 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -450,7 +450,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -461,6 +461,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, m_MachState._sp); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -620,8 +628,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pContextPointers->Ra = (PDWORD64)&pCalleeSaved->ra; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, GetSP()); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -643,7 +659,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -676,7 +692,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -697,6 +713,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, *(DWORD64 *)&m_pCallSiteSP); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -739,7 +762,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -796,7 +819,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 42c0230509d70c..c37fc5b589e599 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -344,7 +344,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -355,6 +355,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, m_MachState._sp); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -534,8 +542,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pContextPointers->Ra = (PDWORD64)&pCalleeSaved->ra; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, GetSP()); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -557,7 +573,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -593,7 +609,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -614,6 +630,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD, *(DWORD64 *)&m_pCallSiteSP); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -659,7 +682,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -716,7 +739,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 92a0cd9dc76b47..cd971db4236f28 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1234,6 +1234,12 @@ BOOL StackFrameIterator::Init(Thread * pThread, // process the REGDISPLAY and stop at the first frame ProcessIp(GetControlPC(m_crawl.pRD)); +#ifdef FEATURE_EH_FUNCLETS + if (m_crawl.isFrameless && !!(m_crawl.pRD->pCurrentContext->ContextFlags & CONTEXT_EXCEPTION_ACTIVE)) + { + m_crawl.hasFaulted = true; + } +#endif // FEATURE_EH_FUNCLETS ProcessCurrentFrame(); // advance to the next frame which matches the stackwalk flags @@ -1373,7 +1379,7 @@ BOOL StackFrameIterator::ResetRegDisp(PREGDISPLAY pRegDisp, else { // unwind the REGDISPLAY using the transition frame and check the EBP - m_crawl.pFrame->UpdateRegDisplay(&tmpRD); + m_crawl.pFrame->UpdateRegDisplay(&tmpRD, m_flags & UNWIND_FLOATS); if (GetRegdisplayFP(&tmpRD) != curEBP) { break; @@ -1400,8 +1406,7 @@ BOOL StackFrameIterator::ResetRegDisp(PREGDISPLAY pRegDisp, m_crawl.isIPadjusted = false; } - m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD); - + m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD, m_flags & UNWIND_FLOATS); _ASSERTE(curPc == GetControlPC(m_crawl.pRD)); } @@ -2721,7 +2726,7 @@ StackWalkAction StackFrameIterator::NextRaw(void) if (m_crawl.isFrameless) { - m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD); + m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD, m_flags & UNWIND_FLOATS); #if defined(RECORD_RESUMABLE_FRAME_SP) CONSISTENCY_CHECK(NULL == m_pvResumableFrameTargetSP); diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index bfb154b0e539de..ef5ba294a1582f 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2768,6 +2768,8 @@ class Thread // may still execute GS cookie tracking/checking code paths. #define SKIP_GSCOOKIE_CHECK 0x10000 + #define UNWIND_FLOATS 0x20000 + StackWalkAction StackWalkFramesEx( PREGDISPLAY pRD, // virtual register set at crawl start PSTACKWALKFRAMESCALLBACK pCallback, diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 84d1ade6037be2..ed6d740f1f9022 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -3973,7 +3973,8 @@ ThrowControlForThread( exceptionRecord.ExceptionFlags = 0; OBJECTREF throwable = ExceptionTracker::CreateThrowable(&exceptionRecord, TRUE); - DispatchManagedException(throwable); + pfef->GetExceptionContext()->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; + DispatchManagedException(throwable, pfef->GetExceptionContext()); } else #endif // FEATURE_EH_FUNCLETS diff --git a/src/native/external/libunwind/src/mi/Gaddress_validator.c b/src/native/external/libunwind/src/mi/Gaddress_validator.c index aaf5a0941214fa..257d1dca93ea8c 100644 --- a/src/native/external/libunwind/src/mi/Gaddress_validator.c +++ b/src/native/external/libunwind/src/mi/Gaddress_validator.c @@ -24,7 +24,6 @@ */ #include "libunwind_i.h" - #ifdef UNW_REMOTE_ONLY bool unw_address_is_valid(UNUSED unw_word_t addr, UNUSED size_t len) @@ -35,17 +34,19 @@ unw_address_is_valid(UNUSED unw_word_t addr, UNUSED size_t len) #else /* !UNW_REMOTE_ONLY */ -#include +static pthread_once_t _unw_address_validator_init_once = PTHREAD_ONCE_INIT; +static sig_atomic_t _unw_address_validator_initialized = 0; +static int _mem_validate_pipe[2] = {-1, -1}; +static bool (*_mem_validate_func) (unw_word_t, size_t); +#pragma weak pthread_once -static atomic_flag _unw_address_validator_initialized = ATOMIC_FLAG_INIT; -static int _mem_validate_pipe[2] = {-1, -1}; #ifdef HAVE_PIPE2 -static int +static void _do_pipe2 (int pipefd[2]) { - return pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); + int result UNUSED = pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); } #else static void @@ -53,80 +54,54 @@ _set_pipe_flags (int fd) { int fd_flags = fcntl (fd, F_GETFD, 0); int status_flags = fcntl (fd, F_GETFL, 0); - fd_flags |= FD_CLOEXEC; fcntl (fd, F_SETFD, fd_flags); - status_flags |= O_NONBLOCK; fcntl (fd, F_SETFL, status_flags); } - -static int +static void _do_pipe2 (int pipefd[2]) { - if (pipe (pipefd) < 0) - { - return -1; - } + pipe (pipefd); _set_pipe_flags(pipefd[0]); _set_pipe_flags(pipefd[1]); } #endif - - -static int +static void _open_pipe (void) { if (_mem_validate_pipe[0] != -1) close (_mem_validate_pipe[0]); if (_mem_validate_pipe[1] != -1) close (_mem_validate_pipe[1]); - - return _do_pipe2 (_mem_validate_pipe); + _do_pipe2 (_mem_validate_pipe); } - - /** * Test is a memory address is valid by trying to write from it * @param[in] addr The address to validate * - * @returns true if the memory address is valid (readable), false otherwise. + * @returns true of the memory address is valid (readable), false otherwise. * * This check works by using the address as a (one-byte) buffer in a * write-to-pipe operation. The write will fail if the memory is not in the - * process's address space and marked as readable. The read will force the page - * to be swapped in if it's not already there. + * process's address space and marked as readable. */ static bool _write_validate (unw_word_t addr) { int ret = -1; ssize_t bytes = 0; - - if (unlikely (!atomic_flag_test_and_set(&_unw_address_validator_initialized))) - { - if (_open_pipe () != 0) - { - return false; - } - } - do { char buf; bytes = read (_mem_validate_pipe[0], &buf, 1); } while ( errno == EINTR ); - if (!(bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK)) { // re-open closed pipe - if (_open_pipe () != 0) - { - return false; - } + _open_pipe (); } - do { #ifdef HAVE_SYS_SYSCALL_H @@ -137,11 +112,71 @@ _write_validate (unw_word_t addr) #endif } while ( errno == EINTR ); - return ret > 0; } +static bool +_msync_validate (unw_word_t addr, size_t len) +{ + if (msync ( (void *)unw_page_start (addr), len, MS_ASYNC) != 0) + { + return false; + } + + return _write_validate (addr); +} + + +#ifdef HAVE_MINCORE +static bool +_mincore_validate (unw_word_t addr, size_t len) +{ + unsigned char mvec[2]; /* Unaligned access may cross page boundary */ + + /* mincore could fail with EAGAIN but we conservatively return false + instead of looping. */ + if (mincore ((void *)unw_page_start (addr), len, mvec) != 0) + { + return false; + } + + return _write_validate (addr); +} +#endif + + +static void +_unw_address_validator_init(void) +{ + _open_pipe (); + + /* Work out dynamically what memory validation function to use. */ +#ifdef HAVE_MINCORE + unsigned char present = 1; + size_t len = unw_page_size; + unw_word_t addr = unw_page_start((unw_word_t)&present); + unsigned char mvec[1]; + int ret; + do + { + ret = mincore ((void*)addr, len, mvec); + } + while (ret == -1 && errno == EAGAIN); + if (ret == 0) + { + Debug(1, "using mincore to validate memory\n"); + _mem_validate_func = _mincore_validate; + } + else +#endif + { + Debug(1, "using msync to validate memory\n"); + _mem_validate_func = _msync_validate; + } + _unw_address_validator_initialized = ~0; +} + /* Cache of already validated addresses */ enum { NLGA = 4 }; @@ -149,99 +184,90 @@ enum { NLGA = 4 }; // thread-local variant static _Thread_local unw_word_t last_good_addr[NLGA]; static _Thread_local int lga_victim; - - static bool -_is_cached_valid_mem(unw_word_t page_addr) +_is_cached_valid_mem(unw_word_t addr) { + addr = unw_page_start (addr); int i; for (i = 0; i < NLGA; i++) { - if (page_addr == last_good_addr[i]) + if (addr == last_good_addr[i]) return true; } return false; } - - static void -_cache_valid_mem(unw_word_t page_addr) +_cache_valid_mem(unw_word_t addr) { + addr = unw_page_start (addr); int i, victim; victim = lga_victim; for (i = 0; i < NLGA; i++) { if (last_good_addr[victim] == 0) { - last_good_addr[victim] = page_addr; + last_good_addr[victim] = addr; return; } victim = (victim + 1) % NLGA; } - /* All slots full. Evict the victim. */ - last_good_addr[victim] = page_addr; + last_good_addr[victim] = addr; victim = (victim + 1) % NLGA; lga_victim = victim; } - #else // global, thread safe variant static _Atomic unw_word_t last_good_addr[NLGA]; static _Atomic int lga_victim; - - static bool -_is_cached_valid_mem(unw_word_t page_addr) +_is_cached_valid_mem(unw_word_t addr) { int i; + addr = unw_page_start (addr); for (i = 0; i < NLGA; i++) { - if (page_addr == atomic_load(&last_good_addr[i])) + if (addr == atomic_load(&last_good_addr[i])) return true; } return false; } - - /** * Adds a known-valid page address to the cache. * * This implementation is racy as all get-out but the worst case is that cached * address get lost, forcing extra unnecessary validation checks. All of the - * atomic operations don't matter because of TOCTOU races. + * atomic operatrions don't matter because of TOCTOU races. */ static void -_cache_valid_mem(unw_word_t page_addr) +_cache_valid_mem(unw_word_t addr) { + int i, victim; + victim = atomic_load(&lga_victim); unw_word_t zero = 0; - int victim = atomic_load(&lga_victim); - for (int i = 0; i < NLGA; i++) + addr = unw_page_start (addr); + for (i = 0; i < NLGA; i++) { - if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, page_addr)) + if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { return; } victim = (victim + 1) % NLGA; } - /* All slots full. Evict the victim. */ - atomic_store(&last_good_addr[victim], page_addr); + atomic_store(&last_good_addr[victim], addr); victim = (victim + 1) % NLGA; atomic_store(&lga_victim, victim); } #endif - - /** * Validate an address is readable - * @param[in] addr The (starting) address of the memory range to validate - * @param[in] len The size of the memory range to validate in bytes + * @param[in] addr The (starting) address of the memory to validate + * @param[in] len The size of the memory to validate in bytes * - * Validates the memory range from @p addr to (@p addr + @p len - 1) is - * readable. Since the granularity of memory readability is the page, only one - * byte needs to be validated per page for each page starting at @p addr and - * encompassing @p len bytes. Only the first address of each page is checked. + * Validates the memory at address @p addr is readable. Since the granularity of + * memory readability is the page, only one byte needs to be validated per page + * for each page starting at @p addr and encompassing @p len bytes. * * @returns true if the memory is readable, false otherwise. */ @@ -250,53 +276,46 @@ unw_address_is_valid(unw_word_t addr, size_t len) { if (len == 0) return true; - - /* - * Find the starting address of the page containing the start of the range. - */ - unw_word_t start_page_addr = unw_page_start (addr); - - /* - * Bounds check on bottom of memory: first page is always deemed inaccessible. - * This is potentially incorrect on an embedded system, especially one running - * on bare metal with no VMM, but the check has always been here and no one - * has complained. - */ - if (start_page_addr == 0) - return false; - - /* - * Bounds check on top of memory. Unsigned wraparound could be hazardous. - */ - if (addr > (UNW_WORD_MAX - len - unw_page_size)) + if (unw_page_start (addr) == 0) return false; /* - * Find the starting address of the page containing the end of the range. + * First time through initialize everything: once case if linked with pthreads + * and another when pthreads are not linked (which assumes the single-threaded + * case). + * + * There is a potential race condition in the second case if multiple signals + * are raised at exactly the same time but the worst case is that several + * unnecessary validations get done. */ - unw_word_t end_page_addr = unw_page_start (addr + (len - 1)) + unw_page_size; + if (likely (pthread_once != NULL)) + { + pthread_once (&_unw_address_validator_init_once, _unw_address_validator_init); + } + else if (unlikely (_unw_address_validator_initialized == 0)) + { + _unw_address_validator_init(); + } - /* - * Step through each page and check if the first address in each is readable. - * The first non-readable page encountered means none of them in the given - * range can be considered readable. - */ - for (unw_word_t page_addr = start_page_addr; - page_addr < end_page_addr; - page_addr += unw_page_size) + unw_word_t lastbyte = addr + (len - 1); // highest addressed byte of data to access + while (1) { - if (!_is_cached_valid_mem(page_addr)) + if (!_is_cached_valid_mem(addr)) { - if (!_write_validate (page_addr)) + if (!_mem_validate_func (addr, len)) { Debug(1, "returning false\n"); return false; } - _cache_valid_mem(page_addr); + _cache_valid_mem(addr); } + // If we're still on the same page, we're done. + size_t stride = len-1 < (size_t) unw_page_size ? len-1 : (size_t) unw_page_size; + len -= stride; + addr += stride; + if (unw_page_start (addr) == unw_page_start (lastbyte)) + break; } - return true; } - #endif /* !UNW_REMOTE_ONLY */