except_i386.c 39.8 KB
Newer Older
1 2 3
/*
 * msvcrt C++ exception handling
 *
4
 * Copyright 2000 Jon Griffiths
5
 * Copyright 2002 Alexandre Julliard
6
 * Copyright 2005 Juan Lang
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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22 23 24 25 26 27 28 29 30
 *
 * NOTES
 * A good reference is the article "How a C++ compiler implements
 * exception handling" by Vishal Kochhar, available on
 * www.thecodeproject.com.
 */

#include "config.h"
#include "wine/port.h"

31 32
#ifdef __i386__

33 34 35 36
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
37
#include "winternl.h"
38 39
#include "msvcrt.h"
#include "wine/exception.h"
40
#include "excpt.h"
41 42
#include "wine/debug.h"

43
#include "cppexcept.h"
44

45 46
WINE_DEFAULT_DEBUG_CHANNEL(seh);

47 48 49 50 51 52 53 54 55 56 57 58 59 60 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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

/* the exception frame used by CxxFrameHandler */
typedef struct __cxx_exception_frame
{
    EXCEPTION_REGISTRATION_RECORD  frame;    /* the standard exception frame */
    int                            trylevel;
    DWORD                          ebp;
} cxx_exception_frame;

/* info about a single catch {} block */
typedef struct __catchblock_info
{
    UINT             flags;         /* flags (see below) */
    const type_info *type_info;     /* C++ type caught by this block */
    int              offset;        /* stack offset to copy exception object to */
    void           (*handler)(void);/* catch block handler code */
} catchblock_info;
#define TYPE_FLAG_CONST      1
#define TYPE_FLAG_VOLATILE   2
#define TYPE_FLAG_REFERENCE  8

/* info about a single try {} block */
typedef struct __tryblock_info
{
    int                    start_level;      /* start trylevel of that block */
    int                    end_level;        /* end trylevel of that block */
    int                    catch_level;      /* initial trylevel of the catch block */
    int                    catchblock_count; /* count of catch blocks in array */
    const catchblock_info *catchblock;       /* array of catch blocks */
} tryblock_info;

/* info about the unwind handler for a given trylevel */
typedef struct __unwind_info
{
    int    prev;          /* prev trylevel unwind handler, to run after this one */
    void (*handler)(void);/* unwind handler */
} unwind_info;

/* descriptor of all try blocks of a given function */
typedef struct __cxx_function_descr
{
    UINT                 magic;          /* must be CXX_FRAME_MAGIC */
    UINT                 unwind_count;   /* number of unwind handlers */
    const unwind_info   *unwind_table;   /* array of unwind handlers */
    UINT                 tryblock_count; /* number of try blocks */
    const tryblock_info *tryblock;       /* array of try blocks */
    UINT                 ipmap_count;
    const void          *ipmap;
    const void          *expect_list;    /* expected exceptions list when magic >= VC7 */
    UINT                 flags;          /* flags when magic >= VC8 */
} cxx_function_descr;

#define FUNC_DESCR_SYNCHRONOUS  1        /* synchronous exceptions only (built with /EHs) */

typedef struct _SCOPETABLE
{
  int previousTryLevel;
  int (*lpfnFilter)(PEXCEPTION_POINTERS);
  int (*lpfnHandler)(void);
} SCOPETABLE, *PSCOPETABLE;

typedef struct _MSVCRT_EXCEPTION_FRAME
{
  EXCEPTION_REGISTRATION_RECORD *prev;
  void (*handler)(PEXCEPTION_RECORD, EXCEPTION_REGISTRATION_RECORD*,
                  PCONTEXT, PEXCEPTION_RECORD);
  PSCOPETABLE scopetable;
  int trylevel;
  int _ebp;
  PEXCEPTION_POINTERS xpointers;
} MSVCRT_EXCEPTION_FRAME;

typedef struct
{
  int   gs_cookie_offset;
  ULONG gs_cookie_xor;
  int   eh_cookie_offset;
  ULONG eh_cookie_xor;
  SCOPETABLE entries[1];
} SCOPETABLE_V4;

#define TRYLEVEL_END (-1) /* End of trylevel list */

130 131
DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame,
                               PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch,
132 133
                               const cxx_function_descr *descr,
                               EXCEPTION_REGISTRATION_RECORD* nested_frame, int nested_trylevel );
134

135
/* call a function with a given ebp */
136
static inline void *call_ebp_func( void *func, void *ebp )
137 138
{
    void *ret;
139 140 141 142 143 144 145 146 147
    int dummy;
    __asm__ __volatile__ ("pushl %%ebx\n\t"
                          "pushl %%ebp\n\t"
                          "movl %4,%%ebp\n\t"
                          "call *%%eax\n\t"
                          "popl %%ebp\n\t"
                          "popl %%ebx"
                          : "=a" (ret), "=S" (dummy), "=D" (dummy)
                          : "0" (func), "1" (ebp) : "ecx", "edx", "memory" );
148 149 150 151
    return ret;
}

