#include <mach/mach_types.h>
#include <mach/machine.h>
#include <new>
#include "libunwind.h"
#include "libunwind_priv.h"
#include "UnwindCursor.hpp"
using namespace libunwind;
INITIALIZE_DEBUG_PRINT_API
INITIALIZE_DEBUG_PRINT_UNWINDING
#if __ppc__ || __i386__ || __x86_64__
static LocalAddressSpace sThisAddressSpace;
extern int unw_getcontext(unw_context_t*);
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);
#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();
return UNW_ESUCCESS;
}
#if UNW_REMOTE
EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace;
EXPORT int unw_init_remote_thread(unw_cursor_t* cursor, unw_addr_space_t as, thread_t thread)
{
if ( as == (unw_addr_space_t)&sThisAddressSpace )
return unw_init_local(cursor, NULL);
switch ( as->cpuType ) {
case CPU_TYPE_I386:
new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >,
Registers_x86>(((unw_addr_space_i386*)as)->oas, thread);
break;
case CPU_TYPE_X86_64:
new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer64<LittleEndian> >,
Registers_x86_64>(((unw_addr_space_x86_64*)as)->oas, thread);
break;
case CPU_TYPE_POWERPC:
new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer32<BigEndian> >,
Registers_ppc>(((unw_addr_space_ppc*)as)->oas, thread);
break;
default:
return UNW_EUNSPEC;
}
return UNW_ESUCCESS;
}
static bool rosetta(task_t task)
{
return false; }
static bool is64bit(task_t task)
{
return false; }
EXPORT unw_addr_space_t unw_create_addr_space_for_task(task_t task)
{
#if __i386__
if ( rosetta(task) ) {
unw_addr_space_ppc* as = new unw_addr_space_ppc(task);
as->taskPort = task;
as->cpuType = CPU_TYPE_POWERPC;
}
else if ( is64bit(task) ) {
unw_addr_space_x86_64* as = new unw_addr_space_x86_64(task);
as->taskPort = task;
as->cpuType = CPU_TYPE_X86_64;
}
else {
unw_addr_space_i386* as = new unw_addr_space_i386(task);
as->taskPort = task;
as->cpuType = CPU_TYPE_I386;
}
#else
#endif
}
EXPORT void unw_destroy_addr_space(unw_addr_space_t asp)
{
switch ( asp->cpuType ) {
#if __i386__ || __x86_64__
case CPU_TYPE_I386:
{
unw_addr_space_i386* as = (unw_addr_space_i386*)asp;
delete as;
}
break;
case CPU_TYPE_X86_64:
{
unw_addr_space_x86_64* as = (unw_addr_space_x86_64*)asp;
delete as;
}
break;
#endif
case CPU_TYPE_POWERPC:
{
unw_addr_space_ppc* as = (unw_addr_space_ppc*)asp;
delete as;
}
break;
}
}
#endif // UNW_REMOTE
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) ) {
*value = co->getReg(regNum);
return UNW_ESUCCESS;
}
return UNW_EBADREG;
}
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);
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);
if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) )
co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize);
}
return UNW_ESUCCESS;
}
return UNW_EBADREG;
}
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) ) {
*value = co->getFloatReg(regNum);
return UNW_ESUCCESS;
}
return UNW_EBADREG;
}
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) ) {
co->setFloatReg(regNum, value);
return UNW_ESUCCESS;
}
return UNW_EBADREG;
}
EXPORT int unw_step(unw_cursor_t* cursor)
{
DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor);
AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
return co->step();
}
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;
}
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;
}
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;
}
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);
}
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);
}
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();
}
#if !FOR_DYLD
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
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 ) {
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);
}
}
void _unw_remove_dynamic_fde(unw_word_t fde)
{
DwarfFDECache<LocalAddressSpace>::removeAllIn(fde);
}
#endif
#endif // __ppc__ || __i386__ || __x86_64__