Skip to content

Commit

Permalink
Remove prolog length, detect prologs from instruction stream
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Aug 2, 2023
1 parent 803f511 commit 53117e3
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 58 deletions.
120 changes: 87 additions & 33 deletions src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#define UBF_FUNC_HAS_EHINFO 0x04
#define UBF_FUNC_REVERSE_PINVOKE 0x08
#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10
#define UBF_FUNC_HAS_PROLOG_LENGTH 0x20

struct UnixNativeMethodInfo
{
Expand All @@ -41,8 +40,6 @@ struct UnixNativeMethodInfo
unw_word_t unwind_info;
uint32_t format;

uint8_t prolog_length;

bool executionAborted;
};

Expand Down Expand Up @@ -71,8 +68,7 @@ bool UnixNativeCodeManager::VirtualUnwind(MethodInfo* pMethodInfo, REGDISPLAY* p
UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;

return UnwindHelpers::StepFrame(
pRegisterSet, pNativeMethodInfo->start_ip, pNativeMethodInfo->format, pNativeMethodInfo->unwind_info,
pNativeMethodInfo->prolog_length);
pRegisterSet, pNativeMethodInfo->start_ip, pNativeMethodInfo->format, pNativeMethodInfo->unwind_info);
}

bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
Expand Down Expand Up @@ -128,15 +124,6 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
pMethodInfo->pMethodStartAddress = dac_cast<PTR_VOID>(procInfo.start_ip);
}

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
{
pMethodInfo->prolog_length = *p++;
}
else
{
pMethodInfo->prolog_length = 0;
}

pMethodInfo->executionAborted = false;

return true;
Expand Down Expand Up @@ -181,9 +168,6 @@ uint32_t UnixNativeCodeManager::GetCodeOffset(MethodInfo* pMethodInfo, PTR_VOID

uint8_t unwindBlockFlags = *p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
p += sizeof(int32_t);

Expand Down Expand Up @@ -280,9 +264,6 @@ uintptr_t UnixNativeCodeManager::GetConservativeUpperBoundForOutgoingArgs(Method
// Reverse PInvoke transition should be on the main function body only
assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA);

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
p += sizeof(int32_t);

Expand Down Expand Up @@ -345,9 +326,6 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo,
// Reverse PInvoke transition should be on the main function body only
assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA);

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
p += sizeof(int32_t);

Expand Down Expand Up @@ -400,7 +378,78 @@ bool UnixNativeCodeManager::IsUnwindable(PTR_VOID pvAddress)
#endif

// VirtualUnwind can't unwind epilogues.
return TrailingEpilogueInstructionsCount(pMethodInfo, pvAddress) == 0;
return TrailingEpilogueInstructionsCount(pMethodInfo, pvAddress) == 0 && IsInProlog(pMethodInfo, pvAddress) != 1;
}

// checks for known prolog instructions generated by ILC and returns
// 1 - in prolog
// 0 - not in prolog,
// -1 - unknown.
int UnixNativeCodeManager::IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddress)
{
#if defined(TARGET_ARM64)

// post/pre


// stp with signed offset
// x010 1001 00xx xxxx xxxx xxxx xxxx xxxx
#define STP_BITS1 0x29000000
#define STP_MASK1 0x7FC00000

// stp with pre/post/no offset
// x010 100x x0xx xxxx xxxx xxxx xxxx xxxx
#define STP_BITS2 0x28000000
#define STP_MASK2 0x7E400000

// add fp, sp, x
// mov fp, sp
// 1001 0001 0xxx xxxx xxxx xx11 1111 1101
#define ADD_FP_SP_BITS 0x910003FD
#define ADD_FP_SP_MASK 0xFF8003FF

#define STP_RT2_RT_MASK 0x7C1F
#define STP_RT2_RT_FP_LR 0x781D
#define STP_RN_MASK 0x3E0
#define STP_RN_SP 0x3E0
#define STP_RN_FP 0x3A0


UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;
ASSERT(pNativeMethodInfo != NULL);

uint32_t* start = (uint32_t*)pNativeMethodInfo->pMethodStartAddress;
bool savedFpLr = false;
bool establishedFp = false;

for (uint32_t* pInstr = (uint32_t*)start; pInstr < pvAddress && !(savedFpLr && establishedFp); pInstr++)
{
uint32_t instr = *pInstr;

if (((instr & STP_MASK1) == STP_BITS1 || (instr & STP_MASK2) == STP_BITS2) &&
((instr & STP_RN_MASK) == STP_RN_SP || (instr & STP_RN_MASK) == STP_RN_FP))
{
// SP/FP-relative store of pair of registers
savedFpLr |= (instr & STP_RT2_RT_MASK) == STP_RT2_RT_FP_LR;
}
else if ((instr & ADD_FP_SP_MASK) == ADD_FP_SP_BITS)
{
establishedFp = true;
}
else
{
// TODO: Detect saving non-paired register, stack pointer adjustments
return -1;
}
}

return savedFpLr && establishedFp ? 0 : 1;

#else

return -1;

#endif
}

