Fixture 160

init and fini

C · 5 functions · 4 lanes · 0 of 20 function-lanes behave identically

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

.init_array / .fini_array are entry points nothing calls. The loader walks the arrays after relocation (init) and at dlclose / exit (fini), so these functions have no incoming call edge anywhere in the object - they are reachable only from a pointer table in a data section, and the table entries are R_X86_64_RELATIVE relocations, not symbols.

That breaks the two assumptions a decompiler usually makes at once. Function discovery driven by call graph reachability drops the constructors as dead code (a real .so's ctors are where the interesting setup lives - this is the standard place packers and droppers hide work, precisely because a naive analysis starts at exported functions). And the globals the constructor writes look, in the static image, like they still hold their .data initialisers, so any recovery that reads initialised values off disk reports the WRONG constant for every read below.

Constructor priorities make the ordering observable: with (101) running before (102), ctor160_order ends at 12 and not 21. Priority lives only in the section name (.init_array.00101), which the linker sorts and then discards, so recovering the order at all means reading the final table order.

Determinism: the constructors run exactly once per load and store fixed values (no counters, no addresses), so every exported function below returns the same answer on every call and on every load. The destructor's effect is observable only after unload, so initfini_witness is 0 for the whole run - what it tests is that the store is not proved dead and the .fini_array entry is still recovered.

tests/decompiler_fixtures/src/160_init_and_fini.c source
#include <stdint.h>

/* .init_array / .fini_array are entry points nothing calls. The loader walks
 * the arrays after relocation (init) and at dlclose / exit (fini), so these
 * functions have no incoming call edge anywhere in the object - they are
 * reachable only from a pointer table in a data section, and the table entries
 * are R_X86_64_RELATIVE relocations, not symbols.
 *
 * That breaks the two assumptions a decompiler usually makes at once. Function
 * discovery driven by call graph reachability drops the constructors as dead
 * code (a real .so's ctors are where the interesting setup lives - this is the
 * standard place packers and droppers hide work, precisely because a naive
 * analysis starts at exported functions). And the globals the constructor
 * writes look, in the static image, like they still hold their .data
 * initialisers, so any recovery that reads initialised values off disk reports
 * the WRONG constant for every read below.
 *
 * Constructor priorities make the ordering observable: with (101) running
 * before (102), ctor160_order ends at 12 and not 21. Priority lives only in the
 * section name (.init_array.00101), which the linker sorts and then discards,
 * so recovering the order at all means reading the final table order.
 *
 * Determinism: the constructors run exactly once per load and store fixed
 * values (no counters, no addresses), so every exported function below returns
 * the same answer on every call and on every load. The destructor's effect is
 * observable only after unload, so initfini_witness is 0 for the whole run -
 * what it tests is that the store is not proved dead and the .fini_array entry
 * is still recovered.
 */

static int32_t ctor160_ready = -1;
static int32_t ctor160_order = 0;
static int32_t ctor160_table[4] = {0, 0, 0, 0};

/* Exported and volatile so the destructor's store cannot be optimised away. */
volatile int32_t initfini_shutdown_witness = 0;

__attribute__((constructor(101))) static void ctor160_early(void) {
    ctor160_order = 1;
}

__attribute__((constructor(102))) static void ctor160_late(void) {
    /* 1 -> 12 if the priorities were honoured; 2 alone if `early` never ran. */
    ctor160_order = ctor160_order * 10 + 2;
}

__attribute__((constructor)) static void ctor160_init(void) {
    int32_t index;
    for (index = 0; index < 4; ++index) {
        ctor160_table[index] = (index + 1) * 10;
    }
    ctor160_ready = 1;
}

__attribute__((destructor)) static void ctor160_fini(void) {
    ctor160_ready = 0;
    initfini_shutdown_witness = 1;
}

/* 1 once the constructor has run; the on-disk initialiser is -1. */
__attribute__((noinline)) int32_t initfini_ready(void) {
    return ctor160_ready;
}

/* 12: priority 101 then priority 102. */
__attribute__((noinline)) int32_t initfini_order(void) {
    return ctor160_order;
}

/* The table is all zeroes in the image and 10/20/30/40 once loaded. */
__attribute__((noinline)) int32_t initfini_table(int32_t index) {
    if (index < 0 || index > 3) {
        return -1;
    }
    return ctor160_table[index];
}

/* 0 while the object is loaded; the destructor sets it during unload, which no
 * call can observe. */
__attribute__((noinline)) int32_t initfini_witness(void) {
    return initfini_shutdown_witness;
}

/* Constructor-initialised data feeding an ordinary bounded loop, so a wrong
 * constant propagated from the .data image shows up as a wrong sum rather than
 * as a missing symbol. */
__attribute__((noinline)) int32_t
initfini_fold(const int32_t *values, int32_t count) {
    uint32_t accumulator = (uint32_t)ctor160_ready;
    int32_t index;
    if (values == 0 || count < 0 || count > 16) {
        return -1;
    }
    for (index = 0; index < count; ++index) {
        accumulator += (uint32_t)values[index] +
                       (uint32_t)ctor160_table[index & 3];
    }
    return (int32_t)accumulator;
}

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/5
initfini_fold structural 31 lines
// glaurung: initfini_fold @ 0x1210
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_fold(const int32_t * arg0, int32_t arg1) {
    extern unsigned char glaurung_global_4020[16];
    unsigned int accumulator;
    int index;
    int local_4;
    // x86-64 prologue: save rbp
    accumulator = *(int *)(&glaurung_global_4020[0]);
    if ((arg0 == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((long)(arg1) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    for (index = 0; (index < arg1); index++) {
        accumulator = ((unsigned int)(((unsigned long)((unsigned int)(arg0[(long)(index)])) + *(int *)((0x4050 + ((long)((int)(((unsigned long)((unsigned int)(index)) & 3))) * 4))))) + accumulator);
    }
    local_4 = accumulator;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
initfini_order structural 8 lines
// glaurung: initfini_order @ 0x11b0
static unsigned char glaurung_global_4044[16] __attribute__((aligned(16)));
int32_t initfini_order(void) {
    extern unsigned char glaurung_global_4044[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(&glaurung_global_4044[0]));
}
initfini_ready structural 8 lines
// glaurung: initfini_ready @ 0x11a0
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_ready(void) {
    extern unsigned char glaurung_global_4020[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(&glaurung_global_4020[0]));
}
initfini_table structural 18 lines
// glaurung: initfini_table @ 0x11c0
int32_t initfini_table(int32_t arg0) {
    int local_4;
    // x86-64 prologue: save rbp
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 3) | ((long)(arg0) < 3)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    local_4 = *(int *)((0x4050 + ((long)(arg0) * 4)));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
initfini_witness fail 8 lines
// glaurung: initfini_witness @ 0x1200
static unsigned char glaurung_global_3fe8[16] __attribute__((aligned(16)));
int32_t initfini_witness(void) {
    extern unsigned char glaurung_global_3fe8[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(*(long *)(&glaurung_global_3fe8[0])));
}

clang -O2

0/5
initfini_fold structural 93 lines
// glaurung: initfini_fold @ 0x11c0
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
static unsigned char glaurung_global_4050[16] __attribute__((aligned(16)));
static unsigned char glaurung_global_4054[16] __attribute__((aligned(16)));
static unsigned char glaurung_global_4058[16] __attribute__((aligned(16)));
static unsigned char glaurung_global_405c[16] __attribute__((aligned(16)));
int32_t initfini_fold(const int32_t * arg0, int32_t arg1) {
    extern unsigned char glaurung_global_4020[16];
    extern unsigned char glaurung_global_4050[16];
    extern unsigned char glaurung_global_4054[16];
    extern unsigned char glaurung_global_4058[16];
    extern unsigned char glaurung_global_405c[16];
    unsigned int accumulator;
    int index;
    long ret;
    long var0;
    long var2;
    int var20;
    int var21;
    int var22;
    int var23;
    long var4;
    long var73;
    long var80;
    long var81;
    int var94;
    var0 = 0xffffffff;
    ret = 0xffffffff;
    if ((arg0 != 0)) {
        ret = var0;
        if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
            return ret;
        }
        accumulator = (unsigned long)((unsigned int)(*(int *)(&glaurung_global_4020[0])));
        ret = (unsigned long)(accumulator);
        if (((unsigned long)((unsigned int)(arg1)) == 0)) {
            return ret;
        }
        var2 = (unsigned long)((unsigned int)(arg1));
        var4 = 0;
        if (((unsigned long)(4) <= (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
            if (((unsigned long)(-4) <= (unsigned long)((var2 - 5)))) {
                var4 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var2)) & -4)));
                var20 = ((*(int *)(((long)arg0)) + accumulator) + *(int *)(&glaurung_global_4050[0]));
                var21 = (*(int *)(((long)arg0 + 0x4)) + *(int *)(&glaurung_global_4054[0]));
                var22 = (*(int *)(((long)arg0 + 0x8)) + *(int *)(&glaurung_global_4058[0]));
                var23 = (*(int *)(((long)arg0 + 0xc)) + *(int *)(&glaurung_global_405c[0]));
                if ((var4 != 4)) {
                    var20 = ((var20 + *(int *)(((long)arg0 + 0x10))) + *(int *)(&glaurung_global_4050[0]));
                    var21 = ((var21 + *(int *)(((long)arg0 + 0x14))) + *(int *)(&glaurung_global_4054[0]));
                    var22 = ((var22 + *(int *)(((long)arg0 + 0x18))) + *(int *)(&glaurung_global_4058[0]));
                    var23 = ((var23 + *(int *)(((long)arg0 + 0x1c))) + *(int *)(&glaurung_global_405c[0]));
                    if (((unsigned long)((unsigned int)(var4)) != 8)) {
                        var20 = ((var20 + *(int *)(((long)arg0 + 0x20))) + *(int *)(&glaurung_global_4050[0]));
                        var21 = ((var21 + *(int *)(((long)arg0 + 0x24))) + *(int *)(&glaurung_global_4054[0]));
                        var22 = ((var22 + *(int *)(((long)arg0 + 0x28))) + *(int *)(&glaurung_global_4058[0]));
                        var23 = ((var23 + *(int *)(((long)arg0 + 0x2c))) + *(int *)(&glaurung_global_405c[0]));
                        if (((unsigned long)((unsigned int)(var4)) != 12)) {
                            var20 = ((*(int *)(((long)arg0 + 0x30)) + var20) + *(int *)(&glaurung_global_4050[0]));
                            var21 = ((*(int *)(((long)arg0 + 0x34)) + var21) + *(int *)(&glaurung_global_4054[0]));
                            var22 = ((*(int *)(((long)arg0 + 0x38)) + var22) + *(int *)(&glaurung_global_4058[0]));
                            var23 = ((*(int *)(((long)arg0 + 0x3c)) + var23) + *(int *)(&glaurung_global_405c[0]));
                        }
                    }
                }
                accumulator = (unsigned long)((unsigned int)(((var23 + var21) + (var22 + var20))));
                ret = (unsigned long)(accumulator);
                if ((var4 == var2)) {
                    return ret;
                }
            }
        }
        var73 = (~var4);
        index = var4;
        if (((unsigned long)((unsigned char)((var2 & 1))) != 0)) {
            accumulator = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((accumulator + *(int *)(((long)arg0 + var4 * 4))))) + *(int *)(&glaurung_global_4050[0]))));
            index = (var4 | 1);
        }
        ret = (unsigned long)(accumulator);
        if (((var73 + var2) == 0)) {
            return ret;
        }
        var80 = (long)(&glaurung_global_4050[0]);
        var81 = (unsigned long)(accumulator);
        do {
            var94 = ((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((var81 + *(int *)(((long)arg0 + index * 4))))) + *(int *)((var80 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(index)) & 3))) * 4)))))) + *(int *)(((long)arg0 + index * 4 + 0x4)))) + *(int *)((var80 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((index + 1))) & 3))) * 4))));
            ret = (unsigned long)((unsigned int)(var94));
            index = (index + 2);
            var81 = (unsigned long)((unsigned int)(var94));
        } while ((var2 != index));
    }
    return ret;
}
initfini_order structural 6 lines
// glaurung: initfini_order @ 0x1180
static unsigned char glaurung_global_4044[16] __attribute__((aligned(16)));
int32_t initfini_order(void) {
    extern unsigned char glaurung_global_4044[16];
    return (unsigned int)(*(int *)(&glaurung_global_4044[0]));
}
initfini_ready structural 6 lines
// glaurung: initfini_ready @ 0x1170
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_ready(void) {
    extern unsigned char glaurung_global_4020[16];
    return (unsigned int)(*(int *)(&glaurung_global_4020[0]));
}
initfini_table structural 9 lines
// glaurung: initfini_table @ 0x1190
int32_t initfini_table(int32_t arg0) {
    long ret;
    ret = 0xffffffff;
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(3))) {
        ret = (unsigned long)((unsigned int)(*(int *)((0x4050 + ((unsigned long)((unsigned int)(arg0)) * 4)))));
    }
    return ret;
}
initfini_witness fail 6 lines
// glaurung: initfini_witness @ 0x11b0
static unsigned char glaurung_global_3fe8[16] __attribute__((aligned(16)));
int32_t initfini_witness(void) {
    extern unsigned char glaurung_global_3fe8[16];
    return (unsigned int)(*(int *)(*(long *)(&glaurung_global_3fe8[0])));
}

