Skip to content

Commit 7fc9c86

Browse files
committed
[release/10.0] Move coreclr EH second pass to native code
There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
1 parent e04af3c commit 7fc9c86

File tree

15 files changed

+549
-507
lines changed

15 files changed

+549
-507
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,17 @@ class AsmOffsets
6868
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x132;
6969
#elif TARGET_X86
7070
public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4;
71-
public const int SIZEOF__StackFrameIterator = 0x3d4;
71+
public const int SIZEOF__StackFrameIterator = 0x3d0;
7272
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3c2;
73-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3d0;
73+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3cc;
7474
#else // TARGET_64BIT
7575
public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4;
7676
#if FEATURE_INTERPRETER
77-
public const int SIZEOF__StackFrameIterator = 0xdc;
78-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd8;
77+
public const int SIZEOF__StackFrameIterator = 0xd8;
78+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd4;
7979
#else
80-
public const int SIZEOF__StackFrameIterator = 0xcc;
81-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc8;
80+
public const int SIZEOF__StackFrameIterator = 0xc8;
81+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc4;
8282
#endif
8383
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xba;
8484
#endif // TARGET_64BIT
@@ -139,17 +139,17 @@ class AsmOffsets
139139
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x12a;
140140
#elif TARGET_X86
141141
public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4;
142-
public const int SIZEOF__StackFrameIterator = 0x3cc;
142+
public const int SIZEOF__StackFrameIterator = 0x3c8;
143143
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0x3ba;
144-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c8;
144+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3c4;
145145
#else // TARGET_64BIT
146146
public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4;
147147
#if FEATURE_INTERPRETER
148-
public const int SIZEOF__StackFrameIterator = 0xd4;
149-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd0;
148+
public const int SIZEOF__StackFrameIterator = 0xd0;
149+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xcc;
150150
#else
151-
public const int SIZEOF__StackFrameIterator = 0xc4;
152-
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc0;
151+
public const int SIZEOF__StackFrameIterator = 0xc0;
152+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xbc;
153153
#endif
154154
public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xb2;
155155
#endif // TARGET_64BIT
@@ -217,6 +217,23 @@ class AsmOffsets
217217
public const int OFFSETOF__ExInfo__m_idxCurClause = 0xbc;
218218
public const int OFFSETOF__ExInfo__m_frameIter = 0xc0;
219219
public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator;
220+
public const int OFFSETOF__ExInfo__m_pCatchHandler = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x48;
221+
public const int OFFSETOF__ExInfo__m_handlingFrameSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x50;
222+
223+
#if TARGET_ARM64
224+
public const int OFFSETOF__ExInfo__m_handlingFramePC = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x58;
225+
#endif
226+
227+
#if TARGET_UNIX
228+
#if TARGET_ARM64
229+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x68;
230+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x70;
231+
#else // TARGET_ARM64
232+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x60;
233+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x68;
234+
#endif // TARGET_ARM64
235+
#endif // TARGET_UNIX
236+
220237
#else // TARGET_64BIT
221238
public const int SIZEOF__EHEnum = 0x10;
222239
public const int OFFSETOF__StackFrameIterator__m_pRegDisplay = 0x14;
@@ -228,6 +245,14 @@ class AsmOffsets
228245
public const int OFFSETOF__ExInfo__m_idxCurClause = 0x68;
229246
public const int OFFSETOF__ExInfo__m_frameIter = 0x6c;
230247
public const int OFFSETOF__ExInfo__m_notifyDebuggerSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator;
248+
public const int OFFSETOF__ExInfo__m_pCatchHandler = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x2c;
249+
public const int OFFSETOF__ExInfo__m_handlingFrameSP = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x30;
250+
251+
#if TARGET_UNIX
252+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x38;
253+
public const int OFFSETOF__ExInfo__m_pReversePInvokePropagationContext = OFFSETOF__ExInfo__m_frameIter + SIZEOF__StackFrameIterator + 0x3c;
254+
#endif
255+
231256
#endif // TARGET_64BIT
232257

