From b059615c47caf5e06a694fa5e846504a808919a5 Mon Sep 17 00:00:00 2001 From: Ivan Arishchenko <36693675+iarischenko@users.noreply.github.com> Date: Fri, 24 Aug 2018 06:51:28 +0300 Subject: [PATCH] Unwinding support for arm (#6251) * Registers_arm_rt :wrapper class around RT's VRS RT uses REGDISPlAY structure for virtual registers set (VRS) representation. The libunwind uses Registers_* classes to hold VRS. The new class connects REGDISPlAY with libunwind Registers_* API. Also the new class reuses validRegister, validFloatRegister, getRegisterName methods from libunwind::Registres_arm class. * Methods implementation for Registers_arm_rt class * Avoid PORTABILITY_ASSERT() in StepFrame for ARM The DoTheStep accepts 3 input parameters: pc - it could be taken for regs input parameter UnwindInfoSections - is not needed in case ARM, it will be located by libunwind later, before unwinding regs - pointer to REGDISPLAY that represents RT's VRS * Remove PORTABILITY_ASSERT() in DoTheStep() SetInfoBaseOnIPRegister - sets unwind info for libunwind::UnwindCursor see UnwindCursor.hpp:1228 for additional comments * libunwind API is updated: unw_set_reg sets pointer to register value Since the unwinder must provide pointer to pc, setting only pc value in the REGDISPLAY is not enough. For this reason the libunwind should set pointers to registers to set pIP pointer. * Clean up libunwind config file The libunwind uses "if defined()" not "!()" * Initialize Registers_arm_rt with REGDISPLAY The pointer to REGDISPLAY is passed to Registers_arm_rt constructor --- src/Native/Runtime/unix/UnixContext.cpp | 4 +- src/Native/Runtime/unix/UnwindHelpers.cpp | 162 ++++++++++++++++++++-- src/Native/libunwind/include/libunwind.h | 2 +- src/Native/libunwind/include/unwind.h | 10 +- src/Native/libunwind/src/Unwind-EHABI.cpp | 24 ++-- src/Native/libunwind/src/UnwindCursor.hpp | 6 +- src/Native/libunwind/src/config.h | 5 - src/Native/libunwind/src/libunwind.cpp | 4 +- 8 files changed, 181 insertions(+), 36 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 18fff0ccbd0..ea538213c05 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -232,11 +232,11 @@ int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*) static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor) { #define ASSIGN_REG(regName1, regName2) \ - unw_set_reg(cursor, regName1, regDisplay->regName2); + unw_set_reg(cursor, regName1, regDisplay->regName2, 0); #define ASSIGN_REG_PTR(regName1, regName2) \ if (regDisplay->p##regName2 != NULL) \ - unw_set_reg(cursor, regName1, *(regDisplay->p##regName2)); + unw_set_reg(cursor, regName1, *(regDisplay->p##regName2), 0); #if defined(_AMD64_) ASSIGN_REG(UNW_REG_SP, SP) diff --git a/src/Native/Runtime/unix/UnwindHelpers.cpp b/src/Native/Runtime/unix/UnwindHelpers.cpp index c77de82009c..7322e1ab1ed 100644 --- a/src/Native/Runtime/unix/UnwindHelpers.cpp +++ b/src/Native/Runtime/unix/UnwindHelpers.cpp @@ -292,13 +292,152 @@ struct Registers_REGDISPLAY : REGDISPLAY }; #endif // _TARGET_AMD64_ +#if defined(_TARGET_ARM_) + +class Registers_arm_rt: public libunwind::Registers_arm { +public: + Registers_arm_rt() { abort(); }; + Registers_arm_rt(void *registers) { regs = (REGDISPLAY *)registers; }; + uint32_t getRegister(int num); + void setRegister(int num, uint32_t value, uint32_t location); + uint32_t getRegisterLocation(int regNum) const { abort();} + unw_fpreg_t getFloatRegister(int num) { abort();} + void setFloatRegister(int num, unw_fpreg_t value) {abort();} + bool validVectorRegister(int num) const { abort();} + uint32_t getVectorRegister(int num) const {abort();}; + void setVectorRegister(int num, uint32_t value) {abort();}; + void jumpto() { abort();}; + uint32_t getSP() const { return regs->SP;} + void setSP(uint32_t value, uint32_t location) { regs->SP = value;} + uint32_t getIP() const { return regs->IP;} + void setIP(uint32_t value, uint32_t location) + { regs->IP = value; regs->pIP = (PTR_UIntNative)location; } + void saveVFPAsX() {abort();}; +private: + REGDISPLAY *regs; +}; + +inline uint32_t Registers_arm_rt::getRegister(int regNum) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return regs->SP; + + if (regNum == UNW_ARM_LR) + return *regs->pLR; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return regs->IP; + + switch (regNum) + { + case (UNW_ARM_R0): + return *regs->pR0; + case (UNW_ARM_R1): + return *regs->pR1; + case (UNW_ARM_R2): + return *regs->pR2; + case (UNW_ARM_R3): + return *regs->pR3; + case (UNW_ARM_R4): + return *regs->pR4; + case (UNW_ARM_R5): + return *regs->pR5; + case (UNW_ARM_R6): + return *regs->pR6; + case (UNW_ARM_R7): + return *regs->pR7; + case (UNW_ARM_R8): + return *regs->pR8; + case (UNW_ARM_R9): + return *regs->pR9; + case (UNW_ARM_R10): + return *regs->pR10; + case (UNW_ARM_R11): + return *regs->pR11; + case (UNW_ARM_R12): + return *regs->pR12; + } + + PORTABILITY_ASSERT("unsupported arm register"); +} + +void Registers_arm_rt::setRegister(int num, uint32_t value, uint32_t location) +{ + + if (num == UNW_REG_SP || num == UNW_ARM_SP) { + regs->SP = (UIntNative )value; + return; + } + + if (num == UNW_ARM_LR) { + regs->pLR = (PTR_UIntNative)location; + return; + } + + if (num == UNW_REG_IP || num == UNW_ARM_IP) { + regs->IP = value; + /* the location could be NULL, we could try to recover + pointer to value in stack from pLR */ + if ((!location) && (regs->pLR) && (*regs->pLR == value)) + regs->pIP = regs->pLR; + else + regs->pIP = (PTR_UIntNative)location; + return; + } + + switch (num) + { + case (UNW_ARM_R0): + regs->pR0 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R1): + regs->pR1 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R2): + regs->pR2 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R3): + regs->pR3 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R4): + regs->pR4 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R5): + regs->pR5 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R6): + regs->pR6 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R7): + regs->pR7 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R8): + regs->pR8 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R9): + regs->pR9 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R10): + regs->pR10 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R11): + regs->pR11 = (PTR_UIntNative)location; + break; + case (UNW_ARM_R12): + regs->pR12 = (PTR_UIntNative)location; + break; + default: + PORTABILITY_ASSERT("unsupported arm register"); + } +} + +#endif // _TARGET_ARM_ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs) { #if defined(_TARGET_AMD64_) libunwind::UnwindCursor uc(_addressSpace); #elif defined(_TARGET_ARM_) - libunwind::UnwindCursor uc(_addressSpace); + libunwind::UnwindCursor uc(_addressSpace, regs); #else #error "Unwinding is not implemented for this architecture yet." #endif @@ -322,8 +461,13 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs } regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR)); -#else - PORTABILITY_ASSERT("DoTheStep"); +#elif defined(_LIBUNWIND_ARM_EHABI) + uc.setInfoBasedOnIPRegister(true); + int stepRet = uc.step(); + if ((stepRet != UNW_STEP_SUCCESS) && (stepRet != UNW_STEP_END)) + { + return false; + } #endif return true; @@ -365,18 +509,20 @@ UnwindInfoSections LocateUnwindSections(uintptr_t pc) bool UnwindHelpers::StepFrame(REGDISPLAY *regs) { +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND uintptr_t pc = regs->GetIP(); - UnwindInfoSections uwInfoSections = LocateUnwindSections(pc); - -#if _LIBUNWIND_SUPPORT_DWARF_UNWIND if (uwInfoSections.dwarf_section == NULL) { return false; } + return DoTheStep(pc, uwInfoSections, regs); +#elif defined(_LIBUNWIND_ARM_EHABI) + // unwind section is located later for ARM + // pc will be taked from regs parameter + UnwindInfoSections uwInfoSections; + return DoTheStep(0, uwInfoSections, regs); #else PORTABILITY_ASSERT("StepFrame"); #endif - - return DoTheStep(pc, uwInfoSections, regs); } diff --git a/src/Native/libunwind/include/libunwind.h b/src/Native/libunwind/include/libunwind.h index 59187798d32..2e11f48d961 100644 --- a/src/Native/libunwind/include/libunwind.h +++ b/src/Native/libunwind/include/libunwind.h @@ -124,7 +124,7 @@ extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; -extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL; +extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t, unw_word_t *) LIBUNWIND_AVAIL; extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; diff --git a/src/Native/libunwind/include/unwind.h b/src/Native/libunwind/include/unwind.h index 1d163ca9af6..a350f51adde 100644 --- a/src/Native/libunwind/include/unwind.h +++ b/src/Native/libunwind/include/unwind.h @@ -200,7 +200,7 @@ _Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, extern _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, uint32_t regno, _Unwind_VRS_DataRepresentation representation, - void *valuep); + void *valuep, uint32_t *pos); extern _Unwind_VRS_Result _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, @@ -212,7 +212,7 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index); extern void _Unwind_SetGR(struct _Unwind_Context *context, int index, - uintptr_t new_value); + uintptr_t new_value, uintptr_t *pos); extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context); extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value); @@ -240,8 +240,8 @@ uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) { _LIBUNWIND_EXPORT_UNWIND_LEVEL1 void _Unwind_SetGR(struct _Unwind_Context *context, int index, - uintptr_t value) { - _Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value); + uintptr_t value,uintptr_t *pos) { + _Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value, pos); } _LIBUNWIND_EXPORT_UNWIND_LEVEL1 @@ -253,7 +253,7 @@ uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { _LIBUNWIND_EXPORT_UNWIND_LEVEL1 void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) { uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1); - _Unwind_SetGR(context, 15, value | thumb_bit); + _Unwind_SetGR(context, 15, value | thumb_bit, NULL); } #endif // _LIBUNWIND_ARM_EHABI diff --git a/src/Native/libunwind/src/Unwind-EHABI.cpp b/src/Native/libunwind/src/Unwind-EHABI.cpp index 11465521510..31da198c4e7 100644 --- a/src/Native/libunwind/src/Unwind-EHABI.cpp +++ b/src/Native/libunwind/src/Unwind-EHABI.cpp @@ -259,7 +259,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, sp -= (((uint32_t)byte & 0x3f) << 2) + 4; else sp += ((uint32_t)byte << 2) + 4; - _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp, NULL); } else { switch (byte & 0xf0) { case 0x80: { @@ -283,7 +283,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_R0 + reg, _UVRSD_UINT32, &sp); _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, - &sp); + &sp, NULL); break; } case 0xa0: { @@ -325,7 +325,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, &sp); sp += 0x204 + (addend << 2); _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, - &sp); + &sp, NULL); break; } case 0xb3: { @@ -411,7 +411,7 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, if (!wrotePC) { uint32_t lr; _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr); - _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr, NULL); } return _URC_CONTINUE_UNWIND; } @@ -559,7 +559,7 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor // // See #7.4.6 for details. unw_set_reg(cursor, UNW_REG_IP, - exception_object->unwinder_cache.reserved2); + exception_object->unwinder_cache.reserved2, NULL); resume = false; } @@ -753,7 +753,7 @@ static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, _LIBUNWIND_EXPORT _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, uint32_t regno, _Unwind_VRS_DataRepresentation representation, - void *valuep) { + void *valuep, unw_word_t *pos) { _LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, " "rep=%d, value=0x%llX)", static_cast(context), regclass, regno, @@ -765,7 +765,7 @@ _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, if (representation != _UVRSD_UINT32 || regno > 15) return _UVRSR_FAILED; return unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), - *(unw_word_t *)valuep) == UNW_ESUCCESS + *(unw_word_t *)valuep,(unw_word_t *)pos) == UNW_ESUCCESS ? _UVRSR_OK : _UVRSR_FAILED; case _UVRSC_VFP: @@ -897,6 +897,7 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, // computed new stack location. See EHABI #7.5.4 table 3. bool poppedSP = false; uint32_t* sp; + uint32_t* pos; if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp) != _UVRSR_OK) { return _UVRSR_FAILED; @@ -904,17 +905,18 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, for (uint32_t i = 0; i < 16; ++i) { if (!(discriminator & static_cast(1 << i))) continue; + pos = sp; uint32_t value = *sp++; if (regclass == _UVRSC_CORE && i == 13) poppedSP = true; if (_Unwind_VRS_Set(context, regclass, i, - _UVRSD_UINT32, &value) != _UVRSR_OK) { + _UVRSD_UINT32, &value, pos) != _UVRSR_OK) { return _UVRSR_FAILED; } } if (!poppedSP) { return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, - _UVRSD_UINT32, &sp); + _UVRSD_UINT32, &sp, NULL); } return _UVRSR_OK; } @@ -939,14 +941,14 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, // SP is only 32-bit aligned so don't copy 64-bit at a time. uint64_t value = *sp++; value |= ((uint64_t)(*sp++)) << 32; - if (_Unwind_VRS_Set(context, regclass, i, representation, &value) != + if (_Unwind_VRS_Set(context, regclass, i, representation, &value, NULL) != _UVRSR_OK) return _UVRSR_FAILED; } if (representation == _UVRSD_VFPX) ++sp; return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, - &sp); + &sp, NULL); } } _LIBUNWIND_ABORT("unsupported register class"); diff --git a/src/Native/libunwind/src/UnwindCursor.hpp b/src/Native/libunwind/src/UnwindCursor.hpp index f68eee18e96..17cd33c47a2 100644 --- a/src/Native/libunwind/src/UnwindCursor.hpp +++ b/src/Native/libunwind/src/UnwindCursor.hpp @@ -628,9 +628,11 @@ UnwindCursor::UnwindCursor(unw_context_t *context, A &as) } template -UnwindCursor::UnwindCursor(A &as, void *) - : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { +UnwindCursor::UnwindCursor(A &as, void *arg) + : _addressSpace(as),_registers(arg), _unwindInfoMissing(false), + _isSignalFrame(false) { memset(&_info, 0, sizeof(_info)); + // FIXME // fill in _registers from thread arg } diff --git a/src/Native/libunwind/src/config.h b/src/Native/libunwind/src/config.h index bba0f9c9f1f..d8bcac88217 100644 --- a/src/Native/libunwind/src/config.h +++ b/src/Native/libunwind/src/config.h @@ -42,13 +42,8 @@ #endif #else #if defined(__ARM_DWARF_EH__) || !defined(__arm__) - #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 0 #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 - #else - #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 0 - #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 0 - #define _LIBUNWIND_SUPPORT_DWARF_INDEX 0 #endif #endif diff --git a/src/Native/libunwind/src/libunwind.cpp b/src/Native/libunwind/src/libunwind.cpp index 3821149d684..00c0fc3776e 100644 --- a/src/Native/libunwind/src/libunwind.cpp +++ b/src/Native/libunwind/src/libunwind.cpp @@ -171,13 +171,13 @@ _LIBUNWIND_EXPORT int unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, /// Set value of specified register at cursor position in stack frame. _LIBUNWIND_EXPORT int unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, - unw_word_t value) { + unw_word_t value, unw_word_t *pos) { _LIBUNWIND_TRACE_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)", static_cast(cursor), regNum, (long long)value); typedef LocalAddressSpace::pint_t pint_t; AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; if (co->validReg(regNum)) { - co->setReg(regNum, (pint_t)value, 0); + co->setReg(regNum, (pint_t)value, (pint_t)pos); // special case altering IP to re-find info (being called by personality // function) if (regNum == UNW_REG_IP)