/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ //===-- AddressSpace.hpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // C++ interface to lower levels of libuwind // #ifndef __ADDRESSSPACE_HPP__ #define __ADDRESSSPACE_HPP__ #include #include #include #include #include #include #if !defined (SUPPORT_REMOTE_UNWINDING) #include #endif #include #include #include #include "FileAbstraction.hpp" #include "libunwind.h" #include "InternalMacros.h" #include "dwarf2.h" #include "RemoteProcInfo.hpp" #if defined (SUPPORT_REMOTE_UNWINDING) bool _dyld_find_unwind_sections(void* addr, void* info) { assert("unwinding with a non-remote process not supported."); return false; } #endif // SUPPORT_REMOTE_UNWINDING namespace lldb_private { /// /// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread /// in the same process. It compiles away and making local unwinds very fast. /// class 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 int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { memcpy(buf, (void*)addr, extent); return 1; } 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); } uint8_t get8(pint_t addr, int& err) { return *((uint8_t*)addr); err = 0; } uint16_t get16(pint_t addr, int& err) { return *((uint16_t*)addr); err = 0; } uint32_t get32(pint_t addr, int& err) { return *((uint32_t*)addr); err = 0; } uint64_t get64(pint_t addr, int& err) { return *((uint64_t*)addr); err = 0; } double getDouble(pint_t addr, int& err) { return *((double*)addr); err = 0; } v128 getVector(pint_t addr, int& err) { return *((v128*)addr); err = 0; } uintptr_t getP(pint_t addr); uintptr_t getP(pint_t addr, int &err); 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 addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); #if defined (SUPPORT_REMOTE_UNWINDING) RemoteProcInfo* getRemoteProcInfo () { return NULL; } unw_accessors_t* accessors() { return NULL; } unw_addr_space_t wrap() { return NULL; } #endif }; LocalAddressSpace sThisAddress; inline uintptr_t LocalAddressSpace::getP(pint_t addr) { #if __LP64__ return get64(addr); #else return get32(addr); #endif } inline uintptr_t LocalAddressSpace::getP(pint_t addr, int &err) { #if __LP64__ return get64(addr); #else return get32(addr); #endif err = 0; } /* 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 ) ABORT("truncated uleb128 expression"); b = *p & 0x7f; if (bit >= 64 || b << bit >> bit != b) { 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; int64_t result = 0; int bit = 0; uint8_t byte; do { 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; } 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 = 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; default: 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: ABORT("DW_EH_PE_textrel pointer encoding not supported"); break; case DW_EH_PE_datarel: ABORT("DW_EH_PE_datarel pointer encoding not supported"); break; case DW_EH_PE_funcrel: ABORT("DW_EH_PE_funcrel pointer encoding not supported"); break; case DW_EH_PE_aligned: ABORT("DW_EH_PE_aligned pointer encoding not supported"); break; default: ABORT("unknown pointer encoding"); break; } if ( encoding & DW_EH_PE_indirect ) result = getP(result); return result; } inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) { #if !defined (SUPPORT_REMOTE_UNWINDING) dyld_unwind_sections info; if ( _dyld_find_unwind_sections((void*)addr, &info) ) { mh = (pint_t)info.mh; dwarfStart = (pint_t)info.dwarf_section; dwarfLen = (pint_t)info.dwarf_section_length; compactStart = (pint_t)info.compact_unwind_section; return true; } #else assert("unwinding with a non-remote process not supported."); #endif return false; } 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 defined (SUPPORT_REMOTE_UNWINDING) /// /// 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 and is handled by the P template parameter. /// template class OtherAddressSpace { public: OtherAddressSpace (unw_addr_space_t remote_addr_space, void* arg) : fAddrSpace ((unw_addr_space_remote *)remote_addr_space), fArg(arg) { if (fAddrSpace->type != UNW_REMOTE) ABORT("OtherAddressSpace ctor called with non-remote address space."); fRemoteProcInfo = fAddrSpace->ras; } typedef typename P::uint_t pint_t; typedef typename P::int_t sint_t; int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { return fRemoteProcInfo->getBytes (addr, extent, buf, fArg); } uint8_t get8(pint_t addr) { return fRemoteProcInfo->get8(addr, fArg); } uint16_t get16(pint_t addr) { return fRemoteProcInfo->get16(addr, fArg); } uint32_t get32(pint_t addr) { return fRemoteProcInfo->get32(addr, fArg); } uint64_t get64(pint_t addr) { return fRemoteProcInfo->get64(addr, fArg); } pint_t getP(pint_t addr) { return fRemoteProcInfo->getP(addr, fArg); } uint8_t get8(pint_t addr, int& err) { return fRemoteProcInfo->get8(addr, err, fArg); } uint16_t get16(pint_t addr, int& err) { return fRemoteProcInfo->get16(addr, err, fArg); } uint32_t get32(pint_t addr, int& err) { return fRemoteProcInfo->get32(addr, err, fArg); } uint64_t get64(pint_t addr, int& err) { return fRemoteProcInfo->get64(addr, err, fArg); } pint_t getP(pint_t addr, int &err) { return fRemoteProcInfo->getP(addr, err, fArg); } uint64_t getULEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getULEB128 (addr, end, fArg); } int64_t getSLEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getSLEB128 (addr, end, fArg); } pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); double getDouble(pint_t addr); v128 getVector(pint_t addr); bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); bool findFunctionExtent(pint_t addr, unw_word_t* begin, unw_word_t* end); bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart); RemoteProcInfo* getRemoteProcInfo () { return fRemoteProcInfo; } unw_accessors_t* accessors() { return fRemoteProcInfo->getAccessors(); } unw_addr_space_t wrap() { return (unw_addr_space_t) fAddrSpace; } private: void* localCopy(pint_t addr); unw_addr_space_remote *fAddrSpace; RemoteProcInfo* fRemoteProcInfo; void* fArg; }; template typename OtherAddressSpace

