Skip to content

Commit

Permalink
Fix arm64 fragment unwinding (#92678)
Browse files Browse the repository at this point in the history
A bug in the Windows arm64 unwinder that existed a long time ago has
caused problems with unwinding in functions split in multiple fragments
in case the location in the function was in a secondary fragment. At
that time, it was not discovered that it was a bug in the unwinder and
it got "fixed" in the runtime by always using the first fragment unwind
info. However, now it turned out that was actually incorrect in some
cases. Checking the current state of the Windows unwinder revealed that
a bug was fixed there that was causing the problem we were seeing.
Effectively ignoring all the shadow prolog unwind info in the secondary
fragments.
This change reverts the old fix after the unwinder was updated.
  • Loading branch information
janvorli authored Sep 27, 2023
1 parent cf978fd commit 273db66
Showing 1 changed file with 21 additions and 42 deletions.
63 changes: 21 additions & 42 deletions src/coreclr/vm/codeman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,35 +920,6 @@ BOOL IsFunctionFragment(TADDR baseAddress, PTR_RUNTIME_FUNCTION pFunctionEntry)
#endif
}

// When we have fragmented unwind we usually want to refer to the
// unwind record that includes the prolog. We can find it by searching
// back in the sequence of unwind records.
PTR_RUNTIME_FUNCTION FindRootEntry(PTR_RUNTIME_FUNCTION pFunctionEntry, TADDR baseAddress)
{
LIMITED_METHOD_DAC_CONTRACT;

PTR_RUNTIME_FUNCTION pRootEntry = pFunctionEntry;

if (pRootEntry != NULL)
{
// Walk backwards in the RUNTIME_FUNCTION array until we find a non-fragment.
// We're guaranteed to find one, because we require that a fragment live in a function or funclet
// that has a prolog, which will have non-fragment .xdata.
while (true)
{
if (!IsFunctionFragment(baseAddress, pRootEntry))
{
// This is not a fragment; we're done
break;
}

--pRootEntry;
}
}

return pRootEntry;
}

#endif // EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS


Expand Down Expand Up @@ -1134,12 +1105,30 @@ TADDR IJitManager::GetFuncletStartAddress(EECodeInfo * pCodeInfo)
#endif

TADDR baseAddress = pCodeInfo->GetModuleBase();
TADDR funcletStartAddress = baseAddress + RUNTIME_FUNCTION__BeginAddress(pFunctionEntry);

#if defined(EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS)
pFunctionEntry = FindRootEntry(pFunctionEntry, baseAddress);
#endif // EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS
// Is the RUNTIME_FUNCTION a fragment? If so, we need to walk backwards until we find the first
// non-fragment RUNTIME_FUNCTION, and use that one. This happens when we have very large functions
// and multiple RUNTIME_FUNCTION entries per function or funclet. However, all but the first will
// have the "F" bit set in the unwind data, indicating a fragment (with phantom prolog unwind codes).

TADDR funcletStartAddress = baseAddress + RUNTIME_FUNCTION__BeginAddress(pFunctionEntry);
for (;;)
{
if (!IsFunctionFragment(baseAddress, pFunctionEntry))
{
// This is not a fragment; we're done
break;
}

// We found a fragment. Walk backwards in the RUNTIME_FUNCTION array until we find a non-fragment.
// We're guaranteed to find one, because we require that a fragment live in a function or funclet
// that has a prolog, which will have non-fragment .xdata.
--pFunctionEntry;

funcletStartAddress = baseAddress + RUNTIME_FUNCTION__BeginAddress(pFunctionEntry);
}
#endif // EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS

return funcletStartAddress;
}
Expand Down Expand Up @@ -4133,8 +4122,6 @@ void EEJitManager::NibbleMapSetUnlocked(HeapList * pHp, TADDR pCode, BOOL bSet)
#endif // !DACCESS_COMPILE

#if defined(FEATURE_EH_FUNCLETS)
// Note: This returns the root unwind record (the one that describes the prolog)
// in cases where there is fragmented unwind.
PTR_RUNTIME_FUNCTION EEJitManager::LazyGetFunctionEntry(EECodeInfo * pCodeInfo)
{
CONTRACTL {
Expand Down Expand Up @@ -4163,14 +4150,6 @@ PTR_RUNTIME_FUNCTION EEJitManager::LazyGetFunctionEntry(EECodeInfo * pCodeInfo)

if (RUNTIME_FUNCTION__BeginAddress(pFunctionEntry) <= address && address < RUNTIME_FUNCTION__EndAddress(pFunctionEntry, baseAddress))
{

#if defined(EXCEPTION_DATA_SUPPORTS_FUNCTION_FRAGMENTS) && (defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64))
// If we might have fragmented unwind, and we're on ARM64/LoongArch64,
// make sure to returning the root record,
// as the trailing records don't have prolog unwind codes.
pFunctionEntry = FindRootEntry(pFunctionEntry, baseAddress);
#endif

return pFunctionEntry;
}
}
Expand Down

0 comments on commit 273db66

Please sign in to comment.