| /* |
| * x86-64 signal handling routines |
| * |
| * Copyright 1999, 2005 Alexandre Julliard |
| * |
| * 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 |
| */ |
| |
| #ifdef __x86_64__ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <assert.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| |
| #ifdef HAVE_SYS_PARAM_H |
| # include <sys/param.h> |
| #endif |
| #ifdef HAVE_SYS_SIGNAL_H |
| # include <sys/signal.h> |
| #endif |
| |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| #include "ntstatus.h" |
| #define WIN32_NO_STATUS |
| #include "windef.h" |
| #include "winternl.h" |
| #include "wine/library.h" |
| #include "wine/exception.h" |
| #include "ntdll_misc.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(seh); |
| |
| struct _DISPATCHER_CONTEXT; |
| |
| typedef LONG (WINAPI *PC_LANGUAGE_EXCEPTION_HANDLER)( EXCEPTION_POINTERS *ptrs, ULONG64 frame ); |
| typedef EXCEPTION_DISPOSITION (WINAPI *PEXCEPTION_ROUTINE)( EXCEPTION_RECORD *rec, |
| ULONG64 frame, |
| CONTEXT *context, |
| struct _DISPATCHER_CONTEXT *dispatch ); |
| |
| typedef struct _DISPATCHER_CONTEXT |
| { |
| ULONG64 ControlPc; |
| ULONG64 ImageBase; |
| PRUNTIME_FUNCTION FunctionEntry; |
| ULONG64 EstablisherFrame; |
| ULONG64 TargetIp; |
| PCONTEXT ContextRecord; |
| PEXCEPTION_ROUTINE LanguageHandler; |
| PVOID HandlerData; |
| PUNWIND_HISTORY_TABLE HistoryTable; |
| ULONG ScopeIndex; |
| } DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT; |
| |
| typedef struct _SCOPE_TABLE |
| { |
| ULONG Count; |
| struct |
| { |
| ULONG BeginAddress; |
| ULONG EndAddress; |
| ULONG HandlerAddress; |
| ULONG JumpTarget; |
| } ScopeRecord[1]; |
| } SCOPE_TABLE, *PSCOPE_TABLE; |
| |
| |
| /*********************************************************************** |
| * signal context platform-specific definitions |
| */ |
| #ifdef linux |
| |
| #include <asm/prctl.h> |
| extern int arch_prctl(int func, void *ptr); |
| |
| #define RAX_sig(context) ((context)->uc_mcontext.gregs[REG_RAX]) |
| #define RBX_sig(context) ((context)->uc_mcontext.gregs[REG_RBX]) |
| #define RCX_sig(context) ((context)->uc_mcontext.gregs[REG_RCX]) |
| #define RDX_sig(context) ((context)->uc_mcontext.gregs[REG_RDX]) |
| #define RSI_sig(context) ((context)->uc_mcontext.gregs[REG_RSI]) |
| #define RDI_sig(context) ((context)->uc_mcontext.gregs[REG_RDI]) |
| #define RBP_sig(context) ((context)->uc_mcontext.gregs[REG_RBP]) |
| #define R8_sig(context) ((context)->uc_mcontext.gregs[REG_R8]) |
| #define R9_sig(context) ((context)->uc_mcontext.gregs[REG_R9]) |
| #define R10_sig(context) ((context)->uc_mcontext.gregs[REG_R10]) |
| #define R11_sig(context) ((context)->uc_mcontext.gregs[REG_R11]) |
| #define R12_sig(context) ((context)->uc_mcontext.gregs[REG_R12]) |
| #define R13_sig(context) ((context)->uc_mcontext.gregs[REG_R13]) |
| #define R14_sig(context) ((context)->uc_mcontext.gregs[REG_R14]) |
| #define R15_sig(context) ((context)->uc_mcontext.gregs[REG_R15]) |
| |
| #define CS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 0)) |
| #define GS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 1)) |
| #define FS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 2)) |
| |
| #define RSP_sig(context) ((context)->uc_mcontext.gregs[REG_RSP]) |
| #define RIP_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) |
| #define EFL_sig(context) ((context)->uc_mcontext.gregs[REG_EFL]) |
| #define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) |
| #define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) |
| |
| #define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.fpregs)) |
| |
| #endif /* linux */ |
| |
| #if defined(__NetBSD__) |
| # include <sys/ucontext.h> |
| # include <sys/types.h> |
| # include <signal.h> |
| |
| #define RAX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RAX]) |
| #define RBX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RBX]) |
| #define RCX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RCX]) |
| #define RDX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RDX]) |
| #define RSI_sig(context) ((context)->uc_mcontext.__gregs[_REG_RSI]) |
| #define RDI_sig(context) ((context)->uc_mcontext.__gregs[_REG_RDI]) |
| #define RBP_sig(context) ((context)->uc_mcontext.__gregs[_REG_RBP]) |
| #define R8_sig(context) ((context)->uc_mcontext.__gregs[_REG_R8]) |
| #define R9_sig(context) ((context)->uc_mcontext.__gregs[_REG_R9]) |
| #define R10_sig(context) ((context)->uc_mcontext.__gregs[_REG_R10]) |
| #define R11_sig(context) ((context)->uc_mcontext.__gregs[_REG_R11]) |
| #define R12_sig(context) ((context)->uc_mcontext.__gregs[_REG_R12]) |
| #define R13_sig(context) ((context)->uc_mcontext.__gregs[_REG_R13]) |
| #define R14_sig(context) ((context)->uc_mcontext.__gregs[_REG_R14]) |
| #define R15_sig(context) ((context)->uc_mcontext.__gregs[_REG_R15]) |
| |
| #define CS_sig(context) ((context)->uc_mcontext.__gregs[_REG_CS]) |
| #define DS_sig(context) ((context)->uc_mcontext.__gregs[_REG_DS]) |
| #define ES_sig(context) ((context)->uc_mcontext.__gregs[_REG_ES]) |
| #define FS_sig(context) ((context)->uc_mcontext.__gregs[_REG_FS]) |
| #define GS_sig(context) ((context)->uc_mcontext.__gregs[_REG_GS]) |
| #define SS_sig(context) ((context)->uc_mcontext.__gregs[_REG_SS]) |
| |
| #define EFL_sig(context) ((context)->uc_mcontext.__gregs[_REG_RFL]) |
| |
| #define RIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.__gregs[_REG_RIP])) |
| #define RSP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.__gregs[_REG_URSP])) |
| |
| #define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) |
| #define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) |
| |
| #define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.__fpregs)) |
| #endif /* __NetBSD__ */ |
| |
| enum i386_trap_code |
| { |
| TRAP_x86_UNKNOWN = -1, /* Unknown fault (TRAP_sig not defined) */ |
| TRAP_x86_DIVIDE = 0, /* Division by zero exception */ |
| TRAP_x86_TRCTRAP = 1, /* Single-step exception */ |
| TRAP_x86_NMI = 2, /* NMI interrupt */ |
| TRAP_x86_BPTFLT = 3, /* Breakpoint exception */ |
| TRAP_x86_OFLOW = 4, /* Overflow exception */ |
| TRAP_x86_BOUND = 5, /* Bound range exception */ |
| TRAP_x86_PRIVINFLT = 6, /* Invalid opcode exception */ |
| TRAP_x86_DNA = 7, /* Device not available exception */ |
| TRAP_x86_DOUBLEFLT = 8, /* Double fault exception */ |
| TRAP_x86_FPOPFLT = 9, /* Coprocessor segment overrun */ |
| TRAP_x86_TSSFLT = 10, /* Invalid TSS exception */ |
| TRAP_x86_SEGNPFLT = 11, /* Segment not present exception */ |
| TRAP_x86_STKFLT = 12, /* Stack fault */ |
| TRAP_x86_PROTFLT = 13, /* General protection fault */ |
| TRAP_x86_PAGEFLT = 14, /* Page fault */ |
| TRAP_x86_ARITHTRAP = 16, /* Floating point exception */ |
| TRAP_x86_ALIGNFLT = 17, /* Alignment check exception */ |
| TRAP_x86_MCHK = 18, /* Machine check exception */ |
| TRAP_x86_CACHEFLT = 19 /* Cache flush exception */ |
| }; |
| |
| 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; |
| } |
| } |
| |
| static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table ) |
| { |
| unsigned int i; |
| |
| TRACE( "scope table at %p\n", table ); |
| for (i = 0; i < table->Count; i++) |
| TRACE( " %u: %lx-%lx handler %lx target %lx\n", i, |
| base + table->ScopeRecord[i].BeginAddress, |
| base + table->ScopeRecord[i].EndAddress, |
| base + table->ScopeRecord[i].HandlerAddress, |
| base + table->ScopeRecord[i].JumpTarget ); |
| } |
| |
| /*********************************************************************** |
| * dispatch_signal |
| */ |
| static inline int dispatch_signal(unsigned int sig) |
| { |
| if (handlers[sig] == NULL) return 0; |
| return handlers[sig](sig); |
| } |
| |
| /*********************************************************************** |
| * save_context |
| * |
| * Set the register values from a sigcontext. |
| */ |
| static void save_context( CONTEXT *context, const ucontext_t *sigcontext ) |
| { |
| context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; |
| context->Rax = RAX_sig(sigcontext); |
| context->Rcx = RCX_sig(sigcontext); |
| context->Rdx = RDX_sig(sigcontext); |
| context->Rbx = RBX_sig(sigcontext); |
| context->Rsp = RSP_sig(sigcontext); |
| context->Rbp = RBP_sig(sigcontext); |
| context->Rsi = RSI_sig(sigcontext); |
| context->Rdi = RDI_sig(sigcontext); |
| context->R8 = R8_sig(sigcontext); |
| context->R9 = R9_sig(sigcontext); |
| context->R10 = R10_sig(sigcontext); |
| context->R11 = R11_sig(sigcontext); |
| context->R12 = R12_sig(sigcontext); |
| context->R13 = R13_sig(sigcontext); |
| context->R14 = R14_sig(sigcontext); |
| context->R15 = R15_sig(sigcontext); |
| context->Rip = RIP_sig(sigcontext); |
| context->SegCs = CS_sig(sigcontext); |
| context->SegFs = FS_sig(sigcontext); |
| context->SegGs = GS_sig(sigcontext); |
| context->EFlags = EFL_sig(sigcontext); |
| #ifdef DS_sig |
| context->SegDs = DS_sig(sigcontext); |
| #else |
| __asm__("movw %%ds,%0" : "=m" (context->SegDs)); |
| #endif |
| #ifdef ES_sig |
| context->SegEs = ES_sig(sigcontext); |
| #else |
| __asm__("movw %%es,%0" : "=m" (context->SegEs)); |
| #endif |
| #ifdef SS_sig |
| context->SegSs = SS_sig(sigcontext); |
| #else |
| __asm__("movw %%ss,%0" : "=m" (context->SegSs)); |
| #endif |
| context->MxCsr = 0; /* FIXME */ |
| if (FPU_sig(sigcontext)) |
| { |
| context->ContextFlags |= CONTEXT_FLOATING_POINT; |
| context->u.FltSave = *FPU_sig(sigcontext); |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * restore_context |
| * |
| * Build a sigcontext from the register values. |
| */ |
| static void restore_context( const CONTEXT *context, ucontext_t *sigcontext ) |
| { |
| RAX_sig(sigcontext) = context->Rax; |
| RCX_sig(sigcontext) = context->Rcx; |
| RDX_sig(sigcontext) = context->Rdx; |
| RBX_sig(sigcontext) = context->Rbx; |
| RSP_sig(sigcontext) = context->Rsp; |
| RBP_sig(sigcontext) = context->Rbp; |
| RSI_sig(sigcontext) = context->Rsi; |
| RDI_sig(sigcontext) = context->Rdi; |
| R8_sig(sigcontext) = context->R8; |
| R9_sig(sigcontext) = context->R9; |
| R10_sig(sigcontext) = context->R10; |
| R11_sig(sigcontext) = context->R11; |
| R12_sig(sigcontext) = context->R12; |
| R13_sig(sigcontext) = context->R13; |
| R14_sig(sigcontext) = context->R14; |
| R15_sig(sigcontext) = context->R15; |
| RIP_sig(sigcontext) = context->Rip; |
| CS_sig(sigcontext) = context->SegCs; |
| FS_sig(sigcontext) = context->SegFs; |
| GS_sig(sigcontext) = context->SegGs; |
| EFL_sig(sigcontext) = context->EFlags; |
| #ifdef DS_sig |
| DS_sig(sigcontext) = context->SegDs; |
| #endif |
| #ifdef ES_sig |
| ES_sig(sigcontext) = context->SegEs; |
| #endif |
| #ifdef SS_sig |
| SS_sig(sigcontext) = context->SegSs; |
| #endif |
| if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->u.FltSave; |
| } |
| |
| |
| /*********************************************************************** |
| * RtlCaptureContext (NTDLL.@) |
| */ |
| void WINAPI __regs_RtlCaptureContext( CONTEXT *context, CONTEXT *regs ) |
| { |
| *context = *regs; |
| } |
| DEFINE_REGS_ENTRYPOINT( RtlCaptureContext, 1 ) |
| |
| |
| /*********************************************************************** |
| * set_cpu_context |
| * |
| * Set the new CPU context. |
| */ |
| void set_cpu_context( const CONTEXT *context ) |
| { |
| extern void CDECL __wine_restore_regs( const CONTEXT * ) DECLSPEC_NORETURN; |
| __wine_restore_regs( context ); |
| } |
| |
| |
| /*********************************************************************** |
| * copy_context |
| * |
| * Copy a register context according to the flags. |
| */ |
| void copy_context( CONTEXT *to, const CONTEXT *from, DWORD flags ) |
| { |
| flags &= ~CONTEXT_AMD64; /* get rid of CPU id */ |
| if (flags & CONTEXT_CONTROL) |
| { |
| to->Rbp = from->Rbp; |
| to->Rip = from->Rip; |
| to->Rsp = from->Rsp; |
| to->SegCs = from->SegCs; |
| to->SegSs = from->SegSs; |
| to->EFlags = from->EFlags; |
| to->MxCsr = from->MxCsr; |
| } |
| if (flags & CONTEXT_INTEGER) |
| { |
| to->Rax = from->Rax; |
| to->Rcx = from->Rcx; |
| to->Rdx = from->Rdx; |
| to->Rbx = from->Rbx; |
| to->Rsi = from->Rsi; |
| to->Rdi = from->Rdi; |
| to->R8 = from->R8; |
| to->R9 = from->R9; |
| to->R10 = from->R10; |
| to->R11 = from->R11; |
| to->R12 = from->R12; |
| to->R13 = from->R13; |
| to->R14 = from->R14; |
| to->R15 = from->R15; |
| } |
| if (flags & CONTEXT_SEGMENTS) |
| { |
| to->SegDs = from->SegDs; |
| to->SegEs = from->SegEs; |
| to->SegFs = from->SegFs; |
| to->SegGs = from->SegGs; |
| } |
| if (flags & CONTEXT_FLOATING_POINT) |
| { |
| to->u.FltSave = from->u.FltSave; |
| } |
| if (flags & CONTEXT_DEBUG_REGISTERS) |
| { |
| to->Dr0 = from->Dr0; |
| to->Dr1 = from->Dr1; |
| to->Dr2 = from->Dr2; |
| to->Dr3 = from->Dr3; |
| to->Dr6 = from->Dr6; |
| to->Dr7 = from->Dr7; |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * context_to_server |
| * |
| * Convert a register context to the server format. |
| */ |
| NTSTATUS context_to_server( context_t *to, const CONTEXT *from ) |
| { |
| DWORD flags = from->ContextFlags & ~CONTEXT_AMD64; /* get rid of CPU id */ |
| |
| memset( to, 0, sizeof(*to) ); |
| to->cpu = CPU_x86_64; |
| |
| if (flags & CONTEXT_CONTROL) |
| { |
| to->flags |= SERVER_CTX_CONTROL; |
| to->ctl.x86_64_regs.rbp = from->Rbp; |
| to->ctl.x86_64_regs.rip = from->Rip; |
| to->ctl.x86_64_regs.rsp = from->Rsp; |
| to->ctl.x86_64_regs.cs = from->SegCs; |
| to->ctl.x86_64_regs.ss = from->SegSs; |
| to->ctl.x86_64_regs.flags = from->EFlags; |
| to->ctl.x86_64_regs.mxcsr = from->MxCsr; |
| } |
| if (flags & CONTEXT_INTEGER) |
| { |
| to->flags |= SERVER_CTX_INTEGER; |
| to->integer.x86_64_regs.rax = from->Rax; |
| to->integer.x86_64_regs.rcx = from->Rcx; |
| to->integer.x86_64_regs.rdx = from->Rdx; |
| to->integer.x86_64_regs.rbx = from->Rbx; |
| to->integer.x86_64_regs.rsi = from->Rsi; |
| to->integer.x86_64_regs.rdi = from->Rdi; |
| to->integer.x86_64_regs.r8 = from->R8; |
| to->integer.x86_64_regs.r9 = from->R9; |
| to->integer.x86_64_regs.r10 = from->R10; |
| to->integer.x86_64_regs.r11 = from->R11; |
| to->integer.x86_64_regs.r12 = from->R12; |
| to->integer.x86_64_regs.r13 = from->R13; |
| to->integer.x86_64_regs.r14 = from->R14; |
| to->integer.x86_64_regs.r15 = from->R15; |
| } |
| if (flags & CONTEXT_SEGMENTS) |
| { |
| to->flags |= SERVER_CTX_SEGMENTS; |
| to->seg.x86_64_regs.ds = from->SegDs; |
| to->seg.x86_64_regs.es = from->SegEs; |
| to->seg.x86_64_regs.fs = from->SegFs; |
| to->seg.x86_64_regs.gs = from->SegGs; |
| } |
| if (flags & CONTEXT_FLOATING_POINT) |
| { |
| to->flags |= SERVER_CTX_FLOATING_POINT; |
| memcpy( to->fp.x86_64_regs.fpregs, &from->u.FltSave, sizeof(to->fp.x86_64_regs.fpregs) ); |
| } |
| if (flags & CONTEXT_DEBUG_REGISTERS) |
| { |
| to->flags |= SERVER_CTX_DEBUG_REGISTERS; |
| to->debug.x86_64_regs.dr0 = from->Dr0; |
| to->debug.x86_64_regs.dr1 = from->Dr1; |
| to->debug.x86_64_regs.dr2 = from->Dr2; |
| to->debug.x86_64_regs.dr3 = from->Dr3; |
| to->debug.x86_64_regs.dr6 = from->Dr6; |
| to->debug.x86_64_regs.dr7 = from->Dr7; |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| |
| /*********************************************************************** |
| * context_from_server |
| * |
| * Convert a register context from the server format. |
| */ |
| NTSTATUS context_from_server( CONTEXT *to, const context_t *from ) |
| { |
| if (from->cpu != CPU_x86_64) return STATUS_INVALID_PARAMETER; |
| |
| to->ContextFlags = CONTEXT_AMD64; |
| if (from->flags & SERVER_CTX_CONTROL) |
| { |
| to->ContextFlags |= CONTEXT_CONTROL; |
| to->Rbp = from->ctl.x86_64_regs.rbp; |
| to->Rip = from->ctl.x86_64_regs.rip; |
| to->Rsp = from->ctl.x86_64_regs.rsp; |
| to->SegCs = from->ctl.x86_64_regs.cs; |
| to->SegSs = from->ctl.x86_64_regs.ss; |
| to->EFlags = from->ctl.x86_64_regs.flags; |
| to->MxCsr = from->ctl.x86_64_regs.mxcsr; |
| } |
| |
| if (from->flags & SERVER_CTX_INTEGER) |
| { |
| to->ContextFlags |= CONTEXT_INTEGER; |
| to->Rax = from->integer.x86_64_regs.rax; |
| to->Rcx = from->integer.x86_64_regs.rcx; |
| to->Rdx = from->integer.x86_64_regs.rdx; |
| to->Rbx = from->integer.x86_64_regs.rbx; |
| to->Rsi = from->integer.x86_64_regs.rsi; |
| to->Rdi = from->integer.x86_64_regs.rdi; |
| to->R8 = from->integer.x86_64_regs.r8; |
| to->R9 = from->integer.x86_64_regs.r9; |
| to->R10 = from->integer.x86_64_regs.r10; |
| to->R11 = from->integer.x86_64_regs.r11; |
| to->R12 = from->integer.x86_64_regs.r12; |
| to->R13 = from->integer.x86_64_regs.r13; |
| to->R14 = from->integer.x86_64_regs.r14; |
| to->R15 = from->integer.x86_64_regs.r15; |
| } |
| if (from->flags & SERVER_CTX_SEGMENTS) |
| { |
| to->ContextFlags |= CONTEXT_SEGMENTS; |
| to->SegDs = from->seg.x86_64_regs.ds; |
| to->SegEs = from->seg.x86_64_regs.es; |
| to->SegFs = from->seg.x86_64_regs.fs; |
| to->SegGs = from->seg.x86_64_regs.gs; |
| } |
| if (from->flags & SERVER_CTX_FLOATING_POINT) |
| { |
| to->ContextFlags |= CONTEXT_FLOATING_POINT; |
| memcpy( &to->u.FltSave, from->fp.x86_64_regs.fpregs, sizeof(from->fp.x86_64_regs.fpregs) ); |
| } |
| if (from->flags & SERVER_CTX_DEBUG_REGISTERS) |
| { |
| to->ContextFlags |= CONTEXT_DEBUG_REGISTERS; |
| to->Dr0 = from->debug.x86_64_regs.dr0; |
| to->Dr1 = from->debug.x86_64_regs.dr1; |
| to->Dr2 = from->debug.x86_64_regs.dr2; |
| to->Dr3 = from->debug.x86_64_regs.dr3; |
| to->Dr6 = from->debug.x86_64_regs.dr6; |
| to->Dr7 = from->debug.x86_64_regs.dr7; |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| |
| /********************************************************************** |
| * find_function_info |
| */ |
| static RUNTIME_FUNCTION *find_function_info( ULONG64 pc, HMODULE module, |
| RUNTIME_FUNCTION *func, ULONG size ) |
| { |
| int min = 0; |
| int max = size/sizeof(*func) - 1; |
| |
| while (min <= max) |
| { |
| int pos = (min + max) / 2; |
| if ((char *)pc < (char *)module + func[pos].BeginAddress) max = pos - 1; |
| else if ((char *)pc >= (char *)module + func[pos].EndAddress) min = pos + 1; |
| else |
| { |
| func += pos; |
| while (func->UnwindData & 1) /* follow chained entry */ |
| func = (RUNTIME_FUNCTION *)((char *)module + (func->UnwindData & ~1)); |
| return func; |
| } |
| } |
| return NULL; |
| } |
| |
| /********************************************************************** |
| * call_stack_handlers |
| * |
| * Call the stack handlers chain. |
| */ |
| static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context ) |
| { |
| EXCEPTION_POINTERS ptrs; |
| UNWIND_HISTORY_TABLE table; |
| ULONG64 frame; |
| RUNTIME_FUNCTION *dir, *info; |
| PEXCEPTION_ROUTINE handler; |
| DISPATCHER_CONTEXT dispatch; |
| CONTEXT context, new_context; |
| LDR_MODULE *module; |
| DWORD size; |
| |
| context = *orig_context; |
| for (;;) |
| { |
| /* FIXME: should use the history table to make things faster */ |
| |
| if (LdrFindEntryForAddress( (void *)context.Rip, &module )) |
| { |
| ERR( "no module found for rip %p, can't dispatch exception\n", (void *)context.Rip ); |
| break; |
| } |
| if (!(dir = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE, |
| IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size ))) |
| { |
| ERR( "module %s doesn't contain exception data, can't dispatch exception\n", |
| debugstr_w(module->BaseDllName.Buffer) ); |
| break; |
| } |
| if (!(info = find_function_info( context.Rip, module->BaseAddress, dir, size ))) |
| { |
| /* leaf function */ |
| context.Rip = *(ULONG64 *)context.Rsp; |
| context.Rsp += sizeof(ULONG64); |
| continue; |
| } |
| |
| new_context = context; |
| |
| handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)module->BaseAddress, context.Rip, |
| info, &new_context, &dispatch.HandlerData, &frame, NULL ); |
| |
| if ((frame & 7) || |
| frame < (ULONG64)NtCurrentTeb()->Tib.StackLimit || |
| frame >= (ULONG64)NtCurrentTeb()->Tib.StackBase) |
| { |
| ERR( "invalid frame %lx\n", frame ); |
| rec->ExceptionFlags |= EH_STACK_INVALID; |
| break; |
| } |
| |
| if (handler) |
| { |
| dispatch.ControlPc = context.Rip; |
| dispatch.ImageBase = (ULONG64)module->BaseAddress; |
| dispatch.FunctionEntry = info; |
| dispatch.EstablisherFrame = frame; |
| dispatch.TargetIp = 0; /* FIXME */ |
| dispatch.ContextRecord = &context; |
| dispatch.LanguageHandler = handler; |
| dispatch.HistoryTable = &table; |
| dispatch.ScopeIndex = 0; /* FIXME */ |
| |
| TRACE( "calling handler %p (rec=%p, frame=%lx context=%p, dispatch=%p)\n", |
| handler, rec, frame, &context, &dispatch ); |
| |
| switch( handler( rec, frame, &context, &dispatch )) |
| { |
| case ExceptionContinueExecution: |
| if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION; |
| *orig_context = context; |
| return STATUS_SUCCESS; |
| case ExceptionContinueSearch: |
| break; |
| case ExceptionNestedException: |
| break; |
| default: |
| return STATUS_INVALID_DISPOSITION; |
| } |
| } |
| context = new_context; |
| } |
| |
| /* hack: call unhandled exception filter directly */ |
| ptrs.ExceptionRecord = rec; |
| ptrs.ContextRecord = orig_context; |
| unhandled_exception_filter( &ptrs ); |
| return STATUS_UNHANDLED_EXCEPTION; |
| } |
| |
| |
| /******************************************************************* |
| * raise_exception |
| * |
| * Implementation of NtRaiseException. |
| */ |
| static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) |
| { |
| NTSTATUS status; |
| |
| if (first_chance) |
| { |
| DWORD c; |
| |
| TRACE( "code=%x flags=%x addr=%p ip=%lx tid=%04x\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
| context->Rip, GetCurrentThreadId() ); |
| for (c = 0; c < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); c++) |
| TRACE( " info[%d]=%08lx\n", c, rec->ExceptionInformation[c] ); |
| if (rec->ExceptionCode == EXCEPTION_WINE_STUB) |
| { |
| if (rec->ExceptionInformation[1] >> 16) |
| MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n", |
| rec->ExceptionAddress, |
| (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] ); |
| else |
| MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n", |
| rec->ExceptionAddress, |
| (char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] ); |
| } |
| else |
| { |
| 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 ); |
| } |
| status = send_debug_event( rec, TRUE, context ); |
| if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED) |
| return STATUS_SUCCESS; |
| |
| if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION) |
| return STATUS_SUCCESS; |
| |
| if ((status = call_stack_handlers( rec, context )) != STATUS_UNHANDLED_EXCEPTION) |
| return status; |
| } |
| |
| /* last chance exception */ |
| |
| status = send_debug_event( rec, FALSE, context ); |
| if (status != DBG_CONTINUE) |
| { |
| if (rec->ExceptionFlags & EH_STACK_INVALID) |
| ERR("Exception frame is not in stack limits => unable to dispatch exception.\n"); |
| else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION) |
| ERR("Process attempted to continue execution after noncontinuable exception.\n"); |
| else |
| ERR("Unhandled exception code %x flags %x addr %p\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); |
| NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode ); |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| |
| /********************************************************************** |
| * segv_handler |
| * |
| * Handler for SIGSEGV and related errors. |
| */ |
| static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| ucontext_t *ucontext = sigcontext; |
| |
| save_context( &context, ucontext ); |
| |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionAddress = (LPVOID)context.Rip; |
| rec.NumberParameters = 0; |
| |
| switch(TRAP_sig(ucontext)) |
| { |
| case TRAP_x86_OFLOW: /* Overflow exception */ |
| rec.ExceptionCode = EXCEPTION_INT_OVERFLOW; |
| break; |
| case TRAP_x86_BOUND: /* Bound range exception */ |
| rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED; |
| break; |
| case TRAP_x86_PRIVINFLT: /* Invalid opcode exception */ |
| rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; |
| break; |
| case TRAP_x86_STKFLT: /* Stack fault */ |
| rec.ExceptionCode = EXCEPTION_STACK_OVERFLOW; |
| break; |
| case TRAP_x86_SEGNPFLT: /* Segment not present exception */ |
| case TRAP_x86_PROTFLT: /* General protection fault */ |
| case TRAP_x86_UNKNOWN: /* Unknown fault code */ |
| rec.ExceptionCode = ERROR_sig(ucontext) ? EXCEPTION_ACCESS_VIOLATION |
| : EXCEPTION_PRIV_INSTRUCTION; |
| break; |
| case TRAP_x86_PAGEFLT: /* Page fault */ |
| rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION; |
| rec.NumberParameters = 2; |
| rec.ExceptionInformation[0] = (ERROR_sig(ucontext) & 2) != 0; |
| rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr; |
| if (!(rec.ExceptionCode = virtual_handle_fault( siginfo->si_addr, rec.ExceptionInformation[0] ))) |
| goto done; |
| break; |
| case TRAP_x86_ALIGNFLT: /* Alignment check exception */ |
| rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT; |
| break; |
| default: |
| ERR( "Got unexpected trap %ld\n", TRAP_sig(ucontext) ); |
| /* fall through */ |
| case TRAP_x86_NMI: /* NMI interrupt */ |
| case TRAP_x86_DNA: /* Device not available exception */ |
| case TRAP_x86_DOUBLEFLT: /* Double fault exception */ |
| case TRAP_x86_TSSFLT: /* Invalid TSS exception */ |
| case TRAP_x86_MCHK: /* Machine check exception */ |
| case TRAP_x86_CACHEFLT: /* Cache flush exception */ |
| rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; |
| break; |
| } |
| |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| done: |
| restore_context( &context, ucontext ); |
| } |
| |
| /********************************************************************** |
| * trap_handler |
| * |
| * Handler for SIGTRAP. |
| */ |
| static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| ucontext_t *ucontext = sigcontext; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.Rip; |
| rec.NumberParameters = 0; |
| |
| switch (siginfo->si_code) |
| { |
| case TRAP_TRACE: /* Single-step exception */ |
| rec.ExceptionCode = EXCEPTION_SINGLE_STEP; |
| EFL_sig(ucontext) &= ~0x100; /* clear single-step flag */ |
| break; |
| case TRAP_BRKPT: /* Breakpoint exception */ |
| rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1; /* back up over the int3 instruction */ |
| /* fall through */ |
| default: |
| rec.ExceptionCode = EXCEPTION_BREAKPOINT; |
| break; |
| } |
| |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| /********************************************************************** |
| * fpe_handler |
| * |
| * Handler for SIGFPE. |
| */ |
| static void fpe_handler( int signal, siginfo_t *siginfo, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.Rip; |
| rec.NumberParameters = 0; |
| |
| switch (siginfo->si_code) |
| { |
| case FPE_FLTSUB: |
| rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED; |
| break; |
| case FPE_INTDIV: |
| rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO; |
| break; |
| case FPE_INTOVF: |
| rec.ExceptionCode = EXCEPTION_INT_OVERFLOW; |
| break; |
| case FPE_FLTDIV: |
| rec.ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO; |
| break; |
| case FPE_FLTOVF: |
| rec.ExceptionCode = EXCEPTION_FLT_OVERFLOW; |
| break; |
| case FPE_FLTUND: |
| rec.ExceptionCode = EXCEPTION_FLT_UNDERFLOW; |
| break; |
| case FPE_FLTRES: |
| rec.ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT; |
| break; |
| case FPE_FLTINV: |
| default: |
| rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION; |
| break; |
| } |
| |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| /********************************************************************** |
| * int_handler |
| * |
| * Handler for SIGINT. |
| */ |
| static void int_handler( int signal, siginfo_t *siginfo, void *ucontext ) |
| { |
| if (!dispatch_signal(SIGINT)) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionCode = CONTROL_C_EXIT; |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.Rip; |
| rec.NumberParameters = 0; |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * abrt_handler |
| * |
| * Handler for SIGABRT. |
| */ |
| static void abrt_handler( int signal, siginfo_t *siginfo, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionCode = EXCEPTION_WINE_ASSERTION; |
| rec.ExceptionFlags = EH_NONCONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.Rip; |
| rec.NumberParameters = 0; |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| |
| /********************************************************************** |
| * quit_handler |
| * |
| * Handler for SIGQUIT. |
| */ |
| static void quit_handler( int signal, siginfo_t *siginfo, void *ucontext ) |
| { |
| abort_thread(0); |
| } |
| |
| |
| /********************************************************************** |
| * usr1_handler |
| * |
| * Handler for SIGUSR1, used to signal a thread that it got suspended. |
| */ |
| static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext ) |
| { |
| CONTEXT context; |
| |
| save_context( &context, ucontext ); |
| wait_suspend( &context ); |
| restore_context( &context, ucontext ); |
| } |
| |
| |
| /********************************************************************** |
| * get_signal_stack_total_size |
| * |
| * Retrieve the size to allocate for the signal stack, including the TEB at the bottom. |
| * Must be a power of two. |
| */ |
| size_t get_signal_stack_total_size(void) |
| { |
| assert( sizeof(TEB) <= 2*getpagesize() ); |
| return 2*getpagesize(); /* this is just for the TEB, we don't need a signal stack */ |
| } |
| |
| |
| /*********************************************************************** |
| * __wine_set_signal_handler (NTDLL.@) |
| */ |
| int CDECL __wine_set_signal_handler(unsigned int sig, wine_signal_handler wsh) |
| { |
| if (sig > sizeof(handlers) / sizeof(handlers[0])) return -1; |
| if (handlers[sig] != NULL) return -2; |
| handlers[sig] = wsh; |
| return 0; |
| } |
| |
| |
| /********************************************************************** |
| * signal_init_thread |
| */ |
| void signal_init_thread( TEB *teb ) |
| { |
| #ifdef __linux__ |
| arch_prctl( ARCH_SET_GS, teb ); |
| #else |
| # error Please define setting %gs for your architecture |
| #endif |
| } |
| |
| /********************************************************************** |
| * signal_init_process |
| */ |
| void signal_init_process(void) |
| { |
| struct sigaction sig_act; |
| |
| sig_act.sa_mask = server_block_set; |
| sig_act.sa_flags = SA_RESTART | SA_SIGINFO; |
| |
| sig_act.sa_sigaction = int_handler; |
| if (sigaction( SIGINT, &sig_act, NULL ) == -1) goto error; |
| sig_act.sa_sigaction = fpe_handler; |
| if (sigaction( SIGFPE, &sig_act, NULL ) == -1) goto error; |
| sig_act.sa_sigaction = abrt_handler; |
| if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error; |
| sig_act.sa_sigaction = quit_handler; |
| if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error; |
| sig_act.sa_sigaction = usr1_handler; |
| if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error; |
| |
| sig_act.sa_sigaction = segv_handler; |
| if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error; |
| if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; |
| #ifdef SIGBUS |
| if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error; |
| #endif |
| |
| #ifdef SIGTRAP |
| sig_act.sa_sigaction = trap_handler; |
| if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error; |
| #endif |
| return; |
| |
| error: |
| perror("sigaction"); |
| exit(1); |
| } |
| |
| |
| /********************************************************************** |
| * RtlLookupFunctionEntry (NTDLL.@) |
| */ |
| PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG64 pc, ULONG64 *base, UNWIND_HISTORY_TABLE *table ) |
| { |
| LDR_MODULE *module; |
| RUNTIME_FUNCTION *func; |
| ULONG size; |
| |
| /* FIXME: should use the history table to make things faster */ |
| |
| if (LdrFindEntryForAddress( (void *)pc, &module )) |
| { |
| WARN( "module not found for %lx\n", pc ); |
| return NULL; |
| } |
| if (!(func = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE, |
| IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size ))) |
| { |
| WARN( "no exception table found in module %p pc %lx\n", module->BaseAddress, pc ); |
| return NULL; |
| } |
| func = find_function_info( pc, module->BaseAddress, func, size ); |
| if (func) *base = (ULONG64)module->BaseAddress; |
| return func; |
| } |
| |
| 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; |
| } |
| } |
| |
| static BOOL is_inside_epilog( BYTE *pc ) |
| { |
| /* add or lea must be the first instruction, and it must have a rex.W prefix */ |
| if ((pc[0] & 0xf8) == 0x48) |
| { |
| switch (pc[1]) |
| { |
| case 0x81: /* add $nnnn,%rsp */ |
| if (pc[0] == 0x48 && pc[2] == 0xc4) |
| { |
| pc += 7; |
| break; |
| } |
| return FALSE; |
| case 0x83: /* add $n,%rsp */ |
| if (pc[0] == 0x48 && pc[2] == 0xc4) |
| { |
| pc += 4; |
| break; |
| } |
| return FALSE; |
| case 0x8d: /* lea n(reg),%rsp */ |
| if (pc[0] & 0x06) return FALSE; /* rex.RX must be cleared */ |
| if (((pc[2] >> 3) & 7) != 4) return FALSE; /* dest reg mus be %rsp */ |
| if ((pc[2] & 7) == 4) return FALSE; /* no SIB byte allowed */ |
| if ((pc[2] >> 6) == 1) /* 8-bit offset */ |
| { |
| pc += 4; |
| break; |
| } |
| if ((pc[2] >> 6) == 2) /* 32-bit offset */ |
| { |
| pc += 7; |
| break; |
| } |
| return FALSE; |
| } |
| } |
| |
| /* now check for various pop instructions */ |
| |
| for (;;) |
| { |
| BYTE rex = 0; |
| |
| if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */ |
| |
| switch (*pc) |
| { |
| 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; |
| } |
| } |
| |
| /* execute a function epilog, which must have been validated with is_inside_epilog() */ |
| static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr ) |
| { |
| for (;;) |
| { |
| BYTE rex = 0; |
| |
| if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */ |
| |
| switch (*pc) |
| { |
| 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 */ |
| set_int_reg( context, ctx_ptr, *pc - 0x58 + (rex & 1) * 8, *(ULONG64 *)context->Rsp ); |
| context->Rsp += sizeof(ULONG64); |
| pc++; |
| continue; |
| case 0x81: /* add $nnnn,%rsp */ |
| context->Rsp += *(LONG *)(pc + 2); |
| pc += 2 + sizeof(LONG); |
| continue; |
| case 0x83: /* add $n,%rsp */ |
| context->Rsp += (signed char)pc[2]; |
| pc += 3; |
| continue; |
| case 0x8d: |
| if ((pc[1] >> 6) == 1) /* lea n(reg),%rsp */ |
| { |
| context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + (signed char)pc[2]; |
| pc += 3; |
| } |
| else /* lea nnnn(reg),%rsp */ |
| { |
| context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + *(LONG *)(pc + 2); |
| pc += 2 + sizeof(LONG); |
| } |
| continue; |
| case 0xc2: /* ret $nn */ |
| context->Rip = *(ULONG64 *)context->Rsp; |
| context->Rsp += sizeof(ULONG64) + *(WORD *)(pc + 1); |
| return; |
| case 0xc3: /* ret */ |
| context->Rip = *(ULONG64 *)context->Rsp; |
| context->Rsp += sizeof(ULONG64); |
| return; |
| /* FIXME: add various jump instructions */ |
| } |
| return; |
| } |
| } |
| |
| /********************************************************************** |
| * RtlVirtualUnwind (NTDLL.@) |
| */ |
| PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc, |
| RUNTIME_FUNCTION *function, CONTEXT *context, |
| PVOID *data, ULONG64 *frame_ret, |
| KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr ) |
| { |
| 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 ); |
| if (TRACE_ON(seh)) dump_unwind_info( base, function ); |
| |
| frame = *frame_ret = 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; |
| if (is_inside_epilog( (BYTE *)pc )) |
| { |
| interpret_epilog( (BYTE *)pc, context, ctx_ptr ); |
| *frame_ret = frame; |
| return NULL; |
| } |
| } |
| |
| 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_ret = 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); |
| |
| 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.@) |
| */ |
| void WINAPI RtlUnwindEx( ULONG64 end_frame, ULONG64 target_ip, EXCEPTION_RECORD *rec, |
| ULONG64 retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table ) |
| { |
| EXCEPTION_RECORD record; |
| ULONG64 frame; |
| RUNTIME_FUNCTION *dir, *info; |
| PEXCEPTION_ROUTINE handler; |
| DISPATCHER_CONTEXT dispatch; |
| CONTEXT new_context; |
| LDR_MODULE *module; |
| DWORD size; |
| |
| /* build an exception record, if we do not have one */ |
| if (!rec) |
| { |
| record.ExceptionCode = STATUS_UNWIND; |
| record.ExceptionFlags = 0; |
| record.ExceptionRecord = NULL; |
| record.ExceptionAddress = (void *)context->Rip; |
| record.NumberParameters = 0; |
| rec = &record; |
| } |
| |
| rec->ExceptionFlags |= EH_UNWINDING | (end_frame ? 0 : EH_EXIT_UNWIND); |
| |
| FIXME( "code=%x flags=%x end_frame=%lx target_ip=%lx\n", |
| rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip ); |
| |
| frame = context->Rsp; |
| while (frame != end_frame) |
| { |
| /* FIXME: should use the history table to make things faster */ |
| |
| if (LdrFindEntryForAddress( (void *)context->Rip, &module )) |
| { |
| ERR( "no module found for rip %p, can't unwind exception\n", (void *)context->Rip ); |
| raise_status( STATUS_BAD_FUNCTION_TABLE, rec ); |
| } |
| if (!(dir = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE, |
| IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size ))) |
| { |
| ERR( "module %s doesn't contain exception data, can't unwind exception\n", |
| debugstr_w(module->BaseDllName.Buffer) ); |
| raise_status( STATUS_BAD_FUNCTION_TABLE, rec ); |
| } |
| if (!(info = find_function_info( context->Rip, module->BaseAddress, dir, size ))) |
| { |
| /* leaf function */ |
| context->Rip = *(ULONG64 *)context->Rsp; |
| context->Rsp += sizeof(ULONG64); |
| continue; |
| } |
| |
| new_context = *context; |
| |
| handler = RtlVirtualUnwind( UNW_FLAG_UHANDLER, (ULONG64)module->BaseAddress, context->Rip, |
| info, &new_context, &dispatch.HandlerData, &frame, NULL ); |
| |
| if ((frame & 7) || |
| frame < (ULONG64)NtCurrentTeb()->Tib.StackLimit || |
| frame >= (ULONG64)NtCurrentTeb()->Tib.StackBase) |
| { |
| ERR( "invalid frame %lx\n", frame ); |
| raise_status( STATUS_BAD_STACK, rec ); |
| } |
| |
| if (end_frame && (frame > end_frame)) |
| { |
| ERR( "invalid frame %lx/%lx\n", frame, end_frame ); |
| raise_status( STATUS_INVALID_UNWIND_TARGET, rec ); |
| } |
| |
| if (handler) |
| { |
| dispatch.ControlPc = context->Rip; |
| dispatch.ImageBase = (ULONG64)module->BaseAddress; |
| dispatch.FunctionEntry = info; |
| dispatch.EstablisherFrame = frame; |
| dispatch.TargetIp = target_ip; |
| dispatch.ContextRecord = context; |
| dispatch.LanguageHandler = handler; |
| dispatch.HistoryTable = table; |
| dispatch.ScopeIndex = 0; /* FIXME */ |
| |
| TRACE( "calling handler %p (rec=%p, frame=%lx context=%p, dispatch=%p)\n", |
| handler, rec, frame, context, &dispatch ); |
| |
| switch( handler( rec, frame, context, &dispatch )) |
| { |
| case ExceptionContinueSearch: |
| break; |
| case ExceptionCollidedUnwind: |
| FIXME( "ExceptionCollidedUnwind not supported yet\n" ); |
| break; |
| default: |
| raise_status( STATUS_INVALID_DISPOSITION, rec ); |
| break; |
| } |
| } |
| *context = new_context; |
| } |
| context->Rax = retval; |
| context->Rip = target_ip; |
| TRACE( "returning to %lx stack %lx\n", context->Rip, context->Rsp ); |
| set_cpu_context( context ); |
| } |
| |
| |
| /******************************************************************* |
| * RtlUnwind (NTDLL.@) |
| */ |
| void WINAPI __regs_RtlUnwind( ULONG64 frame, ULONG64 target_ip, EXCEPTION_RECORD *rec, |
| ULONG64 retval, CONTEXT *context ) |
| { |
| RtlUnwindEx( frame, target_ip, rec, retval, context, NULL ); |
| } |
| DEFINE_REGS_ENTRYPOINT( RtlUnwind, 4 ) |
| |
| |
| /******************************************************************* |
| * __C_specific_handler (NTDLL.@) |
| */ |
| EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec, |
| ULONG64 frame, |
| CONTEXT *context, |
| struct _DISPATCHER_CONTEXT *dispatch ) |
| { |
| SCOPE_TABLE *table = dispatch->HandlerData; |
| ULONG i; |
| |
| TRACE( "%p %lx %p %p\n", rec, frame, context, dispatch ); |
| if (TRACE_ON(seh)) dump_scope_table( dispatch->ImageBase, table ); |
| |
| if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) /* FIXME */ |
| return ExceptionContinueSearch; |
| |
| for (i = 0; i < table->Count; i++) |
| { |
| if (context->Rip >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress && |
| context->Rip < dispatch->ImageBase + table->ScopeRecord[i].EndAddress) |
| { |
| if (!table->ScopeRecord[i].JumpTarget) continue; |
| if (table->ScopeRecord[i].HandlerAddress != EXCEPTION_EXECUTE_HANDLER) |
| { |
| EXCEPTION_POINTERS ptrs; |
| PC_LANGUAGE_EXCEPTION_HANDLER filter; |
| |
| filter = (PC_LANGUAGE_EXCEPTION_HANDLER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress); |
| ptrs.ExceptionRecord = rec; |
| ptrs.ContextRecord = context; |
| TRACE( "calling filter %p ptrs %p frame %lx\n", filter, &ptrs, frame ); |
| switch (filter( &ptrs, frame )) |
| { |
| case EXCEPTION_EXECUTE_HANDLER: |
| break; |
| case EXCEPTION_CONTINUE_SEARCH: |
| continue; |
| case EXCEPTION_CONTINUE_EXECUTION: |
| return ExceptionContinueExecution; |
| } |
| } |
| TRACE( "unwinding to target %lx\n", dispatch->ImageBase + table->ScopeRecord[i].JumpTarget ); |
| RtlUnwindEx( frame, dispatch->ImageBase + table->ScopeRecord[i].JumpTarget, |
| rec, 0, context, dispatch->HistoryTable ); |
| } |
| } |
| return ExceptionContinueSearch; |
| } |
| |
| |
| /******************************************************************* |
| * NtRaiseException (NTDLL.@) |
| */ |
| NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ) |
| { |
| NTSTATUS status = raise_exception( rec, context, first_chance ); |
| if (status == STATUS_SUCCESS) NtSetContextThread( GetCurrentThread(), context ); |
| return status; |
| } |
| |
| |
| /*********************************************************************** |
| * RtlRaiseException (NTDLL.@) |
| */ |
| void WINAPI __regs_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context ) |
| { |
| NTSTATUS status; |
| |
| rec->ExceptionAddress = (void *)context->Rip; |
| status = raise_exception( rec, context, TRUE ); |
| if (status != STATUS_SUCCESS) raise_status( status, rec ); |
| } |
| DEFINE_REGS_ENTRYPOINT( RtlRaiseException, 1 ) |
| |
| |
| /********************************************************************** |
| * __wine_enter_vm86 (NTDLL.@) |
| */ |
| void __wine_enter_vm86( CONTEXT *context ) |
| { |
| MESSAGE("vm86 mode not supported on this platform\n"); |
| } |
| |
| /********************************************************************** |
| * DbgBreakPoint (NTDLL.@) |
| */ |
| __ASM_GLOBAL_FUNC( DbgBreakPoint, "int $3; ret") |
| |
| /********************************************************************** |
| * DbgUserBreakPoint (NTDLL.@) |
| */ |
| __ASM_GLOBAL_FUNC( DbgUserBreakPoint, "int $3; ret") |
| |
| #endif /* __x86_64__ */ |