diff options
author | Nico Weber <nicolasweber@gmx.de> | 2014-06-25 23:39:00 +0000 |
---|---|---|
committer | Nico Weber <nicolasweber@gmx.de> | 2014-06-25 23:39:00 +0000 |
commit | 97080e0c5eb477ae771d1dc2b034d3a6787a47e5 (patch) | |
tree | e19ec0a49d6d3b7831e9b718145b81df6228f881 | |
parent | e44fd66f8c4737dd9db329690cfae6b3ac8563bb (diff) | |
download | bcm5719-llvm-97080e0c5eb477ae771d1dc2b034d3a6787a47e5.tar.gz bcm5719-llvm-97080e0c5eb477ae771d1dc2b034d3a6787a47e5.zip |
Land support for ARM EHABI unwinding for libunwind.
This was written by:
Albert Wong <ajwong@chromium.org>
Antoine Labour <piman@chromium.org>
Dana Jansen <danakj@chromium.org
Jonathan Roelofs <jonathan@codesourcery.com>
Nico Weber <thakis@chromium.org>
llvm-svn: 211743
-rw-r--r-- | libcxxabi/include/libunwind.h | 6 | ||||
-rw-r--r-- | libcxxabi/include/unwind.h | 54 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/Registers.hpp | 190 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/Unwind-EHABI.cpp | 952 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/UnwindCursor.hpp | 264 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c | 21 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/UnwindLevel1.c | 24 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/UnwindRegistersRestore.S | 88 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/UnwindRegistersSave.S | 92 | ||||
-rw-r--r-- | libcxxabi/src/Unwind/libunwind.cpp | 28 |
10 files changed, 1636 insertions, 83 deletions
diff --git a/libcxxabi/include/libunwind.h b/libcxxabi/include/libunwind.h index 7b00f4fec56..7057070b306 100644 --- a/libcxxabi/include/libunwind.h +++ b/libcxxabi/include/libunwind.h @@ -100,6 +100,12 @@ extern int unw_set_reg(unw_cursor_t *, unw_regnum_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; +#if __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif + + extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; diff --git a/libcxxabi/include/unwind.h b/libcxxabi/include/unwind.h index f3a09ecd245..4e77c3a4181 100644 --- a/libcxxabi/include/unwind.h +++ b/libcxxabi/include/unwind.h @@ -55,6 +55,7 @@ typedef enum { _UA_END_OF_STACK = 16 // gcc extension to C++ ABI } _Unwind_Action; +typedef struct _Unwind_Context _Unwind_Context; // opaque #if LIBCXXABI_ARM_EHABI typedef uint32_t _Unwind_State; @@ -73,26 +74,30 @@ struct _Unwind_Control_Block { uint64_t exception_class; void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*); + /* Unwinder cache, private fields for the unwinder's use */ struct { - uint32_t reserved1; + uint32_t reserved1; /* init reserved1 to 0, then don't touch */ uint32_t reserved2; uint32_t reserved3; uint32_t reserved4; uint32_t reserved5; } unwinder_cache; + /* Propagation barrier cache (valid after phase 1): */ struct { uint32_t sp; uint32_t bitpattern[5]; } barrier_cache; + /* Cleanup cache (preserved over cleanup): */ struct { uint32_t bitpattern[4]; } cleanup_cache; + /* Pr cache (for pr's benefit): */ struct { - uint32_t fnstart; - _Unwind_EHT_Header* ehtp; + uint32_t fnstart; /* function start address */ + _Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */ uint32_t additional; uint32_t reserved1; } pr_cache; @@ -165,10 +170,10 @@ extern void _Unwind_DeleteException(_Unwind_Exception *exception_object); #if LIBCXXABI_ARM_EHABI typedef enum { - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4 + _UVRSC_CORE = 0, /* integer register */ + _UVRSC_VFP = 1, /* vfp */ + _UVRSC_WMMXD = 3, /* Intel WMMX data register */ + _UVRSC_WMMXC = 4 /* Intel WMMX control register */ } _Unwind_VRS_RegClass; typedef enum { @@ -185,17 +190,26 @@ typedef enum { _UVRSR_FAILED = 2 } _Unwind_VRS_Result; -extern _Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context* context, - _Unwind_VRS_RegClass regclass, - uint32_t regno, - _Unwind_VRS_DataRepresentation representation, - void *valuep); +extern void _Unwind_Complete(_Unwind_Exception* exception_object); + +extern _Unwind_VRS_Result +_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep); + +extern _Unwind_VRS_Result +_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep); + +extern _Unwind_VRS_Result +_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation); -extern _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context* context, - _Unwind_VRS_RegClass regclass, - uint32_t regno, - _Unwind_VRS_DataRepresentation representation, - void *valuep); +extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, + uint32_t *data, size_t offset, + size_t len); static inline uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) { @@ -276,9 +290,9 @@ extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); // _Unwind_GetIPInfo is a gcc extension that can be called from within a -// personality handler. Similar to _Unwind_GetIP() but also returns in -// *ipBefore a non-zero value if the instruction pointer is at or before the -// instruction causing the unwind. Normally, in a function call, the IP returned +// personality handler. Similar to _Unwind_GetIP() but also returns in +// *ipBefore a non-zero value if the instruction pointer is at or before the +// instruction causing the unwind. Normally, in a function call, the IP returned // is the return address which is after the call instruction and may be past the // end of the function containing the call instruction. extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, diff --git a/libcxxabi/src/Unwind/Registers.hpp b/libcxxabi/src/Unwind/Registers.hpp index e2cfa17d2d4..8c1acfced02 100644 --- a/libcxxabi/src/Unwind/Registers.hpp +++ b/libcxxabi/src/Unwind/Registers.hpp @@ -1283,39 +1283,51 @@ inline void Registers_arm64::setVectorRegister(int, v128) { /// /// NOTE: Assumes VFPv3. On ARM processors without a floating point unit, /// this uses more memory than required. -/// -/// FIXME: Support MMX Data Registers, Control registers, and load/stores -/// for different representations in the VFP registers as listed in -/// Table 1 of EHABI #7.5.2 class _LIBUNWIND_HIDDEN Registers_arm { public: Registers_arm(); Registers_arm(const void *registers); bool validRegister(int num) const; - uint32_t getRegister(int num) const; + uint32_t getRegister(int num); void setRegister(int num, uint32_t value); - // FIXME: Due to ARM VRS's support for reading/writing different - // representations into the VFP registers this set of accessors seem wrong. - // If {get,set}FloatRegister() is the backing store for - // _Unwind_VRS_{Get,Set} then it might be best to return a tagged union - // with types for each representation in _Unwind_VRS_DataRepresentation. - // Similarly, unw_{get,set}_fpreg in the public libunwind API may want to - // use a similar tagged union to back the unw_fpreg_t output parameter type. bool validFloatRegister(int num) const; - unw_fpreg_t getFloatRegister(int num) const; + unw_fpreg_t getFloatRegister(int num); void setFloatRegister(int num, unw_fpreg_t value); bool validVectorRegister(int num) const; v128 getVectorRegister(int num) const; void setVectorRegister(int num, v128 value); const char *getRegisterName(int num); - void jumpto(); + void jumpto() { + restoreSavedFloatRegisters(); + restoreCoreAndJumpTo(); + } uint32_t getSP() const { return _registers.__sp; } void setSP(uint32_t value) { _registers.__sp = value; } uint32_t getIP() const { return _registers.__pc; } void setIP(uint32_t value) { _registers.__pc = value; } + void saveVFPAsX() { + assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); + _use_X_for_vfp_save = true; + } + + void restoreSavedFloatRegisters() { + if (_saved_vfp_d0_d15) { + if (_use_X_for_vfp_save) + restoreVFPWithFLDMX(_vfp_d0_d15_pad); + else + restoreVFPWithFLDMD(_vfp_d0_d15_pad); + } + if (_saved_vfp_d16_d31) + restoreVFPv3(_vfp_d16_d31); + if (_saved_iwmmx) + restoreiWMMX(_iwmmx); + if (_saved_iwmmx_control) + restoreiWMMXControl(_iwmmx_control); + } + private: struct GPRs { uint32_t __r[13]; // r0-r12 @@ -1324,17 +1336,74 @@ private: uint32_t __pc; // Program counter r15 }; - GPRs _registers; + static void saveVFPWithFSTMD(unw_fpreg_t*); + static void saveVFPWithFSTMX(unw_fpreg_t*); + static void saveVFPv3(unw_fpreg_t*); + static void saveiWMMX(unw_fpreg_t*); + static void saveiWMMXControl(uint32_t*); + static void restoreVFPWithFLDMD(unw_fpreg_t*); + static void restoreVFPWithFLDMX(unw_fpreg_t*); + static void restoreVFPv3(unw_fpreg_t*); + static void restoreiWMMX(unw_fpreg_t*); + static void restoreiWMMXControl(uint32_t*); + void restoreCoreAndJumpTo(); + + // ARM registers + GPRs _registers; + + // We save floating point registers lazily because we can't know ahead of + // time which ones are used. See EHABI #4.7. + + // Whether D0-D15 are saved in the FTSMX instead of FSTMD format. + // + // See EHABI #7.5 that explains how matching instruction sequences for load + // and store need to be used to correctly restore the exact register bits. + bool _use_X_for_vfp_save; + // Whether VFP D0-D15 are saved. + bool _saved_vfp_d0_d15; + // Whether VFPv3 D16-D31 are saved. + bool _saved_vfp_d16_d31; + // Whether iWMMX data registers are saved. + bool _saved_iwmmx; + // Whether iWMMX control registers are saved. + bool _saved_iwmmx_control; + // VFP registers D0-D15, + padding if saved using FSTMX + unw_fpreg_t _vfp_d0_d15_pad[17]; + // VFPv3 registers D16-D31, always saved using FSTMD + unw_fpreg_t _vfp_d16_d31[16]; + // iWMMX registers + unw_fpreg_t _iwmmx[16]; + // iWMMX control registers + uint32_t _iwmmx_control[4]; }; -inline Registers_arm::Registers_arm(const void *registers) { +inline Registers_arm::Registers_arm(const void *registers) + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false), + _saved_iwmmx(false), + _saved_iwmmx_control(false) { static_assert(sizeof(Registers_arm) < sizeof(unw_context_t), "arm registers do not fit into unw_context_t"); + // See unw_getcontext() note about data. memcpy(&_registers, registers, sizeof(_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); } -inline Registers_arm::Registers_arm() { +inline Registers_arm::Registers_arm() + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false), + _saved_iwmmx(false), + _saved_iwmmx_control(false) { memset(&_registers, 0, sizeof(_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); } inline bool Registers_arm::validRegister(int regNum) const { @@ -1344,20 +1413,29 @@ inline bool Registers_arm::validRegister(int regNum) const { return true; if (regNum == UNW_REG_SP) return true; - if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R15)) + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) + return true; + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) return true; return false; } -inline uint32_t Registers_arm::getRegister(int regNum) const { +inline uint32_t Registers_arm::getRegister(int regNum) { if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) return _registers.__sp; if (regNum == UNW_ARM_LR) return _registers.__lr; if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) return _registers.__pc; - if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R12)) + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) return _registers.__r[regNum]; + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + return _iwmmx_control[regNum - UNW_ARM_WC0]; + } _LIBUNWIND_ABORT("unsupported arm register"); } @@ -1368,9 +1446,15 @@ inline void Registers_arm::setRegister(int regNum, uint32_t value) { _registers.__lr = value; else if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) _registers.__pc = value; - else if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R12)) + else if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) _registers.__r[regNum] = value; - else + else if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + _iwmmx_control[regNum - UNW_ARM_WC0] = value; + } else _LIBUNWIND_ABORT("unsupported arm register"); } @@ -1543,17 +1627,65 @@ inline const char *Registers_arm::getRegisterName(int regNum) { } } -inline bool Registers_arm::validFloatRegister(int) const { - // FIXME: Implement float register support. - return false; +inline bool Registers_arm::validFloatRegister(int regNum) const { + // NOTE: Consider the intel MMX registers floating points so the + // unw_get_fpreg can be used to transmit the 64-bit data back. + return ((regNum >= UNW_ARM_D0) && (regNum <= UNW_ARM_D31)) + || ((regNum >= UNW_ARM_WR0) && (regNum <= UNW_ARM_WR15)); } -inline unw_fpreg_t Registers_arm::getFloatRegister(int) const { - _LIBUNWIND_ABORT("ARM float register support not yet implemented"); +inline unw_fpreg_t Registers_arm::getFloatRegister(int regNum) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + return _vfp_d0_d15_pad[regNum - UNW_ARM_D0]; + } else if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + return _vfp_d16_d31[regNum - UNW_ARM_D16]; + } else if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + return _iwmmx[regNum - UNW_ARM_WR0]; + } else { + _LIBUNWIND_ABORT("Unknown ARM float register"); + } } -inline void Registers_arm::setFloatRegister(int, unw_fpreg_t) { - _LIBUNWIND_ABORT("ARM float register support not yet implemented"); +inline void Registers_arm::setFloatRegister(int regNum, unw_fpreg_t value) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + _vfp_d0_d15_pad[regNum - UNW_ARM_D0] = value; + } else if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + _vfp_d16_d31[regNum - UNW_ARM_D0] = value; + } else if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + _iwmmx[regNum - UNW_ARM_WR0] = value; + } else { + _LIBUNWIND_ABORT("Unknown ARM float register"); + } } inline bool Registers_arm::validVectorRegister(int) const { diff --git a/libcxxabi/src/Unwind/Unwind-EHABI.cpp b/libcxxabi/src/Unwind/Unwind-EHABI.cpp new file mode 100644 index 00000000000..f4619f08d90 --- /dev/null +++ b/libcxxabi/src/Unwind/Unwind-EHABI.cpp @@ -0,0 +1,952 @@ +//===--------------------------- Unwind-EHABI.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// Implements ARM zero-cost C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include <unwind.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "libunwind.h" +#include "unwind.h" +#include "../private_typeinfo.h" + +#if LIBCXXABI_ARM_EHABI +namespace { + +// Strange order: take words in order, but inside word, take from most to least +// signinficant byte. +uint8_t getByte(uint32_t* data, size_t offset) { + uint8_t* byteData = reinterpret_cast<uint8_t*>(data); + return byteData[(offset & ~0x03) + (3 - (offset&0x03))]; +} + +const char* getNextWord(const char* data, uint32_t* out) { + *out = *reinterpret_cast<const uint32_t*>(data); + return data + 4; +} + +const char* getNextNibble(const char* data, uint32_t* out) { + *out = *reinterpret_cast<const uint16_t*>(data); + return data + 2; +} + +static inline uint32_t signExtendPrel31(uint32_t data) { + return data | ((data & 0x40000000u) << 1); +} + +struct Descriptor { + // See # 9.2 + typedef enum { + SU16 = 0, // Short descriptor, 16-bit entries + LU16 = 1, // Long descriptor, 16-bit entries + LU32 = 3, // Long descriptor, 32-bit entries + RESERVED0 = 4, RESERVED1 = 5, RESERVED2 = 6, RESERVED3 = 7, + RESERVED4 = 8, RESERVED5 = 9, RESERVED6 = 10, RESERVED7 = 11, + RESERVED8 = 12, RESERVED9 = 13, RESERVED10 = 14, RESERVED11 = 15 + } Format; + + // See # 9.2 + typedef enum { + CLEANUP = 0x0, + FUNC = 0x1, + CATCH = 0x2, + INVALID = 0x4 + } Kind; +}; + +_Unwind_Reason_Code ProcessDescriptors( + _Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context, + Descriptor::Format format, + const char* descriptorStart, + int flags) { + // EHT is inlined in the index using compact form. No descriptors. #5 + if (flags & 0x1) + return _URC_CONTINUE_UNWIND; + + const char* descriptor = descriptorStart; + uint32_t descriptorWord; + getNextWord(descriptor, &descriptorWord); + while (descriptorWord) { + // Read descriptor based on # 9.2. + uint32_t length; + uint32_t offset; + switch (format) { + case Descriptor::LU32: + descriptor = getNextWord(descriptor, &length); + descriptor = getNextWord(descriptor, &offset); + case Descriptor::LU16: + descriptor = getNextNibble(descriptor, &length); + descriptor = getNextNibble(descriptor, &offset); + default: + assert(false); + return _URC_FAILURE; + } + + // See # 9.2 table for decoding the kind of descriptor. It's a 2-bit value. + Descriptor::Kind kind = + static_cast<Descriptor::Kind>((length & 0x1) | ((offset & 0x1) << 1)); + + // Clear off flag from last bit. + length &= ~1; + offset &= ~1; + uintptr_t scopeStart = ucbp->pr_cache.fnstart + offset; + uintptr_t scopeEnd = scopeStart + length; + uintptr_t pc = _Unwind_GetIP(context); + bool isInScope = (scopeStart <= pc) && (pc < scopeEnd); + + switch (kind) { + case Descriptor::CLEANUP: { + // TODO(ajwong): Handle cleanup descriptors. + break; + } + case Descriptor::FUNC: { + // TODO(ajwong): Handle function descriptors. + break; + } + case Descriptor::CATCH: { + // Catch descriptors require gobbling one more word. + uint32_t landing_pad; + descriptor = getNextWord(descriptor, &landing_pad); + + if (isInScope) { + // TODO(ajwong): This is only phase1 compatible logic. Implement + // phase2. + bool is_reference_type = landing_pad & 0x80000000; + landing_pad = signExtendPrel31(landing_pad & ~0x80000000); + if (landing_pad == 0xffffffff) { + return _URC_HANDLER_FOUND; + } else if (landing_pad == 0xfffffffe ) { + return _URC_FAILURE; + } else { + void* matched_object; + /* + if (__cxxabiv1::__cxa_type_match( + ucbp, reinterpret_cast<const std::type_info *>(landing_pad), + is_reference_type, + &matched_object) != __cxxabiv1::ctm_failed) + return _URC_HANDLER_FOUND; + */ + _LIBUNWIND_ABORT("Type matching not implemented"); + } + } + break; + } + default: + _LIBUNWIND_ABORT("Invalid descriptor kind found."); + }; + + getNextWord(descriptor, &descriptorWord); + } + + return _URC_CONTINUE_UNWIND; +} + +_Unwind_Reason_Code unwindOneFrame( + _Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context) { + // Read the compact model EHT entry's header # 6.3 + uint32_t* unwindingData = ucbp->pr_cache.ehtp; + uint32_t unwindInfo = *unwindingData; + assert((unwindInfo & 0xf0000000) == 0x80000000 && "Must be a compact entry"); + Descriptor::Format format = + static_cast<Descriptor::Format>((unwindInfo & 0x0f000000) >> 24); + size_t len = 0; + size_t startOffset = 0; + switch (format) { + case Descriptor::SU16: + len = 4; + startOffset = 1; + break; + case Descriptor::LU16: + case Descriptor::LU32: + len = 4 + 4 * ((unwindInfo & 0x00ff0000) >> 16); + startOffset = 2; + break; + default: + return _URC_FAILURE; + } + + // Handle descriptors before unwinding so they are processed in the context + // of the correct stack frame. + _Unwind_Reason_Code result = + ProcessDescriptors( + state, ucbp, context, format, + reinterpret_cast<const char*>(ucbp->pr_cache.ehtp) + len, + ucbp->pr_cache.additional); + + if (result != _URC_CONTINUE_UNWIND) + return result; + + return _Unwind_VRS_Interpret(context, unwindingData, startOffset, len); +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / +// _UVRSD_UINT32. +uint32_t RegisterMask(uint8_t start, uint8_t count_minus_one) { + return ((1U << (count_minus_one + 1)) - 1) << start; +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_VFP / +// _UVRSD_DOUBLE. +uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) { + return (start << 16) | (count_minus_one + 1); +} + +} // end anonymous namespace + +_Unwind_Reason_Code _Unwind_VRS_Interpret( + _Unwind_Context* context, + uint32_t* data, + size_t offset, + size_t len) { + bool wrotePC = false; + bool finish = false; + while (offset < len && !finish) { + uint8_t byte = getByte(data, offset++); + if ((byte & 0x80) == 0) { + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + if (byte & 0x40) + sp -= ((byte & 0x3f) << 2) + 4; + else + sp += (byte << 2) + 4; + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + } else { + switch (byte & 0xf0) { + case 0x80: { + if (offset >= len) + return _URC_FAILURE; + uint16_t registers = + ((byte & 0x0f) << 12) | (getByte(data, offset++) << 4); + if (!registers) + return _URC_FAILURE; + if (registers & (1<<15)) + wrotePC = true; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0x90: { + uint8_t reg = byte & 0x0f; + if (reg == 13 || reg == 15) + return _URC_FAILURE; + uint32_t sp; + _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); + break; + } + case 0xa0: { + uint32_t registers = RegisterMask(4, byte & 0x07); + if (byte & 0x08) + registers |= 1<<14; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb0: { + switch (byte) { + case 0xb0: + finish = true; + break; + case 0xb1: { + if (offset >= len) + return _URC_FAILURE; + uint8_t registers = getByte(data, offset++); + if (registers & 0xf0 || !registers) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb2: { + uint32_t addend = 0; + uint32_t shift = 0; + // This decodes a uleb128 value. + while (true) { + if (offset >= len) + return _URC_FAILURE; + uint32_t v = getByte(data, offset++); + addend |= (v & 0x7f) << shift; + if ((v & 0x80) == 0) + break; + shift += 7; + } + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + sp += 0x204 + (addend << 2); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + break; + } + case 0xb3: { + uint8_t v = getByte(data, offset++); + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(v >> 4, v & 0x0f), _UVRSD_VFPX); + break; + } + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + return _URC_FAILURE; + default: + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(8, byte & 0x07), _UVRSD_VFPX); + break; + } + break; + } + case 0xc0: { + switch (byte) { + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(10, byte & 0x7), _UVRSD_DOUBLE); + break; + case 0xc6: { + uint8_t v = getByte(data, offset++); + uint8_t start = v >> 4; + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 16) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + case 0xc7: { + uint8_t v = getByte(data, offset++); + if (!v || v & 0xf0) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXC, v, _UVRSD_DOUBLE); + break; + } + case 0xc8: + case 0xc9: { + uint8_t v = getByte(data, offset++); + uint8_t start = ((byte == 0xc8) ? 16 : 0) + (v >> 4); + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 32) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + break; + } + case 0xd0: { + if (byte & 0x08) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, RegisterRange(8, byte & 0x7), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + } + } + 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); + } + return _URC_CONTINUE_UNWIND; +} + +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr0( + _Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr1( + _Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr2( + _Unwind_State state, + _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) { + // EHABI #7.3 discusses preserving the VRS in a "temporary VRS" during + // phase 1 and then restoring it to the "primary VRS" for phase 2. The + // effect is phase 2 doesn't see any of the VRS manipulations from phase 1. + // In this implementation, the phases don't share the VRS backing store. + // Instead, they are passed the original |uc| and they create a new VRS + // from scratch thus achieving the same effect. + unw_cursor_t cursor1; + unw_init_local(&cursor1, uc); + + // Walk each frame looking for a place to stop. + for (bool handlerNotFound = true; handlerNotFound;) { + + // Ask libuwind to get next frame (skip over first which is + // _Unwind_RaiseException). + int stepResult = unw_step(&cursor1); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached " + "bottom => _URC_END_OF_STACK\n", + exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => " + "_URC_FATAL_PHASE1_ERROR\n", + exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // See if frame has code to run (has personality routine). + unw_proc_info_t frameInfo; + unw_word_t sp; + if (unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR\n", + exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionName[512]; + unw_word_t offset; + if ((unw_get_proc_name(&cursor1, functionName, 512, &offset) != + UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) + strcpy(functionName, ".anonymous."); + unw_word_t pc; + unw_get_reg(&cursor1, UNW_REG_IP, &pc); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, " + "lsda=0x%llX, personality=0x%llX\n", + exception_object, (long long)pc, (long long)frameInfo.start_ip, + functionName, (long long)frameInfo.lsda, + (long long)frameInfo.handler); + } + + // If there is a personality routine, ask it if it will want to stop at + // this frame. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): calling personality function %p\n", + exception_object, p); + struct _Unwind_Context *context = (struct _Unwind_Context *)(&cursor1); + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): personality result %d " + "start_ip %x ehtp %p additional %x\n", + exception_object, personalityResult, + exception_object->pr_cache.fnstart, exception_object->pr_cache.ehtp, + exception_object->pr_cache.additional); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + // p should have initialized barrier_cache. EHABI #7.3.5 + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " + "_URC_HANDLER_FOUND \n", + exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", + exception_object); + // continue unwinding + break; + + // EHABI #7.3.3 + case _URC_FAILURE: + return _URC_FAILURE; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", + exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + +static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, + _Unwind_Exception *exception_object, + bool resume) { + // See comment at the start of unwind_phase1 regarding VRS integrity. + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + int frame_count = 0; + + // Walk each frame until we reach where search phase said to stop. + while (true) { + // Ask libuwind to get next frame (skip over first which is + // _Unwind_RaiseException or _Unwind_Resume). + // + // Resume only ever makes sense for 1 frame. + _Unwind_State state = + resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING; + if (resume && frame_count == 1) { + // On a resume, first unwind the _Unwind_Resume() frame. The next frame + // is now the landing pad for the cleanup from a previous execution of + // phase2. To continue unwindingly correctly, replace VRS[15] with the + // IP of the frame that the previous run of phase2 installed the context + // for. After this, continue unwinding as if normal. + // + // See #7.4.6 for details. + unw_set_reg(&cursor2, UNW_REG_IP, + exception_object->unwinder_cache.reserved2); + resume = false; + } + + int stepResult = unw_step(&cursor2); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " + "bottom => _URC_END_OF_STACK\n", + exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => " + "_URC_FATAL_PHASE1_ERROR\n", + exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR\n", + exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionName[512]; + unw_word_t offset; + if ((unw_get_proc_name(&cursor2, functionName, 512, &offset) != + UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) + strcpy(functionName, ".anonymous."); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, " + "lsda=0x%llX, personality=0x%llX\n", + exception_object, (long long)frameInfo.start_ip, functionName, + (long long)sp, (long long)frameInfo.lsda, + (long long)frameInfo.handler); + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + struct _Unwind_Context *context = (struct _Unwind_Context *)(&cursor2); + // EHABI #7.2 + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(state, exception_object, context); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", + exception_object); + // EHABI #7.2 + if (sp == exception_object->barrier_cache.sp) { + // Phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now in phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", + exception_object); + // Personality routine says to transfer control to landing pad. + // We may get control back if landing pad calls _Unwind_Resume(). + if (_LIBUNWIND_TRACING_UNWINDING) { + unw_word_t pc; + unw_get_reg(&cursor2, UNW_REG_IP, &pc); + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " + "user code with ip=0x%llX, sp=0x%llX\n", + exception_object, (long long)pc, + (long long)sp); + } + + { + // EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume + // is called back, to find this same frame. + unw_word_t pc; + unw_get_reg(&cursor2, UNW_REG_IP, &pc); + exception_object->unwinder_cache.reserved2 = (uint32_t)pc; + } + unw_resume(&cursor2); + // unw_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + + // # EHABI #7.4.3 + case _URC_FAILURE: + abort(); + + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + frame_count++; + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +/// Called by __cxa_throw. Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)\n", + exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + // This field for is for compatibility with GCC to say this isn't a forced + // unwind. EHABI #7.2 + exception_object->unwinder_cache.reserved1 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, exception_object, false); +} + +_LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) { + // This is to be called when exception handling completes to give us a chance + // to perform any housekeeping. EHABI #7.2. But we have nothing to do here. +} + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call __cxa_rethrow() which +/// in turn calls _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, + // which is in the same position as private_1 below. + // TODO(ajwong): Who wronte the above? Why is it true? + unwind_phase2(&uc, exception_object, true); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.lsda; + _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p)" + "=> 0x%llx\n", context, (long long)result); + if (result != 0) { + if (*((uint8_t *)result) != 0xFF) + _LIBUNWIND_DEBUG_LOG("lsda at 0x%llx does not start with 0xFF\n", + (long long)result); + } + return result; +} + +static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, + void* valuep) { + uint64_t value = 0; + switch (representation) { + case _UVRSD_UINT32: + case _UVRSD_FLOAT: + memcpy(&value, valuep, sizeof(uint32_t)); + break; + + case _UVRSD_VFPX: + case _UVRSD_UINT64: + case _UVRSD_DOUBLE: + memcpy(&value, valuep, sizeof(uint64_t)); + break; + } + return value; +} + +_Unwind_VRS_Result _Unwind_VRS_Set( + _Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX)\n", context, regclass, + regno, representation, + ValueAsBitPattern(representation, valuep)); + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return unw_set_reg(cursor, UNW_ARM_R0 + regno, *(unw_word_t *)valuep) == + UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return unw_set_reg(cursor, UNW_ARM_WC0 + regno, *(unw_word_t *)valuep) == + UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return unw_set_fpreg(cursor, UNW_ARM_D0 + regno, + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return unw_set_fpreg(cursor, UNW_ARM_WR0 + regno, + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + } +} + +static _Unwind_VRS_Result _Unwind_VRS_Get_Internal( + _Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return unw_get_reg(cursor, UNW_ARM_R0 + regno, (unw_word_t *)valuep) == + UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return unw_get_reg(cursor, UNW_ARM_WC0 + regno, (unw_word_t *)valuep) == + UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return unw_get_fpreg(cursor, UNW_ARM_D0 + regno, (unw_fpreg_t *)valuep) == + UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return unw_get_fpreg(cursor, UNW_ARM_WR0 + regno, + (unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + } +} + +_Unwind_VRS_Result _Unwind_VRS_Get( + _Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) { + _Unwind_VRS_Result result = + _Unwind_VRS_Get_Internal(context, regclass, regno, representation, + valuep); + _LIBUNWIND_TRACE_API("_Unwind_VRS_Get(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX, result = %d)\n", + context, regclass, regno, representation, + ValueAsBitPattern(representation, valuep), result); + return result; +} + +_Unwind_VRS_Result _Unwind_VRS_Pop( + _Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Pop(context=%p, regclass=%d, " + "discriminator=%d, representation=%d)\n", + context, regclass, discriminator, representation); + switch (regclass) { + case _UVRSC_CORE: + case _UVRSC_WMMXC: { + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + // When popping SP from the stack, we don't want to override it from the + // computed new stack location. See EHABI #7.5.4 table 3. + bool poppedSP = false; + uint32_t* sp; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + for (int i = 0; i < 16; ++i) { + if (!(discriminator & (1<<i))) + continue; + uint32_t value = *sp++; + if (regclass == _UVRSC_CORE && i == 13) + poppedSP = true; + if (_Unwind_VRS_Set(context, regclass, i, + _UVRSD_UINT32, &value) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + } + if (!poppedSP) { + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp); + } + return _UVRSR_OK; + } + case _UVRSC_VFP: + case _UVRSC_WMMXD: { + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + uint32_t first = discriminator >> 16; + uint32_t count = discriminator & 0xffff; + uint32_t end = first+count; + uint32_t* sp; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + // For _UVRSD_VFPX, we're assuming the data is stored in FSTMX "standard + // format 1", which is equivalent to FSTMD + a padding word. + for (uint32_t i = first; i < end; ++i) { + // 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) != + _UVRSR_OK) + return _UVRSR_FAILED; + } + if (representation == _UVRSD_VFPX) + ++sp; + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + } + }; +} + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%llX\n", + context, (long long)result); + return result; +} + + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)\n", + exception_object); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +#endif // LIBCXXABI_ARM_EHABI diff --git a/libcxxabi/src/Unwind/UnwindCursor.hpp b/libcxxabi/src/Unwind/UnwindCursor.hpp index fd5826b941a..fa79afac387 100644 --- a/libcxxabi/src/Unwind/UnwindCursor.hpp +++ b/libcxxabi/src/Unwind/UnwindCursor.hpp @@ -12,10 +12,12 @@ #ifndef __UNWINDCURSOR_HPP__ #define __UNWINDCURSOR_HPP__ +#include <algorithm> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <pthread.h> +#include <unwind.h> #if __APPLE__ #include <mach-o/dyld.h> @@ -370,8 +372,8 @@ public: virtual unw_word_t getReg(int) = 0; virtual void setReg(int, unw_word_t) = 0; virtual bool validFloatReg(int) = 0; - virtual double getFloatReg(int) = 0; - virtual void setFloatReg(int, double) = 0; + virtual unw_fpreg_t getFloatReg(int) = 0; + virtual void setFloatReg(int, unw_fpreg_t) = 0; virtual int step() = 0; virtual void getInfo(unw_proc_info_t *) = 0; virtual void jumpto() = 0; @@ -379,6 +381,9 @@ public: virtual bool getFunctionName(char *bf, size_t ln, unw_word_t *off) = 0; virtual void setInfoBasedOnIPRegister(bool isReturnAddr = false) = 0; virtual const char *getRegisterName(int num) = 0; +#if __arm__ + virtual void saveVFPAsX() = 0; +#endif }; @@ -395,8 +400,8 @@ public: virtual unw_word_t getReg(int); virtual void setReg(int, unw_word_t); virtual bool validFloatReg(int); - virtual double getFloatReg(int); - virtual void setFloatReg(int, double); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); virtual int step(); virtual void getInfo(unw_proc_info_t *); virtual void jumpto(); @@ -404,11 +409,18 @@ public: virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); virtual const char *getRegisterName(int num); +#if __arm__ + virtual void saveVFPAsX(); +#endif void operator delete(void *, size_t) {} private: +#if LIBCXXABI_ARM_EHABI + bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections §s); +#endif + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint=0); @@ -486,7 +498,9 @@ private: } return false; } +#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND compact_unwind_encoding_t dwarfEncoding() const { R dummy; return dwarfEncoding(dummy); @@ -507,7 +521,7 @@ private: compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const { return UNWIND_ARM64_MODE_DWARF; } -#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND +#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND A &_addressSpace; @@ -524,14 +538,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) _isSignalFrame(false) { static_assert(sizeof(UnwindCursor<A, R>) < sizeof(unw_cursor_t), "UnwindCursor<> does not fit in unw_cursor_t"); - - bzero(&_info, sizeof(_info)); + memset(&_info, 0, sizeof(_info)); } template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(A &as, void *) : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { - bzero(&_info, sizeof(_info)); + memset(&_info, 0, sizeof(_info)); // FIXME // fill in _registers from thread arg } @@ -558,12 +571,12 @@ bool UnwindCursor<A, R>::validFloatReg(int regNum) { } template <typename A, typename R> -double UnwindCursor<A, R>::getFloatReg(int regNum) { +unw_fpreg_t UnwindCursor<A, R>::getFloatReg(int regNum) { return _registers.getFloatRegister(regNum); } template <typename A, typename R> -void UnwindCursor<A, R>::setFloatReg(int regNum, double value) { +void UnwindCursor<A, R>::setFloatReg(int regNum, unw_fpreg_t value) { _registers.setFloatRegister(regNum, value); } @@ -571,6 +584,12 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() { _registers.jumpto(); } +#if __arm__ +template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() { + _registers.saveVFPAsX(); +} +#endif + template <typename A, typename R> const char *UnwindCursor<A, R>::getRegisterName(int regNum) { return _registers.getRegisterName(regNum); @@ -580,6 +599,212 @@ template <typename A, typename R> bool UnwindCursor<A, R>::isSignalFrame() { return _isSignalFrame; } +#if LIBCXXABI_ARM_EHABI +struct EHABIIndexEntry { + uint32_t functionOffset; + uint32_t data; +}; + +// Unable to unwind in the ARM index table (section 5 EHABI). +#define UNW_EXIDX_CANTUNWIND 0x1 + +static inline uint32_t signExtendPrel31(uint32_t data) { + return data | ((data & 0x40000000u) << 1); +} + +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr0( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr1( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); +extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr2( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +template<typename A> +struct EHABISectionIterator { + typedef EHABISectionIterator _Self; + + typedef std::random_access_iterator_tag iterator_category; + typedef typename A::pint_t value_type; + typedef typename A::pint_t* pointer; + typedef typename A::pint_t& reference; + typedef size_t size_type; + typedef size_t difference_type; + + static _Self begin(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, 0); + } + static _Self end(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, sects.arm_section_length); + } + + EHABISectionIterator(A& addressSpace, const UnwindInfoSections& sects, size_t i) + : _addressSpace(&addressSpace), _sects(§s), _i(i) {} + + _Self& operator++() { ++_i; return *this; } + _Self& operator+=(size_t a) { _i += a; return *this; } + _Self& operator--() { assert(_i > 0); --_i; return *this; } + _Self& operator-=(size_t a) { assert(_i >= a); _i -= a; return *this; } + + _Self operator+(size_t a) { _Self out = *this; out._i += a; return out; } + _Self operator-(size_t a) { assert(_i >= a); _Self out = *this; out._i -= a; return out; } + + size_t operator-(const _Self& other) { return _i - other._i; } + + bool operator==(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i == other._i; + } + + typename A::pint_t operator*() const { return functionAddress(); } + + typename A::pint_t functionAddress() const { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, functionOffset); + return indexAddr + signExtendPrel31(_addressSpace->get32(indexAddr)); + } + + typename A::pint_t dataAddress() { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, data); + return indexAddr; + } + + private: + size_t _i; + A* _addressSpace; + const UnwindInfoSections* _sects; +}; + +template <typename A, typename R> +bool UnwindCursor<A, R>::getInfoFromEHABISection( + pint_t pc, + const UnwindInfoSections §s) { + EHABISectionIterator<A> begin = + EHABISectionIterator<A>::begin(_addressSpace, sects); + EHABISectionIterator<A> end = + EHABISectionIterator<A>::end(_addressSpace, sects); + + EHABISectionIterator<A> itNextPC = std::upper_bound(begin, end, pc); + if (itNextPC == begin || itNextPC == end) + return false; + EHABISectionIterator<A> itThisPC = itNextPC - 1; + + pint_t thisPC = itThisPC.functionAddress(); + pint_t nextPC = itNextPC.functionAddress(); + pint_t indexDataAddr = itThisPC.dataAddress(); + + if (indexDataAddr == 0) + return false; + + uint32_t indexData = _addressSpace.get32(indexDataAddr); + if (indexData == UNW_EXIDX_CANTUNWIND) + return false; + + // If the high bit is set, the exception handling table entry is inline inside + // the index table entry on the second word (aka |indexDataAddr|). Otherwise, + // the table points at an offset in the exception handling table (section 5 EHABI). + pint_t exceptionTableAddr; + uint32_t exceptionTableData; + bool isSingleWordEHT; + if (indexData & 0x80000000) { + exceptionTableAddr = indexDataAddr; + // TODO(ajwong): Should this data be 0? + exceptionTableData = indexData; + isSingleWordEHT = true; + } else { + exceptionTableAddr = indexDataAddr + signExtendPrel31(indexData); + exceptionTableData = _addressSpace.get32(exceptionTableAddr); + isSingleWordEHT = false; + } + + // Now we know the 3 things: + // exceptionTableAddr -- exception handler table entry. + // exceptionTableData -- the data inside the first word of the eht entry. + // isSingleWordEHT -- whether the entry is in the index. + unw_word_t personalityRoutine = 0xbadf00d; + bool scope32 = false; + uintptr_t lsda = 0xbadf00d; + + // If the high bit in the exception handling table entry is set, the entry is + // in compact form (section 6.3 EHABI). + if (exceptionTableData & 0x80000000) { + // Grab the index of the personality routine from the compact form. + int choice = (exceptionTableData & 0x0f000000) >> 24; + int extraWords = 0; + switch (choice) { + case 0: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0; + extraWords = 0; + scope32 = false; + break; + case 1: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = false; + break; + case 2: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = true; + break; + default: + _LIBUNWIND_ABORT("unknown personality routine"); + return false; + } + + if (isSingleWordEHT) { + if (extraWords != 0) { + _LIBUNWIND_ABORT("index inlined table detected but pr function " + "requires extra words"); + return false; + } + } + } else { + pint_t personalityAddr = + exceptionTableAddr + signExtendPrel31(exceptionTableData); + personalityRoutine = personalityAddr; + + // ARM EHABI # 6.2, # 9.2 + // + // +---- ehtp + // v + // +--------------------------------------+ + // | +--------+--------+--------+-------+ | + // | |0| prel31 to personalityRoutine | | + // | +--------+--------+--------+-------+ | + // | | N | unwind opcodes | | <-- UnwindData + // | +--------+--------+--------+-------+ | + // | | Word 2 unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | ... | + // | +--------+--------+--------+-------+ | + // | | Word N unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | | LSDA | | <-- lsda + // | | ... | | + // | +--------+--------+--------+-------+ | + // +--------------------------------------+ + + uint32_t *UnwindData = reinterpret_cast<uint32_t*>(exceptionTableAddr) + 1; + uint32_t FirstDataWord = *UnwindData; + size_t N = ((FirstDataWord >> 24) & 0xff); + size_t NDataWords = N + 1; + lsda = reinterpret_cast<uintptr_t>(UnwindData + NDataWords); + } + + _info.start_ip = thisPC; + _info.end_ip = nextPC; + _info.handler = personalityRoutine; + _info.unwind_info = exceptionTableAddr; + _info.lsda = lsda; + // flags is pr_cache.additional. See EHABI #7.2 for definition of bit 0. + _info.flags = isSingleWordEHT ? 1 : 0 | scope32 ? 0x2 : 0; // Use enum? + + return true; +} +#endif + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND template <typename A, typename R> bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc, @@ -913,6 +1138,11 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc, template <typename A, typename R> void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { pint_t pc = (pint_t)this->getReg(UNW_REG_IP); +#if LIBCXXABI_ARM_EHABI + // Remove the thumb bit so the IP represents the actual instruction address. + // This matches the behaviour of _Unwind_GetIP on arm. + pc &= (pint_t)~0x1; +#endif // If the last line of a function is a "throw" the compiler sometimes // emits no instructions after the call to __cxa_throw. This means @@ -957,6 +1187,12 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { } } #endif + +#if LIBCXXABI_ARM_EHABI + // If there is ARM EHABI unwind info, look there next. + if (sects.arm_section != 0 && this->getInfoFromEHABISection(pc, sects)) + return; +#endif } #if _LIBUNWIND_SUPPORT_DWARF_UNWIND @@ -1026,7 +1262,7 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { template <typename A, typename R> int UnwindCursor<A, R>::step() { - // Bottom of stack is defined is when no unwind info cannot be found. + // Bottom of stack is defined is when unwind info cannot be found. if (_unwindInfoMissing) return UNW_STEP_END; @@ -1036,8 +1272,12 @@ int UnwindCursor<A, R>::step() { result = this->stepWithCompactEncoding(); #elif _LIBUNWIND_SUPPORT_DWARF_UNWIND result = this->stepWithDwarfFDE(); +#elif LIBCXXABI_ARM_EHABI + result = UNW_STEP_SUCCESS; #else - #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or _LIBUNWIND_SUPPORT_DWARF_UNWIND + #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \ + _LIBUNWIND_SUPPORT_DWARF_UNWIND or \ + LIBCXXABI_ARM_EHABI #endif // update info based on new PC diff --git a/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c b/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c index c84db76c53f..745c30922c4 100644 --- a/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c +++ b/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c @@ -24,10 +24,21 @@ /// Called by __cxa_rethrow(). _LIBUNWIND_EXPORT _Unwind_Reason_Code -_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { +_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), " - "private_1=%ld\n", - exception_object, exception_object->private_1); + "private_1=%ld\n", + exception_object, +#if LIBCXXABI_ARM_EHABI + (long)exception_object->unwinder_cache.reserved1); +#else + (long)exception_object->private_1); +#endif + +#if LIBCXXABI_ARM_EHABI + // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0, + // which is in the same position as private_1 below. + return _Unwind_RaiseException(exception_object); +#else // If this is non-forced and a stopping place was found, then this is a // re-throw. // Call _Unwind_RaiseException() as if this was a new exception @@ -42,6 +53,7 @@ _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { _Unwind_Resume(exception_object); _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()" " which unexpectedly returned"); +#endif } @@ -116,7 +128,8 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { unw_get_proc_info(&cursor, &frameInfo); _LIBUNWIND_TRACE_UNWINDING( " _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n", - frameInfo.start_ip, functionName, frameInfo.lsda, &cursor); + (long long)frameInfo.start_ip, functionName, (long long)frameInfo.lsda, + &cursor); } // call trace function with this frame diff --git a/libcxxabi/src/Unwind/UnwindLevel1.c b/libcxxabi/src/Unwind/UnwindLevel1.c index b2e93c7e8bc..2bb3eb83c28 100644 --- a/libcxxabi/src/Unwind/UnwindLevel1.c +++ b/libcxxabi/src/Unwind/UnwindLevel1.c @@ -22,10 +22,10 @@ #include "unwind.h" #include "config.h" -#if _LIBUNWIND_BUILD_ZERO_COST_APIS +#if _LIBUNWIND_BUILD_ZERO_COST_APIS && !LIBCXXABI_ARM_EHABI static _Unwind_Reason_Code -unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) { +unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) { unw_cursor_t cursor1; unw_init_local(&cursor1, uc); @@ -117,7 +117,7 @@ unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) { static _Unwind_Reason_Code -unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) { +unwind_phase2(unw_context_t *uc, _Unwind_Exception *exception_object) { unw_cursor_t cursor2; unw_init_local(&cursor2, uc); @@ -187,7 +187,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) { if (sp == exception_object->private_2) { // Phase 1 said we would stop at this frame, but we did not... _LIBUNWIND_ABORT("during phase1 personality function said it would " - "stop here, but now if phase2 it did not stop here"); + "stop here, but now in phase2 it did not stop here"); } break; case _URC_INSTALL_CONTEXT: @@ -202,7 +202,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) { unw_get_reg(&cursor2, UNW_REG_SP, &sp); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " "user code with ip=0x%llX, sp=0x%llX\n", - exception_object, pc, sp); + exception_object, pc, sp); } unw_resume(&cursor2); // unw_resume() only returns if there was an error. @@ -223,7 +223,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) { static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t *uc, - struct _Unwind_Exception *exception_object, + _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter) { unw_cursor_t cursor2; unw_init_local(&cursor2, uc); @@ -237,7 +237,7 @@ unwind_phase2_forced(unw_context_t *uc, _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step " "failed => _URC_END_OF_STACK\n", exception_object); - return _URC_FATAL_PHASE1_ERROR; + return _URC_FATAL_PHASE2_ERROR; } // When tracing, print state information. @@ -324,7 +324,7 @@ unwind_phase2_forced(unw_context_t *uc, /// Called by __cxa_throw. Only returns if there is a fatal error. _LIBUNWIND_EXPORT _Unwind_Reason_Code -_Unwind_RaiseException(struct _Unwind_Exception *exception_object) { +_Unwind_RaiseException(_Unwind_Exception *exception_object) { _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)\n", exception_object); unw_context_t uc; @@ -358,7 +358,7 @@ _Unwind_RaiseException(struct _Unwind_Exception *exception_object) { /// is implemented by having the code call __cxa_rethrow() which /// in turn calls _Unwind_Resume_or_Rethrow(). _LIBUNWIND_EXPORT void -_Unwind_Resume(struct _Unwind_Exception *exception_object) { +_Unwind_Resume(_Unwind_Exception *exception_object) { _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)\n", exception_object); unw_context_t uc; unw_getcontext(&uc); @@ -380,7 +380,7 @@ _Unwind_Resume(struct _Unwind_Exception *exception_object) { /// Unwinds stack, calling "stop" function at each frame. /// Could be used to implement longjmp(). _LIBUNWIND_EXPORT _Unwind_Reason_Code -_Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object, +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter) { _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", exception_object, stop); @@ -484,7 +484,7 @@ _Unwind_GetRegionStart(struct _Unwind_Context *context) { /// Called by personality handler during phase 2 if a foreign exception // is caught. _LIBUNWIND_EXPORT void -_Unwind_DeleteException(struct _Unwind_Exception *exception_object) { +_Unwind_DeleteException(_Unwind_Exception *exception_object) { _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object); if (exception_object->exception_cleanup != NULL) @@ -492,4 +492,4 @@ _Unwind_DeleteException(struct _Unwind_Exception *exception_object) { exception_object); } -#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS +#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS && !LIBCXXABI_ARM_EHABI diff --git a/libcxxabi/src/Unwind/UnwindRegistersRestore.S b/libcxxabi/src/Unwind/UnwindRegistersRestore.S index cf36e7d1a45..1feb1d535fd 100644 --- a/libcxxabi/src/Unwind/UnwindRegistersRestore.S +++ b/libcxxabi/src/Unwind/UnwindRegistersRestore.S @@ -311,13 +311,13 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind15Registers_arm646jumptoEv) #elif __arm__ @ -@ void libunwind::Registers_arm::jumpto() +@ void libunwind::Registers_arm::restoreCoreAndJumpTo() @ @ On entry: @ thread_state pointer is in r0 @ .p2align 2 -DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm6jumptoEv) +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv) @ Use lr as base so that r0 can be restored. mov lr, r0 @ 32bit thumb-2 restrictions for ldm: @@ -326,6 +326,90 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm6jumptoEv) ldm lr, {r0-r12} ldr sp, [lr, #52] ldr lr, [lr, #60] @ restore pc into lr +#if _ARM_ARCH > 4 + bx lr +#else + mov pc, lr +#endif + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMDEPy) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We don't want to do that in the library (because we don't + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instruction using the corresponding coprocessor mnemonic. + ldc p11, cr0, [r0], {#0x20} @ fldmiad r0, {d0-d15} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMXEPy) + ldc p11, cr0, [r0], {#0x21} @ fldmiax r0, {d0-d15} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::restoreVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreVFPv3EPy) + ldcl p11, cr0, [r0], {#0x20} @ vldm r0, {d16-d31} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::restoreiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreiWMMXEPy) + ldcl p1, cr0, [r0], #8 @ wldrd wR0, [r0], #8 + ldcl p1, cr1, [r0], #8 @ wldrd wR1, [r0], #8 + ldcl p1, cr2, [r0], #8 @ wldrd wR2, [r0], #8 + ldcl p1, cr3, [r0], #8 @ wldrd wR3, [r0], #8 + ldcl p1, cr4, [r0], #8 @ wldrd wR4, [r0], #8 + ldcl p1, cr5, [r0], #8 @ wldrd wR5, [r0], #8 + ldcl p1, cr6, [r0], #8 @ wldrd wR6, [r0], #8 + ldcl p1, cr7, [r0], #8 @ wldrd wR7, [r0], #8 + ldcl p1, cr8, [r0], #8 @ wldrd wR8, [r0], #8 + ldcl p1, cr9, [r0], #8 @ wldrd wR9, [r0], #8 + ldcl p1, cr10, [r0], #8 @ wldrd wR10, [r0], #8 + ldcl p1, cr11, [r0], #8 @ wldrd wR11, [r0], #8 + ldcl p1, cr12, [r0], #8 @ wldrd wR12, [r0], #8 + ldcl p1, cr13, [r0], #8 @ wldrd wR13, [r0], #8 + ldcl p1, cr14, [r0], #8 @ wldrd wR14, [r0], #8 + ldcl p1, cr15, [r0], #8 @ wldrd wR15, [r0], #8 + mov pc, lr + +@ +@ static void libunwind::Registers_arm::restoreiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreiWMMXControlEPj) + ldc2 p1, cr8, [r0], #4 @ wldrw wCGR0, [r0], #4 + ldc2 p1, cr9, [r0], #4 @ wldrw wCGR1, [r0], #4 + ldc2 p1, cr10, [r0], #4 @ wldrw wCGR2, [r0], #4 + ldc2 p1, cr11, [r0], #4 @ wldrw wCGR3, [r0], #4 mov pc, lr #endif diff --git a/libcxxabi/src/Unwind/UnwindRegistersSave.S b/libcxxabi/src/Unwind/UnwindRegistersSave.S index a3617ed14aa..d23c81644c9 100644 --- a/libcxxabi/src/Unwind/UnwindRegistersSave.S +++ b/libcxxabi/src/Unwind/UnwindRegistersSave.S @@ -287,6 +287,12 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) @ @ On entry: @ thread_state pointer is in r0 +@ +@ Per EHABI #4.7 this only saves the core integer registers. +@ EHABI #7.4.5 notes that in general all VRS registers should be restored +@ however this is very hard to do for VFP registers because it is unknown +@ to the lbirary how many registers are implemented by the architecture. +@ Instead, VFP registers are demand saved by logic external to unw_getcontext. @ .p2align 2 DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) @@ -297,7 +303,91 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) str sp, [r0, #52] str lr, [r0, #56] str lr, [r0, #60] @ store return address as pc - mov r0, #0 @ return UNW_ESUCCESS + mov r0, #0 @ return UNW_ESUCCESS +#if _ARM_ARCH > 4 + bx lr +#else + mov pc, lr +#endif + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPy) + stc p11, cr0, [r0], {#0x20} @ fstmiad r0, {d0-d15} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPy) + stc p11, cr0, [r0], {#0x21} @ fstmiax r0, {d0-d15} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPy) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We don't want to do that in the library (because we don't + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instructions using the corresponding coprocessor mnemonic. + stcl p11, cr0, [r0], {#0x20} @ vldm r0, {d16-d31} + mov pc, lr + +@ +@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPy) + stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8 + stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8 + stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8 + stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8 + stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8 + stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8 + stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8 + stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8 + stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8 + stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8 + stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8 + stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8 + stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8 + stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8 + stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8 + stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8 + mov pc, lr + +@ +@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj) + stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4 + stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4 + stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4 + stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4 mov pc, lr #endif diff --git a/libcxxabi/src/Unwind/libunwind.cpp b/libcxxabi/src/Unwind/libunwind.cpp index 03d7966f310..b80beaf2308 100644 --- a/libcxxabi/src/Unwind/libunwind.cpp +++ b/libcxxabi/src/Unwind/libunwind.cpp @@ -12,11 +12,16 @@ #include <libunwind.h> +#ifndef NDEBUG +#include <cstdlib> // getenv +#endif #include <new> #include "libunwind_ext.h" #include "config.h" +#include <stdlib.h> + #if _LIBUNWIND_BUILD_ZERO_COST_APIS @@ -50,6 +55,9 @@ _LIBUNWIND_EXPORT int unw_init_local(unw_cursor_t *cursor, #elif __arm64__ new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm64>( context, LocalAddressSpace::sThisAddressSpace); +#elif LIBCXXABI_ARM_EHABI + new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm>( + context, LocalAddressSpace::sThisAddressSpace); #endif AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; co->setInfoBasedOnIPRegister(); @@ -161,7 +169,7 @@ _LIBUNWIND_EXPORT int unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, _LIBUNWIND_EXPORT int unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, unw_word_t value) { _LIBUNWIND_TRACE_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", - cursor, regNum, value); + cursor, regNum, (long long)value); typedef LocalAddressSpace::pint_t pint_t; AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; if (co->validReg(regNum)) { @@ -193,8 +201,13 @@ _LIBUNWIND_EXPORT int unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, /// Set value of specified float register at cursor position in stack frame. _LIBUNWIND_EXPORT int unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, unw_fpreg_t value) { +#if LIBCXXABI_ARM_EHABI + _LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)\n", + cursor, regNum, value); +#else _LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", - cursor, regNum, value); + cursor, regNum, value); +#endif AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; if (co->validFloatReg(regNum)) { co->setFloatReg(regNum, value); @@ -239,7 +252,7 @@ _LIBUNWIND_EXPORT int unw_resume(unw_cursor_t *cursor) { _LIBUNWIND_EXPORT int unw_get_proc_name(unw_cursor_t *cursor, char *buf, size_t bufLen, unw_word_t *offset) { _LIBUNWIND_TRACE_API("unw_get_proc_name(cursor=%p, &buf=%p," - "bufLen=%ld)\n", cursor, buf, bufLen); + "bufLen=%zu)\n", cursor, buf, bufLen); AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; if (co->getFunctionName(buf, bufLen, offset)) return UNW_ESUCCESS; @@ -274,6 +287,15 @@ _LIBUNWIND_EXPORT int unw_is_signal_frame(unw_cursor_t *cursor) { return co->isSignalFrame(); } +#if __arm__ +// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD +_LIBUNWIND_EXPORT void unw_save_vfp_as_X(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("unw_fpreg_save_vfp_as_X(cursor=%p)\n", cursor); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->saveVFPAsX(); +} +#endif + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND /// SPI: walks cached dwarf entries |