is quite gnarly code and there is no good way to clean it up. I will have Jason look at a fix for this. llvm-svn: 107539
429 lines
13 KiB
C++
429 lines
13 KiB
C++
/* -*- 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__
|