except_x86_64.c 28.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * C++ exception handling (ver. 4)
 *
 * Copyright 2020 Piotr Caban
 *
 * 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
 */

#ifdef __x86_64__

23
#include <stdarg.h>
24 25
#include <stdlib.h>

26
#include "wine/exception.h"
27
#include "wine/debug.h"
28
#include "cppexcept.h"
29 30 31

WINE_DEFAULT_DEBUG_CHANNEL(seh);

32 33 34 35
#define CXX_EXCEPTION 0xe06d7363

static DWORD fls_index;

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
typedef struct
{
    BYTE header;
    UINT bbt_flags;
    UINT unwind_count;
    UINT unwind_map;
    UINT tryblock_count;
    UINT tryblock_map;
    UINT ip_count;
    UINT ip_map;
    UINT frame;
} cxx_function_descr;
#define FUNC_DESCR_IS_CATCH     0x01
#define FUNC_DESCR_IS_SEPARATED 0x02
#define FUNC_DESCR_BBT          0x04
#define FUNC_DESCR_UNWIND_MAP   0x08
#define FUNC_DESCR_TRYBLOCK_MAP 0x10
#define FUNC_DESCR_EHS          0x20
#define FUNC_DESCR_NO_EXCEPT    0x40
#define FUNC_DESCR_RESERVED     0x80

typedef struct
{
59
    UINT type;
60 61 62 63 64 65 66 67 68 69 70 71
    BYTE *prev;
    UINT handler;
    UINT object;
} unwind_info;

typedef struct
{
    BYTE header;
    UINT flags;
    UINT type_info;
    int offset;
    UINT handler;
72
    UINT ret_addr[2];
73 74 75 76
} catchblock_info;
#define CATCHBLOCK_FLAGS     0x01
#define CATCHBLOCK_TYPE_INFO 0x02
#define CATCHBLOCK_OFFSET    0x04
77
#define CATCHBLOCK_SEPARATED 0x08
78 79 80
#define CATCHBLOCK_RET_ADDR_MASK 0x30
#define CATCHBLOCK_RET_ADDR      0x10
#define CATCHBLOCK_TWO_RET_ADDRS 0x20
81 82 83 84 85

#define TYPE_FLAG_CONST      1
#define TYPE_FLAG_VOLATILE   2
#define TYPE_FLAG_REFERENCE  8

86 87
#define UNWIND_TYPE_NO_HANDLER 0
#define UNWIND_TYPE_DTOR_OBJ   1
88
#define UNWIND_TYPE_DTOR_PTR   2
89 90
#define UNWIND_TYPE_FRAME      3

91 92
#define CONSOLIDATE_UNWIND_PARAMETER_COUNT 10

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
typedef struct
{
    UINT start_level;
    UINT end_level;
    UINT catch_level;
    UINT catchblock_count;
    UINT catchblock;
} tryblock_info;

typedef struct
{
    UINT ip_off; /* relative to start of function or earlier ipmap_info */
    INT state;
} ipmap_info;

108 109 110 111 112 113
typedef struct
{
    cxx_frame_info frame_info;
    BOOL rethrow;
    INT search_state;
    INT unwind_state;
114
    EXCEPTION_RECORD *prev_rec;
115 116 117 118 119 120 121 122 123 124 125 126
} cxx_catch_ctx;

typedef struct
{
    ULONG64 dest_frame;
    ULONG64 orig_frame;
    EXCEPTION_RECORD *seh_rec;
    DISPATCHER_CONTEXT *dispatch;
    const cxx_function_descr *descr;
    int trylevel;
} se_translator_ctx;

127 128 129
static UINT decode_uint(BYTE **b)
{
    UINT ret;
130
    BYTE *p = *b;
131

132
    if ((*p & 1) == 0)
133
    {
134 135
        ret = p[0] >> 1;
        p += 1;
136
    }
137
    else if ((*p & 3) == 1)
138
    {
139 140
        ret = (p[0] >> 2) + (p[1] << 6);
        p += 2;
141
    }
142
    else if ((*p & 7) == 3)
143
    {
144 145
        ret = (p[0] >> 3) + (p[1] << 5) + (p[2] << 13);
        p += 3;
146
    }
147
    else if ((*p & 15) == 7)
148
    {
149 150
        ret = (p[0] >> 4) + (p[1] << 4) + (p[2] << 12) + (p[3] << 20);
        p += 4;
151 152 153 154 155
    }
    else
    {
        FIXME("not implemented - expect crash\n");
        ret = 0;
156
        p += 5;
157 158
    }

159
    *b = p;
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    return ret;
}

