349 lines
11 KiB
C
349 lines
11 KiB
C
/*
|
|
* 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 PRTL_EXCEPTION_FILTER unhandled_exception_filter;
|
|
|
|
|
|
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;
|
|
DWORD flags = context->ContextFlags;
|
|
|
|
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 );
|
|
if (wine_server_reply_size( reply ))
|
|
{
|
|
context_from_server( context, &server_context );
|
|
context->ContextFlags |= flags; /* unchanged registers are still available */
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* RtlSetUnhandledExceptionFilter (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlSetUnhandledExceptionFilter( PRTL_EXCEPTION_FILTER filter )
|
|
{
|
|
unhandled_exception_filter = filter;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* call_unhandled_exception_filter
|
|
*/
|
|
LONG WINAPI call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr )
|
|
{
|
|
if (!unhandled_exception_filter) return EXCEPTION_CONTINUE_SEARCH;
|
|
return unhandled_exception_filter( eptr );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* __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 );
|
|
}
|