exception.c 14.8 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 19
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 21
 */

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

25
#include <assert.h>
26
#include <signal.h>
27

28
#include "windef.h"
29
#include "winternl.h"
30
#include "global.h"
31
#include "wine/exception.h"
32
#include "stackframe.h"
33
#include "miscemu.h"
34
#include "wine/server.h"
35
#include "wine/debug.h"
36
#include "excpt.h"
37

38
WINE_DEFAULT_DEBUG_CHANNEL(seh);
39 40 41 42 43 44 45 46

/* Exception record for handling exceptions happening inside exception handlers */
typedef struct
{
    EXCEPTION_FRAME frame;
    EXCEPTION_FRAME *prevFrame;
} EXC_NESTED_FRAME;

47 48
#ifdef __i386__
# define GET_IP(context) ((LPVOID)(context)->Eip)
49
#elif defined(__sparc__)
50
# define GET_IP(context) ((LPVOID)(context)->pc)
51
#elif defined(__powerpc__)
52
# define GET_IP(context) ((LPVOID)(context)->Iar)
53
#else
54 55 56
# error You must define GET_IP for this CPU
#endif

57 58
extern void SIGNAL_Unblock(void);

59
void WINAPI EXC_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
60
void WINAPI EXC_RtlUnwind( PEXCEPTION_FRAME, LPVOID,
61 62 63 64
                           PEXCEPTION_RECORD, DWORD, PCONTEXT );
void WINAPI EXC_NtRaiseException( PEXCEPTION_RECORD, PCONTEXT,
                                  BOOL, PCONTEXT );

65 66 67 68 69
/*******************************************************************
 *         EXC_RaiseHandler
 *
 * Handler for exceptions happening inside a handler.
 */
70 71
static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
                               CONTEXT *context, EXCEPTION_FRAME **dispatcher )
72 73 74 75 76 77 78 79 80 81 82 83 84 85
{
    if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
        return ExceptionContinueSearch;
    /* We shouldn't get here so we store faulty frame in dispatcher */
    *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
    return ExceptionNestedException;
}


/*******************************************************************
 *         EXC_UnwindHandler
 *
 * Handler for exceptions happening inside an unwind handler.
 */
86 87
static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
                                CONTEXT *context, EXCEPTION_FRAME **dispatcher )
88 89 90 91 92 93 94 95 96 97 98 99 100 101
{
    if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
        return ExceptionContinueSearch;
    /* We shouldn't get here so we store faulty frame in dispatcher */
    *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
    return ExceptionCollidedUnwind;
}


/*******************************************************************
 *         EXC_CallHandler
 *
 * Call an exception handler, setting up an exception frame to catch exceptions
 * happening during the handler execution.
102 103
 * Please do not change the first 4 parameters order in any way - some exceptions handlers
 * rely on Base Pointer (EBP) to have a fixed position related to the exception frame
104
 */
105
static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
106
                              CONTEXT *context, EXCEPTION_FRAME **dispatcher,
107
                              PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
108 109 110 111 112 113
{
    EXC_NESTED_FRAME newframe;
    DWORD ret;

    newframe.frame.Handler = nested_handler;
    newframe.prevFrame     = frame;
114
    __wine_push_frame( &newframe.frame );
115 116
    TRACE( "calling handler at %p code=%lx flags=%lx\n",
           handler, record->ExceptionCode, record->ExceptionFlags );
117 118
    ret = handler( record, frame, context, dispatcher );
    TRACE( "handler returned %lx\n", ret );
119
    __wine_pop_frame( &newframe.frame );
120 121 122 123
    return ret;
}


124
/**********************************************************************
125
 *           send_debug_event
126 127 128
 *
 * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
 */
129
static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
130
{
131
    int ret;
132 133
    HANDLE handle = 0;

134
    SERVER_START_REQ( queue_exception_event )
135 136
    {
        req->first   = first_chance;
137 138 139
        wine_server_add_data( req, context, sizeof(*context) );
        wine_server_add_data( req, rec, sizeof(*rec) );
        if (!wine_server_call( req )) handle = reply->handle;
140
    }
141
    SERVER_END_REQ;
142 143 144 145 146 147
    if (!handle) return 0;  /* no debugger present or other error */

    /* No need to wait on the handle since the process gets suspended
     * once the event is passed to the debugger, so when we get back
     * here the event has been continued already.
     */
148
    SERVER_START_REQ( get_exception_status )
149 150
    {
        req->handle = handle;
151 152 153
        wine_server_set_reply( req, context, sizeof(*context) );
        wine_server_call( req );
        ret = reply->status;
154
    }
155
    SERVER_END_REQ;
156
    NtClose( handle );
157
    return ret;
158 159 160
}


161 162 163 164 165 166 167
/*******************************************************************
 *         EXC_DefaultHandling
 *
 * Default handling for exceptions. Called when we didn't find a suitable handler.
 */
