Fixture 136

cpp exception unwinding

C++ · 3 functions · 4 lanes · 0 of 12 function-lanes behave identically

4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/3), clang-O2 (0/3), gcc-O0 (0/3), gcc-O2 (0/3).

Throwing runs the personality routine over .eh_frame / .gcc_except_table, selecting a landing pad by the thrown type and running every destructor between throw and catch. None of that control flow appears in the ordinary CFG: the unwind edges live in metadata, and the cleanup calls are only reachable through them.

tests/decompiler_fixtures/src/136_cpp_exception_unwinding.cpp source
/* 136_cpp_exception_unwinding.cpp
 *
 * Throwing runs the personality routine over .eh_frame / .gcc_except_table,
 * selecting a landing pad by the thrown type and running every destructor
 * between throw and catch. None of that control flow appears in the ordinary
 * CFG: the unwind edges live in metadata, and the cleanup calls are only
 * reachable through them.
 */
#include <stdint.h>

namespace {

struct Marker {
    int32_t *log;
    int32_t slot;
    int32_t value;
    Marker(int32_t *l, int32_t s, int32_t v) : log(l), slot(s), value(v) {}
    ~Marker() { log[slot] = value; }
};

struct SmallError {
    int32_t code;
};

struct LargeError {
    int32_t code;
    int32_t detail[4];
};

int32_t may_throw(int32_t selector) {
    if (selector == 1) {
        SmallError error;
        error.code = 7;
        throw error;
    }
    if (selector == 2) {
        LargeError error;
        error.code = 9;
        error.detail[0] = 1;
        error.detail[1] = 2;
        error.detail[2] = 3;
        error.detail[3] = 4;
        throw error;
    }
    if (selector == 3) {
        throw 42; /* a plain int */
    }
    return selector * 100;
}

}  // namespace

extern "C" int32_t cpp_catch_by_type(int32_t selector) {
    try {
        return may_throw(selector & 3);
    } catch (const SmallError &error) {
        return 1000 + error.code;
    } catch (const LargeError &error) {
        return 2000 + error.code + error.detail[3];
    } catch (int value) {
        return 3000 + value;
    }
}

extern "C" int32_t cpp_destructors_run_while_unwinding(int32_t *log,
                                                       int32_t selector) {
    if (log == 0) {
        return -1;
    }
    log[0] = 0;
    log[1] = 0;
    log[2] = 0;
    try {
        Marker outer(log, 0, 11);
        {
            Marker inner(log, 1, 22);
            may_throw(selector & 3);
            log[2] = 33; /* skipped when may_throw actually throws */
        }
    } catch (...) {
        /* Both markers have already been destroyed, innermost first. */
        return log[0] * 100 + log[1] * 10 + log[2];
    }
    return log[0] * 100 + log[1] * 10 + log[2];
}

extern "C" int32_t cpp_rethrow_and_nest(int32_t selector) {
    try {
        try {
            may_throw(selector & 3);
        } catch (const SmallError &) {
            throw; /* rethrow the in-flight exception */
        } catch (int value) {
            throw SmallError{value}; /* replace it with a different type */
        }
    } catch (const SmallError &error) {
        return 500 + error.code;
    } catch (...) {
        return 600;
    }
    return 0;
}

Recovered C

Generated by glaurung decompile --style decbench at b47f6b43. baseline.json records the result after recompiling the C and calling it beside the original with seeded inputs.

clang -O0

0/3
cpp_catch_by_type fail 14 lines
// glaurung: cpp_catch_by_type @ 0x1170
int32_t cpp_catch_by_type(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    int value;
    int local_2c;
    int local_4;
    int var2;
    // x86-64 prologue: save rbp, frame 48 bytes
    var2 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3))));
    local_2c = var2;
    local_4 = local_2c;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
