Skip to content

Commit

Permalink
Fix the new EH with hot-cold split code (#99015)
Browse files Browse the repository at this point in the history
The new EH was not taking into account the fact that with hot-cold split
methods, the offset used for looking up EH funclets need to be computed
as if the hot and cold regions were consecutive in memory. That have
caused failures in a number of tests when the tests themselves were
compiled with R2R and hot-cold split enabled.

Close #98915, #98916, #98917, #98918

Co-authored-by: Jan Vorlicek <janvorli@microsoft.com>
  • Loading branch information
github-actions[bot] and janvorli committed Feb 27, 2024
1 parent 64c3513 commit bbba827
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal static partial class InternalCalls

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EHEnumInitFromStackFrameIterator")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum);
internal static unsafe partial bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, out EH.MethodRegionInfo pMethodRegionInfo, void* pEHEnum);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "EHEnumNext")]
[return: MarshalAs(UnmanagedType.Bool)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ private struct EHEnum
private IntPtr _dummy; // For alignment
}

internal struct MethodRegionInfo
{
internal byte* _hotStartAddress;
internal nuint _hotSize;
internal byte* _coldStartAddress;
internal nuint _coldSize;
}

#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.
Expand Down Expand Up @@ -932,6 +940,20 @@ private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP)
"Handling frame must have a valid stack frame pointer");
}

// Caclulate the code offset from the start of the method as if the hot and cold regions were
// stored sequentially in memory.
private static uint CalculateCodeOffset(byte* pbControlPC, in MethodRegionInfo methodRegionInfo)
{
uint codeOffset = (uint)(pbControlPC - methodRegionInfo._hotStartAddress);
// If the PC is in the cold region, adjust the offset to be relative to the start of the method.
if ((methodRegionInfo._coldSize != 0) && (codeOffset >= methodRegionInfo._hotSize))
{
codeOffset = (uint)(methodRegionInfo._hotSize + (nuint)(pbControlPC - methodRegionInfo._coldStartAddress));
}

return codeOffset;
}

private static void UpdateStackTrace(object exceptionObj, UIntPtr curFramePtr, IntPtr ip, UIntPtr sp,
ref bool isFirstRethrowFrame, ref UIntPtr prevFramePtr, ref bool isFirstFrame, ref ExInfo exInfo)
{
Expand All @@ -958,13 +980,13 @@ private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP)
tryRegionIdx = MaxTryRegionIdx;

EHEnum ehEnum;
byte* pbMethodStartAddress;
if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, &pbMethodStartAddress, &ehEnum))
MethodRegionInfo methodRegionInfo;
if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, out methodRegionInfo, &ehEnum))
return false;

byte* pbControlPC = frameIter.ControlPC;

uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress);
uint codeOffset = CalculateCodeOffset(pbControlPC, in methodRegionInfo);

uint lastTryStart = 0, lastTryEnd = 0;

Expand Down Expand Up @@ -1111,13 +1133,14 @@ private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart)
private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxLimit)
{
EHEnum ehEnum;
byte* pbMethodStartAddress;
if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref exInfo._frameIter, &pbMethodStartAddress, &ehEnum))
MethodRegionInfo methodRegionInfo;

if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref exInfo._frameIter, out methodRegionInfo, &ehEnum))
return;

byte* pbControlPC = exInfo._frameIter.ControlPC;

uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress);
uint codeOffset = CalculateCodeOffset(pbControlPC, in methodRegionInfo);

uint lastTryStart = 0, lastTryEnd = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ internal static int RhEndNoGCRegion()

[RuntimeImport(Redhawk.BaseName, "RhpEHEnumInitFromStackFrameIterator")]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum);
internal static extern unsafe bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, out EH.MethodRegionInfo pMethodRegionInfo, void* pEHEnum);

[RuntimeImport(Redhawk.BaseName, "RhpEHEnumNext")]
[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/nativeaot/Runtime/EHHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,25 @@
#include "MethodTable.inl"
#include "CommonMacros.inl"

struct MethodRegionInfo
{
void* hotStartAddress;
size_t hotSize;
void* coldStartAddress;
size_t coldSize;
};

COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpEHEnumInitFromStackFrameIterator, (
StackFrameIterator* pFrameIter, void ** pMethodStartAddressOut, EHEnum* pEHEnum))
StackFrameIterator* pFrameIter, MethodRegionInfo* pMethodRegionInfoOut, EHEnum* pEHEnum))
{
ICodeManager * pCodeManager = pFrameIter->GetCodeManager();
pEHEnum->m_pCodeManager = pCodeManager;

FC_RETURN_BOOL(pCodeManager->EHEnumInit(pFrameIter->GetMethodInfo(), pMethodStartAddressOut, &pEHEnum->m_state));
pMethodRegionInfoOut->hotSize = 0; // unknown
pMethodRegionInfoOut->coldStartAddress = nullptr;
pMethodRegionInfoOut->coldSize = 0;

FC_RETURN_BOOL(pCodeManager->EHEnumInit(pFrameIter->GetMethodInfo(), &pMethodRegionInfoOut->hotStartAddress, &pEHEnum->m_state));
}

COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpEHEnumNext, (EHEnum* pEHEnum, EHClause* pEHClause))
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7970,7 +7970,7 @@ struct ExtendedEHClauseEnumerator : EH_CLAUSE_ENUMERATOR
unsigned EHCount;
};

extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum)
extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, IJitManager::MethodRegionInfo* pMethodRegionInfo, EH_CLAUSE_ENUMERATOR * pEHEnum)
{
QCALL_CONTRACT;

Expand All @@ -7984,7 +7984,7 @@ extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *p

IJitManager* pJitMan = pFrameIter->m_crawl.GetJitManager();
const METHODTOKEN& MethToken = pFrameIter->m_crawl.GetMethodToken();
*pMethodStartAddress = (BYTE*)pJitMan->JitTokenToStartAddress(MethToken);
pJitMan->JitTokenToMethodRegionInfo(MethToken, pMethodRegionInfo);
pExtendedEHEnum->EHCount = pJitMan->InitializeEHEnumeration(MethToken, pEHEnum);

END_QCALL;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/exceptionhandlingqcalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extern "C" void QCALLTYPE CallFinallyFunclet(BYTE* pHandlerIP, REGDISPLAY* pvReg
extern "C" BOOL QCALLTYPE CallFilterFunclet(QCall::ObjectHandleOnStack exceptionObj, BYTE* pFilterP, REGDISPLAY* pvRegDisplay);
extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay);
extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack exceptionObj, SIZE_T ip, SIZE_T sp, int flags, ExInfo *pExInfo);
extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, BYTE** pMethodStartAddress, EH_CLAUSE_ENUMERATOR * pEHEnum);
extern "C" BOOL QCALLTYPE EHEnumInitFromStackFrameIterator(StackFrameIterator *pFrameIter, IJitManager::MethodRegionInfo *pMethodRegionInfo, 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, bool* pIsExceptionIntercepted);
extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, unsigned int* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* pIsExceptionIntercepted);
Expand Down

0 comments on commit bbba827

Please sign in to comment.