From 1431e1313c22b5e32f4da2c60810b77caacd1f58 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 18 Sep 2025 22:00:31 +0200 Subject: [PATCH 01/15] [WIP] Move coreclr EH second pass to native code There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution. --- .../src/System/Runtime/ExceptionHandling.cs | 86 +++-- src/coreclr/vm/corelib.h | 4 +- src/coreclr/vm/excep.cpp | 14 +- src/coreclr/vm/exceptionhandling.cpp | 299 +++++++++++++++--- src/coreclr/vm/exceptionhandling.h | 2 + src/coreclr/vm/exceptionhandlingqcalls.h | 2 - src/coreclr/vm/exinfo.h | 6 + src/coreclr/vm/metasig.h | 4 +- src/coreclr/vm/qcallentrypoints.cpp | 2 - 9 files changed, 336 insertions(+), 83 deletions(-) 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 d69ec851b2e031..3f4f7e4374c2bd 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -551,14 +551,14 @@ internal object ThrownException internal volatile UIntPtr _notifyDebuggerSP; } - // - // Called by RhpThrowHwEx - // -#if NATIVEAOT - [RuntimeExport("RhThrowHwEx")] -#endif [StackTraceHidden] - public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) + public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + { + FindHwExHandler(exceptionCode, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + } + + [StackTraceHidden] + public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point @@ -629,17 +629,33 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) } exInfo.Init(exceptionToThrow!, instructionFault); - DispatchEx(ref exInfo._frameIter, ref exInfo); + FindExHandler(exceptionToThrow!, ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + } + + // + // Called by RhpThrowHwEx + // +#if NATIVEAOT + [RuntimeExport("RhThrowHwEx")] +#endif + [StackTraceHidden] + public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) + { + FindHwExHandler(exceptionCode, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } private const uint MaxTryRegionIdx = 0xFFFFFFFFu; -#if NATIVEAOT - [RuntimeExport("RhThrowEx")] -#endif [StackTraceHidden] - public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) + public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + { + FindExHandler(exceptionObj, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + } + + [StackTraceHidden] + public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT @@ -662,11 +678,22 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) } exInfo.Init(exceptionObj); - DispatchEx(ref exInfo._frameIter, ref exInfo); + DispatchExPass1(ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + } + +#if NATIVEAOT + [RuntimeExport("RhThrowEx")] +#endif + [StackTraceHidden] + public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) + { + FindExHandler(exceptionObj, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } #if !NATIVEAOT + // TODO: move this to native too public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStackFrameSP) { exInfo._passNumber = 2; @@ -749,23 +776,24 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) object rethrownException = activeExInfo.ThrownException; exInfo.Init(rethrownException, ref activeExInfo); - DispatchEx(ref exInfo._frameIter, ref exInfo); + FindExHandler(rethrownException, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } [StackTraceHidden] - private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExInfo exInfo) + private static void DispatchExPass1(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; - + scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; // ------------------------------------------------ // // First pass // // ------------------------------------------------ - UIntPtr handlingFrameSP = MaxSP; - byte* pCatchHandler = null; + handlingFrameSP = MaxSP; + pCatchHandler = null; uint catchingTryRegionIdx = MaxTryRegionIdx; bool isFirstRethrowFrame = (exInfo._kind & ExKind.RethrowFlag) != 0; @@ -776,8 +804,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn byte* prevOriginalPC = null; UIntPtr prevFramePtr = UIntPtr.Zero; bool unwoundReversePInvoke = false; - IntPtr pReversePInvokePropagationCallback = IntPtr.Zero; - IntPtr pReversePInvokePropagationContext = IntPtr.Zero; + pReversePInvokePropagationCallback = IntPtr.Zero; + pReversePInvokePropagationContext = IntPtr.Zero; bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); @@ -869,6 +897,12 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn // without a catch handler or propagation callback. Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke || isExceptionIntercepted, "We should have a handler if we're starting the second pass"); Debug.Assert(!isExceptionIntercepted || (pCatchHandler == null), "No catch handler should be returned for intercepted exceptions in the first pass"); + exInfo._idxCurClause = catchingTryRegionIdx; + } + + [StackTraceHidden] + private static void DispatchExPass2(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) + { // ------------------------------------------------ // @@ -884,12 +918,14 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn #if NATIVEAOT InternalCalls.RhpSetThreadDoNotTriggerGC(); #endif + scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; exInfo._passNumber = 2; - exInfo._idxCurClause = catchingTryRegionIdx; - startIdx = MaxTryRegionIdx; - unwoundReversePInvoke = false; - isExceptionIntercepted = false; - isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); + object exceptionObj = exInfo.ThrownException; + uint startIdx = MaxTryRegionIdx; + uint catchingTryRegionIdx = exInfo._idxCurClause; + bool unwoundReversePInvoke = false; + bool isExceptionIntercepted = false; + bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 0b979353253f72..cf31e8e649e475 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1344,8 +1344,8 @@ DEFINE_FIELD_U(dictionaryIndexAndSlot, GenericHandleArgs, dictionaryIndexAndSlot #ifdef FEATURE_EH_FUNCLETS DEFINE_CLASS(EH, Runtime, EH) -DEFINE_METHOD(EH, RH_THROW_EX, RhThrowEx, SM_Obj_RefExInfo_RetVoid) -DEFINE_METHOD(EH, RH_THROWHW_EX, RhThrowHwEx, SM_UInt_RefExInfo_RetVoid) +DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) +DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_RetVoid) DEFINE_METHOD(EH, UNWIND_AND_INTERCEPT, RhUnwindAndIntercept, SM_RefExInfo_UIntPtr_RetVoid) DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 634fe52a17070c..03ba0ae111b4c3 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6171,18 +6171,26 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) } } + TADDR handlingFrameSP; + PCODE pCatchHandler; + GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); - DECLARE_ARGHOLDER_ARRAY(args, 2); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.RhThrowHwEx(exceptionCode, &exInfo) + //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) + DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + GCPROTECT_END(); + UNREACHABLE(); } #endif // USE_FEF && !TARGET_UNIX diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 3fd89787003e93..f79a61a6c84f22 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1502,17 +1502,24 @@ BOOL HandleHardwareException(PAL_SEHException* ex) exInfo.TakeExceptionPointersOwnership(ex); } + TADDR handlingFrameSP; + PCODE pCatchHandler; + GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); - DECLARE_ARGHOLDER_ARRAY(args, 2); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.RhThrowHwEx(exceptionCode, &exInfo) + //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) + DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + GCPROTECT_END(); UNREACHABLE(); @@ -1643,17 +1650,24 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROW_EX); - DECLARE_ARGHOLDER_ARRAY(args, 2); + TADDR handlingFrameSP; + PCODE pCatchHandler; + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_EX_HANDLER); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(throwable); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.RhThrowEx(throwable, &exInfo) + //Ex.FindExHandler(throwable, &exInfo, &handlingFrameSP, &pCatchHandler) CRITICAL_CALLSITE; CALL_MANAGED_METHOD_NORET(args) + DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + GCPROTECT_END(); GCPROTECT_END(); @@ -3121,18 +3135,21 @@ PropagateForeignExceptionThroughNativeFrames(IN PEXCEPTION_RECORD pExcepti #endif // HOST_WINDOWS -extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { - QCALL_CONTRACT; - - BEGIN_QCALL; - GCX_COOP_NO_DTOR(); - + CONTRACTL + { + MODE_COOPERATIVE; + GC_NOTRIGGER; + THROWS; + } + CONTRACTL_END; + Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsFuncletCall(pFrame); +// MarkInlinedCallFrameAsFuncletCall(pFrame); exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); DWORD_PTR dwResumePC = 0; UINT_PTR callerTargetSp = 0; @@ -3158,7 +3175,6 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio pCodeManager->EnsureCallerContextIsValid(pvRegDisplay); _ASSERTE(exInfo->m_sfCallerOfActualHandlerFrame == GetSP(pvRegDisplay->pCallerContext)); #endif - OBJECTREF throwable = exceptionObj.Get(); throwable = PossiblyUnwrapThrowable(throwable, exInfo->m_frameIter.m_crawl.GetAssembly()); exInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(exInfo->m_frameIter.m_crawl.GetRegisterSet()); @@ -3304,12 +3320,10 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio #endif { STRESS_LOG2(LF_EH, LL_INFO100, "Resuming propagation of managed exception through native frames at IP=%p, SP=%p\n", GetIP(pvRegDisplay->pCurrentContext), GetSP(pvRegDisplay->pCurrentContext)); - ExecuteFunctionBelowContext((PCODE)PropagateExceptionThroughNativeFrames, pvRegDisplay->pCurrentContext, targetSSP, (size_t)OBJECTREFToObject(exceptionObj.Get())); + ExecuteFunctionBelowContext((PCODE)PropagateExceptionThroughNativeFrames, pvRegDisplay->pCurrentContext, targetSSP, (size_t)OBJECTREFToObject(throwable)); } #undef FIRST_ARG_REG } - END_QCALL; - return NULL; } extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) @@ -3364,11 +3378,8 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext, targetSSP); } -extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { - QCALL_CONTRACT; - - BEGIN_QCALL; GCX_COOP(); Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); @@ -3390,7 +3401,6 @@ extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvReg // Profiler, debugger and ETW events exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); - END_QCALL; } extern "C" CLR_BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterIP, REGDISPLAY* pvRegDisplay) @@ -3466,16 +3476,12 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterato return TRUE; } -extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) +// The onlyFinallys option makes the function return only finally and fault clauses. It is used in +// the 2nd path of EH primarily to avoid calls to ResolveEHClause that can trigger GC. +CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, bool onlyFinallys = false) { - QCALL_CONTRACT; CLR_BOOL result = FALSE; - BEGIN_QCALL; - Thread* pThread = GET_THREAD(); - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsEHHelperCall(pFrame); - ExtendedEHClauseEnumerator *pExtendedEHEnum = (ExtendedEHClauseEnumerator*)pEHEnum; StackFrameIterator *pFrameIter = pExtendedEHEnum->pFrameIter; @@ -3511,6 +3517,12 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau pEHClause->_tryStartOffset, pEHClause->_tryEndOffset, pEHClause->_isSameTry ? ", isSameTry" : "", pEHClause->_handlerAddress)); + if (onlyFinallys && !(flags & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT))) + { + // Skip non-finally clauses + continue; + } + if (flags == COR_ILEXCEPTION_CLAUSE_NONE) { pEHClause->_clauseKind = RH_EH_CLAUSE_TYPED; @@ -3530,8 +3542,8 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau } else if (flags & COR_ILEXCEPTION_CLAUSE_FAULT) { - EH_LOG((LL_INFO100, " fault clause\n")); pEHClause->_clauseKind = RH_EH_CLAUSE_FAULT; + EH_LOG((LL_INFO100, " fault clause\n")); } else { @@ -3552,6 +3564,23 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau } #endif // HOST_WINDOWS } + + return result; +} + +extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) +{ + QCALL_CONTRACT; + + CLR_BOOL result = FALSE; + + BEGIN_QCALL; + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + result = EHEnumNextWorker(pEHEnum, pEHClause); END_QCALL; return result; @@ -3739,24 +3768,18 @@ static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExIn END_PROFILER_CALLBACK(); } -extern "C" CLR_BOOL QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted) +CLR_BOOL SfiInitWorker(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted) { - QCALL_CONTRACT; - CLR_BOOL result = FALSE; Thread* pThread = GET_THREAD(); ExInfo* pExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker(); + Frame* pFrame = pThread->GetFrame(); if (pExInfo->m_passNumber == 1) { pThread->ResetThreadStateNC(Thread::TSNC_SkipManagedPersonalityRoutine); } - BEGIN_QCALL; - - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsEHHelperCall(pFrame); - // we already fixed the context in HijackHandler, so let's // just clear the thread state. pThread->ResetThrowControlForThread(); @@ -3834,8 +3857,6 @@ extern "C" CLR_BOOL QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStack _ASSERTE(!result || pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD); - END_QCALL; - if (result) { TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; @@ -3879,6 +3900,23 @@ extern "C" CLR_BOOL QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStack return result; } +extern "C" CLR_BOOL QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted) +{ + QCALL_CONTRACT; + + CLR_BOOL result = FALSE; + BEGIN_QCALL; + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + result = SfiInitWorker(pThis, pStackwalkCtx, instructionFault, pfIsExceptionIntercepted); + END_QCALL; + + return result; +} + static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrameIterator) { StackWalkAction retVal; @@ -3896,19 +3934,14 @@ static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrame return retVal; } -extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR_BOOL* fUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted) +CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR_BOOL* fUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted) { - QCALL_CONTRACT; - StackWalkAction retVal = SWA_FAILED; CLR_BOOL isPropagatingToNativeCode = FALSE; Thread* pThread = GET_THREAD(); ExInfo* pTopExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker(); - BEGIN_QCALL; - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsEHHelperCall(pFrame); // we already fixed the context in HijackHandler, so let's // just clear the thread state. @@ -4144,7 +4177,6 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid } Exit:; - END_QCALL; if (retVal != SWA_FAILED) { @@ -4174,6 +4206,179 @@ Exit:; return FALSE; } +extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR_BOOL* fUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted) +{ + QCALL_CONTRACT; + + CLR_BOOL result = FALSE; + + BEGIN_QCALL; + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + result = SfiNextWorker(pThis, uExCollideClauseIdx, fUnwoundReversePInvoke, pfIsExceptionIntercepted); + + END_QCALL; + + return result; +} + +const uint32_t MaxTryRegionIdx = 0xFFFFFFFFu; + +static uint32_t CalculateCodeOffset(PCODE pbControlPC, IJitManager::MethodRegionInfo *pMethodRegionInfo) +{ + uint codeOffset = (uint)(pbControlPC - pMethodRegionInfo->hotStartAddress); + // If the PC is in the cold region, adjust the offset to be relative to the start of the method. + if ((pMethodRegionInfo->coldSize != 0) && (codeOffset >= pMethodRegionInfo->hotSize)) + { + codeOffset = (uint32_t)(pMethodRegionInfo->hotSize + (size_t)(pbControlPC - pMethodRegionInfo->coldStartAddress)); + } + + return codeOffset; +} + +static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) +{ + ExtendedEHClauseEnumerator ehEnum; + IJitManager::MethodRegionInfo methodRegionInfo; + + if (!EHEnumInitFromStackFrameIterator(&pExInfo->m_frameIter, &methodRegionInfo, &ehEnum)) + return; + + PCODE pbControlPC = GetControlPC(pExInfo->m_frameIter.m_crawl.GetRegisterSet()); + + uint codeOffset = CalculateCodeOffset(pbControlPC, &methodRegionInfo); + + uint lastTryStart = 0, lastTryEnd = 0; + + // Search the clauses for one that contains the current offset. + RhEHClause ehClause; + for (uint curIdx = 0; EHEnumNextWorker(&ehEnum, &ehClause, /* onlyFinallys */ true) && curIdx < idxLimit; curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd) + && (ehClause._isSameTry) + ) + continue; + + // We are done skipping. This is required to handle empty finally block markers that are used + // to separate runs of different try blocks with same native code offsets. + idxStart = MaxTryRegionIdx; + } + + RhEHClauseKind clauseKind = ehClause._clauseKind; + + if ((clauseKind != RH_EH_CLAUSE_FAULT) || !ehClause.ContainsCodeOffset(codeOffset)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + + // N.B. -- We need to suppress GC "in-between" calls to finallys in this loop because we do + // not have the correct next-execution point live on the stack and, therefore, may cause a GC + // hole if we allow a GC between invocation of finally funclets (i.e. after one has returned + // here to the dispatcher, but before the next one is invoked). Once they are running, it's + // fine for them to trigger a GC, obviously. + // + // As a result, RhpCallFinallyFunclet will set this state in the runtime upon return from the + // funclet, and we need to reset it if/when we fall out of the loop and we know that the + // method will no longer get any more GC callbacks. + + byte* pFinallyHandler = ehClause._handlerAddress; + pExInfo->m_idxCurClause = curIdx; + CallFinallyFunclet(pFinallyHandler, pExInfo->m_frameIter.m_crawl.GetRegisterSet(), pExInfo); + pExInfo->m_idxCurClause = MaxTryRegionIdx; + } +} + +static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) +{ + InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); +} + +void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler) +{ + // ------------------------------------------------ + // + // Second pass + // + // ------------------------------------------------ + + // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we + // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to + // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the + // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass + // funclets will always toggle this mode off before invoking them. + StackFrameIterator *pFrameIter = &pExInfo->m_frameIter; + pExInfo->m_passNumber = 2; + OBJECTREF exceptionObj = pExInfo->m_exception; + uint startIdx = MaxTryRegionIdx; + uint catchingTryRegionIdx = pExInfo->m_idxCurClause; + CLR_BOOL unwoundReversePInvoke = false; + CLR_BOOL isExceptionIntercepted = false; + CLR_BOOL isValid = SfiInitWorker(pFrameIter, pExInfo->m_pExContext, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::InstructionFaultFlag) != 0, &isExceptionIntercepted); + for (; isValid && (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) <= handlingFrameSP); isValid = SfiNextWorker(pFrameIter, &startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) + { + _ASSERTE_MSG(isValid, "second-pass EH unwind failed unexpectedly"); + //DebugScanCallFrame(pExInfo->_passNumber, frameIter.ControlPC, frameIter.SP); + + if (isExceptionIntercepted) + { + pCatchHandler = NULL; + break; + } + + if (unwoundReversePInvoke) + { + _ASSERTE_MSG(GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) == handlingFrameSP, "Encountered a different reverse P/Invoke frame in the second pass."); + // Found the native frame that called the reverse P/invoke. + // It is not possible to run managed second pass handlers on a native frame. + break; + } + + if ((GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) == handlingFrameSP) +#if TARGET_ARM64 + && (GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) == prevControlPC) +#endif + ) + { + // invoke only a partial second-pass here... + InvokeSecondPass(pExInfo, startIdx, catchingTryRegionIdx); + break; + } + + InvokeSecondPass(pExInfo, startIdx); + } + + // ------------------------------------------------ + // + // Call the handler and resume execution + // + // ------------------------------------------------ + pExInfo->m_idxCurClause = catchingTryRegionIdx; + + CallCatchFunclet(exceptionObj, (BYTE *)pCatchHandler, pFrameIter->m_crawl.GetRegisterSet(), pExInfo); + // currently, RhpCallCatchFunclet will resume after the catch + UNREACHABLE(); +} + namespace AsmOffsetsAsserts { // Verify that the offsets into CONTEXT, REGDISPLAY, ExInfo and StackFrameIterator that the new managed exception handling diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 0199b7d26e692a..6866249c9c7474 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -34,6 +34,8 @@ VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); +void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler); + enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; enum TrackerMemoryType diff --git a/src/coreclr/vm/exceptionhandlingqcalls.h b/src/coreclr/vm/exceptionhandlingqcalls.h index fd1feccef6cb91..e6a0429db9d996 100644 --- a/src/coreclr/vm/exceptionhandlingqcalls.h +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -12,8 +12,6 @@ struct ExInfo; #ifndef DACCESS_COMPILE -extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); -extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); extern "C" CLR_BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterP, REGDISPLAY* pvRegDisplay); extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay); extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo); diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index be1b9a923991dc..b42e414a3837d3 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -182,6 +182,12 @@ struct RhEHClause BYTE *_handlerAddress; void *_pTargetType; BOOL _isSameTry; + + bool ContainsCodeOffset(unsigned codeOffset) + { + LIMITED_METHOD_CONTRACT; + return (codeOffset >= _tryStartOffset) && (codeOffset < _tryEndOffset); + } }; enum class ExKind : uint8_t diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 4d6e8666749c2b..2e0b6c92854dbb 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -184,8 +184,8 @@ DEFINE_METASIG(SM(Obj_IntPtr_RetVoid, j I, v)) DEFINE_METASIG(SM(Obj_IntPtr_RetBool, j I, F)) DEFINE_METASIG(SM(Obj_IntPtr_IntPtr_Int_RetIntPtr, j I I i, I)) DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j)) -DEFINE_METASIG_T(SM(Obj_RefExInfo_RetVoid, j r(g(EXINFO)), v)) -DEFINE_METASIG_T(SM(UInt_RefExInfo_RetVoid, K r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, j r(g(EXINFO)) r(U) r(P(b)), v)) +DEFINE_METASIG_T(SM(UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, K r(g(EXINFO)) r(U) r(P(b)), v)) DEFINE_METASIG_T(SM(RefExInfo_UIntPtr_RetVoid, r(g(EXINFO)) U, v)) DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RetVoid, r(g(EXINFO)) r(g(EXINFO)), v)) #ifdef FEATURE_COMINTEROP diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index b4f9dbb0e0ad16..84475a8d960f9b 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -529,10 +529,8 @@ static const Entry s_QCall[] = #ifdef FEATURE_EH_FUNCLETS DllImportEntry(SfiInit) DllImportEntry(SfiNext) - DllImportEntry(CallCatchFunclet) DllImportEntry(ResumeAtInterceptionLocation) DllImportEntry(CallFilterFunclet) - DllImportEntry(CallFinallyFunclet) DllImportEntry(EHEnumInitFromStackFrameIterator) DllImportEntry(EHEnumNext) DllImportEntry(AppendExceptionStackFrame) From 9a066bf1ddf7a37c81dc2b5f7c707dcc931bc0b6 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 01:47:22 +0200 Subject: [PATCH 02/15] Several fixes * Reflect PR feedback * Implement rethrow * Implement new way of collided unwind detection now that the CallCatchFunclet is not called via pinvoke * Remove forced reporting of EH code from stack frame iterator, as we now cannot have that code on the stack during 2nd pass --- .../src/System/Runtime/ExceptionHandling.cs | 62 +++++-- src/coreclr/vm/corelib.h | 2 +- src/coreclr/vm/excep.cpp | 2 +- src/coreclr/vm/exceptionhandling.cpp | 167 +++++++----------- src/coreclr/vm/exceptionhandling.h | 2 +- src/coreclr/vm/metasig.h | 2 +- src/coreclr/vm/stackwalk.cpp | 40 +---- src/coreclr/vm/stackwalk.h | 19 -- 8 files changed, 114 insertions(+), 182 deletions(-) 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 3f4f7e4374c2bd..de1764490062be 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -558,7 +558,7 @@ public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UI } [StackTraceHidden] - public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point @@ -642,7 +642,7 @@ public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UI public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) { FindHwExHandler(exceptionCode, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } @@ -651,11 +651,19 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) [StackTraceHidden] public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) { + // Transform attempted throws of null to a throw of NullReferenceException. + if (exceptionObj == null) + { + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); + } + + exInfo.Init(exceptionObj); FindExHandler(exceptionObj, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); } [StackTraceHidden] - public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT @@ -670,15 +678,7 @@ public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIn InternalCalls.RhpValidateExInfoStack(); #endif // NATIVEAOT - // Transform attempted throws of null to a throw of NullReferenceException. - if (exceptionObj == null) - { - IntPtr faultingCodeAddress = exInfo._pExContext->IP; - exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); - } - - exInfo.Init(exceptionObj); - DispatchExPass1(ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + DispatchExFirstPass(ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); } #if NATIVEAOT @@ -687,8 +687,9 @@ public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIn [StackTraceHidden] public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) { + exInfo.Init(exceptionObj); FindExHandler(exceptionObj, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } @@ -760,6 +761,33 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) { #if NATIVEAOT +#if TARGET_WINDOWS + // Alert the debugger that we threw an exception. + InternalCalls.RhpFirstChanceExceptionNotification(); +#endif // TARGET_WINDOWS + + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + GCStress.TriggerGC(); + + InternalCalls.RhpValidateExInfoStack(); +#endif // NATIVEAOT + + FindRethrowExHandler(ref activeExInfo, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + FallbackFailFast(RhFailFastReason.InternalError, null); + } + + [StackTraceHidden] + public static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + { + FindRethrowExHandler(ref activeExInfo, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + } + + [StackTraceHidden] + private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + { +#if NATIVEAOT + #if TARGET_WINDOWS // Alert the debugger that we threw an exception. InternalCalls.RhpFirstChanceExceptionNotification(); @@ -776,13 +804,11 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) object rethrownException = activeExInfo.ThrownException; exInfo.Init(rethrownException, ref activeExInfo); - FindExHandler(rethrownException, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExPass2(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); - FallbackFailFast(RhFailFastReason.InternalError, null); + FindExHandler(rethrownException, ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); } [StackTraceHidden] - private static void DispatchExPass1(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; @@ -901,7 +927,7 @@ private static void DispatchExPass1(ref ExInfo exInfo, out UIntPtr handlingFrame } [StackTraceHidden] - private static void DispatchExPass2(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) + private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) { // ------------------------------------------------ diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index cf31e8e649e475..895e5ae571ac72 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1346,7 +1346,7 @@ DEFINE_FIELD_U(dictionaryIndexAndSlot, GenericHandleArgs, dictionaryIndexAndSlot DEFINE_CLASS(EH, Runtime, EH) DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_RetVoid) +DEFINE_METHOD(EH, FIND_RETHROW_EX_HANDLER, FindRethrowExHandler, SM_RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, UNWIND_AND_INTERCEPT, RhUnwindAndIntercept, SM_RefExInfo_UIntPtr_RetVoid) DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls) DEFINE_CLASS(STACKFRAMEITERATOR, Runtime, StackFrameIterator) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 03ba0ae111b4c3..07e35d75d1072c 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6187,7 +6187,7 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) - DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); GCPROTECT_END(); UNREACHABLE(); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index f79a61a6c84f22..db8eef133fc46a 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1518,7 +1518,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) - DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); GCPROTECT_END(); @@ -1666,7 +1666,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE CRITICAL_CALLSITE; CALL_MANAGED_METHOD_NORET(args) - DispatchExPass2(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); GCPROTECT_END(); GCPROTECT_END(); @@ -1711,17 +1711,24 @@ VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionConte ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pExceptionContext, ExKind::None); + TADDR handlingFrameSP; + PCODE pCatchHandler; + GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); - DECLARE_ARGHOLDER_ARRAY(args, 2); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_RETHROW_EX_HANDLER); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) + //Ex.FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) CALL_MANAGED_METHOD_NORET(args) + DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); + GCPROTECT_END(); UNREACHABLE(); @@ -3140,7 +3147,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi CONTRACTL { MODE_COOPERATIVE; - GC_NOTRIGGER; + GC_TRIGGERS; THROWS; } CONTRACTL_END; @@ -3380,12 +3387,9 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { - GCX_COOP(); Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsFuncletCall(pFrame); exInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(exInfo->m_frameIter.m_crawl.GetRegisterSet()); exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); @@ -3478,7 +3482,7 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterato // The onlyFinallys option makes the function return only finally and fault clauses. It is used in // the 2nd path of EH primarily to avoid calls to ResolveEHClause that can trigger GC. -CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, bool onlyFinallys = false) +CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, bool doNotCalculateCatchType = false) { CLR_BOOL result = FALSE; @@ -3517,18 +3521,20 @@ CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, pEHClause->_tryStartOffset, pEHClause->_tryEndOffset, pEHClause->_isSameTry ? ", isSameTry" : "", pEHClause->_handlerAddress)); - if (onlyFinallys && !(flags & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT))) - { - // Skip non-finally clauses - continue; - } - if (flags == COR_ILEXCEPTION_CLAUSE_NONE) { pEHClause->_clauseKind = RH_EH_CLAUSE_TYPED; - pEHClause->_pTargetType = pJitMan->ResolveEHClause(&EHClause, &pFrameIter->m_crawl).AsMethodTable(); - EH_LOG((LL_INFO100, " typed clause, target type=%p (%s)\n", - pEHClause->_pTargetType, ((MethodTable*)pEHClause->_pTargetType)->GetDebugClassName())); + if (!doNotCalculateCatchType) + { + pEHClause->_pTargetType = pJitMan->ResolveEHClause(&EHClause, &pFrameIter->m_crawl).AsMethodTable(); + EH_LOG((LL_INFO100, " typed clause, target type=%p (%s)\n", + pEHClause->_pTargetType, ((MethodTable*)pEHClause->_pTargetType)->GetDebugClassName())); + } + else + { + pEHClause->_pTargetType = NULL; + EH_LOG((LL_INFO100, " typed clause, target type not calculated\n")); + } } else if (flags & COR_ILEXCEPTION_CLAUSE_FILTER) { @@ -4087,74 +4093,53 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR goto Exit; } - if (!pThis->m_crawl.IsFrameless()) + if (doingFuncletUnwind && pThis->GetNextExInfo() != NULL && GetRegdisplaySP(pThis->m_crawl.GetRegisterSet()) > (TADDR)pTopExInfo) { - // Detect collided unwind - pFrame = pThis->m_crawl.GetFrame(); - - if (InlinedCallFrame::FrameHasActiveCall(pFrame)) + // Detected collided unwind + if ((pThis->GetNextExInfo()->m_passNumber == 1) || + (pThis->GetNextExInfo()->m_idxCurClause == 0xFFFFFFFF)) { - InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; - if (((TADDR)pInlinedCallFrame->m_Datum & (TADDR)InlinedCallFrameMarker::Mask) == ((TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper | (TADDR)InlinedCallFrameMarker::SecondPassFuncletCaller)) - { - // passing through CallCatchFunclet et al - if (doingFuncletUnwind) - { - // Unwind the CallCatchFunclet - retVal = MoveToNextNonSkippedFrame(pThis); - - if (retVal == SWA_FAILED) - { - _ASSERTE_MSG(FALSE, "StackFrameIterator::Next failed"); - break; - } - - if ((pThis->GetNextExInfo()->m_passNumber == 1) || - (pThis->GetNextExInfo()->m_idxCurClause == 0xFFFFFFFF)) - { - _ASSERTE_MSG(FALSE, "did not expect to collide with a 1st-pass ExInfo during a EH stackwalk"); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } - else - { - *uExCollideClauseIdx = pExInfo->m_idxCurClause; - isCollided = true; - pExInfo->m_kind = (ExKind)((uint8_t)pExInfo->m_kind | (uint8_t)ExKind::SupersededFlag); - - // Unwind to the frame of the prevExInfo - ExInfo* pPrevExInfo = pThis->GetNextExInfo(); - EH_LOG((LL_INFO100, "SfiNext: collided with previous exception handling, skipping from IP=%p, SP=%p to IP=%p, SP=%p\n", - GetControlPC(&pTopExInfo->m_regDisplay), GetRegdisplaySP(&pTopExInfo->m_regDisplay), - GetControlPC(&pPrevExInfo->m_regDisplay), GetRegdisplaySP(&pPrevExInfo->m_regDisplay))); - - pThis->SkipTo(&pPrevExInfo->m_frameIter); - pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); - _ASSERTE_MSG(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD, "Collided unwind should have reached a frameless method"); - break; - } - } - } + _ASSERTE_MSG(FALSE, "did not expect to collide with a 1st-pass ExInfo during a EH stackwalk"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } else { - if (pTopExInfo->m_passNumber == 1) + *uExCollideClauseIdx = pExInfo->m_idxCurClause; + isCollided = true; + pExInfo->m_kind = (ExKind)((uint8_t)pExInfo->m_kind | (uint8_t)ExKind::SupersededFlag); + + // Unwind to the frame of the prevExInfo + ExInfo* pPrevExInfo = pThis->GetNextExInfo(); + EH_LOG((LL_INFO100, "SfiNext: collided with previous exception handling, skipping from IP=%p, SP=%p to IP=%p, SP=%p\n", + GetControlPC(&pTopExInfo->m_regDisplay), GetRegdisplaySP(&pTopExInfo->m_regDisplay), + GetControlPC(&pPrevExInfo->m_regDisplay), GetRegdisplaySP(&pPrevExInfo->m_regDisplay))); + + pThis->SkipTo(&pPrevExInfo->m_frameIter); + pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); + _ASSERTE_MSG(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD, "Collided unwind should have reached a frameless method"); + break; + } + } + + if (!pThis->m_crawl.IsFrameless()) + { + if (pTopExInfo->m_passNumber == 1) + { + MethodDesc *pMD = pFrame->GetFunction(); + if (pMD != NULL) { - MethodDesc *pMD = pFrame->GetFunction(); - if (pMD != NULL) - { - GCX_COOP(); - StackTraceInfo::AppendElement(pTopExInfo->m_hThrowable, 0, GetRegdisplaySP(pTopExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pTopExInfo->m_frameIter.m_crawl); + GCX_COOP(); + StackTraceInfo::AppendElement(pTopExInfo->m_hThrowable, 0, GetRegdisplaySP(pTopExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pTopExInfo->m_frameIter.m_crawl); #if defined(DEBUGGING_SUPPORTED) - if (NotifyDebuggerOfStub(pThread, pFrame)) + if (NotifyDebuggerOfStub(pThread, pFrame)) + { + if (!pTopExInfo->DeliveredFirstChanceNotification()) { - if (!pTopExInfo->DeliveredFirstChanceNotification()) - { - ExceptionNotifications::DeliverFirstChanceNotification(); - } + ExceptionNotifications::DeliverFirstChanceNotification(); } -#endif // DEBUGGING_SUPPORTED } +#endif // DEBUGGING_SUPPORTED } } @@ -4291,16 +4276,6 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) // Found a containing clause. Because of the order of the clauses, we know this is the // most containing. - // N.B. -- We need to suppress GC "in-between" calls to finallys in this loop because we do - // not have the correct next-execution point live on the stack and, therefore, may cause a GC - // hole if we allow a GC between invocation of finally funclets (i.e. after one has returned - // here to the dispatcher, but before the next one is invoked). Once they are running, it's - // fine for them to trigger a GC, obviously. - // - // As a result, RhpCallFinallyFunclet will set this state in the runtime upon return from the - // funclet, and we need to reset it if/when we fall out of the loop and we know that the - // method will no longer get any more GC callbacks. - byte* pFinallyHandler = ehClause._handlerAddress; pExInfo->m_idxCurClause = curIdx; CallFinallyFunclet(pFinallyHandler, pExInfo->m_frameIter.m_crawl.GetRegisterSet(), pExInfo); @@ -4313,22 +4288,10 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); } -void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler) +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler) { - // ------------------------------------------------ - // - // Second pass - // - // ------------------------------------------------ - - // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we - // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to - // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the - // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass - // funclets will always toggle this mode off before invoking them. StackFrameIterator *pFrameIter = &pExInfo->m_frameIter; pExInfo->m_passNumber = 2; - OBJECTREF exceptionObj = pExInfo->m_exception; uint startIdx = MaxTryRegionIdx; uint catchingTryRegionIdx = pExInfo->m_idxCurClause; CLR_BOOL unwoundReversePInvoke = false; @@ -4337,7 +4300,7 @@ void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, P for (; isValid && (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) <= handlingFrameSP); isValid = SfiNextWorker(pFrameIter, &startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { _ASSERTE_MSG(isValid, "second-pass EH unwind failed unexpectedly"); - //DebugScanCallFrame(pExInfo->_passNumber, frameIter.ControlPC, frameIter.SP); + _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != NULL, "IP address must not be null"); if (isExceptionIntercepted) { @@ -4374,8 +4337,8 @@ void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, P // ------------------------------------------------ pExInfo->m_idxCurClause = catchingTryRegionIdx; - CallCatchFunclet(exceptionObj, (BYTE *)pCatchHandler, pFrameIter->m_crawl.GetRegisterSet(), pExInfo); - // currently, RhpCallCatchFunclet will resume after the catch + CallCatchFunclet(pExInfo->m_exception, (BYTE *)pCatchHandler, pFrameIter->m_crawl.GetRegisterSet(), pExInfo); + // CallCatchFunclet will resume after the catch and never return here. UNREACHABLE(); } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 6866249c9c7474..c8bf0d137fb378 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -34,7 +34,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); -void DECLSPEC_NORETURN DispatchExPass2(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler); +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler); enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 2e0b6c92854dbb..38302022126d17 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -187,7 +187,7 @@ DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j)) DEFINE_METASIG_T(SM(Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, j r(g(EXINFO)) r(U) r(P(b)), v)) DEFINE_METASIG_T(SM(UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, K r(g(EXINFO)) r(U) r(P(b)), v)) DEFINE_METASIG_T(SM(RefExInfo_UIntPtr_RetVoid, r(g(EXINFO)) U, v)) -DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RetVoid, r(g(EXINFO)) r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, r(g(EXINFO)) r(g(EXINFO)) r(U) r(P(b)), v)) #ifdef FEATURE_COMINTEROP DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I)) DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RetIntPtr, j I r(I), I)) diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index dd2e03badb3007..bbd2a903e47dc4 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1049,7 +1049,6 @@ void StackFrameIterator::CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 m_fDidFuncletReportGCReferences = true; m_isRuntimeWrappedExceptions = false; #endif // FEATURE_EH_FUNCLETS - m_forceReportingWhileSkipping = ForceGCReportingStage::Off; m_movedPastFirstExInfo = false; m_fFuncletNotSeen = false; m_fFoundFirstFunclet = false; @@ -1770,14 +1769,6 @@ StackWalkAction StackFrameIterator::Filter(void) // can use it. m_sfParent = m_sfIntermediaryFuncletParent; fSkipFuncletCallback = false; - - if (!ExecutionManager::IsManagedCode(GetIP(m_crawl.GetRegisterSet()->pCallerContext))) - { - // Initiate force reporting of references in the new managed exception handling code frames. - // These frames are still alive when we are in a finally funclet. - m_forceReportingWhileSkipping = ForceGCReportingStage::LookForManagedFrame; - STRESS_LOG0(LF_GCROOTS, LL_INFO100, "STACKWALK: Setting m_forceReportingWhileSkipping = ForceGCReportingStage::LookForManagedFrame while processing filter funclet\n"); - } } } } @@ -1829,14 +1820,6 @@ StackWalkAction StackFrameIterator::Filter(void) m_fFoundFirstFunclet = true; } - if (!fFrameWasUnwound && !ExecutionManager::IsManagedCode(GetIP(m_crawl.GetRegisterSet()->pCallerContext))) - { - // Initiate force reporting of references in the new managed exception handling code frames. - // These frames are still alive when we are in a finally funclet. - m_forceReportingWhileSkipping = ForceGCReportingStage::LookForManagedFrame; - STRESS_LOG0(LF_GCROOTS, LL_INFO100, "STACKWALK: Setting m_forceReportingWhileSkipping = ForceGCReportingStage::LookForManagedFrame\n"); - } - // For non-filter funclets, we will make the callback for the funclet // but skip all the frames until we reach the parent method. When we do, // we will make a callback for it as well and then continue to make callbacks @@ -2104,7 +2087,7 @@ StackWalkAction StackFrameIterator::Filter(void) } else if (fSkipFuncletCallback && (m_flags & GC_FUNCLET_REFERENCE_REPORTING)) { - if (!m_sfParent.IsNull() && (m_forceReportingWhileSkipping == ForceGCReportingStage::Off)) + if (!m_sfParent.IsNull()) { STRESS_LOG4(LF_GCROOTS, LL_INFO100, "STACKWALK: %s: not making callback for this frame, SPOfParent = %p, \ @@ -2117,22 +2100,6 @@ StackWalkAction StackFrameIterator::Filter(void) // don't stop here break; } - - if (m_forceReportingWhileSkipping == ForceGCReportingStage::LookForManagedFrame) - { - // State indicating that the next marker frame should turn off the reporting again. That would be the caller of the managed RhThrowEx - m_forceReportingWhileSkipping = ForceGCReportingStage::LookForMarkerFrame; - STRESS_LOG0(LF_GCROOTS, LL_INFO100, "STACKWALK: Setting m_forceReportingWhileSkipping = ForceGCReportingStage::LookForMarkerFrame\n"); - } - -#ifdef _DEBUG - if (m_forceReportingWhileSkipping != ForceGCReportingStage::Off) - { - STRESS_LOG3(LF_GCROOTS, LL_INFO100, - "STACKWALK: Force callback for skipped function m_crawl.pFunc = %pM (%s.%s)\n", m_crawl.pFunc, m_crawl.pFunc->m_pszDebugClassName, m_crawl.pFunc->m_pszDebugMethodName); - _ASSERTE((m_crawl.pFunc->GetMethodTable() == g_pEHClass) || (strcmp(m_crawl.pFunc->m_pszDebugClassName, "ILStubClass") == 0) || (strcmp(m_crawl.pFunc->m_pszDebugMethodName, "CallFinallyFunclet") == 0) || (m_crawl.pFunc->GetMethodTable() == g_pExceptionServicesInternalCallsClass)); - } -#endif } } } @@ -2226,11 +2193,6 @@ StackWalkAction StackFrameIterator::Filter(void) fStop = true; } } - if (m_forceReportingWhileSkipping == ForceGCReportingStage::LookForMarkerFrame) - { - m_forceReportingWhileSkipping = ForceGCReportingStage::Off; - STRESS_LOG0(LF_GCROOTS, LL_INFO100, "STACKWALK: Setting m_forceReportingWhileSkipping = ForceGCReportingStage::Off\n"); - } break; case SFITER_INITIAL_NATIVE_CONTEXT: diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index 6b650a277137e3..cf4f7ae4f30199 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -651,23 +651,6 @@ class StackFrameIterator private: - // For the new exception handling that uses managed code to dispatch the - // exceptions, we need to force the stack walker to report GC references - // in the exception handling code frames, since they are alive. This is - // different from the old exception handling where no frames below the - // funclets upto the parent frame are alive. - enum class ForceGCReportingStage : BYTE - { - Off = 0, - // The stack walker has hit a funclet, we are looking for the first managed - // frame that would be one of the managed exception handling code frames - LookForManagedFrame = 1, - // The stack walker has already hit a managed exception handling code frame, - // we are looking for a marker frame which indicates the native caller of - // the managed exception handling code - LookForMarkerFrame = 2 - }; - // This is a helper for the two constructors. void CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 flags); @@ -760,8 +743,6 @@ class StackFrameIterator bool m_fDidFuncletReportGCReferences; bool m_isRuntimeWrappedExceptions; #endif // FEATURE_EH_FUNCLETS - // State of forcing of GC reference reporting for managed exception handling methods (RhExThrow, RhDispatchEx etc) - ForceGCReportingStage m_forceReportingWhileSkipping; // The stack walk has moved past the first ExInfo location on the stack bool m_movedPastFirstExInfo; // Indicates that no funclet was seen during the current stack walk yet From cf5d23c0e72b2d2667ebee76a57619d6491901d7 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 13:58:25 +0200 Subject: [PATCH 03/15] Fix arm64 build and remove now useless stuff from stackwalk --- .../ExceptionServices/InternalCalls.cs | 7 -- .../src/System/Runtime/ExceptionHandling.cs | 82 ++++++++----------- src/coreclr/vm/corelib.h | 6 +- src/coreclr/vm/excep.cpp | 10 ++- src/coreclr/vm/exceptionhandling.cpp | 36 ++++---- src/coreclr/vm/exceptionhandling.h | 2 +- src/coreclr/vm/exinfo.cpp | 3 +- src/coreclr/vm/exinfo.h | 2 - src/coreclr/vm/gcenv.ee.common.cpp | 48 +---------- src/coreclr/vm/metasig.h | 6 +- src/coreclr/vm/stackwalk.cpp | 53 ------------ src/coreclr/vm/stackwalk.h | 18 ---- 12 files changed, 71 insertions(+), 202 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs index 608d52d768911e..32eb1b3d592abc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs @@ -23,13 +23,6 @@ internal static partial class InternalCalls [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ResumeAtInterceptionLocation")] internal static unsafe partial void ResumeAtInterceptionLocation(void* pvRegDisplay); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallCatchFunclet")] - internal static unsafe partial IntPtr RhpCallCatchFunclet( - ObjectHandleOnStack exceptionObj, byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFinallyFunclet")] - internal static unsafe partial void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFilterFunclet")] [return: MarshalAs(UnmanagedType.U1)] internal static unsafe partial bool RhpCallFilterFunclet( 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 de1764490062be..1a375b006573da 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -552,13 +552,13 @@ internal object ThrownException } [StackTraceHidden] - public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) { - FindHwExHandler(exceptionCode, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + FindHwExHandler(exceptionCode, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); } [StackTraceHidden] - private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point @@ -629,7 +629,7 @@ private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out U } exInfo.Init(exceptionToThrow!, instructionFault); - FindExHandler(exceptionToThrow!, ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + FindExHandler(exceptionToThrow!, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); } // @@ -637,19 +637,19 @@ private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out U // #if NATIVEAOT [RuntimeExport("RhThrowHwEx")] -#endif [StackTraceHidden] public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) { - FindHwExHandler(exceptionCode, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + FindHwExHandler(exceptionCode, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } +#endif private const uint MaxTryRegionIdx = 0xFFFFFFFFu; [StackTraceHidden] - public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) { // Transform attempted throws of null to a throw of NullReferenceException. if (exceptionObj == null) @@ -659,11 +659,11 @@ public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIn } exInfo.Init(exceptionObj); - FindExHandler(exceptionObj, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + FindExHandler(exceptionObj, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); } [StackTraceHidden] - private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT @@ -678,20 +678,20 @@ private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UI InternalCalls.RhpValidateExInfoStack(); #endif // NATIVEAOT - DispatchExFirstPass(ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + DispatchExFirstPass(ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); } #if NATIVEAOT [RuntimeExport("RhThrowEx")] -#endif [StackTraceHidden] public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) { exInfo.Init(exceptionObj); - FindExHandler(exceptionObj, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + FindExHandler(exceptionObj, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } +#endif #if !NATIVEAOT // TODO: move this to native too @@ -720,7 +720,7 @@ public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStac break; } - InvokeSecondPass(ref exInfo, startIdx); +// InvokeSecondPass(ref exInfo, startIdx); if (isExceptionIntercepted) { Debug.Assert(false); @@ -735,12 +735,12 @@ public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStac // ------------------------------------------------ if (unwoundReversePInvoke) { - object exceptionObj = exInfo.ThrownException; - fixed (EH.ExInfo* pExInfo = &exInfo) - { - InternalCalls.RhpCallCatchFunclet( - ObjectHandleOnStack.Create(ref exceptionObj), null, exInfo._frameIter.RegisterSet, pExInfo); - } + // object exceptionObj = exInfo.ThrownException; + // fixed (EH.ExInfo* pExInfo = &exInfo) + // { + // InternalCalls.RhpCallCatchFunclet( + // ObjectHandleOnStack.Create(ref exceptionObj), null, exInfo._frameIter.RegisterSet, pExInfo); + // } } else { @@ -755,12 +755,9 @@ public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStac #if NATIVEAOT [RuntimeExport("RhRethrow")] -#endif [StackTraceHidden] public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) { -#if NATIVEAOT - #if TARGET_WINDOWS // Alert the debugger that we threw an exception. InternalCalls.RhpFirstChanceExceptionNotification(); @@ -770,21 +767,21 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) GCStress.TriggerGC(); InternalCalls.RhpValidateExInfoStack(); -#endif // NATIVEAOT - FindRethrowExHandler(ref activeExInfo, ref exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + FindRethrowExHandler(ref activeExInfo, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); + DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); } +#endif // NATIVEAOT [StackTraceHidden] - public static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + public static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) { - FindRethrowExHandler(ref activeExInfo, ref exInfo, out handlingFrameSP, out pCatchHandler, out _, out _); + FindRethrowExHandler(ref activeExInfo, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); } [StackTraceHidden] - private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { #if NATIVEAOT @@ -804,11 +801,11 @@ private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exI object rethrownException = activeExInfo.ThrownException; exInfo.Init(rethrownException, ref activeExInfo); - FindExHandler(rethrownException, ref exInfo, out handlingFrameSP, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + FindExHandler(rethrownException, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); } [StackTraceHidden] - private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; @@ -924,10 +921,12 @@ private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingF Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke || isExceptionIntercepted, "We should have a handler if we're starting the second pass"); Debug.Assert(!isExceptionIntercepted || (pCatchHandler == null), "No catch handler should be returned for intercepted exceptions in the first pass"); exInfo._idxCurClause = catchingTryRegionIdx; + handlingFramePC = prevControlPC; } +#if NATIVEAOT [StackTraceHidden] - private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) + private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* prevControlPC, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) { // ------------------------------------------------ @@ -941,9 +940,7 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass // funclets will always toggle this mode off before invoking them. -#if NATIVEAOT InternalCalls.RhpSetThreadDoNotTriggerGC(); -#endif scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; exInfo._passNumber = 2; object exceptionObj = exInfo.ThrownException; @@ -965,10 +962,8 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram if (unwoundReversePInvoke) { -#if NATIVEAOT Debug.Assert(pReversePInvokePropagationCallback != IntPtr.Zero, "Unwound to a reverse P/Invoke in the second pass. We should have a propagation handler."); Debug.Assert(frameIter.PreviousTransitionFrame != IntPtr.Zero, "Should have a transition frame for reverse P/Invoke."); -#endif Debug.Assert(frameIter.SP == handlingFrameSP, "Encountered a different reverse P/Invoke frame in the second pass."); // Found the native frame that called the reverse P/invoke. // It is not possible to run managed second pass handlers on a native frame. @@ -992,11 +987,9 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram #if FEATURE_OBJCMARSHAL if (pReversePInvokePropagationCallback != IntPtr.Zero) { -#if NATIVEAOT InternalCalls.RhpCallPropagateExceptionCallback( pReversePInvokePropagationContext, pReversePInvokePropagationCallback, frameIter.RegisterSet, ref exInfo, frameIter.PreviousTransitionFrame); // the helper should jump to propagation handler and not return -#endif Debug.Fail("unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); } @@ -1009,20 +1002,13 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram // // ------------------------------------------------ exInfo._idxCurClause = catchingTryRegionIdx; -#if NATIVEAOT InternalCalls.RhpCallCatchFunclet( exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); -#else // NATIVEAOT - fixed (EH.ExInfo* pExInfo = &exInfo) - { - InternalCalls.RhpCallCatchFunclet( - ObjectHandleOnStack.Create(ref exceptionObj), pCatchHandler, frameIter.RegisterSet, pExInfo); - } -#endif // NATIVEAOT // currently, RhpCallCatchFunclet will resume after the catch Debug.Fail("unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); } +#endif // NATIVEAOT [System.Diagnostics.Conditional("DEBUG")] private static void DebugScanCallFrame(int passNumber, byte* ip, UIntPtr sp) @@ -1227,6 +1213,7 @@ private static bool ShouldTypedClauseCatchThisException(object exception, Method #endif } +#if NATIVEAOT private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart) { InvokeSecondPass(ref exInfo, idxStart, MaxTryRegionIdx); @@ -1311,7 +1298,6 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL } } -#if NATIVEAOT #pragma warning disable IDE0060 [UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp")] public static void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord) diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 895e5ae571ac72..55b29969c91cad 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1344,9 +1344,9 @@ DEFINE_FIELD_U(dictionaryIndexAndSlot, GenericHandleArgs, dictionaryIndexAndSlot #ifdef FEATURE_EH_FUNCLETS DEFINE_CLASS(EH, Runtime, EH) -DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, FIND_RETHROW_EX_HANDLER, FindRethrowExHandler, SM_RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid) +DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) +DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) +DEFINE_METHOD(EH, FIND_RETHROW_EX_HANDLER, FindRethrowExHandler, SM_RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, UNWIND_AND_INTERCEPT, RhUnwindAndIntercept, SM_RefExInfo_UIntPtr_RetVoid) DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls) DEFINE_CLASS(STACKFRAMEITERATOR, Runtime, StackFrameIterator) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 07e35d75d1072c..816eeb5768f26d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6172,22 +6172,24 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) } TADDR handlingFrameSP; + PCODE handlingFramePC; PCODE pCatchHandler; GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); + args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) + //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); GCPROTECT_END(); UNREACHABLE(); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index db8eef133fc46a..f074a036e92f57 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1503,22 +1503,24 @@ BOOL HandleHardwareException(PAL_SEHException* ex) } TADDR handlingFrameSP; + PCODE handlingFramePC; PCODE pCatchHandler; GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); + args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &pCatchHandler) + //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); GCPROTECT_END(); @@ -1651,22 +1653,24 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE GCPROTECT_BEGIN(exInfo.m_exception); TADDR handlingFrameSP; + PCODE handlingFramePC; PCODE pCatchHandler; PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(throwable); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); + args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindExHandler(throwable, &exInfo, &handlingFrameSP, &pCatchHandler) + //Ex.FindExHandler(throwable, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) CRITICAL_CALLSITE; CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); GCPROTECT_END(); GCPROTECT_END(); @@ -1712,22 +1716,24 @@ VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionConte ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pExceptionContext, ExKind::None); TADDR handlingFrameSP; + PCODE handlingFramePC; PCODE pCatchHandler; GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_RETHROW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&pCatchHandler); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); + args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* pCatchHandler) + //Ex.FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out UIntPtr handlingFramePC, out byte* pCatchHandler) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, pCatchHandler); + DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); GCPROTECT_END(); @@ -4288,8 +4294,10 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); } -void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler) +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE handlingFramePC, PCODE pCatchHandler) { + //GCHeapUtilities::GetGCHeap()->GarbageCollect(2, FALSE, collection_aggressive); + StackFrameIterator *pFrameIter = &pExInfo->m_frameIter; pExInfo->m_passNumber = 2; uint startIdx = MaxTryRegionIdx; @@ -4318,7 +4326,7 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrame if ((GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) == handlingFrameSP) #if TARGET_ARM64 - && (GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) == prevControlPC) + && (GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) == handlingFramePC) #endif ) { diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index c8bf0d137fb378..b202e609a09d38 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -34,7 +34,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); -void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE pCatchHandler); +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE handlingFramePC, PCODE pCatchHandler); enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 41c90561aa5b06..61fe853a95ab7a 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -325,9 +325,8 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx #endif // HOST_UNIX m_CurrentClause({}), m_pMDToReportFunctionLeave(NULL), - m_lastReportedFunclet({0, 0, 0}) #ifdef HOST_WINDOWS - , m_pLongJmpBuf(NULL), + m_pLongJmpBuf(NULL), m_longJmpReturnValue(0) #endif // HOST_WINDOWS { diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index b42e414a3837d3..bad198e5421c28 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -340,8 +340,6 @@ struct ExInfo REGDISPLAY m_regDisplay; // Initial explicit frame for stack walking Frame *m_pInitialFrame; - // Info on the last reported funclet used to report references in the parent frame - LastReportedFuncletInfo m_lastReportedFunclet; #ifdef TARGET_WINDOWS // Longjmp buffer used to restart longjmp after a block of managed frames when diff --git a/src/coreclr/vm/gcenv.ee.common.cpp b/src/coreclr/vm/gcenv.ee.common.cpp index e4e4ac4aae092d..c1042ad639a7ce 100644 --- a/src/coreclr/vm/gcenv.ee.common.cpp +++ b/src/coreclr/vm/gcenv.ee.common.cpp @@ -259,54 +259,8 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData) // We may have unwound this crawlFrame and thus, shouldn't report the invalid // references it may contain. fReportGCReferences = pCF->ShouldCrawlframeReportGCReferences(); - - Thread *pThread = pCF->GetThread(); - ExInfo *pExInfo = (ExInfo *)pThread->GetExceptionState()->GetCurrentExceptionTracker(); - - if (pCF->ShouldSaveFuncletInfo()) - { - STRESS_LOG3(LF_GCROOTS, LL_INFO1000, "Saving info on funclet at SP: %p, PC: %p, FP: %p\n", - GetRegdisplaySP(pCF->GetRegisterSet()), GetControlPC(pCF->GetRegisterSet()), GetFP(pCF->GetRegisterSet()->pCurrentContext)); - - _ASSERTE(pExInfo); - REGDISPLAY *pRD = pCF->GetRegisterSet(); - pExInfo->m_lastReportedFunclet.IP = GetControlPC(pRD); - pExInfo->m_lastReportedFunclet.FP = GetFP(pRD->pCurrentContext); - pExInfo->m_lastReportedFunclet.Flags = pCF->GetCodeManagerFlags(); - } - - if (pCF->ShouldParentToFuncletReportSavedFuncletSlots()) - { - STRESS_LOG4(LF_GCROOTS, LL_INFO1000, "Reporting slots in funclet parent frame method at SP: %p, PC: %p using original FP: %p, PC: %p\n", - GetRegdisplaySP(pCF->GetRegisterSet()), GetControlPC(pCF->GetRegisterSet()), pExInfo->m_lastReportedFunclet.FP, pExInfo->m_lastReportedFunclet.IP); - - _ASSERTE(!pCF->ShouldParentToFuncletUseUnwindTargetLocationForGCReporting()); - _ASSERTE(pExInfo); - - ICodeManager * pCM = pCF->GetCodeManager(); - _ASSERTE(pCM != NULL); - - CONTEXT context = {}; - REGDISPLAY partialRD; - SetIP(&context, pExInfo->m_lastReportedFunclet.IP); - SetFP(&context, pExInfo->m_lastReportedFunclet.FP); - SetSP(&context, 0); - - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - FillRegDisplay(&partialRD, &context); - - EECodeInfo codeInfo(pExInfo->m_lastReportedFunclet.IP); - _ASSERTE(codeInfo.IsValid()); - - pCM->EnumGcRefs(&partialRD, - &codeInfo, - pExInfo->m_lastReportedFunclet.Flags | ReportFPBasedSlotsOnly, - GcEnumObject, - pData, - NO_OVERRIDE_OFFSET); - } - else #endif // defined(FEATURE_EH_FUNCLETS) + if (fReportGCReferences) { if (pCF->IsFrameless()) diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 38302022126d17..e1f8cf4844e7e1 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -184,10 +184,10 @@ DEFINE_METASIG(SM(Obj_IntPtr_RetVoid, j I, v)) DEFINE_METASIG(SM(Obj_IntPtr_RetBool, j I, F)) DEFINE_METASIG(SM(Obj_IntPtr_IntPtr_Int_RetIntPtr, j I I i, I)) DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j)) -DEFINE_METASIG_T(SM(Obj_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, j r(g(EXINFO)) r(U) r(P(b)), v)) -DEFINE_METASIG_T(SM(UInt_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, K r(g(EXINFO)) r(U) r(P(b)), v)) +DEFINE_METASIG_T(SM(Obj_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, j r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) +DEFINE_METASIG_T(SM(UInt_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, K r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) DEFINE_METASIG_T(SM(RefExInfo_UIntPtr_RetVoid, r(g(EXINFO)) U, v)) -DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RetVoid, r(g(EXINFO)) r(g(EXINFO)) r(U) r(P(b)), v)) +DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, r(g(EXINFO)) r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) #ifdef FEATURE_COMINTEROP DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I)) DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RetIntPtr, j I r(I), I)) diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index bbd2a903e47dc4..1d5edee010cf4b 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1049,8 +1049,6 @@ void StackFrameIterator::CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 m_fDidFuncletReportGCReferences = true; m_isRuntimeWrappedExceptions = false; #endif // FEATURE_EH_FUNCLETS - m_movedPastFirstExInfo = false; - m_fFuncletNotSeen = false; m_fFoundFirstFunclet = false; #if defined(RECORD_RESUMABLE_FRAME_SP) m_pvResumableFrameTargetSP = NULL; @@ -1398,8 +1396,6 @@ void StackFrameIterator::ResetCrawlFrame() m_crawl.isFilterFuncletCached = false; m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false; m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false; - m_crawl.fShouldSaveFuncletInfo = false; - m_crawl.fShouldParentToFuncletReportSavedFuncletSlots = false; #endif // FEATURE_EH_FUNCLETS m_crawl.pThread = this->m_pThread; @@ -1623,33 +1619,6 @@ StackWalkAction StackFrameIterator::Filter(void) SIZE_T frameSP = (m_frameState == SFITER_FRAME_FUNCTION) ? (SIZE_T)dac_cast(m_crawl.pFrame) : m_crawl.GetRegisterSet()->SP; - if ((m_flags & GC_FUNCLET_REFERENCE_REPORTING) && (pExInfo != NULL) && (frameSP > (SIZE_T)pExInfo)) - { - if (!m_movedPastFirstExInfo) - { - if ((pExInfo->m_passNumber == 2) && !pExInfo->m_csfEnclosingClause.IsNull() && m_sfFuncletParent.IsNull() && pExInfo->m_lastReportedFunclet.IP != 0) - { - // We are in the 2nd pass and we have already called an exceptionally called - // finally funclet and reported that to GC in a previous GC run. But we have - // not seen any funclet on the call stack yet. - // Simulate that we have actualy seen a finally funclet during this pass and - // that it didn't report GC references to ensure that the references will be - // reported by the parent correctly. - m_sfFuncletParent = (StackFrame)pExInfo->m_csfEnclosingClause; - m_sfParent = m_sfFuncletParent; - m_fProcessNonFilterFunclet = true; - m_fDidFuncletReportGCReferences = false; - m_fFuncletNotSeen = true; - STRESS_LOG3(LF_GCROOTS, LL_INFO100, - "STACKWALK: Moved over first ExInfo @ %p in second pass, SP: %p, Enclosing clause: %p\n", - pExInfo, (void*)m_crawl.GetRegisterSet()->SP, (void*)m_sfFuncletParent.SP); - } - m_movedPastFirstExInfo = true; - } - } - - m_crawl.fShouldParentToFuncletReportSavedFuncletSlots = false; - // by default, there is no funclet for the current frame // that reported GC references m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false; @@ -1658,8 +1627,6 @@ StackWalkAction StackFrameIterator::Filter(void) // CrawlFrame m_crawl.fShouldCrawlframeReportGCReferences = true; - m_crawl.fShouldSaveFuncletInfo = false; - // By default, assume that parent frame is going to report GC references from // the actual location reported by the stack walk. m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = false; @@ -1808,18 +1775,6 @@ StackWalkAction StackFrameIterator::Filter(void) // can use it. m_sfParent = m_sfFuncletParent; - if (!m_fFoundFirstFunclet && (pExInfo > (void*)GetRegdisplaySP(m_crawl.GetRegisterSet())) && ((void*)m_sfParent.SP > pExInfo)) - { - // For the first funclet we encounter below the topmost ExInfo that has a parent above that ExInfo - // (so it is an exceptionally called funclet for the exception represented by the ExInfo), - // we instruct the GC scanning of the frame - // to save information on the funclet so that we can use it to report references in the parent frame if - // no such funclet is found in future GC scans for the same exception. - _ASSERTE(pExInfo != NULL); - m_crawl.fShouldSaveFuncletInfo = true; - m_fFoundFirstFunclet = true; - } - // For non-filter funclets, we will make the callback for the funclet // but skip all the frames until we reach the parent method. When we do, // we will make a callback for it as well and then continue to make callbacks @@ -2012,14 +1967,6 @@ StackWalkAction StackFrameIterator::Filter(void) } else if (!m_crawl.IsFunclet()) { - if (m_fFuncletNotSeen) - { - // We have reached a real parent of a funclet that would be on the stack if GC didn't - // kick in between the calls to funclets in the second pass. We instruct GC to report - // roots using the info of the saved funclet we've seen during a previous GC. - m_crawl.fShouldParentToFuncletReportSavedFuncletSlots = true; - m_fFuncletNotSeen = false; - } // we've reached the parent and it's not handling an exception, it's also not // a funclet so reset our state. note that we cannot reset the state when the // parent is a funclet since the leaf funclet didn't report any references and diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index cf4f7ae4f30199..f6fdbf863d6974 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -411,18 +411,6 @@ class CrawlFrame return fShouldParentFrameUseUnwindTargetPCforGCReporting; } - bool ShouldParentToFuncletReportSavedFuncletSlots() - { - LIMITED_METHOD_CONTRACT; - return fShouldParentToFuncletReportSavedFuncletSlots; - } - - bool ShouldSaveFuncletInfo() - { - LIMITED_METHOD_CONTRACT; - return fShouldSaveFuncletInfo; - } - const EE_ILEXCEPTION_CLAUSE& GetEHClauseForCatch() { return ehClauseForCatch; @@ -473,8 +461,6 @@ class CrawlFrame bool fShouldParentToFuncletSkipReportingGCReferences; bool fShouldCrawlframeReportGCReferences; bool fShouldParentFrameUseUnwindTargetPCforGCReporting; - bool fShouldSaveFuncletInfo; - bool fShouldParentToFuncletReportSavedFuncletSlots; EE_ILEXCEPTION_CLAUSE ehClauseForCatch; #endif //FEATURE_EH_FUNCLETS Thread* pThread; @@ -743,10 +729,6 @@ class StackFrameIterator bool m_fDidFuncletReportGCReferences; bool m_isRuntimeWrappedExceptions; #endif // FEATURE_EH_FUNCLETS - // The stack walk has moved past the first ExInfo location on the stack - bool m_movedPastFirstExInfo; - // Indicates that no funclet was seen during the current stack walk yet - bool m_fFuncletNotSeen; // Indicates that the stack walk has moved past a funclet bool m_fFoundFirstFunclet; #ifdef FEATURE_INTERPRETER From 0887cffff5a75d9d53395a0b7286f961c01c3e9f Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 15:49:37 +0200 Subject: [PATCH 04/15] Fix build break --- src/coreclr/vm/exinfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 61fe853a95ab7a..916910fb77e187 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -324,9 +324,9 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx m_propagateExceptionContext(NULL), #endif // HOST_UNIX m_CurrentClause({}), - m_pMDToReportFunctionLeave(NULL), + m_pMDToReportFunctionLeave(NULL) #ifdef HOST_WINDOWS - m_pLongJmpBuf(NULL), + , m_pLongJmpBuf(NULL), m_longJmpReturnValue(0) #endif // HOST_WINDOWS { From cdfae1a53c828b54900ad450a67b24137b3de21f Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 20:35:13 +0200 Subject: [PATCH 05/15] Fix offsets, exception interception and MUSL build break --- .../Runtime/ExceptionServices/AsmOffsets.cs | 24 +-- .../ExceptionServices/InternalCalls.cs | 3 - .../src/System/Runtime/ExceptionHandling.cs | 61 +----- src/coreclr/vm/corelib.h | 1 - src/coreclr/vm/excep.cpp | 36 ---- src/coreclr/vm/exceptionhandling.cpp | 184 +++++++++++------- src/coreclr/vm/exceptionhandling.h | 4 +- src/coreclr/vm/exceptionhandlingqcalls.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 - 9 files changed, 125 insertions(+), 190 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs index 105c8321b2849e..2e4d6559ed3f2c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -68,17 +68,17 @@ class AsmOffsets public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x132; #elif TARGET_X86 public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; - public const int SIZEOF__StackFrameIterator = 0x3d4; + public const int SIZEOF__StackFrameIterator = 0x3d0; public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3c2; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3d0; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3cc; #else // TARGET_64BIT public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; #if FEATURE_INTERPRETER - public const int SIZEOF__StackFrameIterator = 0xdc; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd8; + public const int SIZEOF__StackFrameIterator = 0xd8; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd4; #else - public const int SIZEOF__StackFrameIterator = 0xcc; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc8; + public const int SIZEOF__StackFrameIterator = 0xc8; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc4; #endif public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xba; #endif // TARGET_64BIT @@ -139,17 +139,17 @@ class AsmOffsets public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x12a; #elif TARGET_X86 public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; - public const int SIZEOF__StackFrameIterator = 0x3cc; + public const int SIZEOF__StackFrameIterator = 0x3c8; public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3ba; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c8; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c4; #else // TARGET_64BIT public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; #if FEATURE_INTERPRETER - public const int SIZEOF__StackFrameIterator = 0xd4; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd0; + public const int SIZEOF__StackFrameIterator = 0xd0; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xcc; #else - public const int SIZEOF__StackFrameIterator = 0xc4; - public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc0; + public const int SIZEOF__StackFrameIterator = 0xc0; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xbc; #endif public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xb2; #endif // TARGET_64BIT diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs index 32eb1b3d592abc..c1314ff9fe9db0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs @@ -20,9 +20,6 @@ internal static partial class InternalCalls [return: MarshalAs(UnmanagedType.U1)] internal static unsafe partial bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* fIsExceptionIntercepted); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ResumeAtInterceptionLocation")] - internal static unsafe partial void ResumeAtInterceptionLocation(void* pvRegDisplay); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFilterFunclet")] [return: MarshalAs(UnmanagedType.U1)] internal static unsafe partial bool RhpCallFilterFunclet( 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 1a375b006573da..12cbd39d85f750 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -693,66 +693,6 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) } #endif -#if !NATIVEAOT - // TODO: move this to native too - public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStackFrameSP) - { - exInfo._passNumber = 2; - exInfo._idxCurClause = MaxTryRegionIdx; - uint startIdx = MaxTryRegionIdx; - bool unwoundReversePInvoke = false; - bool isExceptionIntercepted = false; - bool isValid = exInfo._frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); - for (; isValid && !isExceptionIntercepted && ((byte*)exInfo._frameIter.SP <= (byte*)interceptStackFrameSP); isValid = exInfo._frameIter.Next(&startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) - { - Debug.Assert(isValid, "Unwind and intercept failed unexpectedly"); - DebugScanCallFrame(exInfo._passNumber, exInfo._frameIter.ControlPC, exInfo._frameIter.SP); - - if (unwoundReversePInvoke) - { - // Found the native frame that called the reverse P/invoke. - // It is not possible to run managed second pass handlers on a native frame. - break; - } - - if (exInfo._frameIter.SP == interceptStackFrameSP) - { - break; - } - -// InvokeSecondPass(ref exInfo, startIdx); - if (isExceptionIntercepted) - { - Debug.Assert(false); - break; - } - } - - // ------------------------------------------------ - // - // Call the interception code - // - // ------------------------------------------------ - if (unwoundReversePInvoke) - { - // object exceptionObj = exInfo.ThrownException; - // fixed (EH.ExInfo* pExInfo = &exInfo) - // { - // InternalCalls.RhpCallCatchFunclet( - // ObjectHandleOnStack.Create(ref exceptionObj), null, exInfo._frameIter.RegisterSet, pExInfo); - // } - } - else - { - InternalCalls.ResumeAtInterceptionLocation(exInfo._frameIter.RegisterSet); - } - - Debug.Fail("unreachable"); - FallbackFailFast(RhFailFastReason.InternalError, null); - } -#endif // !NATIVEAOT - - #if NATIVEAOT [RuntimeExport("RhRethrow")] [StackTraceHidden] @@ -941,6 +881,7 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass // funclets will always toggle this mode off before invoking them. InternalCalls.RhpSetThreadDoNotTriggerGC(); + scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; exInfo._passNumber = 2; object exceptionObj = exInfo.ThrownException; diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 55b29969c91cad..4ed824fb487012 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1347,7 +1347,6 @@ DEFINE_CLASS(EH, Runtime, EH) DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) DEFINE_METHOD(EH, FIND_RETHROW_EX_HANDLER, FindRethrowExHandler, SM_RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, UNWIND_AND_INTERCEPT, RhUnwindAndIntercept, SM_RefExInfo_UIntPtr_RetVoid) DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls) DEFINE_CLASS(STACKFRAMEITERATOR, Runtime, StackFrameIterator) #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 816eeb5768f26d..ca8235f7a86632 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -7277,42 +7277,6 @@ void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pE #endif } -#ifdef FEATURE_EH_FUNCLETS -// -// This function continues exception interception unwind after it crossed native frames using -// standard EH / SEH. -// -VOID DECLSPEC_NORETURN ContinueExceptionInterceptionUnwind() -{ - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; - STATIC_CONTRACT_MODE_ANY; - - GCX_COOP(); - - Thread *pThread = GetThread(); - ThreadExceptionState* pExState = pThread->GetExceptionState(); - UINT_PTR uInterceptStackFrame = 0; - - pExState->GetDebuggerState()->GetDebuggerInterceptInfo(NULL, NULL, - (PBYTE*)&uInterceptStackFrame, - NULL, NULL); - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__UNWIND_AND_INTERCEPT); - DECLARE_ARGHOLDER_ARRAY(args, 2); - args[ARGNUM_0] = PTR_TO_ARGHOLDER((ExInfo*)pExState->GetCurrentExceptionTracker()); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(uInterceptStackFrame); - pThread->IncPreventAbort(); - - //Ex.RhUnwindAndIntercept(throwable, &exInfo) - CRITICAL_CALLSITE; - CALL_MANAGED_METHOD_NORET(args) - - UNREACHABLE(); -} - -#endif // FEATURE_EH_FUNCLETS - // // This does the work of the Unwind and Continue Hanlder after the catch clause of that handler. The stack has been // unwound by the time this is called. Keep that in mind when deciding where to put new code :) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index f074a036e92f57..5f752ec3ce1d0a 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -787,62 +787,6 @@ OBJECTREF ExInfo::CreateThrowable( return oThrowable; } -#if defined(DEBUGGING_SUPPORTED) - -#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED -//--------------------------------------------------------------------------------------- -// -// This function is called by DefaultCatchHandler() to intercept an exception and start an unwind. -// -// Arguments: -// pCurrentEstablisherFrame - unused on WIN64 -// pExceptionRecord - EXCEPTION_RECORD of the exception being intercepted -// -// Return Value: -// ExceptionContinueSearch if the exception cannot be intercepted -// -// Notes: -// If the exception is intercepted, this function never returns. -// - -EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_REGISTRATION_RECORD* pCurrentEstablisherFrame) - EXCEPTION_RECORD* pExceptionRecord) -{ - if (!CheckThreadExceptionStateForInterception()) - { - return ExceptionContinueSearch; - } - - Thread* pThread = GetThread(); - ThreadExceptionState* pExState = pThread->GetExceptionState(); - - UINT_PTR uInterceptStackFrame = 0; - - pExState->GetDebuggerState()->GetDebuggerInterceptInfo(NULL, NULL, - (PBYTE*)&uInterceptStackFrame, - NULL, NULL); - - - GCX_COOP(); - - ExInfo* pExInfo = (ExInfo*)pExState->GetCurrentExceptionTracker(); - _ASSERTE(pExInfo != NULL); - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__UNWIND_AND_INTERCEPT); - DECLARE_ARGHOLDER_ARRAY(args, 2); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(pExInfo); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(uInterceptStackFrame); - pThread->IncPreventAbort(); - - //Ex.RhUnwindAndIntercept(throwable, &exInfo) - CRITICAL_CALLSITE; - CALL_MANAGED_METHOD_NORET(args) - - UNREACHABLE(); -} -#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED -#endif // DEBUGGING_SUPPORTED - #ifdef _DEBUG // // static @@ -2997,14 +2941,6 @@ void ExInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) #endif // DACCESS_COMPILE #ifndef DACCESS_COMPILE -// Mark the pinvoke frame as invoking CallCatchFunclet (and similar) for collided unwind detection -void MarkInlinedCallFrameAsFuncletCall(Frame* pFrame) -{ - _ASSERTE(pFrame->GetFrameIdentifier() == FrameIdentifier::InlinedCallFrame); - InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; - pInlinedCallFrame->m_Datum = (PTR_PInvokeMethodDesc)((TADDR)pInlinedCallFrame->m_Datum | (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper | (TADDR)InlinedCallFrameMarker::SecondPassFuncletCaller); -} - // Mark the pinvoke frame as invoking any exception handling helper void MarkInlinedCallFrameAsEHHelperCall(Frame* pFrame) { @@ -3161,8 +3097,6 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); - Frame* pFrame = pThread->GetFrame(); -// MarkInlinedCallFrameAsFuncletCall(pFrame); exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); DWORD_PTR dwResumePC = 0; UINT_PTR callerTargetSp = 0; @@ -3339,14 +3273,11 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi } } -extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) +void ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) { Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsFuncletCall(pFrame); - UINT_PTR targetSp = GetSP(pvRegDisplay->pCurrentContext); ExInfo *pExInfo = (PTR_ExInfo)pThread->GetExceptionState()->GetCurrentExceptionTracker(); @@ -4216,8 +4147,6 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid return result; } -const uint32_t MaxTryRegionIdx = 0xFFFFFFFFu; - static uint32_t CalculateCodeOffset(PCODE pbControlPC, IJitManager::MethodRegionInfo *pMethodRegionInfo) { uint codeOffset = (uint)(pbControlPC - pMethodRegionInfo->hotStartAddress); @@ -4230,6 +4159,8 @@ static uint32_t CalculateCodeOffset(PCODE pbControlPC, IJitManager::MethodRegion return codeOffset; } +const uint32_t MaxTryRegionIdx = 0xFFFFFFFFu; + static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) { ExtendedEHClauseEnumerator ehEnum; @@ -4312,7 +4243,7 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrame if (isExceptionIntercepted) { - pCatchHandler = NULL; + pCatchHandler = 0; break; } @@ -4350,6 +4281,113 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrame UNREACHABLE(); } +#if defined(DEBUGGING_SUPPORTED) + +#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED + +// +// This function continues exception interception unwind after it crossed native frames using +// standard EH / SEH. +// +VOID DECLSPEC_NORETURN ContinueExceptionInterceptionUnwind() +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_ANY; + + Thread* pThread = GetThread(); + ThreadExceptionState* pExState = pThread->GetExceptionState(); + + UINT_PTR uInterceptStackFrame = 0; + + pExState->GetDebuggerState()->GetDebuggerInterceptInfo(NULL, NULL, + (PBYTE*)&uInterceptStackFrame, + NULL, NULL); + + + GCX_COOP(); + + ExInfo* pExInfo = (ExInfo*)pExState->GetCurrentExceptionTracker(); + _ASSERTE(pExInfo != NULL); + StackFrameIterator *pFrameIter = &pExInfo->m_frameIter; + + pExInfo->m_passNumber = 2; + pExInfo->m_idxCurClause = MaxTryRegionIdx; + uint32_t startIdx = MaxTryRegionIdx; + CLR_BOOL unwoundReversePInvoke = false; + CLR_BOOL isExceptionIntercepted = false; + CLR_BOOL isValid = SfiInitWorker(pFrameIter, pExInfo->m_pExContext, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::InstructionFaultFlag) != 0, &isExceptionIntercepted); + for (; isValid && (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) <= uInterceptStackFrame); isValid = SfiNextWorker(pFrameIter, &startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) + { + _ASSERTE_MSG(isValid, "Unwind and intercept failed unexpectedly"); + _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != NULL, "IP address must not be null"); + + if (unwoundReversePInvoke) + { + // Found the native frame that called the reverse P/invoke. + // It is not possible to run managed second pass handlers on a native frame. + break; + } + + if (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) == uInterceptStackFrame) + { + break; + } + + InvokeSecondPass(pExInfo, startIdx); + if (isExceptionIntercepted) + { + _ASSERTE(FALSE); + break; + } + } + + // ------------------------------------------------ + // + // Call the interception code + // + // ------------------------------------------------ + if (unwoundReversePInvoke) + { + CallCatchFunclet(pExInfo->m_exception, NULL, pExInfo->m_frameIter.m_crawl.GetRegisterSet(), pExInfo); + } + else + { + ResumeAtInterceptionLocation(pExInfo->m_frameIter.m_crawl.GetRegisterSet()); + } + + UNREACHABLE(); +} + +//--------------------------------------------------------------------------------------- +// +// This function is called by DefaultCatchHandler() to intercept an exception and start an unwind. +// +// Arguments: +// pCurrentEstablisherFrame - unused on WIN64 +// pExceptionRecord - EXCEPTION_RECORD of the exception being intercepted +// +// Return Value: +// ExceptionContinueSearch if the exception cannot be intercepted +// +// Notes: +// If the exception is intercepted, this function never returns. +// + +EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_REGISTRATION_RECORD* pCurrentEstablisherFrame) + EXCEPTION_RECORD* pExceptionRecord) +{ + if (!CheckThreadExceptionStateForInterception()) + { + return ExceptionContinueSearch; + } + + ContinueExceptionInterceptionUnwind(); + UNREACHABLE(); +} +#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED +#endif // DEBUGGING_SUPPORTED + namespace AsmOffsetsAsserts { // Verify that the offsets into CONTEXT, REGDISPLAY, ExInfo and StackFrameIterator that the new managed exception handling diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index b202e609a09d38..e446ae2314b3a2 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -60,12 +60,10 @@ enum class InlinedCallFrameMarker { #ifdef HOST_64BIT ExceptionHandlingHelper = 2, - SecondPassFuncletCaller = 4, #else // HOST_64BIT ExceptionHandlingHelper = 1, - SecondPassFuncletCaller = 2, #endif // HOST_64BIT - Mask = ExceptionHandlingHelper | SecondPassFuncletCaller + Mask = ExceptionHandlingHelper }; #ifdef FEATURE_INTERPRETER diff --git a/src/coreclr/vm/exceptionhandlingqcalls.h b/src/coreclr/vm/exceptionhandlingqcalls.h index e6a0429db9d996..c9dd9ba353250b 100644 --- a/src/coreclr/vm/exceptionhandlingqcalls.h +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -13,7 +13,6 @@ struct ExInfo; #ifndef DACCESS_COMPILE extern "C" CLR_BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterP, REGDISPLAY* pvRegDisplay); -extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay); extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo); extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, IJitManager::MethodRegionInfo *pMethodRegionInfo, EH_CLAUSE_ENUMERATOR * pEHEnum); extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause); diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 84475a8d960f9b..7d70184bf0533d 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -529,7 +529,6 @@ static const Entry s_QCall[] = #ifdef FEATURE_EH_FUNCLETS DllImportEntry(SfiInit) DllImportEntry(SfiNext) - DllImportEntry(ResumeAtInterceptionLocation) DllImportEntry(CallFilterFunclet) DllImportEntry(EHEnumInitFromStackFrameIterator) DllImportEntry(EHEnumNext) From dd51b6d35c4ab5fd797a32af4d2af74148e2c209 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 22:50:51 +0200 Subject: [PATCH 06/15] Alternative change with minimalistic managed code differences --- .../Runtime/ExceptionServices/AsmOffsets.cs | 17 ++ .../src/System/Runtime/ExceptionHandling.cs | 177 ++++++++---------- src/coreclr/vm/corelib.h | 6 +- src/coreclr/vm/excep.cpp | 15 +- src/coreclr/vm/exceptionhandling.cpp | 63 +++---- src/coreclr/vm/exceptionhandling.h | 2 +- src/coreclr/vm/exinfo.h | 9 + src/coreclr/vm/metasig.h | 7 +- 8 files changed, 132 insertions(+), 164 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs index 2e4d6559ed3f2c..fd86a5d7950cab 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -217,6 +217,18 @@ class AsmOffsets public const int OFFSETOF__ExInfo__m_idxCurClause = 0xbc; public const int OFFSETOF__ExInfo__m_frameIter = 0xc0; public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator; + public const int OFFSETOF__ExInfo__m_pCatchHandler = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x48; + public const int OFFSETOF__ExInfo__m_handlingFrameSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x50; + +#if TARGET_ARM64 + public const int OFFSETOF__ExInfo__m_handlingFramePC = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x58; +#endif + +#if TARGET_UNIX + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x30; + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x38; +#endif + #else // TARGET_64BIT public const int SIZEOF__EHEnum = 0x10; public const int OFFSETOF__StackFrameIterator__m_pRegDisplay = 0x14; @@ -268,8 +280,13 @@ class AsmOffsets static_assert(offsetof(ExInfo, m_idxCurClause) == OFFSETOF__ExInfo__m_idxCurClause); static_assert(offsetof(ExInfo, m_frameIter) == OFFSETOF__ExInfo__m_frameIter); static_assert(offsetof(ExInfo, m_notifyDebuggerSP) == OFFSETOF__ExInfo__m_notifyDebuggerSP); + static_assert(offsetof(ExInfo, m_pCatchHandler) == OFFSETOF__ExInfo__m_pCatchHandler); + static_assert(offsetof(ExInfo, m_handlingFrameSP) == OFFSETOF__ExInfo__m_handlingFrameSP); +#if TARGET_ARM64 + static_assert(offsetof(ExInfo, m_handlingFramePC) == OFFSETOF__ExInfo__m_handlingFramePC); #endif +#endif } #if __cplusplus ; 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 12cbd39d85f750..3ebefed7557e6f 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -549,16 +549,37 @@ internal object ThrownException [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_notifyDebuggerSP)] internal volatile UIntPtr _notifyDebuggerSP; - } +#if !NATIVEAOT + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pCatchHandler)] + internal volatile byte* _pCatchHandler; - [StackTraceHidden] - public static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) - { - FindHwExHandler(exceptionCode, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFrameSP)] + internal volatile UIntPtr _handlingFrameSP; + +#if TARGET_ARM64 + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFramePC)] + internal volatile byte* _handlingFramePC; +#endif + +#if TARGET_UNIX + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback)] + internal volatile IntPtr _pReversePInvokePropagationCallback; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pReversePInvokePropagationContext)] + internal volatile IntPtr _pReversePInvokePropagationContext; +#endif // TARGET_UNIX + +#endif // !NATIVEAOT } + // + // Called by RhpThrowHwEx + // +#if NATIVEAOT + [RuntimeExport("RhThrowHwEx")] +#endif [StackTraceHidden] - private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) { #if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point @@ -629,41 +650,19 @@ private static void FindHwExHandler(uint exceptionCode, ref ExInfo exInfo, out U } exInfo.Init(exceptionToThrow!, instructionFault); - FindExHandler(exceptionToThrow!, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); - } - - // - // Called by RhpThrowHwEx - // + DispatchEx(ref exInfo._frameIter, ref exInfo); #if NATIVEAOT - [RuntimeExport("RhThrowHwEx")] - [StackTraceHidden] - public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) - { - FindHwExHandler(exceptionCode, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); FallbackFailFast(RhFailFastReason.InternalError, null); - } #endif + } private const uint MaxTryRegionIdx = 0xFFFFFFFFu; +#if NATIVEAOT + [RuntimeExport("RhThrowEx")] +#endif [StackTraceHidden] - public static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) - { - // Transform attempted throws of null to a throw of NullReferenceException. - if (exceptionObj == null) - { - IntPtr faultingCodeAddress = exInfo._pExContext->IP; - exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); - } - - exInfo.Init(exceptionObj); - FindExHandler(exceptionObj, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); - } - - [StackTraceHidden] - private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) { #if NATIVEAOT @@ -678,51 +677,26 @@ private static void FindExHandler(object exceptionObj, ref ExInfo exInfo, out UI InternalCalls.RhpValidateExInfoStack(); #endif // NATIVEAOT - DispatchExFirstPass(ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); - } + // Transform attempted throws of null to a throw of NullReferenceException. + if (exceptionObj == null) + { + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); + } -#if NATIVEAOT - [RuntimeExport("RhThrowEx")] - [StackTraceHidden] - public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) - { exInfo.Init(exceptionObj); - FindExHandler(exceptionObj, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); + DispatchEx(ref exInfo._frameIter, ref exInfo); +#if NATIVEAOT FallbackFailFast(RhFailFastReason.InternalError, null); - } #endif + } #if NATIVEAOT [RuntimeExport("RhRethrow")] +#endif [StackTraceHidden] public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) { -#if TARGET_WINDOWS - // Alert the debugger that we threw an exception. - InternalCalls.RhpFirstChanceExceptionNotification(); -#endif // TARGET_WINDOWS - - // trigger a GC (only if gcstress) to ensure we can stackwalk at this point - GCStress.TriggerGC(); - - InternalCalls.RhpValidateExInfoStack(); - - FindRethrowExHandler(ref activeExInfo, ref exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext); - DispatchExSecondPass(ref exInfo, handlingFrameSP, handlingFramePC, pCatchHandler, pReversePInvokePropagationCallback, pReversePInvokePropagationContext); - FallbackFailFast(RhFailFastReason.InternalError, null); - } -#endif // NATIVEAOT - - [StackTraceHidden] - public static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler) - { - FindRethrowExHandler(ref activeExInfo, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out _, out _); - } - - [StackTraceHidden] - private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) - { #if NATIVEAOT #if TARGET_WINDOWS @@ -741,22 +715,25 @@ private static void FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exI object rethrownException = activeExInfo.ThrownException; exInfo.Init(rethrownException, ref activeExInfo); - FindExHandler(rethrownException, ref exInfo, out handlingFrameSP, out handlingFramePC, out pCatchHandler, out pReversePInvokePropagationCallback, out pReversePInvokePropagationContext); + DispatchEx(ref exInfo._frameIter, ref exInfo); +#if NATIVEAOT + FallbackFailFast(RhFailFastReason.InternalError, null); +#endif } [StackTraceHidden] - private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingFrameSP, out byte* handlingFramePC, out byte* pCatchHandler, out IntPtr pReversePInvokePropagationCallback, out IntPtr pReversePInvokePropagationContext) + private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExInfo exInfo) { Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); object exceptionObj = exInfo.ThrownException; - scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; + // ------------------------------------------------ // // First pass // // ------------------------------------------------ - handlingFrameSP = MaxSP; - pCatchHandler = null; + UIntPtr handlingFrameSP = MaxSP; + byte* pCatchHandler = null; uint catchingTryRegionIdx = MaxTryRegionIdx; bool isFirstRethrowFrame = (exInfo._kind & ExKind.RethrowFlag) != 0; @@ -767,8 +744,8 @@ private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingF byte* prevOriginalPC = null; UIntPtr prevFramePtr = UIntPtr.Zero; bool unwoundReversePInvoke = false; - pReversePInvokePropagationCallback = IntPtr.Zero; - pReversePInvokePropagationContext = IntPtr.Zero; + IntPtr pReversePInvokePropagationCallback = IntPtr.Zero; + IntPtr pReversePInvokePropagationContext = IntPtr.Zero; bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); @@ -860,15 +837,22 @@ private static void DispatchExFirstPass(ref ExInfo exInfo, out UIntPtr handlingF // without a catch handler or propagation callback. Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke || isExceptionIntercepted, "We should have a handler if we're starting the second pass"); Debug.Assert(!isExceptionIntercepted || (pCatchHandler == null), "No catch handler should be returned for intercepted exceptions in the first pass"); + +#if !NATIVEAOT + exInfo._pCatchHandler = pCatchHandler; + exInfo._handlingFrameSP = handlingFrameSP; +#if TARGET_ARM64 + exInfo._handlingFramePC = prevControlPC; +#endif +#if TARGET_UNIX + exInfo._pReversePInvokePropagationCallback = pReversePInvokePropagationCallback; + exInfo._pReversePInvokePropagationContext = pReversePInvokePropagationContext; +#endif // TARGET_UNIX exInfo._idxCurClause = catchingTryRegionIdx; - handlingFramePC = prevControlPC; - } -#if NATIVEAOT - [StackTraceHidden] - private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFrameSP, byte* prevControlPC, byte* pCatchHandler, IntPtr pReversePInvokePropagationCallback, IntPtr pReversePInvokePropagationContext) - { + return; +#else // !NATIVEAOT // ------------------------------------------------ // // Second pass @@ -880,16 +864,15 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass // funclets will always toggle this mode off before invoking them. + InternalCalls.RhpSetThreadDoNotTriggerGC(); - scoped ref StackFrameIterator frameIter = ref exInfo._frameIter; exInfo._passNumber = 2; - object exceptionObj = exInfo.ThrownException; - uint startIdx = MaxTryRegionIdx; - uint catchingTryRegionIdx = exInfo._idxCurClause; - bool unwoundReversePInvoke = false; - bool isExceptionIntercepted = false; - bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); + exInfo._idxCurClause = catchingTryRegionIdx; + startIdx = MaxTryRegionIdx; + unwoundReversePInvoke = false; + isExceptionIntercepted = false; + isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); @@ -936,7 +919,6 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram } #endif // FEATURE_OBJCMARSHAL - // ------------------------------------------------ // // Call the handler and resume execution @@ -948,8 +930,8 @@ private static void DispatchExSecondPass(ref ExInfo exInfo, UIntPtr handlingFram // currently, RhpCallCatchFunclet will resume after the catch Debug.Fail("unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); +#endif // !NATIVEAOT } -#endif // NATIVEAOT [System.Diagnostics.Conditional("DEBUG")] private static void DebugScanCallFrame(int passNumber, byte* ip, UIntPtr sp) @@ -1192,11 +1174,7 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL // Now, we continue skipping while the try region is identical to the one that invoked the // previous dispatch. - if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd) -#if !NATIVEAOT - && (ehClause._isSameTry) -#endif - ) + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) continue; // We are done skipping. This is required to handle empty finally block markers that are used @@ -1227,14 +1205,7 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL byte* pFinallyHandler = ehClause._handlerAddress; exInfo._idxCurClause = curIdx; -#if NATIVEAOT InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet); -#else // NATIVEAOT - fixed (EH.ExInfo* pExInfo = &exInfo) - { - InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet, pExInfo); - } -#endif // NATIVEAOT exInfo._idxCurClause = MaxTryRegionIdx; } } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 4ed824fb487012..12834093b39290 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1344,9 +1344,9 @@ DEFINE_FIELD_U(dictionaryIndexAndSlot, GenericHandleArgs, dictionaryIndexAndSlot #ifdef FEATURE_EH_FUNCLETS DEFINE_CLASS(EH, Runtime, EH) -DEFINE_METHOD(EH, FIND_EX_HANDLER, FindExHandler, SM_Obj_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, FIND_HW_EX_HANDLER, FindHwExHandler, SM_UInt_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) -DEFINE_METHOD(EH, FIND_RETHROW_EX_HANDLER, FindRethrowExHandler, SM_RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid) +DEFINE_METHOD(EH, RH_THROW_EX, RhThrowEx, SM_Obj_RefExInfo_RetVoid) +DEFINE_METHOD(EH, RH_THROWHW_EX, RhThrowHwEx, SM_UInt_RefExInfo_RetVoid) +DEFINE_METHOD(EH, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_RetVoid) DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls) DEFINE_CLASS(STACKFRAMEITERATOR, Runtime, StackFrameIterator) #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index ca8235f7a86632..9555397240830d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6171,25 +6171,18 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) } } - TADDR handlingFrameSP; - PCODE handlingFramePC; - PCODE pCatchHandler; - GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 5); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); - args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) + //Ex.RhThrowHwEx(exceptionCode, &exInfo) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); + DispatchExSecondPass(&exInfo); GCPROTECT_END(); UNREACHABLE(); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 5f752ec3ce1d0a..9ed9362225db91 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1446,25 +1446,18 @@ BOOL HandleHardwareException(PAL_SEHException* ex) exInfo.TakeExceptionPointersOwnership(ex); } - TADDR handlingFrameSP; - PCODE handlingFramePC; - PCODE pCatchHandler; - GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_HW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 5); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); - args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindHwExHandler(exceptionCode, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) + //Ex.RhThrowHwEx(exceptionCode, &exInfo) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); + DispatchExSecondPass(&exInfo); GCPROTECT_END(); @@ -1596,25 +1589,18 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE GCPROTECT_BEGIN(exInfo.m_exception); - TADDR handlingFrameSP; - PCODE handlingFramePC; - PCODE pCatchHandler; - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 5); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(throwable); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); - args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindExHandler(throwable, &exInfo, &handlingFrameSP, &handlingFramePC, &pCatchHandler) + //Ex.RhThrowEx(throwable, &exInfo) CRITICAL_CALLSITE; CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); + DispatchExSecondPass(&exInfo); GCPROTECT_END(); GCPROTECT_END(); @@ -1659,25 +1645,18 @@ VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionConte ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pExceptionContext, ExKind::None); - TADDR handlingFrameSP; - PCODE handlingFramePC; - PCODE pCatchHandler; - GCPROTECT_BEGIN(exInfo.m_exception); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__FIND_RETHROW_EX_HANDLER); - DECLARE_ARGHOLDER_ARRAY(args, 5); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); + DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo); args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(&handlingFrameSP); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(&handlingFramePC); - args[ARGNUM_4] = PTR_TO_ARGHOLDER(&pCatchHandler); pThread->IncPreventAbort(); - //Ex.FindRethrowExHandler(ref ExInfo activeExInfo, ref ExInfo exInfo, out UIntPtr handlingFrameSP, out UIntPtr handlingFramePC, out byte* pCatchHandler) + //Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) CALL_MANAGED_METHOD_NORET(args) - DispatchExSecondPass(&exInfo, handlingFrameSP, handlingFramePC, pCatchHandler); + DispatchExSecondPass(&exInfo); GCPROTECT_END(); @@ -4225,10 +4204,16 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); } -void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE handlingFramePC, PCODE pCatchHandler) +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo) { //GCHeapUtilities::GetGCHeap()->GarbageCollect(2, FALSE, collection_aggressive); + TADDR handlingFrameSP = pExInfo->m_handlingFrameSP; +#ifdef TARGET_ARM64 + PCODE handlingFramePC = pExInfo->m_handlingFramePC; +#endif + PCODE pCatchHandler = pExInfo->m_pCatchHandler; + StackFrameIterator *pFrameIter = &pExInfo->m_frameIter; pExInfo->m_passNumber = 2; uint startIdx = MaxTryRegionIdx; @@ -4239,7 +4224,7 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrame for (; isValid && (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) <= handlingFrameSP); isValid = SfiNextWorker(pFrameIter, &startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { _ASSERTE_MSG(isValid, "second-pass EH unwind failed unexpectedly"); - _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != NULL, "IP address must not be null"); + _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != 0, "IP address must not be null"); if (isExceptionIntercepted) { @@ -4281,10 +4266,6 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrame UNREACHABLE(); } -#if defined(DEBUGGING_SUPPORTED) - -#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED - // // This function continues exception interception unwind after it crossed native frames using // standard EH / SEH. @@ -4320,7 +4301,7 @@ VOID DECLSPEC_NORETURN ContinueExceptionInterceptionUnwind() for (; isValid && (GetRegdisplaySP(pFrameIter->m_crawl.GetRegisterSet()) <= uInterceptStackFrame); isValid = SfiNextWorker(pFrameIter, &startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { _ASSERTE_MSG(isValid, "Unwind and intercept failed unexpectedly"); - _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != NULL, "IP address must not be null"); + _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != 0, "IP address must not be null"); if (unwoundReversePInvoke) { @@ -4385,8 +4366,6 @@ EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_RE ContinueExceptionInterceptionUnwind(); UNREACHABLE(); } -#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED -#endif // DEBUGGING_SUPPORTED namespace AsmOffsetsAsserts { diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index e446ae2314b3a2..b135582d272e63 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -34,7 +34,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); -void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo, TADDR handlingFrameSP, PCODE handlingFramePC, PCODE pCatchHandler); +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo); enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index bad198e5421c28..26ee34c393e942 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -322,6 +322,15 @@ struct ExInfo // The exception handling clause for the catch handler that was identified during pass 1 EE_ILEXCEPTION_CLAUSE m_ClauseForCatch; + // Catch handler address + PCODE m_pCatchHandler; + // SP of the frame handling the exception + TADDR m_handlingFrameSP; +#ifdef TARGET_ARM64 + // PC of the frame handling the exception + PCODE m_handlingFramePC; +#endif + #ifdef TARGET_UNIX // Set to TRUE to take ownership of the EXCEPTION_RECORD and CONTEXT_RECORD in the m_ptrs. When set, the // memory of those records is freed using PAL_FreeExceptionRecords when the ExInfo is destroyed. diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index e1f8cf4844e7e1..ddb9b5ea403e63 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -184,10 +184,9 @@ DEFINE_METASIG(SM(Obj_IntPtr_RetVoid, j I, v)) DEFINE_METASIG(SM(Obj_IntPtr_RetBool, j I, F)) DEFINE_METASIG(SM(Obj_IntPtr_IntPtr_Int_RetIntPtr, j I I i, I)) DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j)) -DEFINE_METASIG_T(SM(Obj_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, j r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) -DEFINE_METASIG_T(SM(UInt_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, K r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) -DEFINE_METASIG_T(SM(RefExInfo_UIntPtr_RetVoid, r(g(EXINFO)) U, v)) -DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RefUIntPtr_RefBytePtr_RefBytePtr_RetVoid, r(g(EXINFO)) r(g(EXINFO)) r(U) r(P(b)) r(P(b)), v)) +DEFINE_METASIG_T(SM(Obj_RefExInfo_RetVoid, j r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(UInt_RefExInfo_RetVoid, K r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RetVoid, r(g(EXINFO)) r(g(EXINFO)), v)) #ifdef FEATURE_COMINTEROP DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I)) DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RetIntPtr, j I r(I), I)) From da9abb54cee4446d0d68b301f3ac38d8f856ba0d Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Sep 2025 23:27:28 +0200 Subject: [PATCH 07/15] Fix 32 bit offsets --- .../src/System/Runtime/ExceptionServices/AsmOffsets.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs index fd86a5d7950cab..cb303ec0d6afd8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -240,6 +240,14 @@ class AsmOffsets public const int OFFSETOF__ExInfo__m_idxCurClause = 0x68; public const int OFFSETOF__ExInfo__m_frameIter = 0x6c; public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator; + public const int OFFSETOF__ExInfo__m_pCatchHandler = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x2c; + public const int OFFSETOF__ExInfo__m_handlingFrameSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x30; + +#if TARGET_UNIX + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x38; + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x3c; +#endif + #endif // TARGET_64BIT #if __cplusplus From 521dbb36c65b129ea27474e6997a995b332b300c Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sat, 20 Sep 2025 01:17:38 +0200 Subject: [PATCH 08/15] Fix arm64 issue --- .../Runtime.Base/src/System/Runtime/ExceptionHandling.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3ebefed7557e6f..7f3d9caab71ae4 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -842,7 +842,7 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn exInfo._pCatchHandler = pCatchHandler; exInfo._handlingFrameSP = handlingFrameSP; #if TARGET_ARM64 - exInfo._handlingFramePC = prevControlPC; + exInfo._handlingFramePC = prevOriginalPC; #endif #if TARGET_UNIX exInfo._pReversePInvokePropagationCallback = pReversePInvokePropagationCallback; From 43e791bfd0ba23dc7b9d520edadbbdddff1d10eb Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 19 Sep 2025 17:35:50 -0700 Subject: [PATCH 09/15] Update src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs --- .../Runtime.Base/src/System/Runtime/ExceptionHandling.cs | 1 + 1 file changed, 1 insertion(+) 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 7f3d9caab71ae4..3ea1626385fd74 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -867,6 +867,7 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn InternalCalls.RhpSetThreadDoNotTriggerGC(); + exInfo._passNumber = 2; exInfo._idxCurClause = catchingTryRegionIdx; startIdx = MaxTryRegionIdx; From b2925cd33ec1a2ed785958becc73ccad2580a30b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 22 Sep 2025 17:55:23 +0200 Subject: [PATCH 10/15] Add/fix some contracts --- src/coreclr/debug/ee/debugger.cpp | 2 +- src/coreclr/vm/eedbginterfaceimpl.inl | 2 +- src/coreclr/vm/exceptionhandling.cpp | 49 +++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index e797008279411d..c79e770ac9ef5f 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -7780,7 +7780,7 @@ void Debugger::FirstChanceManagedExceptionCatcherFound(Thread *pThread, CONTRACTL { THROWS; - GC_TRIGGERS_FROM_GETJITINFO; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/coreclr/vm/eedbginterfaceimpl.inl b/src/coreclr/vm/eedbginterfaceimpl.inl index bc16a336f253d3..12ae7a161f20a6 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.inl +++ b/src/coreclr/vm/eedbginterfaceimpl.inl @@ -49,7 +49,7 @@ class EEToDebuggerExceptionInterfaceWrapper CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 9ed9362225db91..de91d8ad359731 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3068,7 +3068,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi CONTRACTL { MODE_COOPERATIVE; - GC_TRIGGERS; + GC_NOTRIGGER; THROWS; } CONTRACTL_END; @@ -3112,7 +3112,10 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi EH_LOG((LL_INFO100, "Calling catch funclet at %p\n", pHandlerIP)); - dwResumePC = pCodeManager->CallFunclet(throwable, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + { + CONTRACT_VIOLATION(GCViolation); + dwResumePC = pCodeManager->CallFunclet(throwable, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + } FixContext(pvRegDisplay->pCurrentContext); @@ -3122,6 +3125,8 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi callerTargetSp = CallerStackFrame::FromRegDisplay(pvRegDisplay).SP; } + CONTRACT_VIOLATION(GCViolation); + UINT_PTR targetSp = GetSP(pvRegDisplay->pCurrentContext); PopExplicitFrames(pThread, (void*)targetSp, (void*)callerTargetSp); @@ -3303,6 +3308,14 @@ void ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { + CONTRACTL + { + MODE_COOPERATIVE; + GC_NOTRIGGER; + THROWS; + } + CONTRACTL_END; + Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); @@ -3315,7 +3328,10 @@ void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exIn exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); EH_LOG((LL_INFO100, "Calling finally funclet at %p\n", pHandlerIP)); - exInfo->m_frameIter.m_crawl.GetCodeManager()->CallFunclet(NULL, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + { + CONTRACT_VIOLATION(GCViolation); + exInfo->m_frameIter.m_crawl.GetCodeManager()->CallFunclet(NULL, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + } pThread->IncPreventAbort(); @@ -3692,6 +3708,14 @@ static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExIn CLR_BOOL SfiInitWorker(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted) { + CONTRACTL + { + MODE_COOPERATIVE; + THROWS; + if (GET_THREAD()->GetExceptionState()->GetCurrentExceptionTracker()->m_passNumber == 1) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + } + CONTRACTL_END; + CLR_BOOL result = FALSE; Thread* pThread = GET_THREAD(); ExInfo* pExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker(); @@ -3833,6 +3857,7 @@ extern "C" CLR_BOOL QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStack Frame* pFrame = pThread->GetFrame(); MarkInlinedCallFrameAsEHHelperCall(pFrame); + GCX_COOP(); result = SfiInitWorker(pThis, pStackwalkCtx, instructionFault, pfIsExceptionIntercepted); END_QCALL; @@ -3858,6 +3883,14 @@ static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrame CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR_BOOL* fUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted) { + CONTRACTL + { + MODE_COOPERATIVE; + THROWS; + if (GET_THREAD()->GetExceptionState()->GetCurrentExceptionTracker()->m_passNumber == 1) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + } + CONTRACTL_END; + StackWalkAction retVal = SWA_FAILED; CLR_BOOL isPropagatingToNativeCode = FALSE; Thread* pThread = GET_THREAD(); @@ -3984,6 +4017,8 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR { #ifdef HOST_WINDOWS GetThread()->SetThreadStateNC(Thread::TSNC_SkipManagedPersonalityRoutine); + CONTRACT_VIOLATION(GCViolation); + GCX_PREEMP_NO_DTOR(); RaiseException(pTopExInfo->m_ExceptionCode, EXCEPTION_NONCONTINUABLE, pTopExInfo->m_ptrs.ExceptionRecord->NumberParameters, pTopExInfo->m_ptrs.ExceptionRecord->ExceptionInformation); #else CrashDumpAndTerminateProcess(pTopExInfo->m_ExceptionCode); @@ -4119,6 +4154,7 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid Frame* pFrame = pThread->GetFrame(); MarkInlinedCallFrameAsEHHelperCall(pFrame); + GCX_COOP(); result = SfiNextWorker(pThis, uExCollideClauseIdx, fUnwoundReversePInvoke, pfIsExceptionIntercepted); END_QCALL; @@ -4206,6 +4242,13 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo) { + CONTRACTL + { + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + //GCHeapUtilities::GetGCHeap()->GarbageCollect(2, FALSE, collection_aggressive); TADDR handlingFrameSP = pExInfo->m_handlingFrameSP; From 27424a5c4535504c0fd634ed8ca2f8be51b0fe1b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 22 Sep 2025 20:02:06 +0200 Subject: [PATCH 11/15] Fix another arm64 issue - need to use adjusted PC --- src/coreclr/vm/exceptionhandling.cpp | 26 +++++++++++++++++++++----- src/coreclr/vm/stackwalk.h | 5 +++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index de91d8ad359731..7847c7196925e6 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3412,8 +3412,8 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterato return TRUE; } -// The onlyFinallys option makes the function return only finally and fault clauses. It is used in -// the 2nd path of EH primarily to avoid calls to ResolveEHClause that can trigger GC. +// The doNotCalculateCatchType option makes the function skip calculation of the catch type. It is used in +// the 2nd path of EH to avoid possible GC stemming from a call to ResolveEHClause. CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, bool doNotCalculateCatchType = false) { CLR_BOOL result = FALSE; @@ -4164,6 +4164,8 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid static uint32_t CalculateCodeOffset(PCODE pbControlPC, IJitManager::MethodRegionInfo *pMethodRegionInfo) { + STATIC_CONTRACT_LEAF; + uint codeOffset = (uint)(pbControlPC - pMethodRegionInfo->hotStartAddress); // If the PC is in the cold region, adjust the offset to be relative to the start of the method. if ((pMethodRegionInfo->coldSize != 0) && (codeOffset >= pMethodRegionInfo->hotSize)) @@ -4176,15 +4178,24 @@ static uint32_t CalculateCodeOffset(PCODE pbControlPC, IJitManager::MethodRegion const uint32_t MaxTryRegionIdx = 0xFFFFFFFFu; +// This function is a copy of the code in ExceptionHandling.cs converted to native code. +// The only difference is the ehClause._isSameTry check that is coreclr specific. static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) { + CONTRACTL + { + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + ExtendedEHClauseEnumerator ehEnum; IJitManager::MethodRegionInfo methodRegionInfo; if (!EHEnumInitFromStackFrameIterator(&pExInfo->m_frameIter, &methodRegionInfo, &ehEnum)) return; - PCODE pbControlPC = GetControlPC(pExInfo->m_frameIter.m_crawl.GetRegisterSet()); + PCODE pbControlPC = pExInfo->m_frameIter.GetAdjustedControlPC(); uint codeOffset = CalculateCodeOffset(pbControlPC, &methodRegionInfo); @@ -4237,6 +4248,13 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) { + CONTRACTL + { + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); } @@ -4249,8 +4267,6 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo) } CONTRACTL_END; - //GCHeapUtilities::GetGCHeap()->GarbageCollect(2, FALSE, collection_aggressive); - TADDR handlingFrameSP = pExInfo->m_handlingFrameSP; #ifdef TARGET_ARM64 PCODE handlingFramePC = pExInfo->m_handlingFramePC; diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index f6fdbf863d6974..ddf7158a62eee9 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -597,6 +597,11 @@ class StackFrameIterator m_AdjustedControlPC = pc; } + TADDR GetAdjustedControlPC() + { + return m_AdjustedControlPC; + } + void UpdateIsRuntimeWrappedExceptions() { CONTRACTL From 608b877f9b4eb0feb7c424c3b177002c4dd4cf48 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 23 Sep 2025 00:55:05 +0200 Subject: [PATCH 12/15] Change the GC prevention to single frame only --- src/coreclr/debug/ee/debugger.cpp | 2 +- src/coreclr/vm/eedbginterfaceimpl.inl | 2 +- src/coreclr/vm/exceptionhandling.cpp | 32 +++++++++++++-------------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index c79e770ac9ef5f..e797008279411d 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -7780,7 +7780,7 @@ void Debugger::FirstChanceManagedExceptionCatcherFound(Thread *pThread, CONTRACTL { THROWS; - GC_NOTRIGGER; + GC_TRIGGERS_FROM_GETJITINFO; MODE_ANY; } CONTRACTL_END; diff --git a/src/coreclr/vm/eedbginterfaceimpl.inl b/src/coreclr/vm/eedbginterfaceimpl.inl index 12ae7a161f20a6..bc16a336f253d3 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.inl +++ b/src/coreclr/vm/eedbginterfaceimpl.inl @@ -49,7 +49,7 @@ class EEToDebuggerExceptionInterfaceWrapper CONTRACTL { THROWS; - GC_NOTRIGGER; + GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 7847c7196925e6..7659f4c505a7d7 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3068,7 +3068,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi CONTRACTL { MODE_COOPERATIVE; - GC_NOTRIGGER; + GC_TRIGGERS; THROWS; } CONTRACTL_END; @@ -3112,10 +3112,7 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi EH_LOG((LL_INFO100, "Calling catch funclet at %p\n", pHandlerIP)); - { - CONTRACT_VIOLATION(GCViolation); - dwResumePC = pCodeManager->CallFunclet(throwable, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); - } + dwResumePC = pCodeManager->CallFunclet(throwable, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); FixContext(pvRegDisplay->pCurrentContext); @@ -3125,8 +3122,6 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi callerTargetSp = CallerStackFrame::FromRegDisplay(pvRegDisplay).SP; } - CONTRACT_VIOLATION(GCViolation); - UINT_PTR targetSp = GetSP(pvRegDisplay->pCurrentContext); PopExplicitFrames(pThread, (void*)targetSp, (void*)callerTargetSp); @@ -3328,10 +3323,9 @@ void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exIn exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); EH_LOG((LL_INFO100, "Calling finally funclet at %p\n", pHandlerIP)); - { - CONTRACT_VIOLATION(GCViolation); - exInfo->m_frameIter.m_crawl.GetCodeManager()->CallFunclet(NULL, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); - } + ENDFORBIDGC(); + exInfo->m_frameIter.m_crawl.GetCodeManager()->CallFunclet(NULL, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + BEGINFORBIDGC(); pThread->IncPreventAbort(); @@ -3712,7 +3706,7 @@ CLR_BOOL SfiInitWorker(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, CLR_BO { MODE_COOPERATIVE; THROWS; - if (GET_THREAD()->GetExceptionState()->GetCurrentExceptionTracker()->m_passNumber == 1) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + GC_TRIGGERS; } CONTRACTL_END; @@ -3887,7 +3881,7 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR { MODE_COOPERATIVE; THROWS; - if (GET_THREAD()->GetExceptionState()->GetCurrentExceptionTracker()->m_passNumber == 1) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + GC_TRIGGERS; } CONTRACTL_END; @@ -4017,7 +4011,6 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR { #ifdef HOST_WINDOWS GetThread()->SetThreadStateNC(Thread::TSNC_SkipManagedPersonalityRoutine); - CONTRACT_VIOLATION(GCViolation); GCX_PREEMP_NO_DTOR(); RaiseException(pTopExInfo->m_ExceptionCode, EXCEPTION_NONCONTINUABLE, pTopExInfo->m_ptrs.ExceptionRecord->NumberParameters, pTopExInfo->m_ptrs.ExceptionRecord->ExceptionInformation); #else @@ -4185,7 +4178,7 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) CONTRACTL { MODE_COOPERATIVE; - GC_NOTRIGGER; + GC_TRIGGERS; } CONTRACTL_END; @@ -4201,6 +4194,9 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) uint lastTryStart = 0, lastTryEnd = 0; + // We need to forbid any GC to happen between successive funclet invocations. + BEGINFORBIDGC(); + // Search the clauses for one that contains the current offset. RhEHClause ehClause; for (uint curIdx = 0; EHEnumNextWorker(&ehEnum, &ehClause, /* onlyFinallys */ true) && curIdx < idxLimit; curIdx++) @@ -4244,6 +4240,8 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) CallFinallyFunclet(pFinallyHandler, pExInfo->m_frameIter.m_crawl.GetRegisterSet(), pExInfo); pExInfo->m_idxCurClause = MaxTryRegionIdx; } + + ENDFORBIDGC(); } static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) @@ -4251,7 +4249,7 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) CONTRACTL { MODE_COOPERATIVE; - GC_NOTRIGGER; + GC_TRIGGERS; } CONTRACTL_END; @@ -4263,7 +4261,7 @@ void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo) CONTRACTL { MODE_COOPERATIVE; - GC_NOTRIGGER; + GC_TRIGGERS; } CONTRACTL_END; From dde85e29401dcc74e7a80f9fb393ddf7412724a1 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 23 Sep 2025 01:13:17 +0200 Subject: [PATCH 13/15] Fix one new contract issue --- src/coreclr/vm/exceptionhandling.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 7659f4c505a7d7..0c4ee7eaa4232d 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3303,13 +3303,9 @@ void ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { - CONTRACTL - { - MODE_COOPERATIVE; - GC_NOTRIGGER; - THROWS; - } - CONTRACTL_END; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; Thread* pThread = GET_THREAD(); pThread->DecPreventAbort(); From 53bcc9a20f20182efcb33a1d3fa5c2907d4c9936 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 24 Sep 2025 22:43:08 +0200 Subject: [PATCH 14/15] Update GC forbid locations This is done to ensure that no GC is allowed between the scanned stack range is extended and a funclet for the current frame is called. --- src/coreclr/vm/exceptionhandling.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 0c4ee7eaa4232d..5db67a6a844034 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3311,7 +3311,6 @@ void CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exIn pThread->DecPreventAbort(); exInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(exInfo->m_frameIter.m_crawl.GetRegisterSet()); - exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); MethodDesc *pMD = exInfo->m_frameIter.m_crawl.GetFunction(); // Profiler, debugger and ETW events @@ -4181,8 +4180,16 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) ExtendedEHClauseEnumerator ehEnum; IJitManager::MethodRegionInfo methodRegionInfo; + // We need to forbid any GC to happen between successive funclet invocations. + BEGINFORBIDGC(); + + pExInfo->m_ScannedStackRange.ExtendUpperBound(pExInfo->m_frameIter.m_crawl.GetRegisterSet()->SP); + if (!EHEnumInitFromStackFrameIterator(&pExInfo->m_frameIter, &methodRegionInfo, &ehEnum)) + { + ENDFORBIDGC(); return; + } PCODE pbControlPC = pExInfo->m_frameIter.GetAdjustedControlPC(); @@ -4190,12 +4197,9 @@ static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart, uint idxLimit) uint lastTryStart = 0, lastTryEnd = 0; - // We need to forbid any GC to happen between successive funclet invocations. - BEGINFORBIDGC(); - // Search the clauses for one that contains the current offset. RhEHClause ehClause; - for (uint curIdx = 0; EHEnumNextWorker(&ehEnum, &ehClause, /* onlyFinallys */ true) && curIdx < idxLimit; curIdx++) + for (uint curIdx = 0; EHEnumNextWorker(&ehEnum, &ehClause, /* doNotCalculateCatchType */ true) && curIdx < idxLimit; curIdx++) { // // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where From 280e65124ecb4b259909388eb7284524d328c992 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 25 Sep 2025 23:58:07 +0200 Subject: [PATCH 15/15] Fix/add comment based on review --- .../Runtime.Base/src/System/Runtime/ExceptionHandling.cs | 3 +++ src/coreclr/vm/exceptionhandling.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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 3ea1626385fd74..a3d1ab789ba57e 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -557,6 +557,9 @@ internal object ThrownException internal volatile UIntPtr _handlingFrameSP; #if TARGET_ARM64 + // On ARM64, two frames can have the same SP, when a leaf function + // doesn't use any stack. So to distinguish between the caller frame + // and the leaf one, we also need to know the PC of the handling frame. [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFramePC)] internal volatile byte* _handlingFramePC; #endif diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 5db67a6a844034..823f91dd7bd29d 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -3402,7 +3402,7 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterato } // The doNotCalculateCatchType option makes the function skip calculation of the catch type. It is used in -// the 2nd path of EH to avoid possible GC stemming from a call to ResolveEHClause. +// the 2nd pass of EH to avoid possible GC stemming from a call to ResolveEHClause. CLR_BOOL EHEnumNextWorker(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause, bool doNotCalculateCatchType = false) { CLR_BOOL result = FALSE;