cpp_destructors_run_while_unwinding fail 25 lines
// glaurung: cpp_destructors_run_while_unwinding @ 0x1340
struct Marker;
__attribute__((no_stack_protector)) int32_t cpp_destructors_run_while_unwinding(int32_t * arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_16MarkerC2EPiii(char *, int *, int, int);
    extern void _ZN12_GLOBAL__N_16MarkerD2Ev(struct Marker *);
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    unsigned char local_28[16];
    unsigned char local_48[16];
    int var6;
    // x86-64 prologue: save rbp, frame 80 bytes
    if ((arg0 != 0)) {
        *(int *)((long)arg0) = 0;
        *(int *)(((long)arg0 + 0x4)) = 0;
        *(int *)(((long)arg0 + 0x8)) = 0;
        _ZN12_GLOBAL__N_16MarkerC2EPiii((char *)(&local_28[0]), (int *)(arg0), 0, 11);
        _ZN12_GLOBAL__N_16MarkerC2EPiii((char *)(&local_48[0]), (int *)(arg0), 1, 22);
        var6 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & 3))));
        *(int *)(((long)arg0 + 0x8)) = 33;
        ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_48[0]));
        ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_28[0]));
        return (unsigned int)(((unsigned long)((unsigned int)(((*(int *)((long)arg0) * 100) + (*(int *)(((long)arg0 + 0x4)) * 10)))) + *(int *)(((long)arg0 + 0x8))));
    } else {
        return (unsigned int)(-1);
    }
}
cpp_rethrow_and_nest fail 33 lines
// glaurung: cpp_rethrow_and_nest @ 0x14d0
__attribute__((no_stack_protector)) int32_t cpp_rethrow_and_nest(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_begin_catch(long);
    extern long __cxa_end_catch(void);
    int value;
    long local_10;
    int local_14;
    unsigned char local_28[4];
    int local_4;
    int var2;
    long var41;
    long var47;
    long var49;
    long var51;
    // x86-64 prologue: save rbp, frame 48 bytes
    var2 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3))));
    local_4 = 0;
    L_15f7: ;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
    if (((unsigned long)((unsigned int)(local_14)) == 3)) {
        var41 = __cxa_begin_catch(local_10);
        *(long *)(&local_28[0]) = var41;
        local_4 = ((unsigned int)(*(int *)(*(char *)(&local_28[0]))) + 500);
        var47 = __cxa_end_catch();
        goto L_15f7;
    }
    var49 = __cxa_begin_catch(local_10);
    local_4 = 600;
    var51 = __cxa_end_catch();
    goto L_15f7;
}

clang -O2

0/3
cpp_catch_by_type fail 15 lines
// glaurung: cpp_catch_by_type @ 0x1160
int32_t cpp_catch_by_type(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_end_catch(void);
    int var1;
    long var20;
    long var3;
    var1 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg0 & 3))));
    var3 = (unsigned long)((unsigned int)(var1));
    L_116b: ;
    // x86-64 epilogue: tear down frame
    return (unsigned int)(var3);
    var20 = __cxa_end_catch();
    goto L_116b;
}
cpp_destructors_run_while_unwinding fail 18 lines
// glaurung: cpp_destructors_run_while_unwinding @ 0x1230
int32_t cpp_destructors_run_while_unwinding(int32_t * arg0, int32_t arg1) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    long var1;
    int var4;
    // x86-64 prologue: save callee registers, frame 24 bytes
    if ((arg0 == 0)) {
        return 0xffffffff;
    } else {
        var1 = (long)arg0;
        *(long *)(((long)arg0)) = 0;
        *(int *)(((long)arg0 + 0x8)) = 0;
        var4 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg1 & 3))));
        *(int *)((var1 + 0x8)) = 33;
        *(long *)((var1)) = 0x160000000b;
        return 1353;
    }
}
cpp_rethrow_and_nest fail 30 lines
// glaurung: cpp_rethrow_and_nest @ 0x12b0
int32_t cpp_rethrow_and_nest(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_begin_catch(long);
    extern long __cxa_end_catch(void);
    int value;
    int var11;
    long var12;
    long var13;
    long var16;
    long var2;
    int var3;
    long var5;
    long var7;
    // x86-64 prologue: save callee registers, frame 40 bytes
    var2 = 0;
    var3 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg0 & 3))));
    L_12c1: ;
    // x86-64 epilogue: restore callee registers
    return (unsigned int)(var2);
    var11 = 3;
    var12 = __cxa_begin_catch(var7);
    var13 = var12;
    var2 = 600;
    if (((unsigned int)(var5) == (unsigned int)(var11))) {
        var2 = (unsigned long)((unsigned int)((500 + *(int *)((var13)))));
    }
    var16 = __cxa_end_catch();
    goto L_12c1;
}