233258
#if __cplusplus
@@ -268,8 +293,18 @@ class AsmOffsets
268293
static_assert_no_msg(offsetof(ExInfo, m_idxCurClause) == OFFSETOF__ExInfo__m_idxCurClause);
269294
static_assert_no_msg(offsetof(ExInfo, m_frameIter) == OFFSETOF__ExInfo__m_frameIter);
270295
static_assert_no_msg(offsetof(ExInfo, m_notifyDebuggerSP) == OFFSETOF__ExInfo__m_notifyDebuggerSP);
296+
static_assert_no_msg(offsetof(ExInfo, m_pCatchHandler) == OFFSETOF__ExInfo__m_pCatchHandler);
297+
static_assert_no_msg(offsetof(ExInfo, m_handlingFrameSP) == OFFSETOF__ExInfo__m_handlingFrameSP);
298+
#if TARGET_ARM64
299+
static_assert_no_msg(offsetof(ExInfo, m_handlingFramePC) == OFFSETOF__ExInfo__m_handlingFramePC);
271300
#endif
272301

302+
#if TARGET_UNIX
303+
static_assert_no_msg(offsetof(ExInfo, m_propagateExceptionCallback) == OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback);
304+
static_assert_no_msg(offsetof(ExInfo, m_propagateExceptionContext) == OFFSETOF__ExInfo__m_pReversePInvokePropagationContext);
305+
#endif
306+
307+
#endif
273308
}
274309
#if __cplusplus
275310
;

src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ internal static partial class InternalCalls
2020
[return: MarshalAs(UnmanagedType.U1)]
2121
internal static unsafe partial bool RhpSfiNext(ref StackFrameIterator pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* fIsExceptionIntercepted);
2222

23-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ResumeAtInterceptionLocation")]
24-
internal static unsafe partial void ResumeAtInterceptionLocation(void* pvRegDisplay);
25-
26-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallCatchFunclet")]
27-
internal static unsafe partial IntPtr RhpCallCatchFunclet(
28-
ObjectHandleOnStack exceptionObj, byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo);
29-
30-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFinallyFunclet")]
31-
internal static unsafe partial void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay, EH.ExInfo* exInfo);
32-
3323
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "CallFilterFunclet")]
3424
[return: MarshalAs(UnmanagedType.U1)]
3525
internal static unsafe partial bool RhpCallFilterFunclet(

src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs

Lines changed: 51 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,30 @@ internal object ThrownException
550550

551551
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_notifyDebuggerSP)]
552552
internal volatile UIntPtr _notifyDebuggerSP;
553+
#if !NATIVEAOT
554+
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pCatchHandler)]
555+
internal volatile byte* _pCatchHandler;
556+
557+
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFrameSP)]
558+
internal volatile UIntPtr _handlingFrameSP;
559+
560+
#if TARGET_ARM64
561+
// On ARM64, two frames can have the same SP, when a leaf function
562+
// doesn't use any stack. So to distinguish between the caller frame
563+
// and the leaf one, we also need to know the PC of the handling frame.
564+
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFramePC)]
565+
internal volatile byte* _handlingFramePC;
566+
#endif
567+
568+
#if TARGET_UNIX
569+
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pReversePInvokePropagationCallback)]
570+
internal volatile IntPtr _pReversePInvokePropagationCallback;
571+
572+
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pReversePInvokePropagationContext)]
573+
internal volatile IntPtr _pReversePInvokePropagationContext;
574+
#endif // TARGET_UNIX
575+
576+
#endif // !NATIVEAOT
553577
}
554578

555579
//
@@ -637,7 +661,9 @@ public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo)
637661

638662
exInfo.Init(exceptionToThrow!, instructionFault);
639663
DispatchEx(ref exInfo._frameIter, ref exInfo);
664+
#if NATIVEAOT
640665
FallbackFailFast(RhFailFastReason.InternalError, null);
666+
#endif
641667
}
642668

643669
private const uint MaxTryRegionIdx = 0xFFFFFFFFu;
@@ -670,68 +696,11 @@ public static void RhThrowEx(object exceptionObj, ref ExInfo exInfo)
670696

671697
exInfo.Init(exceptionObj);
672698
DispatchEx(ref exInfo._frameIter, ref exInfo);
699+
#if NATIVEAOT
673700
FallbackFailFast(RhFailFastReason.InternalError, null);
701+
#endif
674702
}
675703

