From 7dfebb01282517c29f4746caa37ee16d50ff0f27 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 10 Nov 2022 02:01:35 +0100 Subject: [PATCH 1/5] Port NativeAOT exception handling to CoreCLR This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when `DOTNET_EnableNewExceptionHandling` env variable is set. By default, the old way is used. This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture. Notes for the PR: * I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here. * Naming of the native helpers like `RhpCallCatchFunclet` was left the same as in the NativeAOT for now. * There are still some little things I'd like to eventually clean up, like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and `CONTEXT` it uses into the `ExInfo` itself or moving debug members of `StackFrameIterator` and `REGDISPLAY` to the end of those structures so that the `AsmOffsets.cs` can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated. * There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR. * `ProfilerEnter` and `ProfilerLeave` for the case of `UnmanagedCallersOnly` method were being called in preemptive mode. * NativeAOT code for rethrowing exception was incorrectly calling `DispatchEx` with last argument set to `activeExInfo._idxCurClause` to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too. * Changes in the stackwalk.cpp add support for * Usage of `ExInfo` instead of `ExceptionTracker` * Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable. * Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case. * I needed to add `GCFrame::Remove` method that can remove the `GCFrame` from any location in the chain. There is a managed runtime method that calls `GCReporting::Unregister` that was popping it with my changes out of order due to the exception handling code being managed. Fix context initialization after rebase --- .../System.Private.CoreLib.csproj | 5 + .../Runtime/ExceptionServices/AsmOffsets.cs | 213 ++++ .../ExceptionServices/InternalCalls.cs | 48 + src/coreclr/clr.featuredefines.props | 5 + .../debug/daccess/dacdbiimplstackwalk.cpp | 44 + src/coreclr/inc/clrconfigvalues.h | 2 + src/coreclr/inc/dacvars.h | 5 + src/coreclr/inc/jithelpers.h | 8 +- .../src/System/Runtime/ExceptionHandling.cs | 236 +++- .../src/System/Runtime/ExceptionIDs.cs | 7 +- .../src/System/Runtime/StackFrameIterator.cs | 27 + src/coreclr/vm/CMakeLists.txt | 5 +- src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm | 5 +- src/coreclr/vm/amd64/Context.S | 61 + src/coreclr/vm/amd64/Context.asm | 45 +- src/coreclr/vm/amd64/calldescrworkeramd64.S | 7 +- src/coreclr/vm/amd64/cgencpu.h | 14 +- src/coreclr/vm/appdomain.cpp | 4 + src/coreclr/vm/arm/asmhelpers.S | 4 + src/coreclr/vm/arm/stubs.cpp | 1 + src/coreclr/vm/arm64/CallDescrWorkerARM64.asm | 6 + src/coreclr/vm/arm64/calldescrworkerarm64.S | 5 + src/coreclr/vm/arm64/stubs.cpp | 2 +- src/coreclr/vm/ceeload.cpp | 2 +- src/coreclr/vm/codeman.h | 2 +- src/coreclr/vm/corelib.h | 11 + src/coreclr/vm/eetwain.cpp | 2 +- src/coreclr/vm/excep.cpp | 113 +- src/coreclr/vm/excep.h | 17 + src/coreclr/vm/exceptionhandling.cpp | 1070 ++++++++++++++++- src/coreclr/vm/exceptionhandling.h | 17 + src/coreclr/vm/exceptionhandlingqcalls.h | 27 + src/coreclr/vm/exceptmacros.h | 7 + src/coreclr/vm/exinfo.cpp | 133 ++ src/coreclr/vm/exinfo.h | 107 ++ src/coreclr/vm/exstate.cpp | 41 +- src/coreclr/vm/exstate.h | 17 + src/coreclr/vm/fcall.cpp | 7 + src/coreclr/vm/fcall.h | 6 +- src/coreclr/vm/frames.cpp | 44 + src/coreclr/vm/frames.h | 11 +- src/coreclr/vm/i386/asmhelpers.S | 6 + src/coreclr/vm/jithelpers.cpp | 77 +- src/coreclr/vm/jitinterface.cpp | 37 + .../loongarch64/calldescrworkerloongarch64.S | 5 + src/coreclr/vm/metasig.h | 3 + src/coreclr/vm/qcallentrypoints.cpp | 12 + src/coreclr/vm/reflectioninvocation.cpp | 2 +- .../vm/riscv64/calldescrworkerriscv64.S | 5 + src/coreclr/vm/stackwalk.cpp | 162 ++- src/coreclr/vm/stackwalk.h | 69 +- src/coreclr/vm/threads.cpp | 4 +- src/coreclr/vm/threadsuspend.cpp | 44 +- src/coreclr/vm/vars.cpp | 4 + src/coreclr/vm/vars.hpp | 5 + .../src/System/AppContext.cs | 7 + 56 files changed, 2697 insertions(+), 138 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs create mode 100644 src/coreclr/vm/amd64/Context.S create mode 100644 src/coreclr/vm/exceptionhandlingqcalls.h diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 14936ccfa6479..197ca5bf9f4b2 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -210,6 +210,11 @@ + + + + + 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 new file mode 100644 index 0000000000000..985d9e7122768 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// NOTE: This file is included also during native runtime compilation by the C++ compiler for verification purposes. +// The static asserts at the end are compiled only when this file is included in native build by c++ compiler. They +// provide compile time verification that all the sizes and offsets match between the managed and native code. + +#if !__cplusplus +internal static +#endif +class AsmOffsets +{ + + // Offsets / sizes that are different in Release / Debug builds +#if DEBUG + // Debug build offsets +#if TARGET_AMD64 +#if TARGET_UNIX + public const int SIZEOF__REGDISPLAY = 0x1a90; + public const int OFFSETOF__REGDISPLAY__SP = 0x1a78; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x1a80; +#else // TARGET_UNIX + public const int SIZEOF__REGDISPLAY = 0xbf0; + public const int OFFSETOF__REGDISPLAY__SP = 0xbd8; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0xbe0; +#endif // TARGET_UNIX +#elif TARGET_ARM64 + public const int SIZEOF__REGDISPLAY = 0x940; + public const int OFFSETOF__REGDISPLAY__SP = 0x898; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x8a0; +#elif TARGET_ARM + public const int SIZEOF__REGDISPLAY = 0x410; + public const int OFFSETOF__REGDISPLAY__SP = 0x3ec; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x3f0; +#elif TARGET_X86 + public const int SIZEOF__REGDISPLAY = 0x5fc; + public const int OFFSETOF__REGDISPLAY__SP = 0x5f0; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x5f4; +#elif TARGET_RISCV64 + public const int SIZEOF__REGDISPLAY = 0x6C0; + public const int OFFSETOF__REGDISPLAY__SP = 0x628; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x630; +#elif TARGET_LOONGARCH64 + public const int SIZEOF__REGDISPLAY = 0x6C0; + public const int OFFSETOF__REGDISPLAY__SP = 0x628; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x630; +#endif + +#if TARGET_64BIT + public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x8; + public const int SIZEOF__StackFrameIterator = 0x370; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x352; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x368; +#else // TARGET_64BIT + public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; + public const int SIZEOF__StackFrameIterator = 0x2d8; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x2c2; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x2d0; +#endif // TARGET_64BIT + +#else // DEBUG + // Release build offsets +#if TARGET_AMD64 +#if TARGET_UNIX + public const int SIZEOF__REGDISPLAY = 0x1a80; + public const int OFFSETOF__REGDISPLAY__SP = 0x1a70; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x1a78; +#else // TARGET_UNIX + public const int SIZEOF__REGDISPLAY = 0xbe0; + public const int OFFSETOF__REGDISPLAY__SP = 0xbd0; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0xbd8; +#endif // TARGET_UNIX +#elif TARGET_ARM64 + public const int SIZEOF__REGDISPLAY = 0x930; + public const int OFFSETOF__REGDISPLAY__SP = 0x890; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x898; +#elif TARGET_ARM + public const int SIZEOF__REGDISPLAY = 0x408; + public const int OFFSETOF__REGDISPLAY__SP = 0x3e8; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x3ec; +#elif TARGET_X86 + public const int SIZEOF__REGDISPLAY = 0x5f8; + public const int OFFSETOF__REGDISPLAY__SP = 0x5ec; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x5f0; +#elif TARGET_RISCV64 + public const int SIZEOF__REGDISPLAY = 0x6B0; + public const int OFFSETOF__REGDISPLAY__SP = 0x620; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x628; +#elif TARGET_LOONGARCH64 + public const int SIZEOF__REGDISPLAY = 0x6B0; + public const int OFFSETOF__REGDISPLAY__SP = 0x620; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x628; +#endif + +#if TARGET_64BIT + public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x8; + public const int SIZEOF__StackFrameIterator = 0x370; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x34a; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x360; +#else // TARGET_64BIT + public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; + public const int SIZEOF__StackFrameIterator = 0x2d0; + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x2ba; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x2c8; +#endif // TARGET_64BIT + +#endif // DEBUG + +#if TARGET_AMD64 +#if TARGET_UNIX + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0xc20; +#else // TARGET_UNIX + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x4d0; +#endif // TARGET_UNIx +#elif TARGET_ARM64 + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x390; +#elif TARGET_ARM + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x1a0; +#elif TARGET_X86 + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x2cc; +#elif TARGET_RISCV64 + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x220; +#elif TARGET_LOONGARCH64 + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x220; +#endif + +#if TARGET_AMD64 + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0xf8; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0xa0; +#elif TARGET_ARM64 + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0x108; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0xf0; +#elif TARGET_ARM + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0x40; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0x30; +#elif TARGET_X86 + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0xb8; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0xb4; +#elif TARGET_RISCV64 + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0x108; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0x48; +#elif TARGET_LOONGARCH64 + public const int OFFSETOF__PAL_LIMITED_CONTEXT__IP = 0x108; + public const int OFFSETOF__PAL_LIMITED_CONTEXT__FP = 0x48; +#endif + + // Offsets / sizes that are different in 64 / 32 bit mode + +#if TARGET_64BIT + public const int SIZEOF__EHEnum = 0x20; + public const int OFFSETOF__StackFrameIterator__m_pRegDisplay = 0x228; + public const int OFFSETOF__ExInfo__m_pPrevExInfo = 0; + public const int OFFSETOF__ExInfo__m_pExContext = 8; + public const int OFFSETOF__ExInfo__m_exception = 0x10; + public const int OFFSETOF__ExInfo__m_kind = 0x18; + public const int OFFSETOF__ExInfo__m_passNumber = 0x19; + public const int OFFSETOF__ExInfo__m_idxCurClause = 0x1c; + public const int OFFSETOF__ExInfo__m_frameIter = 0x20; + public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator; +#else // TARGET_64BIT + public const int SIZEOF__EHEnum = 0x10; + public const int OFFSETOF__StackFrameIterator__m_pRegDisplay = 0x218; + public const int OFFSETOF__ExInfo__m_pPrevExInfo = 0; + public const int OFFSETOF__ExInfo__m_pExContext = 4; + public const int OFFSETOF__ExInfo__m_exception = 8; + public const int OFFSETOF__ExInfo__m_kind = 0xC; + public const int OFFSETOF__ExInfo__m_passNumber = 0xD; + public const int OFFSETOF__ExInfo__m_idxCurClause = 0x10; + public const int OFFSETOF__ExInfo__m_frameIter = 0x18; + public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator; +#endif // TARGET_64BIT + +#if __cplusplus + static_assert_no_msg(sizeof(CONTEXT) == AsmOffsets::SIZEOF__PAL_LIMITED_CONTEXT); +#if TARGET_AMD64 + static_assert_no_msg(offsetof(CONTEXT, Rip) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__IP); + static_assert_no_msg(offsetof(CONTEXT, Rbp) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__FP); +#elif TARGET_ARM64 + static_assert_no_msg(offsetof(CONTEXT, Pc) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__IP); + static_assert_no_msg(offsetof(CONTEXT, Fp) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__FP); +#elif TARGET_ARM + static_assert_no_msg(offsetof(CONTEXT, Pc) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__IP); + static_assert_no_msg(offsetof(CONTEXT, R11) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__FP); +#elif TARGET_X86 + static_assert_no_msg(offsetof(CONTEXT, Eip) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__IP); + static_assert_no_msg(offsetof(CONTEXT, Ebp) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__FP); +#elif TARGET_RISCV64 + static_assert_no_msg(offsetof(CONTEXT, Pc) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__IP); + static_assert_no_msg(offsetof(CONTEXT, Fp) == AsmOffsets::OFFSETOF__PAL_LIMITED_CONTEXT__FP); +#endif + static_assert_no_msg(sizeof(REGDISPLAY) == AsmOffsets::SIZEOF__REGDISPLAY); + static_assert_no_msg(offsetof(REGDISPLAY, SP) == AsmOffsets::OFFSETOF__REGDISPLAY__SP); + static_assert_no_msg(offsetof(REGDISPLAY, ControlPC) == AsmOffsets::OFFSETOF__REGDISPLAY__ControlPC); + static_assert_no_msg(offsetof(REGDISPLAY, pCurrentContext) == AsmOffsets::OFFSETOF__REGDISPLAY__m_pCurrentContext); + static_assert_no_msg(sizeof(StackFrameIterator) == AsmOffsets::SIZEOF__StackFrameIterator); + static_assert_no_msg(offsetof(StackFrameIterator, m_crawl) + offsetof(CrawlFrame, pRD) == OFFSETOF__StackFrameIterator__m_pRegDisplay); + static_assert_no_msg(offsetof(StackFrameIterator, m_isRuntimeWrappedExceptions) == OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions); + static_assert_no_msg(offsetof(StackFrameIterator, m_AdjustedControlPC) == OFFSETOF__StackFrameIterator__m_AdjustedControlPC); + static_assert_no_msg(sizeof(ExtendedEHClauseEnumerator) == AsmOffsets::SIZEOF__EHEnum); + static_assert_no_msg(offsetof(ExInfo, m_pPrevExInfo) == OFFSETOF__ExInfo__m_pPrevExInfo); + static_assert_no_msg(offsetof(ExInfo, m_pExContext) == OFFSETOF__ExInfo__m_pExContext); + static_assert_no_msg(offsetof(ExInfo, m_exception) == OFFSETOF__ExInfo__m_exception); + static_assert_no_msg(offsetof(ExInfo, m_kind) == OFFSETOF__ExInfo__m_kind); + static_assert_no_msg(offsetof(ExInfo, m_passNumber) == OFFSETOF__ExInfo__m_passNumber); + 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); +#endif + +} +#if __cplusplus +; +#endif 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 new file mode 100644 index 0000000000000..9f84e534f61cd --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This is where we group together all the internal calls. +// + +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.ExceptionServices +{ + internal static partial class InternalCalls + { + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpSfiInit")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx, [MarshalAs(UnmanagedType.Bool)] bool instructionFault); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpSfiNext")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); + +#pragma warning disable CS8500 + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallCatchFunclet")] + internal static unsafe partial IntPtr RhpCallCatchFunclet( + ObjectHandleOnStack exceptionObj, byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallFinallyFunclet")] + internal static unsafe partial void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallFilterFunclet")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool RhpCallFilterFunclet( + ObjectHandleOnStack exceptionObj, byte* pFilterIP, void* pvRegDisplay); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpAppendExceptionStackFrame")] + internal static unsafe partial void RhpAppendExceptionStackFrame(ObjectHandleOnStack exceptionObj, IntPtr ip, UIntPtr sp, int flags, EH.ExInfo* exInfo); +#pragma warning restore CS8500 + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpEHEnumInitFromStackFrameIterator")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpEHEnumNext")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool RhpEHEnumNext(void* pEHEnum, void* pEHClause); + } +} diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 1bc919b6dc9c8..0ccfc347d0a0e 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -40,6 +40,10 @@ true + + true + + $(DefineConstants);FEATURE_ARRAYSTUB_AS_IL $(DefineConstants);FEATURE_MULTICASTSTUB_AS_IL @@ -59,6 +63,7 @@ $(DefineConstants);FEATURE_BASICFREEZE $(DefineConstants);FEATURE_PORTABLE_SHUFFLE_THUNKS $(DefineConstants);FEATURE_ICASTABLE + $(DefineConstants);FEATURE_EH_FUNCLETS $(DefineConstants);PROFILING_SUPPORTED $(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index b3766d11be350..8a705996d3e82 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -261,6 +261,22 @@ BOOL DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle) // Just continue onto the next managed stack frame. continue; } +#ifdef FEATURE_EH_FUNCLETS + else if (g_isNewExceptionHandlingEnabled && pIter->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD) + { + // 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) + { + continue; + } + + fIsAtEndOfStack = FALSE; + } +#endif // FEATURE_EH_FUNCLETS else { fIsAtEndOfStack = FALSE; @@ -433,6 +449,20 @@ ULONG32 DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread) ULONG32 uCount = 0; while (pFrame != FRAME_TOP) { +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled && InlinedCallFrame::FrameHasActiveCall(pFrame)) + { + // Skip new exception handling helpers + InlinedCallFrame *pInlinedCallFrame = (InlinedCallFrame *)pFrame; + PTR_NDirectMethodDesc pMD = pInlinedCallFrame->m_Datum; + TADDR datum = dac_cast(pMD); + if ((datum & (TADDR)InlinedCallFrameMarker::Mask) == (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper) + { + pFrame = pFrame->Next(); + continue; + } + } +#endif // FEATURE_EH_FUNCLETS CorDebugInternalFrameType ift = GetInternalFrameType(pFrame); if (ift != STUBFRAME_NONE) { @@ -472,6 +502,20 @@ void DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread while (pFrame != FRAME_TOP) { +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled && InlinedCallFrame::FrameHasActiveCall(pFrame)) + { + // Skip new exception handling helpers + InlinedCallFrame *pInlinedCallFrame = (InlinedCallFrame *)pFrame; + PTR_NDirectMethodDesc pMD = pInlinedCallFrame->m_Datum; + TADDR datum = dac_cast(pMD); + if ((datum & (TADDR)InlinedCallFrameMarker::Mask) == (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper) + { + pFrame = pFrame->Next(); + continue; + } + } +#endif // FEATURE_EH_FUNCLETS // check if the internal frame is interesting frameData.stubFrame.frameType = GetInternalFrameType(pFrame); if (frameData.stubFrame.frameType != STUBFRAME_NONE) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 7b356b0744399..e7f861b0556ca 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -259,6 +259,8 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("le CONFIG_DWORD_INFO(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableNewExceptionHandling, W("EnableNewExceptionHandling"), 0, "Enable new exception handling."); + /// /// Garbage collector diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 8fcc1cced7d43..10b565efeff5f 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -113,6 +113,11 @@ DEFINE_DACVAR(DWORD, dac__g_debuggerWordTLSIndex, g_debuggerWordTLSIndex) #endif DEFINE_DACVAR(DWORD, dac__g_TlsIndex, g_TlsIndex) +#ifdef FEATURE_EH_FUNCLETS +DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pEHClass, ::g_pEHClass) +DEFINE_DACVAR(BOOL, dac__g_isNewExceptionHandlingEnabled, ::g_isNewExceptionHandlingEnabled) +#endif + DEFINE_DACVAR(PTR_SString, SString__s_Empty, SString::s_Empty) DEFINE_DACVAR(INT32, ArrayBase__s_arrayBoundsZero, ArrayBase::s_arrayBoundsZero) diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index d34696ac1c983..06d09a1b5e15a 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -113,8 +113,8 @@ DYNAMICJITHELPER(CORINFO_HELP_LDELEMA_REF, NULL, CORINFO_HELP_SIG_4_STACK) // Exceptions - JITHELPER(CORINFO_HELP_THROW, IL_Throw, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_RETHROW, IL_Rethrow, CORINFO_HELP_SIG_REG_ONLY) + DYNAMICJITHELPER(CORINFO_HELP_THROW, IL_Throw, CORINFO_HELP_SIG_REG_ONLY) + DYNAMICJITHELPER(CORINFO_HELP_RETHROW, IL_Rethrow, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_USER_BREAKPOINT, JIT_UserBreakpoint, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_RNGCHKFAIL, JIT_RngChkFail, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_OVERFLOW, JIT_Overflow, CORINFO_HELP_SIG_REG_ONLY) @@ -271,8 +271,8 @@ JITHELPER(CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) #ifdef FEATURE_EH_FUNCLETS - JITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE, ProcessCLRException, CORINFO_HELP_SIG_UNDEF) - JITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE_FILTER_FUNCLET, ProcessCLRException,CORINFO_HELP_SIG_UNDEF) + DYNAMICJITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE, ProcessCLRException, CORINFO_HELP_SIG_UNDEF) + DYNAMICJITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE_FILTER_FUNCLET, ProcessCLRException,CORINFO_HELP_SIG_UNDEF) #else // FEATURE_EH_FUNCLETS JITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE, NULL, CORINFO_HELP_SIG_UNDEF) JITHELPER(CORINFO_HELP_EE_PERSONALITY_ROUTINE_FILTER_FUNCLET, NULL, CORINFO_HELP_SIG_UNDEF) 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 30ec1ff6051af..251d6c0ae7093 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -2,17 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - +#if NATIVEAOT using Internal.Runtime; +#else +using System.Runtime.ExceptionServices; +#endif // Disable: Filter expression is a constant. We know. We just can't do an unfiltered catch. #pragma warning disable 7095 namespace System.Runtime { - public enum RhFailFastReason +#if NATIVEAOT + public +#else + internal +#endif + enum RhFailFastReason { Unknown = 0, InternalError = 1, // "Runtime internal error" @@ -47,6 +56,9 @@ private struct RhEHClause internal byte* _filterAddress; internal byte* _handlerAddress; internal void* _pTargetType; +#if !NATIVEAOT + internal bool _isSameTry; +#endif /// /// We expect the stackwalker to adjust return addresses to point at 'return address - 1' so that we @@ -72,18 +84,33 @@ private struct EHEnum #pragma warning disable IDE0060 // This is a fail-fast function used by the runtime as a last resort that will terminate the process with // as little effort as possible. No guarantee is made about the semantics of this fail-fast. - internal static void FallbackFailFast(RhFailFastReason reason, object unhandledException) +#if !NATIVEAOT + [DoesNotReturn] +#endif + internal static void FallbackFailFast(RhFailFastReason reason, object? unhandledException) { +#if NATIVEAOT InternalCalls.RhpFallbackFailFast(); +#else + // TODO-NewEH: Debugging the FailFasts is difficult, debugger doesn't stop here, but + // only after the process exits. The Debugger.Break() is a temporary solution to + // help during development. Figure out how to do it properly. + System.Diagnostics.Debugger.Break(); + Environment.FailFast(reason.ToString()); +#endif } #pragma warning restore IDE0060 // Given an address pointing somewhere into a managed module, get the classlib-defined fail-fast // function and invoke it. Any failure to find and invoke the function, or if it returns, results in // MRT-defined fail-fast behavior. - internal static void FailFastViaClasslib(RhFailFastReason reason, object unhandledException, +#if !NATIVEAOT + [DoesNotReturn] +#endif + internal static void FailFastViaClasslib(RhFailFastReason reason, object? unhandledException, IntPtr classlibAddress) { +#if NATIVEAOT // Find the classlib function that will fail fast. This is a RuntimeExport function from the // classlib module, and is therefore managed-callable. IntPtr pFailFastFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(classlibAddress, @@ -107,6 +134,7 @@ internal static void FailFastViaClasslib(RhFailFastReason reason, object unhandl } // The classlib's function should never return and should not throw. If it does, then we fail our way... +#endif FallbackFailFast(reason, unhandledException); } @@ -137,6 +165,7 @@ private struct OSCONTEXT private static void OnFirstChanceExceptionViaClassLib(object exception) { +#if NATIVEAOT IntPtr pOnFirstChanceFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(exception.GetMethodTable(), ClassLibFunctionId.OnFirstChance); @@ -153,10 +182,21 @@ 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 } private static void OnUnhandledExceptionViaClassLib(object exception) { +#if NATIVEAOT IntPtr pOnUnhandledExceptionFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType(exception.GetMethodTable(), ClassLibFunctionId.OnUnhandledException); @@ -173,12 +213,16 @@ private static void OnUnhandledExceptionViaClassLib(object exception) { // disallow all exceptions leaking out of callbacks } +#else + Debug.Assert(false, "Unhandled exceptions should be processed by the native runtime only"); +#endif } [MethodImpl(MethodImplOptions.NoInlining)] internal static unsafe void UnhandledExceptionFailFastViaClasslib( RhFailFastReason reason, object unhandledException, IntPtr classlibAddress, ref ExInfo exInfo) { +#if NATIVEAOT IntPtr pFailFastFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(classlibAddress, ClassLibFunctionId.FailFast); @@ -209,6 +253,12 @@ internal static unsafe void UnhandledExceptionFailFastViaClasslib( // The classlib's function should never return and should not throw. If it does, then we fail our way... FallbackFailFast(reason, unhandledException); +#else + FailFastViaClasslib( + reason, + unhandledException, + classlibAddress); +#endif } private enum RhEHFrameType @@ -218,16 +268,17 @@ private enum RhEHFrameType } private static void AppendExceptionStackFrameViaClasslib(object exception, IntPtr ip, + UIntPtr sp, ref ExInfo exInfo, ref bool isFirstRethrowFrame, ref bool isFirstFrame) { + int flags = (isFirstFrame ? (int)RhEHFrameType.RH_EH_FIRST_FRAME : 0) | + (isFirstRethrowFrame ? (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME : 0); +#if NATIVEAOT IntPtr pAppendStackFrame = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress(ip, ClassLibFunctionId.AppendExceptionStackFrame); if (pAppendStackFrame != IntPtr.Zero) { - int flags = (isFirstFrame ? (int)RhEHFrameType.RH_EH_FIRST_FRAME : 0) | - (isFirstRethrowFrame ? (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME : 0); - try { ((delegate*)pAppendStackFrame)(exception, ip, flags); @@ -241,6 +292,17 @@ private static void AppendExceptionStackFrameViaClasslib(object exception, IntPt isFirstRethrowFrame = false; isFirstFrame = false; } +#else +#pragma warning disable CS8500 + fixed (EH.ExInfo* pExInfo = &exInfo) + { + InternalCalls.RhpAppendExceptionStackFrame(ObjectHandleOnStack.Create(ref exception), ip, sp, flags, pExInfo); + } +#pragma warning restore CS8500 + // Clear flags only if we called the function + isFirstRethrowFrame = false; + isFirstFrame = false; +#endif } // Given an ExceptionID and an address pointing somewhere into a managed module, get @@ -248,6 +310,7 @@ private static void AppendExceptionStackFrameViaClasslib(object exception, IntPt // This finds the classlib-defined GetRuntimeException function and asks it for the exception object. internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) { +#if NATIVEAOT // Find the classlib function that will give us the exception object we want to throw. This // is a RuntimeExport function from the classlib module, and is therefore managed-callable. IntPtr pGetRuntimeExceptionFunction = @@ -263,7 +326,24 @@ internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) { // disallow all exceptions leaking out of callbacks } - +#else + Exception? e = id switch + { + ExceptionIDs.AccessViolation => new AccessViolationException(), + ExceptionIDs.Arithmetic => new ArithmeticException(), + ExceptionIDs.AmbiguousImplementation => new AmbiguousImplementationException(), + ExceptionIDs.ArrayTypeMismatch => new ArrayTypeMismatchException(), + ExceptionIDs.DataMisaligned => new DataMisalignedException(), + ExceptionIDs.DivideByZero => new DivideByZeroException(), + ExceptionIDs.EntrypointNotFound => new EntryPointNotFoundException(), + ExceptionIDs.IndexOutOfRange => new IndexOutOfRangeException(), + ExceptionIDs.InvalidCast => new InvalidCastException(), + ExceptionIDs.NullReference => new NullReferenceException(), + ExceptionIDs.OutOfMemory => new OutOfMemoryException(), + ExceptionIDs.Overflow => new OverflowException(), + _ => null + }; +#endif // If the helper fails to yield an object, then we fail-fast. if (e == null) { @@ -272,7 +352,7 @@ internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) return e; } - +#if NATIVEAOT // Given an ExceptionID and an MethodTable address, get an exception object of a type that the module containing // the given address will understand. This finds the classlib-defined GetRuntimeException function and asks // it for the exception object. @@ -367,6 +447,7 @@ public static Exception GetRuntimeException(ExceptionIDs id) } } #endif +#endif // NATIVEAOT private enum HwExceptionCode : uint { @@ -379,12 +460,15 @@ private enum HwExceptionCode : uint STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094u, STATUS_INTEGER_OVERFLOW = 0xC0000095u, } - [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__PAL_LIMITED_CONTEXT)] public struct PAL_LIMITED_CONTEXT { [FieldOffset(AsmOffsets.OFFSETOF__PAL_LIMITED_CONTEXT__IP)] internal IntPtr IP; +#if !NATIVEAOT + [FieldOffset(AsmOffsets.OFFSETOF__PAL_LIMITED_CONTEXT__FP)] + internal UIntPtr FP; +#endif // the rest of the struct is left unspecified. } @@ -475,14 +559,17 @@ internal object ThrownException // // Called by RhpThrowHwEx // +#if NATIVEAOT [RuntimeExport("RhThrowHwEx")] +#endif public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) { +#if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point GCStress.TriggerGC(); InternalCalls.RhpValidateExInfoStack(); - +#endif IntPtr faultingCodeAddress = exInfo._pExContext->IP; bool instructionFault = true; ExceptionIDs exceptionId = default(ExceptionIDs); @@ -502,9 +589,11 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) exceptionId = ExceptionIDs.NullReference; break; +#if NATIVEAOT case (uint)HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT: exceptionToThrow = InternalCalls.RhpGetThreadAbortException(); break; +#endif case (uint)HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: exceptionId = ExceptionIDs.DataMisaligned; @@ -544,14 +633,17 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) private const uint MaxTryRegionIdx = 0xFFFFFFFFu; +#if NATIVEAOT [RuntimeExport("RhThrowEx")] +#endif public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) { +#if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point GCStress.TriggerGC(); InternalCalls.RhpValidateExInfoStack(); - +#endif // Transform attempted throws of null to a throw of NullReferenceException. if (exceptionObj == null) { @@ -564,14 +656,17 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo) FallbackFailFast(RhFailFastReason.InternalError, null); } +#if NATIVEAOT [RuntimeExport("RhRethrow")] +#endif public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) { +#if NATIVEAOT // trigger a GC (only if gcstress) to ensure we can stackwalk at this point GCStress.TriggerGC(); InternalCalls.RhpValidateExInfoStack(); - +#endif // We need to copy the exception object to this stack location because collided unwinds // will cause the original stack location to go dead. object rethrownException = activeExInfo.ThrownException; @@ -613,17 +708,17 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn uint startIdx = MaxTryRegionIdx; for (; isValid; isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke)) { + prevControlPC = frameIter.ControlPC; + prevOriginalPC = frameIter.OriginalControlPC; + // 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; - prevControlPC = frameIter.ControlPC; - prevOriginalPC = frameIter.OriginalControlPC; - DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); - UpdateStackTrace(exceptionObj, exInfo._frameIter.FramePointer, (IntPtr)frameIter.OriginalControlPC, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame); + UpdateStackTrace(exceptionObj, exInfo._frameIter.FramePointer, (IntPtr)frameIter.OriginalControlPC, frameIter.SP, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame, ref exInfo); byte* pHandler; if (FindFirstPassHandler(exceptionObj, startIdx, ref frameIter, @@ -637,9 +732,10 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn } } -#if FEATURE_OBJCMARSHAL if (unwoundReversePInvoke) { +#if NATIVEAOT +#if FEATURE_OBJCMARSHAL // We did not find any managed handlers before hitting a reverse P/Invoke boundary. // See if the classlib has a handler to propagate the exception to native code. IntPtr pGetHandlerClasslibFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress((IntPtr)prevControlPC, @@ -656,10 +752,18 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn catchingTryRegionIdx = MaxTryRegionIdx; } } - } #endif // FEATURE_OBJCMARSHAL +#else // !NATIVEAOT + handlingFrameSP = frameIter.SP; + catchingTryRegionIdx = MaxTryRegionIdx; +#endif // !NATIVEAOT + } - if (pCatchHandler == null && pReversePInvokePropagationCallback == IntPtr.Zero) + if (pCatchHandler == null && pReversePInvokePropagationCallback == IntPtr.Zero +#if !NATIVEAOT + && !unwoundReversePInvoke +#endif + ) { OnUnhandledExceptionViaClassLib(exceptionObj); @@ -672,7 +776,7 @@ 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, "We should have a handler if we're starting the second pass"); + Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke, "We should have a handler if we're starting the second pass"); // ------------------------------------------------ // @@ -685,9 +789,11 @@ 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; unwoundReversePInvoke = false; isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0); @@ -698,9 +804,11 @@ 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.SP == handlingFrameSP, "Encountered a different reverse P/Invoke frame in the second pass."); 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. break; @@ -723,9 +831,11 @@ 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.Assert(false, "unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); } @@ -738,8 +848,18 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn // // ------------------------------------------------ exInfo._idxCurClause = catchingTryRegionIdx; +#if NATIVEAOT InternalCalls.RhpCallCatchFunclet( exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); +#else // NATIVEAOT +#pragma warning disable CS8500 + fixed (EH.ExInfo* pExInfo = &exInfo) + { + InternalCalls.RhpCallCatchFunclet( + ObjectHandleOnStack.Create(ref exceptionObj), pCatchHandler, frameIter.RegisterSet, pExInfo); + } +#pragma warning restore CS8500 +#endif // NATIVEAOT // currently, RhpCallCatchFunclet will resume after the catch Debug.Assert(false, "unreachable"); FallbackFailFast(RhFailFastReason.InternalError, null); @@ -759,8 +879,8 @@ private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP) "Handling frame must have a valid stack frame pointer"); } - private static void UpdateStackTrace(object exceptionObj, UIntPtr curFramePtr, IntPtr ip, - ref bool isFirstRethrowFrame, ref UIntPtr prevFramePtr, ref bool isFirstFrame) + private static void UpdateStackTrace(object exceptionObj, UIntPtr curFramePtr, IntPtr ip, UIntPtr sp, + ref bool isFirstRethrowFrame, ref UIntPtr prevFramePtr, ref bool isFirstFrame, ref ExInfo exInfo) { // We use the fact that all funclet stack frames belonging to the same logical method activation // will have the same FramePointer value. Additionally, the stackwalker will return a sequence of @@ -768,9 +888,11 @@ private static void UpdateStackTrace(object exceptionObj, UIntPtr curFramePtr, I // want to know about funclets, so we strip them out by only reporting the first frame of a // sequence of funclets. This is correct because the leafmost funclet is first in the sequence // and corresponds to the current 'IP state' of the method. +#if NATIVEAOT if ((prevFramePtr == UIntPtr.Zero) || (curFramePtr != prevFramePtr)) +#endif { - AppendExceptionStackFrameViaClasslib(exceptionObj, ip, + AppendExceptionStackFrameViaClasslib(exceptionObj, ip, sp, ref exInfo, ref isFirstRethrowFrame, ref isFirstFrame); } prevFramePtr = curFramePtr; @@ -811,7 +933,11 @@ private static bool FindFirstPassHandler(object exception, uint idxStart, // 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 ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd) +#if !NATIVEAOT + && (ehClause._isSameTry) +#endif + ) continue; // We are done skipping. This is required to handle empty finally block markers that are used @@ -832,7 +958,7 @@ private static bool FindFirstPassHandler(object exception, uint idxStart, // most containing. if (clauseKind == RhEHClauseKind.RH_EH_CLAUSE_TYPED) { - if (ShouldTypedClauseCatchThisException(exception, (MethodTable*)ehClause._pTargetType)) + if (ShouldTypedClauseCatchThisException(exception, (MethodTable*)ehClause._pTargetType, !frameIter.IsRuntimeWrappedExceptions)) { pHandler = ehClause._handlerAddress; tryRegionIdx = curIdx; @@ -847,7 +973,11 @@ private static bool FindFirstPassHandler(object exception, uint idxStart, try { shouldInvokeHandler = +#if NATIVEAOT InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); +#else + InternalCalls.RhpCallFilterFunclet(ObjectHandleOnStack.Create(ref exception), pFilterFunclet, frameIter.RegisterSet); +#endif } catch when (true) { @@ -866,7 +996,7 @@ private static bool FindFirstPassHandler(object exception, uint idxStart, return false; } -#if DEBUG && !INPLACE_RUNTIME +#if DEBUG && !INPLACE_RUNTIME && NATIVEAOT private static MethodTable* s_pLowLevelObjectType; private static void AssertNotRuntimeObject(MethodTable* pClauseType) { @@ -888,16 +1018,42 @@ private static void AssertNotRuntimeObject(MethodTable* pClauseType) Debug.Assert(!pClauseType->IsEquivalentTo(s_pLowLevelObjectType)); } -#endif // DEBUG && !INPLACE_RUNTIME +#endif // DEBUG && !INPLACE_RUNTIME && NATIVEAOT - private static bool ShouldTypedClauseCatchThisException(object exception, MethodTable* pClauseType) + private static bool ShouldTypedClauseCatchThisException(object exception, MethodTable* pClauseType, bool tryUnwrapException) { +#if NATIVEAOT #if DEBUG && !INPLACE_RUNTIME AssertNotRuntimeObject(pClauseType); #endif return TypeCast.IsInstanceOfException(pClauseType, exception); +#else + bool retry = false; + do + { + MethodTable* mt = RuntimeHelpers.GetMethodTable(exception); + while (mt != null) + { + if (pClauseType == mt) + { + return true; + } + + mt = mt->ParentMethodTable; + } + + if (tryUnwrapException && exception is RuntimeWrappedException ex) + { + exception = ex.WrappedException; + retry = true; + } + } + while (retry); + + return false; +#endif } private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart) @@ -935,7 +1091,11 @@ 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 ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd) +#if !NATIVEAOT + && (ehClause._isSameTry) +#endif + ) continue; // We are done skipping. This is required to handle empty finally block markers that are used @@ -966,11 +1126,21 @@ 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 +#pragma warning disable CS8500 + fixed (EH.ExInfo* pExInfo = &exInfo) + { + InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet, pExInfo); + } +#pragma warning restore CS8500 +#endif // NATIVEAOT exInfo._idxCurClause = MaxTryRegionIdx; } } +#if NATIVEAOT #pragma warning disable IDE0060 [UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp", CallConvs = new Type[] { typeof(CallConvCdecl) })] public static void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord) @@ -983,6 +1153,6 @@ public static void RhpFailFastForPInvokeExceptionCoop(IntPtr classlibBreadcrumb, FailFastViaClasslib(RhFailFastReason.UnhandledExceptionFromPInvoke, null, classlibBreadcrumb); } #pragma warning restore IDE0060 - +#endif } // static class EH } diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs index 022a964020c18..764bd124d6774 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionIDs.cs @@ -3,7 +3,12 @@ namespace System.Runtime { - public enum ExceptionIDs +#if NATIVEAOT + public +#else + internal +#endif + enum ExceptionIDs { OutOfMemory = 1, Arithmetic = 2, 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 9a772bd3e3370..0fb4935828520 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; +#if !NATIVEAOT +using System.Runtime.ExceptionServices; +#endif namespace System.Runtime { @@ -10,11 +13,31 @@ internal unsafe struct REGDISPLAY { [FieldOffset(AsmOffsets.OFFSETOF__REGDISPLAY__SP)] internal UIntPtr SP; +#if !NATIVEAOT + [FieldOffset(AsmOffsets.OFFSETOF__REGDISPLAY__ControlPC)] + internal IntPtr ControlPC; + [FieldOffset(AsmOffsets.OFFSETOF__REGDISPLAY__m_pCurrentContext)] + internal EH.PAL_LIMITED_CONTEXT* m_pCurrentContext; +#endif } [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__StackFrameIterator)] internal unsafe struct StackFrameIterator { +#if !NATIVEAOT + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_pRegDisplay)] + private REGDISPLAY* _pRegDisplay; + + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_AdjustedControlPC)] + internal byte* ControlPC; + internal byte* OriginalControlPC { get { return (byte*)_pRegDisplay->ControlPC; } } + internal void* RegisterSet { get { return _pRegDisplay; } } + internal UIntPtr SP { get { return _pRegDisplay->SP; } } + internal UIntPtr FramePointer { get { return _pRegDisplay->m_pCurrentContext->FP; } } + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions)] + private byte _IsRuntimeWrappedExceptions; + internal bool IsRuntimeWrappedExceptions {get { return _IsRuntimeWrappedExceptions != 0; }} +#else // NATIVEAOT [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_FramePointer)] private UIntPtr _framePointer; [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_ControlPC)] @@ -32,6 +55,10 @@ internal unsafe struct StackFrameIterator internal UIntPtr SP { get { return _regDisplay.SP; } } internal UIntPtr FramePointer { get { return _framePointer; } } internal IntPtr PreviousTransitionFrame { get { return _pPreviousTransitionFrame; } } +#pragma warning disable CA1822 + internal bool IsRuntimeWrappedExceptions {get { return false; }} +#pragma warning restore CA1822 +#endif // NATIVEAOT internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx, bool instructionFault = false) { diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 16ae71c162a47..d8054a250af41 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -71,6 +71,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON eetwain.cpp encee.cpp excep.cpp + exinfo.cpp exstate.cpp field.cpp formattype.cpp @@ -168,6 +169,7 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON eehash.inl encee.h excep.h + exinfo.h exstate.h field.h fptrstubs.h @@ -707,6 +709,7 @@ else(CLR_CMAKE_TARGET_WIN32) ${ARCH_SOURCES_DIR}/redirectedhandledjitcase.S ${ARCH_SOURCES_DIR}/theprestubamd64.S ${ARCH_SOURCES_DIR}/thunktemplates.S + ${ARCH_SOURCES_DIR}/Context.S ${ARCH_SOURCES_DIR}/unixasmhelpers.S ${ARCH_SOURCES_DIR}/umthunkstub.S ${ARCH_SOURCES_DIR}/virtualcallstubamd64.S @@ -788,7 +791,6 @@ if(CLR_CMAKE_TARGET_ARCH_AMD64) ) elseif(CLR_CMAKE_TARGET_ARCH_I386) set(VM_SOURCES_DAC_AND_WKS_ARCH - exinfo.cpp ${ARCH_SOURCES_DIR}/cgenx86.cpp ${ARCH_SOURCES_DIR}/excepx86.cpp ${ARCH_SOURCES_DIR}/gmsx86.cpp @@ -796,7 +798,6 @@ elseif(CLR_CMAKE_TARGET_ARCH_I386) ) set(VM_HEADERS_DAC_AND_WKS_ARCH - exinfo.h ${ARCH_SOURCES_DIR}/cgencpu.h ${ARCH_SOURCES_DIR}/excepcpu.h ${ARCH_SOURCES_DIR}/gmscpu.h diff --git a/src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm b/src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm index 8d4e4e5094293..af0ca575b0566 100644 --- a/src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm +++ b/src/coreclr/vm/amd64/CallDescrWorkerAMD64.asm @@ -94,7 +94,7 @@ Arg4: movsd xmm3, real8 ptr 18h[rsp]; DoCall: call qword ptr [rbx+CallDescrData__pTarget] ; call target function - +CallDescrWorkerInternalReturnAddress: ; Save FP return value mov ecx, dword ptr [rbx+CallDescrData__fpReturnSize] @@ -126,6 +126,9 @@ ReturnsDouble: movsd real8 ptr [rbx+CallDescrData__returnValue], xmm0 jmp Epilog +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + dq CallDescrWorkerInternalReturnAddress - CallDescrWorkerInternal + NESTED_END CallDescrWorkerInternal, _TEXT end diff --git a/src/coreclr/vm/amd64/Context.S b/src/coreclr/vm/amd64/Context.S new file mode 100644 index 0000000000000..0721c45f56727 --- /dev/null +++ b/src/coreclr/vm/amd64/Context.S @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.intel_syntax noprefix +#include "unixasmmacros.inc" +#include "asmconstants.h" + +// Some constants for CONTEXT.ContextFlags. The arch bit (CONTEXT_AMD64) is normally set in these flag constants below. Since +// this is already arch-specific code and the arch bit is not relevant, the arch bit is excluded from the flag constants below +// for simpler tests. +#define CONTEXT_CONTROL 1 +#define CONTEXT_INTEGER 2 + +// Signature: EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); +NESTED_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT, NoHandler + push_nonvol_reg rbp + set_cfa_register rbp, 0 + END_PROLOGUE + + mov r10, rdi + mov r11, rsi + + test byte ptr [r10 + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_INTEGER + je Done_Restore_CONTEXT_INTEGER + mov rbx, [r10 + OFFSETOF__CONTEXT__Rbx] + mov rcx, [r10 + OFFSETOF__CONTEXT__Rcx] + mov rdx, [r10 + OFFSETOF__CONTEXT__Rdx] + mov r8, [r10 + OFFSETOF__CONTEXT__R8] + mov r9, [r10 + OFFSETOF__CONTEXT__R9] + mov rbp, [r10 + OFFSETOF__CONTEXT__Rbp] + mov rsi, [r10 + OFFSETOF__CONTEXT__Rsi] + mov rdi, [r10 + OFFSETOF__CONTEXT__Rdi] + mov r12, [r10 + OFFSETOF__CONTEXT__R12] + mov r13, [r10 + OFFSETOF__CONTEXT__R13] + mov r14, [r10 + OFFSETOF__CONTEXT__R14] + mov r15, [r10 + OFFSETOF__CONTEXT__R15] + Done_Restore_CONTEXT_INTEGER: + + test byte ptr [r10 + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_CONTROL + je Done_Restore_CONTEXT_CONTROL + + test r11, r11 + je No_Ssp_Update + rdsspq rax + sub r11, rax + shr r11, 3 + incsspq r11 + No_Ssp_Update: + + // When user-mode shadow stacks are enabled, and for example the intent is to continue execution in managed code after + // exception handling, iret and ret can't be used because their shadow stack enforcement would not allow that transition, + // and using them would require writing to the shadow stack, which is not preferable. Instead, iret is partially + // simulated. + mov rsp, [r10 + OFFSETOF__CONTEXT__Rsp] + jmp qword ptr [r10 + OFFSETOF__CONTEXT__Rip] + Done_Restore_CONTEXT_CONTROL: + + // The function was not asked to restore the control registers so we return back to the caller + pop rbp + ret +NESTED_END ClrRestoreNonvolatileContextWorker, _TEXT diff --git a/src/coreclr/vm/amd64/Context.asm b/src/coreclr/vm/amd64/Context.asm index 71bc2566f8acc..a7165983cf4fa 100644 --- a/src/coreclr/vm/amd64/Context.asm +++ b/src/coreclr/vm/amd64/Context.asm @@ -21,43 +21,50 @@ NESTED_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT set_frame rbp, 0 END_PROLOGUE - test byte ptr [rcx + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_FLOATING_POINT + mov r10, rcx + mov r11, rdx + + test byte ptr [r10 + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_FLOATING_POINT je Done_Restore_CONTEXT_FLOATING_POINT - fxrstor [rcx + OFFSETOF__CONTEXT__FltSave] + fxrstor [r10 + OFFSETOF__CONTEXT__FltSave] Done_Restore_CONTEXT_FLOATING_POINT: - test byte ptr [rcx + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_INTEGER + test byte ptr [r10 + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_INTEGER je Done_Restore_CONTEXT_INTEGER - mov rbx, [rcx + OFFSETOF__CONTEXT__Rbx] - mov rbp, [rcx + OFFSETOF__CONTEXT__Rbp] - mov rsi, [rcx + OFFSETOF__CONTEXT__Rsi] - mov rdi, [rcx + OFFSETOF__CONTEXT__Rdi] - mov r12, [rcx + OFFSETOF__CONTEXT__R12] - mov r13, [rcx + OFFSETOF__CONTEXT__R13] - mov r14, [rcx + OFFSETOF__CONTEXT__R14] - mov r15, [rcx + OFFSETOF__CONTEXT__R15] + mov rbx, [r10 + OFFSETOF__CONTEXT__Rbx] + mov rcx, [r10 + OFFSETOF__CONTEXT__Rcx] + mov rdx, [r10 + OFFSETOF__CONTEXT__Rdx] + mov r8, [r10 + OFFSETOF__CONTEXT__R8] + mov r9, [r10 + OFFSETOF__CONTEXT__R9] + mov rbp, [r10 + OFFSETOF__CONTEXT__Rbp] + mov rsi, [r10 + OFFSETOF__CONTEXT__Rsi] + mov rdi, [r10 + OFFSETOF__CONTEXT__Rdi] + mov r12, [r10 + OFFSETOF__CONTEXT__R12] + mov r13, [r10 + OFFSETOF__CONTEXT__R13] + mov r14, [r10 + OFFSETOF__CONTEXT__R14] + mov r15, [r10 + OFFSETOF__CONTEXT__R15] Done_Restore_CONTEXT_INTEGER: - test byte ptr [rcx + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_CONTROL + test byte ptr [r10 + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_CONTROL je Done_Restore_CONTEXT_CONTROL - test rdx, rdx + test r11, r11 je No_Ssp_Update rdsspq rax - sub rdx, rax - shr rdx, 3 - incsspq rdx + sub r11, rax + shr r11, 3 + incsspq r11 No_Ssp_Update: ; When user-mode shadow stacks are enabled, and for example the intent is to continue execution in managed code after ; exception handling, iret and ret can't be used because their shadow stack enforcement would not allow that transition, ; and using them would require writing to the shadow stack, which is not preferable. Instead, iret is partially ; simulated. - mov eax, [rcx + OFFSETOF__CONTEXT__EFlags] + mov eax, [r10 + OFFSETOF__CONTEXT__EFlags] push rax popfq - mov rsp, [rcx + OFFSETOF__CONTEXT__Rsp] - jmp qword ptr [rcx + OFFSETOF__CONTEXT__Rip] + mov rsp, [r10 + OFFSETOF__CONTEXT__Rsp] + jmp qword ptr [r10 + OFFSETOF__CONTEXT__Rip] Done_Restore_CONTEXT_CONTROL: ; The function was not asked to restore the control registers so we return back to the caller diff --git a/src/coreclr/vm/amd64/calldescrworkeramd64.S b/src/coreclr/vm/amd64/calldescrworkeramd64.S index 532192bf4fb9f..329979c120a64 100644 --- a/src/coreclr/vm/amd64/calldescrworkeramd64.S +++ b/src/coreclr/vm/amd64/calldescrworkeramd64.S @@ -95,7 +95,7 @@ LOCAL_LABEL(NoStackArguments): movsd xmm7, [rax + 112] LOCAL_LABEL(NoFloatArguments): call qword ptr [rbx + CallDescrData__pTarget] // call target function - +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): // Save FP return value mov ecx, dword ptr [rbx + CallDescrData__fpReturnSize] @@ -159,6 +159,7 @@ LOCAL_LABEL(ReturnsDouble): movsd real8 ptr [rbx+CallDescrData__returnValue], xmm0 jmp LOCAL_LABEL(Epilog) -NESTED_END CallDescrWorkerInternal, _TEXT - +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .quad LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) +NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index fbd0351dd1c73..9603883ed8694 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -342,7 +342,7 @@ inline void SetSP(CONTEXT *context, TADDR rsp) context->Rsp = rsp; } -#if defined(TARGET_WINDOWS) && !defined(DACCESS_COMPILE) +#if !defined(DACCESS_COMPILE) inline DWORD64 GetSSP(const CONTEXT * context) { CONTRACTL @@ -353,13 +353,15 @@ inline DWORD64 GetSSP(const CONTEXT * context) PRECONDITION(CheckPointer(context)); } CONTRACTL_END; - +#ifdef TARGET_WINDOWS XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(const_cast(context), XSTATE_CET_U, NULL); if ((pCET != NULL) && (pCET->Ia32CetUMsr != 0)) { return pCET->Ia32Pl3SspMsr; } - +#else + // TODO: implement when we enable Intel CET on Unix +#endif return 0; } @@ -374,14 +376,18 @@ inline void SetSSP(CONTEXT *context, DWORD64 ssp) } CONTRACTL_END; +#ifdef TARGET_WINDOWS XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(context, XSTATE_CET_U, NULL); if (pCET != NULL) { pCET->Ia32Pl3SspMsr = ssp; pCET->Ia32CetUMsr = 1; } +#else + // TODO: implement when we enable Intel CET on Unix +#endif } -#endif // TARGET_WINDOWS && !DACCESS_COMPILE +#endif // !DACCESS_COMPILE #define SetFP(context, ebp) inline TADDR GetFP(const CONTEXT * context) diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index ab6a99b5af76a..3ae68e8d46034 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1399,6 +1399,10 @@ void SystemDomain::LoadBaseSystemClasses() g_pICastableInterface = CoreLibBinder::GetClass(CLASS__ICASTABLE); #endif // FEATURE_ICASTABLE +#ifdef FEATURE_EH_FUNCLETS + g_pEHClass = CoreLibBinder::GetClass(CLASS__EH); +#endif + // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper. // For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)". ECall::GetFCallImpl(CoreLibBinder::GetMethod(METHOD__MONITOR__ENTER)); diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index fbe856a6a43e3..cff06940290fb 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -71,6 +71,7 @@ LOCAL_LABEL(LNoFloatingPoint): // Note that remoting expect target in r4. ldr r4, [r5,#CallDescrData__pTarget] blx r4 +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): ldr r3, [r5,#CallDescrData__fpReturnSize] @@ -126,6 +127,9 @@ LOCAL_LABEL(LReturnDone): EPILOG_STACK_RESTORE_OFFSET r7, #8 EPILOG_POP "{r4,r5,r7,pc}" +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .word LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) + NESTED_END CallDescrWorkerInternal,_TEXT // ------------------------------------------------------------------ diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 36eaeb51cdc5d..771d244096702 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -1585,6 +1585,7 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->pContext = NULL; *(pRD->pPC) = m_pCallerReturnAddress; + pRD->ControlPC = m_pCallerReturnAddress; pRD->SP = (DWORD) dac_cast(m_pCallSiteSP); pRD->IsCallerContextValid = FALSE; diff --git a/src/coreclr/vm/arm64/CallDescrWorkerARM64.asm b/src/coreclr/vm/arm64/CallDescrWorkerARM64.asm index 502d5a84d2306..f8290ca633b29 100644 --- a/src/coreclr/vm/arm64/CallDescrWorkerARM64.asm +++ b/src/coreclr/vm/arm64/CallDescrWorkerARM64.asm @@ -4,6 +4,7 @@ #include "ksarm64.h" #include "asmconstants.h" +#include "asmmacros.h" @@ -72,6 +73,7 @@ LNoFloatingPoint ;; call pTarget ldr x9, [x19,#CallDescrData__pTarget] blr x9 +LCallDescrWorkerInternalReturnAddress ldr w3, [x19,#CallDescrData__fpReturnSize] @@ -140,6 +142,10 @@ LReturnDone EPILOG_RESTORE_REG x19, #16 ;the stack slot at sp+24 is empty for 16 byte alligment EPILOG_RESTORE_REG_PAIR fp, lr, #32! EPILOG_RETURN + + PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + DCQ LCallDescrWorkerInternalReturnAddress - CallDescrWorkerInternal + NESTED_END END diff --git a/src/coreclr/vm/arm64/calldescrworkerarm64.S b/src/coreclr/vm/arm64/calldescrworkerarm64.S index ccb4c3f73b649..06b8834435946 100644 --- a/src/coreclr/vm/arm64/calldescrworkerarm64.S +++ b/src/coreclr/vm/arm64/calldescrworkerarm64.S @@ -69,6 +69,7 @@ LOCAL_LABEL(NoFloatingPoint): // call pTarget ldr x9, [x19,#CallDescrData__pTarget] blr x9 +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): ldr w3, [x19,#CallDescrData__fpReturnSize] @@ -138,4 +139,8 @@ LOCAL_LABEL(ReturnDone): EPILOG_RESTORE_REG x19, 16 //the stack slot at sp+24 is empty for 16 byte alligment EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32 EPILOG_RETURN + +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .quad LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) + NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 4ae26363fd2d2..cc5dbf7d66b8d 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -699,7 +699,7 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->pCurrentContextPointers->X28 = NULL; pRD->ControlPC = m_pCallerReturnAddress; - pRD->SP = (DWORD) dac_cast(m_pCallSiteSP); + pRD->SP = (DWORD64) dac_cast(m_pCallSiteSP); // reset pContext; it's only valid for active (top-most) frame pRD->pContext = NULL; diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 709ec1e504298..a5ec98ccab0e4 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -1453,7 +1453,7 @@ BOOL Module::IsRuntimeWrapExceptions() { CONTRACTL { - THROWS; + NOTHROW; if (IsRuntimeWrapExceptionsStatusComputed()) GC_NOTRIGGER; else GC_TRIGGERS; MODE_ANY; } diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 8e547e4cc17ce..6fe87885da111 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -2635,7 +2635,7 @@ class EECodeInfo #if defined(TARGET_AMD64) void GetOffsetsFromUnwindInfo(ULONG* pRSPOffset, ULONG* pRBPOffset); - + ULONG GetFrameOffsetFromUnwindInfo(); #if defined(_DEBUG) && defined(HAVE_GCCOVER) // Find first funclet inside (pvFuncletStart, pvFuncletStart + cbCode) static LPVOID findNextFunclet (LPVOID pvFuncletStart, SIZE_T cbCode, LPVOID *ppvFuncletEnd); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 96576a8581312..0dd79fb124f82 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1183,6 +1183,17 @@ DEFINE_METHOD(CASTHELPERS, UNBOX, Unbox, SM_Ptr DEFINE_METHOD(CASTHELPERS, STELEMREF, StelemRef, SM_Array_IntPtr_Obj_RetVoid) DEFINE_METHOD(CASTHELPERS, LDELEMAREF, LdelemaRef, SM_Array_IntPtr_PtrVoid_RetRefObj) +#ifdef FEATURE_EH_FUNCLETS +DEFINE_CLASS(EH, Runtime, EH) +DEFINE_METHOD(EH, RH_THROW_EX, RhThrowEx, SM_Obj_RefExInfo_RetVoid) +DEFINE_METHOD(EH, RH_THROWHW_EX, RhThrowHwEx, SM_UInt_RefExInfo_RetVoid) +DEFINE_METHOD(EH, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_RetVoid) +#endif // FEATURE_EH_FUNCLETS + +#ifndef FOR_ILLINK +DEFINE_CLASS(EXINFO, Runtime, EH+ExInfo) +#endif // FOR_ILLINK + DEFINE_CLASS_U(System, GCMemoryInfoData, GCMemoryInfoData) DEFINE_FIELD_U(_highMemoryLoadThresholdBytes, GCMemoryInfoData, highMemLoadThresholdBytes) DEFINE_FIELD_U(_totalAvailableMemoryBytes, GCMemoryInfoData, totalAvailableMemoryBytes) diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index bb06173bca523..6d8a2c299951f 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -4458,7 +4458,7 @@ FCIMPL1(void, GCReporting::Unregister, GCFrame* frame) // Destroy the GCFrame. _ASSERTE(frame != NULL); - frame->Pop(); + frame->Remove(); } FCIMPLEND #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 5be645ad496d7..d40acb1586566 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -55,12 +55,7 @@ #include "gccover.h" #endif // HAVE_GCCOVER -#ifndef TARGET_UNIX -// Windows uses 64kB as the null-reference area -#define NULL_AREA_SIZE (64 * 1024) -#else // !TARGET_UNIX -#define NULL_AREA_SIZE GetOsPageSize() -#endif // !TARGET_UNIX +#include "exinfo.h" //---------------------------------------------------------------------------- // @@ -2879,6 +2874,22 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable) RealCOMPlusThrow(throwable, FALSE); } +#ifdef USE_CHECKED_OBJECTREFS +VOID DECLSPEC_NORETURN RealCOMPlusThrow(Object *exceptionObj) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + OBJECTREF throwable = ObjectToOBJECTREF(exceptionObj); + RealCOMPlusThrow(throwable, FALSE); +} +#endif // USE_CHECKED_OBJECTREFS + // this function finds the managed callback to get a resource // string from the then current local domain and calls it // this could be a lot of work @@ -6553,6 +6564,46 @@ static LONG HandleManagedFaultFilter(EXCEPTION_POINTERS* ep, LPVOID pv) return EXCEPTION_CONTINUE_SEARCH; } +void HandleManagedFaultNew(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) +{ + WRAPPER_NO_CONTRACT; + + FrameWithCookie frameWithCookie; + FaultingExceptionFrame *frame = &frameWithCookie; +#if defined(FEATURE_EH_FUNCLETS) + *frame->GetGSCookiePtr() = GetProcessGSCookie(); +#endif // FEATURE_EH_FUNCLETS + 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); + + DWORD exceptionCode = pExceptionRecord->ExceptionCode; + if (exceptionCode == STATUS_ACCESS_VIOLATION) + { + if (pExceptionRecord->ExceptionInformation[1] < NULL_AREA_SIZE) + { + exceptionCode = 0; //STATUS_REDHAWK_NULL_REFERENCE; + } + } + + GCPROTECT_BEGIN(exInfo.m_exception); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + + //Ex.RhThrowHwEx(exceptionCode, &exInfo) + CALL_MANAGED_METHOD_NORET(args) + + GCPROTECT_END(); +} + void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) { WRAPPER_NO_CONTRACT; @@ -6822,6 +6873,12 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo // // Not an Out-of-memory situation, so no need for a forbid fault region here // +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + EEPolicy::HandleStackOverflow(); + } +#endif // FEATURE_EH_FUNCLETS return VEH_CONTINUE_SEARCH; } @@ -7517,7 +7574,14 @@ LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo) // // HandleManagedFault may never return, so we cannot use a forbid fault region around it. // - HandleManagedFault(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord); + if (g_isNewExceptionHandlingEnabled) + { + HandleManagedFaultNew(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord); + } + else + { + HandleManagedFault(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord); + } return EXCEPTION_CONTINUE_EXECUTION; } #endif // FEATURE_EH_FUNCLETS @@ -7723,7 +7787,16 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra Exception::Delete(pException); - RaiseTheExceptionInternalOnly(orThrowable, FALSE); +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + DispatchManagedException(orThrowable); + } + else +#endif // FEATURE_EH_FUNCLETS + { + RaiseTheExceptionInternalOnly(orThrowable, FALSE); + } } thread_local DWORD t_dwCurrentExceptionCode; @@ -9294,8 +9367,17 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp) // being thrown, then get them. ThreadExceptionState *pExState = pThread->GetExceptionState(); +#ifdef FEATURE_EH_FUNCLETS // Ensure that the exception tracker exists - _ASSERTE(pExState->GetCurrentExceptionTracker() != NULL); + if (g_isNewExceptionHandlingEnabled) + { + _ASSERTE(pExState->GetCurrentExInfo() != NULL); + } + else +#endif // FEATURE_EH_FUNCLETS + { + _ASSERTE(pExState->GetCurrentExceptionTracker() != NULL); + } // Switch to COOP mode GCX_COOP(); @@ -9319,7 +9401,18 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp) BOOL fIsPreallocatedException = CLRException::IsPreallocatedExceptionObject(gc.oCurrentThrowable); // Get the WatsonBucketTracker for the current exception - PTR_EHWatsonBucketTracker pWatsonBucketTracker = pExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker(); + PTR_EHWatsonBucketTracker pWatsonBucketTracker; +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + pWatsonBucketTracker = pExState->GetCurrentExInfo()->GetWatsonBucketTracker(); + + } + else +#endif // FEATURE_EH_FUNCLETS + { + pWatsonBucketTracker = pExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker(); + } // Get the innermost exception object (if any) gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.oCurrentThrowable)->GetBaseException(); diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 082ba7b91a4c9..a1cc0556b0d2a 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -46,6 +46,13 @@ enum LFH { #include "runtimeexceptionkind.h" +#ifndef TARGET_UNIX +// Windows uses 64kB as the null-reference area +#define NULL_AREA_SIZE (64 * 1024) +#else // !TARGET_UNIX +#define NULL_AREA_SIZE GetOsPageSize() +#endif // !TARGET_UNIX + class IJitManager; // @@ -249,6 +256,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowNonLocalized(RuntimeExceptionKind reKind, //========================================================================== VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable); +VOID DECLSPEC_NORETURN RealCOMPlusThrow(Object *exceptionObj); //========================================================================== // Throw an undecorated runtime exception. @@ -747,6 +755,9 @@ LONG WatsonLastChance( bool DebugIsEECxxException(EXCEPTION_RECORD* pExceptionRecord); +#ifndef FEATURE_EH_FUNCLETS +#define g_isNewExceptionHandlingEnabled false +#endif inline void CopyOSContext(T_CONTEXT* pDest, T_CONTEXT* pSrc) { @@ -756,6 +767,12 @@ inline void CopyOSContext(T_CONTEXT* pDest, T_CONTEXT* pSrc) #endif // TARGET_AMD64 memcpyNoGCRefs(pDest, pSrc, sizeof(T_CONTEXT) - cbReadOnlyPost); +#ifdef TARGET_AMD64 + if (g_isNewExceptionHandlingEnabled) + { + pDest->ContextFlags = (pDest->ContextFlags & ~(CONTEXT_XSTATE | CONTEXT_FLOATING_POINT)) | CONTEXT_AMD64; + } +#endif // TARGET_AMD64 } void SaveCurrentExceptionInfo(PEXCEPTION_RECORD pRecord, PT_CONTEXT pContext); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 0621ae03efe4f..9bbce840404f5 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -17,6 +17,8 @@ #include "utilcode.h" #include "interoplibinterface.h" #include "corinfo.h" +#include "exceptionhandlingqcalls.h" +#include "exinfo.h" #if defined(TARGET_X86) #define USE_CURRENT_CONTEXT_IN_FILTER @@ -234,6 +236,8 @@ void InitializeExceptionHandling() // Initialize the lock used for synchronizing access to the stacktrace in the exception object g_StackTraceArrayLock.Init(LOCK_TYPE_DEFAULT, TRUE); + g_isNewExceptionHandlingEnabled = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableNewExceptionHandling) != 0; + #ifdef TARGET_UNIX // Register handler of hardware exceptions like null reference in PAL PAL_SetHardwareExceptionHandler(HandleHardwareException, IsSafeToHandleHardwareException); @@ -862,6 +866,48 @@ UINT_PTR ExceptionTracker::FinishSecondPass( void CleanUpForSecondPass(Thread* pThread, bool fIsSO, LPVOID MemoryStackFpForFrameChain, LPVOID MemoryStackFp); +EXTERN_C EXCEPTION_DISPOSITION +ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, + IN PVOID pEstablisherFrame, + IN OUT PCONTEXT pContextRecord, + IN OUT PDISPATCHER_CONTEXT pDispatcherContext + ) +{ + // + // This method doesn't always return, so it will leave its + // state on the thread if using dynamic contracts. + // + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_THROWS; + +#ifndef HOST_UNIX + if (!(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) + { + Thread* pThread = GetThread(); + ClrUnwindEx(pExceptionRecord, + (UINT_PTR)pThread, + INVALID_RESUME_ADDRESS, + pDispatcherContext->EstablisherFrame); + } + else + { + GCX_COOP(); + FrameWithCookie frameWithCookie; + FaultingExceptionFrame *frame = &frameWithCookie; + #if defined(FEATURE_EH_FUNCLETS) + *frame->GetGSCookiePtr() = GetProcessGSCookie(); + #endif // FEATURE_EH_FUNCLETS + frame->InitAndLink(pContextRecord); + + OBJECTREF oref = ExceptionTracker::CreateThrowable(pExceptionRecord, FALSE); + DispatchManagedException(oref); + } +#endif // !HOST_UNIX + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, _T("SEH exception leaked into managed code")); + UNREACHABLE(); +} + EXTERN_C EXCEPTION_DISPOSITION ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN PVOID pEstablisherFrame, @@ -877,6 +923,12 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_THROWS; + if (g_isNewExceptionHandlingEnabled) + { + ProcessCLRExceptionNew(pExceptionRecord, pEstablisherFrame, pContextRecord, pDispatcherContext); + UNREACHABLE(); + } + // We must preserve this so that GCStress=4 eh processing doesnt kill last error. DWORD dwLastError = GetLastError(); @@ -4363,14 +4415,16 @@ static void DoEHLog( // Function to update the current context for exception propagation. // // Arguments: -// exception - the PAL_SEHException representing the propagating exception. +// callback - the exception propagation callback +// callbackCtx - the exception propagation callback context // currentContext - the current context to update. // static VOID UpdateContextForPropagationCallback( - PAL_SEHException& ex, + Interop::ManagedToNativeExceptionCallback callback, + void *callbackCtx, CONTEXT* startContext) { - _ASSERTE(ex.ManagedToNativeExceptionCallback != NULL); + _ASSERTE(callback != NULL); #ifdef TARGET_AMD64 @@ -4380,7 +4434,7 @@ static VOID UpdateContextForPropagationCallback( startContext->Rsp -= sizeof(void*); // Pass the context for the callback as the first argument. - startContext->Rdi = (DWORD64)ex.ManagedToNativeExceptionCallbackContext; + startContext->Rdi = (DWORD64)callbackCtx; #elif defined(TARGET_ARM64) @@ -4389,7 +4443,7 @@ static VOID UpdateContextForPropagationCallback( startContext->Lr = GetIP(startContext); // Pass the context for the callback as the first argument. - startContext->X0 = (DWORD64)ex.ManagedToNativeExceptionCallbackContext; + startContext->X0 = (DWORD64)callbackCtx; #elif defined(TARGET_ARM) @@ -4398,7 +4452,7 @@ static VOID UpdateContextForPropagationCallback( startContext->Lr = GetIP(startContext); // Pass the context for the callback as the first argument. - startContext->R0 = (DWORD)ex.ManagedToNativeExceptionCallbackContext; + startContext->R0 = (DWORD)callbackCtx; #else @@ -4409,7 +4463,22 @@ static VOID UpdateContextForPropagationCallback( #endif // The last thing to do is set the supplied callback function. - SetIP(startContext, (PCODE)ex.ManagedToNativeExceptionCallback); + SetIP(startContext, (PCODE)callback); +} + +//--------------------------------------------------------------------------------------- +// +// Function to update the current context for exception propagation. +// +// Arguments: +// exception - the PAL_SEHException representing the propagating exception. +// currentContext - the current context to update. +// +static VOID UpdateContextForPropagationCallback( + PAL_SEHException& ex, + CONTEXT* startContext) +{ + UpdateContextForPropagationCallback(ex.ManagedToNativeExceptionCallback, ex.ManagedToNativeExceptionCallbackContext, startContext); } //--------------------------------------------------------------------------------------- @@ -4840,6 +4909,13 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException) { + if (g_isNewExceptionHandlingEnabled) + { + GCX_COOP(); + OBJECTREF throwable = ExceptionTracker::CreateThrowable(ex.GetExceptionRecord(), FALSE); + DispatchManagedException(throwable); + } + do { try @@ -5362,7 +5438,39 @@ BOOL HandleHardwareException(PAL_SEHException* ex) fef.InitAndLink(ex->GetContextRecord()); } - DispatchManagedException(*ex, true /* isHardwareException */); + 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); + + DWORD exceptionCode = ex->GetExceptionRecord()->ExceptionCode; + if (exceptionCode == STATUS_ACCESS_VIOLATION) + { + if (ex->GetExceptionRecord()->ExceptionInformation[1] < NULL_AREA_SIZE) + { + exceptionCode = 0; //STATUS_REDHAWK_NULL_REFERENCE; + } + } + + GCPROTECT_BEGIN(exInfo.m_exception); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROWHW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = DWORD_TO_ARGHOLDER(exceptionCode); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + + //Ex.RhThrowHwEx(exceptionCode, &exInfo) + CALL_MANAGED_METHOD_NORET(args) + + GCPROTECT_END(); + } + else + { + DispatchManagedException(*ex, true /* isHardwareException */); + } UNREACHABLE(); } else @@ -5411,6 +5519,64 @@ BOOL HandleHardwareException(PAL_SEHException* ex) #endif // TARGET_UNIX +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + GCPROTECT_BEGIN(throwable); + + _ASSERTE(IsException(throwable->GetMethodTable())); + + ExceptionPreserveStackTrace(throwable); + + CONTEXT ctx = {}; + ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + REGDISPLAY rd; + Thread *pThread = GetThread(); + ExInfo exInfo(pThread, &ctx, &rd, ExKind::Throw); + + if (pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&throwable)) + { + pThread->ResetPreparingAbort(); + + if (pThread->GetFrame() == FRAME_TOP) + { + // There is no more managed code on stack. + pThread->ResetAbort(); + } + } + + GCPROTECT_BEGIN(exInfo.m_exception); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_THROW_EX); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(throwable); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + + //Ex.RhThrowEx(throwable, &exInfo) + CRITICAL_CALLSITE; + CALL_MANAGED_METHOD_NORET(args) + + GCPROTECT_END(); + GCPROTECT_END(); + + UNREACHABLE(); +} + +VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + EEException ex(reKind); + OBJECTREF throwable = ex.CreateThrowable(); + + DispatchManagedException(throwable); +} + #ifndef TARGET_UNIX void ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR ReturnValue, UINT_PTR TargetIP, UINT_PTR TargetFrameSp) { @@ -6032,10 +6198,18 @@ CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionReco return ExceptionContinueSearch; } - EXCEPTION_DISPOSITION retVal = ProcessCLRException(pExceptionRecord, - pEstablisherFrame, - pContextRecord, - pDispatcherContext); + EXCEPTION_DISPOSITION retVal; + if (!g_isNewExceptionHandlingEnabled) + { + retVal = ProcessCLRException(pExceptionRecord, + pEstablisherFrame, + pContextRecord, + pDispatcherContext); + } + else + { + retVal = ExceptionContinueSearch; + } if (retVal == ExceptionContinueSearch) { @@ -6258,6 +6432,49 @@ bool ExceptionTracker::StackRange::IsConsistent() } #endif // _DEBUG + +bool ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(CrawlFrame * pCF, PTR_ExInfo pExInfo) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pCF != NULL); + + // The tracker must be in the second pass, and its stack range must not be empty. + if ( (pExInfo == NULL) || + (pExInfo->m_passNumber == 1) || + (pExInfo->m_sfLowBound.IsMaxVal() && + pExInfo->m_sfHighBound.IsNull())) + { + return false; + } + + CallerStackFrame csfToCheck; + if (pCF->IsFrameless()) + { + csfToCheck = CallerStackFrame::FromRegDisplay(pCF->GetRegisterSet()); + } + else + { + csfToCheck = CallerStackFrame((UINT_PTR)pCF->GetFrame()); + } + + StackFrame sfLowerBound = pExInfo->m_sfLowBound; + StackFrame sfUpperBound = pExInfo->m_sfHighBound; + +#ifndef STACK_RANGE_BOUNDS_ARE_CALLER_SP + if ((sfLowerBound < csfToCheck) && (csfToCheck <= sfUpperBound)) +#else // !STACK_RANGE_BOUNDS_ARE_CALLER_SP + if ((sfLowerBound <= csfToCheck) && (csfToCheck < sfUpperBound)) +#endif // STACK_RANGE_BOUNDS_ARE_CALLER_SP + { + return true; + } + else + { + return false; + } +} + // Determine if the given StackFrame is in the stack region unwound by the specified ExceptionTracker. // This is used by the stackwalker to skip funclets. Refer to the calls to this method in StackWalkFramesEx() // for more information. @@ -6371,8 +6588,17 @@ bool ExceptionTracker::IsInStackRegionUnwoundByCurrentException(CrawlFrame * pCF LIMITED_METHOD_CONTRACT; Thread * pThread = pCF->pThread; - PTR_ExceptionTracker pCurrentTracker = pThread->GetExceptionState()->GetCurrentExceptionTracker(); - return ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(pCF, pCurrentTracker); + + if (!g_isNewExceptionHandlingEnabled) + { + PTR_ExceptionTracker pCurrentTracker = pThread->GetExceptionState()->GetCurrentExceptionTracker(); + return ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(pCF, pCurrentTracker); + } + else + { + PTR_ExInfo pCurrentExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + return ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(pCF, pCurrentExInfo); + } } @@ -6399,11 +6625,44 @@ bool ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF) // Enumerate all (nested) exception trackers and see if any of them has unwound the // specified CrawlFrame. Thread * pTargetThread = pCF->pThread; + bool fHasFrameBeenUnwound = false; + + if (g_isNewExceptionHandlingEnabled) + { + { + CallerStackFrame csfToCheck; + if (pCF->IsFrameless()) + { + csfToCheck = CallerStackFrame::FromRegDisplay(pCF->GetRegisterSet()); + } + else + { + csfToCheck = CallerStackFrame((UINT_PTR)pCF->GetFrame()); + } + STRESS_LOG4(LF_EH|LF_GCROOTS, LL_INFO100, "CrawlFrame (%p): Frameless: %s %s: %p\n", + pCF, pCF->IsFrameless() ? "Yes" : "No", pCF->IsFrameless() ? "CallerSP" : "Address", csfToCheck.SP); + } + + PTR_ExInfo pTopExInfo = pTargetThread->GetExceptionState()->GetCurrentExInfo(); + for (PTR_ExInfo pCurrentExInfo = pTopExInfo; pCurrentExInfo != NULL; pCurrentExInfo = dac_cast(pCurrentExInfo->m_pPrevExInfo)) + { + STRESS_LOG2(LF_EH|LF_GCROOTS, LL_INFO100, "Checking lower bound %p, upper bound %p\n", (void*)pCurrentExInfo->m_sfLowBound.SP, (void*)pCurrentExInfo->m_sfHighBound.SP); + if (ExceptionTracker::IsInStackRegionUnwoundBySpecifiedException(pCF, pCurrentExInfo)) + { + fHasFrameBeenUnwound = true; + break; + } + } + + if (fHasFrameBeenUnwound) + STRESS_LOG0(LF_EH|LF_GCROOTS, LL_INFO100, "Has already been unwound\n"); + + return fHasFrameBeenUnwound; + } + PTR_ExceptionTracker pTopTracker = pTargetThread->GetExceptionState()->GetCurrentExceptionTracker(); PTR_ExceptionTracker pCurrentTracker = pTopTracker; - bool fHasFrameBeenUnwound = false; - while (pCurrentTracker != NULL) { bool fSkipCurrentTracker = false; @@ -6867,6 +7126,29 @@ StackFrame ExceptionTracker::FindParentStackFrameHelper(CrawlFrame* pCF, } } + if (g_isNewExceptionHandlingEnabled) + { + for (PTR_ExInfo pCurrentExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + pCurrentExInfo != NULL; + pCurrentExInfo = pCurrentExInfo->m_pPrevExInfo) + { + // Check if the ExInfo has just been created. + if (pCurrentExInfo->m_sfLowBound.IsMaxVal() && pCurrentExInfo->m_sfHighBound.IsNull()) + { + continue; + } + + CallerStackFrame csfFunclet = pCurrentExInfo->m_csfEHClause; + if (csfCurrent == csfFunclet) + { + sfResult = (StackFrame)pCurrentExInfo->m_csfEnclosingClause; + break; + } + } + + goto lExit; + } + for (pCurrentTracker = pThread->GetExceptionState()->m_pCurrentTracker; pCurrentTracker != NULL; pCurrentTracker = pCurrentTracker->m_pPrevNestedInfo) @@ -7199,5 +7481,759 @@ void ExceptionTracker::ResetThreadAbortStatus(PTR_Thread pThread, CrawlFrame *pC } #endif //!DACCESS_COMPILE -#endif // FEATURE_EH_FUNCLETS +#ifndef DACCESS_COMPILE +// Mark the pinvoke frame as invoking RhpCallCatchFunclet (and similar) for collided unwind detection +void MarkInlinedCallFrameAsFuncletCall(Frame* pFrame) +{ + _ASSERTE(pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); + InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; + pInlinedCallFrame->m_Datum = (PTR_NDirectMethodDesc)((TADDR)pInlinedCallFrame->m_Datum | (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper | (TADDR)InlinedCallFrameMarker::SecondPassFuncletCaller); +} + +// Mark the pinvoke frame as invoking any exception handling helper +void MarkInlinedCallFrameAsEHHelperCall(Frame* pFrame) +{ + _ASSERTE(pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); + InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; + pInlinedCallFrame->m_Datum = (PTR_NDirectMethodDesc)((TADDR)pInlinedCallFrame->m_Datum | (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper); +} + +extern "C" void QCALLTYPE RhpAppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + GCX_COOP(); + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsFuncletCall(pFrame); + + bool canAllocateMemory = !(exceptionObj.Get() == CLRException::GetPreallocatedOutOfMemoryException()) && + !(exceptionObj.Get() == CLRException::GetPreallocatedStackOverflowException()); + + MethodDesc *pMD = pExInfo->m_frameIter.m_crawl.GetFunction(); +#if _DEBUG + EECodeInfo codeInfo(ip); + _ASSERTE(codeInfo.IsValid()); + _ASSERTE(pMD == codeInfo.GetMethodDesc()); +#endif // _DEBUG + + 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); + + END_QCALL; +} + +UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +{ +#ifdef HOST_AMD64 + _ASSERTE(exInfo->m_frameIter.m_crawl.GetRegisterSet() == pvRegDisplay); + if (exInfo->m_frameIter.m_crawl.GetCodeInfo()->HasFrameRegister()) + { + ULONG frameOffset = exInfo->m_frameIter.m_crawl.GetCodeInfo()->GetFrameOffsetFromUnwindInfo(); + return pvRegDisplay->pCurrentContext->Rbp - 16 * frameOffset; + } + else + { + return pvRegDisplay->SP; + } +#elif defined(HOST_ARM64) + return pvRegDisplay->SP; +#elif defined(HOST_ARM) + return pvRegDisplay->SP; +#elif defined(HOST_X86) + return pvRegDisplay->SP; +#elif defined(HOST_RISCV64) + return pvRegDisplay->SP; +#elif defined(HOST_LOONGARCH64) + return pvRegDisplay->SP; +#endif +} + +extern "C" void * QCALLTYPE RhpCallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + GCX_COOP_NO_DTOR(); + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsFuncletCall(pFrame); + HandlerFn* pfnHandler = (HandlerFn*)pHandlerIP; + exInfo->m_sfHighBound = exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP; + DWORD_PTR dwResumePC; + + if (pHandlerIP != NULL) + { + _ASSERTE(exInfo->m_sfCallerOfActualHandlerFrame == EECodeManager::GetCallerSp(pvRegDisplay)); + OBJECTREF throwable = exceptionObj.Get(); + throwable = PossiblyUnwrapThrowable(throwable, exInfo->m_frameIter.m_crawl.GetAssembly()); + + UINT_PTR establisherFrame = GetEstablisherFrame(pvRegDisplay, exInfo); + + exInfo->m_csfEHClause = CallerStackFrame((UINT_PTR)GetCurrentSP()); + exInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(exInfo->m_frameIter.m_crawl.GetRegisterSet()); + + 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)); + +#ifdef USE_FUNCLET_CALL_HELPER + // Invoke the catch 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 = &exInfo->m_csfEHClause; + UINT_PTR *pFuncletCallerSP = &(pCallerStackFrame->SP); + dwResumePC = CallEHFunclet(OBJECTREFToObject(throwable), + CastHandlerFn(pfnHandler), + GetFirstNonVolatileRegisterAddress(pvRegDisplay->pCurrentContext), + pFuncletCallerSP); +#else + 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)); + 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(); + } + + ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + +#ifdef HOST_UNIX + Interop::ManagedToNativeExceptionCallback propagateExceptionCallback = pExInfo->m_propagateExceptionCallback; + void* propagateExceptionContext = pExInfo->m_propagateExceptionContext; +#endif // HOST_UNIX + + // Pop ExInfos + while (pExInfo && pExInfo < (void*)targetSp) + { + 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; + } + + pThread->GetExceptionState()->SetCurrentExInfo(pExInfo); + + pThread->SafeSetLastThrownObject(NULL); + + ExceptionTracker::UpdateNonvolatileRegisters(pvRegDisplay->pCurrentContext, pvRegDisplay, FALSE); + if (pHandlerIP != NULL) + { + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + } + else + { +#ifdef HOST_UNIX + if (propagateExceptionCallback) + { + // A propagation callback was supplied. + STRESS_LOG3(LF_EH, LL_INFO100, "Deferring exception propagation to Callback = %p, IP = %p, SP = %p \n", propagateExceptionCallback, GetIP(pvRegDisplay->pCurrentContext), GetSP(pvRegDisplay->pCurrentContext)); + + UpdateContextForPropagationCallback(propagateExceptionCallback, propagateExceptionContext, pvRegDisplay->pCurrentContext); + GCX_PREEMP_NO_DTOR(); + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + } +#endif // HOST_UNIX + // Throw exception from the caller context +#if defined(HOST_AMD64) + ULONG64* returnAddress = (ULONG64*)targetSp; + *returnAddress = pvRegDisplay->pCurrentContext->Rip; +#elif defined(HOST_X86) + ULONG32* returnAddress = (ULONG32*)targetSp; + *returnAddress = pvRegDisplay->pCurrentContext->Eip; +#elif defined(HOST_ARM64) + pvRegDisplay->pCurrentContext->Lr = GetIP(pvRegDisplay->pCurrentContext); +#elif defined(HOST_ARM) + pvRegDisplay->pCurrentContext->Lr = GetIP(pvRegDisplay->pCurrentContext); +#elif defined(HOST_RISCV) || defined(HOST_LOONGARCH64) + pvRegDisplay->pCurrentContext->Ra = GetIP(pvRegDisplay->pCurrentContext); +#endif + SetIP(pvRegDisplay->pCurrentContext, (PCODE)(void (*)(Object*))RealCOMPlusThrow); +#if defined(HOST_AMD64) + SetSP(pvRegDisplay->pCurrentContext, targetSp - 8); +#elif defined(HOST_X86) + SetSP(pvRegDisplay->pCurrentContext, targetSp - 4); +#endif +#ifdef HOST_AMD64 +#ifdef UNIX_AMD64_ABI +#define FIRST_ARG_REG Rdi +#else +#define FIRST_ARG_REG Rcx +#endif +#elif defined(HOST_X86) +#define FIRST_ARG_REG Ecx +#elif defined(HOST_ARM64) +#define FIRST_ARG_REG X0 +#elif defined(HOST_ARM) +#define FIRST_ARG_REG R0 +#elif defined(HOST_RISCV64) || defined(HOST_LOONGARCH64) +#define FIRST_ARG_REG A0 +#endif + + pvRegDisplay->pCurrentContext->FIRST_ARG_REG = (size_t)OBJECTREFToObject(exceptionObj.Get()); +#undef FIRST_ARG_REG + ClrRestoreNonvolatileContext(pvRegDisplay->pCurrentContext); + } + END_QCALL; + return NULL; +} + +extern "C" void QCALLTYPE RhpCallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + GCX_COOP(); + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsFuncletCall(pFrame); + HandlerFn* pfnHandler = (HandlerFn*)pHandlerIP; + UINT_PTR establisherFrame = GetEstablisherFrame(pvRegDisplay, exInfo); + exInfo->m_csfEHClause = CallerStackFrame((UINT_PTR)GetCurrentSP()); + exInfo->m_csfEnclosingClause = CallerStackFrame::FromRegDisplay(exInfo->m_frameIter.m_crawl.GetRegisterSet()); + exInfo->m_sfHighBound = exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP; + + 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)); +#ifdef USE_FUNCLET_CALL_HELPER + // Invoke the finally 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 = &exInfo->m_csfEHClause; + UINT_PTR *pFuncletCallerSP = &(pCallerStackFrame->SP); + DWORD_PTR dwResumePC = CallEHFunclet(NULL, + CastHandlerFn(pfnHandler), + GetFirstNonVolatileRegisterAddress(pvRegDisplay->pCurrentContext), + pFuncletCallerSP); +#else + DWORD_PTR dwResumePC = pfnHandler(establisherFrame, NULL); +#endif + // Profiler, debugger and ETW events + exInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, GetSP(pvRegDisplay->pCurrentContext) ); + END_QCALL; +} + +extern "C" BOOL QCALLTYPE RhpCallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterIP, REGDISPLAY* pvRegDisplay) +{ + QCALL_CONTRACT; + + DWORD_PTR dwResult = 0; + + BEGIN_QCALL; + GCX_COOP(); + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + OBJECTREF throwable = exceptionObj.Get(); + throwable = PossiblyUnwrapThrowable(throwable, pExInfo->m_frameIter.m_crawl.GetAssembly()); + + HandlerFn* pfnHandler = (HandlerFn*)pFilterIP; + UINT_PTR establisherFrame = GetEstablisherFrame(pvRegDisplay, pExInfo); + pExInfo->m_csfEHClause = CallerStackFrame((UINT_PTR)GetCurrentSP()); + 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)); +#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); +#else + dwResult = pfnHandler(establisherFrame, OBJECTREFToObject(throwable)); +#endif + + // Profiler, debugger and ETW events + pExInfo->MakeCallbacksRelatedToHandler(false, pThread, pMD, &pExInfo->m_CurrentClause, (DWORD_PTR)pFilterIP, GetSP(pvRegDisplay->pCurrentContext)); + END_QCALL; + + return dwResult == EXCEPTION_EXECUTE_HANDLER; +} + + +struct ExtendedEHClauseEnumerator : EH_CLAUSE_ENUMERATOR +{ + StackFrameIterator *pFrameIter; + unsigned EHCount; +}; + +extern "C" BOOL QCALLTYPE RhpEHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum) +{ + QCALL_CONTRACT; + + ExtendedEHClauseEnumerator *pExtendedEHEnum = (ExtendedEHClauseEnumerator*)pEHEnum; + pExtendedEHEnum->pFrameIter = pFrameIter; + + BEGIN_QCALL; + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + IJitManager* pJitMan = pFrameIter->m_crawl.GetJitManager(); + const METHODTOKEN& MethToken = pFrameIter->m_crawl.GetMethodToken(); + *pMethodStartAddress = (BYTE*)pJitMan->JitTokenToStartAddress(MethToken); + pExtendedEHEnum->EHCount = pJitMan->InitializeEHEnumeration(MethToken, pEHEnum); + + END_QCALL; + + return pExtendedEHEnum->EHCount != 0; +} + +extern "C" BOOL QCALLTYPE RhpEHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) +{ + QCALL_CONTRACT; + BOOL result = FALSE; + + BEGIN_QCALL; + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + ExtendedEHClauseEnumerator *pExtendedEHEnum = (ExtendedEHClauseEnumerator*)pEHEnum; + StackFrameIterator *pFrameIter = pExtendedEHEnum->pFrameIter; + + if (pEHEnum->iCurrentPos < pExtendedEHEnum->EHCount) + { + IJitManager* pJitMan = pFrameIter->m_crawl.GetJitManager(); + const METHODTOKEN& MethToken = pFrameIter->m_crawl.GetMethodToken(); + + EE_ILEXCEPTION_CLAUSE EHClause; + memset(&EHClause, 0, sizeof(EE_ILEXCEPTION_CLAUSE)); + PTR_EXCEPTION_CLAUSE_TOKEN pEHClauseToken = pJitMan->GetNextEHClause(pEHEnum, &EHClause); + Thread* pThread = GET_THREAD(); + ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + pExInfo->m_CurrentClause = EHClause; + + pEHClause->_tryStartOffset = EHClause.TryStartPC; + pEHClause->_tryEndOffset = EHClause.TryEndPC; + // TODO-NewEH: The GetCodeAddressForRelOffset is expensive when the code is hot/cold split. Postpone this to + // later when we really need the address. + if (IsFilterHandler(&EHClause)) + { + pEHClause->_filterAddress = (BYTE*)pJitMan->GetCodeAddressForRelOffset(MethToken, EHClause.FilterOffset); + } + pEHClause->_handlerAddress = (BYTE*)pJitMan->GetCodeAddressForRelOffset(MethToken, EHClause.HandlerStartPC); + + result = TRUE; + pEHClause->_isSameTry = (EHClause.Flags & 0x10) != 0; // CORINFO_EH_CLAUSE_SAMETRY + + // Clear special flags - like COR_ILEXCEPTION_CLAUSE_CACHED_CLASS + ULONG flags = (CorExceptionFlag)(EHClause.Flags & 0x0f); + + if (flags & COR_ILEXCEPTION_CLAUSE_DUPLICATED) + { + result = FALSE; + } + else if (flags == COR_ILEXCEPTION_CLAUSE_NONE) + { + pEHClause->_clauseKind = RH_EH_CLAUSE_TYPED; + pEHClause->_pTargetType = pJitMan->ResolveEHClause(&EHClause, &pFrameIter->m_crawl).AsMethodTable(); + } + else if (flags & COR_ILEXCEPTION_CLAUSE_FILTER) + { + pEHClause->_clauseKind = RH_EH_CLAUSE_FILTER; + } + else if (flags & COR_ILEXCEPTION_CLAUSE_FINALLY) + { + pEHClause->_clauseKind = RH_EH_CLAUSE_FAULT; + } + else if (flags & COR_ILEXCEPTION_CLAUSE_FAULT) + { + pEHClause->_clauseKind = RH_EH_CLAUSE_FAULT; + } + else + { + result = FALSE; + } + } + END_QCALL; + + return result; +} + +extern uint32_t g_exceptionCount; + +MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut); + +extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault) +{ + QCALL_CONTRACT; + + bool result = false; + BEGIN_QCALL; + + Thread* pThread = GET_THREAD(); + 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 RhpSfiInit pinvoke frame + pFrame = pThread->GetFrame()->PtrNextFrame(); + + ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + REGDISPLAY* pRD = pExInfo->m_pRD; + + if (pExInfo->m_passNumber == 1) + { + GCX_COOP(); + pThread->SafeSetThrowables(pExInfo->m_exception); + EEToProfilerExceptionInterfaceWrapper::ExceptionThrown(pThread); + UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); + } + else // pExInfo->m_passNumber == 2 + { + // 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 + { + EECodeManager::EnsureCallerContextIsValid(pRD, NULL); + pExInfo->m_sfCallerOfActualHandlerFrame = CallerStackFrame::FromRegDisplay(pRD); + + // the 1st pass has just ended, so the m_CurrentClause is the catch clause + pExInfo->m_ClauseForCatch = pExInfo->m_CurrentClause; + + MethodDesc *pMD = pExInfo->m_frameIter.m_crawl.GetFunction(); + TADDR sp = GetRegdisplaySP(pRD); + if (pMD->IsILStub()) + { + MethodDesc * pUserMDForILStub = NULL; + Frame * pILStubFrame = NULL; + if (!pExInfo->m_frameIter.m_crawl.IsFunclet()) // only make this callback on the main method body of IL stubs + { + pUserMDForILStub = GetUserMethodForILStub(pThread, sp, pMD, &pILStubFrame); + } + // + // NotifyOfCHFFilter has two behaviors + // * Notifify debugger, get interception info and unwind (function will not return) + // In this case, m_sfResumeStackFrame is expected to be NULL or the frame of interception. + // 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); + */ + } + else + { + // 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, + &pExInfo->m_ClauseForCatch); + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchCatcherFound(pMD); + } + } + pExInfo->m_ExceptionFlags.SetUnwindHasStarted(); + EEToDebuggerExceptionInterfaceWrapper::ManagedExceptionUnwindBegin(pThread); + } + + pThread->FillRegDisplay(pRD, pStackwalkCtx); + + new (pThis) StackFrameIterator(); + result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE | HANDLESKIPPEDFRAMES/* | FUNCTIONSONLY*/) != FALSE; + + while (result && pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD) + { + if (pExInfo->m_passNumber == 1) + { + Frame *pFrame = pThis->m_crawl.GetFrame(); + if (pFrame != FRAME_TOP) + { + MethodDesc *pMD = pFrame->GetFunction(); + if (pMD != NULL) + { + GCX_COOP(); + bool canAllocateMemory = !(pExInfo->m_exception == CLRException::GetPreallocatedOutOfMemoryException()) && + !(pExInfo->m_exception == CLRException::GetPreallocatedStackOverflowException()); + + pExInfo->m_stackTraceInfo.AppendElement(canAllocateMemory, NULL, GetRegdisplaySP(pExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pExInfo->m_frameIter.m_crawl); + } + } + } + StackWalkAction retVal = pThis->Next(); + 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())); + } + pExInfo->m_sfLowBound = GetRegdisplaySP(pThis->m_crawl.GetRegisterSet()); + + pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); + + _ASSERTE(!result || pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD); + + END_QCALL; + + if (result) + { + TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; + if (!pThis->m_crawl.HasFaulted()) + { + controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; + } + pThis->SetAdjustedControlPC(controlPC); + pThis->UpdateIsRuntimeWrappedExceptions(); + } + + return result; +} + +extern "C" size_t CallDescrWorkerInternalReturnAddressOffset; + +extern "C" bool QCALLTYPE RhpSfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) +{ + QCALL_CONTRACT; + + StackWalkAction retVal = SWA_FAILED; + + BEGIN_QCALL; + + Thread* pThread = GET_THREAD(); + Frame* pFrame = pThread->GetFrame(); + MarkInlinedCallFrameAsEHHelperCall(pFrame); + + // we already fixed the context in HijackHandler, so let's + // just clear the thread state. + pThread->ResetThrowControlForThread(); + + ExInfo* pExInfo = pThis->GetNextExInfo(); + ExInfo* pTopExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + + // Check for reverse pinvoke (but eliminate the case when the caller is managed) or CallDescrWorkerInternal. + if (!ExecutionManager::IsManagedCode(GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext))) + { + bool invalidRevPInvoke; +#ifdef USE_GC_INFO_DECODER + GcInfoDecoder gcInfoDecoder(pThis->m_crawl.GetCodeInfo()->GetGCInfoToken(), DECODE_REVERSE_PINVOKE_VAR); + invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME; +#else // USE_GC_INFO_DECODER + hdrInfo gcHdrInfo; + DecodeGCHdrInfo(pThis->m_crawl.GetCodeInfo()->GetGCInfoToken(), 0, &gcHdrInfo); + invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET; +#endif // USE_GC_INFO_DECODER + + if (invalidRevPInvoke) + { +#ifdef HOST_UNIX + void* callbackCxt = NULL; + Interop::ManagedToNativeExceptionCallback callback = Interop::GetPropagatingExceptionCallback( + pThis->m_crawl.GetCodeInfo(), + pTopExInfo->m_hThrowable, + &callbackCxt); + + if (callback != NULL) + { + pTopExInfo->m_propagateExceptionCallback = callback; + pTopExInfo->m_propagateExceptionContext = callbackCxt; + } +#endif // HOST_UNIX + } + else + { + // TODO-NewEH: Currently there are two other cases of internal VM->managed transitions. The FastCallFinalize and COMToCLRDispatchHelperWithStack + // Either add handling those here as well or rewrite all these perf critical places in C#, so that CallDescrWorker is the only path that + // needs to be handled here. + size_t CallDescrWorkerInternalReturnAddress = (size_t)CallDescrWorkerInternal + CallDescrWorkerInternalReturnAddressOffset; + if (GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext) == CallDescrWorkerInternalReturnAddress) + { + invalidRevPInvoke = true; + } + } + + if (fUnwoundReversePInvoke) + { + *fUnwoundReversePInvoke = invalidRevPInvoke; + } + + if (invalidRevPInvoke) + { + // Unwind to the caller of the managed code + retVal = pThis->Next(); + _ASSERTE(retVal != SWA_FAILED); + goto Exit; + } + } + else + { + if (fUnwoundReversePInvoke) + { + *fUnwoundReversePInvoke = false; + } + } + + do + { + *uExCollideClauseIdx = 0xffffffff; + bool doingFuncletUnwind = pThis->m_crawl.IsFunclet(); + + retVal = pThis->Next(); + if (retVal == SWA_FAILED) + { + break; + } + + if (pThis->GetFrameState() == StackFrameIterator::SFITER_DONE) + { + retVal = SWA_FAILED; + break; + } + + if (!pThis->m_crawl.IsFrameless()) + { + // Detect collided unwind + pFrame = pThis->m_crawl.GetFrame(); + + if (InlinedCallFrame::FrameHasActiveCall(pFrame)) + { + InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; + if (((TADDR)pInlinedCallFrame->m_Datum & 6) == 6) + { + // passing through RhpCallCatchFunclet et al + if (doingFuncletUnwind) + { + // Unwind the RhpCallCatchFunclet + retVal = pThis->Next(); + 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; + 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(); + } + while ((retVal == SWA_CONTINUE) && pThis->m_crawl.GetRegisterSet()->SP != pPrevExInfo->m_pRD->SP); + _ASSERTE(retVal != SWA_FAILED); + + pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); + } + } + } + } + else if (pTopExInfo->m_passNumber == 1) + { + MethodDesc *pMD = pFrame->GetFunction(); + if (pMD != NULL) + { + GCX_COOP(); + bool canAllocateMemory = !(pTopExInfo->m_exception == CLRException::GetPreallocatedOutOfMemoryException()) && + !(pTopExInfo->m_exception == CLRException::GetPreallocatedStackOverflowException()); + + pTopExInfo->m_stackTraceInfo.AppendElement(canAllocateMemory, NULL, GetRegdisplaySP(pTopExInfo->m_frameIter.m_crawl.GetRegisterSet()), pMD, &pTopExInfo->m_frameIter.m_crawl); + } + } + } + } + while (retVal != SWA_FAILED && (pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD)); + + _ASSERTE(retVal == SWA_FAILED || pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD); + + if (pTopExInfo->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())); + } + +Exit:; + END_QCALL; + + if (retVal != SWA_FAILED) + { + TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; + if (!pThis->m_crawl.HasFaulted()) + { + controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; + } + pThis->SetAdjustedControlPC(controlPC); + pThis->UpdateIsRuntimeWrappedExceptions(); + + return true; + } + + return false; +} + +namespace AsmOffsetsAsserts +{ +// Verify that the offsets into CONTEXT, REGDISPLAY, ExInfo and StackFrameIterator that the new managed exception handling +// use match between the managed code and the native ones. +#define public +#define const static constexpr + + #include "../System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs" + +#undef public +#undef const +}; + +#endif + +#endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index d8eb19e6ef208..3e196f63bd090 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -22,6 +22,9 @@ 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(RuntimeExceptionKind reKind); + enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; enum TrackerMemoryType @@ -39,6 +42,19 @@ enum EHFuncletType Catch = 0x0004, }; +struct ExInfo; +typedef DPTR(ExInfo) PTR_ExInfo; + +// These values are or-ed into the InlinedCallFrame::m_Datum field. +// The bit 0 is used for unrelated purposes (see comments on the +// InlinedCallFrame::m_Datum field for details). +enum class InlinedCallFrameMarker +{ + ExceptionHandlingHelper = 2, + SecondPassFuncletCaller = 4, + Mask = ExceptionHandlingHelper | SecondPassFuncletCaller +}; + typedef DPTR(class ExceptionTracker) PTR_ExceptionTracker; class ExceptionTracker { @@ -243,6 +259,7 @@ class ExceptionTracker static StackFrame GetStackFrameForParentCheck(CrawlFrame * pCF); static bool IsInStackRegionUnwoundBySpecifiedException(CrawlFrame * pCF, PTR_ExceptionTracker pExceptionTracker); + static bool IsInStackRegionUnwoundBySpecifiedException(CrawlFrame * pCF, PTR_ExInfo pExInfo); static bool IsInStackRegionUnwoundByCurrentException(CrawlFrame * pCF); static bool HasFrameBeenUnwoundByAnyActiveException(CrawlFrame * pCF); diff --git a/src/coreclr/vm/exceptionhandlingqcalls.h b/src/coreclr/vm/exceptionhandlingqcalls.h new file mode 100644 index 0000000000000..2bdd73c4a0379 --- /dev/null +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef EXCEPTION_HANDLING_QCALLS_H +#define EXCEPTION_HANDLING_QCALLS_H + +#ifdef FEATURE_EH_FUNCLETS + +struct RhEHClause; +struct ExInfo; + +#ifndef DACCESS_COMPILE + +extern "C" void * QCALLTYPE RhpCallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); +extern "C" void QCALLTYPE RhpCallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); +extern "C" BOOL QCALLTYPE RhpCallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterP, REGDISPLAY* pvRegDisplay); +extern "C" void QCALLTYPE RhpAppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo); +extern "C" BOOL QCALLTYPE RhpEHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum); +extern "C" BOOL QCALLTYPE RhpEHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause); +extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault); +extern "C" bool QCALLTYPE RhpSfiNext(StackFrameIterator* pThis, unsigned int* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); +#endif // DACCESS_COMPILE + +#endif // FEATURE_EH_FUNCLETS + +#endif // EXCEPTION_HANDLING_QCALLS_H \ No newline at end of file diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 1627e3d3d65cc..59f293d1c879b 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -116,10 +116,16 @@ struct _EXCEPTION_REGISTRATION_RECORD; class Thread; class Frame; class Exception; +struct REGDISPLAY; + +#ifdef FEATURE_EH_FUNCLETS +struct ExInfo; +#endif VOID DECLSPEC_NORETURN RealCOMPlusThrowOM(); #include +#include //========================================================================== // Macros to allow catching exceptions from within the EE. These are lightweight @@ -310,6 +316,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar #define INSTALL_MANAGED_EXCEPTION_DISPATCHER #define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER + #define INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP #define UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index a4d6c8092f38e..e98e547e5d26a 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -8,6 +8,11 @@ #include "exinfo.h" #include "dbginterface.h" +#ifdef FEATURE_EH_FUNCLETS +#include "eetoprofinterfacewrapper.inl" +#include "eedbginterfaceimpl.inl" +#endif + #ifndef FEATURE_EH_FUNCLETS #ifndef DACCESS_COMPILE // @@ -300,4 +305,132 @@ void ExInfo::SetExceptionCode(const EXCEPTION_RECORD *pCER) DacError(E_UNEXPECTED); #endif // !DACCESS_COMPILE } +#else // !FEATURE_EH_FUNCLETS + +#ifndef DACCESS_COMPILE + +ExInfo::ExInfo(Thread *pThread, CONTEXT *pCtx, REGDISPLAY *pRD, ExKind exceptionKind) : + m_pExContext(pCtx), + 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({}), +#ifdef HOST_UNIX + m_propagateExceptionCallback(NULL), + m_propagateExceptionContext(NULL), +#endif // HOST_UNIX + m_hThrowable(NULL), + m_CurrentClause({}) +{ + m_pPrevExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + m_stackTraceInfo.Init(); + m_stackTraceInfo.AllocateStackTrace(); + m_sfLowBound.SetMaxVal(); + pThread->GetExceptionState()->SetCurrentExInfo(this); +} + +#endif // DACCESS_COMPILE + +bool IsFilterStartOffset(EE_ILEXCEPTION_CLAUSE* pEHClause, DWORD_PTR dwHandlerStartPC) +{ + EECodeInfo codeInfo((PCODE)dwHandlerStartPC); + _ASSERTE(codeInfo.IsValid()); + + return pEHClause->FilterOffset == codeInfo.GetRelOffset(); +} + +void ExInfo::MakeCallbacksRelatedToHandler( + bool fBeforeCallingHandler, + Thread* pThread, + MethodDesc* pMD, + EE_ILEXCEPTION_CLAUSE* pEHClause, + DWORD_PTR dwHandlerStartPC, + StackFrame sf + ) +{ + // 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); + BOOL fIsFaultOrFinallyHandler = IsFaultOrFinally(pEHClause); + + if (fBeforeCallingHandler) + { + StackFrame sfToStore = sf; + if ((m_pPrevExInfo != NULL) && + (m_pPrevExInfo->m_csfEnclosingClause == m_csfEnclosingClause)) + { + // If this is a nested exception which has the same enclosing clause as the previous exception, + // we should just propagate the clause info from the previous exception. + sfToStore = m_pPrevExInfo->m_EHClauseInfo.GetStackFrameForEHClause(); + } + m_EHClauseInfo.SetInfo(COR_PRF_CLAUSE_NONE, (UINT_PTR)dwHandlerStartPC, sfToStore); + + if (pMD->IsILStub()) + { + return; + } + + if (fIsFilterHandler) + { + m_EHClauseInfo.SetEHClauseType(COR_PRF_CLAUSE_FILTER); + EEToDebuggerExceptionInterfaceWrapper::ExceptionFilter(pMD, (TADDR) dwHandlerStartPC, pEHClause->FilterOffset, (BYTE*)sf.SP); + + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFilterEnter(pMD); + ETW::ExceptionLog::ExceptionFilterBegin(pMD, (PVOID)dwHandlerStartPC); + } + else + { + EEToDebuggerExceptionInterfaceWrapper::ExceptionHandle(pMD, (TADDR) dwHandlerStartPC, pEHClause->HandlerStartPC, (BYTE*)sf.SP); + + if (fIsFaultOrFinallyHandler) + { + m_EHClauseInfo.SetEHClauseType(COR_PRF_CLAUSE_FINALLY); + EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFinallyEnter(pMD); + ETW::ExceptionLog::ExceptionFinallyBegin(pMD, (PVOID)dwHandlerStartPC); + } + else + { + m_EHClauseInfo.SetEHClauseType(COR_PRF_CLAUSE_CATCH); + EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherEnter(pThread, pMD); + + DACNotify::DoExceptionCatcherEnterNotification(pMD, pEHClause->HandlerStartPC); + ETW::ExceptionLog::ExceptionCatchBegin(pMD, (PVOID)dwHandlerStartPC); + } + } + } + else + { + if (pMD->IsILStub()) + { + return; + } + + if (fIsFilterHandler) + { + ETW::ExceptionLog::ExceptionFilterEnd(); + EEToProfilerExceptionInterfaceWrapper::ExceptionSearchFilterLeave(); + } + else + { + if (fIsFaultOrFinallyHandler) + { + ETW::ExceptionLog::ExceptionFinallyEnd(); + EEToProfilerExceptionInterfaceWrapper::ExceptionUnwindFinallyLeave(); + } + else + { + ETW::ExceptionLog::ExceptionCatchEnd(); + ETW::ExceptionLog::ExceptionThrownEnd(); + EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherLeave(); + } + } + + m_EHClauseInfo.ResetInfo(); + } +} + #endif // !FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index a270843eec9c8..e83bead1bcb79 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -159,5 +159,112 @@ class ExInfo PTR_ExInfo GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable, PTR_ExInfo pStartingEHTracker); #endif // TARGET_X86 +#else // !FEATURE_EH_FUNCLETS + +enum RhEHClauseKind +{ + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + RH_EH_CLAUSE_FILTER = 2, + RH_EH_CLAUSE_UNUSED = 3, +}; + +struct RhEHClause +{ + RhEHClauseKind _clauseKind; + unsigned _tryStartOffset; + unsigned _tryEndOffset; + BYTE *_filterAddress; + BYTE *_handlerAddress; + void *_pTargetType; + BOOL _isSameTry; +}; + +enum class ExKind : uint8_t +{ + None = 0, + Throw = 1, + HardwareFault = 2, + KindMask = 3, + + RethrowFlag = 4, + + SupersededFlag = 8, + + InstructionFaultFlag = 0x10 +}; + +struct ExInfo +{ + ExInfo(Thread *pThread, CONTEXT *pCtx, REGDISPLAY *pRD, ExKind exceptionKind); + + void MakeCallbacksRelatedToHandler( + bool fBeforeCallingHandler, + Thread* pThread, + MethodDesc* pMD, + EE_ILEXCEPTION_CLAUSE* pEHClause, + DWORD_PTR dwHandlerStartPC, + StackFrame sf); + + // Previous ExInfo in the chain of exceptions rethrown from their catch / finally handlers + PTR_ExInfo m_pPrevExInfo; + // Context used by the stack frame iterator + CONTEXT* m_pExContext; + // actual exception object reference + OBJECTREF m_exception; + // Kind of the exception (software, hardware, rethrown) + ExKind m_kind; + // Exception handling pass (1 or 2) + uint8_t m_passNumber; + // Index of the current exception handling clause + uint32_t m_idxCurClause; + // 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 + Frame* m_pFrame; + + // Low and high bounds of the stack unwound by the exception. They are updated during 2nd pass only. + StackFrame m_sfLowBound; + StackFrame m_sfHighBound; + // Stack frame of the caller of the currently running exception handling clause (catch, finally, filter) + CallerStackFrame m_csfEHClause; + // Stack frame of the caller of the code that encloses the currently running exception handling clause + CallerStackFrame m_csfEnclosingClause; + // Stack frame of the caller of the catch handler + StackFrame m_sfCallerOfActualHandlerFrame; + // The exception handling clause for the catch handler that was identified during pass 1 + EE_ILEXCEPTION_CLAUSE m_ClauseForCatch; + +#ifdef TARGET_UNIX + // Exception propagation callback and context for ObjectiveC exception propagation support + void(*m_propagateExceptionCallback)(void* context); + void *m_propagateExceptionContext; +#endif // TARGET_UNIX + + // thrown exception object handle + OBJECTHANDLE m_hThrowable; + + // The following fields are for profiler / debugger use only + EE_ILEXCEPTION_CLAUSE m_CurrentClause; + DebuggerExState m_DebuggerExState; + EHClauseInfo m_EHClauseInfo; + ExceptionFlags m_ExceptionFlags; + +#ifndef TARGET_UNIX + EHWatsonBucketTracker m_WatsonBucketTracker; + + inline PTR_EHWatsonBucketTracker GetWatsonBucketTracker() + { + LIMITED_METHOD_CONTRACT; + return PTR_EHWatsonBucketTracker(PTR_HOST_MEMBER_TADDR(ExInfo, this, m_WatsonBucketTracker)); + } +#endif // !TARGET_UNIX + +}; + #endif // !FEATURE_EH_FUNCLETS #endif // __ExInfo_h__ diff --git a/src/coreclr/vm/exstate.cpp b/src/coreclr/vm/exstate.cpp index fc3df10c0f647..41c0b8c044536 100644 --- a/src/coreclr/vm/exstate.cpp +++ b/src/coreclr/vm/exstate.cpp @@ -22,6 +22,10 @@ OBJECTHANDLE ThreadExceptionState::GetThrowableAsHandle() { return m_pCurrentTracker->m_hThrowable; } + else if (m_pExInfo) + { + return m_pExInfo->m_hThrowable; + } return NULL; #else // FEATURE_EH_FUNCLETS @@ -34,6 +38,7 @@ ThreadExceptionState::ThreadExceptionState() { #ifdef FEATURE_EH_FUNCLETS m_pCurrentTracker = NULL; + m_pExInfo = NULL; #endif // FEATURE_EH_FUNCLETS m_flag = TEF_None; @@ -109,6 +114,10 @@ OBJECTREF ThreadExceptionState::GetThrowable() { return ObjectFromHandle(m_pCurrentTracker->m_hThrowable); } + if (m_pExInfo && m_pExInfo->m_exception != NULL) + { + return m_pExInfo->m_exception; + } #else // FEATURE_EH_FUNCLETS if (m_currentExInfo.m_hThrowable) { @@ -175,7 +184,7 @@ void ThreadExceptionState::SetThrowable(OBJECTREF throwable DEBUG_ARG(SetThrowab #endif // FEATURE_INTERPRETER ) { - CONSISTENCY_CHECK(CheckPointer(m_pCurrentTracker)); + CONSISTENCY_CHECK(CheckPointer(m_pCurrentTracker, NULL_OK) || CheckPointer(m_pExInfo)); } #endif @@ -183,6 +192,11 @@ void ThreadExceptionState::SetThrowable(OBJECTREF throwable DEBUG_ARG(SetThrowab { m_pCurrentTracker->m_hThrowable = hNewThrowable; } + if (m_pExInfo != NULL) + { + _ASSERTE(m_pExInfo->m_hThrowable == NULL); + m_pExInfo->m_hThrowable = hNewThrowable; + } #else // FEATURE_EH_FUNCLETS m_currentExInfo.m_hThrowable = hNewThrowable; #endif // FEATURE_EH_FUNCLETS @@ -194,8 +208,15 @@ DWORD ThreadExceptionState::GetExceptionCode() LIMITED_METHOD_CONTRACT; #ifdef FEATURE_EH_FUNCLETS - _ASSERTE(m_pCurrentTracker); - return m_pCurrentTracker->m_ExceptionCode; + if (m_pCurrentTracker) + { + return m_pCurrentTracker->m_ExceptionCode; + } + _ASSERTE(m_pExInfo); + { + GCX_COOP(); + return ((EXCEPTIONREF)m_pExInfo->m_exception)->GetXCode(); + } #else // FEATURE_EH_FUNCLETS return m_currentExInfo.m_ExceptionCode; #endif // FEATURE_EH_FUNCLETS @@ -227,7 +248,7 @@ BOOL ThreadExceptionState::IsExceptionInProgress() LIMITED_METHOD_DAC_CONTRACT; #ifdef FEATURE_EH_FUNCLETS - return (m_pCurrentTracker != NULL); + return (m_pCurrentTracker != NULL) || (m_pExInfo != NULL); #else // FEATURE_EH_FUNCLETS return (m_currentExInfo.m_pBottomMostHandler != NULL); #endif // FEATURE_EH_FUNCLETS @@ -307,6 +328,10 @@ PTR_CONTEXT ThreadExceptionState::GetContextRecord() { return m_pCurrentTracker->m_ptrs.ContextRecord; } + else if (m_pExInfo) + { + return dac_cast(m_pExInfo->m_pExContext); + } else { return NULL; @@ -324,6 +349,10 @@ ExceptionFlags* ThreadExceptionState::GetFlags() { return &(m_pCurrentTracker->m_ExceptionFlags); } + else if (m_pExInfo) + { + return &(m_pExInfo->m_ExceptionFlags); + } else { _ASSERTE(!"GetFlags() called when there is no current exception"); @@ -347,6 +376,10 @@ DebuggerExState* ThreadExceptionState::GetDebuggerState() { return &(m_pCurrentTracker->m_DebuggerExState); } + else if (m_pExInfo) + { + return &(m_pExInfo->m_DebuggerExState); + } else { _ASSERTE(!"unexpected use of GetDebuggerState() when no exception in flight"); diff --git a/src/coreclr/vm/exstate.h b/src/coreclr/vm/exstate.h index cc3d25a76f1cb..6905a29e17fbc 100644 --- a/src/coreclr/vm/exstate.h +++ b/src/coreclr/vm/exstate.h @@ -24,6 +24,11 @@ class EHClauseInfo; extern StackWalkAction COMPlusUnwindCallback(CrawlFrame *pCf, ThrowCallbackType *pData); +#ifdef FEATURE_EH_FUNCLETS +struct ExInfo; +typedef DPTR(ExInfo) PTR_ExInfo; +#endif // !FEATURE_EH_FUNCLETS + // // This class serves as a forwarding and abstraction layer for the EH subsystem. // Since we have two different implementations, this class is needed to unify @@ -145,12 +150,24 @@ class ThreadExceptionState #ifdef FEATURE_EH_FUNCLETS PTR_ExceptionTracker m_pCurrentTracker; ExceptionTracker m_OOMTracker; + PTR_ExInfo m_pExInfo; public: PTR_ExceptionTracker GetCurrentExceptionTracker() { LIMITED_METHOD_CONTRACT; return m_pCurrentTracker; } + PTR_ExInfo GetCurrentExInfo() + { + LIMITED_METHOD_CONTRACT; + return m_pExInfo; + } + + void SetCurrentExInfo(PTR_ExInfo pExInfo) + { + LIMITED_METHOD_CONTRACT; + m_pExInfo = pExInfo; + } #else ExInfo m_currentExInfo; public: diff --git a/src/coreclr/vm/fcall.cpp b/src/coreclr/vm/fcall.cpp index 4140a80f802d8..20461bef79880 100644 --- a/src/coreclr/vm/fcall.cpp +++ b/src/coreclr/vm/fcall.cpp @@ -41,6 +41,13 @@ NOINLINE LPVOID __FCThrow(LPVOID __me, RuntimeExceptionKind reKind, UINT resID, _ASSERTE((reKind != kExecutionEngineException) || !"Don't throw kExecutionEngineException from here. Go to EEPolicy directly, or throw something better."); +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + DispatchManagedException(reKind); + } +#endif // FEATURE_EH_FUNCLETS + if (resID == 0) { // If we have an string to add use NonLocalized otherwise just throw the exception. diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index 0bd5e01991ed6..e403918072c04 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -582,8 +582,9 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar HELPER_METHOD_FRAME_BEGIN_EX_BODY(ret, helperFrame, gcpoll, allowGC) \ /* TODO TURN THIS ON!!! */ \ /* gcpoll; */ \ + if (g_isNewExceptionHandlingEnabled) __helperframe.Push(); \ INSTALL_MANAGED_EXCEPTION_DISPATCHER; \ - __helperframe.Push(); \ + if (!g_isNewExceptionHandlingEnabled) __helperframe.Push(); \ MAKE_CURRENT_THREAD_AVAILABLE_EX(__helperframe.GetThread()); \ INSTALL_UNWIND_AND_CONTINUE_HANDLER_FOR_HMF(&__helperframe); @@ -616,8 +617,9 @@ LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR ar #define HELPER_METHOD_FRAME_END_EX(gcpoll,allowGC) \ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; \ - __helperframe.Pop(); \ + if (!g_isNewExceptionHandlingEnabled) __helperframe.Pop(); \ UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; \ + if (g_isNewExceptionHandlingEnabled) __helperframe.Pop(); \ HELPER_METHOD_FRAME_END_EX_BODY(gcpoll,allowGC); #define HELPER_METHOD_FRAME_END_EX_NOTHROW(gcpoll,allowGC) \ diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 4cccc1e6dfc42..90c21e54aa813 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1020,6 +1020,50 @@ void GCFrame::Pop() Thread::ObjectRefNew(&m_pObjRefs[i]); // Unprotect them #endif } + +void GCFrame::Remove() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(m_pCurThread != NULL); + } + CONTRACTL_END; + + GCFrame *pPrevFrame = NULL; + GCFrame *pFrame = m_pCurThread->GetGCFrame(); + while (pFrame != NULL) + { + if (pFrame == this) + { + if (pPrevFrame) + { + pPrevFrame->m_Next = m_Next; + } + else + { + m_pCurThread->SetGCFrame(m_Next); + } + + m_Next = NULL; + +#ifdef _DEBUG + m_pCurThread->EnableStressHeap(); + for(UINT i = 0; i < m_numObjRefs; i++) + Thread::ObjectRefNew(&m_pObjRefs[i]); // Unprotect them +#endif + break; + } + + pPrevFrame = pFrame; + pFrame = pFrame->m_Next; + } + + _ASSERTE_MSG(pFrame != NULL, "GCFrame not found in the current thread's stack"); +} + #endif // !DACCESS_COMPILE // diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index cba39a808a986..c0c8c7f33f93e 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2435,6 +2435,8 @@ class GCFrame // Push and pop this frame from the thread's stack. void Push(Thread* pThread); void Pop(); + // Remove this frame from any position in the thread's stack + void Remove(); #endif // DACCESS_COMPILE @@ -2809,7 +2811,7 @@ class InlinedCallFrame : public Frame #ifdef HOST_64BIT // See code:GenericPInvokeCalliHelper - return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x1)); + return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x3)); #else // HOST_64BIT return ((dac_cast(m_Datum) & ~0xffff) != 0); #endif // HOST_64BIT @@ -2869,8 +2871,11 @@ class InlinedCallFrame : public Frame virtual void UpdateRegDisplay(const PREGDISPLAY); // m_Datum contains MethodDesc ptr or - // - on AMD64: CALLI target address (if lowest bit is set) - // - on X86: argument stack size (if value is <64k) + // - on 64 bit host: CALLI target address (if lowest bit is set) + // - on windows x86 host: argument stack size (if value is <64k) + // When m_Datum contains MethodDesc ptr, then on other than windows x86 host + // - bit 1 set indicates invoking new exception handling helpers + // - bit 2 indicates RhpCallCatchFunclet or RhpCallFinallyFunclet // See code:HasFunction. PTR_NDirectMethodDesc m_Datum; diff --git a/src/coreclr/vm/i386/asmhelpers.S b/src/coreclr/vm/i386/asmhelpers.S index e0b87813592dd..ff777550bfab5 100644 --- a/src/coreclr/vm/i386/asmhelpers.S +++ b/src/coreclr/vm/i386/asmhelpers.S @@ -214,6 +214,8 @@ LOCAL_LABEL(donestack): CHECK_STACK_ALIGNMENT call [ebx + CallDescrData__pTarget] +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): + #ifdef _DEBUG nop // This is a tag that we use in an assert. Fcalls expect to // be called from Jitted code or from certain blessed call sites like @@ -252,6 +254,10 @@ LOCAL_LABEL(ReturnsFloat): LOCAL_LABEL(ReturnsDouble): fstp QWORD PTR [ebx + CallDescrData__returnValue] // Spill the Double return value jmp LOCAL_LABEL(Epilog) + +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .word LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) + NESTED_END CallDescrWorkerInternal, _TEXT #ifdef _DEBUG diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index e225a6e3834b7..585aa98bf10ee 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -56,6 +56,8 @@ #include "excep.h" #endif +#include "exinfo.h" + //======================================================================== // // This file contains implementation of all JIT helpers. The helpers are @@ -4214,6 +4216,39 @@ HCIMPLEND /*************************************************************/ +#ifdef FEATURE_EH_FUNCLETS +void ThrowNew(OBJECTREF oref) +{ + if (oref == 0) + DispatchManagedException(kNullReferenceException); + else + if (!IsException(oref->GetMethodTable())) + { + GCPROTECT_BEGIN(oref); + + WrapNonCompliantException(&oref); + + GCPROTECT_END(); + } + else + { // We know that the object derives from System.Exception + + // If the flag indicating ForeignExceptionRaise has been set, + // then do not clear the "_stackTrace" field of the exception object. + if (GetThread()->GetExceptionState()->IsRaisingForeignException()) + { + ((EXCEPTIONREF)oref)->SetStackTraceString(NULL); + } + else + { + ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace(); + } + } + + DispatchManagedException(oref); +} +#endif // FEATURE_EH_FUNCLETS + HCIMPL1(void, IL_Throw, Object* obj) { FCALL_CONTRACT; @@ -4232,6 +4267,13 @@ HCIMPL1(void, IL_Throw, Object* obj) g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress(); #endif // defined(_DEBUG) && defined(TARGET_X86) +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + ThrowNew(oref); + UNREACHABLE(); + } +#endif if (oref == 0) COMPlusThrow(kNullReferenceException); @@ -4267,6 +4309,31 @@ 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); + + GCPROTECT_BEGIN(exInfo.m_exception); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); + DECLARE_ARGHOLDER_ARRAY(args, 2); + + args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo); + + //Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) + CALL_MANAGED_METHOD_NORET(args) + GCPROTECT_END(); +} +#endif // FEATURE_EH_FUNCLETS + HCIMPL0(void, IL_Rethrow) { FCALL_CONTRACT; @@ -4275,6 +4342,14 @@ HCIMPL0(void, IL_Rethrow) HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + RethrowNew(); + UNREACHABLE(); + } +#endif + OBJECTREF throwable = GetThread()->GetThrowable(); if (throwable != NULL) { @@ -4935,8 +5010,6 @@ HCIMPL0(void, JIT_PInvokeEndRarePath) thread->HandleThreadAbort(); HELPER_METHOD_FRAME_END(); - InlinedCallFrame* frame = (InlinedCallFrame*)thread->m_pFrame; - thread->m_pFrame->Pop(thread); END_PRESERVE_LAST_ERROR; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9215594ced082..f44e6fae5b03f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14730,6 +14730,43 @@ void EECodeInfo::GetOffsetsFromUnwindInfo(ULONG* pRSPOffset, ULONG* pRBPOffset) } #undef kRBP +ULONG EECodeInfo::GetFrameOffsetFromUnwindInfo() +{ + WRAPPER_NO_CONTRACT; + + SUPPORTS_DAC; + + // moduleBase is a target address. + TADDR moduleBase = GetModuleBase(); + + DWORD unwindInfo = RUNTIME_FUNCTION__GetUnwindInfoAddress(GetFunctionEntry()); + + if ((unwindInfo & RUNTIME_FUNCTION_INDIRECT) != 0) + { + unwindInfo = RUNTIME_FUNCTION__GetUnwindInfoAddress(PTR_RUNTIME_FUNCTION(moduleBase + (unwindInfo & ~RUNTIME_FUNCTION_INDIRECT))); + } + + UNWIND_INFO * pInfo = GetUnwindInfoHelper(unwindInfo); + _ASSERTE((pInfo->Flags & UNW_FLAG_CHAININFO) == 0); + + // Either we are not using a frame pointer, or we are using rbp as the frame pointer. + if ( (pInfo->FrameRegister != 0) && (pInfo->FrameRegister != kRBP) ) + { + _ASSERTE(!"GetRbpOffset() - non-RBP frame pointer used, violating assumptions of the security stackwalk cache"); + DebugBreak(); + } + + ULONG frameOffset = pInfo->FrameOffset; +#ifdef UNIX_AMD64_ABI + if ((frameOffset == 15) && (pInfo->UnwindCode[0].UnwindOp == UWOP_SET_FPREG_LARGE)) + { + frameOffset = *(ULONG*)&pInfo->UnwindCode[1]; + } +#endif + + return frameOffset; +} + #if defined(_DEBUG) && defined(HAVE_GCCOVER) diff --git a/src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S b/src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S index 66373c23e79d4..4c7069c40eff0 100644 --- a/src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S +++ b/src/coreclr/vm/loongarch64/calldescrworkerloongarch64.S @@ -72,6 +72,7 @@ LOCAL_LABEL(NoFloatingPoint): // call pTarget jirl $ra, $t4, 0 +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): ld.w $a3, $s0, CallDescrData__fpReturnSize @@ -205,4 +206,8 @@ LOCAL_LABEL(ReturnDone): EPILOG_RESTORE_REG 23, 16 EPILOG_RESTORE_REG_PAIR_INDEXED 22, 1, 0x20 jirl $r0, $ra, 0 + +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .quad LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) + NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 6adf044a0170d..dc2173f044021 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -182,6 +182,9 @@ DEFINE_METASIG(SM(Obj_IntPtr_RetVoid, j I, v)) DEFINE_METASIG(SM(Obj_IntPtr_RetBool, j I, F)) DEFINE_METASIG(SM(Obj_IntPtr_IntPtr_Int_RetIntPtr, j I I i, I)) DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j)) +DEFINE_METASIG_T(SM(Obj_RefExInfo_RetVoid, j r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(UInt_RefExInfo_RetVoid, K r(g(EXINFO)), v)) +DEFINE_METASIG_T(SM(RefExInfo_RefExInfo_RetVoid, r(g(EXINFO)) r(g(EXINFO)), v)) #ifdef FEATURE_COMINTEROP DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I)) DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RetIntPtr, j I r(I), I)) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index bf8039efb201f..e67abfafcea52 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -74,6 +74,8 @@ #include +#include "exceptionhandlingqcalls.h" + static const Entry s_QCall[] = { DllImportEntry(Enum_GetValuesAndNames) @@ -321,6 +323,16 @@ static const Entry s_QCall[] = DllImportEntry(ComWeakRefToObject) DllImportEntry(ObjectToComWeakRef) #endif +#ifdef FEATURE_EH_FUNCLETS + DllImportEntry(RhpSfiInit) + DllImportEntry(RhpSfiNext) + DllImportEntry(RhpCallCatchFunclet) + DllImportEntry(RhpCallFilterFunclet) + DllImportEntry(RhpCallFinallyFunclet) + DllImportEntry(RhpEHEnumInitFromStackFrameIterator) + DllImportEntry(RhpEHEnumNext) + DllImportEntry(RhpAppendExceptionStackFrame) +#endif // FEATURE_EH_FUNCLETS }; const void* QCallResolveDllImport(const char* name) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 0c1f94cd03c79..72cf39758b1fd 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1982,7 +1982,7 @@ extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QC { GCX_COOP(); - struct gc { + struct { BASEARRAYREF values; PTRARRAYREF names; } gc; diff --git a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S index b244fa46a2fc0..2139787bee149 100644 --- a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S +++ b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S @@ -70,6 +70,7 @@ LOCAL_LABEL(NoFloatingPoint): // call pTarget jalr t4 +LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): lw a3, CallDescrData__fpReturnSize(s1) @@ -202,4 +203,8 @@ LOCAL_LABEL(ReturnDone): EPILOG_RESTORE_REG s1, 16 EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0x20 ret + +PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset + .quad LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) + NESTED_END CallDescrWorkerInternal, _TEXT diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index cb1f63a52b41d..5755a31b84243 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -19,11 +19,12 @@ #endif // FEATURE_INTERPRETER #include "gcinfodecoder.h" - #ifdef FEATURE_EH_FUNCLETS #define PROCESS_EXPLICIT_FRAME_BEFORE_MANAGED_FRAME #endif +#include "exinfo.h" + CrawlFrame::CrawlFrame() { LIMITED_METHOD_DAC_CONTRACT; @@ -1108,8 +1109,11 @@ void StackFrameIterator::CommonCtor(Thread * pThread, PTR_Frame pFrame, ULONG32 m_sfParent = StackFrame(); ResetGCRefReportingState(); m_fDidFuncletReportGCReferences = true; + m_isRuntimeWrappedExceptions = false; #endif // FEATURE_EH_FUNCLETS - + m_forceReportingWhileSkipping = ForceGCReportingStage::Off; + m_movedPastFirstExInfo = false; + m_fFuncletNotSeen = false; #if defined(RECORD_RESUMABLE_FRAME_SP) m_pvResumableFrameTargetSP = NULL; #endif @@ -1209,6 +1213,13 @@ BOOL StackFrameIterator::Init(Thread * pThread, m_exInfoWalk.WalkToPosition(dac_cast(m_pStartFrame), false); #endif // ELIMINATE_FEF +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + m_pNextExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); + } +#endif // FEATURE_EH_FUNCLETS + // // These fields are used in the iteration and will be updated on a per-frame basis: // @@ -1613,9 +1624,34 @@ StackWalkAction StackFrameIterator::Filter(void) #if defined(FEATURE_EH_FUNCLETS) ExceptionTracker* pTracker = m_crawl.pThread->GetExceptionState()->GetCurrentExceptionTracker(); + ExInfo* pExInfo = m_crawl.pThread->GetExceptionState()->GetCurrentExInfo(); fRecheckCurrentFrame = false; fSkipFuncletCallback = true; + if ((m_flags & GC_FUNCLET_REFERENCE_REPORTING) && (pExInfo != NULL) && (m_crawl.GetRegisterSet()->SP > (SIZE_T)pExInfo)) + { + if (!m_movedPastFirstExInfo) + { + if ((pExInfo->m_passNumber == 2) && !pExInfo->m_csfEnclosingClause.IsNull() && m_sfFuncletParent.IsNull()) + { + // We are in the 2nd pass and we have already called an exceptionally called + // a finally funclet, 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; + } + } + // by default, there is no funclet for the current frame // that reported GC references m_crawl.fShouldParentToFuncletSkipReportingGCReferences = false; @@ -1812,6 +1848,17 @@ StackWalkAction StackFrameIterator::Filter(void) // can use it. m_sfParent = m_sfIntermediaryFuncletParent; fSkipFuncletCallback = false; + + if (g_isNewExceptionHandlingEnabled) + { + 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\n"); + } + } } } } @@ -1825,6 +1872,7 @@ StackWalkAction StackFrameIterator::Filter(void) { // Get a reference to the funclet's parent frame. m_sfFuncletParent = ExceptionTracker::FindParentStackFrameForStackWalk(&m_crawl, true); + _ASSERTE(!m_fFuncletNotSeen); if (m_sfFuncletParent.IsNull()) { @@ -1850,6 +1898,17 @@ StackWalkAction StackFrameIterator::Filter(void) // can use it. m_sfParent = m_sfFuncletParent; + if (g_isNewExceptionHandlingEnabled) + { + 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\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 @@ -2012,10 +2071,15 @@ StackWalkAction StackFrameIterator::Filter(void) // check if the parent frame of the funclet is also handling an exception. if it is, then we will need to // report roots for it since the catch handler may use references inside it. - STRESS_LOG0(LF_GCROOTS, LL_INFO100, - "STACKWALK: Reached parent of funclet which didn't report GC roots, since funclet is already unwound.\n"); + if (g_isNewExceptionHandlingEnabled) + { + STRESS_LOG2(LF_GCROOTS, LL_INFO100, + "STACKWALK: Reached parent of funclet which didn't report GC roots, since funclet is already unwound, pExInfo->m_sfCallerOfActualHandlerFrame=%p, m_sfFuncletParent=%p\n", (void*)pExInfo->m_sfCallerOfActualHandlerFrame.SP, (void*)m_sfFuncletParent.SP); + } - if (pTracker->GetCallerOfActualHandlingFrame() == m_sfFuncletParent) + _ASSERT(pExInfo != NULL || pTracker != NULL); + if ((pExInfo && pExInfo->m_sfCallerOfActualHandlerFrame == m_sfFuncletParent) || + (pTracker && pTracker->GetCallerOfActualHandlingFrame() == m_sfFuncletParent)) { // we should not skip reporting for this parent frame shouldSkipReporting = false; @@ -2030,15 +2094,29 @@ StackWalkAction StackFrameIterator::Filter(void) // would report garbage values as live objects. So instead parent can use the IP of the resume // address of catch funclet to report live GC references. m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = true; - // Store catch clause info. Helps retrieve IP of resume address. - m_crawl.ehClauseForCatch = pTracker->GetEHClauseForCatch(); - - STRESS_LOG3(LF_GCROOTS, LL_INFO100, - "STACKWALK: Parent of funclet which didn't report GC roots is handling an exception at 0x%p" - "(EH handler range [%x, %x) ), so we need to specially report roots to ensure variables alive" - " in its handler stay live.\n", - pTracker->GetCatchToCallPC(), m_crawl.ehClauseForCatch.HandlerStartPC, - m_crawl.ehClauseForCatch.HandlerEndPC); + + if (g_isNewExceptionHandlingEnabled) + { + m_crawl.ehClauseForCatch = pExInfo->m_ClauseForCatch; + STRESS_LOG2(LF_GCROOTS, LL_INFO100, + "STACKWALK: Parent of funclet which didn't report GC roots is handling an exception" + "(EH handler range [%x, %x) ), so we need to specially report roots to ensure variables alive" + " in its handler stay live.\n", + m_crawl.ehClauseForCatch.HandlerStartPC, + m_crawl.ehClauseForCatch.HandlerEndPC); + } + else + { + // Store catch clause info. Helps retrieve IP of resume address. + m_crawl.ehClauseForCatch = pTracker->GetEHClauseForCatch(); + + STRESS_LOG3(LF_GCROOTS, LL_INFO100, + "STACKWALK: Parent of funclet which didn't report GC roots is handling an exception at 0x%p" + "(EH handler range [%x, %x) ), so we need to specially report roots to ensure variables alive" + " in its handler stay live.\n", + pTracker->GetCatchToCallPC(), m_crawl.ehClauseForCatch.HandlerStartPC, + m_crawl.ehClauseForCatch.HandlerEndPC); + } } else if (!m_crawl.IsFunclet()) { @@ -2047,11 +2125,29 @@ StackWalkAction StackFrameIterator::Filter(void) // parent is a funclet since the leaf funclet didn't report any references and // we might have a catch handler below us that might contain GC roots. m_fDidFuncletReportGCReferences = true; + STRESS_LOG0(LF_GCROOTS, LL_INFO100, + "STACKWALK: Reached parent of funclet which didn't report GC roots is not a funclet, resetting m_fDidFuncletReportGCReferences to true\n"); } - STRESS_LOG4(LF_GCROOTS, LL_INFO100, - "Funclet didn't report references: handling frame: %p, m_sfFuncletParent = %p, is funclet: %d, skip reporting %d\n", - pTracker->GetEstablisherOfActualHandlingFrame().SP, m_sfFuncletParent.SP, m_crawl.IsFunclet(), shouldSkipReporting); + if (g_isNewExceptionHandlingEnabled) + { + _ASSERTE(!ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException(&m_crawl)); + if (m_fFuncletNotSeen && m_crawl.IsFunclet()) + { + _ASSERTE(!m_fProcessIntermediaryNonFilterFunclet); + _ASSERTE(m_crawl.fShouldCrawlframeReportGCReferences); + m_fDidFuncletReportGCReferences = true; + shouldSkipReporting = false; + m_crawl.fShouldParentFrameUseUnwindTargetPCforGCReporting = true; + m_crawl.ehClauseForCatch = pExInfo->m_ClauseForCatch; + } + } + else + { + STRESS_LOG4(LF_GCROOTS, LL_INFO100, + "Funclet didn't report references: handling frame: %p, m_sfFuncletParent = %p, is funclet: %d, skip reporting %d\n", + pTracker->GetEstablisherOfActualHandlingFrame().SP, m_sfFuncletParent.SP, m_crawl.IsFunclet(), shouldSkipReporting); + } } m_crawl.fShouldParentToFuncletSkipReportingGCReferences = shouldSkipReporting; @@ -2109,7 +2205,7 @@ StackWalkAction StackFrameIterator::Filter(void) } else if (fSkipFuncletCallback && (m_flags & GC_FUNCLET_REFERENCE_REPORTING)) { - if (!m_sfParent.IsNull()) + if (!m_sfParent.IsNull() && (m_forceReportingWhileSkipping == ForceGCReportingStage::Off)) { STRESS_LOG4(LF_GCROOTS, LL_INFO100, "STACKWALK: %s: not making callback for this frame, SPOfParent = %p, \ @@ -2122,6 +2218,22 @@ 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, "RhpCallFinallyFunclet") == 0)); + } +#endif } } } @@ -2215,6 +2327,11 @@ 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: @@ -3234,6 +3351,15 @@ void StackFrameIterator::PostProcessingForNoFrameTransition() #endif // ELIMINATE_FEF } // StackFrameIterator::PostProcessingForNoFrameTransition() +#ifdef FEATURE_EH_FUNCLETS +void StackFrameIterator::ResetNextExInfoForSP(TADDR SP) +{ + while (m_pNextExInfo && (SP > (TADDR)(m_pNextExInfo))) + { + m_pNextExInfo = m_pNextExInfo->m_pPrevExInfo; + } +} +#endif // FEATURE_EH_FUNCLETS #if defined(TARGET_AMD64) && !defined(DACCESS_COMPILE) static CrstStatic g_StackwalkCacheLock; // Global StackwalkCache lock; only used on AMD64 diff --git a/src/coreclr/vm/stackwalk.h b/src/coreclr/vm/stackwalk.h index a85c85650a577..fd0fa7c4c6e63 100644 --- a/src/coreclr/vm/stackwalk.h +++ b/src/coreclr/vm/stackwalk.h @@ -63,11 +63,15 @@ StackWalkAction StackWalkFunctions(Thread * thread, PSTACKWALKFRAMESCALLBACK pCa #define StackWalkFunctions(thread, callBack, userdata) thread->StackWalkFrames(METHODSONLY, (callBack),(userData)) */ +namespace AsmOffsetsAsserts +{ + class AsmOffsets; +}; class CrawlFrame { public: - + friend class AsmOffsetsAsserts::AsmOffsets; #ifdef TARGET_X86 friend StackWalkAction TAStackCrawlCallBack(CrawlFrame* pCf, void* data); #endif // TARGET_X86 @@ -567,6 +571,7 @@ class ExInfoWalker class StackFrameIterator { + friend class AsmOffsetsAsserts::AsmOffsets; public: // This constructor is for the usage pattern of creating an uninitialized StackFrameIterator and then // calling Init() on it. @@ -604,6 +609,36 @@ class StackFrameIterator // advance to the next frame according to the stackwalk flags StackWalkAction Next(void); +#ifdef FEATURE_EH_FUNCLETS + void ResetNextExInfoForSP(TADDR SP); + + ExInfo* GetNextExInfo() + { + return m_pNextExInfo; + } + + void SetAdjustedControlPC(TADDR pc) + { + m_AdjustedControlPC = pc; + } + + void UpdateIsRuntimeWrappedExceptions() + { + CONTRACTL + { + MODE_ANY; + GC_TRIGGERS; + NOTHROW; + } + CONTRACTL_END + +#if defined(FEATURE_EH_FUNCLETS) && !defined(DACCESS_COMPILE) + m_isRuntimeWrappedExceptions = (m_crawl.pFunc != NULL) && m_crawl.pFunc->GetModule()->IsRuntimeWrapExceptions(); +#endif // FEATURE_EH_FUNCLETS && !DACCESS_COMPILE + } + +#endif // FEATURE_EH_FUNCLETS + enum FrameState { SFITER_UNINITIALIZED, // uninitialized @@ -626,6 +661,24 @@ class StackFrameIterator #endif // _DEBUG 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); @@ -672,6 +725,7 @@ class StackFrameIterator if (!ResetOnlyIntermediaryState) { + m_fFuncletNotSeen = false; m_sfFuncletParent = StackFrame(); m_fProcessNonFilterFunclet = false; } @@ -692,7 +746,6 @@ class StackFrameIterator // This is the real starting explicit frame. If m_pStartFrame is NULL, // then this is equal to m_pThread->GetFrame(). Otherwise this is equal to m_pStartFrame. INDEBUG(PTR_Frame m_pRealStartFrame); - ULONG32 m_flags; // StackWalkFrames flags. ICodeManagerFlags m_codeManFlags; ExecutionManager::ScanFlag m_scanFlag; @@ -717,11 +770,21 @@ class StackFrameIterator StackFrame m_sfIntermediaryFuncletParent; bool m_fProcessIntermediaryNonFilterFunclet; 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; #if defined(RECORD_RESUMABLE_FRAME_SP) LPVOID m_pvResumableFrameTargetSP; #endif // RECORD_RESUMABLE_FRAME_SP +#ifdef FEATURE_EH_FUNCLETS + ExInfo* m_pNextExInfo; + TADDR m_AdjustedControlPC; +#endif // FEATURE_EH_FUNCLETS }; void SetUpRegdisplayForStackWalk(Thread * pThread, T_CONTEXT * pContext, REGDISPLAY * pRegdisplay); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index edbd6a011f33b..ff0d782e1c86d 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -8233,13 +8233,13 @@ void Thread::InitializeSpecialUserModeApc() #endif // FEATURE_SPECIAL_USER_MODE_APC #if !(defined(TARGET_WINDOWS) && defined(TARGET_X86)) -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +#if defined(TARGET_AMD64) EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); #endif void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) { -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +#if defined(TARGET_AMD64) DWORD64 ssp = GetSSP(ContextRecord); __asan_handle_no_return(); ClrRestoreNonvolatileContextWorker(ContextRecord, ssp); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 1a454a8dc54c1..4cf13a424dc72 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -963,6 +963,14 @@ BOOL Thread::ReadyForAsyncException() return FALSE; } +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + // TODO: make thread abort work for the new exception handling + return FALSE; + } +#endif // FEATURE_EH_FUNCLETS + REGDISPLAY rd; Frame *pStartFrame = NULL; @@ -2255,7 +2263,16 @@ void Thread::HandleThreadAbort () exceptObj = CLRException::GetThrowableFromException(&eeExcept); } - RaiseTheExceptionInternalOnly(exceptObj, FALSE); +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + DispatchManagedException(exceptObj); + } + else +#endif // FEATURE_EH_FUNCLETS + { + RaiseTheExceptionInternalOnly(exceptObj, FALSE); + } } END_PRESERVE_LAST_ERROR; @@ -3955,10 +3972,27 @@ ThrowControlForThread( STRESS_LOG0(LF_SYNC, LL_INFO100, "ThrowControlForThread Aborting\n"); - // Here we raise an exception. - INSTALL_MANAGED_EXCEPTION_DISPATCHER - RaiseComPlusException(); - UNINSTALL_MANAGED_EXCEPTION_DISPATCHER +#ifdef FEATURE_EH_FUNCLETS + if (g_isNewExceptionHandlingEnabled) + { + GCX_COOP(); + + EXCEPTION_RECORD exceptionRecord = {0}; + exceptionRecord.NumberParameters = MarkAsThrownByUs(exceptionRecord.ExceptionInformation); + exceptionRecord.ExceptionCode = EXCEPTION_COMPLUS; + exceptionRecord.ExceptionFlags = 0; + + OBJECTREF throwable = ExceptionTracker::CreateThrowable(&exceptionRecord, TRUE); + DispatchManagedException(throwable); + } + else +#endif // FEATURE_EH_FUNCLETS + { + // Here we raise an exception. + INSTALL_MANAGED_EXCEPTION_DISPATCHER + RaiseComPlusException(); + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER + } } #if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX) diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index e8d4789b29644..f633fffa3cc22 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -107,6 +107,10 @@ GVAL_IMPL_INIT(DWORD, g_debuggerWordTLSIndex, TLS_OUT_OF_INDEXES); GVAL_IMPL_INIT(DWORD, g_TlsIndex, TLS_OUT_OF_INDEXES); MethodTable* g_pCastHelpers; +#ifdef FEATURE_EH_FUNCLETS +GPTR_IMPL(MethodTable, g_pEHClass); +GVAL_IMPL(bool, g_isNewExceptionHandlingEnabled); +#endif #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 5110be83ed1bc..8403522b4010f 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -389,6 +389,11 @@ GVAL_DECL(DWORD, g_debuggerWordTLSIndex); #endif GVAL_DECL(DWORD, g_TlsIndex); +#ifdef FEATURE_EH_FUNCLETS +GPTR_DECL(MethodTable, g_pEHClass); +GVAL_DECL(bool, g_isNewExceptionHandlingEnabled); +#endif + // Global System Information extern SYSTEM_INFO g_SystemInfo; diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index 23e968c7d1729..eef01d3550b0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -82,6 +82,13 @@ 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 event EventHandler? ProcessExit; internal static void OnProcessExit() From 278ba857d14bb283ca21f660847dce9baa3e67b6 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 16 Aug 2023 23:18:43 +0200 Subject: [PATCH 2/5] Remove Rhp prefix from the native helpers --- .../ExceptionServices/InternalCalls.cs | 16 ++++++------- src/coreclr/vm/exceptionhandling.cpp | 24 +++++++++---------- src/coreclr/vm/exceptionhandlingqcalls.h | 16 ++++++------- src/coreclr/vm/frames.h | 2 +- src/coreclr/vm/qcallentrypoints.cpp | 16 ++++++------- src/coreclr/vm/stackwalk.cpp | 2 +- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs index 9f84e534f61cd..563c5b3704119 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 @@ -12,36 +12,36 @@ namespace System.Runtime.ExceptionServices { internal static partial class InternalCalls { - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpSfiInit")] + [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); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpSfiNext")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "SfiNext")] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); #pragma warning disable CS8500 - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallCatchFunclet")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallCatchFunclet")] internal static unsafe partial IntPtr RhpCallCatchFunclet( ObjectHandleOnStack exceptionObj, byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallFinallyFunclet")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFinallyFunclet")] internal static unsafe partial void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpCallFilterFunclet")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFilterFunclet")] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool RhpCallFilterFunclet( ObjectHandleOnStack exceptionObj, byte* pFilterIP, void* pvRegDisplay); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpAppendExceptionStackFrame")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AppendExceptionStackFrame")] internal static unsafe partial void RhpAppendExceptionStackFrame(ObjectHandleOnStack exceptionObj, IntPtr ip, UIntPtr sp, int flags, EH.ExInfo* exInfo); #pragma warning restore CS8500 - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpEHEnumInitFromStackFrameIterator")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EHEnumInitFromStackFrameIterator")] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RhpEHEnumNext")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EHEnumNext")] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool RhpEHEnumNext(void* pEHEnum, void* pEHClause); } diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 9bbce840404f5..5a9a79a1cd972 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -7482,7 +7482,7 @@ void ExceptionTracker::ResetThreadAbortStatus(PTR_Thread pThread, CrawlFrame *pC #endif //!DACCESS_COMPILE #ifndef DACCESS_COMPILE -// Mark the pinvoke frame as invoking RhpCallCatchFunclet (and similar) for collided unwind detection +// Mark the pinvoke frame as invoking CallCatchFunclet (and similar) for collided unwind detection void MarkInlinedCallFrameAsFuncletCall(Frame* pFrame) { _ASSERTE(pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); @@ -7498,7 +7498,7 @@ void MarkInlinedCallFrameAsEHHelperCall(Frame* pFrame) pInlinedCallFrame->m_Datum = (PTR_NDirectMethodDesc)((TADDR)pInlinedCallFrame->m_Datum | (TADDR)InlinedCallFrameMarker::ExceptionHandlingHelper); } -extern "C" void QCALLTYPE RhpAppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo) +extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo) { QCALL_CONTRACT; @@ -7551,7 +7551,7 @@ UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) #endif } -extern "C" void * QCALLTYPE RhpCallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { QCALL_CONTRACT; @@ -7702,7 +7702,7 @@ extern "C" void * QCALLTYPE RhpCallCatchFunclet(QCall::ObjectHandleOnStack excep return NULL; } -extern "C" void QCALLTYPE RhpCallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) +extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { QCALL_CONTRACT; @@ -7738,7 +7738,7 @@ extern "C" void QCALLTYPE RhpCallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pv END_QCALL; } -extern "C" BOOL QCALLTYPE RhpCallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterIP, REGDISPLAY* pvRegDisplay) +extern "C" BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterIP, REGDISPLAY* pvRegDisplay) { QCALL_CONTRACT; @@ -7793,7 +7793,7 @@ struct ExtendedEHClauseEnumerator : EH_CLAUSE_ENUMERATOR unsigned EHCount; }; -extern "C" BOOL QCALLTYPE RhpEHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum) +extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum) { QCALL_CONTRACT; @@ -7815,7 +7815,7 @@ extern "C" BOOL QCALLTYPE RhpEHEnumInitFromStackFrameIterator(StackFrameIterator return pExtendedEHEnum->EHCount != 0; } -extern "C" BOOL QCALLTYPE RhpEHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) +extern "C" BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) { QCALL_CONTRACT; BOOL result = FALSE; @@ -7891,7 +7891,7 @@ extern uint32_t g_exceptionCount; MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut); -extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault) +extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault) { QCALL_CONTRACT; @@ -7906,7 +7906,7 @@ extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackw // just clear the thread state. pThread->ResetThrowControlForThread(); - // Skip the RhpSfiInit pinvoke frame + // Skip the SfiInit pinvoke frame pFrame = pThread->GetFrame()->PtrNextFrame(); ExInfo* pExInfo = pThread->GetExceptionState()->GetCurrentExInfo(); @@ -8030,7 +8030,7 @@ extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackw extern "C" size_t CallDescrWorkerInternalReturnAddressOffset; -extern "C" bool QCALLTYPE RhpSfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) +extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke) { QCALL_CONTRACT; @@ -8138,10 +8138,10 @@ extern "C" bool QCALLTYPE RhpSfiNext(StackFrameIterator* pThis, uint* uExCollide InlinedCallFrame* pInlinedCallFrame = (InlinedCallFrame*)pFrame; if (((TADDR)pInlinedCallFrame->m_Datum & 6) == 6) { - // passing through RhpCallCatchFunclet et al + // passing through CallCatchFunclet et al if (doingFuncletUnwind) { - // Unwind the RhpCallCatchFunclet + // Unwind the CallCatchFunclet retVal = pThis->Next(); if (retVal == SWA_FAILED) { diff --git a/src/coreclr/vm/exceptionhandlingqcalls.h b/src/coreclr/vm/exceptionhandlingqcalls.h index 2bdd73c4a0379..2a1d77ecb94d9 100644 --- a/src/coreclr/vm/exceptionhandlingqcalls.h +++ b/src/coreclr/vm/exceptionhandlingqcalls.h @@ -12,14 +12,14 @@ struct ExInfo; #ifndef DACCESS_COMPILE -extern "C" void * QCALLTYPE RhpCallCatchFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); -extern "C" void QCALLTYPE RhpCallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo); -extern "C" BOOL QCALLTYPE RhpCallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterP, REGDISPLAY* pvRegDisplay); -extern "C" void QCALLTYPE RhpAppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo); -extern "C" BOOL QCALLTYPE RhpEHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum); -extern "C" BOOL QCALLTYPE RhpEHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause); -extern "C" bool QCALLTYPE RhpSfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalkCtx, bool instructionFault); -extern "C" bool QCALLTYPE RhpSfiNext(StackFrameIterator* pThis, unsigned int* uExCollideClauseIdx, bool* fUnwoundReversePInvoke); +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 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); #endif // DACCESS_COMPILE #endif // FEATURE_EH_FUNCLETS diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index c0c8c7f33f93e..907cc2e0e3eb7 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2875,7 +2875,7 @@ class InlinedCallFrame : public Frame // - on windows x86 host: argument stack size (if value is <64k) // When m_Datum contains MethodDesc ptr, then on other than windows x86 host // - bit 1 set indicates invoking new exception handling helpers - // - bit 2 indicates RhpCallCatchFunclet or RhpCallFinallyFunclet + // - bit 2 indicates CallCatchFunclet or CallFinallyFunclet // See code:HasFunction. PTR_NDirectMethodDesc m_Datum; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e67abfafcea52..0725bd7a87f09 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -324,14 +324,14 @@ static const Entry s_QCall[] = DllImportEntry(ObjectToComWeakRef) #endif #ifdef FEATURE_EH_FUNCLETS - DllImportEntry(RhpSfiInit) - DllImportEntry(RhpSfiNext) - DllImportEntry(RhpCallCatchFunclet) - DllImportEntry(RhpCallFilterFunclet) - DllImportEntry(RhpCallFinallyFunclet) - DllImportEntry(RhpEHEnumInitFromStackFrameIterator) - DllImportEntry(RhpEHEnumNext) - DllImportEntry(RhpAppendExceptionStackFrame) + DllImportEntry(SfiInit) + DllImportEntry(SfiNext) + DllImportEntry(CallCatchFunclet) + DllImportEntry(CallFilterFunclet) + DllImportEntry(CallFinallyFunclet) + DllImportEntry(EHEnumInitFromStackFrameIterator) + DllImportEntry(EHEnumNext) + DllImportEntry(AppendExceptionStackFrame) #endif // FEATURE_EH_FUNCLETS }; diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 5755a31b84243..e67334bd51fc7 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -2231,7 +2231,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, "RhpCallFinallyFunclet") == 0)); + _ASSERTE((m_crawl.pFunc->GetMethodTable() == g_pEHClass) || (strcmp(m_crawl.pFunc->m_pszDebugClassName, "ILStubClass") == 0) || (strcmp(m_crawl.pFunc->m_pszDebugMethodName, "CallFinallyFunclet") == 0)); } #endif } From 28f603b51fcec7c72ab4a40660d6e84f7d9d22cc Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 22 Aug 2023 14:58:27 +0200 Subject: [PATCH 3/5] Fix handling of exceptions from JIT The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the `EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native exception handling mechanism instead of calling the new managed exception handling, as the native exception needs to propagate through some native code layers from there. This change adds parameter to the `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether to rethrow the exception as native or to invoke the new managed exception handling. This problem didn't show up until I ran the coreclr tests with tiered compilation disabled. --- src/coreclr/vm/excep.cpp | 4 ++-- src/coreclr/vm/exceptmacros.h | 8 ++++---- src/coreclr/vm/jitinterface.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index d40acb1586566..63fffde692ae1 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -7771,7 +7771,7 @@ void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pE // 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 :) // -VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException) +VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException, bool nativeRethrow) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -7788,7 +7788,7 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra Exception::Delete(pException); #ifdef FEATURE_EH_FUNCLETS - if (g_isNewExceptionHandlingEnabled) + if (g_isNewExceptionHandlingEnabled && !nativeRethrow) { DispatchManagedException(orThrowable); } diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 59f293d1c879b..84a6f0f7902a7 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -272,7 +272,7 @@ VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL r #else // DACCESS_COMPILE void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pException); -VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException); +VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException, bool nativeRethrow); #ifdef TARGET_UNIX VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException); @@ -349,7 +349,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar SCAN_EHMARKER_TRY(); \ DEBUG_ASSURE_NO_RETURN_BEGIN(IUACH); -#define UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE \ +#define UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(nativeRethrow) \ DEBUG_ASSURE_NO_RETURN_END(IUACH) \ SCAN_EHMARKER_END_TRY(); \ } \ @@ -366,12 +366,12 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar if (__fExceptionCaught) \ { \ SCAN_EHMARKER_CATCH(); \ - UnwindAndContinueRethrowHelperAfterCatch(__pUnCEntryFrame, __pUnCException); \ + UnwindAndContinueRethrowHelperAfterCatch(__pUnCEntryFrame, __pUnCException, nativeRethrow); \ } \ } \ #define UNINSTALL_UNWIND_AND_CONTINUE_HANDLER \ - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(false); #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f44e6fae5b03f..3ea47d07eae6d 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -96,7 +96,7 @@ __thread uint32_t t_GCThreadStaticBlocksSize; COOPERATIVE_TRANSITION_BEGIN(); \ #define EE_TO_JIT_TRANSITION() COOPERATIVE_TRANSITION_END(); \ - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true); #define JIT_TO_EE_TRANSITION_LEAF() #define EE_TO_JIT_TRANSITION_LEAF() From c4f2147e958f2b1466584dcc88cfcbdece4fa447 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 23 Aug 2023 19:55:10 +0200 Subject: [PATCH 4/5] Update UNINSTALL_UNWIND_AND_CONTINUE_HANDLER usage There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER needed to be replaced by UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true). --- src/coreclr/vm/excep.cpp | 4 ++-- src/coreclr/vm/interoputil.cpp | 8 ++++---- src/coreclr/vm/threads.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 63fffde692ae1..2ea7cbe774416 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6048,7 +6048,7 @@ CreateCOMPlusExceptionObject(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord ThreadPreventAsyncHolder preventAsync; ResetProcessorStateHolder procState; - INSTALL_UNWIND_AND_CONTINUE_HANDLER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; GCPROTECT_BEGIN(result) @@ -6063,7 +6063,7 @@ CreateCOMPlusExceptionObject(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord GCPROTECT_END(); - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true); } EX_CATCH { diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index d2dc577b30cf7..e579d541f42a4 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -919,9 +919,9 @@ int InternalWideToAnsi(_In_reads_(iNumWideChars) LPCWSTR szWideString, int iNumW if (retval == 0) { - INSTALL_UNWIND_AND_CONTINUE_HANDLER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; COMPlusThrowHR(HRESULT_FROM_WIN32(lastError)); - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true); } if (DefaultCharUsed) @@ -952,9 +952,9 @@ int InternalWideToAnsi(_In_reads_(iNumWideChars) LPCWSTR szWideString, int iNumW if (retval == 0) { - INSTALL_UNWIND_AND_CONTINUE_HANDLER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; COMPlusThrowHR(HRESULT_FROM_WIN32(lastError)); - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true); } } diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index ff0d782e1c86d..4a660b24ff603 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -7284,11 +7284,11 @@ static void ManagedThreadBase_DispatchMiddle(ManagedThreadCallState *pCallState) // // Without unwind_and_continue_handler below, the exception will fly up the stack to // this point, where it will be rethrown and thus leak out. - INSTALL_UNWIND_AND_CONTINUE_HANDLER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; EX_RETHROW; - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true); } } EX_END_CATCH(SwallowAllExceptions); From 0c3bcfdc1ca427866337c3b2f6aa81d980945949 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 23 Aug 2023 23:01:18 +0200 Subject: [PATCH 5/5] Enable new exception handling for CI testing purposes --- src/coreclr/inc/clrconfigvalues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index e7f861b0556ca..239222fb5b2d6 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -259,7 +259,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("le CONFIG_DWORD_INFO(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableNewExceptionHandling, W("EnableNewExceptionHandling"), 0, "Enable new exception handling."); +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableNewExceptionHandling, W("EnableNewExceptionHandling"), 1, "Enable new exception handling."); ///