/* call a copy constructor */
152
static inline void call_copy_ctor( void *func, void *this, void *src, int has_vbase )
153 154 155 156 157
{
    TRACE( "calling copy ctor %p object %p src %p\n", func, this, src );
    if (has_vbase)
        /* in that case copy ctor takes an extra bool indicating whether to copy the base class */
        __asm__ __volatile__("pushl $1; pushl %2; call *%0"
158
                             : : "r" (func), "c" (this), "r" (src) : "eax", "edx", "memory" );
159 160
    else
        __asm__ __volatile__("pushl %2; call *%0"
161
                             : : "r" (func), "c" (this), "r" (src) : "eax", "edx", "memory" );
162 163 164
}

/* call the destructor of the exception object */
165
static inline void call_dtor( void *func, void *object )
166
{
167
    __asm__ __volatile__("call *%0" : : "r" (func), "c" (object) : "eax", "edx", "memory" );
168 169
}

170
/* continue execution to the specified address after exception is caught */
171
static inline void DECLSPEC_NORETURN continue_after_catch( cxx_exception_frame* frame, void *addr )
172 173 174 175 176 177
{
    __asm__ __volatile__("movl -4(%0),%%esp; leal 12(%0),%%ebp; jmp *%1"
                         : : "r" (frame), "a" (addr) );
    for (;;) ; /* unreached */
}

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static inline void call_finally_block( void *code_block, void *base_ptr )
{
    __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax"
                          : : "a" (code_block), "g" (base_ptr));
}

static inline int call_filter( int (*func)(PEXCEPTION_POINTERS), void *arg, void *ebp )
{
    int ret;
    __asm__ __volatile__ ("pushl %%ebp; pushl %3; movl %2,%%ebp; call *%%eax; popl %%ebp; popl %%ebp"
                          : "=a" (ret)
                          : "0" (func), "r" (ebp), "r" (arg)
                          : "ecx", "edx", "memory" );
    return ret;
}

static inline int call_unwind_func( int (*func)(void), void *ebp )
{
    int ret;
    __asm__ __volatile__ ("pushl %%ebp\n\t"
                          "pushl %%ebx\n\t"
                          "pushl %%esi\n\t"
                          "pushl %%edi\n\t"
                          "movl %2,%%ebp\n\t"
                          "call *%0\n\t"
                          "popl %%edi\n\t"
                          "popl %%esi\n\t"
                          "popl %%ebx\n\t"
                          "popl %%ebp"
                          : "=a" (ret)
                          : "0" (func), "r" (ebp)
                          : "ecx", "edx", "memory" );
    return ret;
}

213
static inline void dump_type( const cxx_type_info *type )
214
{
215
    TRACE( "flags %x type %p %s offsets %d,%d,%d size %d copy ctor %p\n",
216 217 218
             type->flags, type->type_info, dbgstr_type_info(type->type_info),
             type->offsets.this_offset, type->offsets.vbase_descr, type->offsets.vbase_offset,
             type->size, type->copy_ctor );
219 220
}

221
static void dump_exception_type( const cxx_exception_type *type )
222
{
223
    UINT i;
224

225
    TRACE( "flags %x destr %p handler %p type info %p\n",
226 227 228
             type->flags, type->destructor, type->custom_handler, type->type_info_table );
    for (i = 0; i < type->type_info_table->count; i++)
    {
229
        TRACE( "    %d: ", i );
230 231 232 233
        dump_type( type->type_info_table->info[i] );
    }
}

234
static void dump_function_descr( const cxx_function_descr *descr )
235
{
236 237
    UINT i;
    int j;
238

239 240
    TRACE( "magic %x\n", descr->magic );
    TRACE( "unwind table: %p %d\n", descr->unwind_table, descr->unwind_count );
241 242
    for (i = 0; i < descr->unwind_count; i++)
    {
243
        TRACE( "    %d: prev %d func %p\n", i,
244 245
                 descr->unwind_table[i].prev, descr->unwind_table[i].handler );
    }
246
    TRACE( "try table: %p %d\n", descr->tryblock, descr->tryblock_count );
247 248
    for (i = 0; i < descr->tryblock_count; i++)
    {
249
        TRACE( "    %d: start %d end %d catchlevel %d catch %p %d\n", i,
250 251 252 253 254
                 descr->tryblock[i].start_level, descr->tryblock[i].end_level,
                 descr->tryblock[i].catch_level, descr->tryblock[i].catchblock,
                 descr->tryblock[i].catchblock_count );
        for (j = 0; j < descr->tryblock[i].catchblock_count; j++)
        {
255
            const catchblock_info *ptr = &descr->tryblock[i].catchblock[j];
256
            TRACE( "        %d: flags %x offset %d handler %p type %p %s\n",
257 258
                     j, ptr->flags, ptr->offset, ptr->handler,
                     ptr->type_info, dbgstr_type_info( ptr->type_info ) );
259 260
        }
    }
261 262 263 264
    if (descr->magic <= CXX_FRAME_MAGIC_VC6) return;
    TRACE( "expect list: %p\n", descr->expect_list );
    if (descr->magic <= CXX_FRAME_MAGIC_VC7) return;
    TRACE( "flags: %08x\n", descr->flags );
265 266 267
}