gcc -O0

0/3
cpp_catch_by_type fail 10 lines
// glaurung: cpp_catch_by_type @ 0x1337
int32_t cpp_catch_by_type(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    int value;
    int var3;
    // x86-64 prologue: save rbp, frame 8 bytes
    var3 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3))));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(var3);
}
cpp_destructors_run_while_unwinding fail 60 lines
// glaurung: cpp_destructors_run_while_unwinding @ 0x13f0
struct Marker;
int32_t cpp_destructors_run_while_unwinding(int32_t * arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_16MarkerC2EPiii(char *, int *, int, int);
    extern void _ZN12_GLOBAL__N_16MarkerD2Ev(const struct Marker *);
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_begin_catch(long);
    extern long __cxa_end_catch(void);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    long local_18;
    unsigned char local_30[16];
    unsigned char local_40[16];
    long ret;
    int var13;
    long var14;
    long var24;
    int var28;
    long var36;
    long var40;
    long var44;
    long var51;
    int var55;
    long var63;
    // x86-64 prologue: save rbp, frame 8 bytes
    local_18 = (long)(0x28);
    if ((arg0 != 0)) {
        *(int *)((long)arg0) = 0;
        *(int *)((arg0 + 1)) = 0;
        *(int *)((arg0 + 2)) = 0;
        _ZN12_GLOBAL__N_16MarkerC2EPiii((char *)(&local_40[0]), (int *)(arg0), 0, 11);
        _ZN12_GLOBAL__N_16MarkerC2EPiii((char *)(&local_30[0]), (int *)(arg0), 1, 22);
        var13 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & 3))));
        var14 = (unsigned long)((unsigned int)(var13));
        *(int *)((arg0 + 2)) = 33;
        ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_30[0]));
        ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_40[0]));
        var24 = (unsigned long)((unsigned int)(*(int *)((arg0 + 1))));
        var28 = ((unsigned int)(((unsigned long)((unsigned int)(var24)) << 2)) + var24);
        var36 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((unsigned long)((unsigned int)(*(int *)((long)arg0))) * 100) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var28)) + (unsigned long)((unsigned int)(var28)))))))) + (unsigned long)((unsigned int)(*(int *)((arg0 + 2)))))));
    } else {
        var36 = 0xffffffff;
    }
    L_14dd: ;
    ret = (unsigned long)((unsigned int)(var36));
    if ((local_18 == 0x28)) {
        // x86-64 epilogue: restore rbp
        return ret;
    }
    __stack_chk_fail();
    // __glaurung_eh_landing_14f0
    var40 = var14;
    ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_30[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_16MarkerD2Ev)((char *)(&local_40[0]));
    var44 = __cxa_begin_catch(var40);
    var51 = (unsigned long)((unsigned int)(*(int *)((arg0 + 1))));
    var55 = ((unsigned int)(((unsigned long)((unsigned int)(var51)) << 2)) + var51);
    var36 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((unsigned long)((unsigned int)(*(int *)((long)arg0))) * 100) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var55)) + (unsigned long)((unsigned int)(var55)))))))) + (unsigned long)((unsigned int)(*(int *)((arg0 + 2)))))));
    var63 = __cxa_end_catch();
    goto L_14dd;
}
cpp_rethrow_and_nest fail 34 lines
// glaurung: cpp_rethrow_and_nest @ 0x1558
int32_t cpp_rethrow_and_nest(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_begin_catch(long);
    extern long __cxa_end_catch(void);
    int value;
    long local_18;
    int var3;
    long var33;
    long var37;
    long var39;
    long var4;
    long var41;
    long var5;
    long var8;
    // x86-64 prologue: save rbp, frame 8 bytes
    var3 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3))));
    var4 = (unsigned long)((unsigned int)(var3));
    var5 = 0;
    L_157c: ;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(var5);
    if ((var8 == 1)) {
        var33 = __cxa_begin_catch(var4);
        local_18 = var33;
        var5 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(local_18))) + 500)));
        var37 = __cxa_end_catch();
        goto L_157c;
    }
    var39 = __cxa_begin_catch(var4);
    var5 = 600;
    var41 = __cxa_end_catch();
    goto L_157c;
}

