mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 01:48:11 +00:00
784 lines
29 KiB
C
784 lines
29 KiB
C
/*
|
|
* File cpu_x86_64.c
|
|
*
|
|
* Copyright (C) 1999, 2005 Alexandre Julliard
|
|
* Copyright (C) 2009 Eric Pouech.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "dbghelp_private.h"
|
|
#include "winternl.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
|
|
|
|
/* x86-64 unwind information, for PE modules, as described on MSDN */
|
|
|
|
typedef enum _UNWIND_OP_CODES
|
|
{
|
|
UWOP_PUSH_NONVOL = 0,
|
|
UWOP_ALLOC_LARGE,
|
|
UWOP_ALLOC_SMALL,
|
|
UWOP_SET_FPREG,
|
|
UWOP_SAVE_NONVOL,
|
|
UWOP_SAVE_NONVOL_FAR,
|
|
UWOP_SAVE_XMM128,
|
|
UWOP_SAVE_XMM128_FAR,
|
|
UWOP_PUSH_MACHFRAME
|
|
} UNWIND_CODE_OPS;
|
|
|
|
typedef union _UNWIND_CODE
|
|
{
|
|
struct
|
|
{
|
|
BYTE CodeOffset;
|
|
BYTE UnwindOp : 4;
|
|
BYTE OpInfo : 4;
|
|
};
|
|
USHORT FrameOffset;
|
|
} UNWIND_CODE, *PUNWIND_CODE;
|
|
|
|
typedef struct _UNWIND_INFO
|
|
{
|
|
BYTE Version : 3;
|
|
BYTE Flags : 5;
|
|
BYTE SizeOfProlog;
|
|
BYTE CountOfCodes;
|
|
BYTE FrameRegister : 4;
|
|
BYTE FrameOffset : 4;
|
|
UNWIND_CODE UnwindCode[1]; /* actually CountOfCodes (aligned) */
|
|
/*
|
|
* union
|
|
* {
|
|
* OPTIONAL ULONG ExceptionHandler;
|
|
* OPTIONAL ULONG FunctionEntry;
|
|
* };
|
|
* OPTIONAL ULONG ExceptionData[];
|
|
*/
|
|
} UNWIND_INFO, *PUNWIND_INFO;
|
|
|
|
#define GetUnwindCodeEntry(info, index) \
|
|
((info)->UnwindCode[index])
|
|
|
|
#define GetLanguageSpecificDataPtr(info) \
|
|
((PVOID)&GetUnwindCodeEntry((info),((info)->CountOfCodes + 1) & ~1))
|
|
|
|
#define GetExceptionHandler(base, info) \
|
|
((PEXCEPTION_HANDLER)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
|
|
|
|
#define GetChainedFunctionEntry(base, info) \
|
|
((PRUNTIME_FUNCTION)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
|
|
|
|
#define GetExceptionDataPtr(info) \
|
|
((PVOID)((PULONG)GetLanguageSpecificData(info) + 1)
|
|
|
|
static unsigned x86_64_get_addr(HANDLE hThread, const CONTEXT* ctx,
|
|
enum cpu_addr ca, ADDRESS64* addr)
|
|
{
|
|
addr->Mode = AddrModeFlat;
|
|
switch (ca)
|
|
{
|
|
#ifdef __x86_64__
|
|
case cpu_addr_pc: addr->Segment = ctx->SegCs; addr->Offset = ctx->Rip; return TRUE;
|
|
case cpu_addr_stack: addr->Segment = ctx->SegSs; addr->Offset = ctx->Rsp; return TRUE;
|
|
case cpu_addr_frame: addr->Segment = ctx->SegSs; addr->Offset = ctx->Rbp; return TRUE;
|
|
#endif
|
|
default: addr->Mode = -1;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
enum st_mode {stm_start, stm_64bit, stm_done};
|
|
|
|
/* indexes in Reserved array */
|
|
#define __CurrentMode 0
|
|
#define __CurrentSwitch 1
|
|
#define __NextSwitch 2
|
|
|
|
#define curr_mode (frame->Reserved[__CurrentMode])
|
|
#define curr_switch (frame->Reserved[__CurrentSwitch])
|
|
#define next_switch (frame->Reserved[__NextSwitch])
|
|
|
|
#ifdef __x86_64__
|
|
union handler_data
|
|
{
|
|
RUNTIME_FUNCTION chain;
|
|
ULONG handler;
|
|
};
|
|
|
|
static void dump_unwind_info(HANDLE hProcess, ULONG64 base, RUNTIME_FUNCTION *function)
|
|
{
|
|
static const char * const reg_names[16] =
|
|
{ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
|
|
|
|
union handler_data *handler_data;
|
|
char buffer[sizeof(UNWIND_INFO) + 256 * sizeof(UNWIND_CODE)];
|
|
UNWIND_INFO* info = (UNWIND_INFO*)buffer;
|
|
unsigned int i, count;
|
|
SIZE_T r;
|
|
|
|
TRACE("**** func %x-%x\n", function->BeginAddress, function->EndAddress);
|
|
for (;;)
|
|
{
|
|
if (function->UnwindData & 1)
|
|
{
|
|
#if 0
|
|
RUNTIME_FUNCTION *next = (RUNTIME_FUNCTION*)((char*)base + (function->UnwindData & ~1));
|
|
TRACE("unwind info for function %p-%p chained to function %p-%p\n",
|
|
(char*)base + function->BeginAddress, (char*)base + function->EndAddress,
|
|
(char*)base + next->BeginAddress, (char*)base + next->EndAddress);
|
|
function = next;
|
|
continue;
|
|
#else
|
|
FIXME("NOT SUPPORTED\n");
|
|
#endif
|
|
}
|
|
ReadProcessMemory(hProcess, (char*)base + function->UnwindData, info, sizeof(*info), &r);
|
|
ReadProcessMemory(hProcess, (char*)base + function->UnwindData + FIELD_OFFSET(UNWIND_INFO, UnwindCode),
|
|
info->UnwindCode, 256 * sizeof(UNWIND_CODE), &r);
|
|
TRACE("unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
|
|
info, info->Flags, info->SizeOfProlog,
|
|
(char*)base + function->BeginAddress, (char*)base + function->EndAddress);
|
|
|
|
if (info->FrameRegister)
|
|
TRACE(" frame register %s offset 0x%x(%%rsp)\n",
|
|
reg_names[info->FrameRegister], info->FrameOffset * 16);
|
|
|
|
for (i = 0; i < info->CountOfCodes; i++)
|
|
{
|
|
TRACE(" 0x%x: ", info->UnwindCode[i].CodeOffset);
|
|
switch (info->UnwindCode[i].UnwindOp)
|
|
{
|
|
case UWOP_PUSH_NONVOL:
|
|
TRACE("pushq %%%s\n", reg_names[info->UnwindCode[i].OpInfo]);
|
|
break;
|
|
case UWOP_ALLOC_LARGE:
|
|
if (info->UnwindCode[i].OpInfo)
|
|
{
|
|
count = *(DWORD*)&info->UnwindCode[i+1];
|
|
i += 2;
|
|
}
|
|
else
|
|
{
|
|
count = *(USHORT*)&info->UnwindCode[i+1] * 8;
|
|
i++;
|
|
}
|
|
TRACE("subq $0x%x,%%rsp\n", count);
|
|
break;
|
|
case UWOP_ALLOC_SMALL:
|
|
count = (info->UnwindCode[i].OpInfo + 1) * 8;
|
|
TRACE("subq $0x%x,%%rsp\n", count);
|
|
break;
|
|
case UWOP_SET_FPREG:
|
|
TRACE("leaq 0x%x(%%rsp),%s\n",
|
|
info->FrameOffset * 16, reg_names[info->FrameRegister]);
|
|
break;
|
|
case UWOP_SAVE_NONVOL:
|
|
count = *(USHORT*)&info->UnwindCode[i+1] * 8;
|
|
TRACE("movq %%%s,0x%x(%%rsp)\n", reg_names[info->UnwindCode[i].OpInfo], count);
|
|
i++;
|
|
break;
|
|
case UWOP_SAVE_NONVOL_FAR:
|
|
count = *(DWORD*)&info->UnwindCode[i+1];
|
|
TRACE("movq %%%s,0x%x(%%rsp)\n", reg_names[info->UnwindCode[i].OpInfo], count);
|
|
i += 2;
|
|
break;
|
|
case UWOP_SAVE_XMM128:
|
|
count = *(USHORT*)&info->UnwindCode[i+1] * 16;
|
|
TRACE("movaps %%xmm%u,0x%x(%%rsp)\n", info->UnwindCode[i].OpInfo, count);
|
|
i++;
|
|
break;
|
|
case UWOP_SAVE_XMM128_FAR:
|
|
count = *(DWORD*)&info->UnwindCode[i+1];
|
|
TRACE("movaps %%xmm%u,0x%x(%%rsp)\n", info->UnwindCode[i].OpInfo, count);
|
|
i += 2;
|
|
break;
|
|
case UWOP_PUSH_MACHFRAME:
|
|
TRACE("PUSH_MACHFRAME %u\n", info->UnwindCode[i].OpInfo);
|
|
break;
|
|
default:
|
|
FIXME("unknown code %u\n", info->UnwindCode[i].UnwindOp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
handler_data = (union handler_data*)&info->UnwindCode[(info->CountOfCodes + 1) & ~1];
|
|
if (info->Flags & UNW_FLAG_CHAININFO)
|
|
{
|
|
TRACE(" chained to function %p-%p\n",
|
|
(char*)base + handler_data->chain.BeginAddress,
|
|
(char*)base + handler_data->chain.EndAddress);
|
|
function = &handler_data->chain;
|
|
continue;
|
|
}
|
|
if (info->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
|
|
TRACE(" handler %p data at %p\n",
|
|
(char*)base + handler_data->handler, &handler_data->handler + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* highly derived from dlls/ntdll/signal_x86_64.c */
|
|
static ULONG64 get_int_reg(CONTEXT *context, int reg)
|
|
{
|
|
return *(&context->Rax + reg);
|
|
}
|
|
|
|
static void set_int_reg(CONTEXT *context, int reg, ULONG64 val)
|
|
{
|
|
*(&context->Rax + reg) = val;
|
|
}
|
|
|
|
static void set_float_reg(CONTEXT *context, int reg, M128A val)
|
|
{
|
|
*(&context->u.s.Xmm0 + reg) = val;
|
|
}
|
|
|
|
static int get_opcode_size(UNWIND_CODE op)
|
|
{
|
|
switch (op.UnwindOp)
|
|
{
|
|
case UWOP_ALLOC_LARGE:
|
|
return 2 + (op.OpInfo != 0);
|
|
case UWOP_SAVE_NONVOL:
|
|
case UWOP_SAVE_XMM128:
|
|
return 2;
|
|
case UWOP_SAVE_NONVOL_FAR:
|
|
case UWOP_SAVE_XMM128_FAR:
|
|
return 3;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static BOOL is_inside_epilog(struct cpu_stack_walk* csw, DWORD64 pc)
|
|
{
|
|
BYTE op0, op1, op2;
|
|
|
|
if (!sw_read_mem(csw, pc, &op0, 1)) return FALSE;
|
|
|
|
/* add or lea must be the first instruction, and it must have a rex.W prefix */
|
|
if ((op0 & 0xf8) == 0x48)
|
|
{
|
|
if (!sw_read_mem(csw, pc + 1, &op1, 1)) return FALSE;
|
|
switch (op1)
|
|
{
|
|
case 0x81: /* add $nnnn,%rsp */
|
|
if (!sw_read_mem(csw, pc + 2, &op2, 1)) return FALSE;
|
|
if (op0 == 0x48 && op2 == 0xc4)
|
|
{
|
|
pc += 7;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
case 0x83: /* add $n,%rsp */
|
|
if (op0 == 0x48 && op2 == 0xc4)
|
|
{
|
|
pc += 4;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
case 0x8d: /* lea n(reg),%rsp */
|
|
if (op0 & 0x06) return FALSE; /* rex.RX must be cleared */
|
|
if (((op2 >> 3) & 7) != 4) return FALSE; /* dest reg mus be %rsp */
|
|
if ((op2 & 7) == 4) return FALSE; /* no SIB byte allowed */
|
|
if ((op2 >> 6) == 1) /* 8-bit offset */
|
|
{
|
|
pc += 4;
|
|
break;
|
|
}
|
|
if ((op2 >> 6) == 2) /* 32-bit offset */
|
|
{
|
|
pc += 7;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* now check for various pop instructions */
|
|
for (;;)
|
|
{
|
|
BYTE rex = 0;
|
|
|
|
if (!sw_read_mem(csw, pc, &op0, 1)) return FALSE;
|
|
if ((op0 & 0xf0) == 0x40)
|
|
{
|
|
rex = op0 & 0x0f; /* rex prefix */
|
|
if (!sw_read_mem(csw, ++pc, &op0, 1)) return FALSE;
|
|
}
|
|
|
|
switch (op0)
|
|
{
|
|
case 0x58: /* pop %rax/%r8 */
|
|
case 0x59: /* pop %rcx/%r9 */
|
|
case 0x5a: /* pop %rdx/%r10 */
|
|
case 0x5b: /* pop %rbx/%r11 */
|
|
case 0x5c: /* pop %rsp/%r12 */
|
|
case 0x5d: /* pop %rbp/%r13 */
|
|
case 0x5e: /* pop %rsi/%r14 */
|
|
case 0x5f: /* pop %rdi/%r15 */
|
|
pc++;
|
|
continue;
|
|
case 0xc2: /* ret $nn */
|
|
case 0xc3: /* ret */
|
|
return TRUE;
|
|
/* FIXME: add various jump instructions */
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static BOOL default_unwind(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
|
|
{
|
|
if (!sw_read_mem(csw, frame->AddrStack.Offset,
|
|
&frame->AddrReturn.Offset, sizeof(DWORD64)))
|
|
{
|
|
WARN("Cannot read new frame offset %s\n", wine_dbgstr_longlong(frame->AddrStack.Offset));
|
|
return FALSE;
|
|
}
|
|
context->Rip = frame->AddrReturn.Offset;
|
|
frame->AddrStack.Offset += sizeof(DWORD64);
|
|
context->Rsp += sizeof(DWORD64);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL interpret_function_table_entry(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame,
|
|
CONTEXT* context, RUNTIME_FUNCTION* function, DWORD64 base)
|
|
{
|
|
char buffer[sizeof(UNWIND_INFO) + 256 * sizeof(UNWIND_CODE)];
|
|
UNWIND_INFO* info = (UNWIND_INFO*)buffer;
|
|
unsigned i;
|
|
DWORD64 newframe, prolog_offset, off, value;
|
|
M128A floatvalue;
|
|
union handler_data handler_data;
|
|
|
|
/* FIXME: we have some assumptions here */
|
|
assert(context);
|
|
if (context->Rsp != frame->AddrStack.Offset) FIXME("unconsistent Stack Pointer\n");
|
|
if (context->Rip != frame->AddrPC.Offset) FIXME("unconsistent Instruction Pointer\n");
|
|
dump_unwind_info(csw->hProcess, sw_module_base(csw, frame->AddrPC.Offset), frame->FuncTableEntry);
|
|
newframe = context->Rsp;
|
|
for (;;)
|
|
{
|
|
if (!sw_read_mem(csw, base + function->UnwindData, info, sizeof(*info)) ||
|
|
!sw_read_mem(csw, base + function->UnwindData + FIELD_OFFSET(UNWIND_INFO, UnwindCode),
|
|
info->UnwindCode, info->CountOfCodes * sizeof(UNWIND_CODE)))
|
|
{
|
|
WARN("Couldn't read unwind_code at %lx\n", base + function->UnwindData);
|
|
return FALSE;
|
|
}
|
|
|
|
if (info->Version != 1)
|
|
{
|
|
WARN("unknown unwind info version %u at %lx\n", info->Version, base + function->UnwindData);
|
|
return FALSE;
|
|
}
|
|
|
|
if (info->FrameRegister)
|
|
newframe = get_int_reg(context, info->FrameRegister) - info->FrameOffset * 16;
|
|
|
|
/* check if in prolog */
|
|
if (frame->AddrPC.Offset >= base + function->BeginAddress &&
|
|
frame->AddrPC.Offset < base + function->BeginAddress + info->SizeOfProlog)
|
|
{
|
|
prolog_offset = frame->AddrPC.Offset - base - function->BeginAddress;
|
|
}
|
|
else
|
|
{
|
|
prolog_offset = ~0;
|
|
if (is_inside_epilog(csw, frame->AddrPC.Offset))
|
|
{
|
|
FIXME("epilog management not fully done\n");
|
|
/* interpret_epilog((const BYTE*)frame->AddrPC.Offset, context); */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < info->CountOfCodes; i += get_opcode_size(info->UnwindCode[i]))
|
|
{
|
|
if (prolog_offset < info->UnwindCode[i].CodeOffset) continue; /* skip it */
|
|
|
|
switch (info->UnwindCode[i].UnwindOp)
|
|
{
|
|
case UWOP_PUSH_NONVOL: /* pushq %reg */
|
|
if (!sw_read_mem(csw, context->Rsp, &value, sizeof(DWORD64))) return FALSE;
|
|
set_int_reg(context, info->UnwindCode[i].OpInfo, value);
|
|
context->Rsp += sizeof(ULONG64);
|
|
break;
|
|
case UWOP_ALLOC_LARGE: /* subq $nn,%rsp */
|
|
if (info->UnwindCode[i].OpInfo) context->Rsp += *(DWORD*)&info->UnwindCode[i+1];
|
|
else context->Rsp += *(USHORT*)&info->UnwindCode[i+1] * 8;
|
|
break;
|
|
case UWOP_ALLOC_SMALL: /* subq $n,%rsp */
|
|
context->Rsp += (info->UnwindCode[i].OpInfo + 1) * 8;
|
|
break;
|
|
case UWOP_SET_FPREG: /* leaq nn(%rsp),%framereg */
|
|
context->Rsp = newframe;
|
|
break;
|
|
case UWOP_SAVE_NONVOL: /* movq %reg,n(%rsp) */
|
|
off = newframe + *(USHORT*)&info->UnwindCode[i+1] * 8;
|
|
if (!sw_read_mem(csw, context->Rsp, &value, sizeof(DWORD64))) return FALSE;
|
|
set_int_reg(context, info->UnwindCode[i].OpInfo, value);
|
|
break;
|
|
case UWOP_SAVE_NONVOL_FAR: /* movq %reg,nn(%rsp) */
|
|
off = newframe + *(DWORD*)&info->UnwindCode[i+1];
|
|
if (!sw_read_mem(csw, context->Rsp, &value, sizeof(DWORD64))) return FALSE;
|
|
set_int_reg(context, info->UnwindCode[i].OpInfo, value);
|
|
break;
|
|
case UWOP_SAVE_XMM128: /* movaps %xmmreg,n(%rsp) */
|
|
off = newframe + *(USHORT*)&info->UnwindCode[i+1] * 16;
|
|
if (!sw_read_mem(csw, context->Rsp, &floatvalue, sizeof(M128A))) return FALSE;
|
|
set_float_reg(context, info->UnwindCode[i].OpInfo, floatvalue);
|
|
break;
|
|
case UWOP_SAVE_XMM128_FAR: /* movaps %xmmreg,nn(%rsp) */
|
|
off = newframe + *(DWORD*)&info->UnwindCode[i+1];
|
|
if (!sw_read_mem(csw, context->Rsp, &floatvalue, sizeof(M128A))) return FALSE;
|
|
set_float_reg(context, info->UnwindCode[i].OpInfo, floatvalue);
|
|
break;
|
|
case UWOP_PUSH_MACHFRAME:
|
|
FIXME("PUSH_MACHFRAME %u\n", info->UnwindCode[i].OpInfo);
|
|
break;
|
|
default:
|
|
FIXME("unknown code %u\n", info->UnwindCode[i].UnwindOp);
|
|
break;
|
|
}
|
|
}
|
|
if (!(info->Flags & UNW_FLAG_CHAININFO)) break;
|
|
if (!sw_read_mem(csw, base + function->UnwindData + FIELD_OFFSET(UNWIND_INFO, UnwindCode) +
|
|
((info->CountOfCodes + 1) & ~1) * sizeof(UNWIND_CODE),
|
|
&handler_data, sizeof(handler_data))) return FALSE;
|
|
function = &handler_data.chain; /* restart with the chained info */
|
|
}
|
|
frame->AddrStack.Offset = context->Rsp;
|
|
return default_unwind(csw, frame, context);
|
|
}
|
|
|
|
static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
|
|
{
|
|
DWORD64 base;
|
|
DWORD_PTR cfa;
|
|
unsigned deltapc = 0;
|
|
|
|
/* sanity check */
|
|
if (curr_mode >= stm_done) return FALSE;
|
|
assert(!csw->is32);
|
|
|
|
TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s\n",
|
|
wine_dbgstr_addr(&frame->AddrPC),
|
|
wine_dbgstr_addr(&frame->AddrFrame),
|
|
wine_dbgstr_addr(&frame->AddrReturn),
|
|
wine_dbgstr_addr(&frame->AddrStack),
|
|
curr_mode == stm_start ? "start" : "64bit");
|
|
|
|
if (curr_mode == stm_start)
|
|
{
|
|
if ((frame->AddrPC.Mode == AddrModeFlat) &&
|
|
(frame->AddrFrame.Mode != AddrModeFlat))
|
|
{
|
|
WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
|
|
goto done_err;
|
|
}
|
|
|
|
/* Init done */
|
|
curr_mode = stm_64bit;
|
|
curr_switch = 0;
|
|
frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrModeFlat;
|
|
/* don't set up AddrStack on first call. Either the caller has set it up, or
|
|
* we will get it in the next frame
|
|
*/
|
|
memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
|
|
}
|
|
else
|
|
{
|
|
if (frame->AddrReturn.Offset == 0) goto done_err;
|
|
frame->AddrPC = frame->AddrReturn;
|
|
deltapc = 1;
|
|
}
|
|
|
|
if (!frame->AddrPC.Offset || !(base = sw_module_base(csw, frame->AddrPC.Offset))) goto done_err;
|
|
frame->FuncTableEntry = sw_table_access(csw, frame->AddrPC.Offset);
|
|
frame->AddrStack.Mode = frame->AddrFrame.Mode = frame->AddrReturn.Mode = AddrModeFlat;
|
|
if (frame->FuncTableEntry)
|
|
{
|
|
if (!interpret_function_table_entry(csw, frame, context, frame->FuncTableEntry, base))
|
|
goto done_err;
|
|
}
|
|
else if (dwarf2_virtual_unwind(csw, frame->AddrPC.Offset - deltapc, context, &cfa))
|
|
{
|
|
frame->AddrStack.Offset = context->Rsp = cfa;
|
|
frame->AddrReturn.Offset = context->Rip;
|
|
TRACE("next function rip=%016lx\n", context->Rip);
|
|
TRACE(" rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
|
|
context->Rax, context->Rbx, context->Rcx, context->Rdx);
|
|
TRACE(" rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
|
|
context->Rsi, context->Rdi, context->Rbp, context->Rsp);
|
|
TRACE(" r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
|
|
context->R8, context->R9, context->R10, context->R11);
|
|
TRACE(" r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
|
|
context->R12, context->R13, context->R14, context->R15);
|
|
}
|
|
else if (!default_unwind(csw, frame, context)) goto done_err;
|
|
|
|
memset(&frame->Params, 0, sizeof(frame->Params));
|
|
|
|
frame->Far = TRUE;
|
|
frame->Virtual = TRUE;
|
|
|
|
TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s FuncTable=%p\n",
|
|
wine_dbgstr_addr(&frame->AddrPC),
|
|
wine_dbgstr_addr(&frame->AddrFrame),
|
|
wine_dbgstr_addr(&frame->AddrReturn),
|
|
wine_dbgstr_addr(&frame->AddrStack),
|
|
curr_mode == stm_start ? "start" : "64bit",
|
|
frame->FuncTableEntry);
|
|
|
|
return TRUE;
|
|
done_err:
|
|
curr_mode = stm_done;
|
|
return FALSE;
|
|
}
|
|
#else
|
|
static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static void* x86_64_find_runtime_function(struct module* module, DWORD64 addr)
|
|
{
|
|
#ifdef __x86_64__
|
|
RUNTIME_FUNCTION* rtf;
|
|
ULONG size;
|
|
int min, max;
|
|
|
|
rtf = (RUNTIME_FUNCTION*)pe_map_directory(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size);
|
|
if (rtf) for (min = 0, max = size / sizeof(*rtf); min <= max; )
|
|
{
|
|
int pos = (min + max) / 2;
|
|
if (addr < module->module.BaseOfImage + rtf[pos].BeginAddress) max = pos - 1;
|
|
else if (addr >= module->module.BaseOfImage + rtf[pos].EndAddress) min = pos + 1;
|
|
else
|
|
{
|
|
rtf += pos;
|
|
while (rtf->UnwindData & 1) /* follow chained entry */
|
|
{
|
|
FIXME("RunTime_Function outside IMAGE_DIRECTORY_ENTRY_EXCEPTION unimplemented yet!\n");
|
|
/* we need to read into the other process */
|
|
/* rtf = (RUNTIME_FUNCTION*)(module->module.BaseOfImage + (rtf->UnwindData & ~1)); */
|
|
}
|
|
return rtf;
|
|
}
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned x86_64_map_dwarf_register(unsigned regno)
|
|
{
|
|
unsigned reg;
|
|
|
|
if (regno >= 17 && regno <= 24)
|
|
reg = CV_AMD64_XMM0 + regno - 17;
|
|
else if (regno >= 25 && regno <= 32)
|
|
reg = CV_AMD64_XMM8 + regno - 25;
|
|
else if (regno >= 33 && regno <= 40)
|
|
reg = CV_AMD64_ST0 + regno - 33;
|
|
else switch (regno)
|
|
{
|
|
case 0: reg = CV_AMD64_RAX; break;
|
|
case 1: reg = CV_AMD64_RDX; break;
|
|
case 2: reg = CV_AMD64_RCX; break;
|
|
case 3: reg = CV_AMD64_RBX; break;
|
|
case 4: reg = CV_AMD64_RSI; break;
|
|
case 5: reg = CV_AMD64_RDI; break;
|
|
case 6: reg = CV_AMD64_RBP; break;
|
|
case 7: reg = CV_AMD64_RSP; break;
|
|
case 8: reg = CV_AMD64_R8; break;
|
|
case 9: reg = CV_AMD64_R9; break;
|
|
case 10: reg = CV_AMD64_R10; break;
|
|
case 11: reg = CV_AMD64_R11; break;
|
|
case 12: reg = CV_AMD64_R12; break;
|
|
case 13: reg = CV_AMD64_R13; break;
|
|
case 14: reg = CV_AMD64_R14; break;
|
|
case 15: reg = CV_AMD64_R15; break;
|
|
case 16: reg = CV_AMD64_RIP; break;
|
|
case 49: reg = CV_AMD64_EFLAGS; break;
|
|
case 50: reg = CV_AMD64_ES; break;
|
|
case 51: reg = CV_AMD64_CS; break;
|
|
case 52: reg = CV_AMD64_SS; break;
|
|
case 53: reg = CV_AMD64_DS; break;
|
|
case 54: reg = CV_AMD64_FS; break;
|
|
case 55: reg = CV_AMD64_GS; break;
|
|
case 62: reg = CV_AMD64_TR; break;
|
|
case 63: reg = CV_AMD64_LDTR; break;
|
|
case 64: reg = CV_AMD64_MXCSR; break;
|
|
case 65: reg = CV_AMD64_CTRL; break;
|
|
case 66: reg = CV_AMD64_STAT; break;
|
|
/*
|
|
* 56-57 reserved
|
|
* 58 %fs.base
|
|
* 59 %gs.base
|
|
* 60-61 reserved
|
|
*/
|
|
default:
|
|
FIXME("Don't know how to map register %d\n", regno);
|
|
return 0;
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
static void* x86_64_fetch_context_reg(CONTEXT* ctx, unsigned regno, unsigned* size)
|
|
{
|
|
#ifdef __x86_64__
|
|
switch (regno)
|
|
{
|
|
case CV_AMD64_RAX: *size = sizeof(ctx->Rax); return &ctx->Rax;
|
|
case CV_AMD64_RDX: *size = sizeof(ctx->Rdx); return &ctx->Rdx;
|
|
case CV_AMD64_RCX: *size = sizeof(ctx->Rcx); return &ctx->Rcx;
|
|
case CV_AMD64_RBX: *size = sizeof(ctx->Rbx); return &ctx->Rbx;
|
|
case CV_AMD64_RSI: *size = sizeof(ctx->Rsi); return &ctx->Rsi;
|
|
case CV_AMD64_RDI: *size = sizeof(ctx->Rdi); return &ctx->Rdi;
|
|
case CV_AMD64_RBP: *size = sizeof(ctx->Rbp); return &ctx->Rbp;
|
|
case CV_AMD64_RSP: *size = sizeof(ctx->Rsp); return &ctx->Rsp;
|
|
case CV_AMD64_R8: *size = sizeof(ctx->R8); return &ctx->R8;
|
|
case CV_AMD64_R9: *size = sizeof(ctx->R9); return &ctx->R9;
|
|
case CV_AMD64_R10: *size = sizeof(ctx->R10); return &ctx->R10;
|
|
case CV_AMD64_R11: *size = sizeof(ctx->R11); return &ctx->R11;
|
|
case CV_AMD64_R12: *size = sizeof(ctx->R12); return &ctx->R12;
|
|
case CV_AMD64_R13: *size = sizeof(ctx->R13); return &ctx->R13;
|
|
case CV_AMD64_R14: *size = sizeof(ctx->R14); return &ctx->R14;
|
|
case CV_AMD64_R15: *size = sizeof(ctx->R15); return &ctx->R15;
|
|
case CV_AMD64_RIP: *size = sizeof(ctx->Rip); return &ctx->Rip;
|
|
|
|
case CV_AMD64_XMM0 + 0: *size = sizeof(ctx->u.s.Xmm0 ); return &ctx->u.s.Xmm0;
|
|
case CV_AMD64_XMM0 + 1: *size = sizeof(ctx->u.s.Xmm1 ); return &ctx->u.s.Xmm1;
|
|
case CV_AMD64_XMM0 + 2: *size = sizeof(ctx->u.s.Xmm2 ); return &ctx->u.s.Xmm2;
|
|
case CV_AMD64_XMM0 + 3: *size = sizeof(ctx->u.s.Xmm3 ); return &ctx->u.s.Xmm3;
|
|
case CV_AMD64_XMM0 + 4: *size = sizeof(ctx->u.s.Xmm4 ); return &ctx->u.s.Xmm4;
|
|
case CV_AMD64_XMM0 + 5: *size = sizeof(ctx->u.s.Xmm5 ); return &ctx->u.s.Xmm5;
|
|
case CV_AMD64_XMM0 + 6: *size = sizeof(ctx->u.s.Xmm6 ); return &ctx->u.s.Xmm6;
|
|
case CV_AMD64_XMM0 + 7: *size = sizeof(ctx->u.s.Xmm7 ); return &ctx->u.s.Xmm7;
|
|
case CV_AMD64_XMM8 + 0: *size = sizeof(ctx->u.s.Xmm8 ); return &ctx->u.s.Xmm8;
|
|
case CV_AMD64_XMM8 + 1: *size = sizeof(ctx->u.s.Xmm9 ); return &ctx->u.s.Xmm9;
|
|
case CV_AMD64_XMM8 + 2: *size = sizeof(ctx->u.s.Xmm10); return &ctx->u.s.Xmm10;
|
|
case CV_AMD64_XMM8 + 3: *size = sizeof(ctx->u.s.Xmm11); return &ctx->u.s.Xmm11;
|
|
case CV_AMD64_XMM8 + 4: *size = sizeof(ctx->u.s.Xmm12); return &ctx->u.s.Xmm12;
|
|
case CV_AMD64_XMM8 + 5: *size = sizeof(ctx->u.s.Xmm13); return &ctx->u.s.Xmm13;
|
|
case CV_AMD64_XMM8 + 6: *size = sizeof(ctx->u.s.Xmm14); return &ctx->u.s.Xmm14;
|
|
case CV_AMD64_XMM8 + 7: *size = sizeof(ctx->u.s.Xmm15); return &ctx->u.s.Xmm15;
|
|
|
|
case CV_AMD64_ST0 + 0: *size = sizeof(ctx->u.s.Legacy[0]); return &ctx->u.s.Legacy[0];
|
|
case CV_AMD64_ST0 + 1: *size = sizeof(ctx->u.s.Legacy[1]); return &ctx->u.s.Legacy[1];
|
|
case CV_AMD64_ST0 + 2: *size = sizeof(ctx->u.s.Legacy[2]); return &ctx->u.s.Legacy[2];
|
|
case CV_AMD64_ST0 + 3: *size = sizeof(ctx->u.s.Legacy[3]); return &ctx->u.s.Legacy[3];
|
|
case CV_AMD64_ST0 + 4: *size = sizeof(ctx->u.s.Legacy[4]); return &ctx->u.s.Legacy[4];
|
|
case CV_AMD64_ST0 + 5: *size = sizeof(ctx->u.s.Legacy[5]); return &ctx->u.s.Legacy[5];
|
|
case CV_AMD64_ST0 + 6: *size = sizeof(ctx->u.s.Legacy[6]); return &ctx->u.s.Legacy[6];
|
|
case CV_AMD64_ST0 + 7: *size = sizeof(ctx->u.s.Legacy[7]); return &ctx->u.s.Legacy[7];
|
|
|
|
case CV_AMD64_EFLAGS: *size = sizeof(ctx->EFlags); return &ctx->EFlags;
|
|
case CV_AMD64_ES: *size = sizeof(ctx->SegEs); return &ctx->SegEs;
|
|
case CV_AMD64_CS: *size = sizeof(ctx->SegCs); return &ctx->SegCs;
|
|
case CV_AMD64_SS: *size = sizeof(ctx->SegSs); return &ctx->SegSs;
|
|
case CV_AMD64_DS: *size = sizeof(ctx->SegDs); return &ctx->SegDs;
|
|
case CV_AMD64_FS: *size = sizeof(ctx->SegFs); return &ctx->SegFs;
|
|
case CV_AMD64_GS: *size = sizeof(ctx->SegGs); return &ctx->SegGs;
|
|
|
|
}
|
|
#endif
|
|
FIXME("Unknown register %x\n", regno);
|
|
return NULL;
|
|
}
|
|
|
|
static const char* x86_64_fetch_regname(unsigned regno)
|
|
{
|
|
switch (regno)
|
|
{
|
|
case CV_AMD64_RAX: return "rax";
|
|
case CV_AMD64_RDX: return "rdx";
|
|
case CV_AMD64_RCX: return "rcx";
|
|
case CV_AMD64_RBX: return "rbx";
|
|
case CV_AMD64_RSI: return "rsi";
|
|
case CV_AMD64_RDI: return "rdi";
|
|
case CV_AMD64_RBP: return "rbp";
|
|
case CV_AMD64_RSP: return "rsp";
|
|
case CV_AMD64_R8: return "r8";
|
|
case CV_AMD64_R9: return "r9";
|
|
case CV_AMD64_R10: return "r10";
|
|
case CV_AMD64_R11: return "r11";
|
|
case CV_AMD64_R12: return "r12";
|
|
case CV_AMD64_R13: return "r13";
|
|
case CV_AMD64_R14: return "r14";
|
|
case CV_AMD64_R15: return "r15";
|
|
case CV_AMD64_RIP: return "rip";
|
|
|
|
case CV_AMD64_XMM0 + 0: return "xmm0";
|
|
case CV_AMD64_XMM0 + 1: return "xmm1";
|
|
case CV_AMD64_XMM0 + 2: return "xmm2";
|
|
case CV_AMD64_XMM0 + 3: return "xmm3";
|
|
case CV_AMD64_XMM0 + 4: return "xmm4";
|
|
case CV_AMD64_XMM0 + 5: return "xmm5";
|
|
case CV_AMD64_XMM0 + 6: return "xmm6";
|
|
case CV_AMD64_XMM0 + 7: return "xmm7";
|
|
case CV_AMD64_XMM8 + 0: return "xmm8";
|
|
case CV_AMD64_XMM8 + 1: return "xmm9";
|
|
case CV_AMD64_XMM8 + 2: return "xmm10";
|
|
case CV_AMD64_XMM8 + 3: return "xmm11";
|
|
case CV_AMD64_XMM8 + 4: return "xmm12";
|
|
case CV_AMD64_XMM8 + 5: return "xmm13";
|
|
case CV_AMD64_XMM8 + 6: return "xmm14";
|
|
case CV_AMD64_XMM8 + 7: return "xmm15";
|
|
|
|
case CV_AMD64_ST0 + 0: return "st0";
|
|
case CV_AMD64_ST0 + 1: return "st1";
|
|
case CV_AMD64_ST0 + 2: return "st2";
|
|
case CV_AMD64_ST0 + 3: return "st3";
|
|
case CV_AMD64_ST0 + 4: return "st4";
|
|
case CV_AMD64_ST0 + 5: return "st5";
|
|
case CV_AMD64_ST0 + 6: return "st6";
|
|
case CV_AMD64_ST0 + 7: return "st7";
|
|
|
|
case CV_AMD64_EFLAGS: return "eflags";
|
|
case CV_AMD64_ES: return "es";
|
|
case CV_AMD64_CS: return "cs";
|
|
case CV_AMD64_SS: return "ss";
|
|
case CV_AMD64_DS: return "ds";
|
|
case CV_AMD64_FS: return "fs";
|
|
case CV_AMD64_GS: return "gs";
|
|
}
|
|
FIXME("Unknown register %x\n", regno);
|
|
return NULL;
|
|
}
|
|
|
|
struct cpu cpu_x86_64 = {
|
|
IMAGE_FILE_MACHINE_AMD64,
|
|
8,
|
|
x86_64_get_addr,
|
|
x86_64_stack_walk,
|
|
x86_64_find_runtime_function,
|
|
x86_64_map_dwarf_register,
|
|
x86_64_fetch_context_reg,
|
|
x86_64_fetch_regname,
|
|
};
|