Skip to content

Commit

Permalink
[NativeAOT] Experiment: Cache location of unwind sections (#82994)
Browse files Browse the repository at this point in the history
* WIP: Cache managed code unwind section lookup

* Clean up

* Attempt to fix EHABI and Darwin builds

* Rename `ip` to `pc`
  • Loading branch information
filipnavara authored Mar 7, 2023
1 parent 074adda commit 013ca67
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 97 deletions.
77 changes: 0 additions & 77 deletions src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include <libunwind.h>

#include "UnixContext.h"
#include "UnwindHelpers.h"

// WebAssembly has a slightly different version of LibUnwind that doesn't define unw_get_save_loc
#if defined(HOST_WASM)
Expand Down Expand Up @@ -372,52 +371,6 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso
#undef ASSIGN_REG_PTR
}

// Returns the unw_proc_info_t for a given IP.
bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo)
{
int st;

unw_context_t unwContext;
unw_cursor_t cursor;

st = unw_getcontext(&unwContext);
if (st < 0)
{
return false;
}

#ifdef HOST_AMD64
// We manually index into the unw_context_t's internals for now because there's
// no better way to modify it. This will go away in the future when we locate the
// LSDA and other information without initializing an unwind cursor.
unwContext.data[16] = ip;
#elif HOST_ARM
((uint32_t*)(unwContext.data))[15] = ip;
#elif HOST_ARM64
unwContext.data[32] = ip;
#elif HOST_WASM
ASSERT(false);
#elif HOST_X86
ASSERT(false);
#else
#error "GetUnwindProcInfo is not supported on this arch yet."
#endif

st = unw_init_local(&cursor, &unwContext);
if (st < 0)
{
return false;
}

st = unw_get_proc_info(&cursor, procInfo);
if (st < 0)
{
return false;
}

return true;
}

// Initialize unw_cursor_t and unw_context_t from REGDISPLAY
bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* cursor, unw_context_t* unwContext)
{
Expand Down Expand Up @@ -699,36 +652,6 @@ uint64_t GetPC(void* context)

#endif // HOST_AMD64

// Find LSDA and start address for a function at address controlPC
bool FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* endAddress, uintptr_t* lsda)
{
unw_proc_info_t procInfo;

if (!GetUnwindProcInfo((PCODE)controlPC, &procInfo))
{
return false;
}

assert((procInfo.start_ip <= controlPC) && (controlPC < procInfo.end_ip));

#if defined(HOST_ARM)
// libunwind fills by reference not by value for ARM
*lsda = *((uintptr_t *)procInfo.lsda);
#else
*lsda = procInfo.lsda;
#endif
*startAddress = procInfo.start_ip;
*endAddress = procInfo.end_ip;

return true;
}

// Virtually unwind stack to the caller of the context specified by the REGDISPLAY
bool VirtualUnwind(REGDISPLAY* pRegisterSet)
{
return UnwindHelpers::StepFrame(pRegisterSet);
}

#ifdef TARGET_ARM64

uint64_t& UNIX_CONTEXT::X0() { return (uint64_t&)MCREG_X0(ctx.uc_mcontext); }
Expand Down
5 changes: 0 additions & 5 deletions src/coreclr/nativeaot/Runtime/unix/UnixContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ void NativeContextToPalContext(const void* context, PAL_LIMITED_CONTEXT* palCont
// Redirect Unix native context to the PAL_LIMITED_CONTEXT and also set the first two argument registers
void RedirectNativeContext(void* context, const PAL_LIMITED_CONTEXT* palContext, uintptr_t arg0Reg, uintptr_t arg1Reg);

// Find LSDA and start address for a function at address controlPC
bool FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* endAddress, uintptr_t* lsda);
// Virtually unwind stack to the caller of the context specified by the REGDISPLAY
bool VirtualUnwind(REGDISPLAY* pRegisterSet);

#ifdef HOST_AMD64
// Get value of a register from the native context. The index is the processor specific
// register index stored in machine instructions.
Expand Down
65 changes: 56 additions & 9 deletions src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "gcinfodecoder.cpp"

