diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index dd8dad9423a..c1210e9db0e 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -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; } diff --git a/include/winternl.h b/include/winternl.h index b06a10c3745..b386787218d 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -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 */