/* check if the exception type is caught by a given catch block, and return the type that matched */
268 269
static const cxx_type_info *find_caught_type( cxx_exception_type *exc_type,
                                              const catchblock_info *catchblock )
270 271 272 273 274
{
    UINT i;

    for (i = 0; i < exc_type->type_info_table->count; i++)
    {
275
        const cxx_type_info *type = exc_type->type_info_table->info[i];
276 277 278 279

        if (!catchblock->type_info) return type;   /* catch(...) matches any type */
        if (catchblock->type_info != type->type_info)
        {
280
            if (strcmp( catchblock->type_info->mangled, type->type_info->mangled )) continue;
281 282 283 284 285 286 287 288 289 290 291 292 293 294
        }
        /* type is the same, now check the flags */
        if ((exc_type->flags & TYPE_FLAG_CONST) &&
            !(catchblock->flags & TYPE_FLAG_CONST)) continue;
        if ((exc_type->flags & TYPE_FLAG_VOLATILE) &&
            !(catchblock->flags & TYPE_FLAG_VOLATILE)) continue;
        return type;  /* it matched */
    }
    return NULL;
}


/* copy the exception object where the catch block wants it */
static void copy_exception( void *object, cxx_exception_frame *frame,
295
                            const catchblock_info *catchblock, const cxx_type_info *type )
296 297 298
{
    void **dest_ptr;

299
    if (!catchblock->type_info || !catchblock->type_info->mangled[0]) return;
300 301 302 303 304
    if (!catchblock->offset) return;
    dest_ptr = (void **)((char *)&frame->ebp + catchblock->offset);

    if (catchblock->flags & TYPE_FLAG_REFERENCE)
    {
305
        *dest_ptr = get_this_pointer( &type->offsets, object );
306 307 308 309 310
    }
    else if (type->flags & CLASS_IS_SIMPLE_TYPE)
    {
        memmove( dest_ptr, object, type->size );
        /* if it is a pointer, adjust it */
311
        if (type->size == sizeof(void *)) *dest_ptr = get_this_pointer( &type->offsets, *dest_ptr );
312 313 314 315
    }
    else  /* copy the object */
    {
        if (type->copy_ctor)
316
            call_copy_ctor( type->copy_ctor, dest_ptr, get_this_pointer(&type->offsets,object),
317 318
                            (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS) );
        else
319
            memmove( dest_ptr, get_this_pointer(&type->offsets,object), type->size );
320 321 322 323
    }
}

/* unwind the local function up to a given trylevel */
324
static void cxx_local_unwind( cxx_exception_frame* frame, const cxx_function_descr *descr, int last_level)
325
{
326
    void (*handler)(void);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    int trylevel = frame->trylevel;

    while (trylevel != last_level)
    {
        if (trylevel < 0 || trylevel >= descr->unwind_count)
        {
            ERR( "invalid trylevel %d\n", trylevel );
            MSVCRT_terminate();
        }
        handler = descr->unwind_table[trylevel].handler;
        if (handler)
        {
            TRACE( "calling unwind handler %p trylevel %d last %d ebp %p\n",
                   handler, trylevel, last_level, &frame->ebp );
            call_ebp_func( handler, &frame->ebp );
        }
        trylevel = descr->unwind_table[trylevel].prev;
    }
    frame->trylevel = last_level;
}

348 349 350
/* exception frame for nested exceptions in catch block */
struct catch_func_nested_frame
{
351 352 353
    EXCEPTION_REGISTRATION_RECORD frame;     /* standard exception frame */
    EXCEPTION_RECORD             *prev_rec;  /* previous record to restore in thread data */
    cxx_exception_frame          *cxx_frame; /* frame of parent exception */
354
    const cxx_function_descr     *descr;     /* descriptor of parent exception */
355
    int                           trylevel;  /* current try level */
356
    EXCEPTION_RECORD             *rec;       /* rec associated with frame */
357 358 359
};

/* handler for exceptions happening while calling a catch function */
360 361
static DWORD catch_function_nested_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
                                            CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
362 363 364 365 366 367 368 369
{
    struct catch_func_nested_frame *nested_frame = (struct catch_func_nested_frame *)frame;

    if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        msvcrt_get_thread_data()->exc_record = nested_frame->prev_rec;
        return ExceptionContinueSearch;
    }
370 371 372 373

    TRACE( "got nested exception in catch function\n" );

    if(rec->ExceptionCode == CXX_EXCEPTION)