676-
#if !NATIVEAOT
677-
public static void RhUnwindAndIntercept(ref ExInfo exInfo, UIntPtr interceptStackFrameSP)
678-
{
679-
exInfo._passNumber = 2;
680-
exInfo._idxCurClause = MaxTryRegionIdx;
681-
uint startIdx = MaxTryRegionIdx;
682-
bool unwoundReversePInvoke = false;
683-
bool isExceptionIntercepted = false;
684-
bool isValid = exInfo._frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0, &isExceptionIntercepted);
685-
for (; isValid && !isExceptionIntercepted && ((byte*)exInfo._frameIter.SP <= (byte*)interceptStackFrameSP); isValid = exInfo._frameIter.Next(&startIdx, &unwoundReversePInvoke, &isExceptionIntercepted))
686-
{
687-
Debug.Assert(isValid, "Unwind and intercept failed unexpectedly");
688-
DebugScanCallFrame(exInfo._passNumber, exInfo._frameIter.ControlPC, exInfo._frameIter.SP);
689-
690-
if (unwoundReversePInvoke)
691-
{
692-
// Found the native frame that called the reverse P/invoke.
693-
// It is not possible to run managed second pass handlers on a native frame.
694-
break;
695-
}
696-
697-
if (exInfo._frameIter.SP == interceptStackFrameSP)
698-
{
699-
break;
700-
}
701-
702-
InvokeSecondPass(ref exInfo, startIdx);
703-
if (isExceptionIntercepted)
704-
{
705-
Debug.Assert(false);
706-
break;
707-
}
708-
}
709-
710-
// ------------------------------------------------
711-
//
712-
// Call the interception code
713-
//
714-
// ------------------------------------------------
715-
if (unwoundReversePInvoke)
716-
{
717-
object exceptionObj = exInfo.ThrownException;
718-
fixed (EH.ExInfo* pExInfo = &exInfo)
719-
{
720-
InternalCalls.RhpCallCatchFunclet(
721-
ObjectHandleOnStack.Create(ref exceptionObj), null, exInfo._frameIter.RegisterSet, pExInfo);
722-
}
723-
}
724-
else
725-
{
726-
InternalCalls.ResumeAtInterceptionLocation(exInfo._frameIter.RegisterSet);
727-
}
728-
729-
Debug.Fail("unreachable");
730-
FallbackFailFast(RhFailFastReason.InternalError, null);
731-
}
732-
#endif // !NATIVEAOT
733-
734-
735704
#if NATIVEAOT
736705
[RuntimeExport("RhRethrow")]
737706
#endif
@@ -757,7 +726,9 @@ public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo)
757726

758727
exInfo.Init(rethrownException, ref activeExInfo);
759728
DispatchEx(ref exInfo._frameIter, ref exInfo);
729+
#if NATIVEAOT
760730
FallbackFailFast(RhFailFastReason.InternalError, null);
731+
#endif
761732
}
762733

763734
[StackTraceHidden]
@@ -877,6 +848,21 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
877848
Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero || unwoundReversePInvoke || isExceptionIntercepted, "We should have a handler if we're starting the second pass");
878849
Debug.Assert(!isExceptionIntercepted || (pCatchHandler == null), "No catch handler should be returned for intercepted exceptions in the first pass");
879850

851+
#if !NATIVEAOT
852+
exInfo._pCatchHandler = pCatchHandler;
853+
exInfo._handlingFrameSP = handlingFrameSP;
854+
#if TARGET_ARM64
855+
exInfo._handlingFramePC = prevOriginalPC;
856+
#endif
857+
#if TARGET_UNIX
858+
exInfo._pReversePInvokePropagationCallback = pReversePInvokePropagationCallback;
859+
exInfo._pReversePInvokePropagationContext = pReversePInvokePropagationContext;
860+
#endif // TARGET_UNIX
861+
exInfo._idxCurClause = catchingTryRegionIdx;
862+
863+
return;
864+
865+
#else // !NATIVEAOT
880866
// ------------------------------------------------
881867
//
882868
// Second pass
@@ -888,9 +874,10 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
888874
// 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the
889875
// 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass
890876
// funclets will always toggle this mode off before invoking them.
891-
#if NATIVEAOT
877+
892878
InternalCalls.RhpSetThreadDoNotTriggerGC();
893-
#endif
879+
880+
894881
exInfo._passNumber = 2;
895882
exInfo._idxCurClause = catchingTryRegionIdx;
896883
startIdx = MaxTryRegionIdx;
@@ -910,10 +897,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
910897

