From 7fc9c86e46890058a902dc00670f0ef2e18f225c Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 30 Sep 2025 01:13:02 +0200 Subject: [PATCH 1/3] [release/10.0] 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. --- .../Runtime/ExceptionServices/AsmOffsets.cs | 59 +- .../ExceptionServices/InternalCalls.cs | 10 - .../src/System/Runtime/ExceptionHandling.cs | 138 ++-- src/coreclr/vm/corelib.h | 1 - src/coreclr/vm/excep.cpp | 39 +- src/coreclr/vm/exceptionhandling.cpp | 592 +++++++++++++----- src/coreclr/vm/exceptionhandling.h | 7 +- src/coreclr/vm/exceptionhandlingqcalls.h | 3 - src/coreclr/vm/exinfo.cpp | 3 +- src/coreclr/vm/exinfo.h | 17 +- src/coreclr/vm/gcenv.ee.common.cpp | 48 +- src/coreclr/vm/metasig.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 3 - src/coreclr/vm/stackwalk.cpp | 93 +-- src/coreclr/vm/stackwalk.h | 42 +- 15 files changed, 549 insertions(+), 507 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 86cb928e36e89d..4b9dae8a00dbf7 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 @@ -217,6 +217,23 @@ 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 +#if TARGET_ARM64 + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x68; + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x70; +#else // TARGET_ARM64 + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x60; + public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x68; +#endif // TARGET_ARM64 +#endif // TARGET_UNIX + #else // TARGET_64BIT public const int SIZEOF__EHEnum = 0x10; public const int OFFSETOF__StackFrameIterator__m_pRegDisplay = 0x14; @@ -228,6 +245,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 @@ -268,8 +293,18 @@ class AsmOffsets static_assert_no_msg(offsetof(ExInfo, m_idxCurClause) == OFFSETOF__ExInfo__m_idxCurClause); static_assert_no_msg(offsetof(ExInfo, m_frameIter) == OFFSETOF__ExInfo__m_frameIter); static_assert_no_msg(offsetof(ExInfo, m_notifyDebuggerSP) == OFFSETOF__ExInfo__m_notifyDebuggerSP); + static_assert_no_msg(offsetof(ExInfo, m_pCatchHandler) == OFFSETOF__ExInfo__m_pCatchHandler); + static_assert_no_msg(offsetof(ExInfo, m_handlingFrameSP) == OFFSETOF__ExInfo__m_handlingFrameSP); +#if TARGET_ARM64 + static_assert_no_msg(offsetof(ExInfo, m_handlingFramePC) == OFFSETOF__ExInfo__m_handlingFramePC); #endif +#if TARGET_UNIX + static_assert_no_msg(offsetof(ExInfo, m_propagateExceptionCallback) == OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback); + static_assert_no_msg(offsetof(ExInfo, m_propagateExceptionContext) == OFFSETOF__ExInfo__m_pReversePInvokePropagationContext); +#endif + +#endif } #if __cplusplus ; 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..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,16 +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 = "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 75e8b469d6f686..93e1ba2af221cd 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -550,6 +550,30 @@ 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; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFrameSP)] + 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 + +#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 } // @@ -637,7 +661,9 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) exInfo.Init(exceptionToThrow!, instructionFault); DispatchEx(ref exInfo._frameIter, ref exInfo); +#if NATIVEAOT FallbackFailFast(RhFailFastReason.InternalError, null); +#endif } private const uint MaxTryRegionIdx = 0xFFFFFFFFu; @@ -670,68 +696,11 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) exInfo.Init(exceptionObj); DispatchEx(ref exInfo._frameIter, ref exInfo); +#if NATIVEAOT FallbackFailFast(RhFailFastReason.InternalError, null); +#endif } -#if !NATIVEAOT - 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")] #endif @@ -757,7 +726,9 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) exInfo.Init(rethrownException, ref activeExInfo); DispatchEx(ref exInfo._frameIter, ref exInfo); +#if NATIVEAOT FallbackFailFast(RhFailFastReason.InternalError, null); +#endif } [StackTraceHidden] @@ -877,6 +848,21 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn 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 = prevOriginalPC; +#endif +#if TARGET_UNIX + exInfo._pReversePInvokePropagationCallback = pReversePInvokePropagationCallback; + exInfo._pReversePInvokePropagationContext = pReversePInvokePropagationContext; +#endif // TARGET_UNIX + exInfo._idxCurClause = catchingTryRegionIdx; + + return; + +#else // !NATIVEAOT // ------------------------------------------------ // // Second pass @@ -888,9 +874,10 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn // '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 + + exInfo._passNumber = 2; exInfo._idxCurClause = catchingTryRegionIdx; startIdx = MaxTryRegionIdx; @@ -910,10 +897,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn 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. @@ -937,36 +922,26 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn #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); } #endif // FEATURE_OBJCMARSHAL - // ------------------------------------------------ // // Call the handler and resume execution // // ------------------------------------------------ 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")] @@ -1170,6 +1145,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); @@ -1207,11 +1183,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 @@ -1242,19 +1214,11 @@ 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; } } -#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 e188c5e1dff185..6de72c8918fa0a 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1357,7 +1357,6 @@ 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, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_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 08620b3c715375..1829b5b16c8b2a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6191,7 +6191,10 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) //Ex.RhThrowHwEx(exceptionCode, &exInfo) CALL_MANAGED_METHOD_NORET(args) + DispatchExSecondPass(&exInfo); + GCPROTECT_END(); + UNREACHABLE(); } #endif // USE_FEF && !TARGET_UNIX @@ -7276,42 +7279,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 818f5dbef72639..a4ccb5e1988b06 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -786,62 +786,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 @@ -1512,6 +1456,8 @@ BOOL HandleHardwareException(PAL_SEHException* ex) //Ex.RhThrowHwEx(exceptionCode, &exInfo) CALL_MANAGED_METHOD_NORET(args) + DispatchExSecondPass(&exInfo); + GCPROTECT_END(); UNREACHABLE(); @@ -1653,6 +1599,8 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE CRITICAL_CALLSITE; CALL_MANAGED_METHOD_NORET(args) + DispatchExSecondPass(&exInfo); + GCPROTECT_END(); GCPROTECT_END(); @@ -1707,6 +1655,8 @@ VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionConte //Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) CALL_MANAGED_METHOD_NORET(args) + DispatchExSecondPass(&exInfo); + GCPROTECT_END(); UNREACHABLE(); @@ -2969,14 +2919,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) { @@ -3120,18 +3062,19 @@ 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_TRIGGERS; + THROWS; + } + CONTRACTL_END; + 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; @@ -3157,7 +3100,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()); @@ -3303,22 +3245,17 @@ 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) +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(); @@ -3363,19 +3300,16 @@ 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; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; - BEGIN_QCALL; - 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); MethodDesc *pMD = exInfo->m_frameIter.m_crawl.GetFunction(); // Profiler, debugger and ETW events @@ -3383,13 +3317,14 @@ extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvReg exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); EH_LOG((LL_INFO100, "Calling finally funclet at %p\n", pHandlerIP)); + ENDFORBIDGC(); exInfo->m_frameIter.m_crawl.GetCodeManager()->CallFunclet(NULL, pHandlerIP, pvRegDisplay, exInfo, false /* isFilterFunclet */); + BEGINFORBIDGC(); pThread->IncPreventAbort(); // 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) @@ -3465,16 +3400,12 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterato return TRUE; } -extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) +// The doNotCalculateCatchType option makes the function skip calculation of the catch type. It is used in +// 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) { - 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; @@ -3513,9 +3444,17 @@ extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClau 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) { @@ -3529,8 +3468,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 { @@ -3551,6 +3490,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; @@ -3738,24 +3694,26 @@ 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; + CONTRACTL + { + MODE_COOPERATIVE; + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; 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(); @@ -3833,8 +3791,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; @@ -3878,6 +3834,24 @@ 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); + + GCX_COOP(); + result = SfiInitWorker(pThis, pStackwalkCtx, instructionFault, pfIsExceptionIntercepted); + END_QCALL; + + return result; +} + static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrameIterator) { StackWalkAction retVal; @@ -3895,19 +3869,22 @@ 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; + CONTRACTL + { + MODE_COOPERATIVE; + THROWS; + GC_TRIGGERS; + } + CONTRACTL_END; 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. @@ -4028,6 +4005,7 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid { #ifdef HOST_WINDOWS GetThread()->SetThreadStateNC(Thread::TSNC_SkipManagedPersonalityRoutine); + 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); @@ -4053,74 +4031,53 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid 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 } } @@ -4143,7 +4100,6 @@ extern "C" CLR_BOOL QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollid } Exit:; - END_QCALL; if (retVal != SWA_FAILED) { @@ -4173,6 +4129,300 @@ 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); + + GCX_COOP(); + result = SfiNextWorker(pThis, uExCollideClauseIdx, fUnwoundReversePInvoke, pfIsExceptionIntercepted); + + END_QCALL; + + return result; +} + +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)) + { + codeOffset = (uint32_t)(pMethodRegionInfo->hotSize + (size_t)(pbControlPC - pMethodRegionInfo->coldStartAddress)); + } + + return codeOffset; +} + +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_TRIGGERS; + } + CONTRACTL_END; + + 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(); + + 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, /* doNotCalculateCatchType */ 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. + + byte* pFinallyHandler = ehClause._handlerAddress; + pExInfo->m_idxCurClause = curIdx; + CallFinallyFunclet(pFinallyHandler, pExInfo->m_frameIter.m_crawl.GetRegisterSet(), pExInfo); + pExInfo->m_idxCurClause = MaxTryRegionIdx; + } + + ENDFORBIDGC(); +} + +static void InvokeSecondPass(ExInfo *pExInfo, uint idxStart) +{ + CONTRACTL + { + MODE_COOPERATIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + InvokeSecondPass(pExInfo, idxStart, MaxTryRegionIdx); +} + +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo) +{ + CONTRACTL + { + MODE_COOPERATIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + 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; + 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"); + _ASSERTE_MSG(GetControlPC(pFrameIter->m_crawl.GetRegisterSet()) != 0, "IP address must not be null"); + + if (isExceptionIntercepted) + { + pCatchHandler = 0; + 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()) == handlingFramePC) +#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(pExInfo->m_exception, (BYTE *)pCatchHandler, pFrameIter->m_crawl.GetRegisterSet(), pExInfo); + // CallCatchFunclet will resume after the catch and never return here. + UNREACHABLE(); +} + +// +// 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()) != 0, "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(); +} + 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..20503d2f83db99 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -34,6 +34,9 @@ VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); +struct ExInfo; +void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo); + enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; enum TrackerMemoryType @@ -58,12 +61,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 fd1feccef6cb91..c9dd9ba353250b 100644 --- a/src/coreclr/vm/exceptionhandlingqcalls.h +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -12,10 +12,7 @@ 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); 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/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 41c90561aa5b06..916910fb77e187 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -324,8 +324,7 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx m_propagateExceptionContext(NULL), #endif // HOST_UNIX m_CurrentClause({}), - m_pMDToReportFunctionLeave(NULL), - m_lastReportedFunclet({0, 0, 0}) + m_pMDToReportFunctionLeave(NULL) #ifdef HOST_WINDOWS , m_pLongJmpBuf(NULL), m_longJmpReturnValue(0) diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index 6c17d51cd2c289..16c99a6ad70c64 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 @@ -315,6 +321,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. @@ -333,8 +348,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 0df511d2eadcbe..a1ea5fbf365f83 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 4d6e8666749c2b..ddb9b5ea403e63 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -186,7 +186,6 @@ 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(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 DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I)) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e2dd498b8b483a..19cb4a2a58c500 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -532,10 +532,7 @@ 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) diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 3376d945e0ca91..c43821f875a2d2 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1049,9 +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; #if defined(RECORD_RESUMABLE_FRAME_SP) m_pvResumableFrameTargetSP = NULL; @@ -1399,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; @@ -1624,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; @@ -1659,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; @@ -1770,14 +1736,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"); - } } } } @@ -1817,26 +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; - } - - 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 @@ -2029,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 @@ -2104,7 +2034,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 +2047,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 +2140,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..ddf7158a62eee9 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; @@ -611,6 +597,11 @@ class StackFrameIterator m_AdjustedControlPC = pc; } + TADDR GetAdjustedControlPC() + { + return m_AdjustedControlPC; + } + void UpdateIsRuntimeWrappedExceptions() { CONTRACTL @@ -651,23 +642,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,12 +734,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 - bool m_fFuncletNotSeen; // Indicates that the stack walk has moved past a funclet bool m_fFoundFirstFunclet; #ifdef FEATURE_INTERPRETER From 288916dcc6dca34b26f7692d0f606ee0549269cd Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 1 Oct 2025 22:01:41 +0200 Subject: [PATCH 2/3] Disable interpreter test for .NET 10 --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 374d96e1acf3f3..7a00fedb7537f0 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -60,6 +60,9 @@ https://github.com/dotnet/runtime/issues/55966 + + Disabled for .NET 10 due to a backported fix that would require backporting another PR to make the interpreter test work with it. + https://github.com/dotnet/runtime/issues/57786 From d6018153b6c576e070a11d352599c8e844ce0e66 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 3 Oct 2025 18:21:53 +0200 Subject: [PATCH 3/3] Fix casing of path in the interpreter test disabling --- src/tests/issues.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 7a00fedb7537f0..e03d287ea4e04a 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -60,7 +60,7 @@ https://github.com/dotnet/runtime/issues/55966 - + Disabled for .NET 10 due to a backported fix that would require backporting another PR to make the interpreter test work with it.