374
    {
375
        PEXCEPTION_RECORD prev_rec = nested_frame->rec;
376 377 378
        if((rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) ||
                (rec->ExceptionInformation[1] == prev_rec->ExceptionInformation[1] &&
                 rec->ExceptionInformation[2] == prev_rec->ExceptionInformation[2]))
379 380 381 382 383 384 385
        {
            /* exception was rethrown */
            rec->ExceptionInformation[1] = prev_rec->ExceptionInformation[1];
            rec->ExceptionInformation[2] = prev_rec->ExceptionInformation[2];
            TRACE("detect rethrow: re-propagate: obj: %lx, type: %lx\n",
                    rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
        }
386 387 388 389 390 391 392
        else if (nested_frame->prev_rec &&
                nested_frame->prev_rec->ExceptionInformation[1] == prev_rec->ExceptionInformation[1] &&
                nested_frame->prev_rec->ExceptionInformation[2] == prev_rec->ExceptionInformation[2])
        {
            TRACE("detect threw new exception in catch block - not owning old(obj: %lx type: %lx)\n",
                    prev_rec->ExceptionInformation[1], prev_rec->ExceptionInformation[2]);
        }
393 394 395 396 397 398 399 400 401
        else {
            /* new exception in exception handler, destroy old */
            void *object = (void*)prev_rec->ExceptionInformation[1];
            cxx_exception_type *info = (cxx_exception_type*) prev_rec->ExceptionInformation[2];
            TRACE("detect threw new exception in catch block - destroy old(obj: %p type: %p)\n",
                    object, info);
            if(info && info->destructor)
                call_dtor( info->destructor, object );
        }
402
    }
403 404 405 406

    return cxx_frame_handler( rec, nested_frame->cxx_frame, context,
                              NULL, nested_frame->descr, &nested_frame->frame,
                              nested_frame->trylevel );
407 408
}

409 410
/* find and call the appropriate catch block for an exception */
/* returns the address to continue execution to after the catch block was called */
411
static inline void call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_frame *frame,
412
                                     const cxx_function_descr *descr, int nested_trylevel,
413
                                     cxx_exception_type *info )
414
{
415 416
    UINT i;
    int j;
417
    void *addr, *object = (void *)rec->ExceptionInformation[1];
418 419
    struct catch_func_nested_frame nested_frame;
    int trylevel = frame->trylevel;
420
    thread_data_t *thread_data = msvcrt_get_thread_data();
421
    DWORD save_esp = ((DWORD*)frame)[-1];
422 423 424

    for (i = 0; i < descr->tryblock_count; i++)
    {
425
        const tryblock_info *tryblock = &descr->tryblock[i];
426 427 428 429 430 431 432

        if (trylevel < tryblock->start_level) continue;
        if (trylevel > tryblock->end_level) continue;

        /* got a try block */
        for (j = 0; j < tryblock->catchblock_count; j++)
        {
433
            const catchblock_info *catchblock = &tryblock->catchblock[j];
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
            if(info)
            {
                const cxx_type_info *type = find_caught_type( info, catchblock );
                if (!type) continue;

                TRACE( "matched type %p in tryblock %d catchblock %d\n", type, i, j );

                /* copy the exception to its destination on the stack */
                copy_exception( object, frame, catchblock, type );
            }
            else
            {
                /* no CXX_EXCEPTION only proceed with a catch(...) block*/
                if(catchblock->type_info)
                    continue;
                TRACE("found catch(...) block\n");
            }
451 452

            /* unwind the stack */
453
            RtlUnwind( frame, 0, rec, 0 );
454 455 456 457
            cxx_local_unwind( frame, descr, tryblock->start_level );
            frame->trylevel = tryblock->end_level + 1;

            /* call the catch block */
458 459
            TRACE( "calling catch block %p addr %p ebp %p\n",
                   catchblock, catchblock->handler, &frame->ebp );
460 461 462 463 464 465 466 467

            /* setup an exception block for nested exceptions */

            nested_frame.frame.Handler = catch_function_nested_handler;
            nested_frame.prev_rec  = thread_data->exc_record;
            nested_frame.cxx_frame = frame;
            nested_frame.descr     = descr;
            nested_frame.trylevel  = nested_trylevel + 1;
468
            nested_frame.rec = rec;
469 470 471

            __wine_push_frame( &nested_frame.frame );
            thread_data->exc_record = rec;
472
            addr = call_ebp_func( catchblock->handler, &frame->ebp );
473 474 475
            thread_data->exc_record = nested_frame.prev_rec;
            __wine_pop_frame( &nested_frame.frame );

476
            ((DWORD*)frame)[-1] = save_esp;
477
            if (info && info->destructor) call_dtor( info->destructor, object );
478
            TRACE( "done, continuing at %p\n", addr );
479

480
            continue_after_catch( frame, addr );
481 482 483 484 485 486 487 488 489 490
        }
    }
}


/*********************************************************************
 *		cxx_frame_handler
 *
 * Implementation of __CxxFrameHandler.
 */
491 492
DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame,
                               PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch,
493 494
                               const cxx_function_descr *descr,
                               EXCEPTION_REGISTRATION_RECORD* nested_frame,
495
                               int nested_trylevel )