static UINT read_rva(BYTE **b)
{
    UINT ret = *(UINT*)(*b);
    *b += sizeof(UINT);
    return ret;
}

static inline void* rva_to_ptr(UINT rva, ULONG64 base)
{
    return rva ? (void*)(base+rva) : NULL;
}

175
static void read_unwind_info(BYTE **b, unwind_info *ui)
176 177 178 179
{
    BYTE *p = *b;

    memset(ui, 0, sizeof(*ui));
180 181 182
    ui->type = decode_uint(b);
    ui->prev = p - (ui->type >> 2);
    ui->type &= 0x3;
183

184
    switch (ui->type)
185
    {
186
    case UNWIND_TYPE_NO_HANDLER:
187
        break;
188
    case UNWIND_TYPE_DTOR_OBJ:
189
        ui->handler = read_rva(b);
190 191 192 193 194 195
        ui->object = decode_uint(b); /* frame offset to object */
        break;
    case UNWIND_TYPE_DTOR_PTR:
        ui->handler = read_rva(b);
        ui->object = decode_uint(b); /* frame offset to pointer to object */
        break;
196 197
    case UNWIND_TYPE_FRAME:
        ui->handler = read_rva(b);
198
        break;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    }
}

static void read_tryblock_info(BYTE **b, tryblock_info *ti, ULONG64 image_base)
{
    BYTE *count, *count_end;

    ti->start_level = decode_uint(b);
    ti->end_level = decode_uint(b);
    ti->catch_level = decode_uint(b);
    ti->catchblock = read_rva(b);

    count = count_end = rva_to_ptr(ti->catchblock, image_base);
    if (count)
    {
        ti->catchblock_count = decode_uint(&count_end);
        ti->catchblock += count_end - count;
    }
    else
    {
        ti->catchblock_count = 0;
    }
}