#include "UnixContext.h"
#include "UnwindHelpers.h"

#define UBF_FUNC_KIND_MASK 0x03
#define UBF_FUNC_KIND_ROOT 0x00
Expand Down Expand Up @@ -46,12 +47,54 @@ UnixNativeCodeManager::UnixNativeCodeManager(TADDR moduleBase,
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
{
// Cache the location of unwind sections
libunwind::LocalAddressSpace::sThisAddressSpace.findUnwindSections(
(uintptr_t)pvManagedCodeStartRange, m_UnwindInfoSections);
}

UnixNativeCodeManager::~UnixNativeCodeManager()
{
}

// Virtually unwind stack to the caller of the context specified by the REGDISPLAY
bool UnixNativeCodeManager::VirtualUnwind(REGDISPLAY* pRegisterSet)
{
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
uintptr_t pc = pRegisterSet->GetIP();
if (pc >= (uintptr_t)m_pvManagedCodeStartRange &&
pc < (uintptr_t)m_pvManagedCodeStartRange + m_cbManagedCodeRange)
{
return UnwindHelpers::StepFrame(pRegisterSet, m_UnwindInfoSections);
}
#endif

return UnwindHelpers::StepFrame(pRegisterSet);
}

// Find LSDA and start address for a function at address controlPC
bool UnixNativeCodeManager::FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* endAddress, uintptr_t* lsda)
{
unw_proc_info_t procInfo;

if (!UnwindHelpers::GetUnwindProcInfo((PCODE)controlPC, m_UnwindInfoSections, &procInfo))
{
return false;
}

assert((procInfo.start_ip <= controlPC) && (controlPC < procInfo.end_ip));

#if defined(HOST_ARM)
// libunwind fills by reference not by value for ARM
*lsda = *((uintptr_t *)procInfo.lsda);
#else
*lsda = procInfo.lsda;
#endif
*startAddress = procInfo.start_ip;
*endAddress = procInfo.end_ip;

return true;
}

bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand Down Expand Up @@ -326,7 +369,7 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo,
*ppPreviousTransitionFrame = NULL;
}

if (!VirtualUnwind(pRegisterSet))
if (!VirtualUnwind(pRegisterSet))
{
return false;
}
Expand Down Expand Up @@ -486,16 +529,20 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(PTR_VOID pvAddress)
// is treated as an epilogue.
//

size_t startAddress;
size_t endAddress;
uintptr_t lsda;
if ((uintptr_t)pvAddress >= (uintptr_t)m_pvManagedCodeStartRange &&
(uintptr_t)pvAddress < (uintptr_t)m_pvManagedCodeStartRange + m_cbManagedCodeRange)
{
size_t startAddress;
size_t endAddress;
uintptr_t lsda;

bool result = FindProcInfo((uintptr_t)pvAddress, &startAddress, &endAddress, &lsda);
ASSERT(result);
bool result = FindProcInfo((uintptr_t)pvAddress, &startAddress, &endAddress, &lsda);
ASSERT(result);

if (branchTarget < startAddress || branchTarget >= endAddress)
{
return trailingEpilogueInstructions;
if (branchTarget < startAddress || branchTarget >= endAddress)
{
return trailingEpilogueInstructions;
}
}
}
else if ((pNextByte[0] == JMP_IND_OP) && (pNextByte[1] == 0x25))
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

#pragma once

// libunwind headers
#include <libunwind.h>
#include <external/llvm-libunwind/src/config.h>
#include <external/llvm-libunwind/src/AddressSpace.hpp>

class UnixNativeCodeManager : public ICodeManager
{
TADDR m_moduleBase;
Expand All @@ -13,6 +18,11 @@ class UnixNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

libunwind::UnwindInfoSections m_UnwindInfoSections;

bool VirtualUnwind(REGDISPLAY* pRegisterSet);
bool FindProcInfo(uintptr_t controlPC, uintptr_t* startAddress, uintptr_t* endAddress, uintptr_t* lsda);

public:
UnixNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
Expand Down
77 changes: 71 additions & 6 deletions src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,32 +834,97 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs
#endif

#elif defined(_LIBUNWIND_ARM_EHABI)
uc.setInfoBasedOnIPRegister(true);
// If there is ARM EHABI unwind info, look there next.
if (uwInfoSections.arm_section == 0 || !this->getInfoFromEHABISection(pc, uwInfoSections))
{
return false;
}

int stepRet = uc.step();
if ((stepRet != UNW_STEP_SUCCESS) && (stepRet != UNW_STEP_END))
{
return false;
}
#else
PORTABILITY_ASSERT("StepFrame");
#endif

return true;
}