496 497 498
{
    cxx_exception_type *exc_type;

499
    if (descr->magic < CXX_FRAME_MAGIC_VC6 || descr->magic > CXX_FRAME_MAGIC_VC8)
500 501 502 503
    {
        ERR( "invalid frame magic %x\n", descr->magic );
        return ExceptionContinueSearch;
    }
504 505 506 507 508
    if (descr->magic >= CXX_FRAME_MAGIC_VC8 &&
        (descr->flags & FUNC_DESCR_SYNCHRONOUS) &&
        (rec->ExceptionCode != CXX_EXCEPTION))
        return ExceptionContinueSearch;  /* handle only c++ exceptions */

509 510
    if (rec->ExceptionFlags & (EH_UNWINDING|EH_EXIT_UNWIND))
    {
511
        if (descr->unwind_count && !nested_trylevel) cxx_local_unwind( frame, descr, -1 );
512 513 514 515
        return ExceptionContinueSearch;
    }
    if (!descr->tryblock_count) return ExceptionContinueSearch;

516
    if(rec->ExceptionCode == CXX_EXCEPTION)
517
    {
518 519 520 521 522 523 524 525 526
        if (rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0)
        {
            EXCEPTION_RECORD *exc_record = msvcrt_get_thread_data()->exc_record;
            rec->ExceptionInformation[1] = exc_record->ExceptionInformation[1];
            rec->ExceptionInformation[2] = exc_record->ExceptionInformation[2];
            TRACE("detect rethrow: obj: %lx, type: %lx\n",
                    rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
        }

527
        exc_type = (cxx_exception_type *)rec->ExceptionInformation[2];
528

529
        if (rec->ExceptionInformation[0] > CXX_FRAME_MAGIC_VC8 &&
530 531
                exc_type->custom_handler)
        {
532
            return exc_type->custom_handler( rec, frame, context, dispatch,
533 534
                                         descr, nested_trylevel, nested_frame, 0 );
        }
535

536 537 538 539 540
        if (TRACE_ON(seh))
        {
            TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p nested_frame %p\n",
                  rec, frame, frame->trylevel, descr, nested_frame );
            dump_exception_type( exc_type );
541
            dump_function_descr( descr );
542 543 544
        }
    }
    else
545
    {
546
        exc_type = NULL;
547
        TRACE("handling C exception code %x  rec %p frame %p trylevel %d descr %p nested_frame %p\n",
548
              rec->ExceptionCode,  rec, frame, frame->trylevel, descr, nested_frame );
549 550
    }

551 552
    call_catch_block( rec, frame, descr, frame->trylevel, exc_type );
    return ExceptionContinueSearch;
553 554 555 556 557 558
}


/*********************************************************************
 *		__CxxFrameHandler (MSVCRT.@)
 */
559 560
extern DWORD CDECL __CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame,
                                      PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch );
561 562
__ASM_GLOBAL_FUNC( __CxxFrameHandler,
                   "pushl $0\n\t"        /* nested_trylevel */
563
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
564
                   "pushl $0\n\t"        /* nested_frame */
565
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
566
                   "pushl %eax\n\t"      /* descr */
567
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
568
                   "pushl 28(%esp)\n\t"  /* dispatch */
569
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
570
                   "pushl 28(%esp)\n\t"  /* context */
571
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
572
                   "pushl 28(%esp)\n\t"  /* frame */
573
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
574
                   "pushl 28(%esp)\n\t"  /* rec */
575
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
576 577
                   "call " __ASM_NAME("cxx_frame_handler") "\n\t"
                   "add $28,%esp\n\t"
578
                   __ASM_CFI(".cfi_adjust_cfa_offset -28\n\t")
579
                   "ret" )
580

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

/*********************************************************************
 *		__CxxLongjmpUnwind (MSVCRT.@)
 *
 * Callback meant to be used as UnwindFunc for setjmp/longjmp.
 */
void __stdcall __CxxLongjmpUnwind( const struct MSVCRT___JUMP_BUFFER *buf )
{
    cxx_exception_frame *frame = (cxx_exception_frame *)buf->Registration;
    const cxx_function_descr *descr = (const cxx_function_descr *)buf->UnwindData[0];

    TRACE( "unwinding frame %p descr %p trylevel %ld\n", frame, descr, buf->TryLevel );
    cxx_local_unwind( frame, descr, buf->TryLevel );
}

596 597 598 599 600 601 602 603 604 605
/*********************************************************************
 *		__CppXcptFilter (MSVCRT.@)
 */
int CDECL __CppXcptFilter(NTSTATUS ex, PEXCEPTION_POINTERS ptr)
{
    /* only filter c++ exceptions */
    if (ex != CXX_EXCEPTION) return EXCEPTION_CONTINUE_SEARCH;
    return _XcptFilter( ex, ptr );
}

606 607 608
/*********************************************************************
 *		__CxxDetectRethrow (MSVCRT.@)
 */
609
BOOL CDECL __CxxDetectRethrow(PEXCEPTION_POINTERS ptrs)
610 611 612 613 614 615 616 617 618 619
{
  PEXCEPTION_RECORD rec;

  if (!ptrs)
    return FALSE;

  rec = ptrs->ExceptionRecord;

  if (rec->ExceptionCode == CXX_EXCEPTION &&
      rec->NumberParameters == 3 &&
620
      rec->ExceptionInformation[0] == CXX_FRAME_MAGIC_VC6 &&
621 622 623 624 625 626 627 628 629 630 631
      rec->ExceptionInformation[2])
  {
    ptrs->ExceptionRecord = msvcrt_get_thread_data()->exc_record;
    return TRUE;
  }
  return (msvcrt_get_thread_data()->exc_record == rec);
}

/*********************************************************************
 *		__CxxQueryExceptionSize (MSVCRT.@)
 */