223
static BOOL read_catchblock_info(BYTE **b, catchblock_info *ci, DWORD func_rva)
224
{
225
    BYTE ret_addr_type;
226 227 228
    memset(ci, 0, sizeof(*ci));
    ci->header = **b;
    (*b)++;
229 230
    if (ci->header & ~(CATCHBLOCK_FLAGS | CATCHBLOCK_TYPE_INFO | CATCHBLOCK_OFFSET |
                CATCHBLOCK_SEPARATED | CATCHBLOCK_RET_ADDR_MASK))
231 232 233 234
    {
        FIXME("unknown header: %x\n", ci->header);
        return FALSE;
    }
235 236 237 238 239 240 241
    ret_addr_type = ci->header & CATCHBLOCK_RET_ADDR_MASK;
    if (ret_addr_type == (CATCHBLOCK_RET_ADDR | CATCHBLOCK_TWO_RET_ADDRS))
    {
        FIXME("unsupported ret addr type.\n");
        return FALSE;
    }

242 243 244 245
    if (ci->header & CATCHBLOCK_FLAGS) ci->flags = decode_uint(b);
    if (ci->header & CATCHBLOCK_TYPE_INFO) ci->type_info = read_rva(b);
    if (ci->header & CATCHBLOCK_OFFSET) ci->offset = decode_uint(b);
    ci->handler = read_rva(b);
246 247 248 249 250 251 252 253 254 255 256 257 258 259
    if (ci->header & CATCHBLOCK_SEPARATED)
    {
        if (ret_addr_type == CATCHBLOCK_RET_ADDR || ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
            ci->ret_addr[0] = read_rva(b);
        if (ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
            ci->ret_addr[1] = read_rva(b);
    }
    else
    {
        if (ret_addr_type == CATCHBLOCK_RET_ADDR || ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
            ci->ret_addr[0] = decode_uint(b) + func_rva;
        if (ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
            ci->ret_addr[1] = decode_uint(b) + func_rva;
    }
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    return TRUE;
}

static void read_ipmap_info(BYTE **b, ipmap_info *ii)
{
    ii->ip_off = decode_uint(b);
    ii->state = (INT)decode_uint(b) - 1;
}

static inline void dump_type(UINT type_rva, ULONG64 base)
{
    const cxx_type_info *type = rva_to_ptr(type_rva, base);

    TRACE("flags %x type %x %s offsets %d,%d,%d size %d copy ctor %x(%p)\n",
            type->flags, type->type_info, dbgstr_type_info(rva_to_ptr(type->type_info, base)),
            type->offsets.this_offset, type->offsets.vbase_descr, type->offsets.vbase_offset,
            type->size, type->copy_ctor, rva_to_ptr(type->copy_ctor, base));
}

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
static void dump_exception_type(const cxx_exception_type *type, ULONG64 base)
{
    const cxx_type_info_table *type_info_table = rva_to_ptr(type->type_info_table, base);
    UINT i;

    TRACE("flags %x destr %x(%p) handler %x(%p) type info %x(%p)\n",
            type->flags, type->destructor, rva_to_ptr(type->destructor, base),
            type->custom_handler, rva_to_ptr(type->custom_handler, base),
            type->type_info_table, type_info_table);
    for (i = 0; i < type_info_table->count; i++)
    {
        TRACE("    %d: ", i);
        dump_type(type_info_table->info[i], base);
    }
}

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
static BOOL validate_cxx_function_descr4(const cxx_function_descr *descr, DISPATCHER_CONTEXT *dispatch)
{
    ULONG64 image_base = dispatch->ImageBase;
    BYTE *unwind_map = rva_to_ptr(descr->unwind_map, image_base);
    BYTE *tryblock_map = rva_to_ptr(descr->tryblock_map, image_base);
    BYTE *ip_map = rva_to_ptr(descr->ip_map, image_base);
    UINT i, j;
    char *ip;

    TRACE("header 0x%x\n", descr->header);
    TRACE("basic block transformations flags: 0x%x\n", descr->bbt_flags);

    TRACE("unwind table: 0x%x(%p) %d\n", descr->unwind_map, unwind_map, descr->unwind_count);
    for (i = 0; i < descr->unwind_count; i++)
    {
        BYTE *entry = unwind_map;
        unwind_info ui;

314
        read_unwind_info(&unwind_map, &ui);
315
        if (ui.prev < (BYTE*)rva_to_ptr(descr->unwind_map, image_base)) ui.prev = NULL;
316 317
        TRACE("    %d (%p): type 0x%x prev %p func 0x%x(%p) object 0x%x\n",
                i, entry, ui.type, ui.prev, ui.handler,
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
                rva_to_ptr(ui.handler, image_base), ui.object);
    }

    TRACE("try table: 0x%x(%p) %d\n", descr->tryblock_map, tryblock_map, descr->tryblock_count);
    for (i = 0; i < descr->tryblock_count; i++)
    {
        tryblock_info ti;
        BYTE *catchblock;

        read_tryblock_info(&tryblock_map, &ti, image_base);
        catchblock = rva_to_ptr(ti.catchblock, image_base);
        TRACE("    %d: start %d end %d catchlevel %d catch 0x%x(%p) %d\n",
                i, ti.start_level, ti.end_level, ti.catch_level,
                ti.catchblock, catchblock, ti.catchblock_count);
        for (j = 0; j < ti.catchblock_count; j++)
        {
            catchblock_info ci;
335 336
            if (!read_catchblock_info(&catchblock, &ci,
                        dispatch->FunctionEntry->BeginAddress)) return FALSE;
337
            TRACE("        %d: header 0x%x offset %d handler 0x%x(%p) "
338
                    "ret addr[0] %#x ret_addr[1] %#x type %#x %s\n", j, ci.header, ci.offset,
339
                    ci.handler, rva_to_ptr(ci.handler, image_base),
340
                    ci.ret_addr[0], ci.ret_addr[1], ci.type_info,
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
                    dbgstr_type_info(rva_to_ptr(ci.type_info, image_base)));
        }
    }

    TRACE("ipmap: 0x%x(%p) %d\n", descr->ip_map, ip_map, descr->ip_count);
    ip = rva_to_ptr(dispatch->FunctionEntry->BeginAddress, image_base);
    for (i = 0; i < descr->ip_count; i++)
    {
        ipmap_info ii;

        read_ipmap_info(&ip_map, &ii);
        ip += ii.ip_off;
        TRACE("    %d: ip offset 0x%x (%p) state %d\n", i, ii.ip_off, ip, ii.state);
    }

    TRACE("establisher frame: %x\n", descr->frame);
    return TRUE;
}

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 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
static inline int ip_to_state4(BYTE *ip_map, UINT count, DISPATCHER_CONTEXT *dispatch, ULONG64 ip)
{
    ULONG64 state_ip;
    ipmap_info ii;
    int ret = -1;
    UINT i;

    state_ip = dispatch->ImageBase + dispatch->FunctionEntry->BeginAddress;
    for (i = 0; i < count; i++)
    {
        read_ipmap_info(&ip_map, &ii);
        state_ip += ii.ip_off;
        if (ip < state_ip) break;
        ret = ii.state;
    }

    TRACE("state %d\n", ret);
    return ret;
}

static const cxx_type_info *find_caught_type(cxx_exception_type *exc_type, ULONG64 exc_base,
                                             const type_info *catch_ti, UINT catch_flags)
{
    const cxx_type_info_table *type_info_table = rva_to_ptr(exc_type->type_info_table, exc_base);
    UINT i;

    for (i = 0; i < type_info_table->count; i++)
    {
        const cxx_type_info *type = rva_to_ptr(type_info_table->info[i], exc_base);
        const type_info *ti = rva_to_ptr(type->type_info, exc_base);

        if (!catch_ti) return type;   /* catch(...) matches any type */
        if (catch_ti != ti)
        {
            if (strcmp( catch_ti->mangled, ti->mangled )) continue;
        }
        /* type is the same, now check the flags */
        if ((exc_type->flags & TYPE_FLAG_CONST) &&
                !(catch_flags & TYPE_FLAG_CONST)) continue;
        if ((exc_type->flags & TYPE_FLAG_VOLATILE) &&
                !(catch_flags & TYPE_FLAG_VOLATILE)) continue;
        return type;  /* it matched */
    }
    return NULL;
}

static inline void copy_exception(void *object, ULONG64 frame, DISPATCHER_CONTEXT *dispatch,
        const catchblock_info *catchblock, const cxx_type_info *type, ULONG64 exc_base)
{
    const type_info *catch_ti = rva_to_ptr(catchblock->type_info, dispatch->ImageBase);
    void **dest = rva_to_ptr(catchblock->offset, frame);

    if (!catch_ti || !catch_ti->mangled[0]) return;
    if (!catchblock->offset) return;

    if (catchblock->flags & TYPE_FLAG_REFERENCE)
    {
        *dest = get_this_pointer(&type->offsets, object);
    }
    else if (type->flags & CLASS_IS_SIMPLE_TYPE)
    {
        memmove(dest, object, type->size);
        /* if it is a pointer, adjust it */
        if (type->size == sizeof(void*)) *dest = get_this_pointer(&type->offsets, *dest);
    }
    else  /* copy the object */
    {
        if (type->copy_ctor)
        {
            if (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS)
            {
                void (__cdecl *copy_ctor)(void*, void*, int) =
                    rva_to_ptr(type->copy_ctor, exc_base);
                copy_ctor(dest, get_this_pointer(&type->offsets, object), 1);
            }
            else
            {
                void (__cdecl *copy_ctor)(void*, void*) =
                    rva_to_ptr(type->copy_ctor, exc_base);
                copy_ctor(dest, get_this_pointer(&type->offsets, object));
            }
        }
        else
            memmove(dest, get_this_pointer(&type->offsets,object), type->size);
    }
}

static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch,
        const cxx_function_descr *descr, int trylevel, int last_level)
{
450
    void (__cdecl *handler_dtor)(void *obj, ULONG64 frame);
451 452 453 454 455 456 457 458 459 460 461 462 463
    BYTE *unwind_data, *last;
    unwind_info ui;
    void *obj;
    int i;

    if (trylevel == -2)
    {
        trylevel = ip_to_state4(rva_to_ptr(descr->ip_map, dispatch->ImageBase),
                descr->ip_count, dispatch, dispatch->ControlPc);
    }

    TRACE("current level: %d, last level: %d\n", trylevel, last_level);

464
    if (trylevel<-1 || trylevel>=(int)descr->unwind_count)
465 466 467 468 469
    {
        ERR("invalid trylevel %d\n", trylevel);
        terminate();
    }

470 471
    if (trylevel <= last_level) return;

472
    unwind_data = rva_to_ptr(descr->unwind_map, dispatch->ImageBase);
473
    last = unwind_data - 1;
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
    for (i = 0; i < trylevel; i++)
    {
        BYTE *addr = unwind_data;
        read_unwind_info(&unwind_data, &ui);
        if (i == last_level) last = addr;
    }

    while (unwind_data > last)
    {
        read_unwind_info(&unwind_data, &ui);
        unwind_data = ui.prev;

        if (ui.handler)
        {
            handler_dtor = rva_to_ptr(ui.handler, dispatch->ImageBase);
            obj = rva_to_ptr(ui.object, frame);
490 491
            if(ui.type == UNWIND_TYPE_DTOR_PTR)
                obj = *(void**)obj;
492
            TRACE("handler: %p object: %p\n", handler_dtor, obj);
493
            handler_dtor(obj, frame);
494 495 496 497 498 499 500 501 502
        }
    }
}

static LONG CALLBACK cxx_rethrow_filter(PEXCEPTION_POINTERS eptrs, void *c)
{
    EXCEPTION_RECORD *rec = eptrs->ExceptionRecord;
    cxx_catch_ctx *ctx = c;

503 504
    if (rec->ExceptionCode == CXX_EXCEPTION && !rec->ExceptionInformation[1] && !rec->ExceptionInformation[2])
        return EXCEPTION_EXECUTE_HANDLER;
505

506
    FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx->search_state);
507 508
    if (rec->ExceptionCode != CXX_EXCEPTION)
        return EXCEPTION_CONTINUE_SEARCH;
509
    if (rec->ExceptionInformation[1] == ctx->prev_rec->ExceptionInformation[1])
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
        ctx->rethrow = TRUE;
    return EXCEPTION_CONTINUE_SEARCH;
}

static void CALLBACK cxx_catch_cleanup(BOOL normal, void *c)
{
    cxx_catch_ctx *ctx = c;
    __CxxUnregisterExceptionObject(&ctx->frame_info, ctx->rethrow);

    FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx->unwind_state);
}

static void* WINAPI call_catch_block4(EXCEPTION_RECORD *rec)
{
    ULONG64 frame = rec->ExceptionInformation[1];
    EXCEPTION_RECORD *prev_rec = (void*)rec->ExceptionInformation[4];
    EXCEPTION_RECORD *untrans_rec = (void*)rec->ExceptionInformation[6];
    CONTEXT *context = (void*)rec->ExceptionInformation[7];
    void* (__cdecl *handler)(ULONG64 unk, ULONG64 rbp) = (void*)rec->ExceptionInformation[5];
    EXCEPTION_POINTERS ep = { prev_rec, context };
    cxx_catch_ctx ctx;
    void *ret_addr = NULL;

    TRACE("calling handler %p\n", handler);

    ctx.rethrow = FALSE;
    __CxxRegisterExceptionObject(&ep, &ctx.frame_info);
    ctx.search_state = rec->ExceptionInformation[2];
    ctx.unwind_state = rec->ExceptionInformation[3];
539
    ctx.prev_rec = prev_rec;
540 541 542 543 544 545 546 547 548
    (*__processing_throw())--;
    __TRY
    {
        __TRY
        {
            ret_addr = handler(0, frame);
        }
        __EXCEPT_CTX(cxx_rethrow_filter, &ctx)
        {
549
            TRACE("detect rethrow: exception code: %lx\n", prev_rec->ExceptionCode);
550
            ctx.rethrow = TRUE;
551
            FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx.search_state);
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569

            if (untrans_rec)
            {
                __DestructExceptionObject(prev_rec);
                RaiseException(untrans_rec->ExceptionCode, untrans_rec->ExceptionFlags,
                        untrans_rec->NumberParameters, untrans_rec->ExceptionInformation);
            }
            else
            {
                RaiseException(prev_rec->ExceptionCode, prev_rec->ExceptionFlags,
                        prev_rec->NumberParameters, prev_rec->ExceptionInformation);
            }
        }
        __ENDTRY
    }
    __FINALLY_CTX(cxx_catch_cleanup, &ctx)

    FlsSetValue(fls_index, (void*)-2);
570 571
    TRACE("handler returned %p, ret_addr[0] %#Ix, ret_addr[1] %#Ix.\n",
          ret_addr, rec->ExceptionInformation[8], rec->ExceptionInformation[9]);
572 573 574 575 576 577 578 579 580 581 582

    if (rec->ExceptionInformation[9])
    {
        if ((ULONG_PTR)ret_addr > 1)
        {
            ERR("unexpected handler result %p.\n", ret_addr);
            abort();
        }
        return (void*)rec->ExceptionInformation[8 + (ULONG_PTR)ret_addr];
    }
    return rec->ExceptionInformation[8] ? (void *)rec->ExceptionInformation[8] : ret_addr;
583 584 585 586
}

