| /* |
| * Sparc signal handling routines |
| * |
| * Copyright 1999 Ulrich Weigand |
| * |
| * 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 __sparc__ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <assert.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <sys/ucontext.h> |
| |
| #include "ntstatus.h" |
| #define WIN32_NO_STATUS |
| #include "windef.h" |
| #include "winternl.h" |
| #include "winnt.h" |
| |
| #include "wine/exception.h" |
| #include "ntdll_misc.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(seh); |
| |
| static pthread_key_t teb_key; |
| |
| 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); |
| } |
| |
| |
| /* |
| * FIXME: All this works only on Solaris for now |
| */ |
| |
| /********************************************************************** |
| * save_context |
| */ |
| static void save_context( CONTEXT *context, ucontext_t *ucontext ) |
| { |
| context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; |
| |
| /* Special registers */ |
| context->psr = ucontext->uc_mcontext.gregs[REG_PSR]; |
| context->pc = ucontext->uc_mcontext.gregs[REG_PC]; |
| context->npc = ucontext->uc_mcontext.gregs[REG_nPC]; |
| context->y = ucontext->uc_mcontext.gregs[REG_Y]; |
| context->wim = 0; /* FIXME */ |
| context->tbr = 0; /* FIXME */ |
| |
| /* Global registers */ |
| context->g0 = 0; /* always */ |
| context->g1 = ucontext->uc_mcontext.gregs[REG_G1]; |
| context->g2 = ucontext->uc_mcontext.gregs[REG_G2]; |
| context->g3 = ucontext->uc_mcontext.gregs[REG_G3]; |
| context->g4 = ucontext->uc_mcontext.gregs[REG_G4]; |
| context->g5 = ucontext->uc_mcontext.gregs[REG_G5]; |
| context->g6 = ucontext->uc_mcontext.gregs[REG_G6]; |
| context->g7 = ucontext->uc_mcontext.gregs[REG_G7]; |
| |
| /* Current 'out' registers */ |
| context->o0 = ucontext->uc_mcontext.gregs[REG_O0]; |
| context->o1 = ucontext->uc_mcontext.gregs[REG_O1]; |
| context->o2 = ucontext->uc_mcontext.gregs[REG_O2]; |
| context->o3 = ucontext->uc_mcontext.gregs[REG_O3]; |
| context->o4 = ucontext->uc_mcontext.gregs[REG_O4]; |
| context->o5 = ucontext->uc_mcontext.gregs[REG_O5]; |
| context->o6 = ucontext->uc_mcontext.gregs[REG_O6]; |
| context->o7 = ucontext->uc_mcontext.gregs[REG_O7]; |
| |
| /* FIXME: what if the current register window isn't saved? */ |
| if ( ucontext->uc_mcontext.gwins && ucontext->uc_mcontext.gwins->wbcnt > 0 ) |
| { |
| /* Current 'local' registers from first register window */ |
| context->l0 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[0]; |
| context->l1 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[1]; |
| context->l2 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[2]; |
| context->l3 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[3]; |
| context->l4 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[4]; |
| context->l5 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[5]; |
| context->l6 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[6]; |
| context->l7 = ucontext->uc_mcontext.gwins->wbuf[0].rw_local[7]; |
| |
| /* Current 'in' registers from first register window */ |
| context->i0 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[0]; |
| context->i1 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[1]; |
| context->i2 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[2]; |
| context->i3 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[3]; |
| context->i4 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[4]; |
| context->i5 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[5]; |
| context->i6 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[6]; |
| context->i7 = ucontext->uc_mcontext.gwins->wbuf[0].rw_in[7]; |
| } |
| } |
| |
| /********************************************************************** |
| * restore_context |
| */ |
| static void restore_context( CONTEXT *context, ucontext_t *ucontext ) |
| { |
| /* FIXME */ |
| } |
| |
| /********************************************************************** |
| * save_fpu |
| */ |
| static void save_fpu( CONTEXT *context, ucontext_t *ucontext ) |
| { |
| /* FIXME */ |
| } |
| |
| /********************************************************************** |
| * restore_fpu |
| */ |
| static void restore_fpu( CONTEXT *context, ucontext_t *ucontext ) |
| { |
| /* FIXME */ |
| } |
| |
| |
| /********************************************************************** |
| * call_stack_handlers |
| * |
| * Call the stack handlers chain. |
| */ |
| static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *context ) |
| { |
| EXCEPTION_POINTERS ptrs; |
| |
| FIXME( "not implemented on Sparc\n" ); |
| |
| /* hack: call unhandled exception filter directly */ |
| ptrs.ExceptionRecord = rec; |
| ptrs.ContextRecord = 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=%x tid=%04x\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
| context->pc, GetCurrentThreadId() ); |
| for (c = 0; c < 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 |
| { |
| /* FIXME: dump context */ |
| } |
| |
| 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; |
| } |
| |
| |
| /*********************************************************************** |
| * RtlCaptureContext (NTDLL.@) |
| */ |
| void WINAPI RtlCaptureContext( CONTEXT *context ) |
| { |
| FIXME("not implemented\n"); |
| memset( context, 0, sizeof(*context) ); |
| } |
| |
| |
| /*********************************************************************** |
| * set_cpu_context |
| * |
| * Set the new CPU context. |
| */ |
| void set_cpu_context( const CONTEXT *context ) |
| { |
| FIXME("not implemented\n"); |
| } |
| |
| |
| /*********************************************************************** |
| * copy_context |
| * |
| * Copy a register context according to the flags. |
| */ |
| void copy_context( CONTEXT *to, const CONTEXT *from, DWORD flags ) |
| { |
| flags &= ~CONTEXT_SPARC; /* get rid of CPU id */ |
| if (flags & CONTEXT_CONTROL) |
| { |
| to->psr = from->psr; |
| to->pc = from->pc; |
| to->npc = from->npc; |
| to->y = from->y; |
| to->wim = from->wim; |
| to->tbr = from->tbr; |
| } |
| if (flags & CONTEXT_INTEGER) |
| { |
| to->g0 = from->g0; |
| to->g1 = from->g1; |
| to->g2 = from->g2; |
| to->g3 = from->g3; |
| to->g4 = from->g4; |
| to->g5 = from->g5; |
| to->g6 = from->g6; |
| to->g7 = from->g7; |
| to->o0 = from->o0; |
| to->o1 = from->o1; |
| to->o2 = from->o2; |
| to->o3 = from->o3; |
| to->o4 = from->o4; |
| to->o5 = from->o5; |
| to->o6 = from->o6; |
| to->o7 = from->o7; |
| to->l0 = from->l0; |
| to->l1 = from->l1; |
| to->l2 = from->l2; |
| to->l3 = from->l3; |
| to->l4 = from->l4; |
| to->l5 = from->l5; |
| to->l6 = from->l6; |
| to->l7 = from->l7; |
| to->i0 = from->i0; |
| to->i1 = from->i1; |
| to->i2 = from->i2; |
| to->i3 = from->i3; |
| to->i4 = from->i4; |
| to->i5 = from->i5; |
| to->i6 = from->i6; |
| to->i7 = from->i7; |
| } |
| if (flags & CONTEXT_FLOATING_POINT) |
| { |
| /* FIXME */ |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * 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_SPARC; /* get rid of CPU id */ |
| |
| memset( to, 0, sizeof(*to) ); |
| to->cpu = CPU_SPARC; |
| |
| if (flags & CONTEXT_CONTROL) |
| { |
| to->flags |= SERVER_CTX_CONTROL; |
| to->ctl.sparc_regs.psr = from->psr; |
| to->ctl.sparc_regs.pc = from->pc; |
| to->ctl.sparc_regs.npc = from->npc; |
| to->ctl.sparc_regs.y = from->y; |
| to->ctl.sparc_regs.wim = from->wim; |
| to->ctl.sparc_regs.tbr = from->tbr; |
| } |
| if (flags & CONTEXT_INTEGER) |
| { |
| to->flags |= SERVER_CTX_INTEGER; |
| to->integer.sparc_regs.g[0] = from->g0; |
| to->integer.sparc_regs.g[1] = from->g1; |
| to->integer.sparc_regs.g[2] = from->g2; |
| to->integer.sparc_regs.g[3] = from->g3; |
| to->integer.sparc_regs.g[4] = from->g4; |
| to->integer.sparc_regs.g[5] = from->g5; |
| to->integer.sparc_regs.g[6] = from->g6; |
| to->integer.sparc_regs.g[7] = from->g7; |
| to->integer.sparc_regs.o[0] = from->o0; |
| to->integer.sparc_regs.o[1] = from->o1; |
| to->integer.sparc_regs.o[2] = from->o2; |
| to->integer.sparc_regs.o[3] = from->o3; |
| to->integer.sparc_regs.o[4] = from->o4; |
| to->integer.sparc_regs.o[5] = from->o5; |
| to->integer.sparc_regs.o[6] = from->o6; |
| to->integer.sparc_regs.o[7] = from->o7; |
| to->integer.sparc_regs.l[0] = from->l0; |
| to->integer.sparc_regs.l[1] = from->l1; |
| to->integer.sparc_regs.l[2] = from->l2; |
| to->integer.sparc_regs.l[3] = from->l3; |
| to->integer.sparc_regs.l[4] = from->l4; |
| to->integer.sparc_regs.l[5] = from->l5; |
| to->integer.sparc_regs.l[6] = from->l6; |
| to->integer.sparc_regs.l[7] = from->l7; |
| to->integer.sparc_regs.i[0] = from->i0; |
| to->integer.sparc_regs.i[1] = from->i1; |
| to->integer.sparc_regs.i[2] = from->i2; |
| to->integer.sparc_regs.i[3] = from->i3; |
| to->integer.sparc_regs.i[4] = from->i4; |
| to->integer.sparc_regs.i[5] = from->i5; |
| to->integer.sparc_regs.i[6] = from->i6; |
| to->integer.sparc_regs.i[7] = from->i7; |
| } |
| if (flags & CONTEXT_FLOATING_POINT) |
| { |
| /* FIXME */ |
| } |
| 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_SPARC) return STATUS_INVALID_PARAMETER; |
| |
| to->ContextFlags = CONTEXT_SPARC; |
| if (from->flags & SERVER_CTX_CONTROL) |
| { |
| to->ContextFlags |= CONTEXT_CONTROL; |
| to->psr = from->ctl.sparc_regs.psr; |
| to->pc = from->ctl.sparc_regs.pc; |
| to->npc = from->ctl.sparc_regs.npc; |
| to->y = from->ctl.sparc_regs.y; |
| to->wim = from->ctl.sparc_regs.wim; |
| to->tbr = from->ctl.sparc_regs.tbr; |
| } |
| if (from->flags & SERVER_CTX_INTEGER) |
| { |
| to->ContextFlags |= CONTEXT_INTEGER; |
| to->g0 = from->integer.sparc_regs.g[0]; |
| to->g1 = from->integer.sparc_regs.g[1]; |
| to->g2 = from->integer.sparc_regs.g[2]; |
| to->g3 = from->integer.sparc_regs.g[3]; |
| to->g4 = from->integer.sparc_regs.g[4]; |
| to->g5 = from->integer.sparc_regs.g[5]; |
| to->g6 = from->integer.sparc_regs.g[6]; |
| to->g7 = from->integer.sparc_regs.g[7]; |
| to->o0 = from->integer.sparc_regs.o[0]; |
| to->o1 = from->integer.sparc_regs.o[1]; |
| to->o2 = from->integer.sparc_regs.o[2]; |
| to->o3 = from->integer.sparc_regs.o[3]; |
| to->o4 = from->integer.sparc_regs.o[4]; |
| to->o5 = from->integer.sparc_regs.o[5]; |
| to->o6 = from->integer.sparc_regs.o[6]; |
| to->o7 = from->integer.sparc_regs.o[7]; |
| to->l0 = from->integer.sparc_regs.l[0]; |
| to->l1 = from->integer.sparc_regs.l[1]; |
| to->l2 = from->integer.sparc_regs.l[2]; |
| to->l3 = from->integer.sparc_regs.l[3]; |
| to->l4 = from->integer.sparc_regs.l[4]; |
| to->l5 = from->integer.sparc_regs.l[5]; |
| to->l6 = from->integer.sparc_regs.l[6]; |
| to->l7 = from->integer.sparc_regs.l[7]; |
| to->i0 = from->integer.sparc_regs.i[0]; |
| to->i1 = from->integer.sparc_regs.i[1]; |
| to->i2 = from->integer.sparc_regs.i[2]; |
| to->i3 = from->integer.sparc_regs.i[3]; |
| to->i4 = from->integer.sparc_regs.i[4]; |
| to->i5 = from->integer.sparc_regs.i[5]; |
| to->i6 = from->integer.sparc_regs.i[6]; |
| to->i7 = from->integer.sparc_regs.i[7]; |
| } |
| if (from->flags & SERVER_CTX_FLOATING_POINT) |
| { |
| /* FIXME */ |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| |
| /********************************************************************** |
| * segv_handler |
| * |
| * Handler for SIGSEGV. |
| */ |
| static void segv_handler( int signal, siginfo_t *info, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION; |
| |
| /* we want the page-fault case to be fast */ |
| if ( info->si_code == SEGV_ACCERR ) |
| if (!(rec.ExceptionCode = virtual_handle_fault( info->si_addr, 0 ))) return; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionAddress = (LPVOID)context.pc; |
| rec.NumberParameters = 2; |
| rec.ExceptionInformation[0] = 0; /* FIXME: read/write access ? */ |
| rec.ExceptionInformation[1] = (ULONG_PTR)info->si_addr; |
| |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| /********************************************************************** |
| * bus_handler |
| * |
| * Handler for SIGBUS. |
| */ |
| static void bus_handler( int signal, siginfo_t *info, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionAddress = (LPVOID)context.pc; |
| rec.NumberParameters = 0; |
| |
| if ( info->si_code == BUS_ADRALN ) |
| rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT; |
| else |
| rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION; |
| |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| /********************************************************************** |
| * ill_handler |
| * |
| * Handler for SIGILL. |
| */ |
| static void ill_handler( int signal, siginfo_t *info, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| switch ( info->si_code ) |
| { |
| default: |
| case ILL_ILLOPC: |
| case ILL_ILLOPN: |
| case ILL_ILLADR: |
| case ILL_ILLTRP: |
| rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; |
| break; |
| |
| case ILL_PRVOPC: |
| case ILL_PRVREG: |
| rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION; |
| break; |
| |
| case ILL_BADSTK: |
| rec.ExceptionCode = EXCEPTION_STACK_OVERFLOW; |
| break; |
| } |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionAddress = (LPVOID)context.pc; |
| rec.NumberParameters = 0; |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| } |
| |
| |
| /********************************************************************** |
| * trap_handler |
| * |
| * Handler for SIGTRAP. |
| */ |
| static void trap_handler( int signal, siginfo_t *info, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| switch ( info->si_code ) |
| { |
| case TRAP_TRACE: |
| rec.ExceptionCode = EXCEPTION_SINGLE_STEP; |
| break; |
| case TRAP_BRKPT: |
| default: |
| rec.ExceptionCode = EXCEPTION_BREAKPOINT; |
| break; |
| } |
| |
| save_context( &context, ucontext ); |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.pc; |
| rec.NumberParameters = 0; |
| 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 *info, void *ucontext ) |
| { |
| EXCEPTION_RECORD rec; |
| CONTEXT context; |
| NTSTATUS status; |
| |
| switch ( info->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; |
| } |
| |
| save_context( &context, ucontext ); |
| save_fpu( &context, ucontext ); |
| rec.ExceptionFlags = EXCEPTION_CONTINUABLE; |
| rec.ExceptionRecord = NULL; |
| rec.ExceptionAddress = (LPVOID)context.pc; |
| rec.NumberParameters = 0; |
| status = raise_exception( &rec, &context, TRUE ); |
| if (status) raise_status( status, &rec ); |
| restore_context( &context, ucontext ); |
| restore_fpu( &context, ucontext ); |
| } |
| |
| |
| /********************************************************************** |
| * int_handler |
| * |
| * Handler for SIGINT. |
| */ |
| static void int_handler( int signal, siginfo_t *info, 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.pc; |
| 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, struct siginfo *info, 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.pc; |
| 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, struct siginfo *info, 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, struct siginfo *info, void *ucontext ) |
| { |
| CONTEXT context; |
| |
| save_context( &context, ucontext ); |
| wait_suspend( &context ); |
| restore_context( &context, ucontext ); |
| } |
| |
| |
| /*********************************************************************** |
| * __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_alloc_thread |
| */ |
| NTSTATUS signal_alloc_thread( TEB **teb ) |
| { |
| static size_t sigstack_zero_bits; |
| SIZE_T size; |
| NTSTATUS status; |
| |
| if (!sigstack_zero_bits) |
| { |
| size_t min_size = getpagesize(); /* this is just for the TEB, we don't use a signal stack yet */ |
| /* find the first power of two not smaller than min_size */ |
| while ((1u << sigstack_zero_bits) < min_size) sigstack_zero_bits++; |
| assert( sizeof(TEB) <= min_size ); |
| } |
| |
| size = 1 << sigstack_zero_bits; |
| *teb = NULL; |
| if (!(status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)teb, sigstack_zero_bits, |
| &size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE ))) |
| { |
| (*teb)->Tib.Self = &(*teb)->Tib; |
| (*teb)->Tib.ExceptionList = (void *)~0UL; |
| } |
| return status; |
| } |
| |
| |
| /********************************************************************** |
| * signal_free_thread |
| */ |
| void signal_free_thread( TEB *teb ) |
| { |
| SIZE_T size; |
| |
| if (teb->DeallocationStack) |
| { |
| size = 0; |
| NtFreeVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size, MEM_RELEASE ); |
| } |
| size = 0; |
| NtFreeVirtualMemory( NtCurrentProcess(), (void **)&teb, &size, MEM_RELEASE ); |
| } |
| |
| |
| /********************************************************************** |
| * signal_init_thread |
| */ |
| void signal_init_thread( TEB *teb ) |
| { |
| static int init_done; |
| |
| if (!init_done) |
| { |
| pthread_key_create( &teb_key, NULL ); |
| init_done = 1; |
| } |
| pthread_setspecific( teb_key, teb ); |
| } |
| |
| |
| /********************************************************************** |
| * 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; |
| sig_act.sa_sigaction = ill_handler; |
| if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error; |
| #ifdef SIGBUS |
| sig_act.sa_sigaction = bus_handler; |
| 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 |
| |
| #ifdef __sun__ |
| /* 'ta 6' tells the kernel to synthesize any unaligned accesses this |
| process makes, instead of just signalling an error and terminating |
| the process. wine-devel did not reach a conclusion on whether |
| this is correct, because that is what x86 does, or it is harmful |
| because it could obscure problems in user code */ |
| __asm__("ta 6"); /* 6 == ST_FIX_ALIGN defined in sys/trap.h */ |
| #endif |
| |
| return; |
| error: |
| perror("sigaction"); |
| exit(1); |
| } |
| |
| |
| /********************************************************************** |
| * __wine_enter_vm86 |
| */ |
| void __wine_enter_vm86( CONTEXT *context ) |
| { |
| MESSAGE("vm86 mode not supported on this platform\n"); |
| } |
| |
| /*********************************************************************** |
| * RtlUnwind (NTDLL.@) |
| */ |
| void WINAPI RtlUnwind( PVOID pEndFrame, PVOID targetIp, PEXCEPTION_RECORD pRecord, PVOID retval ) |
| { |
| FIXME( "Not implemented on Sparc\n" ); |
| } |
| |
| /******************************************************************* |
| * 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 RtlRaiseException( EXCEPTION_RECORD *rec ) |
| { |
| CONTEXT context; |
| NTSTATUS status; |
| |
| RtlCaptureContext( &context ); |
| rec->ExceptionAddress = (void *)context.pc; |
| status = raise_exception( rec, &context, TRUE ); |
| if (status) raise_status( status, rec ); |
| } |
| |
| /************************************************************************* |
| * RtlCaptureStackBackTrace (NTDLL.@) |
| */ |
| USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, ULONG *hash ) |
| { |
| FIXME( "(%d, %d, %p, %p) stub!\n", skip, count, buffer, hash ); |
| return 0; |
| } |
| |
| /*********************************************************************** |
| * call_thread_entry_point |
| */ |
| void call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg ) |
| { |
| __TRY |
| { |
| exit_thread( entry( arg )); |
| } |
| __EXCEPT(unhandled_exception_filter) |
| { |
| NtTerminateThread( GetCurrentThread(), GetExceptionCode() ); |
| } |
| __ENDTRY |
| abort(); /* should not be reached */ |
| } |
| |
| /*********************************************************************** |
| * RtlExitUserThread (NTDLL.@) |
| */ |
| void WINAPI RtlExitUserThread( ULONG status ) |
| { |
| exit_thread( status ); |
| } |
| |
| /*********************************************************************** |
| * abort_thread |
| */ |
| void abort_thread( int status ) |
| { |
| terminate_thread( status ); |
| } |
| |
| /********************************************************************** |
| * DbgBreakPoint (NTDLL.@) |
| */ |
| void WINAPI DbgBreakPoint(void) |
| { |
| kill(getpid(), SIGTRAP); |
| } |
| |
| /********************************************************************** |
| * DbgUserBreakPoint (NTDLL.@) |
| */ |
| void WINAPI DbgUserBreakPoint(void) |
| { |
| kill(getpid(), SIGTRAP); |
| } |
| |
| /********************************************************************** |
| * NtCurrentTeb (NTDLL.@) |
| */ |
| TEB * WINAPI NtCurrentTeb(void) |
| { |
| return pthread_getspecific( teb_key ); |
| } |
| |
| #endif /* __sparc__ */ |