static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
{
168
    if (send_debug_event( rec, FALSE, context ) == DBG_CONTINUE) return;  /* continue execution */
169

170 171 172 173 174 175 176
    if (rec->ExceptionFlags & EH_STACK_INVALID)
        ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
    else if (rec->ExceptionCode == EXCEPTION_NONCONTINUABLE_EXCEPTION)
        ERR("Process attempted to continue execution after noncontinuable exception.\n");
    else
        ERR("Unhandled exception code %lx flags %lx addr %p\n",
            rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
177
    NtTerminateProcess( NtCurrentProcess(), 1 );
178 179 180
}


181
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
182
 *		RtlRaiseException (NTDLL.@)
183
 */
184
DEFINE_REGS_ENTRYPOINT_1( RtlRaiseException, EXC_RtlRaiseException, EXCEPTION_RECORD * );
185
void WINAPI EXC_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context )
186 187 188
{
    PEXCEPTION_FRAME frame, dispatch, nested_frame;
    EXCEPTION_RECORD newrec;
189
    DWORD res, c;
190

191 192
    TRACE( "code=%lx flags=%lx addr=%p\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
    for (c=0; c<rec->NumberParameters; c++) TRACE(" info[%ld]=%08lx\n", c, rec->ExceptionInformation[c]);
193 194
    if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
        FIXME( "call to unimplemented function %s\n", (char*)rec->ExceptionInformation[1] );
195

196
    if (send_debug_event( rec, TRUE, context ) == DBG_CONTINUE) return;  /* continue execution */
197

198 199
    SIGNAL_Unblock(); /* we may be in a signal handler, and exception handlers may jump out */

200
    frame = NtCurrentTeb()->except;
201 202 203 204
    nested_frame = NULL;
    while (frame != (PEXCEPTION_FRAME)0xFFFFFFFF)
    {
        /* Check frame address */
205 206
        if (((void*)frame < NtCurrentTeb()->stack_low) ||
            ((void*)(frame+1) > NtCurrentTeb()->stack_top) ||
207 208 209 210 211 212 213
            (int)frame & 3)
        {
            rec->ExceptionFlags |= EH_STACK_INVALID;
            break;
        }

        /* Call handler */
214
        res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
        if (frame == nested_frame)
        {
            /* no longer nested */
            nested_frame = NULL;
            rec->ExceptionFlags &= ~EH_NESTED_CALL;
        }

        switch(res)
        {
        case ExceptionContinueExecution:
            if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
            newrec.ExceptionCode    = STATUS_NONCONTINUABLE_EXCEPTION;
            newrec.ExceptionFlags   = EH_NONCONTINUABLE;
            newrec.ExceptionRecord  = rec;
            newrec.NumberParameters = 0;
            RtlRaiseException( &newrec );  /* never returns */
            break;
        case ExceptionContinueSearch:
            break;
        case ExceptionNestedException:
            if (nested_frame < dispatch) nested_frame = dispatch;
            rec->ExceptionFlags |= EH_NESTED_CALL;
            break;
        default:
            newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
            newrec.ExceptionFlags   = EH_NONCONTINUABLE;
            newrec.ExceptionRecord  = rec;
            newrec.NumberParameters = 0;
            RtlRaiseException( &newrec );  /* never returns */
            break;
        }
        frame = frame->Prev;
    }
    EXC_DefaultHandling( rec, context );
}


/*******************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
253
 *		RtlUnwind (NTDLL.@)
254
 */
255
DEFINE_REGS_ENTRYPOINT_4( RtlUnwind, EXC_RtlUnwind,
256
                          PVOID, PVOID, PEXCEPTION_RECORD, PVOID );
257
void WINAPI EXC_RtlUnwind( PEXCEPTION_FRAME pEndFrame, LPVOID unusedEip,
258 259
                           PEXCEPTION_RECORD pRecord, DWORD returnEax,
                           CONTEXT *context )
260 261 262 263
{
    EXCEPTION_RECORD record, newrec;
    PEXCEPTION_FRAME frame, dispatch;

264
#ifdef __i386__
265
    context->Eax = returnEax;
266
#endif
267

268 269 270 271 272 273
    /* build an exception record, if we do not have one */
    if (!pRecord)
    {
        record.ExceptionCode    = STATUS_UNWIND;
        record.ExceptionFlags   = 0;
        record.ExceptionRecord  = NULL;
274
        record.ExceptionAddress = GET_IP(context);
275 276 277 278 279
        record.NumberParameters = 0;
        pRecord = &record;
    }

    pRecord->ExceptionFlags |= EH_UNWINDING | (pEndFrame ? 0 : EH_EXIT_UNWIND);
280 281 282

    TRACE( "code=%lx flags=%lx\n", pRecord->ExceptionCode, pRecord->ExceptionFlags );

283
    /* get chain of exception frames */
284
    frame = NtCurrentTeb()->except;
285 286 287 288 289 290 291 292 293 294 295
    while ((frame != (PEXCEPTION_FRAME)0xffffffff) && (frame != pEndFrame))
    {
        /* Check frame address */
        if (pEndFrame && (frame > pEndFrame))
        {
            newrec.ExceptionCode    = STATUS_INVALID_UNWIND_TARGET;
            newrec.ExceptionFlags   = EH_NONCONTINUABLE;
            newrec.ExceptionRecord  = pRecord;
            newrec.NumberParameters = 0;
            RtlRaiseException( &newrec );  /* never returns */
        }
296 297
        if (((void*)frame < NtCurrentTeb()->stack_low) ||
            ((void*)(frame+1) > NtCurrentTeb()->stack_top) ||
298 299 300 301 302 303 304 305 306 307
            (int)frame & 3)
        {
            newrec.ExceptionCode    = STATUS_BAD_STACK;
            newrec.ExceptionFlags   = EH_NONCONTINUABLE;
            newrec.ExceptionRecord  = pRecord;
            newrec.NumberParameters = 0;
            RtlRaiseException( &newrec );  /* never returns */
        }

        /* Call handler */
308 309
        switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
                                frame->Handler, EXC_UnwindHandler ))
310 311 312 313 314 315 316 317 318 319 320 321 322 323
        {
        case ExceptionContinueSearch:
            break;
        case ExceptionCollidedUnwind:
            frame = dispatch;
            break;
        default:
            newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
            newrec.ExceptionFlags   = EH_NONCONTINUABLE;
            newrec.ExceptionRecord  = pRecord;
            newrec.NumberParameters = 0;
            RtlRaiseException( &newrec );  /* never returns */
            break;
        }
324
        frame = __wine_pop_frame( frame );
325 326 327 328 329
    }
}