static inline BOOL cxx_is_consolidate(const EXCEPTION_RECORD *rec)
{
587 588 589
    return rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE
        && rec->NumberParameters == CONSOLIDATE_UNWIND_PARAMETER_COUNT
        && rec->ExceptionInformation[0] == (ULONG_PTR)call_catch_block4;
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
}

static inline void find_catch_block4(EXCEPTION_RECORD *rec, CONTEXT *context,
        EXCEPTION_RECORD *untrans_rec, ULONG64 frame, DISPATCHER_CONTEXT *dispatch,
        const cxx_function_descr *descr, cxx_exception_type *info,
        ULONG64 orig_frame, int trylevel)
{
    ULONG64 exc_base = (rec->NumberParameters == 4 ? rec->ExceptionInformation[3] : 0);
    int *processing_throw = __processing_throw();
    EXCEPTION_RECORD catch_record;
    BYTE *tryblock_map;
    CONTEXT ctx;
    UINT i, j;

    (*processing_throw)++;

    if (trylevel == -2)
    {
        trylevel = ip_to_state4(rva_to_ptr(descr->ip_map, dispatch->ImageBase),
                descr->ip_count, dispatch, dispatch->ControlPc);
    }
    TRACE("current trylevel: %d\n", trylevel);

    tryblock_map = rva_to_ptr(descr->tryblock_map, dispatch->ImageBase);
    for (i=0; i<descr->tryblock_count; i++)
    {
        tryblock_info tryblock;
        BYTE *catchblock;

        read_tryblock_info(&tryblock_map, &tryblock, dispatch->ImageBase);

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

        /* got a try block */
        catchblock = rva_to_ptr(tryblock.catchblock, dispatch->ImageBase);
        for (j=0; j<tryblock.catchblock_count; j++)
        {
            catchblock_info ci;

630
            read_catchblock_info(&catchblock, &ci, dispatch->FunctionEntry->BeginAddress);
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656

            if (info)
            {
                const cxx_type_info *type = find_caught_type(info, exc_base,
                        rva_to_ptr(ci.type_info, dispatch->ImageBase),
                        ci.flags);
                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((void*)rec->ExceptionInformation[1],
                        orig_frame, dispatch, &ci, type, exc_base);
            }
            else
            {
                /* no CXX_EXCEPTION only proceed with a catch(...) block*/
                if (ci.type_info)
                    continue;
                TRACE("found catch(...) block\n");
            }

            /* unwind stack and call catch */
            memset(&catch_record, 0, sizeof(catch_record));
            catch_record.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
            catch_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
657
            catch_record.NumberParameters = CONSOLIDATE_UNWIND_PARAMETER_COUNT;
658 659 660 661 662 663 664 665 666
            catch_record.ExceptionInformation[0] = (ULONG_PTR)call_catch_block4;
            catch_record.ExceptionInformation[1] = orig_frame;
            catch_record.ExceptionInformation[2] = tryblock.catch_level;
            catch_record.ExceptionInformation[3] = tryblock.start_level;
            catch_record.ExceptionInformation[4] = (ULONG_PTR)rec;
            catch_record.ExceptionInformation[5] =
                (ULONG_PTR)rva_to_ptr(ci.handler, dispatch->ImageBase);
            catch_record.ExceptionInformation[6] = (ULONG_PTR)untrans_rec;
            catch_record.ExceptionInformation[7] = (ULONG_PTR)context;
667
            if (ci.ret_addr[0])
668
            {
669
                catch_record.ExceptionInformation[8] = (ULONG_PTR)rva_to_ptr(
670 671
                        ci.ret_addr[0], dispatch->ImageBase);
            }
672
            if (ci.ret_addr[1])
673
            {
674
                catch_record.ExceptionInformation[9] = (ULONG_PTR)rva_to_ptr(
675 676
                        ci.ret_addr[1], dispatch->ImageBase);
            }
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
            RtlUnwindEx((void*)frame, (void*)dispatch->ControlPc, &catch_record, NULL, &ctx, NULL);
        }
    }

    TRACE("no matching catch block found\n");
    (*processing_throw)--;
}

