| /* |
| * 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 ); |
| } |