exception.c 10.2 KB
Newer Older
1 2
/*
 * NT exception handling routines
3
 *
4 5
 * Copyright 1999 Turchanov Sergey
 * Copyright 1999 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */

22
#include "config.h"
23
#include "wine/port.h"
24

25
#include <assert.h>
26
#include <errno.h>
27
#include <signal.h>
28
#include <stdarg.h>
29

30
#include "ntstatus.h"
31
#define WIN32_NO_STATUS
32
#include "windef.h"
33
#include "winternl.h"
34
#include "wine/exception.h"
35
#include "wine/server.h"
36
#include "wine/list.h"
37
#include "wine/debug.h"
38
#include "excpt.h"
39
#include "ntdll_misc.h"
40

41
WINE_DEFAULT_DEBUG_CHANNEL(seh);
42

43 44 45 46
typedef struct
{
    struct list                 entry;
    PVECTORED_EXCEPTION_HANDLER func;
47
    ULONG                       count;
48 49
} VECTORED_HANDLER;

50 51
static struct list vectored_exception_handlers = LIST_INIT(vectored_exception_handlers);
static struct list vectored_continue_handlers  = LIST_INIT(vectored_continue_handlers);
52

53 54
static RTL_CRITICAL_SECTION vectored_handlers_section;
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
55
{
56 57 58 59 60
    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 };
61

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

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;
}


103 104 105 106 107 108 109 110
/**********************************************************************
 *           wait_suspend
 *
 * Wait until the thread is no longer suspended.
 */
void wait_suspend( CONTEXT *context )
{
    LARGE_INTEGER timeout;
111
    int saved_errno = errno;
112 113 114
    context_t server_context;

    context_to_server( &server_context, context );
115 116

    /* store the context we got at suspend time */
117
    SERVER_START_REQ( set_suspend_context )
118
    {
119
        wine_server_add_data( req, &server_context, sizeof(server_context) );
120 121 122 123 124 125
        wine_server_call( req );
    }
    SERVER_END_REQ;

    /* wait with 0 timeout, will only return once the thread is no longer suspended */
    timeout.QuadPart = 0;
126
    server_select( NULL, 0, SELECT_INTERRUPTIBLE, &timeout );
127 128

    /* retrieve the new context */
129
    SERVER_START_REQ( get_suspend_context )
130
    {
131
        wine_server_set_reply( req, &server_context, sizeof(server_context) );
132 133 134
        wine_server_call( req );
    }
    SERVER_END_REQ;
135

136
    context_from_server( context, &server_context );
137
    errno = saved_errno;
138 139 140
}


141
/**********************************************************************
142
 *           send_debug_event
143 144 145
 *
 * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
 */
146
NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
147
{
148
    NTSTATUS ret;
149
    DWORD i;
150
    obj_handle_t handle = 0;
151
    client_ptr_t params[EXCEPTION_MAXIMUM_PARAMETERS];
152
    context_t server_context;
153
    select_op_t select_op;
154

155 156
    if (!NtCurrentTeb()->Peb->BeingDebugged) return 0;  /* no debugger present */

157 158 159
    for (i = 0; i < min( rec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS ); i++)
        params[i] = rec->ExceptionInformation[i];

160 161
    context_to_server( &server_context, context );

162
    SERVER_START_REQ( queue_exception_event )
163 164
    {
        req->first   = first_chance;
165 166 167 168 169 170
        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 );
171
        wine_server_add_data( req, &server_context, sizeof(server_context) );
172
        if (!wine_server_call( req )) handle = reply->handle;
173
    }
174
    SERVER_END_REQ;
175
    if (!handle) return 0;
176

177 178
    select_op.wait.op = SELECT_WAIT;
    select_op.wait.handles[0] = handle;
179
    server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, NULL );
180

181
    SERVER_START_REQ( get_exception_status )
182
    {
183
        req->handle = handle;
184
        wine_server_set_reply( req, &server_context, sizeof(server_context) );
185
        ret = wine_server_call( req );
186
    }
187
    SERVER_END_REQ;
188
    if (ret >= 0) context_from_server( context, &server_context );
189
    return ret;
190 191 192
}


193 194 195 196 197
/**********************************************************************
 *           call_vectored_handlers
 *
 * Call the vectored handlers chain.
 */
198
LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
199 200 201 202
{
    struct list *ptr;
    LONG ret = EXCEPTION_CONTINUE_SEARCH;
    EXCEPTION_POINTERS except_ptrs;
203
    PVECTORED_EXCEPTION_HANDLER func;
204
    VECTORED_HANDLER *handler, *to_free = NULL;
205 206 207 208

    except_ptrs.ExceptionRecord = rec;
    except_ptrs.ContextRecord = context;

209
    RtlEnterCriticalSection( &vectored_handlers_section );
210
    ptr = list_head( &vectored_exception_handlers );
211
    while (ptr)
212
    {
213 214
        handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
        handler->count++;
215
        func = RtlDecodePointer( handler->func );
216 217 218 219
        RtlLeaveCriticalSection( &vectored_handlers_section );
        RtlFreeHeap( GetProcessHeap(), 0, to_free );
        to_free = NULL;

220
        TRACE( "calling handler at %p code=%x flags=%x\n",
221 222 223
               func, rec->ExceptionCode, rec->ExceptionFlags );
        ret = func( &except_ptrs );
        TRACE( "handler at %p returned %x\n", func, ret );
224 225

        RtlEnterCriticalSection( &vectored_handlers_section );
226
        ptr = list_next( &vectored_exception_handlers, ptr );
227 228 229 230 231
        if (!--handler->count)  /* removed during execution */
        {
            list_remove( &handler->entry );
            to_free = handler;
        }
232 233
        if (ret == EXCEPTION_CONTINUE_EXECUTION) break;
    }
234
    RtlLeaveCriticalSection( &vectored_handlers_section );
235
    RtlFreeHeap( GetProcessHeap(), 0, to_free );
236 237 238 239
    return ret;
}


240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
/*******************************************************************
 *		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 */
}


257
/***********************************************************************
258
 *            RtlRaiseStatus  (NTDLL.@)
259 260 261 262 263
 *
 * Raise an exception with ExceptionCode = status
 */
void WINAPI RtlRaiseStatus( NTSTATUS status )
{
264
    raise_status( status, NULL );
265
}
266 267


268 269 270 271 272
/*******************************************************************
 *         RtlAddVectoredContinueHandler   (NTDLL.@)
 */
PVOID WINAPI RtlAddVectoredContinueHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
{
273
    return add_vectored_handler( &vectored_continue_handlers, first, func );
274 275 276 277 278 279 280 281
}


/*******************************************************************
 *         RtlRemoveVectoredContinueHandler   (NTDLL.@)
 */
ULONG WINAPI RtlRemoveVectoredContinueHandler( PVOID handler )
{
282
    return remove_vectored_handler( &vectored_continue_handlers, handler );
283 284 285
}


286 287 288
/*******************************************************************
 *         RtlAddVectoredExceptionHandler   (NTDLL.@)
 */
289
PVOID WINAPI DECLSPEC_HOTPATCH RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
290
{
291
    return add_vectored_handler( &vectored_exception_handlers, first, func );
292 293 294 295 296 297 298 299
}


/*******************************************************************
 *         RtlRemoveVectoredExceptionHandler   (NTDLL.@)
 */
ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler )
{
300
    return remove_vectored_handler( &vectored_exception_handlers, handler );
301 302 303
}


304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
/*************************************************************
 *            __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;
321
    for (;;) RtlRaiseException( &record );
322
}