static LONG CALLBACK se_translation_filter(EXCEPTION_POINTERS *ep, void *c)
{
    se_translator_ctx *ctx = (se_translator_ctx *)c;
    EXCEPTION_RECORD *rec = ep->ExceptionRecord;
    cxx_exception_type *exc_type;

    if (rec->ExceptionCode != CXX_EXCEPTION)
    {
693
        TRACE("non-c++ exception thrown in SEH handler: %lx\n", rec->ExceptionCode);
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
        terminate();
    }

    exc_type = (cxx_exception_type *)rec->ExceptionInformation[2];
    find_catch_block4(rec, ep->ContextRecord, ctx->seh_rec, ctx->dest_frame, ctx->dispatch,
            ctx->descr, exc_type, ctx->orig_frame, ctx->trylevel);

    __DestructExceptionObject(rec);
    return ExceptionContinueSearch;
}

/* Hacky way to obtain se_translator */
static inline _se_translator_function get_se_translator(void)
{
    return __current_exception()[-2];
}

711 712 713 714 715 716 717 718 719 720 721
static void check_noexcept( PEXCEPTION_RECORD rec, const cxx_function_descr *descr )
{
    if (!(descr->header & FUNC_DESCR_IS_CATCH) &&
            rec->ExceptionCode == CXX_EXCEPTION &&
            (descr->header & FUNC_DESCR_NO_EXCEPT))
    {
        ERR("noexcept function propagating exception\n");
        terminate();
    }
}