632
unsigned int CDECL __CxxQueryExceptionSize(void)
633 634 635
{
  return sizeof(cxx_exception_type);
}
636

637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052

/*********************************************************************
 *		_EH_prolog (MSVCRT.@)
 */

/* Provided for VC++ binary compatibility only */
__ASM_GLOBAL_FUNC(_EH_prolog,
                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")  /* skip ret addr */
                  "pushl $-1\n\t"
                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                  "pushl %eax\n\t"
                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                  "pushl %fs:0\n\t"
                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                  "movl  %esp, %fs:0\n\t"
                  "movl  12(%esp), %eax\n\t"
                  "movl  %ebp, 12(%esp)\n\t"
                  "leal  12(%esp), %ebp\n\t"
                  "pushl %eax\n\t"
                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                  "ret")

static const SCOPETABLE_V4 *get_scopetable_v4( MSVCRT_EXCEPTION_FRAME *frame, ULONG_PTR cookie )
{
    return (const SCOPETABLE_V4 *)((ULONG_PTR)frame->scopetable ^ cookie);
}

static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec,
                                   EXCEPTION_REGISTRATION_RECORD* frame,
                                   PCONTEXT context,
                                   EXCEPTION_REGISTRATION_RECORD** dispatch)
{
  if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
    return ExceptionContinueSearch;
  *dispatch = frame;
  return ExceptionCollidedUnwind;
}

static void msvcrt_local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp)
{
  EXCEPTION_REGISTRATION_RECORD reg;

  TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel);

  /* Register a handler in case of a nested exception */
  reg.Handler = MSVCRT_nested_handler;
  reg.Prev = NtCurrentTeb()->Tib.ExceptionList;
  __wine_push_frame(&reg);

  while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel)
  {
      int level = frame->trylevel;
      frame->trylevel = frame->scopetable[level].previousTryLevel;
      if (!frame->scopetable[level].lpfnFilter)
      {
          TRACE( "__try block cleanup level %d handler %p ebp %p\n",
                 level, frame->scopetable[level].lpfnHandler, ebp );
          call_unwind_func( frame->scopetable[level].lpfnHandler, ebp );
      }
  }
  __wine_pop_frame(&reg);
  TRACE("unwound OK\n");
}

static void msvcrt_local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp )
{
    EXCEPTION_REGISTRATION_RECORD reg;
    const SCOPETABLE_V4 *scopetable = get_scopetable_v4( frame, *cookie );

    TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel);

    /* Register a handler in case of a nested exception */
    reg.Handler = MSVCRT_nested_handler;
    reg.Prev = NtCurrentTeb()->Tib.ExceptionList;
    __wine_push_frame(&reg);

    while (frame->trylevel != -2 && frame->trylevel != trylevel)
    {
        int level = frame->trylevel;
        frame->trylevel = scopetable->entries[level].previousTryLevel;
        if (!scopetable->entries[level].lpfnFilter)
        {
            TRACE( "__try block cleanup level %d handler %p ebp %p\n",
                   level, scopetable->entries[level].lpfnHandler, ebp );
            call_unwind_func( scopetable->entries[level].lpfnHandler, ebp );
        }
    }
    __wine_pop_frame(&reg);
    TRACE("unwound OK\n");
}

/*******************************************************************
 *		_local_unwind2 (MSVCRT.@)
 */
void CDECL _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel)
{
    msvcrt_local_unwind2( frame, trylevel, &frame->_ebp );
}

/*******************************************************************
 *		_local_unwind4 (MSVCRT.@)
 */
void CDECL _local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel )
{
    msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp );
}

/*******************************************************************
 *		_global_unwind2 (MSVCRT.@)
 */
void CDECL _global_unwind2(EXCEPTION_REGISTRATION_RECORD* frame)
{
    TRACE("(%p)\n",frame);
    RtlUnwind( frame, 0, 0, 0 );
}

/*********************************************************************
 *		_except_handler2 (MSVCRT.@)
 */
int CDECL _except_handler2(PEXCEPTION_RECORD rec,
                           EXCEPTION_REGISTRATION_RECORD* frame,
                           PCONTEXT context,
                           EXCEPTION_REGISTRATION_RECORD** dispatcher)
{
  FIXME("exception %x flags=%x at %p handler=%p %p %p stub\n",
        rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
        frame->Handler, context, dispatcher);
  return ExceptionContinueSearch;
}

/*********************************************************************
 *		_except_handler3 (MSVCRT.@)
 */
