/* * 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 #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_SYS_SIGNAL_H # include #endif #define NONAMELESSUNION #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); /*********************************************************************** * signal context platform-specific definitions */ #ifdef linux typedef struct ucontext SIGCONTEXT; # define HANDLER_DEF(name) void name( int __signal, struct siginfo *__siginfo, SIGCONTEXT *__context ) # define HANDLER_CONTEXT (__context) #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)) #define FAULT_CODE (__siginfo->si_code) #define FAULT_ADDRESS (__siginfo->si_addr) #endif /* linux */ 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]; /*********************************************************************** * 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 SIGCONTEXT *sigcontext ) { 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); context->SegDs = 0; /* FIXME */ context->SegEs = 0; /* FIXME */ context->SegSs = 0; /* FIXME */ context->MxCsr = 0; /* FIXME */ if (FPU_sig(sigcontext)) context->u.FltSave = *FPU_sig(sigcontext); } /*********************************************************************** * restore_context * * Build a sigcontext from the register values. */ static void restore_context( const CONTEXT *context, SIGCONTEXT *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; if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->u.FltSave; } /*********************************************************************** * get_cpu_context * * Get the context of the current thread. */ void get_cpu_context( CONTEXT *context ) { FIXME("not implemented\n"); } /*********************************************************************** * set_cpu_context * * Set the new CPU context. */ void set_cpu_context( const CONTEXT *context ) { FIXME("not implemented\n"); } /********************************************************************** * segv_handler * * Handler for SIGSEGV and related errors. */ static HANDLER_DEF(segv_handler) { EXCEPTION_RECORD rec; CONTEXT context; save_context( &context, HANDLER_CONTEXT ); rec.ExceptionRecord = NULL; rec.ExceptionFlags = EXCEPTION_CONTINUABLE; rec.ExceptionAddress = (LPVOID)context.Rip; rec.NumberParameters = 0; switch(TRAP_sig(HANDLER_CONTEXT)) { 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(HANDLER_CONTEXT) ? EXCEPTION_ACCESS_VIOLATION : EXCEPTION_PRIV_INSTRUCTION; break; case TRAP_x86_PAGEFLT: /* Page fault */ rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION; #ifdef FAULT_ADDRESS rec.NumberParameters = 2; rec.ExceptionInformation[0] = (ERROR_sig(HANDLER_CONTEXT) & 2) != 0; rec.ExceptionInformation[1] = (ULONG_PTR)FAULT_ADDRESS; #endif break; case TRAP_x86_ALIGNFLT: /* Alignment check exception */ rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT; break; default: ERR( "Got unexpected trap %ld\n", TRAP_sig(HANDLER_CONTEXT) ); /* 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; } __regs_RtlRaiseException( &rec, &context ); restore_context( &context, HANDLER_CONTEXT ); } /********************************************************************** * trap_handler * * Handler for SIGTRAP. */ static HANDLER_DEF(trap_handler) { EXCEPTION_RECORD rec; CONTEXT context; save_context( &context, HANDLER_CONTEXT ); rec.ExceptionFlags = EXCEPTION_CONTINUABLE; rec.ExceptionRecord = NULL; rec.ExceptionAddress = (LPVOID)context.Rip; rec.NumberParameters = 0; switch(FAULT_CODE) { case TRAP_TRACE: /* Single-step exception */ rec.ExceptionCode = EXCEPTION_SINGLE_STEP; EFL_sig(HANDLER_CONTEXT) &= ~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; } __regs_RtlRaiseException( &rec, &context ); restore_context( &context, HANDLER_CONTEXT ); } /********************************************************************** * fpe_handler * * Handler for SIGFPE. */ static HANDLER_DEF(fpe_handler) { EXCEPTION_RECORD rec; CONTEXT context; save_context( &context, HANDLER_CONTEXT ); rec.ExceptionFlags = EXCEPTION_CONTINUABLE; rec.ExceptionRecord = NULL; rec.ExceptionAddress = (LPVOID)context.Rip; rec.NumberParameters = 0; switch (FAULT_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; } __regs_RtlRaiseException( &rec, &context ); restore_context( &context, HANDLER_CONTEXT ); } /********************************************************************** * int_handler * * Handler for SIGINT. */ static HANDLER_DEF(int_handler) { if (!dispatch_signal(SIGINT)) { EXCEPTION_RECORD rec; CONTEXT context; save_context( &context, HANDLER_CONTEXT ); rec.ExceptionCode = CONTROL_C_EXIT; rec.ExceptionFlags = EXCEPTION_CONTINUABLE; rec.ExceptionRecord = NULL; rec.ExceptionAddress = (LPVOID)context.Rip; rec.NumberParameters = 0; __regs_RtlRaiseException( &rec, &context ); restore_context( &context, HANDLER_CONTEXT ); } } /********************************************************************** * abrt_handler * * Handler for SIGABRT. */ static HANDLER_DEF(abrt_handler) { EXCEPTION_RECORD rec; CONTEXT context; save_context( &context, HANDLER_CONTEXT ); rec.ExceptionCode = EXCEPTION_WINE_ASSERTION; rec.ExceptionFlags = EH_NONCONTINUABLE; rec.ExceptionRecord = NULL; rec.ExceptionAddress = (LPVOID)context.Rip; rec.NumberParameters = 0; __regs_RtlRaiseException( &rec, &context ); /* Should never return.. */ restore_context( &context, HANDLER_CONTEXT ); } /********************************************************************** * quit_handler * * Handler for SIGQUIT. */ static HANDLER_DEF(quit_handler) { server_abort_thread(0); } /********************************************************************** * usr1_handler * * Handler for SIGUSR1, used to signal a thread that it got suspended. */ static HANDLER_DEF(usr1_handler) { CONTEXT context; save_context( &context, HANDLER_CONTEXT ); wait_suspend( &context ); restore_context( &context, HANDLER_CONTEXT ); } /********************************************************************** * 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 */ } /*********************************************************************** * set_handler * * Set a signal handler */ static int set_handler( int sig, void (*func)() ) { struct sigaction sig_act; sig_act.sa_sigaction = func; sig_act.sa_mask = server_block_set; sig_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; return sigaction( sig, &sig_act, NULL ); } /*********************************************************************** * __wine_set_signal_handler (NTDLL.@) */ int __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 */ BOOL SIGNAL_Init(void) { if (set_handler( SIGINT, (void (*)())int_handler ) == -1) goto error; if (set_handler( SIGFPE, (void (*)())fpe_handler ) == -1) goto error; if (set_handler( SIGSEGV, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGILL, (void (*)())segv_handler ) == -1) goto error; if (set_handler( SIGABRT, (void (*)())abrt_handler ) == -1) goto error; if (set_handler( SIGQUIT, (void (*)())quit_handler ) == -1) goto error; if (set_handler( SIGUSR1, (void (*)())usr1_handler ) == -1) goto error; #ifdef SIGBUS if (set_handler( SIGBUS, (void (*)())segv_handler ) == -1) goto error; #endif #ifdef SIGTRAP if (set_handler( SIGTRAP, (void (*)())trap_handler ) == -1) goto error; #endif return TRUE; error: perror("sigaction"); return FALSE; } /********************************************************************** * __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__ */