gcc -O0

0/5
initfini_fold structural 26 lines
// glaurung: initfini_fold @ 0x120d
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_fold(const int32_t * arg0, int32_t arg1) {
    extern unsigned char glaurung_global_4020[16];
    unsigned int accumulator;
    int index;
    // x86-64 prologue: save rbp
    accumulator = *(int *)(&glaurung_global_4020[0]);
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((long)(arg1) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    for (index = 0; (index < arg1); index++) {
        accumulator = (accumulator + (unsigned int)(((unsigned long)((unsigned int)(*(int *)((((long)((int)(((unsigned long)((unsigned int)(index)) & 3))) * 4) + 0x4050)))) + (unsigned long)((unsigned int)(arg0[(long)(index)])))));
    }
    // x86-64 epilogue: restore rbp
    return accumulator;
}
initfini_order structural 8 lines
// glaurung: initfini_order @ 0x11b3
static unsigned char glaurung_global_4044[16] __attribute__((aligned(16)));
int32_t initfini_order(void) {
    extern unsigned char glaurung_global_4044[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(&glaurung_global_4044[0]));
}
initfini_ready structural 8 lines
// glaurung: initfini_ready @ 0x11a3
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_ready(void) {
    extern unsigned char glaurung_global_4020[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(&glaurung_global_4020[0]));
}
initfini_table structural 14 lines
// glaurung: initfini_table @ 0x11c3
int32_t initfini_table(int32_t arg0) {
    // x86-64 prologue: save rbp
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 3) | ((long)(arg0) < 3)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((((long)(arg0) * 4) + 0x4050)));
}
initfini_witness fail 8 lines
// glaurung: initfini_witness @ 0x11fa
static unsigned char glaurung_global_3ff0[16] __attribute__((aligned(16)));
int32_t initfini_witness(void) {
    extern unsigned char glaurung_global_3ff0[16];
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)(*(long *)(&glaurung_global_3ff0[0])));
}

