| //===------------------------- 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__ |