/*******************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
330
 *		NtRaiseException (NTDLL.@)
331
 */
332
DEFINE_REGS_ENTRYPOINT_3( NtRaiseException, EXC_NtRaiseException,
333
                          EXCEPTION_RECORD *, CONTEXT *, BOOL );
334 335
void WINAPI EXC_NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx,
                                  BOOL first, CONTEXT *context )
336
{
337
    EXC_RtlRaiseException( rec, ctx );
338 339 340 341 342
    *context = *ctx;
}


/***********************************************************************
343
 *            RtlRaiseStatus  (NTDLL.@)
344 345 346 347 348 349 350 351 352 353 354 355 356
 *
 * Raise an exception with ExceptionCode = status
 */
void WINAPI RtlRaiseStatus( NTSTATUS status )
{
    EXCEPTION_RECORD ExceptionRec;

    ExceptionRec.ExceptionCode    = status;
    ExceptionRec.ExceptionFlags   = EH_NONCONTINUABLE;
    ExceptionRec.ExceptionRecord  = NULL;
    ExceptionRec.NumberParameters = 0;
    RtlRaiseException( &ExceptionRec );
}
357 358 359


/*************************************************************
360
 *            __wine_exception_handler (NTDLL.@)
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
 *
 * Exception handler for exception blocks declared in Wine code.
 */
DWORD __wine_exception_handler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
                                CONTEXT *context, LPVOID pdispatcher )
{
    __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;

    if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))
        return ExceptionContinueSearch;
    if (wine_frame->u.filter)
    {
        EXCEPTION_POINTERS ptrs;
        ptrs.ExceptionRecord = record;
        ptrs.ContextRecord = context;
        switch(wine_frame->u.filter( &ptrs ))
        {
        case EXCEPTION_CONTINUE_SEARCH:
            return ExceptionContinueSearch;
        case EXCEPTION_CONTINUE_EXECUTION:
            return ExceptionContinueExecution;
        case EXCEPTION_EXECUTE_HANDLER:
            break;
        default:
            MESSAGE( "Invalid return value from exception filter\n" );
            assert( FALSE );
        }
    }
    /* hack to make GetExceptionCode() work in handler */
    wine_frame->ExceptionCode   = record->ExceptionCode;
    wine_frame->ExceptionRecord = wine_frame;

    RtlUnwind( frame, 0, record, 0 );
    __wine_pop_frame( frame );
    longjmp( wine_frame->jmp, 1 );
}


/*************************************************************
400
 *            __wine_finally_handler (NTDLL.@)
401 402 403 404 405 406
 *
 * Exception handler for try/finally blocks declared in Wine code.
 */
DWORD __wine_finally_handler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
                              CONTEXT *context, LPVOID pdispatcher )
{
407 408 409 410 411 412 413
    if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
        wine_frame->u.finally_func( FALSE );
    }
    return ExceptionContinueSearch;
}
414

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430

/*************************************************************
 *            __wine_callto16_handler
 *
 * Handler for exceptions occuring in 16-bit code.
 */
DWORD __wine_callto16_handler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
                               CONTEXT *context, LPVOID pdispatcher )
{
    if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
        STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame));
        NtCurrentTeb()->cur_stack = frame32->frame16;
        _LeaveWin16Lock();
    }
431 432
    return ExceptionContinueSearch;
}