gcc -O2

0/5
initfini_fold structural 32 lines
// glaurung: initfini_fold @ 0x11f0
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_fold(const int32_t * arg0, int32_t arg1) {
    extern unsigned char glaurung_global_4020[16];
    int index;
    unsigned int accumulator;
    long ret;
    long var1;
    long var4;
    long var6;
    long zf_10;
    var6 = (unsigned long)((unsigned int)(*(int *)(&glaurung_global_4020[0])));
    ret = (unsigned long)((unsigned int)(var6));
    if ((arg0 == 0)) {
        return 0xffffffff;
    }
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        return ret;
    }
    var1 = (unsigned long)((unsigned int)((arg1 - 1)));
    var4 = (long)(0x4050);
    index = 0;
    do {
        var6 = (unsigned long)((unsigned int)((var6 + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(((long)arg0 + index * 4)))) + *(int *)((var4 + ((unsigned long)((unsigned int)((index & 3))) * 4)))))))));
        zf_10 = (var1 == index);
        index = (index + 1);
    } while ((zf_10 == 0));
    return (unsigned int)(var6);
}
initfini_order structural 6 lines
// glaurung: initfini_order @ 0x11a0
static unsigned char glaurung_global_4060[16] __attribute__((aligned(16)));
int32_t initfini_order(void) {
    extern unsigned char glaurung_global_4060[16];
    return (unsigned int)(*(int *)(&glaurung_global_4060[0]));
}
initfini_ready structural 6 lines
// glaurung: initfini_ready @ 0x1190
static unsigned char glaurung_global_4020[16] __attribute__((aligned(16)));
int32_t initfini_ready(void) {
    extern unsigned char glaurung_global_4020[16];
    return (unsigned int)(*(int *)(&glaurung_global_4020[0]));
}
initfini_table structural 7 lines
// glaurung: initfini_table @ 0x11b0
int32_t initfini_table(int32_t arg0) {
    if (((unsigned long)(3) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    return (unsigned int)(*(int *)((0x4050 + ((long)(arg0) * 4))));
}
initfini_witness fail 6 lines
// glaurung: initfini_witness @ 0x11e0
static unsigned char glaurung_global_3ff0[16] __attribute__((aligned(16)));
int32_t initfini_witness(void) {
    extern unsigned char glaurung_global_3ff0[16];
    return (unsigned int)(*(int *)(*(long *)(&glaurung_global_3ff0[0])));
}

← 213 fixtures