diff options
author | Alon Zakai <alonzakai@gmail.com> | 2014-01-13 16:55:19 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2014-01-13 16:56:03 -0800 |
commit | cd1edebb5034ea52396a5b68304e84ae80878740 (patch) | |
tree | 6a6f8364ea74985d11c4f6ff74e5b80e08dc849d /system/lib/libcxxabi/src/Unwind | |
parent | af59788f8b7b76515e36bee1bf66edf497b801db (diff) | |
parent | 2914deb17f3857bb02eeec87a58a3ed6d4a8853a (diff) |
Merge branch 'incoming' of github.com:kripken/emscripten into incoming1.8.8
conflicts: tests/test_core.py tools/shared.py
Diffstat (limited to 'system/lib/libcxxabi/src/Unwind')
18 files changed, 8238 insertions, 0 deletions
diff --git a/system/lib/libcxxabi/src/Unwind/AddressSpace.hpp b/system/lib/libcxxabi/src/Unwind/AddressSpace.hpp new file mode 100644 index 00000000..67b0973d --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/AddressSpace.hpp @@ -0,0 +1,430 @@ +//===------------------------- AddressSpace.hpp ---------------------------===// +// +// 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. +// +// +// Abstracts accessing local vs remote address spaces. +// +//===----------------------------------------------------------------------===// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +#if __APPLE__ +#include <mach-o/getsect.h> +namespace libunwind { + bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde); +} +#endif + +#include "libunwind.h" +#include "config.h" +#include "dwarf2.h" +#include "Registers.hpp" + +namespace libunwind { + +/// Used by findUnwindSections() to return info about needed sections. +struct UnwindInfoSections { + uintptr_t dso_base; +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + uintptr_t dwarf_section; + uintptr_t dwarf_section_length; +#endif +#if _LIBUNWIND_SUPPORT_DWARF_INDEX + uintptr_t dwarf_index_section; + uintptr_t dwarf_index_section_length; +#endif +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + uintptr_t compact_unwind_section; + uintptr_t compact_unwind_section_length; +#endif +}; + + +/// LocalAddressSpace is used as a template parameter to UnwindCursor when +/// unwinding a thread in the same process. The wrappers compile away, +/// making local unwinds fast. +class __attribute__((visibility("hidden"))) LocalAddressSpace { +public: +#if __LP64__ + typedef uint64_t pint_t; + typedef int64_t sint_t; +#else + typedef uint32_t pint_t; + typedef int32_t sint_t; +#endif + uint8_t get8(pint_t addr) { return *((uint8_t *)addr); } + uint16_t get16(pint_t addr) { return *((uint16_t *)addr); } + uint32_t get32(pint_t addr) { return *((uint32_t *)addr); } + uint64_t get64(pint_t addr) { return *((uint64_t *)addr); } + double getDouble(pint_t addr) { return *((double *)addr); } + v128 getVector(pint_t addr) { return *((v128 *)addr); } + uintptr_t getP(pint_t addr); + static uint64_t getULEB128(pint_t &addr, pint_t end); + static int64_t getSLEB128(pint_t &addr, pint_t end); + + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char *buf, size_t bufLen, + unw_word_t *offset); + bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); + bool findOtherFDE(pint_t targetAddr, pint_t &fde); + + static LocalAddressSpace sThisAddressSpace; +}; + + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) { +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif +} + +/// Read a ULEB128 into a 64-bit word. +inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if (p == pend) + _LIBUNWIND_ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + _LIBUNWIND_ABORT("malformed uleb128 expression"); + } else { + result |= b << bit; + bit += 7; + } + } while (*p++ >= 0x80); + addr = (pint_t) p; + return result; +} + +/// Read a SLEB128 into a 64-bit word. +inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == pend) + _LIBUNWIND_ABORT("truncated sleb128 expression"); + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ((byte & 0x40) != 0) + result |= (-1LL) << bit; + addr = (pint_t) p; + return result; +} + +inline LocalAddressSpace::pint_t LocalAddressSpace::getEncodedP(pint_t &addr, + pint_t end, + uint8_t encoding) { + pint_t startAddr = addr; + const uint8_t *p = (uint8_t *)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t) p; + break; + case DW_EH_PE_uleb128: + result = (pint_t)getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_udata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + case DW_EH_PE_sleb128: + result = (pint_t)getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (uint16_t)get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata4: + result = (uint32_t)get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + _LIBUNWIND_ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + break; + } + + if (encoding & DW_EH_PE_indirect) + result = getP(result); + + return result; +} + +#if __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; + }; + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \ + && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) + // In 10.7.0 or later, libSystem.dylib implements this function. + extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *); + #else + // In 10.6.x and earlier, we need to implement this functionality. + static inline bool _dyld_find_unwind_sections(void* addr, + dyld_unwind_sections* info) { + // Find mach-o image containing address. + Dl_info dlinfo; + if (!dladdr(addr, &dlinfo)) + return false; + const mach_header *mh = (const mach_header *)dlinfo.dli_saddr; + + // Find dwarf unwind section in that image. + unsigned long size; + const uint8_t *p = getsectiondata(mh, "__TEXT", "__eh_frame", &size); + if (!p) + return false; + + // Fill in return struct. + info->mh = mh; + info->dwarf_section = p; + info->dwarf_section_length = size; + info->compact_unwind_section = 0; + info->compact_unwind_section_length = 0; + + return true; + } + #endif +#endif + +inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, + UnwindInfoSections &info) { +#if __APPLE__ + dyld_unwind_sections dyldInfo; + if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) { + info.dso_base = (uintptr_t)dyldInfo.mh; + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND + info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; + info.dwarf_section_length = dyldInfo.dwarf_section_length; + #endif + info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section; + info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length; + return true; + } +#else + // TO DO + +#endif + + return false; +} + + +inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) { +#if __APPLE__ + return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde)); +#else + // TO DO: if OS has way to dynamically register FDEs, check that. + return false; +#endif +} + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, + size_t bufLen, + unw_word_t *offset) { + dl_info dyldInfo; + if (dladdr((void *)addr, &dyldInfo)) { + if (dyldInfo.dli_sname != NULL) { + strlcpy(buf, dyldInfo.dli_sname, bufLen); + *offset = (addr - (pint_t) dyldInfo.dli_saddr); + return true; + } + } + return false; +} + + + +#if UNW_REMOTE + +/// OtherAddressSpace is used as a template parameter to UnwindCursor when +/// unwinding a thread in the another process. The other process can be a +/// different endianness and a different pointer size which is handled by +/// the P template parameter. +template <typename P> +class OtherAddressSpace { +public: + OtherAddressSpace(task_t task) : fTask(task) {} + + typedef typename P::uint_t pint_t; + + uint8_t get8(pint_t addr); + uint16_t get16(pint_t addr); + uint32_t get32(pint_t addr); + uint64_t get64(pint_t addr); + pint_t getP(pint_t addr); + uint64_t getULEB128(pint_t &addr, pint_t end); + int64_t getSLEB128(pint_t &addr, pint_t end); + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char *buf, size_t bufLen, + unw_word_t *offset); + bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); + bool findOtherFDE(pint_t targetAddr, pint_t &fde); +private: + void *localCopy(pint_t addr); + + task_t fTask; +}; + +template <typename P> uint8_t OtherAddressSpace<P>::get8(pint_t addr) { + return *((uint8_t *)localCopy(addr)); +} + +template <typename P> uint16_t OtherAddressSpace<P>::get16(pint_t addr) { + return P::E::get16(*(uint16_t *)localCopy(addr)); +} + +template <typename P> uint32_t OtherAddressSpace<P>::get32(pint_t addr) { + return P::E::get32(*(uint32_t *)localCopy(addr)); +} + +template <typename P> uint64_t OtherAddressSpace<P>::get64(pint_t addr) { + return P::E::get64(*(uint64_t *)localCopy(addr)); +} + +template <typename P> +typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr) { + return P::getP(*(uint64_t *)localCopy(addr)); +} + +template <typename P> +uint64_t OtherAddressSpace<P>::getULEB128(pint_t &addr, pint_t end) { + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr + size); + addr += (laddr - sladdr); + return result; +} + +template <typename P> +int64_t OtherAddressSpace<P>::getSLEB128(pint_t &addr, pint_t end) { + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr + size); + addr += (laddr - sladdr); + return result; +} + +template <typename P> void *OtherAddressSpace<P>::localCopy(pint_t addr) { + // FIX ME +} + +template <typename P> +bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char *buf, + size_t bufLen, unw_word_t *offset) { + // FIX ME +} + +/// unw_addr_space is the base class that abstract unw_addr_space_t type in +/// libunwind.h points to. +struct unw_addr_space { + cpu_type_t cpuType; + task_t taskPort; +}; + +/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points +/// to when examining +/// a 32-bit intel process. +struct unw_addr_space_i386 : public unw_addr_space { + unw_addr_space_i386(task_t task) : oas(task) {} + OtherAddressSpace<Pointer32<LittleEndian> > oas; +}; + +/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t +/// points to when examining +/// a 64-bit intel process. +struct unw_addr_space_x86_64 : public unw_addr_space { + unw_addr_space_x86_64(task_t task) : oas(task) {} + OtherAddressSpace<Pointer64<LittleEndian> > oas; +}; + +/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points +/// to when examining +/// a 32-bit PowerPC process. +struct unw_addr_space_ppc : public unw_addr_space { + unw_addr_space_ppc(task_t task) : oas(task) {} + OtherAddressSpace<Pointer32<BigEndian> > oas; +}; + +#endif // UNW_REMOTE + +} // namespace libunwind + +#endif // __ADDRESSSPACE_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/CompactUnwinder.hpp b/system/lib/libcxxabi/src/Unwind/CompactUnwinder.hpp new file mode 100644 index 00000000..0dc187f1 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/CompactUnwinder.hpp @@ -0,0 +1,693 @@ +//===-------------------------- CompactUnwinder.hpp -----------------------===// +// +// 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. +// +// +// Does runtime stack unwinding using compact unwind encodings. +// +//===----------------------------------------------------------------------===// + +#ifndef __COMPACT_UNWINDER_HPP__ +#define __COMPACT_UNWINDER_HPP__ + +#include <stdint.h> +#include <stdlib.h> + +#include <libunwind.h> +#include <mach-o/compact_unwind_encoding.h> + +#include "AddressSpace.hpp" +#include "Registers.hpp" + +#define EXTRACT_BITS(value, mask) \ + ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) + +namespace libunwind { + +/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86 register set +template <typename A> +class CompactUnwinder_x86 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86 ®isters); + static void framelessUnwind(A &addressSpace, + typename A::pint_t returnAddressLocation, + Registers_x86 ®isters); + static int + stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize); +}; + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + switch (compactEncoding & UNWIND_X86_MODE_MASK) { + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for " + "function starting at 0x%X\n", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 4; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 4 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%X\n", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template <typename A> +void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace, + Registers_x86 ®isters) { + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp)); + // old esp is ebp less saved ebp and return address + registers.setSP((uint32_t)bp + 8); + // pop return address into eip + registers.setIP(addressSpace.get32(bp + 4)); +} + +template <typename A> +void CompactUnwinder_x86<A>::framelessUnwind( + A &addressSpace, typename A::pint_t returnAddressLocation, + Registers_x86 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation)); + // old esp is before return address + registers.setSP((uint32_t)returnAddressLocation + 4); +} + + +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86_64 register set +template <typename A> +class CompactUnwinder_x86_64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters); + static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation, + Registers_x86_64 ®isters); + static int + stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize); +}; + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for " + "function starting at 0x%llX\n", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 8; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 8 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%llX\n", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template <typename A> +void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace, + Registers_x86_64 ®isters) { + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp)); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp + 16); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp + 8)); +} + +template <typename A> +void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace, + uint64_t returnAddressLocation, + Registers_x86_64 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation + 8); +} + + + +/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_arm64 register set +template <typename A> +class CompactUnwinder_arm64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + +private: + typename A::pint_t pint_t; + + static int + stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters); +}; + +template <typename A> +int CompactUnwinder_arm64<A>::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters) { + switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { + case UNWIND_ARM64_MODE_FRAME: + return stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_ARM64_MODE_FRAMELESS: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template <typename A> +int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint32_t stackSize = + 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + + uint64_t savedRegisterLoc = registers.getSP() + stackSize; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_ARM64_D8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_ARM64_D10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_ARM64_D12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_ARM64_D14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + // subtract stack size off of sp + registers.setSP(savedRegisterLoc); + + // set pc to be value in lr + registers.setIP(registers.getRegister(UNW_ARM64_LR)); + + return UNW_STEP_SUCCESS; +} + +template <typename A> +int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint64_t savedRegisterLoc = registers.getFP() - 8; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_ARM64_D8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_ARM64_D10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_ARM64_D12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_ARM64_D14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_ARM64_D15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + uint64_t fp = registers.getFP(); + // fp points to old fp + registers.setFP(addressSpace.get64(fp)); + // old sp is fp less saved fp and lr + registers.setSP(fp + 16); + // pop return address into pc + registers.setIP(addressSpace.get64(fp + 8)); + + return UNW_STEP_SUCCESS; +} + + +}; // namespace libunwind + +#endif // __COMPACT_UNWINDER_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/DwarfInstructions.hpp b/system/lib/libcxxabi/src/Unwind/DwarfInstructions.hpp new file mode 100644 index 00000000..158bad87 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/DwarfInstructions.hpp @@ -0,0 +1,888 @@ +//===-------------------------- DwarfInstructions.hpp ---------------------===// +// +// 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. +// +// +// Processor specific interpretation of dwarf unwind info. +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "config.h" + + +namespace libunwind { + + +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular +/// architecture +template <typename A, typename R> +class DwarfInstructions { +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, + R ®isters); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + typedef typename CFI_Parser<A>::RegisterLocation RegisterLocation; + typedef typename CFI_Parser<A>::PrologInfo PrologInfo; + typedef typename CFI_Parser<A>::FDE_Info FDE_Info; + typedef typename CFI_Parser<A>::CIE_Info CIE_Info; + + static pint_t evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue); + static pint_t getSavedRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static double getSavedFloatRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static v128 getSavedVectorRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + + // x86 specific variants + static int lastRestoreReg(const Registers_x86 &); + static bool isReturnAddressRegister(int regNum, const Registers_x86 &); + static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, + const Registers_x86 &); + + // x86_64 specific variants + static int lastRestoreReg(const Registers_x86_64 &); + static bool isReturnAddressRegister(int regNum, const Registers_x86_64 &); + static pint_t getCFA(A &addressSpace, + const PrologInfo &prolog, + const Registers_x86_64 &); + + // ppc specific variants + static int lastRestoreReg(const Registers_ppc &); + static bool isReturnAddressRegister(int regNum, const Registers_ppc &); + static pint_t getCFA(A &addressSpace, + const PrologInfo &prolog, + const Registers_ppc &); + + // arm64 specific variants + static bool isReturnAddressRegister(int regNum, const Registers_arm64 &); + static int lastRestoreReg(const Registers_arm64 &); + static pint_t getCFA(A &addressSpace, + const PrologInfo &prolog, + const Registers_arm64 &); + +}; + + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A, R>::getSavedRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getP(cfa + (pint_t)savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getP( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + return evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa); + + case CFI_Parser<A>::kRegisterInRegister: + return registers.getRegister((int)savedReg.value); + + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for register"); +} + +template <typename A, typename R> +double DwarfInstructions<A, R>::getSavedFloatRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getDouble(cfa + (pint_t)savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getDouble( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + case CFI_Parser<A>::kRegisterInRegister: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for float register"); +} + +template <typename A, typename R> +v128 DwarfInstructions<A, R>::getSavedVectorRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getVector(cfa + (pint_t)savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getVector( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + case CFI_Parser<A>::kRegisterInRegister: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for vector register"); +} + +template <typename A, typename R> +int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc, + pint_t fdeStart, R ®isters) { + FDE_Info fdeInfo; + CIE_Info cieInfo; + if (CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, + &fdeInfo, &cieInfo) == NULL) { + PrologInfo prolog; + if (CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, + &prolog)) { + R newRegisters = registers; + + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that dwarf says were saved + pint_t returnAddress = 0; + for (int i = 0; i <= lastRestoreReg(newRegisters); ++i) { + if (prolog.savedRegisters[i].location != + CFI_Parser<A>::kRegisterUnused) { + if (registers.validFloatRegister(i)) + newRegisters.setFloatRegister( + i, getSavedFloatRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (registers.validVectorRegister(i)) + newRegisters.setVectorRegister( + i, getSavedVectorRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (isReturnAddressRegister(i, registers)) + returnAddress = getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i]); + else if (registers.validRegister(i)) + newRegisters.setRegister( + i, getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } + } + + // By definition, the CFA is the stack pointer at the call site, so + // restoring SP means setting it to CFA. + newRegisters.setSP(cfa); + + // Return address is address after call site instruction, so setting IP to + // that does simualates a return. + newRegisters.setIP(returnAddress); + + // Simulate the step by replacing the register set with the new ones. + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + +template <typename A, typename R> +typename A::pint_t +DwarfInstructions<A, R>::evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue) { + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression + 20; // temp, until len read + pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) + fprintf(stderr, "evaluateExpression(): length=%llu\n", (uint64_t)length); + pint_t stack[100]; + pint_t *sp = stack; + *(++sp) = initialStackValue; + + while (p < expressionEnd) { + if (log) { + for (pint_t *t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue, svalue2; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) + fprintf(stderr, "dereference 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t) addressSpace.get8(p); + p += 1; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t) addressSpace.get16(p); + p += 2; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = (pint_t)addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%llX\n", (uint64_t) svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) + fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) + fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) + fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((pint_t*)value); + if (log) + fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_abs: + svalue = (sint_t)*sp; + if (svalue < 0) + *sp = (pint_t)(-svalue); + if (log) + fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) + fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 / svalue); + if (log) + fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + value = *sp--; + *sp = *sp - value; + if (log) + fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 % svalue); + if (log) + fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 * svalue); + if (log) + fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) + fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = (sint_t)(*sp); + *sp = (pint_t)(~svalue); + if (log) + fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) + fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) + fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + if (log) + fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = (sint_t)*sp; + *sp = (pint_t)(svalue >> value); + if (log) + fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) + fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "skip %lld\n", (uint64_t) svalue); + break; + + case DW_OP_bra: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + if (*sp--) + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "bra %lld\n", (uint64_t) svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) + fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) + fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) + fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) + fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) + fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) + fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = opcode - DW_OP_lit0; + *(++sp) = value; + if (log) + fprintf(stderr, "push literal 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = opcode - DW_OP_reg0; + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = (uint32_t)addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t) svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = opcode - DW_OP_breg0; + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += registers.getRegister((int)reg); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t) svalue); + break; + + case DW_OP_bregx: + reg = (uint32_t)addressSpace.getULEB128(p, expressionEnd); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += registers.getRegister((int)reg); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t) svalue); + break; + + case DW_OP_fbreg: + _LIBUNWIND_ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + _LIBUNWIND_ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch (addressSpace.get8(p++)) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = (pint_t)addressSpace.get64(value); + break; + default: + _LIBUNWIND_ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) + fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t) value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + _LIBUNWIND_ABORT("dwarf opcode not implemented"); + } + + } + if (log) + fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t) * sp); + return *sp; +} + +// +// x86_64 specific functions +// +template <typename A, typename R> +int DwarfInstructions<A, R>::lastRestoreReg(const Registers_x86_64 &) { + static_assert((int)CFI_Parser<A>::kMaxRegisterNumber + > (int)DW_X86_64_RET_ADDR, "register number out of range"); + return DW_X86_64_RET_ADDR; +} + +template <typename A, typename R> +bool +DwarfInstructions<A, R>::isReturnAddressRegister(int regNum, + const Registers_x86_64 &) { + return (regNum == DW_X86_64_RET_ADDR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A, R>::getCFA( + A &addressSpace, const PrologInfo &prolog, + const Registers_x86_64 ®isters) { + if (prolog.cfaRegister != 0) + return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + + prolog.cfaRegisterOffset); + else if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); + else + _LIBUNWIND_ABORT("getCFA(): unknown location for x86_64 cfa"); +} + + +// +// x86 specific functions +// +template <typename A, typename R> +int DwarfInstructions<A, R>::lastRestoreReg(const Registers_x86 &) { + static_assert((int)CFI_Parser<A>::kMaxRegisterNumber + > (int)DW_X86_RET_ADDR, "register number out of range"); + return DW_X86_RET_ADDR; +} + +template <typename A, typename R> +bool DwarfInstructions<A, R>::isReturnAddressRegister(int regNum, + const Registers_x86 &) { + return (regNum == DW_X86_RET_ADDR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A, R>::getCFA( + A &addressSpace, const PrologInfo &prolog, + const Registers_x86 ®isters) { + if (prolog.cfaRegister != 0) + return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + + prolog.cfaRegisterOffset); + else if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + registers, 0); + else + _LIBUNWIND_ABORT("getCFA(): unknown location for x86 cfa"); +} + + +// +// ppc specific functions +// +template <typename A, typename R> +int DwarfInstructions<A, R>::lastRestoreReg(const Registers_ppc &) { + static_assert((int)CFI_Parser<A>::kMaxRegisterNumber + > (int)UNW_PPC_SPEFSCR, "register number out of range"); + return UNW_PPC_SPEFSCR; +} + +template <typename A, typename R> +bool DwarfInstructions<A, R>::isReturnAddressRegister(int regNum, + const Registers_ppc &) { + return (regNum == UNW_PPC_LR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A, R>::getCFA( + A &addressSpace, const PrologInfo &prolog, + const Registers_ppc ®isters) { + if (prolog.cfaRegister != 0) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + registers, 0); + else + _LIBUNWIND_ABORT("getCFA(): unknown location for ppc cfa"); +} + + + +// +// arm64 specific functions +// +template <typename A, typename R> +bool DwarfInstructions<A, R>::isReturnAddressRegister(int regNum, + const Registers_arm64 &) { + return (regNum == UNW_ARM64_LR); +} + +template <typename A, typename R> +int DwarfInstructions<A, R>::lastRestoreReg(const Registers_arm64 &) { + static_assert((int)CFI_Parser<A>::kMaxRegisterNumber + > (int)UNW_ARM64_D31, "register number out of range"); + return UNW_ARM64_D31; +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A, R>::getCFA(A&, const PrologInfo &prolog, + const Registers_arm64 ®isters) { + if (prolog.cfaRegister != 0) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else + _LIBUNWIND_ABORT("getCFA(): unsupported location for arm64 cfa"); +} + + +} // namespace libunwind + +#endif // __DWARF_INSTRUCTIONS_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/DwarfParser.hpp b/system/lib/libcxxabi/src/Unwind/DwarfParser.hpp new file mode 100644 index 00000000..152baab9 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/DwarfParser.hpp @@ -0,0 +1,713 @@ +//===--------------------------- DwarfParser.hpp --------------------------===// +// +// 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. +// +// +// Parses DWARF CFIs (FDEs and CIEs). +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <vector> + +#include "libunwind.h" +#include "dwarf2.h" + +#include "AddressSpace.hpp" + +namespace libunwind { + +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See Dwarf Spec for details: +/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template <typename A> +class CFI_Parser { +public: + typedef typename A::pint_t pint_t; + + /// Information encoded in a CIE (Common Information Entry) + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + uint32_t codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + }; + + /// Information about an FDE (Frame Description Entry) + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + enum { + kMaxRegisterNumber = 120 + }; + enum RegisterSavedWhere { + kRegisterUnused, + kRegisterInCFA, + kRegisterOffsetFromCFA, + kRegisterInRegister, + kRegisterAtExpression, + kRegisterIsExpression + }; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + /// Information about a frame layout and registers saved determined + /// by "running" the dwarf FDE "instructions" + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + bool registersInOtherRegisters; + bool sameValueUsed; + RegisterLocation savedRegisters[kMaxRegisterNumber]; + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) + : next(n), info(i) {} + PrologInfoStackEntry *next; + PrologInfo info; + }; + + static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, + CIE_Info *cieInfo); + static const char *decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo); + static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + PrologInfo *results); + + static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); + +private: + static bool parseInstructions(A &addressSpace, pint_t instructions, + pint_t instructionsEnd, const CIE_Info &cieInfo, + pint_t pcoffset, + PrologInfoStackEntry *&rememberStack, + PrologInfo *results); +}; + +/// Parse a FDE into a CIE_Info and an FDE_Info +template <typename A> +const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + pint_t p = fdeStart; + pint_t cfiLength = (pint_t)addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return "FDE has zero length"; // end marker + uint32_t ciePointer = addressSpace.get32(p); + if (ciePointer == 0) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p - ciePointer; + const char *err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // parse pc begin and range + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != 0) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != + 0) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = + addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return NULL; // success +} + +/// Scan an eh_frame section to find an FDE for a pc +template <typename A> +bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uint32_t sectionLength, pint_t fdeHint, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + while (p < ehSectionEnd) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + pint_t cfiLength = addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if (id == 0) { + // skip over CIEs + p += cfiLength; + } else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p - ciePointer; + // validate pointer to CIE is within section + if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) { + if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) { + p += 4; + // parse pc begin and range + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP( + p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // test if pc is within the function this FDE covers + if ((pcStart < pc) && (pc <= pcStart + pcRange)) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != 0) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if (addressSpace.getEncodedP( + p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace + .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return true; + } else { + // pc is not in begin/range, skip this FDE + } + } else { + // malformed CIE, now augmentation describing pc range encoding + } + } else { + // malformed FDE. CIE is bad + } + p = nextCFI; + } + } + return false; +} + +/// Extract info from a CIE +template <typename A> +const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie, + CIE_Info *cieInfo) { + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = 0; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; + cieInfo->cieStart = cie; + pint_t p = cie; + pint_t cieLength = (pint_t)addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if (cieLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cieLength = (pint_t)addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if (cieLength == 0) + return NULL; + // CIE ID is always 0 + if (addressSpace.get32(p) != 0) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ((version != 1) && (version != 3)) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while (addressSpace.get8(p) != 0) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + addressSpace.getULEB128(p, cieContentEnd); + // parse augmentation data based on augmentation string + const char *result = NULL; + if (addressSpace.get8(strStart) == 'z') { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) { + switch (addressSpace.get8(s)) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); + cieInfo->personality = addressSpace + .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE +template <typename A> +bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace, + const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + PrologInfo *results) { + // clear results + bzero(results, sizeof(PrologInfo)); + PrologInfoStackEntry *rememberStack = NULL; + + // parse CIE then FDE instructions + return parseInstructions(addressSpace, cieInfo.cieInstructions, + cieInfo.cieStart + cieInfo.cieLength, cieInfo, + (pint_t)(-1), rememberStack, results) && + parseInstructions(addressSpace, fdeInfo.fdeInstructions, + fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, + upToPC - fdeInfo.pcStart, rememberStack, results); +} + +/// "run" the dwarf instructions +template <typename A> +bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, + pint_t instructionsEnd, + const CIE_Info &cieInfo, pint_t pcoffset, + PrologInfoStackEntry *&rememberStack, + PrologInfo *results) { + const bool logDwarf = false; + pint_t p = instructions; + pint_t codeOffset = 0; + PrologInfo initialState = *results; + if (logDwarf) + fprintf(stderr, "parseInstructions(instructions=0x%0llX)\n", + (uint64_t) instructionsEnd); + + // see Dwarf Spec, section 6.4.2 for details on unwind opcodes + while ((p < instructionsEnd) && (codeOffset < pcoffset)) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; + PrologInfoStackEntry *entry; + ++p; + switch (opcode) { + case DW_CFA_nop: + if (logDwarf) + fprintf(stderr, "DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = + addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); + if (logDwarf) + fprintf(stderr, "DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + if (logDwarf) + fprintf(stderr, "DW_CFA_advance_loc1: new offset=%llu\n", + (uint64_t)codeOffset); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + if (logDwarf) + fprintf(stderr, "DW_CFA_advance_loc2: new offset=%llu\n", + (uint64_t)codeOffset); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + if (logDwarf) + fprintf(stderr, "DW_CFA_advance_loc4: new offset=%llu\n", + (uint64_t)codeOffset); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, + offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + ; + if (reg > kMaxRegisterNumber) { + fprintf( + stderr, + "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if (logDwarf) + fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_undefined dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterUnused; + if (logDwarf) + fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); + return false; + } + // <rdar://problem/8456377> DW_CFA_same_value unsupported + // "same value" means register was stored in frame, but its current + // value has not changed, so no need to restore from frame. + // We model this as if the register was never saved. + results->savedRegisters[reg].location = kRegisterUnused; + // set flag to disable conversion to compact unwind + results->sameValueUsed = true; + if (logDwarf) + fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_register dwarf unwind, reg too big\n"); + return false; + } + if (reg2 > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_register dwarf unwind, reg2 too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = (int64_t)reg2; + // set flag to disable conversion to compact unwind + results->registersInOtherRegisters = true; + if (logDwarf) + fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2); + break; + case DW_CFA_remember_state: + // avoid operator new, because that would be an upward dependency + entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry)); + if (entry != NULL) { + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + } else { + return false; + } + if (logDwarf) + fprintf(stderr, "DW_CFA_remember_state\n"); + break; + case DW_CFA_restore_state: + if (rememberStack != NULL) { + PrologInfoStackEntry *top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char *)top); + } else { + return false; + } + if (logDwarf) + fprintf(stderr, "DW_CFA_restore_state\n"); + break; + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf( + stderr, + "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = (uint32_t)reg; + if (logDwarf) + fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = (int32_t) + addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; + if (logDwarf) + fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if (logDwarf) + fprintf(stderr, + "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if (logDwarf) + fprintf(stderr, + "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf( + stderr, + "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); + return false; + } + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", + reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, + offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = (int32_t) + (addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor); + results->codeOffsetAtStackDecrement = (uint32_t)codeOffset; + if (logDwarf) + fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, + offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n"); + return false; + } + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, + offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, + "malformed DW_CFA_val_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if (logDwarf) + fprintf( + stderr, + "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + length = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = (uint32_t)length; + if (logDwarf) + fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", length); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf " + "unwind, reg too big\n"); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset); + break; + default: + operand = opcode & 0x3F; + switch (opcode & 0xC0) { + case DW_CFA_offset: + reg = operand; + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) + * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if (logDwarf) + fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, + offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + if (logDwarf) + fprintf(stderr, "DW_CFA_advance_loc: new offset=%llu\n", + (uint64_t)codeOffset); + break; + case DW_CFA_restore: + reg = operand; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if (logDwarf) + fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg); + break; + default: + if (logDwarf) + fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + + return true; +} + +} // namespace libunwind + +#endif // __DWARF_PARSER_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/Registers.hpp b/system/lib/libcxxabi/src/Unwind/Registers.hpp new file mode 100644 index 00000000..584e4492 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/Registers.hpp @@ -0,0 +1,1568 @@ +//===----------------------------- Registers.hpp --------------------------===// +// +// 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. +// +// +// Models register sets for supported processors. +// +//===----------------------------------------------------------------------===// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include <stdint.h> +#include <strings.h> + +#include "libunwind.h" +#include "config.h" + +namespace libunwind { + +// For emulating 128-bit registers +struct v128 { uint32_t vec[4]; }; + + +/// Registers_x86 holds the register state of a thread in a 32-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86 { +public: + Registers_x86(); + Registers_x86(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + + uint32_t getSP() const { return _registers.__esp; } + void setSP(uint32_t value) { _registers.__esp = value; } + uint32_t getIP() const { return _registers.__eip; } + void setIP(uint32_t value) { _registers.__eip = value; } + uint32_t getEBP() const { return _registers.__ebp; } + void setEBP(uint32_t value) { _registers.__ebp = value; } + uint32_t getEBX() const { return _registers.__ebx; } + void setEBX(uint32_t value) { _registers.__ebx = value; } + uint32_t getECX() const { return _registers.__ecx; } + void setECX(uint32_t value) { _registers.__ecx = value; } + uint32_t getEDX() const { return _registers.__edx; } + void setEDX(uint32_t value) { _registers.__edx = value; } + uint32_t getESI() const { return _registers.__esi; } + void setESI(uint32_t value) { _registers.__esi = value; } + uint32_t getEDI() const { return _registers.__edi; } + void setEDI(uint32_t value) { _registers.__edi = value; } + +private: + struct GPRs { + unsigned int __eax; + unsigned int __ebx; + unsigned int __ecx; + unsigned int __edx; + unsigned int __edi; + unsigned int __esi; + unsigned int __ebp; + unsigned int __esp; + unsigned int __ss; + unsigned int __eflags; + unsigned int __eip; + unsigned int __cs; + unsigned int __ds; + unsigned int __es; + unsigned int __fs; + unsigned int __gs; + }; + + GPRs _registers; +}; + +inline Registers_x86::Registers_x86(const void *registers) { + static_assert(sizeof(Registers_x86) < sizeof(unw_context_t), + "x86 registers do not fit into unw_context_t"); + _registers = *((GPRs *)registers); +} + +inline Registers_x86::Registers_x86() { + bzero(&_registers, sizeof(_registers)); +} + +inline bool Registers_x86::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 7) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__eip; + case UNW_REG_SP: + return _registers.__esp; + case UNW_X86_EAX: + return _registers.__eax; + case UNW_X86_ECX: + return _registers.__ecx; + case UNW_X86_EDX: + return _registers.__edx; + case UNW_X86_EBX: + return _registers.__ebx; + case UNW_X86_EBP: + return _registers.__ebp; + case UNW_X86_ESP: + return _registers.__esp; + case UNW_X86_ESI: + return _registers.__esi; + case UNW_X86_EDI: + return _registers.__edi; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__eip = value; + return; + case UNW_REG_SP: + _registers.__esp = value; + return; + case UNW_X86_EAX: + _registers.__eax = value; + return; + case UNW_X86_ECX: + _registers.__ecx = value; + return; + case UNW_X86_EDX: + _registers.__edx = value; + return; + case UNW_X86_EBX: + _registers.__ebx = value; + return; + case UNW_X86_EBP: + _registers.__ebp = value; + return; + case UNW_X86_ESP: + _registers.__esp = value; + return; + case UNW_X86_ESI: + _registers.__esi = value; + return; + case UNW_X86_EDI: + _registers.__edi = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline const char *Registers_x86::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no x86 vector registers"); +} + + +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86_64 { +public: + Registers_x86_64(); + Registers_x86_64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + + uint64_t getSP() const { return _registers.__rsp; } + void setSP(uint64_t value) { _registers.__rsp = value; } + uint64_t getIP() const { return _registers.__rip; } + void setIP(uint64_t value) { _registers.__rip = value; } + uint64_t getRBP() const { return _registers.__rbp; } + void setRBP(uint64_t value) { _registers.__rbp = value; } + uint64_t getRBX() const { return _registers.__rbx; } + void setRBX(uint64_t value) { _registers.__rbx = value; } + uint64_t getR12() const { return _registers.__r12; } + void setR12(uint64_t value) { _registers.__r12 = value; } + uint64_t getR13() const { return _registers.__r13; } + void setR13(uint64_t value) { _registers.__r13 = value; } + uint64_t getR14() const { return _registers.__r14; } + void setR14(uint64_t value) { _registers.__r14 = value; } + uint64_t getR15() const { return _registers.__r15; } + void setR15(uint64_t value) { _registers.__r15 = value; } + +private: + struct GPRs { + uint64_t __rax; + uint64_t __rbx; + uint64_t __rcx; + uint64_t __rdx; + uint64_t __rdi; + uint64_t __rsi; + uint64_t __rbp; + uint64_t __rsp; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __rip; + uint64_t __rflags; + uint64_t __cs; + uint64_t __fs; + uint64_t __gs; + }; + GPRs _registers; +}; + +inline Registers_x86_64::Registers_x86_64(const void *registers) { + static_assert(sizeof(Registers_x86_64) < sizeof(unw_context_t), + "x86_64 registers do not fit into unw_context_t"); + _registers = *((GPRs *)registers); +} + +inline Registers_x86_64::Registers_x86_64() { + bzero(&_registers, sizeof(_registers)); +} + +inline bool Registers_x86_64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 15) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__rip; + case UNW_REG_SP: + return _registers.__rsp; + case UNW_X86_64_RAX: + return _registers.__rax; + case UNW_X86_64_RDX: + return _registers.__rdx; + case UNW_X86_64_RCX: + return _registers.__rcx; + case UNW_X86_64_RBX: + return _registers.__rbx; + case UNW_X86_64_RSI: + return _registers.__rsi; + case UNW_X86_64_RDI: + return _registers.__rdi; + case UNW_X86_64_RBP: + return _registers.__rbp; + case UNW_X86_64_RSP: + return _registers.__rsp; + case UNW_X86_64_R8: + return _registers.__r8; + case UNW_X86_64_R9: + return _registers.__r9; + case UNW_X86_64_R10: + return _registers.__r10; + case UNW_X86_64_R11: + return _registers.__r11; + case UNW_X86_64_R12: + return _registers.__r12; + case UNW_X86_64_R13: + return _registers.__r13; + case UNW_X86_64_R14: + return _registers.__r14; + case UNW_X86_64_R15: + return _registers.__r15; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__rip = value; + return; + case UNW_REG_SP: + _registers.__rsp = value; + return; + case UNW_X86_64_RAX: + _registers.__rax = value; + return; + case UNW_X86_64_RDX: + _registers.__rdx = value; + return; + case UNW_X86_64_RCX: + _registers.__rcx = value; + return; + case UNW_X86_64_RBX: + _registers.__rbx = value; + return; + case UNW_X86_64_RSI: + _registers.__rsi = value; + return; + case UNW_X86_64_RDI: + _registers.__rdi = value; + return; + case UNW_X86_64_RBP: + _registers.__rbp = value; + return; + case UNW_X86_64_RSP: + _registers.__rsp = value; + return; + case UNW_X86_64_R8: + _registers.__r8 = value; + return; + case UNW_X86_64_R9: + _registers.__r9 = value; + return; + case UNW_X86_64_R10: + _registers.__r10 = value; + return; + case UNW_X86_64_R11: + _registers.__r11 = value; + return; + case UNW_X86_64_R12: + _registers.__r12 = value; + return; + case UNW_X86_64_R13: + _registers.__r13 = value; + return; + case UNW_X86_64_R14: + _registers.__r14 = value; + return; + case UNW_X86_64_R15: + _registers.__r15 = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline const char *Registers_x86_64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + default: + return "unknown register"; + } +} + +inline double Registers_x86_64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline void Registers_x86_64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline v128 Registers_x86_64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no x86_64 vector registers"); +} + +inline void Registers_x86_64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no x86_64 vector registers"); +} + + +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc { +public: + Registers_ppc(); + Registers_ppc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint32_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint32_t value) { _registers.__srr0 = value; } + +private: + struct ppc_thread_state_t { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t _registers; + ppc_float_state_t _floatRegisters; + v128 _vectorRegisters[32]; // offset 424 +}; + +inline Registers_ppc::Registers_ppc(const void *registers) { + static_assert(sizeof(Registers_ppc) < sizeof(unw_context_t), + "ppc registers do not fit into unw_context_t"); + _registers = *((ppc_thread_state_t *)registers); + _floatRegisters = *((ppc_float_state_t *)((char *)registers + 160)); + memcpy(_vectorRegisters, ((char *)registers + 424), sizeof(_vectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() { + bzero(&_registers, sizeof(_registers)); + bzero(&_floatRegisters, sizeof(_floatRegisters)); + bzero(&_vectorRegisters, sizeof(_vectorRegisters)); +} + +inline bool Registers_ppc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum == UNW_PPC_VRSAVE) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_PPC_R31) + return true; + if (regNum == UNW_PPC_MQ) + return true; + if (regNum == UNW_PPC_LR) + return true; + if (regNum == UNW_PPC_CTR) + return true; + if ((UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7)) + return true; + return false; +} + +inline uint32_t Registers_ppc::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC_R0: + return _registers.__r0; + case UNW_PPC_R1: + return _registers.__r1; + case UNW_PPC_R2: + return _registers.__r2; + case UNW_PPC_R3: + return _registers.__r3; + case UNW_PPC_R4: + return _registers.__r4; + case UNW_PPC_R5: + return _registers.__r5; + case UNW_PPC_R6: + return _registers.__r6; + case UNW_PPC_R7: + return _registers.__r7; + case UNW_PPC_R8: + return _registers.__r8; + case UNW_PPC_R9: + return _registers.__r9; + case UNW_PPC_R10: + return _registers.__r10; + case UNW_PPC_R11: + return _registers.__r11; + case UNW_PPC_R12: + return _registers.__r12; + case UNW_PPC_R13: + return _registers.__r13; + case UNW_PPC_R14: + return _registers.__r14; + case UNW_PPC_R15: + return _registers.__r15; + case UNW_PPC_R16: + return _registers.__r16; + case UNW_PPC_R17: + return _registers.__r17; + case UNW_PPC_R18: + return _registers.__r18; + case UNW_PPC_R19: + return _registers.__r19; + case UNW_PPC_R20: + return _registers.__r20; + case UNW_PPC_R21: + return _registers.__r21; + case UNW_PPC_R22: + return _registers.__r22; + case UNW_PPC_R23: + return _registers.__r23; + case UNW_PPC_R24: + return _registers.__r24; + case UNW_PPC_R25: + return _registers.__r25; + case UNW_PPC_R26: + return _registers.__r26; + case UNW_PPC_R27: + return _registers.__r27; + case UNW_PPC_R28: + return _registers.__r28; + case UNW_PPC_R29: + return _registers.__r29; + case UNW_PPC_R30: + return _registers.__r30; + case UNW_PPC_R31: + return _registers.__r31; + case UNW_PPC_LR: + return _registers.__lr; + case UNW_PPC_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) { + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC_R0: + _registers.__r0 = value; + return; + case UNW_PPC_R1: + _registers.__r1 = value; + return; + case UNW_PPC_R2: + _registers.__r2 = value; + return; + case UNW_PPC_R3: + _registers.__r3 = value; + return; + case UNW_PPC_R4: + _registers.__r4 = value; + return; + case UNW_PPC_R5: + _registers.__r5 = value; + return; + case UNW_PPC_R6: + _registers.__r6 = value; + return; + case UNW_PPC_R7: + _registers.__r7 = value; + return; + case UNW_PPC_R8: + _registers.__r8 = value; + return; + case UNW_PPC_R9: + _registers.__r9 = value; + return; + case UNW_PPC_R10: + _registers.__r10 = value; + return; + case UNW_PPC_R11: + _registers.__r11 = value; + return; + case UNW_PPC_R12: + _registers.__r12 = value; + return; + case UNW_PPC_R13: + _registers.__r13 = value; + return; + case UNW_PPC_R14: + _registers.__r14 = value; + return; + case UNW_PPC_R15: + _registers.__r15 = value; + return; + case UNW_PPC_R16: + _registers.__r16 = value; + return; + case UNW_PPC_R17: + _registers.__r17 = value; + return; + case UNW_PPC_R18: + _registers.__r18 = value; + return; + case UNW_PPC_R19: + _registers.__r19 = value; + return; + case UNW_PPC_R20: + _registers.__r20 = value; + return; + case UNW_PPC_R21: + _registers.__r21 = value; + return; + case UNW_PPC_R22: + _registers.__r22 = value; + return; + case UNW_PPC_R23: + _registers.__r23 = value; + return; + case UNW_PPC_R24: + _registers.__r24 = value; + return; + case UNW_PPC_R25: + _registers.__r25 = value; + return; + case UNW_PPC_R26: + _registers.__r26 = value; + return; + case UNW_PPC_R27: + _registers.__r27 = value; + return; + case UNW_PPC_R28: + _registers.__r28 = value; + return; + case UNW_PPC_R29: + _registers.__r29 = value; + return; + case UNW_PPC_R30: + _registers.__r30 = value; + return; + case UNW_PPC_R31: + _registers.__r31 = value; + return; + case UNW_PPC_MQ: + _registers.__mq = value; + return; + case UNW_PPC_LR: + _registers.__lr = value; + return; + case UNW_PPC_CTR: + _registers.__ctr = value; + return; + case UNW_PPC_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + _registers.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + _registers.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const { + if (regNum < UNW_PPC_F0) + return false; + if (regNum > UNW_PPC_F31) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _floatRegisters.__fpregs[regNum - UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _floatRegisters.__fpregs[regNum - UNW_PPC_F0] = value; +} + +inline bool Registers_ppc::validVectorRegister(int regNum) const { + if (regNum < UNW_PPC_V0) + return false; + if (regNum > UNW_PPC_V31) + return false; + return true; +} + +inline v128 Registers_ppc::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + v128 result = _vectorRegisters[regNum - UNW_PPC_V0]; + return result; +} + +inline void Registers_ppc::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorRegisters[regNum - UNW_PPC_V0] = value; +} + +inline const char *Registers_ppc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + +} + + +/// Registers_arm64 holds the register state of a thread in a 64-bit arm +/// process. +class _LIBUNWIND_HIDDEN Registers_arm64 { +public: + Registers_arm64(); + Registers_arm64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + + uint64_t getSP() const { return _registers.__sp; } + void setSP(uint64_t value) { _registers.__sp = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + uint64_t getFP() const { return _registers.__fp; } + void setFP(uint64_t value) { _registers.__fp = value; } + +private: + struct GPRs { + uint64_t __x[29]; // x0-x28 + uint64_t __fp; // Frame pointer x29 + uint64_t __lr; // Link register x30 + uint64_t __sp; // Stack pointer x31 + uint64_t __pc; // Program counter + uint64_t padding; // 16-byte align + }; + + GPRs _registers; + double _vectorHalfRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void *registers) { + static_assert(sizeof(Registers_arm64) < sizeof(unw_context_t), + "arm64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memcpy(_vectorHalfRegisters, (((char *)registers) + 0x110), + sizeof(_vectorHalfRegisters)); +} + +inline Registers_arm64::Registers_arm64() { + bzero(&_registers, sizeof(_registers)); + bzero(&_vectorHalfRegisters, sizeof(_vectorHalfRegisters)); +} + +inline bool Registers_arm64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 95) + return false; + if ((regNum > 31) && (regNum < 64)) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return _registers.__pc; + if (regNum == UNW_REG_SP) + return _registers.__sp; + if ((regNum >= 0) && (regNum < 32)) + return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) { + if (regNum == UNW_REG_IP) + _registers.__pc = value; + else if (regNum == UNW_REG_SP) + _registers.__sp = value; + else if ((regNum >= 0) && (regNum < 32)) + _registers.__x[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline const char *Registers_arm64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_ARM64_X0: + return "x0"; + case UNW_ARM64_X1: + return "x1"; + case UNW_ARM64_X2: + return "x2"; + case UNW_ARM64_X3: + return "x3"; + case UNW_ARM64_X4: + return "x4"; + case UNW_ARM64_X5: + return "x5"; + case UNW_ARM64_X6: + return "x6"; + case UNW_ARM64_X7: + return "x7"; + case UNW_ARM64_X8: + return "x8"; + case UNW_ARM64_X9: + return "x9"; + case UNW_ARM64_X10: + return "x10"; + case UNW_ARM64_X11: + return "x11"; + case UNW_ARM64_X12: + return "x12"; + case UNW_ARM64_X13: + return "x13"; + case UNW_ARM64_X14: + return "x14"; + case UNW_ARM64_X15: + return "x15"; + case UNW_ARM64_X16: + return "x16"; + case UNW_ARM64_X17: + return "x17"; + case UNW_ARM64_X18: + return "x18"; + case UNW_ARM64_X19: + return "x19"; + case UNW_ARM64_X20: + return "x20"; + case UNW_ARM64_X21: + return "x21"; + case UNW_ARM64_X22: + return "x22"; + case UNW_ARM64_X23: + return "x23"; + case UNW_ARM64_X24: + return "x24"; + case UNW_ARM64_X25: + return "x25"; + case UNW_ARM64_X26: + return "x26"; + case UNW_ARM64_X27: + return "x27"; + case UNW_ARM64_X28: + return "x28"; + case UNW_ARM64_X29: + return "fp"; + case UNW_ARM64_X30: + return "lr"; + case UNW_ARM64_X31: + return "sp"; + case UNW_ARM64_D0: + return "d0"; + case UNW_ARM64_D1: + return "d1"; + case UNW_ARM64_D2: + return "d2"; + case UNW_ARM64_D3: + return "d3"; + case UNW_ARM64_D4: + return "d4"; + case UNW_ARM64_D5: + return "d5"; + case UNW_ARM64_D6: + return "d6"; + case UNW_ARM64_D7: + return "d7"; + case UNW_ARM64_D8: + return "d8"; + case UNW_ARM64_D9: + return "d9"; + case UNW_ARM64_D10: + return "d10"; + case UNW_ARM64_D11: + return "d11"; + case UNW_ARM64_D12: + return "d12"; + case UNW_ARM64_D13: + return "d13"; + case UNW_ARM64_D14: + return "d14"; + case UNW_ARM64_D15: + return "d15"; + case UNW_ARM64_D16: + return "d16"; + case UNW_ARM64_D17: + return "d17"; + case UNW_ARM64_D18: + return "d18"; + case UNW_ARM64_D19: + return "d19"; + case UNW_ARM64_D20: + return "d20"; + case UNW_ARM64_D21: + return "d21"; + case UNW_ARM64_D22: + return "d22"; + case UNW_ARM64_D23: + return "d23"; + case UNW_ARM64_D24: + return "d24"; + case UNW_ARM64_D25: + return "d25"; + case UNW_ARM64_D26: + return "d26"; + case UNW_ARM64_D27: + return "d27"; + case UNW_ARM64_D28: + return "d28"; + case UNW_ARM64_D29: + return "d29"; + case UNW_ARM64_D30: + return "d30"; + case UNW_ARM64_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm64::validFloatRegister(int regNum) const { + if (regNum < UNW_ARM64_D0) + return false; + if (regNum > UNW_ARM64_D31) + return false; + return true; +} + +inline double Registers_arm64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorHalfRegisters[regNum - UNW_ARM64_D0]; +} + +inline void Registers_arm64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorHalfRegisters[regNum - UNW_ARM64_D0] = value; +} + +inline bool Registers_arm64::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} + +/// Registers_arm holds the register state of a thread in a 32-bit arm +/// process. +/// +/// 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; + 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; + 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(); + + 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; } + +private: + struct GPRs { + uint32_t __r[13]; // r0-r12 + uint32_t __sp; // Stack pointer r13 + uint32_t __lr; // Link register r14 + uint32_t __pc; // Program counter r15 + }; + + GPRs _registers; +}; + +inline Registers_arm::Registers_arm(const void *registers) { + static_assert(sizeof(Registers_arm) < sizeof(unw_context_t), + "arm registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +} + +inline Registers_arm::Registers_arm() { + bzero(&_registers, sizeof(_registers)); +} + +inline bool Registers_arm::validRegister(int regNum) const { + // Returns true for all non-VFP registers supported by the EHABI + // virtual register set (VRS). + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if ((regNum >= UNW_ARM_R0) && (regNum <= UNW_ARM_R15)) + return true; + return false; +} + +inline uint32_t Registers_arm::getRegister(int regNum) const { + 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)) + return _registers.__r[regNum]; + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline void Registers_arm::setRegister(int regNum, uint32_t value) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + _registers.__sp = value; + else if (regNum == UNW_ARM_LR) + _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)) + _registers.__r[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline const char *Registers_arm::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_ARM_IP: // UNW_ARM_R15 is alias + return "pc"; + case UNW_ARM_LR: // UNW_ARM_R14 is alias + return "lr"; + case UNW_REG_SP: + case UNW_ARM_SP: // UNW_ARM_R13 is alias + return "sp"; + case UNW_ARM_R0: + return "r0"; + case UNW_ARM_R1: + return "r1"; + case UNW_ARM_R2: + return "r2"; + case UNW_ARM_R3: + return "r3"; + case UNW_ARM_R4: + return "r4"; + case UNW_ARM_R5: + return "r5"; + case UNW_ARM_R6: + return "r6"; + case UNW_ARM_R7: + return "r7"; + case UNW_ARM_R8: + return "r8"; + case UNW_ARM_R9: + return "r9"; + case UNW_ARM_R10: + return "r10"; + case UNW_ARM_R11: + return "r11"; + case UNW_ARM_R12: + return "r12"; + case UNW_ARM_S0: + return "s0"; + case UNW_ARM_S1: + return "s1"; + case UNW_ARM_S2: + return "s2"; + case UNW_ARM_S3: + return "s3"; + case UNW_ARM_S4: + return "s4"; + case UNW_ARM_S5: + return "s5"; + case UNW_ARM_S6: + return "s6"; + case UNW_ARM_S7: + return "s7"; + case UNW_ARM_S8: + return "s8"; + case UNW_ARM_S9: + return "s9"; + case UNW_ARM_S10: + return "s10"; + case UNW_ARM_S11: + return "s11"; + case UNW_ARM_S12: + return "s12"; + case UNW_ARM_S13: + return "s13"; + case UNW_ARM_S14: + return "s14"; + case UNW_ARM_S15: + return "s15"; + case UNW_ARM_S16: + return "s16"; + case UNW_ARM_S17: + return "s17"; + case UNW_ARM_S18: + return "s18"; + case UNW_ARM_S19: + return "s19"; + case UNW_ARM_S20: + return "s20"; + case UNW_ARM_S21: + return "s21"; + case UNW_ARM_S22: + return "s22"; + case UNW_ARM_S23: + return "s23"; + case UNW_ARM_S24: + return "s24"; + case UNW_ARM_S25: + return "s25"; + case UNW_ARM_S26: + return "s26"; + case UNW_ARM_S27: + return "s27"; + case UNW_ARM_S28: + return "s28"; + case UNW_ARM_S29: + return "s29"; + case UNW_ARM_S30: + return "s30"; + case UNW_ARM_S31: + return "s31"; + case UNW_ARM_D0: + return "d0"; + case UNW_ARM_D1: + return "d1"; + case UNW_ARM_D2: + return "d2"; + case UNW_ARM_D3: + return "d3"; + case UNW_ARM_D4: + return "d4"; + case UNW_ARM_D5: + return "d5"; + case UNW_ARM_D6: + return "d6"; + case UNW_ARM_D7: + return "d7"; + case UNW_ARM_D8: + return "d8"; + case UNW_ARM_D9: + return "d9"; + case UNW_ARM_D10: + return "d10"; + case UNW_ARM_D11: + return "d11"; + case UNW_ARM_D12: + return "d12"; + case UNW_ARM_D13: + return "d13"; + case UNW_ARM_D14: + return "d14"; + case UNW_ARM_D15: + return "d15"; + case UNW_ARM_D16: + return "d16"; + case UNW_ARM_D17: + return "d17"; + case UNW_ARM_D18: + return "d18"; + case UNW_ARM_D19: + return "d19"; + case UNW_ARM_D20: + return "d20"; + case UNW_ARM_D21: + return "d21"; + case UNW_ARM_D22: + return "d22"; + case UNW_ARM_D23: + return "d23"; + case UNW_ARM_D24: + return "d24"; + case UNW_ARM_D25: + return "d25"; + case UNW_ARM_D26: + return "d26"; + case UNW_ARM_D27: + return "d27"; + case UNW_ARM_D28: + return "d28"; + case UNW_ARM_D29: + return "d29"; + case UNW_ARM_D30: + return "d30"; + case UNW_ARM_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm::validFloatRegister(int) const { + // FIXME: Implement float register support. + return false; +} + +inline unw_fpreg_t Registers_arm::getFloatRegister(int) const { + _LIBUNWIND_ABORT("ARM float register support not yet implemented"); +} + +inline void Registers_arm::setFloatRegister(int, unw_fpreg_t) { + _LIBUNWIND_ABORT("ARM float register support not yet implemented"); +} + +inline bool Registers_arm::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm::getVectorRegister(int) const { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} + +inline void Registers_arm::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} + +} // namespace libunwind + +#endif // __REGISTERS_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/Unwind-sjlj.c b/system/lib/libcxxabi/src/Unwind/Unwind-sjlj.c new file mode 100644 index 00000000..f9256b5a --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/Unwind-sjlj.c @@ -0,0 +1,468 @@ +//===--------------------------- Unwind-sjlj.c ----------------------------===// +// +// 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 setjump-longjump based C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include <unwind.h> + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#include "config.h" +#include "unwind_ext.h" + +// +// 32-bit iOS uses setjump/longjump based C++ exceptions. +// Other architectures use "zero cost" exceptions. +// +// With SJLJ based exceptions, any function that has a catch clause or needs to +// do any clean up when an exception propagates through it, needs to call +// _Unwind_SjLj_Register() at the start of the function and +// _Unwind_SjLj_Unregister() at the end. The register function is called with +// the address of a block of memory in the function's stack frame. The runtime +// keeps a linked list (stack) of these blocks - one per thread. The calling +// function also sets the personality and lsda fields of the block. +// + +#if _LIBUNWIND_BUILD_SJLJ_APIS + +struct _Unwind_FunctionContext { + // next function in stack of handlers + struct _Unwind_FunctionContext *prev; + + // set by calling function before registering to be the landing pad + uintptr_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uintptr_t resumeParameters[4]; + + // set by calling function before registering + __personality_routine personality; // arm offset=24 + uintptr_t lsda; // arm offset=28 + + // variable length array, contains registers to restore + // 0 = r7, 1 = pc, 2 = sp + void *jbuf[]; +}; + + +/// Called at start of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) { + fc->prev = __Unwind_SjLj_GetTopOfFunctionStack(); + __Unwind_SjLj_SetTopOfFunctionStack(fc); +} + + +/// Called at end of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) { + __Unwind_SjLj_SetTopOfFunctionStack(fc->prev); +} + + +static _Unwind_Reason_Code +unwind_phase1(struct _Unwind_Exception *exception_object) { + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p\n", c); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; c = c->prev) { + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached " + "bottom => _URC_END_OF_STACK\n", + exception_object); + return _URC_END_OF_STACK; + } + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p\n", c); + // if there is a personality routine, ask it if it will want to stop at this + // frame + if (c->personality != NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling " + "personality function %p\n", + exception_object, c->personality); + _Unwind_Reason_Code personalityResult = (*c->personality)( + 1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember function context + handlerNotFound = false; + exception_object->private_2 = (uintptr_t) c; + _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; + + 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(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p\n", + exception_object, c); + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " + "bottom => _URC_END_OF_STACK\n", + exception_object); + return _URC_END_OF_STACK; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ((uintptr_t) c == exception_object->private_2) + action = (_Unwind_Action)( + _UA_CLEANUP_PHASE | + _UA_HANDLER_FRAME); // tell personality this was the frame it marked + // in phase 1 + _Unwind_Reason_Code personalityResult = + (*c->personality)(1, action, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", + exception_object); + if ((uintptr_t) c == 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"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): " + "_URC_INSTALL_CONTEXT, will resume at " + "landing pad %p\n", + exception_object, c->jbuf[1]); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + // unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // clean up phase did not resume at the frame that the search phase said it + // would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code +unwind_phase2_forced(struct _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + + // get next frame (skip over first which is _Unwind_RaiseException) + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " + "bottom => _URC_END_OF_STACK\n", + exception_object); + return _URC_END_OF_STACK; + } + + // call stop function at each frame + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stop function returned %d\n", + exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stopped by stop function\n", + exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + __personality_routine p = (__personality_routine) c->personality; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "calling personality function %p\n", + exception_object, p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_CONTINUE_UNWIND\n", + exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_INSTALL_CONTEXT\n", + exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + break; + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // call stop function one last time and tell it we've reached the end of the + // stack + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK\n", + exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + + // 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_SjLj_RaiseException(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)\n", exception_object); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right + // thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(exception_object); +} + + + +/// 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. +/// +/// Re-throwing an exception is implemented by having the code call +/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)\n", exception_object); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return"); +} + + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), " + "private_1=%ld\n", + exception_object, exception_object->private_1); + // 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. + if (exception_object->private_1 == 0) { + return _Unwind_SjLj_RaiseException(exception_object); + // should return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate() + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_SjLj_Resume(exception_object); + _LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called " + "_Unwind_SjLj_Resume() which unexpectedly returned"); +} + + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) " + "=> 0x%0lX\n", context, ufc->lsda); + return ufc->lsda; +} + + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, + int index) { + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)\n", + context, index); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + return ufc->resumeParameters[index]; +} + + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0lX)\n" + , context, index, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeParameters[index] = new_value; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%lX\n", context, + ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + *ipBefore = 0; + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%lX\n", + context, ipBefore, ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to alter instruction pointer. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0lX)\n", + context, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeLocation = new_value - 1; +} + + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)\n", context); + return 0; +} + + +/// Called by personality handler during phase 2 if a foreign exception +/// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(struct _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); +} + + + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)\n", context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)\n", context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Called by personality handler to get "Call Frame Area" for current frame. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)\n", context); + if (context != NULL) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + // Setjmp/longjmp based exceptions don't have a true CFA. + // Instead, the SP in the jmpbuf is the closest approximation. + return (uintptr_t) ufc->jbuf[2]; + } + return 0; +} + +#endif // _LIBUNWIND_BUILD_SJLJ_APIS diff --git a/system/lib/libcxxabi/src/Unwind/UnwindCursor.hpp b/system/lib/libcxxabi/src/Unwind/UnwindCursor.hpp new file mode 100644 index 00000000..256a72da --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/UnwindCursor.hpp @@ -0,0 +1,1063 @@ +//===------------------------- UnwindCursor.hpp ---------------------------===// +// +// 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. +// +// +// C++ interface to lower levels of libuwind +//===----------------------------------------------------------------------===// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +#if __APPLE__ + #include <mach-o/dyld.h> +#endif + +#include "libunwind.h" + +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfInstructions.hpp" +#include "CompactUnwinder.hpp" +#include "config.h" + +namespace libunwind { + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND +/// Cache of recently found FDEs. +template <typename A> +class _LIBUNWIND_HIDDEN DwarfFDECache { + typedef typename A::pint_t pint_t; +public: + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, + unw_word_t ip_end, + unw_word_t fde, unw_word_t mh)); + +private: + + struct entry { + pint_t mh; + pint_t ip_start; + pint_t ip_end; + pint_t fde; + }; + + // These fields are all static to avoid needing an initializer. + // There is only one instance of this class per process. + static pthread_rwlock_t _lock; +#if __APPLE__ + static void dyldUnloadHook(const struct mach_header *mh, intptr_t slide); + static bool _registeredForDyldUnloads; +#endif + // Can't use std::vector<> here because this code is below libc++. + static entry *_buffer; + static entry *_bufferUsed; + static entry *_bufferEnd; + static entry _initialBuffer[64]; +}; + +template <typename A> +typename DwarfFDECache<A>::entry * +DwarfFDECache<A>::_buffer = _initialBuffer; + +template <typename A> +typename DwarfFDECache<A>::entry * +DwarfFDECache<A>::_bufferUsed = _initialBuffer; + +template <typename A> +typename DwarfFDECache<A>::entry * +DwarfFDECache<A>::_bufferEnd = &_initialBuffer[64]; + +template <typename A> +typename DwarfFDECache<A>::entry DwarfFDECache<A>::_initialBuffer[64]; + +template <typename A> +pthread_rwlock_t DwarfFDECache<A>::_lock = PTHREAD_RWLOCK_INITIALIZER; + +#if __APPLE__ +template <typename A> +bool DwarfFDECache<A>::_registeredForDyldUnloads = false; +#endif + +template <typename A> +typename A::pint_t DwarfFDECache<A>::findFDE(pint_t mh, pint_t pc) { + pint_t result = 0; + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_rdlock(&_lock)); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + if ((mh == p->mh) || (mh == 0)) { + if ((p->ip_start <= pc) && (pc < p->ip_end)) { + result = p->fde; + break; + } + } + } + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_unlock(&_lock)); + return result; +} + +template <typename A> +void DwarfFDECache<A>::add(pint_t mh, pint_t ip_start, pint_t ip_end, + pint_t fde) { + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_wrlock(&_lock)); + if (_bufferUsed >= _bufferEnd) { + size_t oldSize = (size_t)(_bufferEnd - _buffer); + size_t newSize = oldSize * 4; + // Can't use operator new (we are below it). + entry *newBuffer = (entry *)malloc(newSize * sizeof(entry)); + memcpy(newBuffer, _buffer, oldSize * sizeof(entry)); + if (_buffer != _initialBuffer) + free(_buffer); + _buffer = newBuffer; + _bufferUsed = &newBuffer[oldSize]; + _bufferEnd = &newBuffer[newSize]; + } + _bufferUsed->mh = mh; + _bufferUsed->ip_start = ip_start; + _bufferUsed->ip_end = ip_end; + _bufferUsed->fde = fde; + ++_bufferUsed; +#if __APPLE__ + if (!_registeredForDyldUnloads) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + _registeredForDyldUnloads = true; + } +#endif + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_unlock(&_lock)); +} + +template <typename A> +void DwarfFDECache<A>::removeAllIn(pint_t mh) { + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_wrlock(&_lock)); + entry *d = _buffer; + for (const entry *s = _buffer; s < _bufferUsed; ++s) { + if (s->mh != mh) { + if (d != s) + *d = *s; + ++d; + } + } + _bufferUsed = d; + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_unlock(&_lock)); +} + +template <typename A> +void DwarfFDECache<A>::dyldUnloadHook(const struct mach_header *mh, intptr_t ) { + removeAllIn((pint_t) mh); +} + +template <typename A> +void DwarfFDECache<A>::iterateCacheEntries(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_wrlock(&_lock)); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + _LIBUNWIND_LOG_NON_ZERO(::pthread_rwlock_unlock(&_lock)); +} +#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND +template <typename A> class UnwindSectionHeader { +public: + UnwindSectionHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t version() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, version)); + } + uint32_t commonEncodingsArraySectionOffset() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, + commonEncodingsArraySectionOffset)); + } + uint32_t commonEncodingsArrayCount() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + commonEncodingsArrayCount)); + } + uint32_t personalityArraySectionOffset() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + personalityArraySectionOffset)); + } + uint32_t personalityArrayCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, personalityArrayCount)); + } + uint32_t indexSectionOffset() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexSectionOffset)); + } + uint32_t indexCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + functionOffset)); + } + uint32_t secondLevelPagesSectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + secondLevelPagesSectionOffset)); + } + uint32_t lsdaIndexArraySectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + lsdaIndexArraySectionOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_regular_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_regular_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_regular_second_level_page_header, entryCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_regular_second_level_entry, index, + functionOffset)); + } + uint32_t encoding(uint32_t index) const { + return _addressSpace.get32( + _addr + + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); + } + uint16_t encodingsPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsPageOffset)); + } + uint16_t encodingsCount() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + uint16_t encodingIndex(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template <typename A> class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, functionOffset)); + } + uint32_t lsdaOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, lsdaOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; +#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND + + +class _LIBUNWIND_HIDDEN AbstractUnwindCursor { +public: + virtual bool validReg(int) = 0; + 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 int step() = 0; + virtual void getInfo(unw_proc_info_t *) = 0; + virtual void jumpto() = 0; + virtual bool isSignalFrame() = 0; + 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; +}; + + +/// UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack allocated inside a unw_cursor_t. +template <typename A, typename R> +class UnwindCursor : public AbstractUnwindCursor{ + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + 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 int step(); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); + + void operator delete(void *, size_t) {} + +private: + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint=0); + int stepWithDwarfFDE() { + return DwarfInstructions<A, R>::stepWithDwarf(_addressSpace, + (pint_t)this->getReg(UNW_REG_IP), + (pint_t)_info.unwind_info, + _registers); + } +#endif + +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + bool getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s); + int stepWithCompactEncoding() { + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND + if ( compactSaysUseDwarf() ) + return stepWithDwarfFDE(); + #endif + R dummy; + return stepWithCompactEncoding(dummy); + } + + int stepWithCompactEncoding(Registers_x86_64 &) { + return CompactUnwinder_x86_64<A>::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } + + int stepWithCompactEncoding(Registers_x86 &) { + return CompactUnwinder_x86<A>::stepWithCompactEncoding( + _info.format, (uint32_t)_info.start_ip, _addressSpace, _registers); + } + + int stepWithCompactEncoding(Registers_ppc &) { + return UNW_EINVAL; + } + + int stepWithCompactEncoding(Registers_arm64 &) { + return CompactUnwinder_arm64<A>::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } + + bool compactSaysUseDwarf(uint32_t *offset=NULL) const { + R dummy; + return compactSaysUseDwarf(dummy, offset); + } + + bool compactSaysUseDwarf(Registers_x86_64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } + + bool compactSaysUseDwarf(Registers_x86 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } + return false; + } + + bool compactSaysUseDwarf(Registers_ppc &, uint32_t *) const { + return true; + } + + bool compactSaysUseDwarf(Registers_arm64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_ARM64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } + + compact_unwind_encoding_t dwarfEncoding() const { + R dummy; + return dwarfEncoding(dummy); + } + + compact_unwind_encoding_t dwarfEncoding(Registers_x86_64 &) const { + return UNWIND_X86_64_MODE_DWARF; + } + + compact_unwind_encoding_t dwarfEncoding(Registers_x86 &) const { + return UNWIND_X86_MODE_DWARF; + } + + compact_unwind_encoding_t dwarfEncoding(Registers_ppc &) const { + return 0; + } + + compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const { + return UNWIND_ARM64_MODE_DWARF; + } +#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND + + + A &_addressSpace; + R _registers; + unw_proc_info_t _info; + bool _unwindInfoMissing; + bool _isSignalFrame; +}; + + +template <typename A, typename R> +UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _registers(context), _unwindInfoMissing(false), + _isSignalFrame(false) { + static_assert(sizeof(UnwindCursor<A, R>) < sizeof(unw_cursor_t), + "UnwindCursor<> does not fit in unw_cursor_t"); + + bzero(&_info, sizeof(_info)); +} + +template <typename A, typename R> +UnwindCursor<A, R>::UnwindCursor(A &as, void *) + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { + bzero(&_info, sizeof(_info)); + // FIXME + // fill in _registers from thread arg +} + + +template <typename A, typename R> +bool UnwindCursor<A, R>::validReg(int regNum) { + return _registers.validRegister(regNum); +} + +template <typename A, typename R> +unw_word_t UnwindCursor<A, R>::getReg(int regNum) { + return _registers.getRegister(regNum); +} + +template <typename A, typename R> +void UnwindCursor<A, R>::setReg(int regNum, unw_word_t value) { + _registers.setRegister(regNum, (typename A::pint_t)value); +} + +template <typename A, typename R> +bool UnwindCursor<A, R>::validFloatReg(int regNum) { + return _registers.validFloatRegister(regNum); +} + +template <typename A, typename R> +double UnwindCursor<A, R>::getFloatReg(int regNum) { + return _registers.getFloatRegister(regNum); +} + +template <typename A, typename R> +void UnwindCursor<A, R>::setFloatReg(int regNum, double value) { + _registers.setFloatRegister(regNum, value); +} + +template <typename A, typename R> void UnwindCursor<A, R>::jumpto() { + _registers.jumpto(); +} + +template <typename A, typename R> +const char *UnwindCursor<A, R>::getRegisterName(int regNum) { + return _registers.getRegisterName(regNum); +} + +template <typename A, typename R> bool UnwindCursor<A, R>::isSignalFrame() { + return _isSignalFrame; +} + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND +template <typename A, typename R> +bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc, + const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint) { + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + // If compact encoding table gave offset into dwarf section, go directly there + if (fdeSectionOffsetHint != 0) { + foundFDE = CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, + sects.dwarf_section + fdeSectionOffsetHint, + &fdeInfo, &cieInfo); + } +#if _LIBUNWIND_SUPPORT_DWARF_INDEX + if (!foundFDE && (sects.dwarf_index_section != 0)) { + // Have eh_frame_hdr section which is index into dwarf section. + // TO DO: implement index search + } +#endif + if (!foundFDE) { + // otherwise, search cache of previously found FDEs. + pint_t cachedFDE = DwarfFDECache<A>::findFDE(sects.dso_base, pc); + if (cachedFDE != 0) { + foundFDE = + CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, + cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + } + } + if (!foundFDE) { + // Still not found, do full scan of __eh_frame section. + foundFDE = CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); + } + if (foundFDE) { + typename CFI_Parser<A>::PrologInfo prolog; + if (CFI_Parser<A>::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, pc, + &prolog)) { + // Save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = (unw_word_t) sects.dso_base; + + // Add to cache (to make next lookup faster) if we had no hint + // and there was no index. + if (!foundInCache && (fdeSectionOffsetHint == 0)) { + #if _LIBUNWIND_SUPPORT_DWARF_INDEX + if (sects.dwarf_index_section == 0) + #endif + DwarfFDECache<A>::add(sects.dso_base, fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } + return true; + } + } + //_LIBUNWIND_DEBUG_LOG("can't find/use FDE for pc=0x%llX\n", (uint64_t)pc); + return false; +} +#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND + + +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND +template <typename A, typename R> +bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s) { + const bool log = false; + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", + (uint64_t)pc, (uint64_t)sects.dso_base); + + const UnwindSectionHeader<A> sectionHeader(_addressSpace, + sects.compact_unwind_section); + if (sectionHeader.version() != UNWIND_SECTION_VERSION) + return false; + + // do a binary search of top level index to find page with unwind info + pint_t targetFunctionOffset = pc - sects.dso_base; + const UnwindSectionIndexArray<A> topIndex(_addressSpace, + sects.compact_unwind_section + + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + uint32_t last = high - 1; + while (low < high) { + uint32_t mid = (low + high) / 2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", + //mid, low, high, topIndex.functionOffset(mid)); + if (topIndex.functionOffset(mid) <= targetFunctionOffset) { + if ((mid == last) || + (topIndex.functionOffset(mid + 1) > targetFunctionOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = + topIndex.functionOffset(low + 1); + const pint_t secondLevelAddr = + sects.compact_unwind_section + topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low+1); + if (log) + fprintf(stderr, "\tfirst level search for result index=%d " + "to secondLevelAddr=0x%llX\n", + low, (uint64_t) secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = _addressSpace.get32(secondLevelAddr); + if (pageKind == UNWIND_SECOND_LEVEL_REGULAR) { + // regular page + UnwindSectionRegularPageHeader<A> pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionRegularArray<A> pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in " + "regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t) targetFunctionOffset, (uint64_t) secondLevelAddr); + low = 0; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionOffset) { + if (mid == (uint32_t)(pageHeader.entryCount() - 1)) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + break; + } else if (pageIndex.functionOffset(mid + 1) > targetFunctionOffset) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low + 1) + sects.dso_base; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + sects.dso_base; + if (pc < funcStart) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + if (pc > funcEnd) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + } else if (pageKind == UNWIND_SECOND_LEVEL_COMPRESSED) { + // compressed page + UnwindSectionCompressedPageHeader<A> pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionCompressedArray<A> pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = + (uint32_t)(targetFunctionOffset - firstLevelFunctionOffset); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search of compressed page starting at " + "secondLevelAddr=0x%llX\n", + (uint64_t) secondLevelAddr); + low = 0; + last = pageHeader.entryCount() - 1; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionPageOffset) { + if ((mid == last) || + (pageIndex.functionOffset(mid + 1) > targetFunctionPageOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + + sects.dso_base; + if (low < last) + funcEnd = + pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset + + sects.dso_base; + else + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + if (pc < funcStart) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX not in second " + "level compressed unwind table. funcStart=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart); + return false; + } + if (pc > funcEnd) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX not in second " + "level compressed unwind table. funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if (encodingIndex < sectionHeader.commonEncodingsArrayCount()) { + // encoding is in common table in section header + encoding = _addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.commonEncodingsArraySectionOffset() + + encodingIndex * sizeof(uint32_t)); + } else { + // encoding is in page specific table + uint16_t pageEncodingIndex = + encodingIndex - (uint16_t)sectionHeader.commonEncodingsArrayCount(); + encoding = _addressSpace.get32(secondLevelAddr + + pageHeader.encodingsPageOffset() + + pageEncodingIndex * sizeof(uint32_t)); + } + } else { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info at 0x%0llX bad second " + "level page\n", + (uint64_t) sects.compact_unwind_section); + return false; + } + + // look up LSDA, if encoding says function has one + if (encoding & UNWIND_HAS_LSDA) { + UnwindSectionLsdaArray<A> lsdaIndex(_addressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = (uint32_t)(funcStart - sects.dso_base); + low = 0; + high = (uint32_t)(lsdaArrayEndAddr - lsdaArrayStartAddr) / + sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if (log) + fprintf(stderr, + "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", + funcStartOffset); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (lsdaIndex.functionOffset(mid) == funcStartOffset) { + lsda = lsdaIndex.lsdaOffset(mid) + sects.dso_base; + break; + } else if (lsdaIndex.functionOffset(mid) < funcStartOffset) { + low = mid + 1; + } else { + high = mid; + } + } + if (lsda == 0) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with HAS_LSDA bit set for " + "pc=0x%0llX, but lsda table has no entry\n", + encoding, (uint64_t) pc); + return false; + } + } + + // extact personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> + (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if (personalityIndex != 0) { + --personalityIndex; // change 1-based to zero-based index + if (personalityIndex > sectionHeader.personalityArrayCount()) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with personality index %d, " + "but personality table has only %d entires\n", + encoding, personalityIndex, + sectionHeader.personalityArrayCount()); + return false; + } + uint32_t personalityDelta = _addressSpace.get32( + sects.compact_unwind_section + sectionHeader.personalityArraySectionOffset() + + personalityIndex * sizeof(uint32_t)); + pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; + personality = _addressSpace.getP(personalityPointer); + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t) pc, personalityDelta, (uint64_t) personality); + } + + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t) pc, encoding, (uint64_t) lsda, (uint64_t) funcStart); + _info.start_ip = funcStart; + _info.end_ip = funcEnd; + _info.lsda = lsda; + _info.handler = personality; + _info.gp = 0; + _info.flags = 0; + _info.format = encoding; + _info.unwind_info = 0; + _info.unwind_info_size = 0; + _info.extra = sects.dso_base; + return true; +} +#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND + + +template <typename A, typename R> +void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { + pint_t pc = (pint_t)this->getReg(UNW_REG_IP); + + // If the last line of a function is a "throw" the compiler sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if (isReturnAddress) + --pc; + + // Ask address space object to find unwind sections for this pc. + UnwindInfoSections sects; + if (_addressSpace.findUnwindSections(pc, sects)) { +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + // If there is a compact unwind encoding table, look there first. + if (sects.compact_unwind_section != 0) { + if (this->getInfoFromCompactEncodingSection(pc, sects)) { + #if _LIBUNWIND_SUPPORT_DWARF_UNWIND + // Found info in table, done unless encoding says to use dwarf. + uint32_t dwarfOffset; + if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset)) { + if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) { + // found info in dwarf, done + return; + } + } + #endif + // If unwind table has entry, but entry says there is no unwind info, + // record that we have no unwind info. + if (_info.format == 0) + _unwindInfoMissing = true; + return; + } + } +#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + // If there is dwarf unwind info, look there next. + if (sects.dwarf_section != 0) { + if (this->getInfoFromDwarfSection(pc, sects)) { + // found info in dwarf, done + return; + } + } +#endif + } + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + // There is no static unwind info for this pc. Look to see if an FDE was + // dynamically registered for it. + pint_t cachedFDE = DwarfFDECache<A>::findFDE(0, pc); + if (cachedFDE != 0) { + CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; + CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; + const char *msg = CFI_Parser<A>::decodeFDE(_addressSpace, + cachedFDE, &fdeInfo, &cieInfo); + if (msg == NULL) { + typename CFI_Parser<A>::PrologInfo prolog; + if (CFI_Parser<A>::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, + pc, &prolog)) { + // save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + // Some frameless functions need SP + // altered when resuming in function. + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = 0; + return; + } + } + } + + // Lastly, ask AddressSpace object about platform specific ways to locate + // other FDEs. + pint_t fde; + if (_addressSpace.findOtherFDE(pc, fde)) { + CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; + CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; + if (!CFI_Parser<A>::decodeFDE(_addressSpace, fde, &fdeInfo, &cieInfo)) { + // Double check this FDE is for a function that includes the pc. + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) { + typename CFI_Parser<A>::PrologInfo prolog; + if (CFI_Parser<A>::parseFDEInstructions(_addressSpace, fdeInfo, + cieInfo, pc, &prolog)) { + // save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = (uint32_t)fdeInfo.fdeLength; + _info.extra = 0; + return; + } + } + } + } +#endif // #if _LIBUNWIND_SUPPORT_DWARF_UNWIND + + // no unwind info, flag that we can't reliably unwind + _unwindInfoMissing = true; +} + +template <typename A, typename R> +int UnwindCursor<A, R>::step() { + // Bottom of stack is defined is when no unwind info cannot be found. + if (_unwindInfoMissing) + return UNW_STEP_END; + + // Use unwinding info to modify register set as if function returned. + int result; +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + result = this->stepWithCompactEncoding(); +#elif _LIBUNWIND_SUPPORT_DWARF_UNWIND + result = this->stepWithDwarfFDE(); +#else + #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or _LIBUNWIND_SUPPORT_DWARF_UNWIND +#endif + + // update info based on new PC + if (result == UNW_STEP_SUCCESS) { + this->setInfoBasedOnIPRegister(true); + if (_unwindInfoMissing) + return UNW_STEP_END; + } + + return result; +} + +template <typename A, typename R> +void UnwindCursor<A, R>::getInfo(unw_proc_info_t *info) { + *info = _info; +} + +template <typename A, typename R> +bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen, + unw_word_t *offset) { + return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), + buf, bufLen, offset); +} + +}; // namespace libunwind + +#endif // __UNWINDCURSOR_HPP__ diff --git a/system/lib/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c b/system/lib/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c new file mode 100644 index 00000000..c84db76c --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c @@ -0,0 +1,268 @@ +//===--------------------- UnwindLevel1-gcc-ext.c -------------------------===// +// +// 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 gcc extensions to the C++ ABI Exception Handling Level 1. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include "libunwind.h" +#include "unwind.h" +#include "libunwind_ext.h" +#include "config.h" + +#if _LIBUNWIND_BUILD_ZERO_COST_APIS + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), " + "private_1=%ld\n", + exception_object, exception_object->private_1); + // 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 + if (exception_object->private_1 == 0) { + return _Unwind_RaiseException(exception_object); + // Will return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate(). + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_Resume(exception_object); + _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()" + " which unexpectedly returned"); +} + + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)\n", context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)\n", context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Scans unwind information to find the function that contains the +/// specified code address "pc". +_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) { + _LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc); + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc); + if (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) + return (void *)(long) info.start_ip; + else + return NULL; +} + + +/// Walk every frame and call trace function at each one. If trace function +/// returns anything other than _URC_NO_REASON, then walk is terminated. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { + unw_cursor_t cursor; + unw_context_t uc; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + + _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)\n", callback); + + // walk each frame + while (true) { + + // ask libuwind to get next frame (skip over first frame which is + // _Unwind_Backtrace()) + if (unw_step(&cursor) <= 0) { + _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached " + "bottom of stack, returning %d\n", + _URC_END_OF_STACK); + return _URC_END_OF_STACK; + } + + // debugging + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionName[512]; + unw_proc_info_t frameInfo; + unw_word_t offset; + unw_get_proc_name(&cursor, functionName, 512, &offset); + 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); + } + + // call trace function with this frame + _Unwind_Reason_Code result = + (*callback)((struct _Unwind_Context *)(&cursor), ref); + if (result != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because callback " + "returned %d\n", + result); + return result; + } + } +} + + +/// Find dwarf unwind info for an address 'pc' in some function. +_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc, + struct dwarf_eh_bases *bases) { + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc); + unw_get_proc_info(&cursor, &info); + bases->tbase = (uintptr_t)info.extra; + bases->dbase = 0; // dbase not used on Mac OS X + bases->func = (uintptr_t)info.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc, + (void *)(long) info.unwind_info); + return (void *)(long) info.unwind_info; +} + +/// Returns the CFA (call frame area, or stack pointer at start of function) +/// for the current context. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_SP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%llX\n", context, + (uint64_t) result); + return (uintptr_t)result; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)\n", context); + *ipBefore = 0; + return _Unwind_GetIP(context); +} + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + +/// Called by programs with dynamic code generators that want +/// to register a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __register_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__register_frame(%p)\n", fde); + _unw_add_dynamic_fde((unw_word_t)(uintptr_t) fde); +} + + +/// Called by programs with dynamic code generators that want +/// to unregister a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__deregister_frame(%p)\n", fde); + _unw_remove_dynamic_fde((unw_word_t)(uintptr_t) fde); +} + + +// The following register/deregister functions are gcc extensions. +// They have existed on Mac OS X, but have never worked because Mac OS X +// before 10.6 used keymgr to track known FDEs, but these functions +// never got updated to use keymgr. +// For now, we implement these as do-nothing functions to keep any existing +// applications working. We also add the not in 10.6 symbol so that nwe +// application won't be able to use them. + +#if _LIBUNWIND_SUPPORT_FRAME_APIS +_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob, + void *tb, void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)\n", + fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde, + void *ob, void *tb, + void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_table_bases" + "(%p,%p, %p, %p)\n", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__register_frame_table(%p)\n", fde); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} +#endif // _LIBUNWIND_SUPPORT_FRAME_APIS + +#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND + +#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS diff --git a/system/lib/libcxxabi/src/Unwind/UnwindLevel1.c b/system/lib/libcxxabi/src/Unwind/UnwindLevel1.c new file mode 100644 index 00000000..b2e93c7e --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/UnwindLevel1.c @@ -0,0 +1,495 @@ +//===------------------------- UnwindLevel1.c -----------------------------===// +// +// 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 C++ ABI Exception Handling Level 1 as documented at: +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// using libunwind +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "libunwind.h" +#include "unwind.h" +#include "config.h" + +#if _LIBUNWIND_BUILD_ZERO_COST_APIS + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) { + 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, pc, frameInfo.start_ip, functionName, + frameInfo.lsda, 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); + _Unwind_Reason_Code personalityResult = + (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)(&cursor1)); + 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; + unw_get_reg(&cursor1, UNW_REG_SP, &sp); + exception_object->private_2 = (uintptr_t)sp; + _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; + + 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, struct _Unwind_Exception *exception_object) { + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // 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). + 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, frameInfo.start_ip, functionName, sp, + frameInfo.lsda, 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); + _Unwind_Action action = _UA_CLEANUP_PHASE; + if (sp == exception_object->private_2) { + // Tell personality this was the frame it marked in phase 1. + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); + } + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", + 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"); + } + 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, pc, sp); + } + unw_resume(&cursor2); + // unw_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, + struct _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + // Walk each frame until we reach where search phase said to stop + while (unw_step(&cursor2) > 0) { + + // Update info about this frame. + unw_proc_info_t frameInfo; + if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step " + "failed => _URC_END_OF_STACK\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(&cursor2, functionName, 512, &offset) != + UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) + strcpy(functionName, ".anonymous."); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "start_ip=0x%llX, func=%s, lsda=0x%llX, " + " personality=0x%llX\n", + exception_object, frameInfo.start_ip, + functionName, frameInfo.lsda, + frameInfo.handler); + } + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", + exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", + exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + __personality_routine p = + (__personality_routine)(long)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", + exception_object, p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_CONTINUE_UNWIND\n", + exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_INSTALL_CONTEXT\n", + exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + unw_resume(&cursor2); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK\n", + exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + + // 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(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)\n", + exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + // Mark that this is a non-forced unwind, so _Unwind_Resume() + // can do the right thing. + exception_object->private_1 = 0; + exception_object->private_2 = 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); +} + + + +/// 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(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(&uc, exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(&uc, exception_object); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + + + +/// Not used by C++. +/// 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_Stop_Fn stop, void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", + exception_object, stop); + unw_context_t uc; + unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->private_1 = (uintptr_t) stop; + exception_object->private_2 = (uintptr_t) stop_parameter; + + // do it + return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); +} + + +/// 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%lX\n", context, result); + if (result != 0) { + if (*((uint8_t *)result) != 0xFF) + _LIBUNWIND_DEBUG_LOG("lsda at 0x%lX does not start with 0xFF\n", result); + } + return result; +} + + + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, + int index) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + unw_get_reg(cursor, index, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%llX\n", + context, + index, (uint64_t) result); + return (uintptr_t)result; +} + + + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, " + "value=0x%0llX)\n", context, + index, (uint64_t) new_value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_set_reg(cursor, index, new_value); +} + + + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_IP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%llX\n", context, + (uint64_t) result); + return (uintptr_t)result; +} + + + +/// Called by personality handler during phase 2 to alter instruction pointer, +/// such as setting where the landing pad is, so _Unwind_Resume() will +/// start executing in the landing pad. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0llX)\n", + context, (uint64_t) new_value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_set_reg(cursor, UNW_REG_IP, new_value); +} + + +/// 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%lX\n", + context, result); + return result; +} + + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(struct _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 // _LIBUNWIND_BUILD_ZERO_COST_APIS diff --git a/system/lib/libcxxabi/src/Unwind/UnwindRegistersRestore.S b/system/lib/libcxxabi/src/Unwind/UnwindRegistersRestore.S new file mode 100644 index 00000000..15e2072f --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/UnwindRegistersRestore.S @@ -0,0 +1,329 @@ +//===-------------------- UnwindRegistersRestore.S ------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if __i386__ +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_x866jumptoEv) +# +# void libunwind::Registers_x86::jumpto() +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + + movl 4(%esp), %eax + # set up eax and ret on new stack location + movl 28(%eax), %edx # edx holds new stack pointer + subl $8,%edx + movl %edx, 28(%eax) + movl 0(%eax), %ebx + movl %ebx, 0(%edx) + movl 40(%eax), %ebx + movl %ebx, 4(%edx) + # we now have ret and eax pushed onto where new stack will be + # restore all registers + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + # skip ss + # skip eflags + pop %eax # eax was already pushed on new stack + ret # eip was already pushed on new stack + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + +#elif __x86_64__ + +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind16Registers_x86_646jumptoEv) +# +# void libunwind::Registers_x86_64::jumpto() +# +# On entry, thread_state pointer is in rdi + + movq 56(%rdi), %rax # rax holds new stack pointer + subq $16, %rax + movq %rax, 56(%rdi) + movq 32(%rdi), %rbx # store new rdi on new stack + movq %rbx, 0(%rax) + movq 128(%rdi), %rbx # store new rip on new stack + movq %rbx, 8(%rax) + # restore all registers + movq 0(%rdi), %rax + movq 8(%rdi), %rbx + movq 16(%rdi), %rcx + movq 24(%rdi), %rdx + # restore rdi later + movq 40(%rdi), %rsi + movq 48(%rdi), %rbp + # restore rsp later + movq 64(%rdi), %r8 + movq 72(%rdi), %r9 + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + # skip rflags + # skip cs + # skip fs + # skip gs + movq 56(%rdi), %rsp # cut back rsp to new location + pop %rdi # rdi was saved here earlier + ret # rip was saved here + + +#elif __ppc__ + +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv) +; +; void libunwind::Registers_ppc::jumpto() +; +; On entry: +; thread_state pointer is in r3 +; + + ; restore integral registerrs + ; skip r0 for now + ; skip r1 for now + lwz r2, 16(r3) + ; skip r3 for now + ; skip r4 for now + ; skip r5 for now + lwz r6, 32(r3) + lwz r7, 36(r3) + lwz r8, 40(r3) + lwz r9, 44(r3) + lwz r10, 48(r3) + lwz r11, 52(r3) + lwz r12, 56(r3) + lwz r13, 60(r3) + lwz r14, 64(r3) + lwz r15, 68(r3) + lwz r16, 72(r3) + lwz r17, 76(r3) + lwz r18, 80(r3) + lwz r19, 84(r3) + lwz r20, 88(r3) + lwz r21, 92(r3) + lwz r22, 96(r3) + lwz r23,100(r3) + lwz r24,104(r3) + lwz r25,108(r3) + lwz r26,112(r3) + lwz r27,116(r3) + lwz r28,120(r3) + lwz r29,124(r3) + lwz r30,128(r3) + lwz r31,132(r3) + + ; restore float registers + lfd f0, 160(r3) + lfd f1, 168(r3) + lfd f2, 176(r3) + lfd f3, 184(r3) + lfd f4, 192(r3) + lfd f5, 200(r3) + lfd f6, 208(r3) + lfd f7, 216(r3) + lfd f8, 224(r3) + lfd f9, 232(r3) + lfd f10,240(r3) + lfd f11,248(r3) + lfd f12,256(r3) + lfd f13,264(r3) + lfd f14,272(r3) + lfd f15,280(r3) + lfd f16,288(r3) + lfd f17,296(r3) + lfd f18,304(r3) + lfd f19,312(r3) + lfd f20,320(r3) + lfd f21,328(r3) + lfd f22,336(r3) + lfd f23,344(r3) + lfd f24,352(r3) + lfd f25,360(r3) + lfd f26,368(r3) + lfd f27,376(r3) + lfd f28,384(r3) + lfd f29,392(r3) + lfd f30,400(r3) + lfd f31,408(r3) + + ; restore vector registers if any are in use + lwz r5,156(r3) ; test VRsave + cmpwi r5,0 + beq Lnovec + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + ; the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer + + +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. r0,r5,(1<<(15-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ +Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. r0,r5,(1<<(31-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ + Ldone ## _index: + + + LOAD_VECTOR_UNALIGNEDl(0) + LOAD_VECTOR_UNALIGNEDl(1) + LOAD_VECTOR_UNALIGNEDl(2) + LOAD_VECTOR_UNALIGNEDl(3) + LOAD_VECTOR_UNALIGNEDl(4) + LOAD_VECTOR_UNALIGNEDl(5) + LOAD_VECTOR_UNALIGNEDl(6) + LOAD_VECTOR_UNALIGNEDl(7) + LOAD_VECTOR_UNALIGNEDl(8) + LOAD_VECTOR_UNALIGNEDl(9) + LOAD_VECTOR_UNALIGNEDl(10) + LOAD_VECTOR_UNALIGNEDl(11) + LOAD_VECTOR_UNALIGNEDl(12) + LOAD_VECTOR_UNALIGNEDl(13) + LOAD_VECTOR_UNALIGNEDl(14) + LOAD_VECTOR_UNALIGNEDl(15) + LOAD_VECTOR_UNALIGNEDh(16) + LOAD_VECTOR_UNALIGNEDh(17) + LOAD_VECTOR_UNALIGNEDh(18) + LOAD_VECTOR_UNALIGNEDh(19) + LOAD_VECTOR_UNALIGNEDh(20) + LOAD_VECTOR_UNALIGNEDh(21) + LOAD_VECTOR_UNALIGNEDh(22) + LOAD_VECTOR_UNALIGNEDh(23) + LOAD_VECTOR_UNALIGNEDh(24) + LOAD_VECTOR_UNALIGNEDh(25) + LOAD_VECTOR_UNALIGNEDh(26) + LOAD_VECTOR_UNALIGNEDh(27) + LOAD_VECTOR_UNALIGNEDh(28) + LOAD_VECTOR_UNALIGNEDh(29) + LOAD_VECTOR_UNALIGNEDh(30) + LOAD_VECTOR_UNALIGNEDh(31) + +Lnovec: + lwz r0, 136(r3) ; __cr + mtocrf 255,r0 + lwz r0, 148(r3) ; __ctr + mtctr r0 + lwz r0, 0(r3) ; __ssr0 + mtctr r0 + lwz r0, 8(r3) ; do r0 now + lwz r5,28(r3) ; do r5 now + lwz r4,24(r3) ; do r4 now + lwz r1,12(r3) ; do sp now + lwz r3,20(r3) ; do r3 last + bctr + +#elif __arm64__ + +; +; void libunwind::Registers_arm64::jumpto() +; +; On entry: +; thread_state pointer is in x0 +; +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind15Registers_arm646jumptoEv) + ; skip restore of x0,x1 for now + ldp x2, x3, [x0, #0x010] + ldp x4, x5, [x0, #0x020] + ldp x6, x7, [x0, #0x030] + ldp x8, x9, [x0, #0x040] + ldp x10,x11, [x0, #0x050] + ldp x12,x13, [x0, #0x060] + ldp x14,x15, [x0, #0x070] + ldp x16,x17, [x0, #0x080] + ldp x18,x19, [x0, #0x090] + ldp x20,x21, [x0, #0x0A0] + ldp x22,x23, [x0, #0x0B0] + ldp x24,x25, [x0, #0x0C0] + ldp x26,x27, [x0, #0x0D0] + ldp x28,fp, [x0, #0x0E0] + ldr lr, [x0, #0x100] ; restore pc into lr + ldr x1, [x0, #0x0F8] + mov sp,x1 ; restore sp + + ldp d0, d1, [x0, #0x110] + ldp d2, d3, [x0, #0x120] + ldp d4, d5, [x0, #0x130] + ldp d6, d7, [x0, #0x140] + ldp d8, d9, [x0, #0x150] + ldp d10,d11, [x0, #0x160] + ldp d12,d13, [x0, #0x170] + ldp d14,d15, [x0, #0x180] + ldp d16,d17, [x0, #0x190] + ldp d18,d19, [x0, #0x1A0] + ldp d20,d21, [x0, #0x1B0] + ldp d22,d23, [x0, #0x1C0] + ldp d24,d25, [x0, #0x1D0] + ldp d26,d27, [x0, #0x1E0] + ldp d28,d29, [x0, #0x1F0] + ldr d30, [x0, #0x200] + ldr d31, [x0, #0x208] + + ldp x0, x1, [x0, #0x000] ; restore x0,x1 + ret lr ; jump to pc + +#elif __arm__ + +@ +@ void libunwind::Registers_arm::jumpto() +@ +@ On entry: +@ thread_state pointer is in r0 +@ +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm6jumptoEv) + @ Use lr as base so that r0 can be restored. + mov lr, r0 + @ 32bit thumb-2 restrictions for ldm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) and lr (r14) cannot both be in the list in an LDM instruction + ldm lr, {r0-r12} + ldr sp, [lr, #52] + ldr lr, [lr, #60] @ restore pc into lr + mov pc, lr + +#endif diff --git a/system/lib/libcxxabi/src/Unwind/UnwindRegistersSave.S b/system/lib/libcxxabi/src/Unwind/UnwindRegistersSave.S new file mode 100644 index 00000000..6f19f6c6 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/UnwindRegistersSave.S @@ -0,0 +1,301 @@ +//===------------------------ UnwindRegistersSave.S -----------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if __i386__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + +# +DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) + push %eax + movl 8(%esp), %eax + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %edi, 16(%eax) + movl %esi, 20(%eax) + movl %ebp, 24(%eax) + movl %esp, %edx + addl $8, %edx + movl %edx, 28(%eax) # store what sp was at call site as esp + # skip ss + # skip eflags + movl 4(%esp), %edx + movl %edx, 40(%eax) # store return address as eip + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + movl (%esp), %edx + movl %edx, (%eax) # store original eax + popl %eax + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __x86_64__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in rdi +# +DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) + movq %rax, (%rdi) + movq %rbx, 8(%rdi) + movq %rcx, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rdi, 32(%rdi) + movq %rsi, 40(%rdi) + movq %rbp, 48(%rdi) + movq %rsp, 56(%rdi) + addq $8, 56(%rdi) + movq %r8, 64(%rdi) + movq %r9, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13,104(%rdi) + movq %r14,112(%rdi) + movq %r15,120(%rdi) + movq (%rsp),%rsi + movq %rsi,128(%rdi) # store return address as rip + # skip rflags + # skip cs + # skip fs + # skip gs + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __ppc__ + +; +; extern int unw_getcontext(unw_context_t* thread_state) +; +; On entry: +; thread_state pointer is in r3 +; +DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) + stw r0, 8(r3) + mflr r0 + stw r0, 0(r3) ; store lr as ssr0 + stw r1, 12(r3) + stw r2, 16(r3) + stw r3, 20(r3) + stw r4, 24(r3) + stw r5, 28(r3) + stw r6, 32(r3) + stw r7, 36(r3) + stw r8, 40(r3) + stw r9, 44(r3) + stw r10, 48(r3) + stw r11, 52(r3) + stw r12, 56(r3) + stw r13, 60(r3) + stw r14, 64(r3) + stw r15, 68(r3) + stw r16, 72(r3) + stw r17, 76(r3) + stw r18, 80(r3) + stw r19, 84(r3) + stw r20, 88(r3) + stw r21, 92(r3) + stw r22, 96(r3) + stw r23,100(r3) + stw r24,104(r3) + stw r25,108(r3) + stw r26,112(r3) + stw r27,116(r3) + stw r28,120(r3) + stw r29,124(r3) + stw r30,128(r3) + stw r31,132(r3) + + ; save VRSave register + mfspr r0,256 + stw r0,156(r3) + ; save CR registers + mfcr r0 + stw r0,136(r3) + ; save CTR register + mfctr r0 + stw r0,148(r3) + + ; save float registers + stfd f0, 160(r3) + stfd f1, 168(r3) + stfd f2, 176(r3) + stfd f3, 184(r3) + stfd f4, 192(r3) + stfd f5, 200(r3) + stfd f6, 208(r3) + stfd f7, 216(r3) + stfd f8, 224(r3) + stfd f9, 232(r3) + stfd f10,240(r3) + stfd f11,248(r3) + stfd f12,256(r3) + stfd f13,264(r3) + stfd f14,272(r3) + stfd f15,280(r3) + stfd f16,288(r3) + stfd f17,296(r3) + stfd f18,304(r3) + stfd f19,312(r3) + stfd f20,320(r3) + stfd f21,328(r3) + stfd f22,336(r3) + stfd f23,344(r3) + stfd f24,352(r3) + stfd f25,360(r3) + stfd f26,368(r3) + stfd f27,376(r3) + stfd f28,384(r3) + stfd f29,392(r3) + stfd f30,400(r3) + stfd f31,408(r3) + + + ; save vector registers + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + +#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ + stvx _vec,0,r4 @\ + lwz r5, 0(r4) @\ + stw r5, _offset(r3) @\ + lwz r5, 4(r4) @\ + stw r5, _offset+4(r3) @\ + lwz r5, 8(r4) @\ + stw r5, _offset+8(r3) @\ + lwz r5, 12(r4) @\ + stw r5, _offset+12(r3) + + SAVE_VECTOR_UNALIGNED( v0, 424+0x000) + SAVE_VECTOR_UNALIGNED( v1, 424+0x010) + SAVE_VECTOR_UNALIGNED( v2, 424+0x020) + SAVE_VECTOR_UNALIGNED( v3, 424+0x030) + SAVE_VECTOR_UNALIGNED( v4, 424+0x040) + SAVE_VECTOR_UNALIGNED( v5, 424+0x050) + SAVE_VECTOR_UNALIGNED( v6, 424+0x060) + SAVE_VECTOR_UNALIGNED( v7, 424+0x070) + SAVE_VECTOR_UNALIGNED( v8, 424+0x080) + SAVE_VECTOR_UNALIGNED( v9, 424+0x090) + SAVE_VECTOR_UNALIGNED(v10, 424+0x0A0) + SAVE_VECTOR_UNALIGNED(v11, 424+0x0B0) + SAVE_VECTOR_UNALIGNED(v12, 424+0x0C0) + SAVE_VECTOR_UNALIGNED(v13, 424+0x0D0) + SAVE_VECTOR_UNALIGNED(v14, 424+0x0E0) + SAVE_VECTOR_UNALIGNED(v15, 424+0x0F0) + SAVE_VECTOR_UNALIGNED(v16, 424+0x100) + SAVE_VECTOR_UNALIGNED(v17, 424+0x110) + SAVE_VECTOR_UNALIGNED(v18, 424+0x120) + SAVE_VECTOR_UNALIGNED(v19, 424+0x130) + SAVE_VECTOR_UNALIGNED(v20, 424+0x140) + SAVE_VECTOR_UNALIGNED(v21, 424+0x150) + SAVE_VECTOR_UNALIGNED(v22, 424+0x160) + SAVE_VECTOR_UNALIGNED(v23, 424+0x170) + SAVE_VECTOR_UNALIGNED(v24, 424+0x180) + SAVE_VECTOR_UNALIGNED(v25, 424+0x190) + SAVE_VECTOR_UNALIGNED(v26, 424+0x1A0) + SAVE_VECTOR_UNALIGNED(v27, 424+0x1B0) + SAVE_VECTOR_UNALIGNED(v28, 424+0x1C0) + SAVE_VECTOR_UNALIGNED(v29, 424+0x1D0) + SAVE_VECTOR_UNALIGNED(v30, 424+0x1E0) + SAVE_VECTOR_UNALIGNED(v31, 424+0x1F0) + + li r3, 0 ; return UNW_ESUCCESS + blr + + +#elif __arm64__ + +; +; extern int unw_getcontext(unw_context_t* thread_state) +; +; On entry: +; thread_state pointer is in x0 +; +DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) + stp x0, x1, [x0, #0x000] + stp x2, x3, [x0, #0x010] + stp x4, x5, [x0, #0x020] + stp x6, x7, [x0, #0x030] + stp x8, x9, [x0, #0x040] + stp x10,x11, [x0, #0x050] + stp x12,x13, [x0, #0x060] + stp x14,x15, [x0, #0x070] + stp x16,x17, [x0, #0x080] + stp x18,x19, [x0, #0x090] + stp x20,x21, [x0, #0x0A0] + stp x22,x23, [x0, #0x0B0] + stp x24,x25, [x0, #0x0C0] + stp x26,x27, [x0, #0x0D0] + stp x28,fp, [x0, #0x0E0] + str lr, [x0, #0x0F0] + mov x1,sp + str x1, [x0, #0x0F8] + str lr, [x0, #0x100] ; store return address as pc + ; skip cpsr + stp d0, d1, [x0, #0x110] + stp d2, d3, [x0, #0x120] + stp d4, d5, [x0, #0x130] + stp d6, d7, [x0, #0x140] + stp d8, d9, [x0, #0x150] + stp d10,d11, [x0, #0x160] + stp d12,d13, [x0, #0x170] + stp d14,d15, [x0, #0x180] + stp d16,d17, [x0, #0x190] + stp d18,d19, [x0, #0x1A0] + stp d20,d21, [x0, #0x1B0] + stp d22,d23, [x0, #0x1C0] + stp d24,d25, [x0, #0x1D0] + stp d26,d27, [x0, #0x1E0] + stp d28,d29, [x0, #0x1F0] + str d30, [x0, #0x200] + str d31, [x0, #0x208] + ldr x0, #0 ; return UNW_ESUCCESS + ret + +#elif __arm__ && !__APPLE__ + +@ +@ extern int unw_getcontext(unw_context_t* thread_state) +@ +@ On entry: +@ thread_state pointer is in r0 +@ +DEFINE_LIBUNWIND_FUNCTION(unw_getcontext) + @ 32bit thumb-2 restrictions for stm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) cannot be in the list in an STM instruction + stm r0, {r0-r12} + str sp, [r0, #52] + str lr, [r0, #56] + str lr, [r0, #60] @ store return address as pc + mov r0, #0 @ return UNW_ESUCCESS + mov pc, lr + +#endif diff --git a/system/lib/libcxxabi/src/Unwind/Unwind_AppleExtras.cpp b/system/lib/libcxxabi/src/Unwind/Unwind_AppleExtras.cpp new file mode 100644 index 00000000..6d02a66b --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/Unwind_AppleExtras.cpp @@ -0,0 +1,205 @@ +//===--------------------- Unwind_AppleExtras.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. +// +// +//===----------------------------------------------------------------------===// + +#include "config.h" +#include "DwarfParser.hpp" +#include "unwind_ext.h" + + +// private keymgr stuff +#define KEYMGR_GCC3_DW2_OBJ_LIST 302 +extern "C" { + extern void _keymgr_set_and_unlock_processwide_ptr(int key, void *ptr); + extern void *_keymgr_get_and_lock_processwide_ptr(int key); +} + +// undocumented libgcc "struct object" +struct libgcc_object { + void *start; + void *unused1; + void *unused2; + void *fde; + unsigned long encoding; + void *fde_end; + libgcc_object *next; +}; + +// undocumented libgcc "struct km_object_info" referenced by +// KEYMGR_GCC3_DW2_OBJ_LIST +struct libgcc_object_info { + libgcc_object *seen_objects; + libgcc_object *unseen_objects; + unsigned spare[2]; +}; + + +// static linker symbols to prevent wrong two level namespace for _Unwind symbols +#if __arm__ + #define NOT_HERE_BEFORE_5_0(sym) \ + extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp30 = 0; \ + extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp31 = 0; \ + extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\ + __attribute__((visibility("default"))) const char sym##_tmp32 = 0; \ + extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp40 = 0; \ + extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp41 = 0; \ + extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp42 = 0; \ + extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp43 = 0; +#elif __arm64__ + #define NOT_HERE_BEFORE_10_6(sym) + #define NEVER_HERE(sym) +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + +#if _LIBUNWIND_BUILD_ZERO_COST_APIS + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) +NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) +NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) +NOT_HERE_BEFORE_10_6(_Unwind_GetGR) +NOT_HERE_BEFORE_10_6(_Unwind_GetIP) +NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) +NOT_HERE_BEFORE_10_6(_Unwind_Resume) +NOT_HERE_BEFORE_10_6(_Unwind_SetGR) +NOT_HERE_BEFORE_10_6(_Unwind_SetIP) +NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) +NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) +NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) +NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) +NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_10_6(__register_frame) +NOT_HERE_BEFORE_10_6(__deregister_frame) + +// +// symbols in libSystem.dylib for compatibility, but we don't want any new code +// using them +// +NEVER_HERE(__register_frame_info_bases) +NEVER_HERE(__register_frame_info) +NEVER_HERE(__register_frame_info_table_bases) +NEVER_HERE(__register_frame_info_table) +NEVER_HERE(__register_frame_table) +NEVER_HERE(__deregister_frame_info) +NEVER_HERE(__deregister_frame_info_bases) + +#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS + + + + +#if _LIBUNWIND_BUILD_SJLJ_APIS +// +// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_5_0(_Unwind_GetIP) +NOT_HERE_BEFORE_5_0(_Unwind_SetGR) +NOT_HERE_BEFORE_5_0(_Unwind_SetIP) +NOT_HERE_BEFORE_5_0(_Unwind_DeleteException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register) +NOT_HERE_BEFORE_5_0(_Unwind_GetGR) +NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_5_0(_Unwind_GetCFA) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister) + +#endif // _LIBUNWIND_BUILD_SJLJ_APIS + + +namespace libunwind { + +_LIBUNWIND_HIDDEN +bool checkKeyMgrRegisteredFDEs(uintptr_t pc, void *&fde) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // lastly check for old style keymgr registration of dynamically generated + // FDEs acquire exclusive access to libgcc_object_info + libgcc_object_info *head = (libgcc_object_info *) + _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); + if (head != NULL) { + // look at each FDE in keymgr + for (libgcc_object *ob = head->unseen_objects; ob != NULL; ob = ob->next) { + CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; + CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; + const char *msg = CFI_Parser<LocalAddressSpace>::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (uintptr_t)ob->fde, &fdeInfo, &cieInfo); + if (msg == NULL) { + // Check if this FDE is for a function that includes the pc + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) { + fde = (void*)fdeInfo.pcStart; + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, + head); + return true; + } + } + } + } + // release libgcc_object_info + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); +#else + (void)pc; + (void)fde; +#endif + return false; +} + +} + + +#if !FOR_DYLD && _LIBUNWIND_BUILD_SJLJ_APIS + +#include <System/pthread_machdep.h> + +// Accessors to get get/set linked list of frames for sjlj based execeptions. +_LIBUNWIND_HIDDEN +struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() { + return (struct _Unwind_FunctionContext *) + _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key); +} + +_LIBUNWIND_HIDDEN +void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) { + _pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc); +} +#endif + + + + diff --git a/system/lib/libcxxabi/src/Unwind/assembly.h b/system/lib/libcxxabi/src/Unwind/assembly.h new file mode 100644 index 00000000..d282de8c --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/assembly.h @@ -0,0 +1,44 @@ +/* ===-- assembly.h - libUnwind assembler support macros -------------------=== + * + * 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. + * + * ===----------------------------------------------------------------------=== + * + * This file defines macros for use in libUnwind assembler source. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef UNWIND_ASSEMBLY_H +#define UNWIND_ASSEMBLY_H + +#if defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) +#define SEPARATOR @ +#else +#define SEPARATOR ; +#endif + +#if defined(__APPLE__) +#define HIDDEN_DIRECTIVE .private_extern +#else +#define HIDDEN_DIRECTIVE .hidden +#endif + +#define GLUE2(a, b) a ## b +#define GLUE(a, b) GLUE2(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#define DEFINE_LIBUNWIND_FUNCTION(name) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_NAME(name): + +#define DEFINE_LIBUNWIND_PRIVATE_FUNCTION(name) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_NAME(name): + +#endif /* UNWIND_ASSEMBLY_H */ diff --git a/system/lib/libcxxabi/src/Unwind/config.h b/system/lib/libcxxabi/src/Unwind/config.h new file mode 100644 index 00000000..2b924641 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/config.h @@ -0,0 +1,108 @@ +//===----------------------------- config.h -------------------------------===// +// +// 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. +// +// +// Defines macros used within libuwind project. +// +//===----------------------------------------------------------------------===// + + +#ifndef LIBUNWIND_CONFIG_H +#define LIBUNWIND_CONFIG_H + +#include <assert.h> + +// Define static_assert() unless already defined by compiler. +#ifndef __has_feature + #define __has_feature(__x) 0 +#endif +#if !(__has_feature(cxx_static_assert)) + #define static_assert(__b, __m) \ + extern int compile_time_assert_failed[ ( __b ) ? 1 : -1 ] \ + __attribute__( ( unused ) ); +#endif + +// Platform specific configuration defines. +#if __APPLE__ + #include <Availability.h> + #ifdef __cplusplus + extern "C" { + #endif + void __assert_rtn(const char *, const char *, int, const char *) + __attribute__((noreturn)); + #ifdef __cplusplus + } + #endif + + #define _LIBUNWIND_BUILD_ZERO_COST_APIS (__i386__ || __x86_64__ || __arm64__) + #define _LIBUNWIND_BUILD_SJLJ_APIS (__arm__) + #define _LIBUNWIND_SUPPORT_FRAME_APIS (__i386__ || __x86_64__) + #define _LIBUNWIND_EXPORT __attribute__((visibility("default"))) + #define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden"))) + #define _LIBUNWIND_LOG(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__) + #define _LIBUNWIND_ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg) + + #if FOR_DYLD + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 0 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 0 + #else + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 0 + #endif + +#else + // #define _LIBUNWIND_BUILD_ZERO_COST_APIS + // #define _LIBUNWIND_BUILD_SJLJ_APIS + // #define _LIBUNWIND_SUPPORT_FRAME_APIS + // #define _LIBUNWIND_EXPORT + // #define _LIBUNWIND_HIDDEN + // #define _LIBUNWIND_LOG() + // #define _LIBUNWIND_ABORT() + // #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND + // #define _LIBUNWIND_SUPPORT_DWARF_UNWIND + // #define _LIBUNWIND_SUPPORT_DWARF_INDEX +#endif + + +// Macros that define away in non-Debug builds +#ifdef NDEBUG + #define _LIBUNWIND_DEBUG_LOG(msg, ...) + #define _LIBUNWIND_TRACE_API(msg, ...) + #define _LIBUNWIND_TRACING_UNWINDING 0 + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) + #define _LIBUNWIND_LOG_NON_ZERO(x) x +#else + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + #ifdef __cplusplus + } + #endif + #define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__) + #define _LIBUNWIND_LOG_NON_ZERO(x) \ + do { \ + int _err = x; \ + if ( _err != 0 ) \ + _LIBUNWIND_LOG("" #x "=%d in %s", _err, __FUNCTION__); \ + } while (0) + #define _LIBUNWIND_TRACE_API(msg, ...) \ + do { \ + if ( logAPIs() ) _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while(0) + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \ + do { \ + if ( logUnwinding() ) _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while(0) + #define _LIBUNWIND_TRACING_UNWINDING logUnwinding() +#endif + + +#endif // LIBUNWIND_CONFIG_H diff --git a/system/lib/libcxxabi/src/Unwind/dwarf2.h b/system/lib/libcxxabi/src/Unwind/dwarf2.h new file mode 100644 index 00000000..0dcd2ca9 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/dwarf2.h @@ -0,0 +1,237 @@ +//===------------------------------- dwarf2.h -----------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + + +/* + These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. +*/ + +#ifndef __DWARF2__ +#define __DWARF2__ + +// DWARF unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by GCC +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + +#endif diff --git a/system/lib/libcxxabi/src/Unwind/libunwind.cpp b/system/lib/libcxxabi/src/Unwind/libunwind.cpp new file mode 100644 index 00000000..2043ac23 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/libunwind.cpp @@ -0,0 +1,353 @@ +//===--------------------------- libuwind.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 unw_* functions from <libunwind.h> +// +//===----------------------------------------------------------------------===// + +#include <libunwind.h> + +#include <new> + +#include "libunwind_ext.h" +#include "config.h" + + +#if _LIBUNWIND_BUILD_ZERO_COST_APIS + +#include "UnwindCursor.hpp" + +using namespace libunwind; + +/// internal object to represent this processes address space +LocalAddressSpace LocalAddressSpace::sThisAddressSpace; + +/// record the registers and stack position of the caller +extern int unw_getcontext(unw_context_t *); +// note: unw_getcontext() implemented in assembly + +/// Create a cursor of a thread in this process given 'context' recorded by +/// unw_getcontext(). +_LIBUNWIND_EXPORT int unw_init_local(unw_cursor_t *cursor, + unw_context_t *context) { + _LIBUNWIND_TRACE_API("unw_init_local(cursor=%p, context=%p)\n", + cursor, context); + // Use "placement new" to allocate UnwindCursor in the cursor buffer. +#if __i386__ + new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_x86>( + context, LocalAddressSpace::sThisAddressSpace); +#elif __x86_64__ + new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_x86_64>( + context, LocalAddressSpace::sThisAddressSpace); +#elif __ppc__ + new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_ppc>( + context, LocalAddressSpace::sThisAddressSpace); +#elif __arm64__ + new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm64>( + context, LocalAddressSpace::sThisAddressSpace); +#endif + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->setInfoBasedOnIPRegister(); + + return UNW_ESUCCESS; +} + +#if UNW_REMOTE + +_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = + (unw_addr_space_t) & sThisAddressSpace; + +/// Create a cursor into a thread in another process. +_LIBUNWIND_EXPORT int unw_init_remote_thread(unw_cursor_t *cursor, + unw_addr_space_t as, + void *arg) { + // special case: unw_init_remote(xx, unw_local_addr_space, xx) + if (as == (unw_addr_space_t) & sThisAddressSpace) + return unw_init_local(cursor, NULL); //FIXME + + // use "placement new" to allocate UnwindCursor in the cursor buffer + switch (as->cpuType) { + case CPU_TYPE_I386: + new ((void *)cursor) + UnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >, + Registers_x86>(((unw_addr_space_i386 *)as)->oas, arg); + break; + case CPU_TYPE_X86_64: + new ((void *)cursor) UnwindCursor< + OtherAddressSpace<Pointer64<LittleEndian> >, Registers_x86_64>( + ((unw_addr_space_x86_64 *)as)->oas, arg); + break; + case CPU_TYPE_POWERPC: + new ((void *)cursor) + UnwindCursor<OtherAddressSpace<Pointer32<BigEndian> >, Registers_ppc>( + ((unw_addr_space_ppc *)as)->oas, arg); + break; + default: + return UNW_EUNSPEC; + } + return UNW_ESUCCESS; +} + + +static bool is64bit(task_t task) { + return false; // FIXME +} + +/// Create an address_space object for use in examining another task. +_LIBUNWIND_EXPORT unw_addr_space_t unw_create_addr_space_for_task(task_t task) { +#if __i386__ + if (is64bit(task)) { + unw_addr_space_x86_64 *as = new unw_addr_space_x86_64(task); + as->taskPort = task; + as->cpuType = CPU_TYPE_X86_64; + //as->oas + } else { + unw_addr_space_i386 *as = new unw_addr_space_i386(task); + as->taskPort = task; + as->cpuType = CPU_TYPE_I386; + //as->oas + } +#else +// FIXME +#endif +} + + +/// Delete an address_space object. +_LIBUNWIND_EXPORT void unw_destroy_addr_space(unw_addr_space_t asp) { + switch (asp->cpuType) { +#if __i386__ || __x86_64__ + case CPU_TYPE_I386: { + unw_addr_space_i386 *as = (unw_addr_space_i386 *)asp; + delete as; + } + break; + case CPU_TYPE_X86_64: { + unw_addr_space_x86_64 *as = (unw_addr_space_x86_64 *)asp; + delete as; + } + break; +#endif + case CPU_TYPE_POWERPC: { + unw_addr_space_ppc *as = (unw_addr_space_ppc *)asp; + delete as; + } + break; + } +} +#endif // UNW_REMOTE + + +/// Get value of specified register at cursor position in stack frame. +_LIBUNWIND_EXPORT int unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t *value) { + _LIBUNWIND_TRACE_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", + cursor, regNum, value); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + *value = co->getReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + + +/// 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) { + _LIBUNWIND_TRACE_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", + cursor, regNum, value); + typedef LocalAddressSpace::pint_t pint_t; + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + co->setReg(regNum, (pint_t)value); + // specical case altering IP to re-find info (being called by personality + // function) + if (regNum == UNW_REG_IP) { + unw_proc_info_t info; + co->getInfo(&info); + pint_t orgArgSize = (pint_t)info.gp; + uint64_t orgFuncStart = info.start_ip; + co->setInfoBasedOnIPRegister(false); + // and adjust REG_SP if there was a DW_CFA_GNU_args_size + if ((orgFuncStart == info.start_ip) && (orgArgSize != 0)) + co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize); + } + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + + +/// Get value of specified float register at cursor position in stack frame. +_LIBUNWIND_EXPORT int unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_fpreg_t *value) { + _LIBUNWIND_TRACE_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", + cursor, regNum, value); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + *value = co->getFloatReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + + +/// 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) { + _LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", + cursor, regNum, value); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + co->setFloatReg(regNum, value); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + + +/// Move cursor to next frame. +_LIBUNWIND_EXPORT int unw_step(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("unw_step(cursor=%p)\n", cursor); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->step(); +} + + +/// Get unwind info at cursor position in stack frame. +_LIBUNWIND_EXPORT int unw_get_proc_info(unw_cursor_t *cursor, + unw_proc_info_t *info) { + _LIBUNWIND_TRACE_API("unw_get_proc_info(cursor=%p, &info=%p)\n", + cursor, info); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->getInfo(info); + if (info->end_ip == 0) + return UNW_ENOINFO; + else + return UNW_ESUCCESS; +} + + +/// Resume execution at cursor position (aka longjump). +_LIBUNWIND_EXPORT int unw_resume(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("unw_resume(cursor=%p)\n", cursor); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->jumpto(); + return UNW_EUNSPEC; +} + + +/// Get name of function at cursor position in stack frame. +_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); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->getFunctionName(buf, bufLen, offset)) + return UNW_ESUCCESS; + else + return UNW_EUNSPEC; +} + + +/// Checks if a register is a floating-point register. +_LIBUNWIND_EXPORT int unw_is_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", + cursor, regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->validFloatReg(regNum); +} + + +/// Checks if a register is a floating-point register. +_LIBUNWIND_EXPORT const char *unw_regname(unw_cursor_t *cursor, + unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("unw_regname(cursor=%p, regNum=%d)\n", + cursor, regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->getRegisterName(regNum); +} + + +/// Checks if current frame is signal trampoline. +_LIBUNWIND_EXPORT int unw_is_signal_frame(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("unw_is_signal_frame(cursor=%p)\n", cursor); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->isSignalFrame(); +} + + +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND +/// SPI: walks cached dwarf entries +_LIBUNWIND_EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_TRACE_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func); + DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func); +} + + +/// IPI: for __register_frame() +void _unw_add_dynamic_fde(unw_word_t fde) { + CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; + CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; + const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo); + if (message == NULL) { + // dynamically registered FDEs don't have a mach_header group they are in. + // Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group, + fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } else { + _LIBUNWIND_DEBUG_LOG("_unw_add_dynamic_fde: bad fde: %s", message); + } +} + +/// IPI: for __deregister_frame() +void _unw_remove_dynamic_fde(unw_word_t fde) { + // fde is own mh_group + DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde); +} +#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND + +#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS + + + +// Add logging hooks in Debug builds only +#ifndef NDEBUG + +_LIBUNWIND_HIDDEN +bool logAPIs() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); + checked = true; + } + return log; +} + +_LIBUNWIND_HIDDEN +bool logUnwinding() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); + checked = true; + } + return log; +} + +#endif // NDEBUG + diff --git a/system/lib/libcxxabi/src/Unwind/libunwind_ext.h b/system/lib/libcxxabi/src/Unwind/libunwind_ext.h new file mode 100644 index 00000000..38d71cce --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/libunwind_ext.h @@ -0,0 +1,38 @@ +//===------------------------ libunwind_ext.h -----------------------------===// +// +// 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. +// +// +// Extensions to libunwind API. +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND_EXT__ +#define __LIBUNWIND_EXT__ + +#include <libunwind.h> + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __cplusplus +extern "C" { +#endif +// SPI +extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, + unw_word_t ip_end, + unw_word_t fde, + unw_word_t mh)); + +// IPI +extern void _unw_add_dynamic_fde(unw_word_t fde); +extern void _unw_remove_dynamic_fde(unw_word_t fde); + +#ifdef __cplusplus +} +#endif + +#endif // __LIBUNWIND_EXT__ diff --git a/system/lib/libcxxabi/src/Unwind/unwind_ext.h b/system/lib/libcxxabi/src/Unwind/unwind_ext.h new file mode 100644 index 00000000..c40ce6a1 --- /dev/null +++ b/system/lib/libcxxabi/src/Unwind/unwind_ext.h @@ -0,0 +1,37 @@ +//===-------------------------- unwind_ext.h ------------------------------===// +// +// 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. +// +// +// Extensions to unwind API. +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_EXT__ +#define __UNWIND_EXT__ + +#include "unwind.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These platform specific functions to get and set the top context are +// implemented elsewhere. + +extern struct _Unwind_FunctionContext * +__Unwind_SjLj_GetTopOfFunctionStack(); + +extern void +__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc); + +#ifdef __cplusplus +} +#endif + +#endif // __UNWIND_EXT__ + + |