diff options
author | Aditya Mandaleeka <adityam@microsoft.com> | 2017-11-16 03:27:54 +0300 |
---|---|---|
committer | Aditya Mandaleeka <adityam@microsoft.com> | 2017-12-08 02:45:58 +0300 |
commit | 1bd9c3c2472e656d7e2c90b2c8273e99469faf05 (patch) | |
tree | 77a83c7a0914a08a2064975e221620cb5334559e /src/Native | |
parent | 9f70bcc80bad914b45040c95cd7ebb8838a336e7 (diff) |
Find unwind info ourselves rather than initializing unwind cursors each time.
Diffstat (limited to 'src/Native')
-rw-r--r-- | src/Native/Runtime/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/Native/Runtime/regdisplay.h | 200 | ||||
-rw-r--r-- | src/Native/Runtime/unix/UnixContext.cpp | 36 | ||||
-rw-r--r-- | src/Native/Runtime/unix/UnwindHelpers.cpp | 187 | ||||
-rw-r--r-- | src/Native/Runtime/unix/UnwindHelpers.h | 14 | ||||
-rw-r--r-- | src/Native/libunwind/src/UnwindCursor.hpp | 9 |
6 files changed, 416 insertions, 34 deletions
diff --git a/src/Native/Runtime/CMakeLists.txt b/src/Native/Runtime/CMakeLists.txt index 12fa1532d..1cd4cd949 100644 --- a/src/Native/Runtime/CMakeLists.txt +++ b/src/Native/Runtime/CMakeLists.txt @@ -106,6 +106,9 @@ else() include_directories($ENV{EMSCRIPTEN/system/lib/libcxxabi/include}) endif() + include_directories(../libunwind/include) + include_directories(../libunwind) + # Disable building _Unwind_XXX style APIs of libunwind, since we don't use them. add_definitions(-D_LIBUNWIND_DISABLE_ZERO_COST_APIS=1) @@ -119,6 +122,7 @@ else() list(APPEND FULL_RUNTIME_SOURCES unix/HardwareExceptions.cpp unix/UnixContext.cpp + unix/UnwindHelpers.cpp unix/UnixNativeCodeManager.cpp ../libunwind/src/Unwind-EHABI.cpp ../libunwind/src/libunwind.cpp diff --git a/src/Native/Runtime/regdisplay.h b/src/Native/Runtime/regdisplay.h index aa234717e..eb96f400f 100644 --- a/src/Native/Runtime/regdisplay.h +++ b/src/Native/Runtime/regdisplay.h @@ -4,6 +4,39 @@ #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) +// These definitions are from our copy of libunwind. Including them directly here +// so that the REGDISPLAY code doesn't need to reference libunwind. +namespace LibunwindConstants +{ + // copied from Registers.hpp + // Architecture independent register numbers + enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer + }; + + // copied from libunwind.h + // 64-bit x86_64 registers + enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15 + }; +} + struct REGDISPLAY { PTR_UIntNative pRax; @@ -45,6 +78,173 @@ struct REGDISPLAY inline void SetIP(PCODE IP) { this->IP = IP; } inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } inline void SetSP(UIntNative SP) { this->SP = SP; } + + // Everything below was added to enable libunwind to work with REGDISPLAYs. + +#if defined(_TARGET_AMD64_) + + inline uint64_t getRegister(int regNum) const + { + switch (regNum) + { + case LibunwindConstants::UNW_REG_IP: + return IP; + case LibunwindConstants::UNW_REG_SP: + return SP; + case LibunwindConstants::UNW_X86_64_RAX: + return *pRax; + case LibunwindConstants::UNW_X86_64_RDX: + return *pRdx; + case LibunwindConstants::UNW_X86_64_RCX: + return *pRcx; + case LibunwindConstants::UNW_X86_64_RBX: + return *pRbx; + case LibunwindConstants::UNW_X86_64_RSI: + return *pRsi; + case LibunwindConstants::UNW_X86_64_RDI: + return *pRdi; + case LibunwindConstants::UNW_X86_64_RBP: + return *pRbp; + case LibunwindConstants::UNW_X86_64_RSP: + return SP; + case LibunwindConstants::UNW_X86_64_R8: + return *pR8; + case LibunwindConstants::UNW_X86_64_R9: + return *pR9; + case LibunwindConstants::UNW_X86_64_R10: + return *pR10; + case LibunwindConstants::UNW_X86_64_R11: + return *pR11; + case LibunwindConstants::UNW_X86_64_R12: + return *pR12; + case LibunwindConstants::UNW_X86_64_R13: + return *pR13; + case LibunwindConstants::UNW_X86_64_R14: + return *pR14; + case LibunwindConstants::UNW_X86_64_R15: + return *pR15; + } + + // Unsupported register requested + abort(); + } + + inline void setRegister(int regNum, uint64_t value, uint64_t location) + { + switch (regNum) + { + case LibunwindConstants::UNW_REG_IP: + IP = value; + pIP = (PTR_PCODE)location; + return; + case LibunwindConstants::UNW_REG_SP: + SP = value; + return; + case LibunwindConstants::UNW_X86_64_RAX: + pRax = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RDX: + pRdx = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RCX: + pRcx = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RBX: + pRbx = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RSI: + pRsi = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RDI: + pRdi = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RBP: + pRbp = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_RSP: + SP = value; + return; + case LibunwindConstants::UNW_X86_64_R8: + pR8 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R9: + pR9 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R10: + pR10 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R11: + pR11 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R12: + pR12 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R13: + pR13 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R14: + pR14 = (PTR_UIntNative)location; + return; + case LibunwindConstants::UNW_X86_64_R15: + pR15 = (PTR_UIntNative)location; + return; + } + + // Unsupported x86_64 register + abort(); + } + + // N/A for x86_64 + inline bool validFloatRegister(int) { return false; } + inline bool validVectorRegister(int) { return false; } + + inline static int lastDwarfRegNum() { return 16; } + + inline bool validRegister(int regNum) const + { + if (regNum == LibunwindConstants::UNW_REG_IP) + return true; + if (regNum == LibunwindConstants::UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; + } + + // N/A for x86_64 + inline double getFloatRegister(int) const { abort(); } + inline void setFloatRegister(int, double) { abort(); } + inline double getVectorRegister(int) const { abort(); } + inline void setVectorRegister(int, ...) { abort(); } + + uint64_t getSP() const { return SP; } + void setSP(uint64_t value, uint64_t location) { SP = value; } + + uint64_t getIP() const { return IP; } + + void setIP(uint64_t value, uint64_t location) + { + IP = value; + pIP = (PTR_PCODE)location; + } + + uint64_t getRBP() const { return *pRbp; } + void setRBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } + uint64_t getRBX() const { return *pRbx; } + void setRBX(uint64_t value, uint64_t location) { pRbx = (PTR_UIntNative)location; } + uint64_t getR12() const { return *pR12; } + void setR12(uint64_t value, uint64_t location) { pR12 = (PTR_UIntNative)location; } + uint64_t getR13() const { return *pR13; } + void setR13(uint64_t value, uint64_t location) { pR13 = (PTR_UIntNative)location; } + uint64_t getR14() const { return *pR14; } + void setR14(uint64_t value, uint64_t location) { pR14 = (PTR_UIntNative)location; } + uint64_t getR15() const { return *pR15; } + void setR15(uint64_t value, uint64_t location) { pR15 = (PTR_UIntNative)location; } + +#endif // _TARGET_AMD64_ + }; #elif defined(_TARGET_ARM_) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 549271e30..cf88a5994 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -18,6 +18,7 @@ #endif // HAVE_UCONTEXT_T #include "UnixContext.h" +#include "UnwindHelpers.h" // WebAssembly has a slightly different version of LibUnwind that doesn't define unw_get_save_loc #if defined(_WASM_) @@ -599,38 +600,5 @@ bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* ls // Virtually unwind stack to the caller of the context specified by the REGDISPLAY bool VirtualUnwind(REGDISPLAY* pRegisterSet) { - unw_context_t unwContext; - unw_cursor_t cursor; - - if (!InitializeUnwindContextAndCursor(pRegisterSet, &cursor, &unwContext)) - { - return false; - } - - // FreeBSD, NetBSD and OSX appear to do two different things when unwinding - // 1: If it reaches where it cannot unwind anymore, say a - // managed frame. It wil return 0, but also update the $pc - // 2: If it unwinds all the way to _start it will return - // 0 from the step, but $pc will stay the same. - // The behaviour of libunwind from nongnu.org is to null the PC - // So we bank the original PC here, so we can compare it after - // the step - uintptr_t curPc = pRegisterSet->GetIP(); - - int st = unw_step(&cursor); - if (st < 0) - { - return false; - } - - // Update the REGDISPLAY to reflect the unwind - UnwindCursorToRegDisplay(&cursor, &unwContext, pRegisterSet); - - if (st == 0 && pRegisterSet->GetIP() == curPc) - { - // TODO: is this correct for CoreRT? Should we return false instead? - pRegisterSet->SetIP(0); - } - - return true; + return UnwindHelpers::StepFrame(pRegisterSet); } diff --git a/src/Native/Runtime/unix/UnwindHelpers.cpp b/src/Native/Runtime/unix/UnwindHelpers.cpp new file mode 100644 index 000000000..34008b18d --- /dev/null +++ b/src/Native/Runtime/unix/UnwindHelpers.cpp @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" +#include "daccess.h" + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __APPLE__ +#include <mach-o/getsect.h> +#endif + +#include <regdisplay.h> +#include "UnwindHelpers.h" + +// libunwind headers +#include <libunwind.h> +#include <src/config.h> +#include <src/Registers.hpp> +#include <src/AddressSpace.hpp> +#include <src/UnwindCursor.hpp> + +using libunwind::Registers_x86_64; +using libunwind::LocalAddressSpace; +using libunwind::EHHeaderParser; +using libunwind::DwarfInstructions; +using libunwind::UnwindInfoSections; + +LocalAddressSpace _addressSpace; + +#ifdef __APPLE__ + +struct dyld_unwind_sections +{ + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; +}; + +#else // __APPLE__ + +// Passed to the callback function called by dl_iterate_phdr +struct dl_iterate_cb_data +{ + UnwindInfoSections *sects; + uintptr_t targetAddr; +}; + +// Callback called by dl_iterate_phdr. Locates unwind info sections for the target +// address. +static int LocateSectionsCallback(struct dl_phdr_info *info, size_t size, void *data) +{ + // info is a pointer to a structure containing information about the shared object + dl_iterate_cb_data* cbdata = static_cast<dl_iterate_cb_data*>(data); + uintptr_t addrOfInterest = (uintptr_t)cbdata->targetAddr; + + size_t object_length; + bool found_obj = false; + bool found_hdr = false; + + // If the base address of the SO is past the address we care about, move on. + if (info->dlpi_addr > addrOfInterest) + { + return 0; + } + + // Iterate through the program headers for this SO + for (ElfW(Half) i = 0; i < info->dlpi_phnum; i++) + { + const ElfW(Phdr) *phdr = &info->dlpi_phdr[i]; + + if (phdr->p_type == PT_LOAD) + { + // This is a loadable entry. Loader loads all segments of this type. + + uintptr_t begin = info->dlpi_addr + phdr->p_vaddr; + uintptr_t end = begin + phdr->p_memsz; + + if (addrOfInterest >= begin && addrOfInterest < end) + { + cbdata->sects->dso_base = begin; + object_length = phdr->p_memsz; + found_obj = true; + } + } + else if (phdr->p_type == PT_GNU_EH_FRAME) + { + // This element specifies the location and size of the exception handling + // information as defined by the .eh_frame_hdr section. + + EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo; + + uintptr_t eh_frame_hdr_start = info->dlpi_addr + phdr->p_vaddr; + cbdata->sects->dwarf_index_section = eh_frame_hdr_start; + cbdata->sects->dwarf_index_section_length = phdr->p_memsz; + + EHHeaderParser<LocalAddressSpace> ehp; + ehp.decodeEHHdr(_addressSpace, eh_frame_hdr_start, phdr->p_memsz, hdrInfo); + + cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; + found_hdr = true; + } + } + + return 0; +} + +#endif // __APPLE__ + +bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs) +{ +#if defined(_TARGET_AMD64_) + libunwind::UnwindCursor<LocalAddressSpace, Registers_x86_64> uc(_addressSpace); +#elif defined(_TARGET_ARM_) + libunwind::UnwindCursor<LocalAddressSpace, Registers_arm> uc(_addressSpace); +#else + #error "Unwinding is not implemented for this architecture yet." +#endif + + bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, 0 /* fdeSectionOffsetHint */); + if (!retVal) + { + return false; + } + + unw_proc_info_t procInfo; + uc.getInfo(&procInfo); + + DwarfInstructions<LocalAddressSpace, REGDISPLAY> dwarfInst; + + int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, procInfo.unwind_info, *regs); + if (stepRet != UNW_STEP_SUCCESS) + { + return false; + } + + regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR)); + + return true; +} + +UnwindInfoSections LocateUnwindSections(uintptr_t pc) +{ + UnwindInfoSections uwInfoSections; + +#ifdef __APPLE__ + // On macOS, we can use a dyld function from libSystem in order + // to find the unwind sections. + + libunwind::dyld_unwind_sections dyldInfo; + + if (libunwind::_dyld_find_unwind_sections((void *)pc, &dyldInfo)) + { + uwInfoSections.dso_base = (uintptr_t)dyldInfo.mh; + + uwInfoSections.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; + uwInfoSections.dwarf_section_length = dyldInfo.dwarf_section_length; + + uwInfoSections.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section; + uwInfoSections.compact_unwind_section_length = dyldInfo.compact_unwind_section_length; + } +#else // __APPLE__ + + dl_iterate_cb_data cb_data = {&uwInfoSections, pc }; + dl_iterate_phdr(LocateSectionsCallback, &cb_data); + +#endif + + return uwInfoSections; +} + +bool UnwindHelpers::StepFrame(REGDISPLAY *regs) +{ + uintptr_t pc = regs->GetIP(); + + UnwindInfoSections uwInfoSections = LocateUnwindSections(pc); + if (uwInfoSections.dwarf_section == NULL) + { + return false; + } + + return DoTheStep(pc, uwInfoSections, regs); +} diff --git a/src/Native/Runtime/unix/UnwindHelpers.h b/src/Native/Runtime/unix/UnwindHelpers.h new file mode 100644 index 000000000..cf9e91ea4 --- /dev/null +++ b/src/Native/Runtime/unix/UnwindHelpers.h @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "common.h" + +// 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); +}; diff --git a/src/Native/libunwind/src/UnwindCursor.hpp b/src/Native/libunwind/src/UnwindCursor.hpp index 7a1149eaf..f68eee18e 100644 --- a/src/Native/libunwind/src/UnwindCursor.hpp +++ b/src/Native/libunwind/src/UnwindCursor.hpp @@ -427,6 +427,7 @@ template <typename A, typename R> class UnwindCursor : public AbstractUnwindCursor{ typedef typename A::pint_t pint_t; public: + UnwindCursor(A &as); UnwindCursor(unw_context_t *context, A &as); UnwindCursor(A &as, void *threadArg); virtual ~UnwindCursor() {} @@ -469,6 +470,7 @@ private: #endif #if _LIBUNWIND_SUPPORT_DWARF_UNWIND +public: bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint=0); int stepWithDwarfFDE() { @@ -608,6 +610,13 @@ private: bool _isSignalFrame; }; +template <typename A, typename R> +UnwindCursor<A, R>::UnwindCursor(A &as) + : _addressSpace(as) + , _unwindInfoMissing(false) + , _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); +} template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) |