int CDECL _except_handler3(PEXCEPTION_RECORD rec,
                           MSVCRT_EXCEPTION_FRAME* frame,
                           PCONTEXT context, void* dispatcher)
{
  int retval, trylevel;
  EXCEPTION_POINTERS exceptPtrs;
  PSCOPETABLE pScopeTable;

  TRACE("exception %x flags=%x at %p handler=%p %p %p semi-stub\n",
        rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
        frame->handler, context, dispatcher);

  __asm__ __volatile__ ("cld");

  if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
  {
    /* Unwinding the current frame */
    msvcrt_local_unwind2(frame, TRYLEVEL_END, &frame->_ebp);
    TRACE("unwound current frame, returning ExceptionContinueSearch\n");
    return ExceptionContinueSearch;
  }
  else
  {
    /* Hunting for handler */
    exceptPtrs.ExceptionRecord = rec;
    exceptPtrs.ContextRecord = context;
    *((DWORD *)frame-1) = (DWORD)&exceptPtrs;
    trylevel = frame->trylevel;
    pScopeTable = frame->scopetable;

    while (trylevel != TRYLEVEL_END)
    {
      TRACE( "level %d prev %d filter %p\n", trylevel, pScopeTable[trylevel].previousTryLevel,
             pScopeTable[trylevel].lpfnFilter );
      if (pScopeTable[trylevel].lpfnFilter)
      {
        retval = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp );

        TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ?
              "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ?
              "EXECUTE_HANDLER" : "CONTINUE_SEARCH");

        if (retval == EXCEPTION_CONTINUE_EXECUTION)
          return ExceptionContinueExecution;

        if (retval == EXCEPTION_EXECUTE_HANDLER)
        {
          /* Unwind all higher frames, this one will handle the exception */
          _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)frame);
          msvcrt_local_unwind2(frame, trylevel, &frame->_ebp);

          /* Set our trylevel to the enclosing block, and call the __finally
           * code, which won't return
           */
          frame->trylevel = pScopeTable[trylevel].previousTryLevel;
          TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler);
          call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp);
          ERR("Returned from __finally block - expect crash!\n");
       }
      }
      trylevel = pScopeTable[trylevel].previousTryLevel;
    }
  }
  TRACE("reached TRYLEVEL_END, returning ExceptionContinueSearch\n");
  return ExceptionContinueSearch;
}

/*********************************************************************
 *		_except_handler4_common (MSVCRT.@)
 */
int CDECL _except_handler4_common( ULONG *cookie, void (*check_cookie)(void),
                                   EXCEPTION_RECORD *rec, MSVCRT_EXCEPTION_FRAME *frame,
                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
{
    int retval, trylevel;
    EXCEPTION_POINTERS exceptPtrs;
    const SCOPETABLE_V4 *scope_table = get_scopetable_v4( frame, *cookie );

    TRACE( "exception %x flags=%x at %p handler=%p %p %p cookie=%x scope table=%p cookies=%d/%x,%d/%x\n",
           rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
           frame->handler, context, dispatcher, *cookie, scope_table,
           scope_table->gs_cookie_offset, scope_table->gs_cookie_xor,
           scope_table->eh_cookie_offset, scope_table->eh_cookie_xor );

    /* FIXME: no cookie validation yet */

    if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        /* Unwinding the current frame */
        msvcrt_local_unwind4( cookie, frame, -2, &frame->_ebp );
        TRACE("unwound current frame, returning ExceptionContinueSearch\n");
        return ExceptionContinueSearch;
    }
    else
    {
        /* Hunting for handler */
        exceptPtrs.ExceptionRecord = rec;
        exceptPtrs.ContextRecord = context;
        *((DWORD *)frame-1) = (DWORD)&exceptPtrs;
        trylevel = frame->trylevel;

        while (trylevel != -2)
        {
            TRACE( "level %d prev %d filter %p\n", trylevel,
                   scope_table->entries[trylevel].previousTryLevel,
                   scope_table->entries[trylevel].lpfnFilter );
            if (scope_table->entries[trylevel].lpfnFilter)
            {
                retval = call_filter( scope_table->entries[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp );

                TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ?
                      "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ?
                      "EXECUTE_HANDLER" : "CONTINUE_SEARCH");

                if (retval == EXCEPTION_CONTINUE_EXECUTION)
                    return ExceptionContinueExecution;

                if (retval == EXCEPTION_EXECUTE_HANDLER)
                {
                    /* Unwind all higher frames, this one will handle the exception */
                    _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)frame);
                    msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp );

                    /* Set our trylevel to the enclosing block, and call the __finally
                     * code, which won't return
                     */
                    frame->trylevel = scope_table->entries[trylevel].previousTryLevel;
                    TRACE("__finally block %p\n",scope_table->entries[trylevel].lpfnHandler);
                    call_finally_block(scope_table->entries[trylevel].lpfnHandler, &frame->_ebp);
                    ERR("Returned from __finally block - expect crash!\n");
                }
            }
            trylevel = scope_table->entries[trylevel].previousTryLevel;
        }
    }
    TRACE("reached -2, returning ExceptionContinueSearch\n");
    return ExceptionContinueSearch;
}


/*
 * setjmp/longjmp implementation
 */

#define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */
typedef void (__stdcall *MSVCRT_unwind_function)(const struct MSVCRT___JUMP_BUFFER *);

