mirror of
https://github.com/reactos/wine.git
synced 2024-11-29 06:30:37 +00:00
ntdll: Initial implementation of RtlVirtualUnwind on x86_64.
This commit is contained in:
parent
47d7702522
commit
f7b1e94f98
@ -40,6 +40,7 @@
|
||||
#endif
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
#include "ntstatus.h"
|
||||
#define WIN32_NO_STATUS
|
||||
#include "windef.h"
|
||||
@ -157,6 +158,149 @@ typedef int (*wine_signal_handler)(unsigned int sig);
|
||||
|
||||
static wine_signal_handler handlers[256];
|
||||
|
||||
/* definitions for unwind tables */
|
||||
|
||||
union handler_data
|
||||
{
|
||||
RUNTIME_FUNCTION chain;
|
||||
ULONG handler;
|
||||
};
|
||||
|
||||
struct opcode
|
||||
{
|
||||
BYTE offset;
|
||||
BYTE code : 4;
|
||||
BYTE info : 4;
|
||||
};
|
||||
|
||||
struct UNWIND_INFO
|
||||
{
|
||||
BYTE version : 3;
|
||||
BYTE flags : 5;
|
||||
BYTE prolog;
|
||||
BYTE count;
|
||||
BYTE frame_reg : 4;
|
||||
BYTE frame_offset : 4;
|
||||
struct opcode opcodes[1]; /* info->count entries */
|
||||
/* followed by handler_data */
|
||||
};
|
||||
|
||||
#define UWOP_PUSH_NONVOL 0
|
||||
#define UWOP_ALLOC_LARGE 1
|
||||
#define UWOP_ALLOC_SMALL 2
|
||||
#define UWOP_SET_FPREG 3
|
||||
#define UWOP_SAVE_NONVOL 4
|
||||
#define UWOP_SAVE_NONVOL_FAR 5
|
||||
#define UWOP_SAVE_XMM128 8
|
||||
#define UWOP_SAVE_XMM128_FAR 9
|
||||
#define UWOP_PUSH_MACHFRAME 10
|
||||
|
||||
static void dump_unwind_info( 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;
|
||||
struct UNWIND_INFO *info;
|
||||
unsigned int i, count;
|
||||
|
||||
TRACE( "**** func %x-%x\n", function->BeginAddress, function->EndAddress );
|
||||
for (;;)
|
||||
{
|
||||
if (function->UnwindData & 1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
|
||||
|
||||
TRACE( "unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
|
||||
info, info->flags, info->prolog,
|
||||
(char *)base + function->BeginAddress, (char *)base + function->EndAddress );
|
||||
|
||||
if (info->frame_reg)
|
||||
TRACE( " frame register %s offset 0x%x(%%rsp)\n",
|
||||
reg_names[info->frame_reg], info->frame_offset * 16 );
|
||||
|
||||
for (i = 0; i < info->count; i++)
|
||||
{
|
||||
TRACE( " 0x%x: ", info->opcodes[i].offset );
|
||||
switch (info->opcodes[i].code)
|
||||
{
|
||||
case UWOP_PUSH_NONVOL:
|
||||
TRACE( "pushq %%%s\n", reg_names[info->opcodes[i].info] );
|
||||
break;
|
||||
case UWOP_ALLOC_LARGE:
|
||||
if (info->opcodes[i].info)
|
||||
{
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
i++;
|
||||
}
|
||||
TRACE( "subq $0x%x,%%rsp\n", count );
|
||||
break;
|
||||
case UWOP_ALLOC_SMALL:
|
||||
count = (info->opcodes[i].info + 1) * 8;
|
||||
TRACE( "subq $0x%x,%%rsp\n", count );
|
||||
break;
|
||||
case UWOP_SET_FPREG:
|
||||
TRACE( "leaq 0x%x(%%rsp),%s\n",
|
||||
info->frame_offset * 16, reg_names[info->frame_reg] );
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL:
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
|
||||
i++;
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL_FAR:
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
|
||||
i += 2;
|
||||
break;
|
||||
case UWOP_SAVE_XMM128:
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 16;
|
||||
TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
|
||||
i++;
|
||||
break;
|
||||
case UWOP_SAVE_XMM128_FAR:
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
|
||||
i += 2;
|
||||
break;
|
||||
case UWOP_PUSH_MACHFRAME:
|
||||
TRACE( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
|
||||
break;
|
||||
default:
|
||||
FIXME( "unknown code %u\n", info->opcodes[i].code );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler_data = (union handler_data *)&info->opcodes[(info->count + 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;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* dispatch_signal
|
||||
*/
|
||||
@ -882,19 +1026,144 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG64 pc, ULONG64 *base,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ULONG64 get_int_reg( CONTEXT *context, int reg )
|
||||
{
|
||||
return *(&context->Rax + reg);
|
||||
}
|
||||
|
||||
static void set_int_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, ULONG64 val )
|
||||
{
|
||||
*(&context->Rax + reg) = val;
|
||||
if (ctx_ptr) ctx_ptr->u2.IntegerContext[reg] = &context->Rax + reg;
|
||||
}
|
||||
|
||||
static void set_float_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, M128A val )
|
||||
{
|
||||
*(&context->u.s.Xmm0 + reg) = val;
|
||||
if (ctx_ptr) ctx_ptr->u1.FloatingContext[reg] = &context->u.s.Xmm0 + reg;
|
||||
}
|
||||
|
||||
static int get_opcode_size( struct opcode op )
|
||||
{
|
||||
switch (op.code)
|
||||
{
|
||||
case UWOP_ALLOC_LARGE:
|
||||
return 2 + (op.info != 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* RtlVirtualUnwind (NTDLL.@)
|
||||
*/
|
||||
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
|
||||
RUNTIME_FUNCTION *function, CONTEXT *context,
|
||||
PVOID *data, ULONG64 *frame,
|
||||
PVOID *data, ULONG64 *frame_ret,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
|
||||
{
|
||||
FIXME("stub\n");
|
||||
union handler_data *handler_data;
|
||||
ULONG64 frame, off;
|
||||
struct UNWIND_INFO *info;
|
||||
unsigned int i, prolog_offset;
|
||||
|
||||
TRACE( "type %x rip %lx rsp %lx\n", type, pc, context->Rsp );
|
||||
|
||||
dump_unwind_info( base, function );
|
||||
|
||||
frame = context->Rsp;
|
||||
for (;;)
|
||||
{
|
||||
info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
|
||||
handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
|
||||
|
||||
if (info->version != 1)
|
||||
{
|
||||
FIXME( "unknown unwind info version %u at %p\n", info->version, info );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (info->frame_reg)
|
||||
frame = get_int_reg( context, info->frame_reg ) - info->frame_offset * 16;
|
||||
|
||||
/* check if in prolog */
|
||||
if (pc >= base + function->BeginAddress && pc < base + function->BeginAddress + info->prolog)
|
||||
{
|
||||
prolog_offset = pc - base - function->BeginAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
prolog_offset = ~0;
|
||||
/* FIXME: check for function epilog */
|
||||
}
|
||||
|
||||
for (i = 0; i < info->count; i += get_opcode_size(info->opcodes[i]))
|
||||
{
|
||||
if (prolog_offset < info->opcodes[i].offset) continue; /* skip it */
|
||||
|
||||
switch (info->opcodes[i].code)
|
||||
{
|
||||
case UWOP_PUSH_NONVOL: /* pushq %reg */
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, *(ULONG64 *)context->Rsp );
|
||||
context->Rsp += sizeof(ULONG64);
|
||||
break;
|
||||
case UWOP_ALLOC_LARGE: /* subq $nn,%rsp */
|
||||
if (info->opcodes[i].info) context->Rsp += *(DWORD *)&info->opcodes[i+1];
|
||||
else context->Rsp += *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
break;
|
||||
case UWOP_ALLOC_SMALL: /* subq $n,%rsp */
|
||||
context->Rsp += (info->opcodes[i].info + 1) * 8;
|
||||
break;
|
||||
case UWOP_SET_FPREG: /* leaq nn(%rsp),%framereg */
|
||||
context->Rsp = frame;
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL: /* movq %reg,n(%rsp) */
|
||||
off = frame + *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, *(ULONG64 *)off );
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL_FAR: /* movq %reg,nn(%rsp) */
|
||||
off = frame + *(DWORD *)&info->opcodes[i+1];
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, *(ULONG64 *)off );
|
||||
break;
|
||||
case UWOP_SAVE_XMM128: /* movaps %xmmreg,n(%rsp) */
|
||||
off = frame + *(USHORT *)&info->opcodes[i+1] * 16;
|
||||
set_float_reg( context, ctx_ptr, info->opcodes[i].info, *(M128A *)off );
|
||||
break;
|
||||
case UWOP_SAVE_XMM128_FAR: /* movaps %xmmreg,nn(%rsp) */
|
||||
off = frame + *(DWORD *)&info->opcodes[i+1];
|
||||
set_float_reg( context, ctx_ptr, info->opcodes[i].info, *(M128A *)off );
|
||||
break;
|
||||
case UWOP_PUSH_MACHFRAME:
|
||||
FIXME( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
|
||||
break;
|
||||
default:
|
||||
FIXME( "unknown code %u\n", info->opcodes[i].code );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(info->flags & UNW_FLAG_CHAININFO)) break;
|
||||
function = &handler_data->chain; /* restart with the chained info */
|
||||
}
|
||||
|
||||
/* now pop return address */
|
||||
context->Rip = *(ULONG64 *)context->Rsp;
|
||||
context->Rsp += sizeof(ULONG64);
|
||||
*frame_ret = frame;
|
||||
|
||||
if (!(info->flags & type)) return NULL; /* no matching handler */
|
||||
if (prolog_offset != ~0) return NULL; /* inside prolog */
|
||||
|
||||
*data = &handler_data->handler + 1;
|
||||
return (char *)base + handler_data->handler;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* RtlUnwindEx (NTDLL.@)
|
||||
|
@ -994,6 +994,11 @@ typedef struct _KNONVOLATILE_CONTEXT_POINTERS
|
||||
|
||||
PVOID WINAPI RtlVirtualUnwind(ULONG,ULONG64,ULONG64,RUNTIME_FUNCTION*,CONTEXT*,PVOID*,ULONG64*,KNONVOLATILE_CONTEXT_POINTERS*);
|
||||
|
||||
#define UNW_FLAG_NHANDLER 0
|
||||
#define UNW_FLAG_EHANDLER 1
|
||||
#define UNW_FLAG_UHANDLER 2
|
||||
#define UNW_FLAG_CHAININFO 4
|
||||
|
||||
#endif /* __x86_64 */
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user