// when stopped in an epilogue, returns the count of remaining stack-consuming instructions
Expand Down Expand Up @@ -714,9 +763,6 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0)
return false;

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
p += sizeof(int32_t);

Expand Down Expand Up @@ -744,6 +790,20 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn
return true;
}

#if defined(TARGET_APPLE) && defined(TARGET_ARM64)
// If we are inside a prolog without a saved frame then we cannot safely unwind.
//
// Some known frame layouts use compact unwind encoding which cannot handle unwinding
// inside prolog or epilog, so don't even try that. These known sequences must be
// recognized by IsInProlog. Any other instruction sequence, known or unknown, falls
// through to the platform unwinder which should have DWARF information about the
// frame.
if (IsInProlog(pMethodInfo, (PTR_VOID)pRegisterSet->IP) == 1)
{
return false;
}
#endif

ASSERT(IsUnwindable((PTR_VOID)pRegisterSet->IP));

// Unwind the current method context to the caller's context to get its stack pointer
Expand Down Expand Up @@ -836,9 +896,6 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet

uint8_t unwindBlockFlags = *p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0)
p += sizeof(int32_t);

Expand Down Expand Up @@ -952,9 +1009,6 @@ PTR_VOID UnixNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT)
p += sizeof(uint32_t);

if ((unwindBlockFlags & UBF_FUNC_HAS_PROLOG_LENGTH) != 0)
p++;

if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0)
return NULL;

Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class UnixNativeCodeManager : public ICodeManager

bool IsUnwindable(PTR_VOID pvAddress);

int IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddress);

int TrailingEpilogueInstructionsCount(MethodInfo * pMethodInfo, PTR_VOID pvAddress);

bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo,
Expand Down
9 changes: 1 addition & 8 deletions src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,19 +761,12 @@ void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value)

#endif // TARGET_ARM64

bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info, uint8_t prolog_length)
bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info)
{
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND

#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND

// Compact unwinding doesn't support asynchronous unwinding in prolog. We can interpret a code
// subset in some cases if it becomes necessary.
if (regs->GetIP() - start_ip < prolog_length)
{
return false;
}

#if defined(TARGET_ARM64)
if ((format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) {
CompactUnwinder_arm64<LocalAddressSpace, Registers_REGDISPLAY> compactInst;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
class UnwindHelpers
{
public:
static bool StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info, uint8_t prolog_length);
static bool StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info);
static bool GetUnwindProcInfo(PCODE ip, libunwind::UnwindInfoSections &uwInfoSections, unw_proc_info_t *procInfo);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ public enum FrameInfoFlags
HasEHInfo = 0x04,
ReversePInvoke = 0x08,
HasAssociatedData = 0x10,

HasPrologLength = 0x20,
}

public struct FrameInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,6 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node)
int len = frameInfo.BlobData.Length;
byte[] blob = frameInfo.BlobData;
bool emitDwarf = true;
byte dwarfPrologLength = 0;

ObjectNodeSection lsdaSection = LsdaSection;
if (ShouldShareSymbol(node))
Expand All @@ -662,19 +661,11 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node)
{
_offsetToCfiCompactEncoding[start] = compactEncoding;
emitDwarf = false;

// Calculate the length of the prolog covered by DWARF codes
for (int j = 0; j < len; j += CfiCodeSize)
{
// The first byte of CFI_CODE is code offset.
dwarfPrologLength = Math.Max(blob[j], dwarfPrologLength);
}
}

FrameInfoFlags flags = frameInfo.Flags;
flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0;
flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0;
flags |= !emitDwarf ? FrameInfoFlags.HasPrologLength : 0;

EmitIntValue((byte)flags, 1);

Expand All @@ -686,11 +677,6 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node)
EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4);
}

if (!emitDwarf)
{
EmitIntValue(dwarfPrologLength, 1);
}

if (associatedDataNode != null)
{
EmitSymbolReference(associatedDataNode, 0, RelocType.IMAGE_REL_BASED_RELPTR32);
Expand Down

0 comments on commit 53117e3

Please sign in to comment.