/* define an entrypoint for setjmp/setjmp3 that stores the registers in the jmp buf */
/* and then jumps to the C backend function */
#define DEFINE_SETJMP_ENTRYPOINT(name) \
    __ASM_GLOBAL_FUNC( name, \
                       "movl 4(%esp),%ecx\n\t"   /* jmp_buf */      \
                       "movl %ebp,0(%ecx)\n\t"   /* jmp_buf.Ebp */  \
                       "movl %ebx,4(%ecx)\n\t"   /* jmp_buf.Ebx */  \
                       "movl %edi,8(%ecx)\n\t"   /* jmp_buf.Edi */  \
                       "movl %esi,12(%ecx)\n\t"  /* jmp_buf.Esi */  \
                       "movl %esp,16(%ecx)\n\t"  /* jmp_buf.Esp */  \
                       "movl 0(%esp),%eax\n\t"                      \
                       "movl %eax,20(%ecx)\n\t"  /* jmp_buf.Eip */  \
                       "jmp " __ASM_NAME("__regs_") # name )

/* restore the registers from the jmp buf upon longjmp */
extern void DECLSPEC_NORETURN longjmp_set_regs( struct MSVCRT___JUMP_BUFFER *jmp, int retval );
__ASM_GLOBAL_FUNC( longjmp_set_regs,
                   "movl 4(%esp),%ecx\n\t"   /* jmp_buf */
                   "movl 8(%esp),%eax\n\t"   /* retval */
                   "movl 0(%ecx),%ebp\n\t"   /* jmp_buf.Ebp */
                   "movl 4(%ecx),%ebx\n\t"   /* jmp_buf.Ebx */
                   "movl 8(%ecx),%edi\n\t"   /* jmp_buf.Edi */
                   "movl 12(%ecx),%esi\n\t"  /* jmp_buf.Esi */
                   "movl 16(%ecx),%esp\n\t"  /* jmp_buf.Esp */
                   "addl $4,%esp\n\t"        /* get rid of return address */
                   "jmp *20(%ecx)\n\t"       /* jmp_buf.Eip */ )

/*
 * The signatures of the setjmp/longjmp functions do not match that
 * declared in the setjmp header so they don't follow the regular naming
 * convention to avoid conflicts.
 */

/*******************************************************************
 *		_setjmp (MSVCRT.@)
 */
DEFINE_SETJMP_ENTRYPOINT(MSVCRT__setjmp)
int CDECL __regs_MSVCRT__setjmp(struct MSVCRT___JUMP_BUFFER *jmp)
{
    jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList;
    if (jmp->Registration == ~0UL)
        jmp->TryLevel = TRYLEVEL_END;
    else
        jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel;

    TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n",
          jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration );
    return 0;
}

/*******************************************************************
 *		_setjmp3 (MSVCRT.@)
 */
DEFINE_SETJMP_ENTRYPOINT( MSVCRT__setjmp3 )
int CDECL __regs_MSVCRT__setjmp3(struct MSVCRT___JUMP_BUFFER *jmp, int nb_args, ...)
{
    jmp->Cookie = MSVCRT_JMP_MAGIC;
    jmp->UnwindFunc = 0;
    jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList;
    if (jmp->Registration == ~0UL)
    {
        jmp->TryLevel = TRYLEVEL_END;
    }
    else
    {
        int i;
        va_list args;

        va_start( args, nb_args );
        if (nb_args > 0) jmp->UnwindFunc = va_arg( args, unsigned long );
        if (nb_args > 1) jmp->TryLevel = va_arg( args, unsigned long );
        else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel;
        for (i = 0; i < 6 && i < nb_args - 2; i++)
            jmp->UnwindData[i] = va_arg( args, unsigned long );
        va_end( args );
    }

    TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n",
          jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration );
    return 0;
}

/*********************************************************************
 *		longjmp (MSVCRT.@)
 */
void CDECL MSVCRT_longjmp(struct MSVCRT___JUMP_BUFFER *jmp, int retval)
{
    unsigned long cur_frame = 0;

    TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx retval=%08x\n",
          jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration, retval );

    cur_frame=(unsigned long)NtCurrentTeb()->Tib.ExceptionList;
    TRACE("cur_frame=%lx\n",cur_frame);

    if (cur_frame != jmp->Registration)
        _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)jmp->Registration);

    if (jmp->Registration)
    {
        if (!IsBadReadPtr(&jmp->Cookie, sizeof(long)) &&
            jmp->Cookie == MSVCRT_JMP_MAGIC && jmp->UnwindFunc)
        {
            MSVCRT_unwind_function unwind_func;

            unwind_func=(MSVCRT_unwind_function)jmp->UnwindFunc;
            unwind_func(jmp);
        }
        else
            msvcrt_local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration,
                                 jmp->TryLevel, (void *)jmp->Ebp);
    }

    if (!retval)
        retval = 1;

    longjmp_set_regs( jmp, retval );
}

/*********************************************************************
 *		_seh_longjmp_unwind (MSVCRT.@)
 */
void __stdcall _seh_longjmp_unwind(struct MSVCRT___JUMP_BUFFER *jmp)
{
    msvcrt_local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel, (void *)jmp->Ebp );
}

/*********************************************************************
 *		_seh_longjmp_unwind4 (MSVCRT.@)
 */
void __stdcall _seh_longjmp_unwind4(struct MSVCRT___JUMP_BUFFER *jmp)
{
    msvcrt_local_unwind4( (void *)jmp->Cookie, (MSVCRT_EXCEPTION_FRAME *)jmp->Registration,
                          jmp->TryLevel, (void *)jmp->Ebp );
}

1053
#endif  /* __i386__ */