| /* | |
| * NT exception handling routines | |
| * | |
| * Copyright 1999 Turchanov Sergey | |
| * Copyright 1999 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 | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <signal.h> | |
| #include <stdarg.h> | |
| #include "ntstatus.h" | |
| #define WIN32_NO_STATUS | |
| #include "windef.h" | |
| #include "winternl.h" | |
| #include "wine/exception.h" | |
| #include "wine/server.h" | |
| #include "wine/list.h" | |
| #include "wine/debug.h" | |
| #include "excpt.h" | |
| #include "ntdll_misc.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(seh); | |
| typedef struct | |
| { | |
| struct list entry; | |
| PVECTORED_EXCEPTION_HANDLER func; | |
| ULONG count; | |
| } VECTORED_HANDLER; | |
| static struct list vectored_exception_handlers = LIST_INIT(vectored_exception_handlers); | |
| static struct list vectored_continue_handlers = LIST_INIT(vectored_continue_handlers); | |
| static RTL_CRITICAL_SECTION vectored_handlers_section; | |
| static RTL_CRITICAL_SECTION_DEBUG critsect_debug = | |
| { | |
| 0, 0, &vectored_handlers_section, | |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, | |
| 0, 0, { (DWORD_PTR)(__FILE__ ": vectored_handlers_section") } | |
| }; | |
| static RTL_CRITICAL_SECTION vectored_handlers_section = { &critsect_debug, -1, 0, 0, 0, 0 }; | |
| static VECTORED_HANDLER *add_vectored_handler( struct list *handler_list, ULONG first, | |
| PVECTORED_EXCEPTION_HANDLER func ) | |
| { | |
| VECTORED_HANDLER *handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) ); | |
| if (handler) | |
| { | |
| handler->func = RtlEncodePointer( func ); | |
| handler->count = 1; | |
| RtlEnterCriticalSection( &vectored_handlers_section ); | |
| if (first) list_add_head( handler_list, &handler->entry ); | |
| else list_add_tail( handler_list, &handler->entry ); | |
| RtlLeaveCriticalSection( &vectored_handlers_section ); | |
| } | |
| return handler; | |
| } | |
| static ULONG remove_vectored_handler( struct list *handler_list, VECTORED_HANDLER *handler ) | |
| { | |
| struct list *ptr; | |
| ULONG ret = FALSE; | |
| RtlEnterCriticalSection( &vectored_handlers_section ); | |
| LIST_FOR_EACH( ptr, handler_list ) | |
| { | |
| VECTORED_HANDLER *curr_handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry ); | |
| if (curr_handler == handler) | |
| { | |
| if (!--curr_handler->count) list_remove( ptr ); | |
| else handler = NULL; /* don't free it yet */ | |
| ret = TRUE; | |
| break; | |
| } | |
| } | |
| RtlLeaveCriticalSection( &vectored_handlers_section ); | |
| if (ret) RtlFreeHeap( GetProcessHeap(), 0, handler ); | |
| return ret; | |
| } | |
| /********************************************************************** | |
| * wait_suspend | |
| * | |
| * Wait until the thread is no longer suspended. | |
| */ | |
| void wait_suspend( CONTEXT *context ) | |
| { | |
| LARGE_INTEGER timeout; | |
| int saved_errno = errno; | |
| context_t server_context; | |
| context_to_server( &server_context, context ); | |
| /* store the context we got at suspend time */ | |
| SERVER_START_REQ( set_suspend_context ) | |
| { | |
| wine_server_add_data( req, &server_context, sizeof(server_context) ); | |
| wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| /* wait with 0 timeout, will only return once the thread is no longer suspended */ | |
| timeout.QuadPart = 0; | |
| server_select( NULL, 0, SELECT_INTERRUPTIBLE, &timeout ); | |
| /* retrieve the new context */ | |
| SERVER_START_REQ( get_suspend_context ) | |
| { | |
| wine_server_set_reply( req, &server_context, sizeof(server_context) ); | |
| wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| context_from_server( context, &server_context ); | |
| errno = saved_errno; | |
| } | |
| /********************************************************************** | |
| * send_debug_event | |
| * | |
| * Send an EXCEPTION_DEBUG_EVENT event to the debugger. | |
| */ | |
| NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context ) | |
| { | |
| NTSTATUS ret; | |
| DWORD i; | |
| obj_handle_t handle = 0; | |
| client_ptr_t params[EXCEPTION_MAXIMUM_PARAMETERS]; | |
| context_t server_context; | |
| select_op_t select_op; | |
| if (!NtCurrentTeb()->Peb->BeingDebugged) return 0; /* no debugger present */ | |
| for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++) | |
| params[i] = rec->ExceptionInformation[i]; | |
| context_to_server( &server_context, context ); | |
| SERVER_START_REQ( queue_exception_event ) | |
| { | |
| req->first = first_chance; | |
| req->code = rec->ExceptionCode; | |
| req->flags = rec->ExceptionFlags; | |
| req->record = wine_server_client_ptr( rec->ExceptionRecord ); | |
| req->address = wine_server_client_ptr( rec->ExceptionAddress ); | |
| req->len = i * sizeof(params[0]); | |
| wine_server_add_data( req, params, req->len ); | |
| wine_server_add_data( req, &server_context, sizeof(server_context) ); | |
| if (!wine_server_call( req )) handle = reply->handle; | |
| } | |
| SERVER_END_REQ; | |
| if (!handle) return 0; | |
| select_op.wait.op = SELECT_WAIT; | |
| select_op.wait.handles[0] = handle; | |
| server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, NULL ); | |
| SERVER_START_REQ( get_exception_status ) | |
| { | |
| req->handle = handle; | |
| wine_server_set_reply( req, &server_context, sizeof(server_context) ); | |
| ret = wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| if (ret >= 0) context_from_server( context, &server_context ); | |
| return ret; | |
| } | |
| /********************************************************************** | |
| * call_vectored_handlers | |
| * | |
| * Call the vectored handlers chain. | |
| */ | |
| LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context ) | |
| { | |
| struct list *ptr; | |
| LONG ret = EXCEPTION_CONTINUE_SEARCH; | |
| EXCEPTION_POINTERS except_ptrs; | |
| PVECTORED_EXCEPTION_HANDLER func; | |
| VECTORED_HANDLER *handler, *to_free = NULL; | |
| except_ptrs.ExceptionRecord = rec; | |
| except_ptrs.ContextRecord = context; | |
| RtlEnterCriticalSection( &vectored_handlers_section ); | |
| ptr = list_head( &vectored_exception_handlers ); | |
| while (ptr) | |
| { | |
| handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry ); | |
| handler->count++; | |
| func = RtlDecodePointer( handler->func ); | |
| RtlLeaveCriticalSection( &vectored_handlers_section ); | |
| RtlFreeHeap( GetProcessHeap(), 0, to_free ); | |
| to_free = NULL; | |
| TRACE( "calling handler at %p code=%x flags=%x\n", | |
| func, rec->ExceptionCode, rec->ExceptionFlags ); | |
| ret = func( &except_ptrs ); | |
| TRACE( "handler at %p returned %x\n", func, ret ); | |
| RtlEnterCriticalSection( &vectored_handlers_section ); | |
| ptr = list_next( &vectored_exception_handlers, ptr ); | |
| if (!--handler->count) /* removed during execution */ | |
| { | |
| list_remove( &handler->entry ); | |
| to_free = handler; | |
| } | |
| if (ret == EXCEPTION_CONTINUE_EXECUTION) break; | |
| } | |
| RtlLeaveCriticalSection( &vectored_handlers_section ); | |
| RtlFreeHeap( GetProcessHeap(), 0, to_free ); | |
| return ret; | |
| } | |
| /******************************************************************* | |
| * raise_status | |
| * | |
| * Implementation of RtlRaiseStatus with a specific exception record. | |
| */ | |
| void raise_status( NTSTATUS status, EXCEPTION_RECORD *rec ) | |
| { | |
| EXCEPTION_RECORD ExceptionRec; | |
| ExceptionRec.ExceptionCode = status; | |
| ExceptionRec.ExceptionFlags = EH_NONCONTINUABLE; | |
| ExceptionRec.ExceptionRecord = rec; | |
| ExceptionRec.NumberParameters = 0; | |
| for (;;) RtlRaiseException( &ExceptionRec ); /* never returns */ | |
| } | |
| /*********************************************************************** | |
| * RtlRaiseStatus (NTDLL.@) | |
| * | |
| * Raise an exception with ExceptionCode = status | |
| */ | |
| void WINAPI RtlRaiseStatus( NTSTATUS status ) | |
| { | |
| raise_status( status, NULL ); | |
| } | |
| /******************************************************************* | |
| * RtlAddVectoredContinueHandler (NTDLL.@) | |
| */ | |
| PVOID WINAPI RtlAddVectoredContinueHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func ) | |
| { | |
| return add_vectored_handler( &vectored_continue_handlers, first, func ); | |
| } | |
| /******************************************************************* | |
| * RtlRemoveVectoredContinueHandler (NTDLL.@) | |
| */ | |
| ULONG WINAPI RtlRemoveVectoredContinueHandler( PVOID handler ) | |
| { | |
| return remove_vectored_handler( &vectored_continue_handlers, handler ); | |
| } | |
| /******************************************************************* | |
| * RtlAddVectoredExceptionHandler (NTDLL.@) | |
| */ | |
| PVOID WINAPI DECLSPEC_HOTPATCH RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func ) | |
| { | |
| return add_vectored_handler( &vectored_exception_handlers, first, func ); | |
| } | |
| /******************************************************************* | |
| * RtlRemoveVectoredExceptionHandler (NTDLL.@) | |
| */ | |
| ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler ) | |
| { | |
| return remove_vectored_handler( &vectored_exception_handlers, handler ); | |
| } | |
| /************************************************************* | |
| * __wine_spec_unimplemented_stub | |
| * | |
| * ntdll-specific implementation to avoid depending on kernel functions. | |
| * Can be removed once ntdll.spec no longer contains stubs. | |
| */ | |
| void __wine_spec_unimplemented_stub( const char *module, const char *function ) | |
| { | |
| EXCEPTION_RECORD record; | |
| record.ExceptionCode = EXCEPTION_WINE_STUB; | |
| record.ExceptionFlags = EH_NONCONTINUABLE; | |
| record.ExceptionRecord = NULL; | |
| record.ExceptionAddress = __wine_spec_unimplemented_stub; | |
| record.NumberParameters = 2; | |
| record.ExceptionInformation[0] = (ULONG_PTR)module; | |
| record.ExceptionInformation[1] = (ULONG_PTR)function; | |
| for (;;) RtlRaiseException( &record ); | |
| } |