blob: 5173dc0068e0dfd2f10496410d68562797df0a3b [file] [log] [blame]
/* -*- 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <mach-o/loader.h>
#include <mach-o/getsect.h>
#if !defined (SUPPORT_REMOTE_UNWINDING)
#include <mach-o/dyld_priv.h>
#endif
#include <mach/ppc/thread_status.h>
#include <mach/i386/thread_status.h>
#include <Availability.h>
#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 <typename P>
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 P>
typename OtherAddressSpace<P>::pint_t OtherAddressSpace<P>::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 <typename P>
double OtherAddressSpace<P>::getDouble(pint_t addr)
{
return fRemoteProcInfo->getDouble(addr, fArg);
}
template <typename P>
v128 OtherAddressSpace<P>::getVector(pint_t addr)
{
return fRemoteProcInfo->getVector(addr, fArg);
}
template <typename P>
bool OtherAddressSpace<P>::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 <typename P>
bool OtherAddressSpace<P>::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__