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 6a6a9a332d59e..4ae608fc17d23 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 @@ -14,11 +14,14 @@ internal static partial class InternalCalls { [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "SfiInit")] [return: MarshalAs(UnmanagedType.Bool)] - internal static unsafe partial bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, [MarshalAs(UnmanagedType.Bool)] bool instructionFault); + internal static unsafe partial bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, [MarshalAs(UnmanagedType.Bool)] bool instructionFault, bool* fIsExceptionIntercepted); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "SfiNext")] [return: MarshalAs(UnmanagedType.Bool)] - internal static unsafe partial bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); + 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); #pragma warning disable CS8500 [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallCatchFunclet")] diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index 8a705996d3e82..0707a72536997 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -266,10 +266,9 @@ BOOL DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle) { // Skip the new exception handling managed code, the debugger clients are not supposed to see them MethodDesc *pMD = pIter->m_crawl.GetFunction(); - PTR_MethodDesc ptrMD = dac_cast(pMD); - // EH.DispatchEx, EH.RhThrowEx, EH.RhThrowHwEx - if (ptrMD->GetMethodTable() == g_pEHClass) + // EH.DispatchEx, EH.RhThrowEx, EH.RhThrowHwEx, ExceptionServices.InternalCalls.SfiInit, ExceptionServices.InternalCalls.SfiNext + if (pMD->GetMethodTable() == g_pEHClass || pMD->GetMethodTable() == g_pExceptionServicesInternalCallsClass) { continue; } @@ -375,8 +374,21 @@ IDacDbiInterface::FrameType DacDbiInterfaceImpl::GetStackWalkCurrentFrameInfo(St break; case StackFrameIterator::SFITER_FRAMELESS_METHOD: - ftResult = kManagedStackFrame; - fInitFrameData = TRUE; + { +#ifdef FEATURE_EH_FUNCLETS + MethodDesc *pMD = pIter->m_crawl.GetFunction(); + // EH.DispatchEx, EH.RhThrowEx, EH.RhThrowHwEx, ExceptionServices.InternalCalls.SfiInit, ExceptionServices.InternalCalls.SfiNext + if (pMD->GetMethodTable() == g_pEHClass || pMD->GetMethodTable() == g_pExceptionServicesInternalCallsClass) + { + ftResult = kManagedExceptionHandlingCodeFrame; + } + else +#endif // FEATURE_EH_FUNCLETS + { + ftResult = kManagedStackFrame; + fInitFrameData = TRUE; + } + } break; case StackFrameIterator::SFITER_FRAME_FUNCTION: diff --git a/src/coreclr/debug/di/rsstackwalk.cpp b/src/coreclr/debug/di/rsstackwalk.cpp index 213c2fe68b3e8..751d18dcc1797 100644 --- a/src/coreclr/debug/di/rsstackwalk.cpp +++ b/src/coreclr/debug/di/rsstackwalk.cpp @@ -595,6 +595,11 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - native stack frame (%p)", this); return S_FALSE; } + else if (ft == IDacDbiInterface::kManagedExceptionHandlingCodeFrame) + { + STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - managed exception handling code frame (%p)", this); + return S_FALSE; + } else if (ft == IDacDbiInterface::kExplicitFrame) { STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - explicit frame (%p)", this); diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 5edff15a9f7ed..1c689b8eee542 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -6142,6 +6142,18 @@ bool DebuggerStepper::IsInterestingFrame(FrameInfo * pFrame) { LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_EH_FUNCLETS + // Ignore managed exception handling frames + if (pFrame->md != NULL) + { + MethodTable *pMT = pFrame->md->GetMethodTable(); + if ((pMT == g_pEHClass) || (pMT == g_pExceptionServicesInternalCallsClass)) + { + return false; + } + } +#endif // FEATURE_EH_FUNCLETS + return true; } diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 690c06b4950d9..d4bc6f5dd87a5 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -63,8 +63,6 @@ SVAL_IMPL_INIT(BOOL, Debugger, s_fCanChangeNgenFlags, TRUE); // process is waiting for JIT debugging attach. GVAL_IMPL_INIT(ULONG, CLRJitAttachState, 0); -bool g_EnableSIS = false; - // The following instances are used for invoking overloaded new/delete InteropSafe interopsafe; @@ -1830,9 +1828,6 @@ HRESULT Debugger::Startup(void) { DebuggerLockHolder dbgLockHolder(this); - // Stubs in Stacktraces are always enabled. - g_EnableSIS = true; - // We can get extra Interop-debugging test coverage by having some auxiliary unmanaged // threads running and throwing debug events. Keep these stress procs separate so that // we can focus on certain problem areas. @@ -7960,12 +7955,6 @@ LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID p #endif } - // @todo - when Stubs-In-Stacktraces is always enabled, remove this. - if (!g_EnableSIS) - { - return EXCEPTION_CONTINUE_SEARCH; - } - // Stubs don't have an IL offset. const SIZE_T offset = (SIZE_T)ICorDebugInfo::NO_MAPPING; Thread *pThread = GetThread(); diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 3652d15ef5391..5b809d9795f0d 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1332,6 +1332,7 @@ class IDacDbiInterface kExplicitFrame, kNativeStackFrame, kNativeRuntimeUnwindableStackFrame, + kManagedExceptionHandlingCodeFrame, kAtEndOfStack, } FrameType; diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 5b77893be7f3b..b632887e86d0f 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -115,6 +115,8 @@ DEFINE_DACVAR(DWORD, dac__g_TlsIndex, g_TlsIndex) #ifdef FEATURE_EH_FUNCLETS DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pEHClass, ::g_pEHClass) +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pExceptionServicesInternalCallsClass, ::g_pExceptionServicesInternalCallsClass) +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pStackFrameIteratorClass, ::g_pStackFrameIteratorClass) DEFINE_DACVAR(BOOL, dac__g_isNewExceptionHandlingEnabled, ::g_isNewExceptionHandlingEnabled) #endif 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 6237a54f13ba8..fac623a25cbaf 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -150,9 +150,9 @@ private struct OSCONTEXT #endif } +#if NATIVEAOT private static void OnFirstChanceExceptionViaClassLib(object exception) { -#if NATIVEAOT IntPtr pOnFirstChanceFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(exception.GetMethodTable(), ClassLibFunctionId.OnFirstChance); @@ -169,17 +169,8 @@ private static void OnFirstChanceExceptionViaClassLib(object exception) { // disallow all exceptions leaking out of callbacks } -#else - try - { - AppContext.OnFirstChanceException(exception); - } - catch when (true) - { - // disallow all exceptions leaking out of callbacks - } -#endif } +#endif // NATIVEAOT private static void OnUnhandledExceptionViaClassLib(object exception) { @@ -642,6 +633,66 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) DispatchEx(ref exInfo._frameIter, ref exInfo); FallbackFailFast(RhFailFastReason.InternalError, null); } +#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; +#pragma warning disable CS8500 + fixed (EH.ExInfo* pExInfo = &exInfo) + { + InternalCalls.RhpCallCatchFunclet( + ObjectHandleOnStack.Create(ref exceptionObj), null, exInfo._frameIter.RegisterSet, pExInfo); + } +#pragma warning restore CS8500 + } + else + { + InternalCalls.ResumeAtInterceptionLocation(exInfo._frameIter.RegisterSet); + } + + Debug.Assert(false, "unreachable"); + FallbackFailFast(RhFailFastReason.InternalError, null); + } +#endif // !NATIVEAOT + #if NATIVEAOT [RuntimeExport("RhRethrow")] @@ -679,6 +730,7 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn bool isFirstRethrowFrame = (exInfo._kind & ExKind.RethrowFlag) != 0; bool isFirstFrame = true; + bool isExceptionIntercepted = false; byte* prevControlPC = null; byte* prevOriginalPC = null; @@ -687,19 +739,25 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn IntPtr pReversePInvokePropagationCallback = IntPtr.Zero; IntPtr pReversePInvokePropagationContext = IntPtr.Zero; - bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); + bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted); Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); +#if NATIVEAOT OnFirstChanceExceptionViaClassLib(exceptionObj); - +#endif uint startIdx = MaxTryRegionIdx; - for (; isValid; isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke)) + for (; isValid; isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke, &isExceptionIntercepted)) { // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we // disallow dispatching exceptions across native code. if (unwoundReversePInvoke) break; + if (isExceptionIntercepted) + { + break; + } + prevControlPC = frameIter.ControlPC; prevOriginalPC = frameIter.OriginalControlPC; @@ -746,7 +804,7 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn #endif // !NATIVEAOT } - if (pCatchHandler == null && pReversePInvokePropagationCallback == IntPtr.Zero + if (pCatchHandler == null && pReversePInvokePropagationCallback == IntPtr.Zero && !isExceptionIntercepted #if !NATIVEAOT && !unwoundReversePInvoke #endif @@ -763,7 +821,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn // We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass // without a catch handler or propagation callback. - Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke, "We should have a handler if we're starting the second pass"); + 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"); // ------------------------------------------------ // @@ -783,12 +842,19 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn exInfo._idxCurClause = catchingTryRegionIdx; startIdx = MaxTryRegionIdx; unwoundReversePInvoke = false; - isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); - for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke)) + 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"); DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); + if (isExceptionIntercepted) + { + pCatchHandler = null; + break; + } + if (unwoundReversePInvoke) { #if NATIVEAOT @@ -957,19 +1023,20 @@ private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP) byte* pFilterFunclet = ehClause._filterAddress; bool shouldInvokeHandler = false; +#if NATIVEAOT try { shouldInvokeHandler = -#if NATIVEAOT InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); -#else - InternalCalls.RhpCallFilterFunclet(ObjectHandleOnStack.Create(ref exception), pFilterFunclet, frameIter.RegisterSet); -#endif } catch when (true) { // Prevent leaking any exception from the filter funclet } +#else // NATIVEAOT + shouldInvokeHandler = + InternalCalls.RhpCallFilterFunclet(ObjectHandleOnStack.Create(ref exception), pFilterFunclet, frameIter.RegisterSet); +#endif // NATIVEAOT if (shouldInvokeHandler) { diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs index f39cce128aa99..9eb227e70f201 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -209,11 +209,11 @@ internal static int RhEndNoGCRegion() [RuntimeImport(Redhawk.BaseName, "RhpSfiInit")] [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, bool instructionFault); + internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, bool instructionFault, bool* fIsExceptionIntercepted); [RuntimeImport(Redhawk.BaseName, "RhpSfiNext")] [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); + internal static extern unsafe bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* fIsExceptionIntercepted); // // Miscellaneous helpers. diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs index a42274c4bb9c3..f758b16b5d22a 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs @@ -60,24 +60,24 @@ internal unsafe struct StackFrameIterator #pragma warning restore CA1822 #endif // NATIVEAOT - internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx, bool instructionFault = false) + internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx, bool instructionFault = false, bool* fIsExceptionIntercepted = null) { - return InternalCalls.RhpSfiInit(ref this, pStackwalkCtx, instructionFault); + return InternalCalls.RhpSfiInit(ref this, pStackwalkCtx, instructionFault, fIsExceptionIntercepted); } internal bool Next() { - return Next(null, null); + return Next(null, null, null); } - internal bool Next(uint* uExCollideClauseIdx) + internal bool Next(uint* uExCollideClauseIdx, bool* fIsExceptionIntercepted) { - return Next(uExCollideClauseIdx, null); + return Next(uExCollideClauseIdx, null, fIsExceptionIntercepted); } - internal bool Next(uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) + internal bool Next(uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* fIsExceptionIntercepted) { - return InternalCalls.RhpSfiNext(ref this, uExCollideClauseIdx, fUnwoundReversePInvoke); + return InternalCalls.RhpSfiNext(ref this, uExCollideClauseIdx, fUnwoundReversePInvoke, fIsExceptionIntercepted); } } } diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 61354586ba49b..6e5b0fe1a0c13 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -1846,7 +1846,7 @@ bool StackFrameIterator::ShouldSkipRegularGcReporting() #ifndef DACCESS_COMPILE -COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiInit, (StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault)) +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiInit, (StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted)) { Thread * pCurThread = ThreadStore::GetCurrentThread(); @@ -1865,10 +1865,16 @@ COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiInit, (StackFrameIterator* pThis, PAL_LIM bool isValid = pThis->IsValid(); if (isValid) pThis->CalculateCurrentMethodState(); + + if (pfIsExceptionIntercepted) + { + *pfIsExceptionIntercepted = false; + } + FC_RETURN_BOOL(isValid); } -COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiNext, (StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke)) +COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiNext, (StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted)) { // The stackwalker is intolerant to hijacked threads, as it is largely expecting to be called from C++ // where the hijack state of the thread is invariant. Because we've exposed the iterator out to C#, we @@ -1903,6 +1909,11 @@ COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpSfiNext, (StackFrameIterator* pThis, uint32_ *pfUnwoundReversePInvoke = (pThis->m_dwFlags & StackFrameIterator::UnwoundReversePInvoke) != 0; } + if (pfIsExceptionIntercepted) + { + *pfIsExceptionIntercepted = false; + } + FC_RETURN_BOOL(isValid); } diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index 74fde58307e74..dc7cdca795cc1 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -28,8 +28,8 @@ struct EHEnum }; class StackFrameIterator; -EXTERN_C FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault); -EXTERN_C FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke); +EXTERN_C FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted); +EXTERN_C FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted); struct PInvokeTransitionFrame; typedef DPTR(PInvokeTransitionFrame) PTR_PInvokeTransitionFrame; @@ -38,8 +38,8 @@ typedef DPTR(PAL_LIMITED_CONTEXT) PTR_PAL_LIMITED_CONTEXT; class StackFrameIterator { friend class AsmOffsets; - friend FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault); - friend FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke); + friend FC_BOOL_RET FASTCALL RhpSfiInit(StackFrameIterator* pThis, PAL_LIMITED_CONTEXT* pStackwalkCtx, CLR_BOOL instructionFault, CLR_BOOL* pfIsExceptionIntercepted); + friend FC_BOOL_RET FASTCALL RhpSfiNext(StackFrameIterator* pThis, uint32_t* puExCollideClauseIdx, CLR_BOOL* pfUnwoundReversePInvoke, CLR_BOOL* pfIsExceptionIntercepted); public: StackFrameIterator() {} diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 51aec1b9e1c69..2bfdd6cf29422 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1401,6 +1401,8 @@ void SystemDomain::LoadBaseSystemClasses() #ifdef FEATURE_EH_FUNCLETS g_pEHClass = CoreLibBinder::GetClass(CLASS__EH); + g_pExceptionServicesInternalCallsClass = CoreLibBinder::GetClass(CLASS__EXCEPTIONSERVICES_INTERNALCALLS); + g_pStackFrameIteratorClass = CoreLibBinder::GetClass(CLASS__STACKFRAMEITERATOR); #endif // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 9d091de403ee1..c2c4af97b6768 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1187,6 +1187,9 @@ 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 #ifndef FOR_ILLINK diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a5596604aa35e..3ff8eeb701547 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -2853,7 +2853,10 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow) // a good thing to preserve the stack trace. if (!rethrow) { + Thread *pThread = GetThread(); + pThread->IncPreventAbort(); ExceptionPreserveStackTrace(throwable); + pThread->DecPreventAbort(); } RealCOMPlusThrowWorker(throwable, rethrow); @@ -3415,20 +3418,6 @@ BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT return bRetVal; } -void StackTraceInfo::GetLeafFrameInfo(StackTraceElement* pStackTraceElement) -{ - LIMITED_METHOD_CONTRACT; - - if (NULL == m_pStackTrace) - { - return; - } - _ASSERTE(NULL != pStackTraceElement); - - *pStackTraceElement = m_pStackTrace[0]; -} - - void UnwindFrameChain(Thread* pThread, LPVOID pvLimitSP) { CONTRACTL @@ -6577,12 +6566,9 @@ void HandleManagedFaultNew(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext pContext->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; frame->InitAndLink(pContext); - CONTEXT ctx = {}; - ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - REGDISPLAY rd; Thread *pThread = GetThread(); - ExInfo exInfo(pThread, &ctx, &rd, ExKind::HardwareFault); + ExInfo exInfo(pThread, pExceptionRecord, pContext, ExKind::HardwareFault); DWORD exceptionCode = pExceptionRecord->ExceptionCode; if (exceptionCode == STATUS_ACCESS_VIOLATION) @@ -8673,7 +8659,11 @@ void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject) // But if the tracker exists, simply copy the bucket details to the UE Watson Bucket // tracker for use by the "WatsonLastChance" path. BOOL fDoWeHaveWatsonBuckets = FALSE; - if (pExState->GetCurrentExceptionTracker() != NULL) + if ((pExState->GetCurrentExceptionTracker() != NULL) +#ifdef FEATURE_EH_FUNCLETS + || (pExState->GetCurrentExInfo() != NULL) +#endif // FEATURE_EH_FUNCLETS + ) { // Check the exception state if we have Watson bucket details fDoWeHaveWatsonBuckets = pExState->GetFlags()->GotWatsonBucketDetails(); @@ -10605,7 +10595,7 @@ BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, OBJECTREF throwable { NOTHROW; GC_NOTRIGGER; - MODE_COOPERATIVE; + if (throwable != NULL) MODE_COOPERATIVE; else MODE_ANY; } CONTRACTL_END; @@ -10924,8 +10914,18 @@ void ExceptionNotifications::DeliverFirstChanceNotification() // processing for subsequent frames on the stack since FirstChance notification // will be delivered only when the exception is first thrown/rethrown. ThreadExceptionState *pCurTES = GetThread()->GetExceptionState(); - _ASSERTE(pCurTES->GetCurrentExceptionTracker()); - _ASSERTE(!(pCurTES->GetCurrentExceptionTracker()->DeliveredFirstChanceNotification())); +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + _ASSERTE(pCurTES->GetCurrentExInfo()); + _ASSERTE(!(pCurTES->GetCurrentExInfo()->DeliveredFirstChanceNotification())); + } + else +#endif // FEATURE_EH_FUNCLETS + { + _ASSERTE(pCurTES->GetCurrentExceptionTracker()); + _ASSERTE(!(pCurTES->GetCurrentExceptionTracker()->DeliveredFirstChanceNotification())); + } { GCX_COOP(); if (ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(FirstChanceExceptionHandler)) @@ -10941,8 +10941,18 @@ void ExceptionNotifications::DeliverFirstChanceNotification() } - // Mark the exception tracker as having delivered the first chance notification - pCurTES->GetCurrentExceptionTracker()->SetFirstChanceNotificationStatus(TRUE); +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + // Mark the exception info as having delivered the first chance notification + pCurTES->GetCurrentExInfo()->SetFirstChanceNotificationStatus(TRUE); + } + else +#endif // FEATURE_EH_FUNCLETS + { + // Mark the exception tracker as having delivered the first chance notification + pCurTES->GetCurrentExceptionTracker()->SetFirstChanceNotificationStatus(TRUE); + } } } diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index a1cc0556b0d2a..9acd2b945c406 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -587,6 +587,7 @@ UINT GetResourceIDForFileLoadExceptionHR(HRESULT hr); #define EXCEPTION_NESTED_CALL 0x10 // Nested exception handler call #define EXCEPTION_TARGET_UNWIND 0x20 // Target unwind in progress #define EXCEPTION_COLLIDED_UNWIND 0x40 // Collided exception handler call +#define EXCEPTION_SOFTWARE_ORIGINATE 0x80 // Exception originated in software #define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | \ EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 6b3ab03b3f843..0325ec53c138a 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -884,6 +884,12 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, #ifndef HOST_UNIX if (!(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) { + // Failfast if exception indicates corrupted process state + if (IsProcessCorruptedStateException(pExceptionRecord->ExceptionCode, /* throwable */ NULL)) + { + EEPOLICY_HANDLE_FATAL_ERROR(pExceptionRecord->ExceptionCode); + } + Thread* pThread = GetThread(); ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, @@ -2300,6 +2306,38 @@ bool ExceptionTracker::HandleNestedExceptionEscape(StackFrame sf, bool fIsFirstP return fResult; } +#if defined(DEBUGGING_SUPPORTED) +BOOL NotifyDebuggerOfStub(Thread* pThread, Frame* pCurrentFrame) +{ + LIMITED_METHOD_CONTRACT; + + BOOL fDeliveredFirstChanceNotification = FALSE; + + _ASSERTE(GetThreadNULLOk() == pThread); + + GCX_COOP(); + + // For debugger, we may want to notify 1st chance exceptions if they're coming out of a stub. + // We recognize stubs as Frames with a M2U transition type. The debugger's stackwalker also + // recognizes these frames and publishes ICorDebugInternalFrames in the stackwalk. It's + // important to use pFrame as the stack address so that the Exception callback matches up + // w/ the ICorDebugInternalFrame stack range. + if (CORDebuggerAttached()) + { + if (pCurrentFrame->GetTransitionType() == Frame::TT_M2U) + { + // Use -1 for the backing store pointer whenever we use the address of a frame as the stack pointer. + EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, + (SIZE_T)0, + (SIZE_T)pCurrentFrame); + fDeliveredFirstChanceNotification = TRUE; + } + } + + return fDeliveredFirstChanceNotification; +} +#endif // DEBUGGING_SUPPORTED + CLRUnwindStatus ExceptionTracker::ProcessExplicitFrame( CrawlFrame* pcfThisFrame, StackFrame sf, @@ -2379,7 +2417,7 @@ CLRUnwindStatus ExceptionTracker::ProcessExplicitFrame( // make callback to debugger and/or profiler // #if defined(DEBUGGING_SUPPORTED) - if (ExceptionTracker::NotifyDebuggerOfStub(pThread, sf, pFrame)) + if (NotifyDebuggerOfStub(pThread, pFrame)) { // Deliver the FirstChanceNotification after the debugger, if not already delivered. if (!this->DeliveredFirstChanceNotification()) @@ -4105,43 +4143,6 @@ BOOL ExceptionTracker::ClauseCoversPC( } #if defined(DEBUGGING_SUPPORTED) -BOOL ExceptionTracker::NotifyDebuggerOfStub(Thread* pThread, StackFrame sf, Frame* pCurrentFrame) -{ - LIMITED_METHOD_CONTRACT; - - BOOL fDeliveredFirstChanceNotification = FALSE; - - // - // Remove this once SIS is fully enabled. - // - extern bool g_EnableSIS; - - if (g_EnableSIS) - { - _ASSERTE(GetThreadNULLOk() == pThread); - - GCX_COOP(); - - // For debugger, we may want to notify 1st chance exceptions if they're coming out of a stub. - // We recognize stubs as Frames with a M2U transition type. The debugger's stackwalker also - // recognizes these frames and publishes ICorDebugInternalFrames in the stackwalk. It's - // important to use pFrame as the stack address so that the Exception callback matches up - // w/ the ICorDebugInternlFrame stack range. - if (CORDebuggerAttached()) - { - if (pCurrentFrame->GetTransitionType() == Frame::TT_M2U) - { - // Use -1 for the backing store pointer whenever we use the address of a frame as the stack pointer. - EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, - (SIZE_T)0, - (SIZE_T)pCurrentFrame); - fDeliveredFirstChanceNotification = TRUE; - } - } - } - - return fDeliveredFirstChanceNotification; -} bool ExceptionTracker::IsFilterStartOffset(EE_ILEXCEPTION_CLAUSE* pEHClause, DWORD_PTR dwHandlerStartPC) { @@ -4266,17 +4267,35 @@ EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_RE (PBYTE*)&uInterceptStackFrame, NULL, NULL); + if (g_isNewExceptionHandlingEnabled) + { + GCX_COOP(); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__UNWIND_AND_INTERCEPT); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(pExState->GetCurrentExInfo()); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(uInterceptStackFrame); + pThread->IncPreventAbort(); + + //Ex.RhUnwindAndIntercept(throwable, &exInfo) + CRITICAL_CALLSITE; + CALL_MANAGED_METHOD_NORET(args) + + UNREACHABLE(); + } + else + { #ifdef TARGET_UNIX - CONTEXT *pContext = pThread->GetExceptionState()->GetContextRecord(); - PAL_SEHException ex(pThread->GetExceptionState()->GetExceptionRecord(), pContext, /* onStack */ false); - ex.TargetIp = INVALID_RESUME_ADDRESS; - ex.TargetFrameSp = uInterceptStackFrame; - ex.ReturnValue = (UINT_PTR)pThread; - UnwindManagedExceptionPass2(ex, pContext); + CONTEXT *pContext = pThread->GetExceptionState()->GetContextRecord(); + PAL_SEHException ex(pThread->GetExceptionState()->GetExceptionRecord(), pContext, /* onStack */ false); + ex.TargetIp = INVALID_RESUME_ADDRESS; + ex.TargetFrameSp = uInterceptStackFrame; + ex.ReturnValue = (UINT_PTR)pThread; + UnwindManagedExceptionPass2(ex, pContext); #else // TARGET_UNIX - ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, INVALID_RESUME_ADDRESS, uInterceptStackFrame); + ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, INVALID_RESUME_ADDRESS, uInterceptStackFrame); #endif // TARGET_UNIX - + } UNREACHABLE(); } #endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED @@ -5442,12 +5461,9 @@ BOOL HandleHardwareException(PAL_SEHException* ex) if (g_isNewExceptionHandlingEnabled) { - REGDISPLAY rd; Thread *pThread = GetThread(); - CONTEXT ctx = {}; - ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - ExInfo exInfo(pThread, &ctx /*ex->GetContextRecord()*/, &rd, ExKind::HardwareFault); + ExInfo exInfo(pThread, ex->GetExceptionRecord(), ex->GetContextRecord(), ExKind::HardwareFault); DWORD exceptionCode = ex->GetExceptionRecord()->ExceptionCode; if (exceptionCode == STATUS_ACCESS_VIOLATION) @@ -5458,6 +5474,11 @@ BOOL HandleHardwareException(PAL_SEHException* ex) } } + if (!ex->RecordsOnStack) + { + exInfo.TakeExceptionPointersOwnership(ex); + } + GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); DECLARE_ARGHOLDER_ARRAY(args, 2); @@ -5523,7 +5544,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) #endif // TARGET_UNIX -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable) +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -5533,13 +5554,26 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable) _ASSERTE(IsException(throwable->GetMethodTable())); - ExceptionPreserveStackTrace(throwable); + if (preserveStackTrace) + { + ExceptionPreserveStackTrace(throwable); + } - CONTEXT ctx = {}; - ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - REGDISPLAY rd; Thread *pThread = GetThread(); - ExInfo exInfo(pThread, &ctx, &rd, ExKind::Throw); + + ULONG_PTR hr = GetHRFromThrowable(throwable); + + EXCEPTION_RECORD exceptionRecord; + exceptionRecord.ExceptionCode = EXCEPTION_COMPLUS; + exceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE; + exceptionRecord.ExceptionAddress = (void *)(void (*)(OBJECTREF, bool))&DispatchManagedException; + exceptionRecord.NumberParameters = MarkAsThrownByUs(exceptionRecord.ExceptionInformation, hr); + exceptionRecord.ExceptionRecord = NULL; + + CONTEXT exceptionContext; + RtlCaptureContext(&exceptionContext); + + ExInfo exInfo(pThread, &exceptionRecord, &exceptionContext, ExKind::Throw); if (pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&throwable)) { @@ -7527,10 +7561,43 @@ extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack e pExInfo->m_stackTraceInfo.AppendElement(canAllocateMemory, ip, sp, pMD, &pExInfo->m_frameIter.m_crawl); pExInfo->m_stackTraceInfo.SaveStackTrace(canAllocateMemory, pExInfo->m_hThrowable, /*bReplaceStack*/FALSE, /*bSkipLastElement*/FALSE); + if (!pExInfo->DeliveredFirstChanceNotification()) + { + ExceptionNotifications::DeliverFirstChanceNotification(); + } END_QCALL; } +static void PopExplicitFrames(Thread *pThread, void *targetSp) +{ + Frame* pFrame = pThread->GetFrame(); + while (pFrame < targetSp) + { + pFrame->ExceptionUnwind(); + pFrame->Pop(pThread); + pFrame = pThread->GetFrame(); + } + + GCFrame* pGCFrame = pThread->GetGCFrame(); + while (pGCFrame && pGCFrame < targetSp) + { + pGCFrame->Pop(); + pGCFrame = pThread->GetGCFrame(); + } +} + +static void PopExInfos(Thread *pThread, void *targetSp) +{ + ExInfo *pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + while (pExInfo && pExInfo < (void*)targetSp) + { + pExInfo->ReleaseResources(); + pExInfo = pExInfo->m_pPrevExInfo; + } + pThread->GetExceptionState()->SetCurrentExInfo(pExInfo); +} + UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { #ifdef HOST_AMD64 @@ -7557,6 +7624,15 @@ UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) #endif } +static TADDR GetSpForDiagnosticReporting(REGDISPLAY *pRD) +{ +#ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP + return CallerStackFrame::FromRegDisplay(pRD).SP; +#else + return GetSP(pRD->pCurrentContext); +#endif +} + extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { QCALL_CONTRACT; @@ -7586,7 +7662,8 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio MethodDesc *pMD = exInfo->m_frameIter.m_crawl.GetFunction(); // Profiler, debugger and ETW events - exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_ClauseForCatch, (DWORD_PTR)pHandlerIP, GetSP(pvRegDisplay->pCurrentContext)); + TADDR spForDebugger = GetSpForDiagnosticReporting(pvRegDisplay); + exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_ClauseForCatch, (DWORD_PTR)pHandlerIP, spForDebugger); #ifdef USE_FUNCLET_CALL_HELPER // Invoke the catch funclet. @@ -7602,25 +7679,12 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio dwResumePC = pfnHandler(establisherFrame, OBJECTREFToObject(throwable)); #endif // Profiler, debugger and ETW events - exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_ClauseForCatch, (DWORD_PTR)pHandlerIP, GetSP(pvRegDisplay->pCurrentContext)); + exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_ClauseForCatch, (DWORD_PTR)pHandlerIP, spForDebugger); SetIP(pvRegDisplay->pCurrentContext, dwResumePC); } UINT_PTR targetSp = GetSP(pvRegDisplay->pCurrentContext); - - while (pFrame < (void*)targetSp) - { - pFrame->ExceptionUnwind(); - pFrame->Pop(pThread); - pFrame = pThread->GetFrame(); - } - - GCFrame* pGCFrame = pThread->GetGCFrame(); - while (pGCFrame && pGCFrame < (void*)targetSp) - { - pGCFrame->Pop(); - pGCFrame = pThread->GetGCFrame(); - } + PopExplicitFrames(pThread, (void*)targetSp); ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); @@ -7629,31 +7693,53 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio void* propagateExceptionContext = pExInfo->m_propagateExceptionContext; #endif // HOST_UNIX - // Pop ExInfos - while (pExInfo && pExInfo < (void*)targetSp) +#ifdef DEBUGGING_SUPPORTED + // This must be done before we pop the trackers. + BOOL fIntercepted = pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo(); + if (fIntercepted) { - if (pExInfo->m_hThrowable) - { - if (!CLRException::IsPreallocatedExceptionHandle(pExInfo->m_hThrowable)) - { - DestroyHandle(pExInfo->m_hThrowable); - } - pExInfo->m_hThrowable = NULL; - } - pExInfo->m_stackTraceInfo.FreeStackTrace(); - pExInfo = pExInfo->m_pPrevExInfo; + // retrieve the interception information + MethodDesc *pInterceptMD = NULL; + StackFrame sfInterceptStackFrame; + UINT_PTR uResumePC = 0; + ULONG_PTR ulRelOffset; + + pThread->GetExceptionState()->GetDebuggerState()->GetDebuggerInterceptInfo(&pInterceptMD, NULL, (PBYTE*)&(sfInterceptStackFrame.SP), &ulRelOffset, NULL); + + PCODE pStartAddress = pInterceptMD->GetNativeCode(); + + EECodeInfo codeInfo(pStartAddress); + _ASSERTE(codeInfo.IsValid()); + + // Note that the value returned for ulRelOffset is actually the offset, + // so we need to adjust it to get the actual IP. + _ASSERTE(FitsIn(ulRelOffset)); + uResumePC = codeInfo.GetJitManager()->GetCodeAddressForRelOffset(codeInfo.GetMethodToken(), static_cast(ulRelOffset)); + + SetIP(pvRegDisplay->pCurrentContext, uResumePC); } +#endif // DEBUGGING_SUPPORTED - pThread->GetExceptionState()->SetCurrentExInfo(pExInfo); + PopExInfos(pThread, (void*)targetSp); - pThread->SafeSetLastThrownObject(NULL); + if (!pThread->GetExceptionState()->IsExceptionInProgress()) + { + pThread->SafeSetLastThrownObject(NULL); + } + + // Sync managed exception state, for the managed thread, based upon any active exception tracker + pThread->SyncManagedExceptionState(false); ExceptionTracker::UpdateNonvolatileRegisters(pvRegDisplay->pCurrentContext, pvRegDisplay, FALSE); if (pHandlerIP != NULL) { - CopyOSContext(pThread->m_OSContext, pvRegDisplay->pCurrentContext); - SetIP(pThread->m_OSContext, (PCODE)dwResumePC); - UINT_PTR uAbortAddr = (UINT_PTR)COMPlusCheckForAbort(dwResumePC); + UINT_PTR uAbortAddr = 0; + if (!fIntercepted) + { + CopyOSContext(pThread->m_OSContext, pvRegDisplay->pCurrentContext); + SetIP(pThread->m_OSContext, (PCODE)dwResumePC); + uAbortAddr = (UINT_PTR)COMPlusCheckForAbort(dwResumePC); + } if (uAbortAddr) { #ifdef TARGET_AMD64 @@ -7678,6 +7764,10 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio } else { + if (fIntercepted) + { + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + } #ifdef HOST_UNIX if (propagateExceptionCallback) { @@ -7733,6 +7823,45 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio return NULL; } +extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) +{ + Thread* pThread = GET_THREAD(); + pThread->DecPreventAbort(); + + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsFuncletCall(pFrame); + + UINT_PTR targetSp = GetSP(pvRegDisplay->pCurrentContext); + PopExplicitFrames(pThread, (void*)targetSp); + + // This must be done before we pop the ExInfos. + BOOL fIntercepted = pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo(); + _ASSERTE(fIntercepted); + + PopExInfos(pThread, (void*)targetSp); + + // retrieve the interception information + MethodDesc *pInterceptMD = NULL; + StackFrame sfInterceptStackFrame; + UINT_PTR uResumePC = 0; + ULONG_PTR ulRelOffset; + + pThread->GetExceptionState()->GetDebuggerState()->GetDebuggerInterceptInfo(&pInterceptMD, NULL, (PBYTE*)&(sfInterceptStackFrame.SP), &ulRelOffset, NULL); + + PCODE pStartAddress = pInterceptMD->GetNativeCode(); + + EECodeInfo codeInfo(pStartAddress); + _ASSERTE(codeInfo.IsValid()); + + // Note that the value returned for ulRelOffset is actually the offset, + // so we need to adjust it to get the actual IP. + _ASSERTE(FitsIn(ulRelOffset)); + uResumePC = codeInfo.GetJitManager()->GetCodeAddressForRelOffset(codeInfo.GetMethodToken(), static_cast(ulRelOffset)); + + SetIP(pvRegDisplay->pCurrentContext, uResumePC); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); +} + extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { QCALL_CONTRACT; @@ -7752,7 +7881,8 @@ extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvReg MethodDesc *pMD = exInfo->m_frameIter.m_crawl.GetFunction(); // Profiler, debugger and ETW events - exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, GetSP(pvRegDisplay->pCurrentContext)); + TADDR spForDebugger = GetSpForDiagnosticReporting(pvRegDisplay); + exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); #ifdef USE_FUNCLET_CALL_HELPER // Invoke the finally funclet. // Since the actual caller of the funclet is the assembly helper, pass the reference @@ -7770,7 +7900,7 @@ extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvReg pThread->IncPreventAbort(); // Profiler, debugger and ETW events - exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, GetSP(pvRegDisplay->pCurrentContext) ); + exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); END_QCALL; } @@ -7797,26 +7927,37 @@ extern "C" BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exception pExInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(pExInfo->m_frameIter.m_crawl.GetRegisterSet()); MethodDesc *pMD = pExInfo->m_frameIter.m_crawl.GetFunction(); // Profiler, debugger and ETW events - pExInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &pExInfo->m_CurrentClause, (DWORD_PTR)pFilterIP, GetSP(pvRegDisplay->pCurrentContext)); + TADDR spForDebugger = GetSpForDiagnosticReporting(pvRegDisplay); + pExInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &pExInfo->m_CurrentClause, (DWORD_PTR)pFilterIP, spForDebugger); + EX_TRY + { #ifdef USE_FUNCLET_CALL_HELPER - // Invoke the filter funclet. - // Since the actual caller of the funclet is the assembly helper, pass the reference - // to the CallerStackFrame instance so that it can be updated. - CallerStackFrame* pCallerStackFrame = &pExInfo->m_csfEHClause; - UINT_PTR *pFuncletCallerSP = &(pCallerStackFrame->SP); - // For invoking IL filter funclet, we pass the CallerSP to the funclet using which - // it will retrieve the framepointer for accessing the locals in the parent - // method. - dwResult = CallEHFilterFunclet(OBJECTREFToObject(throwable), - GetFrameRestoreBase(pvRegDisplay->pCallerContext), - CastHandlerFn(pfnHandler), - pFuncletCallerSP); + // Invoke the filter funclet. + // Since the actual caller of the funclet is the assembly helper, pass the reference + // to the CallerStackFrame instance so that it can be updated. + CallerStackFrame* pCallerStackFrame = &pExInfo->m_csfEHClause; + UINT_PTR *pFuncletCallerSP = &(pCallerStackFrame->SP); + // For invoking IL filter funclet, we pass the CallerSP to the funclet using which + // it will retrieve the framepointer for accessing the locals in the parent + // method. + dwResult = CallEHFilterFunclet(OBJECTREFToObject(throwable), + GetFrameRestoreBase(pvRegDisplay->pCallerContext), + CastHandlerFn(pfnHandler), + pFuncletCallerSP); #else - dwResult = pfnHandler(establisherFrame, OBJECTREFToObject(throwable)); -#endif + dwResult = pfnHandler(establisherFrame, OBJECTREFToObject(throwable)); +#endif + } + EX_CATCH + { + // Exceptions that occur in the filter funclet are swallowed and the return value is simulated + // to be false. + dwResult = EXCEPTION_CONTINUE_SEARCH; + } + EX_END_CATCH(SwallowAllExceptions); // Profiler, debugger and ETW events - pExInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &pExInfo->m_CurrentClause, (DWORD_PTR)pFilterIP, GetSP(pvRegDisplay->pCurrentContext)); + pExInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &pExInfo->m_CurrentClause, (DWORD_PTR)pFilterIP, spForDebugger); END_QCALL; return dwResult == EXCEPTION_EXECUTE_HANDLER; @@ -7927,27 +8068,35 @@ extern uint32_t g_exceptionCount; MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut); -extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault) +static BOOL CheckExceptionInterception(StackFrameIterator* pStackFrameIterator, ExInfo *pExInfo) { - QCALL_CONTRACT; - - bool result = false; - BEGIN_QCALL; + // check if the exception is intercepted. + BOOL isIntercepted = FALSE; + if (pExInfo->m_ExceptionFlags.DebuggerInterceptInfo()) + { + MethodDesc *pMD = pStackFrameIterator->m_crawl.GetFunction(); + MethodDesc* pInterceptMD = NULL; + StackFrame sfInterceptStackFrame; - Thread* pThread = GET_THREAD(); - Frame* pFrame = pThread->GetFrame(); - MarkInlinedCallFrameAsEHHelperCall(pFrame); + // check if we have reached the interception point yet + pExInfo->m_DebuggerExState.GetDebuggerInterceptInfo(&pInterceptMD, NULL, + reinterpret_cast(&(sfInterceptStackFrame.SP)), + NULL, NULL); - // we already fixed the context in HijackHandler, so let's - // just clear the thread state. - pThread->ResetThrowControlForThread(); + TADDR spForDebugger = GetSpForDiagnosticReporting(pStackFrameIterator->m_crawl.GetRegisterSet()); - // Skip the SfiInit pinvoke frame - pFrame = pThread->GetFrame()->PtrNextFrame(); + if ((pExInfo->m_passNumber == 1) || + ((pInterceptMD == pMD) && (sfInterceptStackFrame == spForDebugger))) + { + isIntercepted = TRUE; + } + } - ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); - REGDISPLAY* pRD = pExInfo->m_pRD; + return isIntercepted; +} +static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo) +{ if (pExInfo->m_passNumber == 1) { GCX_COOP(); @@ -7957,6 +8106,8 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk } else // pExInfo->m_passNumber == 2 { + REGDISPLAY* pRD = &pExInfo->m_regDisplay; + // Clear the enclosing clause to indicate we have not processed any 2nd pass funclet yet. pExInfo->m_csfEnclosingClause.Clear(); if (pExInfo->m_idxCurClause != 0xffffffff) // the reverse pinvoke case doesn't have the m_idxCurClause set @@ -7984,34 +8135,91 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk // We NULL it out because we get the interception event after this point. // * Notifify debugger and return. // In this case the normal EH proceeds and we need to reset m_sfResumeStackFrame to the sf catch handler. - - // TODO-NewEH: New exception handling debugger events completion - /* - EXCEPTION_POINTERS ptrs; - EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter(&ptrs, pILStubFrame); - */ + EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter((EXCEPTION_POINTERS *)&pExInfo->m_ptrs, pILStubFrame); } else { + _ASSERTE(pExInfo->m_pMDToReportFunctionLeave != NULL); + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchCatcherFound(pMD); + if (pExInfo->m_pMDToReportFunctionLeave != NULL) + { + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pExInfo->m_pMDToReportFunctionLeave); + pExInfo->m_pMDToReportFunctionLeave = NULL; + } + // We don't need to do anything special for continuable exceptions after calling // this callback. We are going to start unwinding anyway. PCODE uMethodStartPC = pExInfo->m_frameIter.m_crawl.GetCodeInfo()->GetStartAddress(); - EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedExceptionCatcherFound(pThread, pMD, (TADDR) uMethodStartPC, sp, + TADDR spForDebugger = GetSpForDiagnosticReporting(pRD); + EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedExceptionCatcherFound(pThread, pMD, (TADDR) uMethodStartPC, spForDebugger, &pExInfo->m_ClauseForCatch); - EEToProfilerExceptionInterfaceWrapper::ExceptionSearchCatcherFound(pMD); } + pExInfo->m_ExceptionFlags.SetUnwindHasStarted(); + EEToDebuggerExceptionInterfaceWrapper::ManagedExceptionUnwindBegin(pThread); } - pExInfo->m_ExceptionFlags.SetUnwindHasStarted(); - EEToDebuggerExceptionInterfaceWrapper::ManagedExceptionUnwindBegin(pThread); } +} + +static void NotifyFunctionEnter(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo) +{ + MethodDesc *pMD = pThis->m_crawl.GetFunction(); + + if (pExInfo->m_passNumber == 1) + { + if (pExInfo->m_pMDToReportFunctionLeave != NULL) + { + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionLeave(pExInfo->m_pMDToReportFunctionLeave); + } + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pMD); + // Notify the debugger that we are on the first pass for a managed exception. + // Note that this callback is made for every managed frame. + TADDR spForDebugger = GetSpForDiagnosticReporting(pThis->m_crawl.GetRegisterSet()); + EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, GetControlPC(pThis->m_crawl.GetRegisterSet()), spForDebugger); + } + else + { + if (pExInfo->m_pMDToReportFunctionLeave != NULL) + { + EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionLeave(pExInfo->m_pMDToReportFunctionLeave); + } + EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFunctionEnter(pMD); + } + + pExInfo->m_pMDToReportFunctionLeave = pMD; +} + +extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault, bool* pfIsExceptionIntercepted) +{ + QCALL_CONTRACT; + + bool result = false; + Thread* pThread = GET_THREAD(); + ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + + 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(); + + // Skip the SfiInit pinvoke frame + pFrame = pThread->GetFrame()->PtrNextFrame(); + + NotifyExceptionPassStarted(pThis, pThread, pExInfo); + + REGDISPLAY* pRD = &pExInfo->m_regDisplay; pThread->FillRegDisplay(pRD, pStackwalkCtx); new (pThis) StackFrameIterator(); - result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE | HANDLESKIPPEDFRAMES/* | FUNCTIONSONLY*/) != FALSE; + result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE) != FALSE; + // Walk the stack until it finds the first managed method while (result && pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD) { + // In the first pass, add all explicit frames that have a managed function to the exception stack trace if (pExInfo->m_passNumber == 1) { Frame *pFrame = pThis->m_crawl.GetFrame(); @@ -8025,6 +8233,16 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk !(pExInfo->m_exception == CLRException::GetPreallocatedStackOverflowException()); pExInfo->m_stackTraceInfo.AppendElement(canAllocateMemory, NULL, GetRegdisplaySP(pExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pExInfo->m_frameIter.m_crawl); + +#if defined(DEBUGGING_SUPPORTED) + if (NotifyDebuggerOfStub(pThread, pFrame)) + { + if (!pExInfo->DeliveredFirstChanceNotification()) + { + ExceptionNotifications::DeliverFirstChanceNotification(); + } + } +#endif // DEBUGGING_SUPPORTED } } } @@ -8032,15 +8250,7 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk result = (retVal != SWA_FAILED); } - if (pExInfo->m_passNumber == 1) - { - MethodDesc *pMD = pThis->m_crawl.GetFunction(); - EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pMD); - - // Notify the debugger that we are on the first pass for a managed exception. - // Note that this callback is made for every managed frame. - EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, GetControlPC(pThis->m_crawl.GetRegisterSet()), GetRegdisplaySP(pThis->m_crawl.GetRegisterSet())); - } + NotifyFunctionEnter(pThis, pThread, pExInfo); pExInfo->m_sfLowBound = GetRegdisplaySP(pThis->m_crawl.GetRegisterSet()); @@ -8059,22 +8269,42 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk } pThis->SetAdjustedControlPC(controlPC); pThis->UpdateIsRuntimeWrappedExceptions(); + + *pfIsExceptionIntercepted = CheckExceptionInterception(pThis, pExInfo); } return result; } +static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrameIterator) +{ + StackWalkAction retVal; + + do + { + retVal = pStackFrameIterator->Next(); + if (retVal == SWA_FAILED) + { + break; + } + } + while (pStackFrameIterator->GetFrameState() == StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION); + + return retVal; +} + extern "C" size_t CallDescrWorkerInternalReturnAddressOffset; -extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) +extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* pfIsExceptionIntercepted) { QCALL_CONTRACT; StackWalkAction retVal = SWA_FAILED; + Thread* pThread = GET_THREAD(); + ExInfo* pTopExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); BEGIN_QCALL; - Thread* pThread = GET_THREAD(); Frame* pFrame = pThread->GetFrame(); MarkInlinedCallFrameAsEHHelperCall(pFrame); @@ -8083,7 +8313,9 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla pThread->ResetThrowControlForThread(); ExInfo* pExInfo = pThis->GetNextExInfo(); - ExInfo* pTopExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + bool isCollided = false; + + MethodDesc *pMD = pThis->m_crawl.GetFunction(); // Check for reverse pinvoke (but eliminate the case when the caller is managed) or CallDescrWorkerInternal. if (!ExecutionManager::IsManagedCode(GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext))) @@ -8124,6 +8356,10 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla { invalidRevPInvoke = true; } + else if (pThis->m_crawl.IsFilterFunclet()) + { + invalidRevPInvoke = true; + } } if (fUnwoundReversePInvoke) @@ -8136,6 +8372,26 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla // Unwind to the caller of the managed code retVal = pThis->Next(); _ASSERTE(retVal != SWA_FAILED); + _ASSERTE(pThis->GetFrameState() != StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION); + + if (pThis->m_crawl.GetFrame() == FRAME_TOP) + { + if (pTopExInfo->m_passNumber == 1) + { + LONG disposition = InternalUnhandledExceptionFilter_Worker((EXCEPTION_POINTERS *)&pTopExInfo->m_ptrs); +#ifdef HOST_WINDOWS + CreateCrashDumpIfEnabled(/* fSOException */ FALSE); +#endif + } + else + { +#ifdef HOST_WINDOWS + RaiseFailFastException(pTopExInfo->m_ptrs.ExceptionRecord, pTopExInfo->m_ptrs.ContextRecord, 0); +#else + CrashDumpAndTerminateProcess(pTopExInfo->m_ExceptionCode); +#endif + } + } goto Exit; } } @@ -8172,13 +8428,14 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla if (InlinedCallFrame::FrameHasActiveCall(pFrame)) { InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; - if (((TADDR)pInlinedCallFrame->m_Datum & 6) == 6) + 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 = pThis->Next(); + retVal = MoveToNextNonSkippedFrame(pThis); + if (retVal == SWA_FAILED) { _ASSERTE_MSG(FALSE, "StackFrameIterator::Next failed"); @@ -8194,15 +8451,16 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla else { *uExCollideClauseIdx = pExInfo->m_idxCurClause; + isCollided = true; pExInfo->m_kind = (ExKind)((uint8_t)pExInfo->m_kind | (uint8_t)ExKind::SupersededFlag); // Unwind until we hit the frame of the prevExInfo ExInfo* pPrevExInfo = pThis->GetNextExInfo(); do { - retVal = pThis->Next(); + retVal = MoveToNextNonSkippedFrame(pThis); } - while ((retVal == SWA_CONTINUE) && pThis->m_crawl.GetRegisterSet()->SP != pPrevExInfo->m_pRD->SP); + while ((retVal == SWA_CONTINUE) && pThis->m_crawl.GetRegisterSet()->SP != pPrevExInfo->m_regDisplay.SP); _ASSERTE(retVal != SWA_FAILED); pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); @@ -8212,7 +8470,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla } else if (pTopExInfo->m_passNumber == 1) { - MethodDesc *pMD = pFrame->GetFunction(); + pMD = pFrame->GetFunction(); if (pMD != NULL) { GCX_COOP(); @@ -8220,6 +8478,15 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla !(pTopExInfo->m_exception == CLRException::GetPreallocatedStackOverflowException()); pTopExInfo->m_stackTraceInfo.AppendElement(canAllocateMemory, NULL, GetRegdisplaySP(pTopExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pTopExInfo->m_frameIter.m_crawl); +#if defined(DEBUGGING_SUPPORTED) + if (NotifyDebuggerOfStub(pThread, pFrame)) + { + if (!pTopExInfo->DeliveredFirstChanceNotification()) + { + ExceptionNotifications::DeliverFirstChanceNotification(); + } + } +#endif // DEBUGGING_SUPPORTED } } } @@ -8228,14 +8495,9 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla _ASSERTE(retVal == SWA_FAILED || pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD); - if (pTopExInfo->m_passNumber == 1) + if (!isCollided) { - MethodDesc *pMD = pThis->m_crawl.GetFunction(); - EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFunctionEnter(pMD); - - // Notify the debugger that we are on the first pass for a managed exception. - // Note that this callback is made for every managed frame. - EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, GetControlPC(pThis->m_crawl.GetRegisterSet()), GetRegdisplaySP(pThis->m_crawl.GetRegisterSet())); + NotifyFunctionEnter(pThis, pThread, pTopExInfo); } Exit:; @@ -8251,6 +8513,8 @@ Exit:; pThis->SetAdjustedControlPC(controlPC); pThis->UpdateIsRuntimeWrappedExceptions(); + *pfIsExceptionIntercepted = CheckExceptionInterception(pThis, pTopExInfo); + return true; } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 3e196f63bd090..1eb592c65757c 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -22,7 +22,7 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN OUT PT_CONTEXT pContextRecord, IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable); +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace = true); VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; @@ -441,8 +441,6 @@ class ExceptionTracker bool HandleNestedExceptionEscape(StackFrame sf, bool fIsFirstPass); #if defined(DEBUGGING_SUPPORTED) - BOOL NotifyDebuggerOfStub(Thread* pThread, StackFrame sf, Frame* pCurrentFrame); - void MakeCallbacksRelatedToHandler(bool fBeforeCallingHandler, Thread* pThread, diff --git a/src/coreclr/vm/exceptionhandlingqcalls.h b/src/coreclr/vm/exceptionhandlingqcalls.h index 2a1d77ecb94d9..7747c14f531d2 100644 --- a/src/coreclr/vm/exceptionhandlingqcalls.h +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -15,11 +15,12 @@ struct ExInfo; 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" 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" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum); extern "C" BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause); -extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault); -extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, unsigned int* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); +extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault, bool* pIsExceptionIntercepted); +extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, unsigned int* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* pIsExceptionIntercepted); #endif // DACCESS_COMPILE #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index e98e547e5d26a..7420c710cd437 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -113,6 +113,7 @@ void ExInfo::Init() m_StackAddress = this; DestroyExceptionHandle(); m_hThrowable = NULL; + m_ExceptionCode = 0; // By default, mark the tracker as not having delivered the first // chance exception notification @@ -309,33 +310,71 @@ void ExInfo::SetExceptionCode(const EXCEPTION_RECORD *pCER) #ifndef DACCESS_COMPILE -ExInfo::ExInfo(Thread *pThread, CONTEXT *pCtx, REGDISPLAY *pRD, ExKind exceptionKind) : - m_pExContext(pCtx), +ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pExceptionContext, ExKind exceptionKind) : + m_pExContext(&m_exContext), m_exception((Object*)NULL), m_kind(exceptionKind), m_passNumber(1), m_idxCurClause(0xffffffff), m_notifyDebuggerSP(NULL), - m_pRD(pRD), m_pFrame(pThread->GetFrame()), m_ClauseForCatch({}), + m_ptrs({pExceptionRecord, pExceptionContext}), #ifdef HOST_UNIX + m_fOwnsExceptionPointers(FALSE), m_propagateExceptionCallback(NULL), m_propagateExceptionContext(NULL), #endif // HOST_UNIX m_hThrowable(NULL), - m_CurrentClause({}) + m_CurrentClause({}), + m_ExceptionCode(pExceptionRecord->ExceptionCode), + m_pMDToReportFunctionLeave(NULL), + m_fDeliveredFirstChanceNotification(FALSE), + m_exContext({}) { m_pPrevExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); m_stackTraceInfo.Init(); m_stackTraceInfo.AllocateStackTrace(); m_sfLowBound.SetMaxVal(); pThread->GetExceptionState()->SetCurrentExInfo(this); + m_exContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; } -#endif // DACCESS_COMPILE +#if defined(TARGET_UNIX) +void ExInfo::TakeExceptionPointersOwnership(PAL_SEHException* ex) +{ + _ASSERTE(ex->GetExceptionRecord() == m_ptrs.ExceptionRecord); + _ASSERTE(ex->GetContextRecord() == m_ptrs.ContextRecord); + ex->Clear(); + m_fOwnsExceptionPointers = TRUE; +} +#endif // TARGET_UNIX + +void ExInfo::ReleaseResources() +{ + if (m_hThrowable) + { + if (!CLRException::IsPreallocatedExceptionHandle(m_hThrowable)) + { + DestroyHandle(m_hThrowable); + } + m_hThrowable = NULL; + } + m_stackTraceInfo.FreeStackTrace(); + +#ifndef TARGET_UNIX + // Clear any held Watson Bucketing details + GetWatsonBucketTracker()->ClearWatsonBucketDetails(); +#else // !TARGET_UNIX + if (m_fOwnsExceptionPointers) + { + PAL_FreeExceptionRecords(m_ptrs.ExceptionRecord, m_ptrs.ContextRecord); + m_fOwnsExceptionPointers = FALSE; + } +#endif // !TARGET_UNIX +} -bool IsFilterStartOffset(EE_ILEXCEPTION_CLAUSE* pEHClause, DWORD_PTR dwHandlerStartPC) +static bool IsFilterStartOffset(EE_ILEXCEPTION_CLAUSE* pEHClause, DWORD_PTR dwHandlerStartPC) { EECodeInfo codeInfo((PCODE)dwHandlerStartPC); _ASSERTE(codeInfo.IsValid()); @@ -352,6 +391,7 @@ void ExInfo::MakeCallbacksRelatedToHandler( StackFrame sf ) { +#ifdef DEBUGGING_SUPPORTED // Here we need to make an extra check for filter handlers because we could be calling the catch handler // associated with a filter handler and yet the EH clause we have saved is for the filter handler. BOOL fIsFilterHandler = IsFilterHandler(pEHClause) && IsFilterStartOffset(pEHClause, dwHandlerStartPC); @@ -359,6 +399,7 @@ void ExInfo::MakeCallbacksRelatedToHandler( if (fBeforeCallingHandler) { + m_EHClauseInfo.SetManagedCodeEntered(TRUE); StackFrame sfToStore = sf; if ((m_pPrevExInfo != NULL) && (m_pPrevExInfo->m_csfEnclosingClause == m_csfEnclosingClause)) @@ -429,8 +470,20 @@ void ExInfo::MakeCallbacksRelatedToHandler( } } + m_EHClauseInfo.SetManagedCodeEntered(FALSE); m_EHClauseInfo.ResetInfo(); } +#endif // DEBUGGING_SUPPORTED +} + +#else // DACCESS_COMPILE +void ExInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + // ExInfo is embedded so don't enum 'this'. + OBJECTHANDLE_EnumMemoryRegions(m_hThrowable); + m_ptrs.ExceptionRecord.EnumMem(); + m_ptrs.ContextRecord.EnumMem(); } +#endif // DACCESS_COMPILE #endif // !FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index e83bead1bcb79..bfab243c5ec5a 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -194,10 +194,22 @@ enum class ExKind : uint8_t InstructionFaultFlag = 0x10 }; +struct PAL_SEHException; + struct ExInfo { - ExInfo(Thread *pThread, CONTEXT *pCtx, REGDISPLAY *pRD, ExKind exceptionKind); + struct DAC_EXCEPTION_POINTERS + { + PTR_EXCEPTION_RECORD ExceptionRecord; + PTR_CONTEXT ContextRecord; + }; + + ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pExceptionContext, ExKind exceptionKind); + // Releases all the resources owned by the ExInfo + void ReleaseResources(); + + // Make debugger and profiler callbacks before and after an exception handler (catch, finally, filter) is called void MakeCallbacksRelatedToHandler( bool fBeforeCallingHandler, Thread* pThread, @@ -221,7 +233,6 @@ struct ExInfo // Stack frame iterator used to walk stack frames while handling the exception StackFrameIterator m_frameIter; volatile size_t m_notifyDebuggerSP; - REGDISPLAY *m_pRD; // Stack trace of the current exception StackTraceInfo m_stackTraceInfo; // Initial explicit frame @@ -238,8 +249,13 @@ struct ExInfo StackFrame m_sfCallerOfActualHandlerFrame; // The exception handling clause for the catch handler that was identified during pass 1 EE_ILEXCEPTION_CLAUSE m_ClauseForCatch; + // EXCEPTION_RECORD and CONTEXT_RECORD describing the exception and its location + DAC_EXCEPTION_POINTERS m_ptrs; #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. + BOOL m_fOwnsExceptionPointers; // Exception propagation callback and context for ObjectiveC exception propagation support void(*m_propagateExceptionCallback)(void* context); void *m_propagateExceptionContext; @@ -250,11 +266,38 @@ struct ExInfo // The following fields are for profiler / debugger use only EE_ILEXCEPTION_CLAUSE m_CurrentClause; + // Stores information necessary to intercept an exception DebuggerExState m_DebuggerExState; + // Information for the funclet we are calling EHClauseInfo m_EHClauseInfo; + // Flags representing exception handling state (exception is rethrown, unwind has started, various debugger notifications sent etc) ExceptionFlags m_ExceptionFlags; + // Code of the current exception + DWORD m_ExceptionCode; + // Method to report to the debugger / profiler when stack frame iterator leaves a frame + MethodDesc *m_pMDToReportFunctionLeave; + // Set to TRUE when the first chance notification was delivered for the current exception + BOOL m_fDeliveredFirstChanceNotification; + // CONTEXT and REGDISPLAY used by the StackFrameIterator for stack walking + CONTEXT m_exContext; + REGDISPLAY m_regDisplay; + + inline BOOL DeliveredFirstChanceNotification() + { + LIMITED_METHOD_CONTRACT; + + return m_fDeliveredFirstChanceNotification; + } + + inline void SetFirstChanceNotificationStatus(BOOL fDelivered) + { + LIMITED_METHOD_CONTRACT; + + m_fDeliveredFirstChanceNotification = fDelivered; + } #ifndef TARGET_UNIX + // Used to track Watson bucketing information for an exception. EHWatsonBucketTracker m_WatsonBucketTracker; inline PTR_EHWatsonBucketTracker GetWatsonBucketTracker() @@ -264,6 +307,14 @@ struct ExInfo } #endif // !TARGET_UNIX +#if defined(TARGET_UNIX) + void TakeExceptionPointersOwnership(PAL_SEHException* ex); +#endif // TARGET_UNIX + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); +#endif // DACCESS_COMPILE + }; #endif // !FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/exstate.cpp b/src/coreclr/vm/exstate.cpp index 41c0b8c044536..857fab5003f2d 100644 --- a/src/coreclr/vm/exstate.cpp +++ b/src/coreclr/vm/exstate.cpp @@ -214,8 +214,7 @@ DWORD ThreadExceptionState::GetExceptionCode() } _ASSERTE(m_pExInfo); { - GCX_COOP(); - return ((EXCEPTIONREF)m_pExInfo->m_exception)->GetXCode(); + return m_pExInfo->m_ExceptionCode; } #else // FEATURE_EH_FUNCLETS return m_currentExInfo.m_ExceptionCode; @@ -256,17 +255,6 @@ BOOL ThreadExceptionState::IsExceptionInProgress() #if !defined(DACCESS_COMPILE) -void ThreadExceptionState::GetLeafFrameInfo(StackTraceElement* pStackTraceElement) -{ - WRAPPER_NO_CONTRACT; - -#ifdef FEATURE_EH_FUNCLETS - m_pCurrentTracker->m_StackTraceInfo.GetLeafFrameInfo(pStackTraceElement); -#else - m_currentExInfo.m_StackTraceInfo.GetLeafFrameInfo(pStackTraceElement); -#endif -} - EXCEPTION_POINTERS* ThreadExceptionState::GetExceptionPointers() { LIMITED_METHOD_CONTRACT; @@ -276,6 +264,10 @@ EXCEPTION_POINTERS* ThreadExceptionState::GetExceptionPointers() { return (EXCEPTION_POINTERS*)&(m_pCurrentTracker->m_ptrs); } + else if (m_pExInfo) + { + return (EXCEPTION_POINTERS*)&(m_pExInfo->m_ptrs); + } else { return NULL; @@ -310,6 +302,10 @@ PTR_EXCEPTION_RECORD ThreadExceptionState::GetExceptionRecord() { return m_pCurrentTracker->m_ptrs.ExceptionRecord; } + else if (m_pExInfo) + { + return m_pExInfo->m_ptrs.ExceptionRecord; + } else { return NULL; @@ -520,6 +516,10 @@ EHClauseInfo* ThreadExceptionState::GetCurrentEHClauseInfo() { return &(m_pCurrentTracker->m_EHClauseInfo); } + else if (m_pExInfo) + { + return &(m_pExInfo->m_EHClauseInfo); + } else { _ASSERTE(!"unexpected use of GetCurrentEHClauseInfo() when no exception in flight"); @@ -590,6 +590,14 @@ ThreadExceptionState::EnumChainMemoryRegions(CLRDataEnumMemoryFlags flags) if (head == NULL) { + PTR_ExInfo exInfo = m_pExInfo; + while (exInfo != NULL) + { + exInfo->EnumMemoryRegions(flags); + exInfo.EnumMem(); + exInfo = exInfo->m_pPrevExInfo; + } + return; } diff --git a/src/coreclr/vm/exstate.h b/src/coreclr/vm/exstate.h index 6905a29e17fbc..500c9a0f455e1 100644 --- a/src/coreclr/vm/exstate.h +++ b/src/coreclr/vm/exstate.h @@ -80,7 +80,6 @@ class ThreadExceptionState PTR_EXCEPTION_RECORD GetExceptionRecord(); PTR_CONTEXT GetContextRecord(); BOOL IsExceptionInProgress(); - void GetLeafFrameInfo(StackTraceElement* pStackTrace); ExceptionFlags* GetFlags(); diff --git a/src/coreclr/vm/i386/excepx86.cpp b/src/coreclr/vm/i386/excepx86.cpp index f0c4dcc8b2a2c..f52766d7f51e5 100644 --- a/src/coreclr/vm/i386/excepx86.cpp +++ b/src/coreclr/vm/i386/excepx86.cpp @@ -2285,30 +2285,25 @@ StackWalkAction COMPlusThrowCallback( // SWA value if (!pCf->IsFrameless()) { - // @todo - remove this once SIS is fully enabled. - extern bool g_EnableSIS; - if (g_EnableSIS) + // For debugger, we may want to notify 1st chance exceptions if they're coming out of a stub. + // We recognize stubs as Frames with a M2U transition type. The debugger's stackwalker also + // recognizes these frames and publishes ICorDebugInternalFrames in the stackwalk. It's + // important to use pFrame as the stack address so that the Exception callback matches up + // w/ the ICorDebugInternalFrame stack range. + if (CORDebuggerAttached()) { - // For debugger, we may want to notify 1st chance exceptions if they're coming out of a stub. - // We recognize stubs as Frames with a M2U transition type. The debugger's stackwalker also - // recognizes these frames and publishes ICorDebugInternalFrames in the stackwalk. It's - // important to use pFrame as the stack address so that the Exception callback matches up - // w/ the ICorDebugInternlFrame stack range. - if (CORDebuggerAttached()) + Frame * pFrameStub = pCf->GetFrame(); + Frame::ETransitionType t = pFrameStub->GetTransitionType(); + if (t == Frame::TT_M2U) { - Frame * pFrameStub = pCf->GetFrame(); - Frame::ETransitionType t = pFrameStub->GetTransitionType(); - if (t == Frame::TT_M2U) + // Use address of the frame as the stack address. + currentSP = (SIZE_T) ((void*) pFrameStub); + currentIP = 0; // no IP. + EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, (SIZE_T)currentIP, (SIZE_T)currentSP); + // Deliver the FirstChanceNotification after the debugger, if not already delivered. + if (!pExInfo->DeliveredFirstChanceNotification()) { - // Use address of the frame as the stack address. - currentSP = (SIZE_T) ((void*) pFrameStub); - currentIP = 0; // no IP. - EEToDebuggerExceptionInterfaceWrapper::FirstChanceManagedException(pThread, (SIZE_T)currentIP, (SIZE_T)currentSP); - // Deliver the FirstChanceNotification after the debugger, if not already delivered. - if (!pExInfo->DeliveredFirstChanceNotification()) - { - ExceptionNotifications::DeliverFirstChanceNotification(); - } + ExceptionNotifications::DeliverFirstChanceNotification(); } } } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 17f2e4a3277ed..73a41ab24241f 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -4234,7 +4234,7 @@ void ThrowNew(OBJECTREF oref) } } - DispatchManagedException(oref); + DispatchManagedException(oref, /* preserveStackTrace */ false); } #endif // FEATURE_EH_FUNCLETS @@ -4301,14 +4301,11 @@ HCIMPLEND #ifdef FEATURE_EH_FUNCLETS void RethrowNew() { - CONTEXT ctx = {}; - ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - REGDISPLAY rd; Thread *pThread = GetThread(); ExInfo *pActiveExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); - ExInfo exInfo(pThread, &ctx, &rd, ExKind::None); + ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pActiveExInfo->m_ptrs.ContextRecord, ExKind::None); GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index ae546fd6d42fc..b86add72c6d8e 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -184,6 +184,7 @@ 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/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index 6448df98733bf..ae2a45a0e3027 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -8073,6 +8073,19 @@ StackWalkAction ProfilerStackWalkCallback(CrawlFrame *pCf, PROFILER_STACK_WALK_D CONTEXT builtContext; #endif +#ifdef FEATURE_EH_FUNCLETS + // + // Skip all managed exception handling functions + // + if (pFunc != NULL && ( + (pFunc->GetMethodTable() == g_pEHClass) || + (pFunc->GetMethodTable() == g_pExceptionServicesInternalCallsClass) || + (pFunc->GetMethodTable() == g_pStackFrameIteratorClass))) + { + return SWA_CONTINUE; + } +#endif // FEATURE_EH_FUNCLETS + // // For Unmanaged-to-managed transitions we get a NativeMarker back, which we want // to return to the profiler as the context seed if it wants to walk the unmanaged @@ -8091,6 +8104,20 @@ StackWalkAction ProfilerStackWalkCallback(CrawlFrame *pCf, PROFILER_STACK_WALK_D return SWA_CONTINUE; } +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled && !pCf->IsFrameless() && InlinedCallFrame::FrameHasActiveCall(pCf->GetFrame())) + { + // Skip new exception handling helpers + InlinedCallFrame *pInlinedCallFrame = (InlinedCallFrame *)pCf->GetFrame(); + PTR_NDirectMethodDesc pMD = pInlinedCallFrame->m_Datum; + TADDR datum = dac_cast(pMD); + if ((datum & (TADDR)InlinedCallFrameMarker::Mask) == (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper) + { + return SWA_CONTINUE; + } + } +#endif // FEATURE_EH_FUNCLETS + // // If this is not a transition of any sort and not a managed // method, ignore it. diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 593e37387afaa..52f142c6cceab 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -372,6 +372,7 @@ static const Entry s_QCall[] = DllImportEntry(SfiInit) DllImportEntry(SfiNext) DllImportEntry(CallCatchFunclet) + DllImportEntry(ResumeAtInterceptionLocation) DllImportEntry(CallFilterFunclet) DllImportEntry(CallFinallyFunclet) DllImportEntry(EHEnumInitFromStackFrameIterator) diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index e67334bd51fc7..4dcef382cccce 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1628,7 +1628,9 @@ StackWalkAction StackFrameIterator::Filter(void) fRecheckCurrentFrame = false; fSkipFuncletCallback = true; - if ((m_flags & GC_FUNCLET_REFERENCE_REPORTING) && (pExInfo != NULL) && (m_crawl.GetRegisterSet()->SP > (SIZE_T)pExInfo)) + 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) { @@ -2231,7 +2233,7 @@ StackWalkAction StackFrameIterator::Filter(void) { 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)); + _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 } @@ -2398,7 +2400,6 @@ StackWalkAction StackFrameIterator::NextRaw(void) // make sure we're not skipping a different transition if (m_crawl.pFrame->NeedsUpdateRegDisplay()) { - CONSISTENCY_CHECK(m_crawl.pFrame->IsTransitionToNativeFrame()); if (m_crawl.pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()) { // ControlPC may be different as the InlinedCallFrame stays active throughout diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index 31e17cacb52e9..b5e9263e35ce0 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -109,6 +109,8 @@ GVAL_IMPL_INIT(DWORD, g_TlsIndex, TLS_OUT_OF_INDEXES); MethodTable* g_pCastHelpers; #ifdef FEATURE_EH_FUNCLETS GPTR_IMPL(MethodTable, g_pEHClass); +GPTR_IMPL(MethodTable, g_pExceptionServicesInternalCallsClass); +GPTR_IMPL(MethodTable, g_pStackFrameIteratorClass); GVAL_IMPL(bool, g_isNewExceptionHandlingEnabled); #endif diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 8ecbd1d3f591b..ba653f6743928 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -391,6 +391,8 @@ GVAL_DECL(DWORD, g_TlsIndex); #ifdef FEATURE_EH_FUNCLETS GPTR_DECL(MethodTable, g_pEHClass); +GPTR_DECL(MethodTable, g_pExceptionServicesInternalCallsClass); +GPTR_DECL(MethodTable, g_pStackFrameIteratorClass); GVAL_DECL(bool, g_isNewExceptionHandlingEnabled); #endif diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index ac2b5b9244327..b59a7bdcc372f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -82,13 +82,6 @@ public static void SetData(string name, object? data) internal static event EventHandler? FirstChanceException; #pragma warning restore CS0067 -#if !NATIVEAOT - internal static void OnFirstChanceException(object e) - { - FirstChanceException?.Invoke(AppDomain.CurrentDomain, new FirstChanceExceptionEventArgs((Exception)e)); - } -#endif - internal static void OnProcessExit() { AssemblyLoadContext.OnProcessExit();