911898
if (unwoundReversePInvoke)
912899
{
913-
#if NATIVEAOT
914900
Debug.Assert(pReversePInvokePropagationCallback != IntPtr.Zero, "Unwound to a reverse P/Invoke in the second pass. We should have a propagation handler.");
915901
Debug.Assert(frameIter.PreviousTransitionFrame != IntPtr.Zero, "Should have a transition frame for reverse P/Invoke.");
916-
#endif
917902
Debug.Assert(frameIter.SP == handlingFrameSP, "Encountered a different reverse P/Invoke frame in the second pass.");
918903
// Found the native frame that called the reverse P/invoke.
919904
// It is not possible to run managed second pass handlers on a native frame.
@@ -937,36 +922,26 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
937922
#if FEATURE_OBJCMARSHAL
938923
if (pReversePInvokePropagationCallback != IntPtr.Zero)
939924
{
940-
#if NATIVEAOT
941925
InternalCalls.RhpCallPropagateExceptionCallback(
942926
pReversePInvokePropagationContext, pReversePInvokePropagationCallback, frameIter.RegisterSet, ref exInfo, frameIter.PreviousTransitionFrame);
943927
// the helper should jump to propagation handler and not return
944-
#endif
945928
Debug.Fail("unreachable");
946929
FallbackFailFast(RhFailFastReason.InternalError, null);
947930
}
948931
#endif // FEATURE_OBJCMARSHAL
949932

950-
951933
// ------------------------------------------------
952934
//
953935
// Call the handler and resume execution
954936
//
955937
// ------------------------------------------------
956938
exInfo._idxCurClause = catchingTryRegionIdx;
957-
#if NATIVEAOT
958939
InternalCalls.RhpCallCatchFunclet(
959940
exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo);
960-
#else // NATIVEAOT
961-
fixed (EH.ExInfo* pExInfo = &exInfo)
962-
{
963-
InternalCalls.RhpCallCatchFunclet(
964-
ObjectHandleOnStack.Create(ref exceptionObj), pCatchHandler, frameIter.RegisterSet, pExInfo);
965-
}
966-
#endif // NATIVEAOT
967941
// currently, RhpCallCatchFunclet will resume after the catch
968942
Debug.Fail("unreachable");
969943
FallbackFailFast(RhFailFastReason.InternalError, null);
944+
#endif // !NATIVEAOT
970945
}
971946

972947
[System.Diagnostics.Conditional("DEBUG")]
@@ -1170,6 +1145,7 @@ private static bool ShouldTypedClauseCatchThisException(object exception, Method
11701145
#endif
11711146
}
11721147

1148+
#if NATIVEAOT
11731149
private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart)
11741150
{
11751151
InvokeSecondPass(ref exInfo, idxStart, MaxTryRegionIdx);
@@ -1207,11 +1183,7 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL
12071183

12081184
// Now, we continue skipping while the try region is identical to the one that invoked the
12091185
// previous dispatch.
1210-
if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)
1211-
#if !NATIVEAOT
1212-
&& (ehClause._isSameTry)
1213-
#endif
1214-
)
1186+
if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd))
12151187
continue;
12161188

12171189
// We are done skipping. This is required to handle empty finally block markers that are used
@@ -1242,19 +1214,11 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxL
12421214

12431215
byte* pFinallyHandler = ehClause._handlerAddress;
12441216
exInfo._idxCurClause = curIdx;
1245-
#if NATIVEAOT
12461217
InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet);
1247-
#else // NATIVEAOT
1248-
fixed (EH.ExInfo* pExInfo = &exInfo)
1249-
{
1250-
InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet, pExInfo);
1251-
}
1252-
#endif // NATIVEAOT
12531218
exInfo._idxCurClause = MaxTryRegionIdx;
12541219
}
12551220
}
12561221

1257-
#if NATIVEAOT
12581222
#pragma warning disable IDE0060
12591223
[UnmanagedCallersOnly(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp")]
12601224
public static void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord)

src/coreclr/vm/corelib.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1357,7 +1357,6 @@ DEFINE_CLASS(EH, Runtime, EH)
13571357
DEFINE_METHOD(EH, RH_THROW_EX, RhThrowEx, SM_Obj_RefExInfo_RetVoid)
13581358
DEFINE_METHOD(EH, RH_THROWHW_EX, RhThrowHwEx, SM_UInt_RefExInfo_RetVoid)
13591359
DEFINE_METHOD(EH, RH_RETHROW, RhRethrow, SM_RefExInfo_RefExInfo_RetVoid)
1360-
DEFINE_METHOD(EH, UNWIND_AND_INTERCEPT, RhUnwindAndIntercept, SM_RefExInfo_UIntPtr_RetVoid)
13611360
DEFINE_CLASS(EXCEPTIONSERVICES_INTERNALCALLS, ExceptionServices, InternalCalls)
13621361
DEFINE_CLASS(STACKFRAMEITERATOR, Runtime, StackFrameIterator)
13631362
#endif // FEATURE_EH_FUNCLETS

0 commit comments

Comments
 (0)