exception.c 14.1 KB
Newer Older
1 2 3 4 5
/*
 * NT exception handling routines
 * 
 * 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 23
#include "config.h"

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

27 28
#include "winnt.h"
#include "ntddk.h"
29
#include "global.h"
30
#include "wine/exception.h"
31
#include "stackframe.h"
32
#include "miscemu.h"
33
#include "wine/server.h"
34
#include "wine/debug.h"
35
#include "msvcrt/excpt.h"
36

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

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

46 47 48 49 50 51 52 53 54 55
#ifdef __i386__
# define GET_IP(context) ((LPVOID)(context)->Eip)
#endif
#ifdef __sparc__
# define GET_IP(context) ((LPVOID)(context)->pc)
#endif
#ifndef GET_IP
# error You must define GET_IP for this CPU
#endif

56 57
extern void SIGNAL_Unblock(void);

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

64 65 66 67 68
/*******************************************************************
 *         EXC_RaiseHandler
 *
 * Handler for exceptions happening inside a handler.
 */
69 70
static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
                               CONTEXT *context, EXCEPTION_FRAME **dispatcher )
71 72 73 74 75 76 77 78 79 80 81 82 83 84
{
    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.
 */
85 86
static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
                                CONTEXT *context, EXCEPTION_FRAME **dispatcher )
87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
    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.
101 102
 * 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
103
 */
104 105 106
static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
                              CONTEXT *context, EXCEPTION_FRAME **dispatcher, 
                              PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
107 108 109 110 111 112
{
    EXC_NESTED_FRAME newframe;
    DWORD ret;

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


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

133
    SERVER_START_REQ( queue_exception_event )
134 135
    {
        req->first   = first_chance;
136 137 138
        wine_server_add_data( req, context, sizeof(*context) );
        wine_server_add_data( req, rec, sizeof(*rec) );
        if (!wine_server_call( req )) handle = reply->handle;
139
    }
140
    SERVER_END_REQ;
141 142 143 144 145 146
    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.
     */
147
    SERVER_START_REQ( get_exception_status )
148 149
    {
        req->handle = handle;
150 151 152
        wine_server_set_reply( req, context, sizeof(*context) );
        wine_server_call( req );
        ret = reply->status;
153
    }
154
    SERVER_END_REQ;
155
    NtClose( handle );
156
    return ret;
157 158 159
}


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

169 170 171 172 173 174 175
    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 );
176
    NtTerminateProcess( NtCurrentProcess(), 1 );
177 178 179
}


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

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]);
    if (rec->ExceptionCode == EXCEPTION_WINE_STUB) TRACE(" stub=%s\n", (char*)rec->ExceptionInformation[1]);
193

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

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

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

        /* Call handler */
212
        res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
213 214 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
        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
251
 *		RtlUnwind (NTDLL.@)
252
 */
253
DEFINE_REGS_ENTRYPOINT_4( RtlUnwind, EXC_RtlUnwind, 
254
                          PEXCEPTION_FRAME, LPVOID, PEXCEPTION_RECORD, DWORD );
255 256 257
void WINAPI EXC_RtlUnwind( PEXCEPTION_FRAME pEndFrame, LPVOID unusedEip, 
                           PEXCEPTION_RECORD pRecord, DWORD returnEax,
                           CONTEXT *context )
258 259 260 261
{
    EXCEPTION_RECORD record, newrec;
    PEXCEPTION_FRAME frame, dispatch;

262
#ifdef __i386__
263
    context->Eax = returnEax;
264
#endif
265

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

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

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

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

        /* Call handler */
306 307
        switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
                                frame->Handler, EXC_UnwindHandler ))
308 309 310 311 312 313 314 315 316 317 318 319 320 321
        {
        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;
        }
322
        frame = __wine_pop_frame( frame );
323 324 325 326 327
    }
}


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


/***********************************************************************
341
 *            RtlRaiseStatus  (NTDLL.@)
342 343 344 345 346 347 348 349 350 351 352 353 354
 *
 * 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 );
}
355 356 357


/*************************************************************
358
 *            __wine_exception_handler (NTDLL.@)
359 360 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
 *
 * 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 );
}


/*************************************************************
398
 *            __wine_finally_handler (NTDLL.@)
399 400 401 402 403 404 405 406 407 408 409 410 411
 *
 * Exception handler for try/finally blocks declared in Wine code.
 */
DWORD __wine_finally_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)))
        return ExceptionContinueSearch;
    wine_frame->u.finally_func( FALSE );
    return ExceptionContinueSearch;
}