::pint_t OtherAddressSpace

::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) { pint_t startAddr = addr; pint_t p = addr; pint_t result; // first get value switch (encoding & 0x0F) { case DW_EH_PE_ptr: result = fRemoteProcInfo->getP(addr, fArg); p += sizeof(pint_t); addr = p; break; case DW_EH_PE_uleb128: result = fRemoteProcInfo->getULEB128(addr, end, fArg); break; case DW_EH_PE_udata2: result = fRemoteProcInfo->get16(addr, fArg); p += 2; addr = p; break; case DW_EH_PE_udata4: result = fRemoteProcInfo->get32(addr, fArg); p += 4; addr = p; break; case DW_EH_PE_udata8: result = fRemoteProcInfo->get64(addr, fArg); p += 8; addr = p; break; case DW_EH_PE_sleb128: result = fRemoteProcInfo->getSLEB128(addr, end, fArg); break; case DW_EH_PE_sdata2: result = (int16_t)fRemoteProcInfo->get16(addr, fArg); p += 2; addr = p; break; case DW_EH_PE_sdata4: result = (int32_t)fRemoteProcInfo->get32(addr, fArg); p += 4; addr = p; break; case DW_EH_PE_sdata8: result = fRemoteProcInfo->get64(addr, fArg); p += 8; addr = p; break; default: 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: ABORT("DW_EH_PE_textrel pointer encoding not supported"); break; case DW_EH_PE_datarel: ABORT("DW_EH_PE_datarel pointer encoding not supported"); break; case DW_EH_PE_funcrel: ABORT("DW_EH_PE_funcrel pointer encoding not supported"); break; case DW_EH_PE_aligned: ABORT("DW_EH_PE_aligned pointer encoding not supported"); break; default: ABORT("unknown pointer encoding"); break; } if ( encoding & DW_EH_PE_indirect ) result = fRemoteProcInfo->getP(result, fArg); return result; } template double OtherAddressSpace

::getDouble(pint_t addr) { return fRemoteProcInfo->getDouble(addr, fArg); } template v128 OtherAddressSpace

::getVector(pint_t addr) { return fRemoteProcInfo->getVector(addr, fArg); } template bool OtherAddressSpace

::findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart) { compactStart = 0; uint64_t t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start; if (fRemoteProcInfo->getImageAddresses (addr, t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start, fArg)) { mh = t_mh; eh_frame_start = t_eh_frame_start; eh_frame_len = t_eh_frame_len; compactStart = t_compact_start; return true; } return false; } template bool OtherAddressSpace

::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) { return fRemoteProcInfo->findFunctionName (addr, buf, bufLen, offset, fArg); } #endif // SUPPORT_REMOTE_UNWINDING } // namespace lldb_private #endif // __ADDRESSSPACE_HPP__