722 723 724 725 726 727 728 729 730 731 732
static DWORD cxx_frame_handler4(EXCEPTION_RECORD *rec, ULONG64 frame,
        CONTEXT *context, DISPATCHER_CONTEXT *dispatch,
        const cxx_function_descr *descr, int trylevel)
{
    cxx_exception_type *exc_type;
    ULONG64 orig_frame = frame;

    if (descr->header & FUNC_DESCR_IS_CATCH)
    {
        TRACE("nested exception detected\n");
        orig_frame = *(ULONG64*)rva_to_ptr(descr->frame, frame);
733
        TRACE("setting orig_frame to %Ix\n", orig_frame);
734 735 736 737 738 739 740 741 742 743 744 745 746 747
    }

    if (rec->ExceptionFlags & (EH_UNWINDING|EH_EXIT_UNWIND))
    {
        int last_level = -1;
        if ((rec->ExceptionFlags & EH_TARGET_UNWIND) && cxx_is_consolidate(rec))
            last_level = rec->ExceptionInformation[3];
        else if ((rec->ExceptionFlags & EH_TARGET_UNWIND) && rec->ExceptionCode == STATUS_LONGJUMP)
            last_level = ip_to_state4(rva_to_ptr(descr->ip_map, dispatch->ImageBase),
                    descr->ip_count, dispatch, dispatch->TargetIp);

        cxx_local_unwind4(orig_frame, dispatch, descr, trylevel, last_level);
        return ExceptionContinueSearch;
    }
748 749 750 751 752
    if (!descr->tryblock_map)
    {
        check_noexcept(rec, descr);
        return ExceptionContinueSearch;
    }
753 754 755

    if (rec->ExceptionCode == CXX_EXCEPTION)
    {
756 757 758 759 760 761
        if (!rec->ExceptionInformation[1] && !rec->ExceptionInformation[2])
        {
            TRACE("rethrow detected.\n");
            *rec = *(EXCEPTION_RECORD*)*__current_exception();
        }

762 763 764 765
        exc_type = (cxx_exception_type *)rec->ExceptionInformation[2];

        if (TRACE_ON(seh))
        {
766
            TRACE("handling C++ exception rec %p frame %Ix descr %p\n", rec, frame,  descr);
767 768 769 770 771 772 773 774
            dump_exception_type(exc_type, rec->ExceptionInformation[3]);
        }
    }
    else
    {
        _se_translator_function se_translator = get_se_translator();

        exc_type = NULL;
775
        TRACE("handling C exception code %lx rec %p frame %Ix descr %p\n",
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
                rec->ExceptionCode, rec, frame, descr);

        if (se_translator) {
            EXCEPTION_POINTERS except_ptrs;
            se_translator_ctx ctx;

            ctx.dest_frame = frame;
            ctx.orig_frame = orig_frame;
            ctx.seh_rec    = rec;
            ctx.dispatch   = dispatch;
            ctx.descr      = descr;
            ctx.trylevel   = trylevel;
            __TRY
            {
                except_ptrs.ExceptionRecord = rec;
                except_ptrs.ContextRecord = context;
                se_translator(rec->ExceptionCode, &except_ptrs);
            }
            __EXCEPT_CTX(se_translation_filter, &ctx)
            {
            }
            __ENDTRY
        }
    }

    find_catch_block4(rec, context, NULL, frame, dispatch, descr, exc_type, orig_frame, trylevel);
802
    check_noexcept(rec, descr);
803 804 805
    return ExceptionContinueSearch;
}

