blob: d841677db37fed67b667f9fcf396841d7a495f9a [file] [log] [blame]
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
//===-- libuwind.cxx --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#if __ppc__ || __i386__ || __x86_64__
#include <mach/mach_types.h>
#include <mach/machine.h>
#include <new>
#include "libunwind.h"
#include "libunwind_priv.h"
#include "UnwindCursor.hpp"
#include "AddressSpace.hpp"
#include "RemoteProcInfo.hpp"
namespace lldb_private {
// setup debug logging hooks
INITIALIZE_DEBUG_PRINT_API
INITIALIZE_DEBUG_PRINT_UNWINDING
// internal object to represent this processes address space
static LocalAddressSpace sThisAddressSpace;
#pragma mark Local API
///
/// record the registers and stack position of the caller
///
extern int unw_getcontext(unw_context_t*);
// note: unw_getcontext() implemented in assembly
///
/// create a cursor of a thread in this process given 'context' recorded by unw_getcontext()
///
EXPORT int unw_init_local(unw_cursor_t* cursor, unw_context_t* context)
{
DEBUG_PRINT_API("unw_init_local(cursor=%p, context=%p)\n", cursor, context);
// use "placement new" to allocate UnwindCursor in the cursor buffer
#if __i386__
new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86>(context, sThisAddressSpace);
#elif __x86_64__
new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86_64>(context, sThisAddressSpace);
#elif __ppc__
new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_ppc>(context, sThisAddressSpace);
#endif
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
co->setInfoBasedOnIPRegister(NULL);
return UNW_ESUCCESS;
}
///
/// move cursor to next frame
///
EXPORT int unw_step(unw_cursor_t* cursor)
{
DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
return co->step();
}
///
/// get value of specified register at cursor position in stack frame
///
EXPORT int unw_get_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t* value)
{
DEBUG_PRINT_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
if (co->validReg(regNum) == 0)
return UNW_EBADREG;
return co->getReg(regNum, value);
}
///
/// get value of specified float register at cursor position in stack frame
///
EXPORT int unw_get_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t* value)
{
DEBUG_PRINT_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
if ( co->validFloatReg(regNum) ) {
return co->getFloatReg(regNum, value);
}
return UNW_EBADREG;
}
///
/// set value of specified register at cursor position in stack frame
///
EXPORT int unw_set_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t value)
{
DEBUG_PRINT_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", cursor, regNum, value);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
if ( co->validReg(regNum) ) {
co->setReg(regNum, value);
// specical case altering IP to re-find info (being called by personality function)
if ( regNum == UNW_REG_IP ) {
unw_proc_info_t info;
co->getInfo(&info);
uint64_t orgArgSize = info.gp;
uint64_t orgFuncStart = info.start_ip;
co->setInfoBasedOnIPRegister(false);
// and adjust REG_SP if there was a DW_CFA_GNU_args_size
if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) )
co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize);
}
return UNW_ESUCCESS;
}
return UNW_EBADREG;
}
///
/// set value of specified float register at cursor position in stack frame
///
EXPORT int unw_set_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t value)
{
DEBUG_PRINT_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", cursor, regNum, value);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
if ( co->validFloatReg(regNum) ) {
return co->setFloatReg(regNum, value);
}
return UNW_EBADREG;
}
///
/// resume execution at cursor position (aka longjump)
///
EXPORT int unw_resume(unw_cursor_t* cursor)
{
DEBUG_PRINT_API("unw_resume(cursor=%p)\n", cursor);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
co->jumpto();
return UNW_EUNSPEC;
}
///
/// returns the name of a register
///
EXPORT const char* unw_regname(unw_cursor_t* cursor, unw_regnum_t regNum)
{
DEBUG_PRINT_API("unw_regname(cursor=%p, regNum=%d)\n", cursor, regNum);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
return co->getRegisterName(regNum);
}
///
/// get unwind info at cursor position in stack frame
///
EXPORT int unw_get_proc_info(unw_cursor_t* cursor, unw_proc_info_t* info)
{
DEBUG_PRINT_API("unw_get_proc_info(cursor=%p, &info=%p)\n", cursor, info);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
co->getInfo(info);
if ( info->end_ip == 0 )
return UNW_ENOINFO;
else
return UNW_ESUCCESS;
}
///
/// checks if a register is a floating-point register
///
EXPORT int unw_is_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum)
{
DEBUG_PRINT_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", cursor, regNum);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
return co->validFloatReg(regNum);
}
///
/// checks if current frame is signal trampoline
///
EXPORT int unw_is_signal_frame(unw_cursor_t* cursor)
{
DEBUG_PRINT_API("unw_is_signal_frame(cursor=%p)\n", cursor);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
return co->isSignalFrame();
}
///
/// get name of function at cursor position in stack frame
///
EXPORT int unw_get_proc_name(unw_cursor_t* cursor, char* buf, size_t bufLen, unw_word_t* offset)
{
DEBUG_PRINT_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%ld)\n", cursor, buf, bufLen);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
if ( co->getFunctionName(buf, bufLen, offset) )
return UNW_ESUCCESS;
else
return UNW_EUNSPEC;
}
#pragma mark Remote API
#if defined (SUPPORT_REMOTE_UNWINDING)
EXPORT int unw_init_remote(unw_cursor_t *cursor, unw_addr_space_t as, void *arg)
{
DEBUG_PRINT_API("init_remote(c=%p, as=%p, arg=%p)\n", cursor, as, arg);
// API docs at http://www.nongnu.org/libunwind/docs.html say we should
// handle a local address space but we're not doing the "remote" unwinding
// with local process accessors so punt on that.
if(as->type != UNW_REMOTE)
{
ABORT("unw_init_remote was passed a non-remote address space");
return UNW_EINVAL;
}
unw_accessors_t* acc = unw_get_accessors(as);
if(!acc) {
ABORT("unw_get_accessors returned NULL");
return UNW_EINVAL;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
// use "placement new" to allocate UnwindCursor in the cursor buffer
// It isn't really necessary to use placement new in the remote API but we'll stay consistent
// with the rest of the code here.
switch ( remote->ras->getTargetArch() ) {
case UNW_TARGET_I386:
{
Registers_x86 r;
// LEAK: "addrSpace" is being leaked every time here with no easy solution.
// The address space is in the cursor and the cursor may get
// duplicated, though if it does get duplicated, it will just be
// memcpy'ed since unw_cursor_t is just a bunch of uint64_t types...
OtherAddressSpace<Pointer32<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer32<LittleEndian> >(as, arg);
getRemoteContext (remote->ras, r, arg);
unw_context_t *context = (unw_context_t*) &r;
new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >, Registers_x86>(*addrSpace, context, arg);
}
break;
case UNW_TARGET_X86_64:
{
Registers_x86_64 r;
// LEAK: "addrSpace" is being leaked every time here with no easy solution.
// The address space is in the cursor and the cursor may get
// duplicated, though if it does get duplicated, it will just be
// memcpy'ed since unw_cursor_t is just a bunch of uint64_t types...
OtherAddressSpace<Pointer64<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer64<LittleEndian> >(as, arg);
getRemoteContext (remote->ras, r, arg);
unw_context_t *context = (unw_context_t*) &r;
new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer64<LittleEndian> >, Registers_x86_64>(*addrSpace, context, arg);
}
break;
case UNW_TARGET_PPC:
ABORT("ppc not supported for remote unwinds");
break;
case UNW_TARGET_ARM:
ABORT("arm not supported for remote unwinds");
break;
default:
return UNW_EUNSPEC;
}
AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor;
co->setRemoteContext(arg);
return UNW_ESUCCESS;
}
// The documentation disagrees about whether or not this returns a pointer. Now it does.
EXPORT unw_accessors_t* unw_get_accessors(unw_addr_space_t as)
{
if(as->type != UNW_REMOTE)
{
ABORT("unw_get_accessors was passed a non-remote address space");
return NULL;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
if(remote->type != UNW_REMOTE)
return NULL;
return remote->ras->getAccessors();
}
EXPORT unw_addr_space_t unw_create_addr_space(unw_accessors_t *ap, unw_targettype_t targarch)
{
unw_addr_space_remote* remote = (unw_addr_space_remote*)malloc(sizeof(unw_addr_space_remote));
remote->type = UNW_REMOTE;
remote->ras = new RemoteProcInfo(ap, targarch);
return (unw_addr_space_t)remote;
}
EXPORT void unw_flush_caches(unw_addr_space_t as)
{
if(as->type != UNW_REMOTE)
{
ABORT("unw_flush_caches was passed a non-remote address space");
return;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
remote->ras->flushAllCaches();
return;
}
EXPORT void unw_image_was_unloaded (unw_addr_space_t as, unw_word_t mh)
{
if(as->type != UNW_REMOTE)
{
ABORT("unw_image_was_unloaded was passed a non-remote address space");
return;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
remote->ras->flushCacheByMachHeader(mh);
return;
}
EXPORT int unw_set_caching_policy(unw_addr_space_t as, unw_caching_policy_t policy)
{
if(as->type != UNW_REMOTE)
{
ABORT("unw_set_caching_policy was passed a non-remote address space");
return UNW_EINVAL;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
return remote->ras->setCachingPolicy(policy);
}
EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace;
///
/// delete an address_space object
///
EXPORT void unw_destroy_addr_space(unw_addr_space_t asp)
{
if(asp->type != UNW_REMOTE) {
ABORT("unw_destroy_addr_space was passed a non-remote address space");
return;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)asp;
delete remote->ras;
}
EXPORT void unw_set_logging_level(unw_addr_space_t as, FILE *f, unw_log_level_t level)
{
if (as->type != UNW_REMOTE) {
ABORT("unw_set_logging_level was passed a non-remote address space");
return;
}
unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
return remote->ras->setLoggingLevel(f, level);
}
EXPORT int unw_end_of_prologue_setup(unw_cursor_t* cursor, unw_word_t start, unw_word_t end, unw_word_t *endofprologue)
{
AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor;
if (!co->remoteUnwindCursor())
ABORT("unw_end_of_prologue_setup called with a non-remote unwind cursor.");
return co->endOfPrologueInsns (start, end, endofprologue);
}
#endif // SUPPORT_REMOTE_UNWINDING
#pragma mark Dynamic unwinding API
#if !FOR_DYLD
///
/// SPI: walks cached dwarf entries
///
EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh))
{
DEBUG_PRINT_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func);
DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
}
#endif // !FOR_DYLD
#if !FOR_DYLD
//
// IPI: for __register_frame()
//
void _unw_add_dynamic_fde(unw_word_t fde)
{
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
const char* message = CFI_Parser<LocalAddressSpace>::decodeFDE(sThisAddressSpace, (LocalAddressSpace::pint_t)fde, & fdeInfo, &cieInfo);
if ( message == NULL ) {
// dynamically registered FDEs don't have a mach_header group they are in. Use fde as mh_group
unw_word_t mh_group = fdeInfo.fdeStart;
DwarfFDECache<LocalAddressSpace>::add(mh_group, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart);
}
else {
DEBUG_MESSAGE("_unw_add_dynamic_fde: bad fde: %s", message);
}
}
//
// IPI: for __deregister_frame()
//
void _unw_remove_dynamic_fde(unw_word_t fde)
{
// fde is own mh_group
DwarfFDECache<LocalAddressSpace>::removeAllIn(fde);
}
#endif
}; // namespace lldb_private
#endif // __ppc__ || __i386__ || __x86_64__