gcc -O2

0/3
cpp_catch_by_type fail 28 lines
// glaurung: cpp_catch_by_type @ 0x13b0
int32_t cpp_catch_by_type(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_begin_catch(void);
    extern long __cxa_end_catch(void);
    int value;
    long ret;
    int stack_0;
    int var1;
    long var12;
    long var17;
    long var19;
    long var24;
    var1 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg0 & 3))));
    ret = (unsigned long)((unsigned int)(var1));
    L_13c0: ;
    return ret;
    var12 = __cxa_begin_catch();
    stack_0 = ((unsigned int)(*(int *)((var12))) + 1000);
    var17 = __cxa_end_catch();
    ret = (unsigned long)((unsigned int)(stack_0));
    goto L_13c0;
    var19 = __cxa_begin_catch();
    stack_0 = (((unsigned int)(*(int *)((var19))) + (unsigned int)(*(int *)((var19 + 0x10)))) + 2000);
    var24 = __cxa_end_catch();
    ret = (unsigned long)((unsigned int)(stack_0));
    goto L_13c0;
}
cpp_destructors_run_while_unwinding fail 17 lines
// glaurung: cpp_destructors_run_while_unwinding @ 0x13e0
int32_t cpp_destructors_run_while_unwinding(int32_t * arg0, int32_t arg1) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    long var2;
    int var3;
    if ((arg0 == 0)) {
        return 0xffffffff;
    }
    var2 = (long)arg0;
    *(long *)(((long)arg0)) = 0;
    *(int *)(((long)arg0 + 0x8)) = 0;
    var3 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg1 & 3))));
    *(int *)((var2 + 0x8)) = 33;
    *(long *)((var2)) = 0x160000000b;
    // x86-64 epilogue: tear down frame
    return 1353;
}
cpp_rethrow_and_nest fail 57 lines
// glaurung: cpp_rethrow_and_nest @ 0x1440
int32_t cpp_rethrow_and_nest(int32_t arg0) {
    extern int _ZN12_GLOBAL__N_19may_throwEi(int);
    extern long __cxa_allocate_exception(int);
    extern long __cxa_begin_catch(void);
    extern long __cxa_end_catch(void);
    extern __attribute__((noreturn)) long __cxa_rethrow(void);
    extern __attribute__((noreturn)) long __cxa_throw(long, long, int);
    int value;
    long ret;
    int stack_0;
    int var1;
    long var10;
    long var15;
    long var17;
    long var19;
    long var21;
    long var24;
    long var27;
    long var29;
    long var30;
    long var33;
    long var35;
    long var4;
    long var8;
    var1 = _ZN12_GLOBAL__N_19may_throwEi((unsigned long)((unsigned int)((arg0 & 3))));
    ret = 0;
    L_1454: ;
    // x86-64 epilogue: tear down frame
    return ret;
    L_1233: ;
    if ((var8 != 1)) {
        goto L_12a2;
    }
    var10 = __cxa_begin_catch();
    stack_0 = ((unsigned int)(*(int *)((var10))) + 500);
    var15 = __cxa_end_catch();
    ret = (unsigned long)((unsigned int)(stack_0));
    goto L_1454;
    var17 = __cxa_begin_catch();
    var19 = __cxa_rethrow();
    var21 = __cxa_begin_catch();
    value = (unsigned long)((unsigned int)(*(int *)((var21))));
    var24 = __cxa_allocate_exception(4);
    *(int *)((var24)) = value;
    var27 = __cxa_throw(var24, 0x3de8, 0);
    // __glaurung_eh_landing_1285
    var29 = var4;
    var30 = __cxa_end_catch();
    var8 = var29;
    goto L_1233;
    L_12a2: ;
    var33 = __cxa_begin_catch();
    var35 = __cxa_end_catch();
    ret = 600;
    goto L_1454;
}

← 213 fixtures