bool UnwindHelpers::StepFrame(REGDISPLAY *regs, UnwindInfoSections &uwInfoSections)
{
uintptr_t pc = regs->GetIP();
return DoTheStep(pc, uwInfoSections, regs);
}

bool UnwindHelpers::StepFrame(REGDISPLAY *regs)
{
UnwindInfoSections uwInfoSections;
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
uintptr_t pc = regs->GetIP();
if (!_addressSpace.findUnwindSections(pc, uwInfoSections))
{
return false;
}
return DoTheStep(pc, uwInfoSections, regs);
}

bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSections, unw_proc_info_t *procInfo)
{
#if defined(TARGET_AMD64)
libunwind::UnwindCursor<LocalAddressSpace, Registers_x86_64> uc(_addressSpace);
#elif defined(TARGET_ARM)
libunwind::UnwindCursor<LocalAddressSpace, Registers_arm_rt> uc(_addressSpace);
#elif defined(TARGET_ARM64)
libunwind::UnwindCursor<LocalAddressSpace, Registers_arm64> uc(_addressSpace);
#elif defined(HOST_X86)
libunwind::UnwindCursor<LocalAddressSpace, Registers_x86> uc(_addressSpace);
#else
#error "Unwinding is not implemented for this architecture yet."
#endif

#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
uint32_t dwarfOffsetHint = 0;

#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND
// If there is a compact unwind encoding table, look there first.
if (uwInfoSections.compact_unwind_section != 0 && uc.getInfoFromCompactEncodingSection(pc, uwInfoSections)) {
uc.getInfo(procInfo);

#if defined(TARGET_ARM64)
if ((procInfo->format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) {
return true;
} else {
dwarfOffsetHint = procInfo->format & UNWIND_ARM64_DWARF_SECTION_OFFSET;
}
#elif defined(TARGET_AMD64)
if ((procInfo->format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) {
return true;
} else {
dwarfOffsetHint = procInfo->format & UNWIND_X86_64_DWARF_SECTION_OFFSET;
}
#else
PORTABILITY_ASSERT("GetUnwindProcInfo");
#endif
}
#endif

bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, dwarfOffsetHint);
if (!retVal)
{
return false;
}

#elif defined(_LIBUNWIND_ARM_EHABI)
// unwind section is located later for ARM
// pc will be taked from regs parameter
return DoTheStep(0, uwInfoSections, regs);
// If there is ARM EHABI unwind info, look there next.
if (uwInfoSections.arm_section == 0 || !this->getInfoFromEHABISection(pc, uwInfoSections))
{
return false;
}
#else
PORTABILITY_ASSERT("StepFrame");
PORTABILITY_ASSERT("GetUnwindProcInfo");
#endif

uc.getInfo(procInfo);
return true;
}
6 changes: 6 additions & 0 deletions src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

#include "common.h"

#include <libunwind.h>
#include <external/llvm-libunwind/src/config.h>
#include <external/llvm-libunwind/src/AddressSpace.hpp>

// This class is used to encapsulate the internals of our unwinding implementation
// and any custom versions of libunwind structures that we use for performance
// reasons.
class UnwindHelpers
{
public:
static bool StepFrame(REGDISPLAY *regs, libunwind::UnwindInfoSections &uwInfoSections);
static bool StepFrame(REGDISPLAY *regs);
static bool GetUnwindProcInfo(PCODE ip, libunwind::UnwindInfoSections &uwInfoSections, unw_proc_info_t *procInfo);
};

0 comments on commit 013ca67

Please sign in to comment.