806 807 808
EXCEPTION_DISPOSITION __cdecl __CxxFrameHandler4(EXCEPTION_RECORD *rec,
        ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatch)
{
809 810
    cxx_function_descr descr;
    BYTE *p, *count, *count_end;
811
    int trylevel;
812

813
    TRACE("%p %Ix %p %p\n", rec, frame, context, dispatch);
814 815 816

    trylevel = (DWORD_PTR)FlsGetValue(fls_index);
    FlsSetValue(fls_index, (void*)-2);
817 818 819 820 821

    memset(&descr, 0, sizeof(descr));
    p = rva_to_ptr(*(UINT*)dispatch->HandlerData, dispatch->ImageBase);
    descr.header = *p++;

822 823 824 825 826 827
    if ((descr.header & FUNC_DESCR_EHS) &&
            rec->ExceptionCode != CXX_EXCEPTION &&
            !cxx_is_consolidate(rec) &&
            rec->ExceptionCode != STATUS_LONGJUMP)
        return ExceptionContinueSearch;  /* handle only c++ exceptions */

828 829 830
    if (descr.header & ~(FUNC_DESCR_IS_CATCH | FUNC_DESCR_IS_SEPARATED |
                FUNC_DESCR_UNWIND_MAP | FUNC_DESCR_TRYBLOCK_MAP | FUNC_DESCR_EHS |
                FUNC_DESCR_NO_EXCEPT))
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
    {
        FIXME("unsupported flags: %x\n", descr.header);
        return ExceptionContinueSearch;
    }

    if (descr.header & FUNC_DESCR_BBT) descr.bbt_flags = decode_uint(&p);
    if (descr.header & FUNC_DESCR_UNWIND_MAP)
    {
        descr.unwind_map = read_rva(&p);
        count_end = count = rva_to_ptr(descr.unwind_map, dispatch->ImageBase);
        descr.unwind_count = decode_uint(&count_end);
        descr.unwind_map += count_end - count;
    }
    if (descr.header & FUNC_DESCR_TRYBLOCK_MAP)
    {
        descr.tryblock_map = read_rva(&p);
        count_end = count = rva_to_ptr(descr.tryblock_map, dispatch->ImageBase);
        descr.tryblock_count = decode_uint(&count_end);
        descr.tryblock_map += count_end - count;
    }
    descr.ip_map = read_rva(&p);
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
    if (descr.header & FUNC_DESCR_IS_SEPARATED)
    {
        UINT i, num, func;
        BYTE *map;

        map = rva_to_ptr(descr.ip_map, dispatch->ImageBase);
        num = decode_uint(&map);
        for (i = 0; i < num; i++)
        {
            func = read_rva(&map);
            descr.ip_map = read_rva(&map);
            if (func == dispatch->FunctionEntry->BeginAddress)
                break;
        }
        if (i == num)
        {
            FIXME("function ip_map not found\n");
            return ExceptionContinueSearch;
        }
    }
872 873 874 875 876 877 878 879
    count_end = count = rva_to_ptr(descr.ip_map, dispatch->ImageBase);
    descr.ip_count = decode_uint(&count_end);
    descr.ip_map += count_end - count;
    if (descr.header & FUNC_DESCR_IS_CATCH) descr.frame = decode_uint(&p);

    if (!validate_cxx_function_descr4(&descr, dispatch))
        return ExceptionContinueSearch;

880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
    return cxx_frame_handler4(rec, frame, context, dispatch, &descr, trylevel);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        fls_index = FlsAlloc(NULL);
        if (fls_index == FLS_OUT_OF_INDEXES)
            return FALSE;
        /* fall through */
    case DLL_THREAD_ATTACH:
        FlsSetValue(fls_index, (void*)-2);
        break;
    case DLL_PROCESS_DETACH:
        if (reserved) break;
        FlsFree(fls_index);
        break;
    }
    return TRUE;
901 902 903
}

#endif