From: Lionel Sambuc Date: Sat, 7 Dec 2013 18:53:33 +0000 (+0100) Subject: Importing sys/libunwind for clang X-Git-Tag: v3.3.0~167 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/already%20found%20%2A/doxygen-warnings.log?a=commitdiff_plain;h=472758f313bea9d25f2bf25a77fd07feeaa76cfc;p=minix.git Importing sys/libunwind for clang Change-Id: Ib073b27e3b883837e682414ef7df56c84ca10816 --- diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 05f87dfb3..470c2bee9 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -1780,6 +1780,7 @@ ./usr/include/ulimit.h minix-sys ./usr/include/unctrl.h minix-sys ./usr/include/unistd.h minix-sys +./usr/include/unwind.h minix-sys llvm ./usr/include/util.h minix-sys ./usr/include/utime.h minix-sys ./usr/include/utmp.h minix-sys diff --git a/sys/lib/libunwind/AddressSpace.hpp b/sys/lib/libunwind/AddressSpace.hpp new file mode 100644 index 000000000..31b56191b --- /dev/null +++ b/sys/lib/libunwind/AddressSpace.hpp @@ -0,0 +1,480 @@ +//===------------------------- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(__minix) +#include +#else +#define pthread_rwlock_init(l, d) /* nothing */ +#define pthread_rwlock_rdlock(l) /* nothing */ +#define pthread_rwlock_wrlock(l) /* nothing */ +#define pthread_rwlock_unlock(l) /* nothing */ +#endif /* !defined(__minix) */ + +#include "dwarf2.h" + +namespace _Unwind { + +static int rangeCmp(void *, const void *, const void *); +static int rangeCmpKey(void *, const void *, const void *); +static int dsoTableCmp(void *, const void *, const void *); +static int dsoTableCmpKey(void *, const void *, const void *); +static int phdr_callback(struct dl_phdr_info *, size_t, void *); + +struct unw_proc_info_t { + uintptr_t data_base; // Base address for data-relative relocations + uintptr_t start_ip; // Start address of function + uintptr_t end_ip; // First address after end of function + uintptr_t lsda; // Address of Language Specific Data Area + uintptr_t handler; // Personality routine + uintptr_t extra_args; // Extra stack space for frameless routines + uint32_t unwind_info_size; // Size of DWARF unwind info + uintptr_t unwind_info; // Address of DWARF unwind info +}; + +/// 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 LocalAddressSpace { +public: + typedef uintptr_t pint_t; + typedef intptr_t sint_t; + + typedef void (*findPCRange_t)(LocalAddressSpace &, pint_t, pint_t &pcStart, + pint_t &pcEnd); + + LocalAddressSpace(findPCRange_t findPCRange_) + : findPCRange(findPCRange_), needsReload(true) { + static const rb_tree_ops_t segmentTreeOps = { + rangeCmp, rangeCmpKey, offsetof(Range, range_link), NULL + }; + static const rb_tree_ops_t dsoTreeOps = { + dsoTableCmp, dsoTableCmpKey, offsetof(Range, dso_link), NULL + }; + rb_tree_init(&segmentTree, &segmentTreeOps); + rb_tree_init(&dsoTree, &dsoTreeOps); + pthread_rwlock_init(&fdeTreeLock, NULL); + } + + 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); } + + uintptr_t getP(pint_t addr) { + if (sizeof(uintptr_t) == sizeof(uint32_t)) + return get32(addr); + else + return get64(addr); + } + + uint64_t getULEB128(pint_t &addr, pint_t end) { + uint64_t result = 0; + uint8_t byte; + int bit = 0; + do { + uint64_t b; + + assert(addr != end); + + byte = get8(addr++); + b = byte & 0x7f; + + assert(bit < 64); + assert(b << bit >> bit == b); + + result |= b << bit; + bit += 7; + } while (byte >= 0x80); + return result; + } + + int64_t getSLEB128(pint_t &addr, pint_t end) { + uint64_t result = 0; + uint8_t byte; + int bit = 0; + do { + uint64_t b; + + assert(addr != end); + + byte = get8(addr++); + b = byte & 0x7f; + + assert(bit < 64); + assert(b << bit >> bit == b); + + result |= b << bit; + bit += 7; + } while (byte >= 0x80); + // sign extend negative numbers + if ((byte & 0x40) != 0) + result |= (-1LL) << bit; + return result; + } + + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + const unw_proc_info_t *ctx) { + pint_t startAddr = addr; + const uint8_t *p = (uint8_t *)addr; + pint_t result; + + if (encoding == DW_EH_PE_omit) + return 0; + if (encoding == DW_EH_PE_aligned) { + addr = (addr + sizeof(pint_t) - 1) & sizeof(pint_t); + return getP(addr); + } + + // 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 = 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 = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_omit: + result = 0; + break; + default: + assert(0 && "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: + assert(0 && "DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + assert(ctx != NULL && "DW_EH_PE_datarel without context"); + if (ctx) + result += ctx->data_base; + break; + case DW_EH_PE_funcrel: + assert(ctx != NULL && "DW_EH_PE_funcrel without context"); + if (ctx) + result += ctx->start_ip; + break; + case DW_EH_PE_aligned: + __builtin_unreachable(); + default: + assert(0 && "unknown pointer encoding"); + break; + } + + if (encoding & DW_EH_PE_indirect) + result = getP(result); + + return result; + } + + bool findFDE(pint_t pc, pint_t &fdeStart, pint_t &data_base) { + Range *n; + for (;;) { + pthread_rwlock_rdlock(&fdeTreeLock); + n = (Range *)rb_tree_find_node(&segmentTree, &pc); + pthread_rwlock_unlock(&fdeTreeLock); + if (n != NULL) + break; + if (!needsReload) + break; + lazyReload(); + } + if (n == NULL) + return false; + if (n->hdr_start == 0) { + fdeStart = n->hdr_base; + return true; + } + + pint_t base = n->hdr_base; + pint_t first = n->hdr_start; + pint_t len = n->hdr_entries; + while (len) { + pint_t next = first + ((len + 1) / 2) * 8; + pint_t nextPC = base + (int32_t)get32(next); + if (nextPC == pc) { + first = next; + break; + } + if (nextPC < pc) { + len -= (len + 1) / 2; + first = next; + } else if (len == 1) + break; + else + len = (len + 1) / 2; + } + fdeStart = base + (int32_t)get32(first + 4); + return true; + } + + bool addFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) { + pthread_rwlock_wrlock(&fdeTreeLock); + Range *n = (Range *)malloc(sizeof(*n)); + n->hdr_base = fde; + n->hdr_start = 0; + n->hdr_entries = 0; + n->first_pc = pcStart; + n->last_pc = pcEnd; + n->data_base = 0; + n->ehframe_base = 0; + if (rb_tree_insert_node(&segmentTree, n) == n) { + pthread_rwlock_unlock(&fdeTreeLock); + return true; + } + free(n); + pthread_rwlock_unlock(&fdeTreeLock); + return false; + } + + bool removeFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) { + pthread_rwlock_wrlock(&fdeTreeLock); + Range *n = (Range *)rb_tree_find_node(&segmentTree, &pcStart); + if (n == NULL) { + pthread_rwlock_unlock(&fdeTreeLock); + return false; + } + assert(n->first_pc == pcStart); + assert(n->last_pc == pcEnd); + assert(n->hdr_base == fde); + assert(n->hdr_start == 0); + assert(n->hdr_entries == 0); + assert(n->data_base == 0); + assert(n->ehframe_base == 0); + rb_tree_remove_node(&segmentTree, n); + free(n); + pthread_rwlock_unlock(&fdeTreeLock); + return true; + } + + void removeDSO(pint_t ehFrameBase) { + pthread_rwlock_wrlock(&fdeTreeLock); + Range *n; + n = (Range *)rb_tree_find_node(&dsoTree, &ehFrameBase); + if (n == NULL) { + pthread_rwlock_unlock(&fdeTreeLock); + return; + } + rb_tree_remove_node(&dsoTree, n); + rb_tree_remove_node(&segmentTree, n); + free(n); + pthread_rwlock_unlock(&fdeTreeLock); + } + + void setLazyReload() { + pthread_rwlock_wrlock(&fdeTreeLock); + needsReload = true; + pthread_rwlock_unlock(&fdeTreeLock); + } + +private: + findPCRange_t findPCRange; + bool needsReload; +#if !defined(__minix) + pthread_rwlock_t fdeTreeLock; +#endif /* !defined(__minix) */ + rb_tree_t segmentTree; + rb_tree_t dsoTree; + + friend int phdr_callback(struct dl_phdr_info *, size_t, void *); + friend int rangeCmp(void *, const void *, const void *); + friend int rangeCmpKey(void *, const void *, const void *); + friend int dsoTableCmp(void *, const void *, const void *); + friend int dsoTableCmpKey(void *, const void *, const void *); + + void updateRange(); + + struct Range { + rb_node_t range_link; + rb_node_t dso_link; + pint_t hdr_base; // Pointer to FDE if hdr_start == 0 + pint_t hdr_start; + pint_t hdr_entries; + pint_t first_pc; + pint_t last_pc; + pint_t data_base; + pint_t ehframe_base; + }; + + void lazyReload() { + pthread_rwlock_wrlock(&fdeTreeLock); + dl_iterate_phdr(phdr_callback, this); + needsReload = false; + pthread_rwlock_unlock(&fdeTreeLock); + } + + void addDSO(pint_t header, pint_t data_base) { + if (header == 0) + return; + if (get8(header) != 1) + return; + if (get8(header + 3) != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) + return; + pint_t end = header + 4; + pint_t ehframe_base = getEncodedP(end, 0, get8(header + 1), NULL); + pint_t entries = getEncodedP(end, 0, get8(header + 2), NULL); + pint_t start = (end + 3) & ~pint_t(3); + if (entries == 0) + return; + Range *n = (Range *)malloc(sizeof(*n)); + n->hdr_base = header; + n->hdr_start = start; + n->hdr_entries = entries; + n->first_pc = header + (int32_t)get32(n->hdr_start); + pint_t tmp; + (*findPCRange)( + *this, header + (int32_t)get32(n->hdr_start + (entries - 1) * 8 + 4), + tmp, n->last_pc); + n->data_base = data_base; + n->ehframe_base = ehframe_base; + + if (rb_tree_insert_node(&segmentTree, n) != n) { + free(n); + return; + } + rb_tree_insert_node(&dsoTree, n); + } +}; + +static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data_) { + LocalAddressSpace *data = (LocalAddressSpace *)data_; + size_t eh_frame = 0, data_base = 0; + const Elf_Phdr *hdr = info->dlpi_phdr; + const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum; + const Elf_Dyn *dyn; + + for (; hdr != last_hdr; ++hdr) { + switch (hdr->p_type) { + case PT_GNU_EH_FRAME: + eh_frame = info->dlpi_addr + hdr->p_vaddr; + break; + case PT_DYNAMIC: + dyn = (const Elf_Dyn *)(info->dlpi_addr + hdr->p_vaddr); + while (dyn->d_tag != DT_NULL) { + if (dyn->d_tag == DT_PLTGOT) { + data_base = info->dlpi_addr + dyn->d_un.d_ptr; + break; + } + ++dyn; + } + } + } + + if (eh_frame) + data->addDSO(eh_frame, data_base); + + return 0; +} + +static int rangeCmp(void *context, const void *n1_, const void *n2_) { + LocalAddressSpace::Range *n1 = (LocalAddressSpace::Range *)n1_; + LocalAddressSpace::Range *n2 = (LocalAddressSpace::Range *)n2_; + + if (n1->first_pc < n2->first_pc) + return -1; + if (n1->first_pc > n2->first_pc) + return 1; + assert(n1->last_pc == n2->last_pc); + return 0; +} + +static int rangeCmpKey(void *context, const void *n_, const void *pc_) { + LocalAddressSpace::Range *n = (LocalAddressSpace::Range *)n_; + LocalAddressSpace::pint_t *pc = (LocalAddressSpace::pint_t *)pc_; + if (n->last_pc < *pc) + return -1; + if (n->first_pc > *pc) + return 1; + return 0; +} + +static int dsoTableCmp(void *context, const void *n1_, const void *n2_) { + LocalAddressSpace::Range *n1 = (LocalAddressSpace::Range *)n1_; + LocalAddressSpace::Range *n2 = (LocalAddressSpace::Range *)n2_; + + if (n1->ehframe_base < n2->ehframe_base) + return -1; + if (n1->ehframe_base > n2->ehframe_base) + return 1; + return 0; +} + +static int dsoTableCmpKey(void *context, const void *n_, const void *ptr_) { + LocalAddressSpace::Range *n = (LocalAddressSpace::Range *)n_; + LocalAddressSpace::pint_t *ptr = (LocalAddressSpace::pint_t *)ptr_; + if (n->ehframe_base < *ptr) + return -1; + if (n->ehframe_base > *ptr) + return 1; + return 0; +} + +} // namespace _Unwind + +#endif // __ADDRESSSPACE_HPP__ diff --git a/sys/lib/libunwind/CREDITS.TXT b/sys/lib/libunwind/CREDITS.TXT new file mode 100644 index 000000000..bd54ba905 --- /dev/null +++ b/sys/lib/libunwind/CREDITS.TXT @@ -0,0 +1,42 @@ +This file is a partial list of people who have contributed to the LLVM/libc++abi +project. If you have contributed a patch or made some other contribution to +LLVM/libc++abi, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Marshall Clow +E: mclow.lists@gmail.com +E: marshall@idio.com +D: Architect and primary coauthor of libc++abi + +N: Matthew Dempsky +E: matthew@dempsky.org +D: Minor patches and bug fixes. + +N: Nowar Gu +E: wenhan.gu@gmail.com +D: Minor patches and fixes + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary coauthor of libc++abi + +N: Nick Kledzik +E: kledzik@apple.com + +N: Bruce Mitchener, Jr. +E: bruce.mitchener@gmail.com +D: Minor typo fixes + +N: Andrew Morrow +E: andrew.c.morrow@gmail.com +D: Minor patches and fixes + +N: Erik Olofsson +E: erik.olofsson@hansoft.se +E: erik@olofsson.info +D: Minor patches and fixes diff --git a/sys/lib/libunwind/DwarfInstructions.hpp b/sys/lib/libunwind/DwarfInstructions.hpp new file mode 100644 index 000000000..37c0a3c13 --- /dev/null +++ b/sys/lib/libunwind/DwarfInstructions.hpp @@ -0,0 +1,597 @@ +//===-------------------------- 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 +#include + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" + +namespace _Unwind { + +enum step_result { + UNW_STEP_SUCCESS, + UNW_STEP_END, + UNW_STEP_FAILED +}; + +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular +/// architecture +template class DwarfInstructions { +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static step_result stepWithDwarf(A &, pint_t, pint_t, R &, unw_proc_info_t *); + +private: + // Pseudo-register used for return addresses. + enum { + DW_X86_RET_ADDR = 8, + DW_X86_64_RET_ADDR = 16, + }; + + static pint_t evaluateExpression(pint_t, A &, const R &, pint_t); + static pint_t + getSavedRegister(A &, const R &, pint_t, + const typename CFI_Parser::RegisterLocation &); + static pint_t + computeRegisterLocation(A &, const R &, pint_t, + const typename CFI_Parser::RegisterLocation &); + + static int lastRestoreReg(const R &) { return R::LAST_RESTORE_REG; } + static bool isReturnAddressRegister(int regno, const R &) { + return regno == R::IP_PSEUDO_REG; + } + + static pint_t getCFA(A &addressSpace, + const typename CFI_Parser::PrologInfo &prolog, + const R ®isters) { + if (prolog.cfaRegister != 0) + return registers.getRegister(prolog.cfaRegister) + + prolog.cfaRegisterOffset; + if (prolog.cfaExpression != 0) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, + 0); + assert(0 && "getCFA(): unknown location"); + __builtin_unreachable(); + } +}; + +template +typename A::pint_t DwarfInstructions::getSavedRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const typename CFI_Parser::RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getP(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getP( + evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister(savedReg.value); + + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + assert(0 && "unsupported restore location for register"); + } + __builtin_unreachable(); +} + +template +typename DwarfInstructions::pint_t +DwarfInstructions::computeRegisterLocation( + A &addressSpace, const R ®isters, pint_t cfa, + const typename CFI_Parser::RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return cfa + savedReg.value; + + case CFI_Parser::kRegisterAtExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + assert(0 && "unsupported restore location for float/vector register"); + } + __builtin_unreachable(); +} + +template +step_result DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, + pint_t fdeStart, + R ®isters, + unw_proc_info_t *ctx) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo, + ctx)) + return UNW_STEP_FAILED; + + typename CFI_Parser::PrologInfo prolog; + if (!CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, + pc, &prolog, ctx)) + return UNW_STEP_FAILED; + + // Create working copy of the register set. + R newRegisters = registers; + + // Get pointer to CFA by the architecture-specific code. + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // Restore registers according to DWARF instructions + pint_t returnAddress = 0; + for (int i = 0; i <= lastRestoreReg(newRegisters); ++i) { + if (prolog.savedRegisters[i].location == CFI_Parser::kRegisterUnused) + continue; + 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 if (registers.validFloatVectorRegister(i)) + newRegisters.copyFloatVectorRegister( + i, computeRegisterLocation(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else + return UNW_STEP_FAILED; + } + + // The CFA is defined as the stack pointer at the call site. + // Therefore the SP is restored by setting it to the CFA. + newRegisters.setSP(cfa); + newRegisters.setIP(returnAddress); + + // Now replace register set with the working copy. + registers = newRegisters; + + return UNW_STEP_SUCCESS; +} + +template +typename A::pint_t +DwarfInstructions::evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue) { + pint_t p = expression; + pint_t expressionEnd = expression + 20; // Rough estimate + uint64_t length = addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + pint_t stack[100]; + pint_t *sp = stack; + *(++sp) = initialStackValue; + + while (p < expressionEnd) { + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue; + 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; + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t)addressSpace.get8(p); + p += 1; + *(++sp) = svalue; + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t)addressSpace.get16(p); + p += 2; + *(++sp) = svalue; + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = svalue; + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = addressSpace.get64(p); + p += 8; + *(++sp) = value; + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (int32_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = svalue; + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + break; + + case DW_OP_drop: + // pop + --sp; + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((uint64_t *)value); + break; + + case DW_OP_abs: + svalue = *sp; + if (svalue < 0) + *sp = -svalue; + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + break; + + case DW_OP_div: + svalue = *sp--; + *sp = *sp / svalue; + break; + + case DW_OP_minus: + svalue = *sp--; + *sp = *sp - svalue; + break; + + case DW_OP_mod: + svalue = *sp--; + *sp = *sp % svalue; + break; + + case DW_OP_mul: + svalue = *sp--; + *sp = *sp * svalue; + break; + + case DW_OP_neg: + *sp = 0 - *sp; + break; + + case DW_OP_not: + svalue = *sp; + *sp = ~svalue; + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + break; + + case DW_OP_shra: + value = *sp--; + svalue = *sp; + *sp = svalue >> value; + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + break; + + case DW_OP_skip: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + p += svalue; + break; + + case DW_OP_bra: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + if (*sp--) + p += svalue; + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + 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; + 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(reg); + break; + + case DW_OP_regx: + reg = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg); + 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 = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + break; + + case DW_OP_bregx: + reg = addressSpace.getULEB128(p, expressionEnd); + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + 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 = addressSpace.get64(value); + break; + default: + assert(0 && "DW_OP_deref_size with bad size"); + } + *(++sp) = value; + break; + + case DW_OP_fbreg: + case DW_OP_piece: + 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: + assert(0 && "dwarf opcode not implemented"); + } + } + return *sp; +} + +} // namespace _Unwind + +#endif // __DWARF_INSTRUCTIONS_HPP__ diff --git a/sys/lib/libunwind/DwarfParser.hpp b/sys/lib/libunwind/DwarfParser.hpp new file mode 100644 index 000000000..671502022 --- /dev/null +++ b/sys/lib/libunwind/DwarfParser.hpp @@ -0,0 +1,529 @@ +//===--------------------------- 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 +#include + +#include "dwarf2.h" +#include "AddressSpace.hpp" + +namespace _Unwind { + +/// 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 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; + pint_t personality; + uint32_t codeAlignFactor; + int dataAlignFactor; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + 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; + }; + + /// Information about a frame layout and registers saved determined + /// by "running" the DWARF FDE "instructions" + enum { + kMaxRegisterNumber = R::LAST_REGISTER + 1 + }; + enum RegisterSavedWhere { + kRegisterUnused, + kRegisterInCFA, + kRegisterOffsetFromCFA, + kRegisterInRegister, + kRegisterAtExpression, + kRegisterIsExpression, + }; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + RegisterLocation savedRegisters[kMaxRegisterNumber]; + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) + : next(n), info(i) {} + PrologInfoStackEntry *next; + PrologInfo info; + }; + + static void findPCRange(A &, pint_t, pint_t &, pint_t &); + + static bool decodeFDE(A &, pint_t, FDE_Info *, CIE_Info *, + unw_proc_info_t *ctx); + static bool parseFDEInstructions(A &, const FDE_Info &, const CIE_Info &, + pint_t, PrologInfo *, unw_proc_info_t *ctx); + + static bool parseCIE(A &, pint_t, CIE_Info *); + +private: + static bool parseInstructions(A &, pint_t, pint_t, const CIE_Info &, pint_t, + PrologInfoStackEntry *&, PrologInfo *, + unw_proc_info_t *ctx); +}; + +/// +/// Parse a FDE and return the last PC it covers. +/// +template +void CFI_Parser::findPCRange(A &addressSpace, pint_t fde, pint_t &pcStart, + pint_t &pcEnd) { + pcStart = 0; + pcEnd = 0; + pint_t p = fde; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really the next 8 Bytes. + cfiLength = addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return; + uint32_t ciePointer = addressSpace.get32(p); + if (ciePointer == 0) + return; + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p - ciePointer; + typename CFI_Parser::CIE_Info cieInfo; + if (!parseCIE(addressSpace, cieStart, &cieInfo)) + return; + p += 4; + // Parse pc begin and range. + pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding, NULL); + pcEnd = pcStart + addressSpace.getEncodedP( + p, nextCFI, cieInfo.pointerEncoding & 0x0F, NULL); +} + +/// +/// Parse a FDE into a CIE_Info and an FDE_Info +/// +template +bool CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo, + unw_proc_info_t *ctx) { + pint_t p = fdeStart; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really the next 8 Bytes. + cfiLength = addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return false; + uint32_t ciePointer = addressSpace.get32(p); + if (ciePointer == 0) + return false; + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p - ciePointer; + if (!parseCIE(addressSpace, cieStart, cieInfo)) + return false; + p += 4; + // Parse pc begin and range. + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding, ctx); + pint_t pcRange = addressSpace.getEncodedP( + p, nextCFI, cieInfo->pointerEncoding & 0x0F, ctx); + // Parse rest of info. + fdeInfo->lsda = 0; + // Check for augmentation length + if (cieInfo->fdesHaveAugmentationData) { + uintptr_t augLen = 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, + ctx) != 0) { + // Reset pointer and re-parse LSDA address. + p = lsdaStart; + fdeInfo->lsda = + addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding, ctx); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return true; +} + +/// Extract info from a CIE +template +bool CFI_Parser::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; + uint64_t cieLength = addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if (cieLength == 0xffffffff) { + // 0xffffffff means length is really the next 8 Bytes. + cieLength = addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if (cieLength == 0) + return true; + // CIE ID is always 0 + if (addressSpace.get32(p) != 0) + return false; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if (version != 1 && version != 3) + return false; + ++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 = addressSpace.getULEB128(p, cieContentEnd); + // Parse data alignment factor + cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); + // Parse return address register + addressSpace.getULEB128(p, cieContentEnd); + // Parse augmentation data based on augmentation string. + 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 = p - cie; + cieInfo->personality = addressSpace.getEncodedP( + p, cieContentEnd, cieInfo->personalityEncoding, NULL); + 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 true; +} + +/// "Run" the dwarf instructions and create the abstact PrologInfo for an FDE. +template +bool CFI_Parser::parseFDEInstructions(A &addressSpace, + const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, + pint_t upToPC, PrologInfo *results, + unw_proc_info_t *ctx) { + // Clear results. + memset(results, 0, sizeof(*results)); + PrologInfoStackEntry *rememberStack = NULL; + + // First parse the CIE then FDE instructions. + if (!parseInstructions(addressSpace, cieInfo.cieInstructions, + cieInfo.cieStart + cieInfo.cieLength, cieInfo, + (pint_t)(-1), rememberStack, results, ctx)) + return false; + return parseInstructions(addressSpace, fdeInfo.fdeInstructions, + fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, + upToPC - fdeInfo.pcStart, rememberStack, results, + ctx); +} + +/// "Run" the DWARF instructions. +template +bool +CFI_Parser::parseInstructions(A &addressSpace, pint_t instructions, + pint_t instructionsEnd, + const CIE_Info &cieInfo, pint_t pcoffset, + PrologInfoStackEntry *&rememberStack, + PrologInfo *results, unw_proc_info_t *ctx) { + pint_t p = instructions; + uint32_t codeOffset = 0; + PrologInfo initialState = *results; + + // 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: + break; + case DW_CFA_set_loc: + codeOffset = addressSpace.getEncodedP(p, instructionsEnd, + cieInfo.pointerEncoding, ctx); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + break; + case DW_CFA_offset_extended: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + offset = + addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + break; + case DW_CFA_restore_extended: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + break; + case DW_CFA_undefined: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg].location = kRegisterUnused; + break; + case DW_CFA_same_value: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + // "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; + break; + case DW_CFA_register: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + reg2 = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + if (reg2 > kMaxRegisterNumber) + return false; + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = 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) + return false; + + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + break; + case DW_CFA_restore_state: + if (rememberStack == NULL) + return false; + { + PrologInfoStackEntry *top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char *)top); + } + break; + case DW_CFA_def_cfa: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + offset = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) + return false; + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + break; + case DW_CFA_def_cfa_register: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + results->cfaRegister = reg; + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = codeOffset; + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + break; + case DW_CFA_expression: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + break; + case DW_CFA_offset_extended_sf: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + break; + case DW_CFA_def_cfa_sf: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) + return false; + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->codeOffsetAtStackDecrement = codeOffset; + break; + case DW_CFA_val_offset: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + offset = + addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + break; + case DW_CFA_val_offset_sf: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + offset = + addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + break; + case DW_CFA_val_expression: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + break; + case DW_CFA_GNU_args_size: + offset = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = offset; + break; + case DW_CFA_GNU_negative_offset_extended: + reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); + if (reg > kMaxRegisterNumber) + return false; + offset = + addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + break; + default: + operand = opcode & 0x3F; + switch (opcode & 0xC0) { + case DW_CFA_offset: + reg = R::dwarf2regno(operand); + if (reg > kMaxRegisterNumber) + return false; + offset = addressSpace.getULEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + break; + case DW_CFA_restore: + reg = R::dwarf2regno(operand); + if (reg > kMaxRegisterNumber) + return false; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + break; + default: + return false; + } + } + } + + return true; +} + +} // namespace _Unwind + +#endif // __DWARF_PARSER_HPP__ diff --git a/sys/lib/libunwind/LICENSE.TXT b/sys/lib/libunwind/LICENSE.TXT new file mode 100644 index 000000000..b04ba2c96 --- /dev/null +++ b/sys/lib/libunwind/LICENSE.TXT @@ -0,0 +1,76 @@ +============================================================================== +libc++abi License +============================================================================== + +The libc++abi library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/sys/lib/libunwind/Makefile.inc b/sys/lib/libunwind/Makefile.inc new file mode 100644 index 000000000..4a5940786 --- /dev/null +++ b/sys/lib/libunwind/Makefile.inc @@ -0,0 +1,10 @@ +# $NetBSD: Makefile.inc,v 1.1 2013/10/14 01:14:57 joerg Exp $ + +.PATH: ${NETBSDSRCDIR}/sys/lib/libunwind + +SRCS+= libunwind.cxx \ + unwind_registers.S + +INCS+= unwind.h + +COPTS.libuwind.cxx+= -funwind-tables -fno-rtti -fno-exceptions -fvisibility=hidden -Wno-old-style-cast diff --git a/sys/lib/libunwind/Registers.hpp b/sys/lib/libunwind/Registers.hpp new file mode 100644 index 000000000..dc18d076b --- /dev/null +++ b/sys/lib/libunwind/Registers.hpp @@ -0,0 +1,234 @@ +//===----------------------------- 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 +#include + +namespace _Unwind { + +enum { + REGNO_X86_EAX = 0, + REGNO_X86_ECX = 1, + REGNO_X86_EDX = 2, + REGNO_X86_EBX = 3, + REGNO_X86_ESP = 4, + REGNO_X86_EBP = 5, + REGNO_X86_ESI = 6, + REGNO_X86_EDI = 7, + REGNO_X86_EIP = 8, +}; + +class Registers_x86 { +public: + enum { + LAST_RESTORE_REG = REGNO_X86_EIP, + IP_PSEUDO_REG = REGNO_X86_EIP, + LAST_REGISTER = REGNO_X86_EIP, + }; + + __dso_hidden Registers_x86(); + + static int dwarf2regno(int num) { return num; } + + bool validRegister(int num) const { + return num >= REGNO_X86_EAX && num <= REGNO_X86_EDI; + } + + uint32_t getRegister(int num) const { + assert(validRegister(num)); + return reg[num]; + } + + void setRegister(int num, uint32_t value) { + assert(validRegister(num)); + reg[num] = value; + } + + uint32_t getIP() const { return reg[REGNO_X86_EIP]; } + + void setIP(uint32_t value) { reg[REGNO_X86_EIP] = value; } + + uint32_t getSP() const { return reg[REGNO_X86_ESP]; } + + void setSP(uint32_t value) { reg[REGNO_X86_ESP] = value; } + + bool validFloatVectorRegister(int num) const { return false; } + + void copyFloatVectorRegister(int num, uint32_t addr) { + } + + __dso_hidden void jumpto() const __dead; + +private: + uint32_t reg[REGNO_X86_EIP + 1]; +}; + +enum { + REGNO_X86_64_RAX = 0, + REGNO_X86_64_RDX = 1, + REGNO_X86_64_RCX = 2, + REGNO_X86_64_RBX = 3, + REGNO_X86_64_RSI = 4, + REGNO_X86_64_RDI = 5, + REGNO_X86_64_RBP = 6, + REGNO_X86_64_RSP = 7, + REGNO_X86_64_R8 = 8, + REGNO_X86_64_R9 = 9, + REGNO_X86_64_R10 = 10, + REGNO_X86_64_R11 = 11, + REGNO_X86_64_R12 = 12, + REGNO_X86_64_R13 = 13, + REGNO_X86_64_R14 = 14, + REGNO_X86_64_R15 = 15, + REGNO_X86_64_RIP = 16, +}; + +class Registers_x86_64 { +public: + enum { + LAST_RESTORE_REG = REGNO_X86_64_RIP, + IP_PSEUDO_REG = REGNO_X86_64_RIP, + LAST_REGISTER = REGNO_X86_64_RIP, + }; + + __dso_hidden Registers_x86_64(); + + static int dwarf2regno(int num) { return num; } + + bool validRegister(int num) const { + return num >= REGNO_X86_64_RAX && num <= REGNO_X86_64_R15; + } + + uint64_t getRegister(int num) const { + assert(validRegister(num)); + return reg[num]; + } + + void setRegister(int num, uint64_t value) { + assert(validRegister(num)); + reg[num] = value; + } + + uint64_t getIP() const { return reg[REGNO_X86_64_RIP]; } + + void setIP(uint64_t value) { reg[REGNO_X86_64_RIP] = value; } + + uint64_t getSP() const { return reg[REGNO_X86_64_RSP]; } + + void setSP(uint64_t value) { reg[REGNO_X86_64_RSP] = value; } + + bool validFloatVectorRegister(int num) const { return false; } + + void copyFloatVectorRegister(int num, uint64_t addr) { + } + + __dso_hidden void jumpto() const __dead; + +private: + uint64_t reg[REGNO_X86_64_RIP + 1]; +}; + +enum { + DWARF_PPC32_R0 = 0, + DWARF_PPC32_R31 = 31, + DWARF_PPC32_F0 = 32, + DWARF_PPC32_F31 = 63, + DWARF_PPC32_V0 = 1124, + DWARF_PPC32_V31 = 1155, + DWARF_PPC32_LR = 65, + DWARF_PPC32_CTR = 66, + DWARF_PPC32_XER = 76, + REGNO_PPC32_R0 = 0, + REGNO_PPC32_R1 = 0, + REGNO_PPC32_R31 = 31, + REGNO_PPC32_CR = 32, + REGNO_PPC32_LR = 33, + REGNO_PPC32_CTR = 34, + REGNO_PPC32_XER = 35, + REGNO_PPC32_SRR0 = 36, + REGNO_PPC32_F0 = REGNO_PPC32_SRR0 + 1, + REGNO_PPC32_F31 = REGNO_PPC32_F0 + 31, + REGNO_PPC32_V0 = REGNO_PPC32_F31 + 1, + REGNO_PPC32_V31 = REGNO_PPC32_V0 + 31, +}; + +class Registers_ppc32 { +public: + enum { + LAST_RESTORE_REG = REGNO_PPC32_V31, + IP_PSEUDO_REG = REGNO_PPC32_SRR0, + LAST_REGISTER = REGNO_PPC32_V31, + }; + + __dso_hidden Registers_ppc32(); + + static int dwarf2regno(int num) { + if (num >= DWARF_PPC32_R0 && num <= DWARF_PPC32_R31) + return REGNO_PPC32_R0 + (num - DWARF_PPC32_R0); + if (num >= DWARF_PPC32_F0 && num <= DWARF_PPC32_F31) + return REGNO_PPC32_F0 + (num - DWARF_PPC32_F0); + if (num >= DWARF_PPC32_V0 && num <= DWARF_PPC32_V31) + return REGNO_PPC32_V0 + (num - DWARF_PPC32_V0); + return LAST_REGISTER + 1; + } + + bool validRegister(int num) const { + return num >= 0 && num <= LAST_RESTORE_REG; + } + + uint64_t getRegister(int num) const { + assert(validRegister(num)); + return reg[num]; + } + + void setRegister(int num, uint64_t value) { + assert(validRegister(num)); + reg[num] = value; + } + + uint64_t getIP() const { return reg[REGNO_PPC32_SRR0]; } + + void setIP(uint64_t value) { reg[REGNO_PPC32_SRR0] = value; } + + uint64_t getSP() const { return reg[REGNO_PPC32_R1]; } + + void setSP(uint64_t value) { reg[REGNO_PPC32_R1] = value; } + + bool validFloatVectorRegister(int num) const { + return (num >= REGNO_PPC32_F0 && num <= REGNO_PPC32_F31) || + (num >= REGNO_PPC32_V0 && num <= REGNO_PPC32_V31); + } + + void copyFloatVectorRegister(int num, uint64_t addr_) { + const void *addr = reinterpret_cast(addr_); + if (num >= REGNO_PPC32_F0 && num <= REGNO_PPC32_F31) + memcpy(fpreg + (num - REGNO_PPC32_F0), addr, sizeof(fpreg[0])); + else + memcpy(vecreg + (num - REGNO_PPC32_V0), addr, sizeof(vecreg[0])); + } + + __dso_hidden void jumpto() const __dead; + +private: + struct vecreg_t { + uint64_t low, high; + }; + uint32_t reg[REGNO_PPC32_SRR0 + 1]; + uint64_t fpreg[32]; + vecreg_t vecreg[64]; +}; + +} // namespace _Unwind + +#endif // __REGISTERS_HPP__ diff --git a/sys/lib/libunwind/UnwindCursor.hpp b/sys/lib/libunwind/UnwindCursor.hpp new file mode 100644 index 000000000..2c094674b --- /dev/null +++ b/sys/lib/libunwind/UnwindCursor.hpp @@ -0,0 +1,142 @@ +//===------------------------- 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 +#include +#if !defined(__minix) +#include +#endif /* !defined(__minix) */ + +#include "AddressSpace.hpp" +#include "DwarfInstructions.hpp" +#include "Registers.hpp" + +namespace _Unwind { + +template class UnwindCursor { +public: + UnwindCursor(R ®s, A &as) + : fRegisters(regs), fAddressSpace(as), fUnwindInfoMissing(false), + fIsSignalFrame(false) { + memset(&fInfo, 0, sizeof(fInfo)); + } + + uint64_t getIP() const { return fRegisters.getIP(); } + + void setIP(uint64_t value) { return fRegisters.setIP(value); } + + uint64_t getSP() const { return fRegisters.getSP(); } + + void setSP(uint64_t value) { return fRegisters.setSP(value); } + + bool validReg(int regNum) { return fRegisters.validRegister(regNum); } + + uint64_t getReg(int regNum) { return fRegisters.getRegister(regNum); } + + void setReg(int regNum, uint64_t value) { + fRegisters.setRegister(regNum, value); + } + + step_result step() { + // Bottom of stack is defined as having no more unwind info. + if (fUnwindInfoMissing) + return UNW_STEP_END; + + // Apply unwinding to register set. + switch (this->stepWithDwarfFDE()) { + case UNW_STEP_FAILED: + return UNW_STEP_FAILED; + case UNW_STEP_END: + return UNW_STEP_END; + case UNW_STEP_SUCCESS: + this->setInfoBasedOnIPRegister(true); + if (fUnwindInfoMissing) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; + } + __builtin_unreachable(); + } + + void getInfo(unw_proc_info_t *info) { *info = fInfo; } + + bool isSignalFrame() { return fIsSignalFrame; } + void setInfoBasedOnIPRegister(bool isReturnAddress = false); + + void jumpto() { fRegisters.jumpto(); } + +private: + typedef typename A::pint_t pint_t; + typedef uint32_t EncodedUnwindInfo; + + bool getInfoFromDwarfSection(pint_t, pint_t, uint32_t, uint32_t); + + step_result stepWithDwarfFDE() { + return DwarfInstructions::stepWithDwarf( + fAddressSpace, this->getIP(), fInfo.unwind_info, fRegisters, &fInfo); + } + + unw_proc_info_t fInfo; + R fRegisters; + A &fAddressSpace; + bool fUnwindInfoMissing; + bool fIsSignalFrame; +}; + +template +void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { + pint_t pc = this->getIP(); + + // 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; + + pint_t fdeStart, data_base; + if (!fAddressSpace.findFDE(pc, fdeStart, data_base)) { + fUnwindInfoMissing = true; + return; + } + fInfo.data_base = data_base; + + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + CFI_Parser::decodeFDE(fAddressSpace, fdeStart, &fdeInfo, &cieInfo, + &fInfo); + if (pc < fdeInfo.pcStart || pc > fdeInfo.pcEnd) { + fUnwindInfoMissing = true; + return; + } + fInfo.start_ip = fdeInfo.pcStart; + + typename CFI_Parser::PrologInfo prolog; + if (!CFI_Parser::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, + pc, &prolog, &fInfo)) { + fUnwindInfoMissing = true; + return; + } + // Save off parsed FDE info + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.extra_args = prolog.spExtraArgSize; + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; +} + +}; // namespace _Unwind + +#endif // __UNWINDCURSOR_HPP__ diff --git a/sys/lib/libunwind/dwarf2.h b/sys/lib/libunwind/dwarf2.h new file mode 100644 index 000000000..0dcd2ca99 --- /dev/null +++ b/sys/lib/libunwind/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/sys/lib/libunwind/libunwind.cxx b/sys/lib/libunwind/libunwind.cxx new file mode 100644 index 000000000..30f579e86 --- /dev/null +++ b/sys/lib/libunwind/libunwind.cxx @@ -0,0 +1,377 @@ +//===--------------------------- 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 C++ ABI Exception Handling Level 1 as documented at: +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#include + +#include "UnwindCursor.hpp" + +using namespace _Unwind; + +#if __i386__ +typedef Registers_x86 ThisUnwindRegisters; +#elif __x86_64__ +typedef Registers_x86_64 ThisUnwindRegisters; +#elif __powerpc__ +typedef Registers_ppc32 ThisUnwindRegisters; +#else +#error Unsupported architecture +#endif + +typedef CFI_Parser MyCFIParser; + +// Internal object representing the address space of this process. +static LocalAddressSpace sThisAddressSpace(MyCFIParser::findPCRange); + +typedef UnwindCursor ThisUnwindCursor; + +static _Unwind_Reason_Code unwind_phase1(ThisUnwindCursor &cursor, + struct _Unwind_Exception *exc) { + cursor.setInfoBasedOnIPRegister(); + + // Walk frames looking for a place to stop. + for (;;) { + // Get next frame. + // First frame is _Unwind_RaiseException and skipped. + switch (cursor.step()) { + case UNW_STEP_END: + return _URC_END_OF_STACK; + case UNW_STEP_FAILED: + return _URC_FATAL_PHASE1_ERROR; + case UNW_STEP_SUCCESS: + break; + } + + // Check if there is a personality routine for this frame. + unw_proc_info_t frameInfo; + cursor.getInfo(&frameInfo); + if (frameInfo.end_ip == 0) + return _URC_FATAL_PHASE1_ERROR; + + if (frameInfo.handler == 0) + continue; // No personality routine, so try next frame. + + __personality_routine p = (__personality_routine)(frameInfo.handler); + _Unwind_Reason_Code result = (*p)(1, _UA_SEARCH_PHASE, exc->exception_class, + exc, (struct _Unwind_Context *)(&cursor)); + + switch (result) { + case _URC_HANDLER_FOUND: + // This is either a catch clause or a local variable + // with destructor. + // Stop search and remember the frame for phase 2. + exc->private_2 = cursor.getSP(); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + // Continue unwinding + break; + + default: + // Bad personality routine. + return _URC_FATAL_PHASE1_ERROR; + } + } +} + +static _Unwind_Reason_Code unwind_phase2(ThisUnwindCursor &cursor, + struct _Unwind_Exception *exc) { + cursor.setInfoBasedOnIPRegister(); + + // Walk frames until the frame selected in phase 1 is reached. + for (;;) { + // Get next frame. + // First frame is _Unwind_RaiseException and skipped. + switch (cursor.step()) { + case UNW_STEP_END: + return _URC_END_OF_STACK; + case UNW_STEP_FAILED: + return _URC_FATAL_PHASE2_ERROR; + case UNW_STEP_SUCCESS: + break; + } + + unw_proc_info_t frameInfo; + cursor.getInfo(&frameInfo); + if (frameInfo.end_ip == 0) + return _URC_FATAL_PHASE2_ERROR; + + if (frameInfo.handler == 0) + continue; // No personality routine, continue. + + uintptr_t sp = cursor.getSP(); + + _Unwind_Action action = _UA_CLEANUP_PHASE; + // If this frame was selected in phase 1, + // inform the personality routine. + if (sp == exc->private_2) + action = (_Unwind_Action)(action | _UA_HANDLER_FRAME); + __personality_routine p = (__personality_routine)(frameInfo.handler); + _Unwind_Reason_Code result = (*p)(1, action, exc->exception_class, exc, + (struct _Unwind_Context *)(&cursor)); + switch (result) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding unless the selected frame passed. + if (sp == exc->private_2) + return _URC_FATAL_PHASE2_ERROR; + break; + case _URC_INSTALL_CONTEXT: + // Transfer control to landing pad. + cursor.jumpto(); + default: + // Bad personality routine. + return _URC_FATAL_PHASE2_ERROR; + } + } +} + +static _Unwind_Reason_Code unwind_phase2_forced(ThisUnwindCursor &cursor, + struct _Unwind_Exception *exc, + _Unwind_Stop_Fn stop, + void *stop_arg) { + _Unwind_Action action; + cursor.setInfoBasedOnIPRegister(); + + // Walk frames until the frame selected in phase 1 is reached. + for (;;) { + // Get next frame. + // First frame is _Unwind_RaiseException and skipped. + switch (cursor.step()) { + case UNW_STEP_END: + case UNW_STEP_FAILED: + // End of stack or error condition. + // Call the stop function one last time. + action = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | + _UA_END_OF_STACK); + (*stop)(1, action, exc->exception_class, exc, + (struct _Unwind_Context *)(&cursor), stop_arg); + + // Didn't stop at the expected frame, so return error. + return _URC_FATAL_PHASE2_ERROR; + + case UNW_STEP_SUCCESS: + break; + } + + unw_proc_info_t frameInfo; + cursor.getInfo(&frameInfo); + if (frameInfo.end_ip == 0) + return _URC_FATAL_PHASE2_ERROR; + + // Call stop function for each frame + action = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code result = + (*stop)(1, action, exc->exception_class, exc, + (struct _Unwind_Context *)(&cursor), stop_arg); + if (result != _URC_NO_REASON) + return _URC_FATAL_PHASE2_ERROR; + + if (frameInfo.handler == 0) + continue; // No personality routine, continue. + + __personality_routine p = (__personality_routine)(frameInfo.handler); + result = (*p)(1, action, exc->exception_class, exc, + (struct _Unwind_Context *)(&cursor)); + + switch (result) { + case _URC_CONTINUE_UNWIND: + // Destructors called, continue. + break; + case _URC_INSTALL_CONTEXT: + // Transfer control to landing pad. + cursor.jumpto(); + default: + // Bad personality routine. + return _URC_FATAL_PHASE2_ERROR; + } + } +} + +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *exc) { + ThisUnwindRegisters registers; + ThisUnwindCursor cursor1(registers, sThisAddressSpace); + ThisUnwindCursor cursor2(registers, sThisAddressSpace); + + // Mark this as a non-forced unwind for _Unwind_Resume(). + exc->private_1 = 0; + exc->private_2 = 0; + + // Phase 1: searching. + _Unwind_Reason_Code phase1 = unwind_phase1(cursor1, exc); + if (phase1 != _URC_NO_REASON) + return phase1; + + // Phase 2: cleaning up. + return unwind_phase2(cursor2, exc); +} + +_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *exc, + _Unwind_Stop_Fn stop, void *stop_arg) { + ThisUnwindRegisters registers; + ThisUnwindCursor cursor(registers, sThisAddressSpace); + + // Mark this as forced unwind for _Unwind_Resume(). + exc->private_1 = (uintptr_t)stop; + exc->private_2 = (uintptr_t)stop_arg; + + return unwind_phase2_forced(cursor, exc, stop, stop_arg); +} + +void _Unwind_Resume(struct _Unwind_Exception *exc) { + ThisUnwindRegisters registers; + ThisUnwindCursor cursor(registers, sThisAddressSpace); + + if (exc->private_1 != 0) + unwind_phase2_forced(cursor, exc, (_Unwind_Stop_Fn)exc->private_1, + (void *)exc->private_2); + else + unwind_phase2(cursor, exc); + abort(); +} + +_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exc) { + // This is a re-throw, if this is a non-forced unwind + // and the stopping place was found. + // In that case, call _Unwind_RaiseException() as if + // it was a new exception. + + if (exc->private_1 != 0) + _Unwind_Resume(exc); + + // This can return if there is no catch clause. + // In that case, __cxa_rethrow is expected to call std::terminate(). + return _Unwind_RaiseException(exc); +} + +void _Unwind_DeleteException(struct _Unwind_Exception *exc) { + if (exc->exception_cleanup != NULL) + (*exc->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exc); +} + +uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + return cursor->getReg(index); +} + +void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + cursor->setReg(index, new_value); +} + +uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + return cursor->getIP(); +} + +void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t new_value) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + cursor->setIP(new_value); + unw_proc_info_t info; + cursor->getInfo(&info); + uint64_t orgArgSize = info.extra_args; + uint64_t orgFuncStart = info.start_ip; + cursor->setInfoBasedOnIPRegister(false); + // Adjust REG_SP if there was a DW_CFA_GNU_args_size. + if (orgFuncStart == info.start_ip && orgArgSize != 0) + cursor->setSP(cursor->getSP() + orgArgSize); +} + +uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + unw_proc_info_t frameInfo; + cursor->getInfo(&frameInfo); + return frameInfo.end_ip ? frameInfo.start_ip : 0; +} + +uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + unw_proc_info_t frameInfo; + cursor->getInfo(&frameInfo); + return frameInfo.end_ip ? frameInfo.lsda : 0; +} + +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { + ThisUnwindRegisters registers; + ThisUnwindCursor cursor(registers, sThisAddressSpace); + cursor.setInfoBasedOnIPRegister(); + + // Walk each frame. + while (true) { + + // Ask libuwind to get next frame (skip over first frame which is + // _Unwind_Backtrace()). + if (cursor.step() != UNW_STEP_SUCCESS) + return _URC_END_OF_STACK; + + // Call trace function with this frame. + _Unwind_Reason_Code result = + (*callback)((struct _Unwind_Context *)(&cursor), ref); + if (result != _URC_NO_REASON) + return result; + } +} + +uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + return cursor->getSP(); +} + +void *_Unwind_FindEnclosingFunction(void *pc) { + ThisUnwindRegisters registers; + ThisUnwindCursor cursor(registers, sThisAddressSpace); + + unw_proc_info_t info; + cursor.setIP((uintptr_t)pc); + cursor.setInfoBasedOnIPRegister(); + + cursor.getInfo(&info); + return info.end_ip ? (void *)info.start_ip : NULL; +} + +uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) { + ThisUnwindCursor *cursor = (ThisUnwindCursor *)context; + unw_proc_info_t frameInfo; + cursor->getInfo(&frameInfo); + return frameInfo.data_base; +} + +uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context) { return 0; } + +void __register_frame(const void *fde) { + MyCFIParser::pint_t pcStart, pcEnd; + + MyCFIParser::findPCRange(sThisAddressSpace, (uintptr_t)fde, pcStart, pcEnd); + if (pcEnd == 0) + return; // Bad FDE. + + sThisAddressSpace.addFDE(pcStart, pcEnd, (uintptr_t)fde); +} + +void __register_frame_info(const void *ehframe, void *storage) { + sThisAddressSpace.setLazyReload(); +} + +void __deregister_frame(const void *fde) { + MyCFIParser::pint_t pcStart, pcEnd; + + MyCFIParser::findPCRange(sThisAddressSpace, (uintptr_t)fde, pcStart, pcEnd); + if (pcEnd == 0) + return; // Bad FDE. + + sThisAddressSpace.removeFDE(pcStart, pcEnd, (uintptr_t)fde); +} + +void *__deregister_frame_info(const void *ehFrameStart) { + sThisAddressSpace.removeDSO((LocalAddressSpace::pint_t)ehFrameStart); + return NULL; +} diff --git a/sys/lib/libunwind/unwind.h b/sys/lib/libunwind/unwind.h new file mode 100644 index 000000000..c89b01f87 --- /dev/null +++ b/sys/lib/libunwind/unwind.h @@ -0,0 +1,89 @@ +//===------------------------------- unwind.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. +// +// +// C++ ABI Level 1 ABI documented at: +// http://mentorembedded.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef _UNWIND_H +#define _UNWIND_H + +#include +#include + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 /* GCC extension */ +} _Unwind_Action; + +struct _Unwind_Context; + +struct _Unwind_Exception { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code, struct _Unwind_Exception *); + uintptr_t private_1; + uintptr_t private_2; +} __attribute__((__aligned__)); + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int, _Unwind_Action, uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *, + void *); + +typedef _Unwind_Reason_Code (*__personality_routine)(int, _Unwind_Action, + uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *); + +__BEGIN_DECLS + +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *); +void _Unwind_Resume(struct _Unwind_Exception *) __dead; +_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *); +_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); +void _Unwind_DeleteException(struct _Unwind_Exception *); +uintptr_t _Unwind_GetGR(struct _Unwind_Context *, int); +void _Unwind_SetGR(struct _Unwind_Context *, int, uintptr_t); +uintptr_t _Unwind_GetIP(struct _Unwind_Context *); +uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); +void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t); +uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *); +uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *); +uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *); +uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *); + +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); +_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); +void *_Unwind_FindEnclosingFunction(void *); + +void __register_frame(const void *); +void __register_frame_info(const void *, void *); +void __deregister_frame(const void *); +void *__deregister_frame_info(const void *); + +__END_DECLS + +#endif // _UNWIND_H diff --git a/sys/lib/libunwind/unwind_registers.S b/sys/lib/libunwind/unwind_registers.S new file mode 100644 index 000000000..b87f16380 --- /dev/null +++ b/sys/lib/libunwind/unwind_registers.S @@ -0,0 +1,212 @@ +//===------------------------- unwind_registers.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. +// +// +// Abstracts accessing local vs remote address spaces. +// +//===----------------------------------------------------------------------===// +#include + +#ifdef __i386__ + .hidden _ZN7_Unwind13Registers_x86C1Ev +ENTRY(_ZN7_Unwind13Registers_x86C1Ev) + pushl %eax + movl 8(%esp), %eax /* Load this */ + /* Save all registers except EAX, EIP and ESP */ + /* Skip ECX */ + /* Skip EDX */ + movl %ebx, 12(%eax) + movl %ebp, 20(%eax) + movl %esi, 24(%eax) + movl %edi, 28(%eax) + + leal 8(%esp), %edx /* Compute ESP from the call site */ + movl %edx, 16(%eax) /* ...and store it as ESP */ + movl 4(%esp), %edx /* Load return address */ + movl %edx, 32(%eax) /* ...and store it as EIP */ + popl %edx /* Take old EAX from stack */ + movl %edx, 0(%eax) /* ...and store it */ // XXX skip + ret + + .hidden _ZNK7_Unwind13Registers_x866jumptoEv +ENTRY(_ZNK7_Unwind13Registers_x866jumptoEv) + movl 4(%esp), %eax /* Load this */ + movl 16(%eax), %edx /* Load new stack pointer */ + subl $4, %edx /* Reserve space on new stack for EIP */ + movl 32(%eax), %ebx /* Load new EIP */ + movl %ebx, 0(%edx) /* ...and save it on the new stack */ + pushl %edx /* Save new stack pointer on old stack */ + /* New stack is prepared, now restore all registers except ESP */ + /* EAX is the index register and must be restored last */ + movl 4(%eax), %ecx + movl 8(%eax), %edx + movl 12(%eax), %ebx + movl 20(%eax), %ebp + /* 16 is ESP */ + movl 24(%eax), %esi + movl 28(%eax), %edi + movl 0(%eax), %eax + /* Now load new stack pointer pushed on the old stack earlier */ + popl %esp + /* Return address is already on the new stack. */ + ret +#endif + +#ifdef __x86_64 + .hidden _ZN7_Unwind16Registers_x86_64C1Ev +ENTRY(_ZN7_Unwind16Registers_x86_64C1Ev) + /* RDI == this */ + /* Skip RAX */ + /* Skip RDX */ + /* Skip RCX */ + movq %rbx, 24(%rdi) + /* Skip RSI */ + /* Skip RDI */ + movq %rbp, 48(%rdi) + leaq 8(%rsp), %rax + movq %rax, 56(%rdi) + /* Skip R8 */ + /* Skip R9 */ + /* Skip R10 */ + /* Skip R11 */ + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + movq (%rsp), %rax + movq %rax, 128(%rdi) + ret + + .hidden _ZNK7_Unwind16Registers_x86_646jumptoEv +ENTRY(_ZNK7_Unwind16Registers_x86_646jumptoEv) + /* RDI == this */ + movq 56(%rdi), %rax + subq $8, %rax /* Reserve space on new stack for RIP */ + movq 128(%rdi), %rbx /* Load new RIP */ + movq %rbx, 0(%rax) /* ...and save it on the new stack */ + pushq %rax /* Save new stack pointer on old stack */ + /* New stack is prepared, now restore all registers */ + movq 0(%rdi), %rax + movq 8(%rdi), %rdx + movq 16(%rdi), %rcx + movq 24(%rdi), %rbx + movq 32(%rdi), %rsi + /* RDI restored later as it is still used as index register */ + movq 48(%rdi), %rbp + /* RSP is restored 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 + movq 40(%rdi), %rdi + /* Now load new stack pointer pushed on the old stack earlier */ + popq %rsp + /* Return address is already on the new stack. */ + ret +#endif + +#ifdef __powerpc__ + .hidden _ZN7_Unwind15Registers_ppc32C1Ev +ENTRY(_ZN7_Unwind15Registers_ppc32C1Ev) + stw %r0, 0(%r3) + stw %r1, 4(%r3) + stw %r2, 8(%r3) + stw %r3, 12(%r3) + stw %r4, 16(%r3) + stw %r5, 20(%r3) + stw %r6, 24(%r3) + stw %r7, 28(%r3) + stw %r8, 32(%r3) + stw %r9, 36(%r3) + stw %r10, 40(%r3) + stw %r11, 44(%r3) + stw %r12, 48(%r3) + stw %r13, 52(%r3) + stw %r14, 56(%r3) + stw %r15, 60(%r3) + stw %r16, 64(%r3) + stw %r17, 68(%r3) + stw %r18, 72(%r3) + stw %r19, 76(%r3) + stw %r20, 80(%r3) + stw %r21, 84(%r3) + stw %r22, 88(%r3) + stw %r23, 92(%r3) + stw %r24, 96(%r3) + stw %r25,100(%r3) + stw %r26,104(%r3) + stw %r27,108(%r3) + stw %r28,112(%r3) + stw %r29,116(%r3) + stw %r30,120(%r3) + stw %r31,124(%r3) + + mfcr %r0 + stw %r0, 128(%r3) /* CR */ + mflr %r0 + stw %r0, 132(%r3) /* LR */ + stw %r0, 144(%r3) /* LR */ + mfctr %r0 + stw %r0, 136(%r3) /* CTR */ + mfxer %r0 + stw %r0, 140(%r3) /* XER */ + + blr + + .hidden _ZNK7_Unwind15Registers_ppc326jumptoEv +ENTRY(_ZNK7_Unwind15Registers_ppc326jumptoEv) + lwz %r2, 8(%r3) + /* skip r3 for now */ + lwz %r4, 16(%r3) + lwz %r5, 20(%r3) + lwz %r6, 24(%r3) + lwz %r7, 28(%r3) + lwz %r8, 32(%r3) + lwz %r9, 36(%r3) + lwz %r10, 40(%r3) + lwz %r11, 44(%r3) + lwz %r12, 48(%r3) + lwz %r13, 52(%r3) + lwz %r14, 56(%r3) + lwz %r15, 60(%r3) + lwz %r16, 64(%r3) + lwz %r17, 68(%r3) + lwz %r18, 72(%r3) + lwz %r19, 76(%r3) + lwz %r20, 80(%r3) + lwz %r21, 84(%r3) + lwz %r22, 88(%r3) + lwz %r23, 92(%r3) + lwz %r24, 96(%r3) + lwz %r25,100(%r3) + lwz %r26,104(%r3) + lwz %r27,108(%r3) + lwz %r28,112(%r3) + lwz %r29,116(%r3) + lwz %r30,120(%r3) + lwz %r31,124(%r3) + + lwz %r0, 128(%r3) /* CR */ + mtcr %r0 + lwz %r0, 132(%r3) /* LR */ + mtlr %r0 + lwz %r0, 136(%r3) /* CTR */ + mtctr %r0 + lwz %r0, 140(%r3) /* XER */ + mtxer %r0 + lwz %r0, 144(%r3) /* SRR0 ? */ + mtctr %r0 + + lwz %r0, 0(%r3) /* do r0 now */ + lwz %r1,4(%r3) /* do sp now */ + lwz %r3,12(%r3) /* do r3 last */ + bctr +#endif