ntdll: Initial implementation of RtlVirtualUnwind on x86_64.

This commit is contained in:
Alexandre Julliard 2009-05-15 20:18:59 +02:00
parent 47d7702522
commit f7b1e94f98
2 changed files with 280 additions and 6 deletions

View File

@ -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,17 +1026,142 @@ 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,
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
RUNTIME_FUNCTION *function, CONTEXT *context,
PVOID *data, ULONG64 *frame_ret,
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
{
FIXME("stub\n");
return NULL;
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;
}

View File

@ -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 */