Fixture 148

dispatch obfuscation

C · 4 functions · 4 lanes · 10 of 16 function-lanes behave identically

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

Jump-table and dispatch obfuscation: the control-flow edge is a value in a table, not a branch in the code.

Four variants appear here. A function-pointer table reached through an index that is itself a table lookup, so the callee cannot be resolved without constant-folding two loads. A switch whose selector is permuted by a byte map, so the case labels in the binary bear no relation to the caller's opcode. An indirect call whose index is produced by arithmetic and then masked into range. And a state walk that chases a next-index table, which is a jump table used as a linked list.

Why this breaks decompilers: recovering an indirect call requires proving what the target set is. Given TABLE[MAP[op & 7] & 3](a, b) a decompiler either (a) gives up and prints a call through a variable, losing every callee's name and signature, or (b) guesses. Guessing is the dangerous outcome: emitting a direct call to whichever entry happened to be first, or to a "most likely" target, produces output that compiles, type-checks, reads naturally, and computes the wrong function for seven of the eight opcodes. The permuted switch attacks the same weakness from the other side — the jump-table reconstruction is correct but the case *labels* are meaningless unless the permutation is carried through, so a decompiler that recovers the table but drops the map produces a beautifully structured wrong program.

Safety: every index is masked to the table's size before use, every count is validated against a small constant bound, and no arithmetic can overflow a signed type (all of it runs through uint32_t).

tests/decompiler_fixtures/src/148_dispatch_obfuscation.c source
#include <stdint.h>

/* Jump-table and dispatch obfuscation: the control-flow edge is a value in a
 * table, not a branch in the code.
 *
 * Four variants appear here. A function-pointer table reached through an index
 * that is itself a table lookup, so the callee cannot be resolved without
 * constant-folding two loads. A `switch` whose selector is permuted by a byte
 * map, so the case labels in the binary bear no relation to the caller's
 * opcode. An indirect call whose index is produced by arithmetic and then
 * masked into range. And a state walk that chases a next-index table, which is
 * a jump table used as a linked list.
 *
 * Why this breaks decompilers: recovering an indirect call requires proving
 * what the target set is. Given `TABLE[MAP[op & 7] & 3](a, b)` a decompiler
 * either (a) gives up and prints a call through a variable, losing every
 * callee's name and signature, or (b) guesses. Guessing is the dangerous
 * outcome: emitting a direct call to whichever entry happened to be first, or
 * to a "most likely" target, produces output that compiles, type-checks, reads
 * naturally, and computes the wrong function for seven of the eight opcodes.
 * The permuted `switch` attacks the same weakness from the other side — the
 * jump-table reconstruction is correct but the case *labels* are meaningless
 * unless the permutation is carried through, so a decompiler that recovers the
 * table but drops the map produces a beautifully structured wrong program.
 *
 * Safety: every index is masked to the table's size before use, every count is
 * validated against a small constant bound, and no arithmetic can overflow a
 * signed type (all of it runs through uint32_t).
 */

#define JT148_TABLE_SIZE 4
#define JT148_MAP_SIZE 8
#define JT148_MAX_ELEMS 16

static int32_t jt148_op_add(int32_t a, int32_t b) {
    return (int32_t)((uint32_t)a + (uint32_t)b);
}

static int32_t jt148_op_sub(int32_t a, int32_t b) {
    return (int32_t)((uint32_t)a - (uint32_t)b);
}

static int32_t jt148_op_xor(int32_t a, int32_t b) {
    return (int32_t)((uint32_t)a ^ (uint32_t)b);
}

static int32_t jt148_op_pick(int32_t a, int32_t b) {
    return (a < b) ? a : b;
}

typedef int32_t (*jt148_handler)(int32_t, int32_t);

static jt148_handler const JT148_HANDLERS[JT148_TABLE_SIZE] = {
    jt148_op_add,
    jt148_op_sub,
    jt148_op_xor,
    jt148_op_pick,
};

/* Opcode -> handler slot. Deliberately not the identity, and not monotone. */
static const uint8_t JT148_SLOT_MAP[JT148_MAP_SIZE] = {2, 0, 3, 1, 1, 3, 0, 2};

/* Selector -> case label. The inverse permutation of what an analyst expects. */
static const uint8_t JT148_CASE_MAP[JT148_MAP_SIZE] = {5, 3, 7, 1, 6, 0, 4, 2};

/* State -> next state. A jump table used as a successor list. */
static const uint8_t JT148_NEXT_MAP[JT148_MAP_SIZE] = {3, 5, 7, 2, 0, 6, 1, 4};

/* Two dependent loads before the call: the callee is only knowable by folding
 * both tables. */
__attribute__((noinline)) int32_t
obfuscated_dispatch(int32_t op, int32_t a, int32_t b) {
    uint32_t slot = (uint32_t)op & (uint32_t)(JT148_MAP_SIZE - 1);
    uint32_t index = (uint32_t)JT148_SLOT_MAP[slot] & (uint32_t)(JT148_TABLE_SIZE - 1);
    return JT148_HANDLERS[index](a, b);
}

/* The index is computed rather than loaded, then masked into range. Masking is
 * what makes this memory-safe for every input while keeping the target
 * genuinely input-dependent. */
__attribute__((noinline)) int32_t
computed_index_dispatch(int32_t base, int32_t delta) {
    uint32_t mixed = (uint32_t)base * 3u + (uint32_t)delta;
    uint32_t index = (mixed ^ (mixed >> 5)) & (uint32_t)(JT148_TABLE_SIZE - 1);
    return JT148_HANDLERS[index](base, delta);
}

/* A dense switch reached through a permutation table. The compiler emits a real
 * jump table for the switch; the permutation sits in .rodata in front of it. */
__attribute__((noinline)) int32_t
permuted_switch(int32_t selector, int32_t value) {
    uint32_t key = (uint32_t)JT148_CASE_MAP[(uint32_t)selector & (uint32_t)(JT148_MAP_SIZE - 1)];
    uint32_t v = (uint32_t)value;

    switch (key) {
    case 0u:
        return (int32_t)(v + 1u);
    case 1u:
        return (int32_t)(v * 2u);
    case 2u:
        return (int32_t)(v ^ 0x5A5Au);
    case 3u:
        return (int32_t)(v >> 1);
    case 4u:
        return (int32_t)(v - 7u);
    case 5u:
        return (int32_t)(v & 0xFFu);
    case 6u:
        return (int32_t)(v | 0x100u);
    case 7u:
        return (int32_t)(0u - v);
    default:
        return 0;
    }
}

/* Chasing the successor table: each iteration's control decision is a load from
 * .rodata, so the walk's trajectory is data, not code. Bounded by a validated
 * count and by the table mask. */
__attribute__((noinline)) int32_t
chained_table_walk(int32_t *out, int32_t count, int32_t start) {
    uint32_t state = (uint32_t)start & (uint32_t)(JT148_MAP_SIZE - 1);
    uint32_t acc = 0u;
    int32_t index;

    if (out == 0 || count < 0 || count > JT148_MAX_ELEMS) {
        return -1;
    }

    for (index = 0; index < count; ++index) {
        uint32_t slot = (uint32_t)JT148_SLOT_MAP[state] & (uint32_t)(JT148_TABLE_SIZE - 1);
        acc = (uint32_t)JT148_HANDLERS[slot]((int32_t)acc, (int32_t)state);
        out[index] = (int32_t)acc;
        state = (uint32_t)JT148_NEXT_MAP[state] & (uint32_t)(JT148_MAP_SIZE - 1);
    }
    return (int32_t)(acc ^ state);
}

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

2/4
chained_table_walk fail 47 lines
// glaurung: chained_table_walk @ 0x1270
int32_t chained_table_walk(int32_t * arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int state;
    unsigned int acc;
    int index;
    unsigned int slot;
    int local_4;
    long var15;
    // x86-64 prologue: save rbp, frame 48 bytes
    state = ((unsigned int)(arg2) & 7);
    acc = 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++) {
        slot = ((unsigned int)((unsigned char)(*(char *)((0x2020 + state)))) & 3);
        var15 = ((long (*)(unsigned int, unsigned int))(JT148_HANDLERS[slot]))(acc, state);
        acc = var15;
        arg0[(long)(index)] = acc;
        state = ((unsigned int)((unsigned char)(*(char *)((0x2030 + state)))) & 7);
    }
    local_4 = (acc ^ state);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
computed_index_dispatch pass 22 lines
// glaurung: computed_index_dispatch @ 0x1150
int32_t computed_index_dispatch(int32_t arg0, int32_t arg1) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int mixed;
    unsigned int index;
    int ret;
    // x86-64 prologue: save rbp, frame 16 bytes
    mixed = ((arg0 * 3) + arg1);
    index = ((unsigned int)((mixed ^ (unsigned long)((unsigned int)(((unsigned int)(mixed) >> 5))))) & 3);
    ret = ((int (*)(long, long))(JT148_HANDLERS[index]))((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
    // x86-64 epilogue: restore rbp
    return ret;
}
obfuscated_dispatch fail 22 lines
// glaurung: obfuscated_dispatch @ 0x1100
int32_t obfuscated_dispatch(int32_t arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int slot;
    unsigned int index;
    int ret;
    // x86-64 prologue: save rbp, frame 32 bytes
    slot = ((unsigned int)(arg0) & 7);
    index = ((unsigned int)((unsigned char)(*(char *)((0x2020 + slot)))) & 3);
    ret = ((int (*)(long, long))(JT148_HANDLERS[index]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
    // x86-64 epilogue: restore rbp
    return ret;
}
permuted_switch pass 29 lines
// glaurung: permuted_switch @ 0x11a0
int32_t permuted_switch(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2028))))))))));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)((v + 1));
        case 1:
            return (unsigned int)((v << 1));
        case 2:
            return (unsigned int)((v ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 1));
        case 4:
            return (unsigned int)((v - 7));
        case 5:
            return (unsigned int)((v & 255));
        case 6:
            return (unsigned int)((v | 256));
        case 7:
            return (unsigned int)((0 - v));
        default:
            return 0;
    }
    // x86-64 epilogue: restore rbp
}

gcc -O0

2/4
chained_table_walk fail 42 lines
// glaurung: chained_table_walk @ 0x12a8
int32_t chained_table_walk(int32_t * arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int state;
    unsigned int acc;
    int index;
    unsigned int slot;
    long var16;
    // x86-64 prologue: save rbp, frame 32 bytes
    state = ((unsigned int)(arg2) & 7);
    acc = 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++) {
        slot = ((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(*(char *)((state + 0x2000)))) & 255))) & 3);
        var16 = ((long (*)(unsigned int, unsigned int))(JT148_HANDLERS[slot]))(acc, state);
        acc = var16;
        arg0[(long)(index)] = acc;
        state = ((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(*(char *)((state + 0x2010)))) & 255))) & 7);
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)(acc) ^ state));
}
computed_index_dispatch pass 22 lines
// glaurung: computed_index_dispatch @ 0x11b5
int32_t computed_index_dispatch(int32_t arg0, int32_t arg1) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int mixed;
    unsigned int index;
    int ret;
    // x86-64 prologue: save rbp, frame 32 bytes
    mixed = ((unsigned int)(arg1) + (unsigned int)(((unsigned long)((unsigned int)(arg0)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) + (unsigned long)((unsigned int)(arg0))))))));
    index = ((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)(mixed) >> 5))) ^ mixed)) & 3);
    ret = ((int (*)(long, long))(JT148_HANDLERS[index]))((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
    // x86-64 epilogue: restore rbp
    return ret;
}
obfuscated_dispatch fail 22 lines
// glaurung: obfuscated_dispatch @ 0x115c
int32_t obfuscated_dispatch(int32_t arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int slot;
    unsigned int index;
    int ret;
    // x86-64 prologue: save rbp, frame 32 bytes
    slot = ((unsigned int)(arg0) & 7);
    index = ((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(*(char *)((slot + 0x2000)))) & 255))) & 3);
    ret = ((int (*)(long, long))(JT148_HANDLERS[index]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
    // x86-64 epilogue: restore rbp
    return ret;
}
permuted_switch pass 29 lines
// glaurung: permuted_switch @ 0x120b
int32_t permuted_switch(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2008)))))))))))) & 255));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)(((unsigned long)(v) + 1));
        case 1:
            return (unsigned int)(((unsigned long)(v) + (unsigned long)(v)));
        case 2:
            return (unsigned int)(((unsigned long)(v) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)(v) >> 1));
        case 4:
            return (unsigned int)(((unsigned long)(v) - 7));
        case 5:
            return (unsigned int)((unsigned char)(((unsigned long)(v) & 255)));
        case 6:
            return (((unsigned long)(v) & -0xff01LL) | ((((((unsigned long)(v) >> 8) & 255) | 1) & 255) << 8));
        case 7:
            return (-(unsigned long)(v));
        default:
            return 0;
    }
    // x86-64 epilogue: restore rbp
}

clang -O2

3/4
chained_table_walk fail 73 lines
// glaurung: chained_table_walk @ 0x1190
__attribute__((no_stack_protector)) int32_t chained_table_walk(int32_t * arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int acc;
    int index;
    unsigned int state;
    unsigned int slot;
    unsigned char local_38[56];
    long rbp;
    long ret;
    long var0;
    long var1;
    long var10;
    long var11;
    long var12;
    long var16;
    long var2;
    long var22;
    long var27;
    long var3;
    long var30;
    long var4;
    long var6;
    long var9;
    ret = 0xffffffff;
    if ((arg0 == 0)) {
        return ret;
    }
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return ret;
    }
    *(long *)((&local_38[0] + 48)) = rbp;
    *(long *)((&local_38[0] + 40)) = var0;
    *(long *)((&local_38[0] + 32)) = var1;
    *(long *)((&local_38[0] + 24)) = var2;
    *(long *)((&local_38[0] + 16)) = var3;
    *(long *)((&local_38[0] + 8)) = var4;
    *(long *)(&local_38[0]) = 0xffffffff;
    var6 = (unsigned long)((unsigned int)((arg2 & 7)));
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        var9 = 0;
        var10 = var6;
    } else {
        var11 = (long)arg0;
        var12 = (unsigned long)((unsigned int)(arg1));
        var16 = (long)(0x2030);
        acc = 0;
        index = 0;
        state = var6;
        do {
            var22 = (unsigned long)(state);
            var27 = ((long (*)(unsigned int, unsigned int))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((state + 0x2020)))) & 3)))]))(acc, state);
            acc = var27;
            *(int *)((var11 + index * 4)) = var27;
            index = (index + 1);
            var30 = (unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((var22 + var16)))) & 7)));
            state = var30;
        } while ((var12 != index));
        var9 = (unsigned long)(acc);
        var10 = var30;
    }
    // x86-64 epilogue: tear down frame
    return (unsigned int)((var9 ^ var10));
}
computed_index_dispatch pass 20 lines
// glaurung: computed_index_dispatch @ 0x1120
int32_t computed_index_dispatch(int32_t arg0, int32_t arg1) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int index;
    unsigned int mixed;
    int ret;
    int var1;
    var1 = ((unsigned int)((arg0 + (arg0 * 2))) + arg1);
    ret = ((int (*)(int, int))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((unsigned long)((unsigned int)((unsigned long)((unsigned int)(var1)))))) >> 5))) ^ (unsigned long)((unsigned int)(var1))))) & 3)))]))(arg0, arg1);
    return ret;
}
obfuscated_dispatch pass 18 lines
// glaurung: obfuscated_dispatch @ 0x1100
int32_t obfuscated_dispatch(int32_t arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int index;
    unsigned int slot;
    int ret;
    ret = ((int (*)(long, long))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)((arg0 & 7))) == 0) ? 2 : (((unsigned long)((unsigned int)((arg0 & 7))) == 1) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 2) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 4) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 5) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 6) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)((arg0 & 7))) + 0x2020)))))))))))) & 3)))]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
    return ret;
}
permuted_switch pass 23 lines
// glaurung: permuted_switch @ 0x1140
int32_t permuted_switch(int32_t arg0, int32_t arg1) {
    switch ((unsigned int)((unsigned char)((((unsigned long)((unsigned int)((arg0 & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)((arg0 & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)((arg0 & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)((arg0 & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)((arg0 & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)((arg0 & 7))) + 0x2028))))))))))))) {
        case 0:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + 1));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + (unsigned long)((unsigned int)(arg1))));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 1));
        case 4:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) - 7));
        case 5:
            return (unsigned int)((unsigned char)(((unsigned long)((unsigned int)(arg1)) & 255)));
        case 6:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) | 256));
        case 7:
            return (-(unsigned long)((unsigned int)(arg1)));
        default:
            return 0;
    }
}

gcc -O2

3/4
chained_table_walk fail 57 lines
// glaurung: chained_table_walk @ 0x1220
int32_t chained_table_walk(int32_t * arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int acc;
    int index;
    unsigned int state;
    long var1;
    long var11;
    long var12;
    long var13;
    long var14;
    long var18;
    long var19;
    int var20;
    long var23;
    long var6;
    long var7;
    long var9;
    var1 = (unsigned long)((unsigned int)((arg2 & 7)));
    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 (unsigned int)(var1);
    }
    var6 = (long)arg0;
    var7 = (long)((((long)arg0 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) - 1))) * 4)) + 4));
    var9 = 0;
    var11 = (long)(0x2030);
    var12 = (long)(0x2020);
    var13 = (unsigned long)((unsigned int)(var1));
    do {
        var14 = (unsigned long)((unsigned int)(var13));
        var6 = (var6 + 4);
        var18 = ((long (*)(long, long))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((var11 + (unsigned long)((unsigned int)(var13)))))) & 3)))]))(var9, var13);
        var19 = var18;
        var20 = (unsigned int)((unsigned char)(*(char *)((var12 + var14))));
        *(int *)((var6 - 0x4)) = var18;
        var23 = (unsigned long)((unsigned int)((var20 & 7)));
        var9 = (unsigned long)((unsigned int)(var18));
        var13 = var23;
    } while ((var6 != var7));
    // x86-64 epilogue: tear down frame
    return (unsigned int)((var23 ^ var19));
}
computed_index_dispatch pass 19 lines
// glaurung: computed_index_dispatch @ 0x1180
int32_t computed_index_dispatch(int32_t arg0, int32_t arg1) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    unsigned int mixed;
    int ret;
    int var1;
    var1 = ((unsigned int)((arg0 + (arg0 * 2))) + arg1);
    ret = ((int (*)(int, int))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((unsigned long)((unsigned int)((unsigned long)((unsigned int)(var1)))))) >> 5))) ^ (unsigned long)((unsigned int)(var1))))) & 3)))]))(arg0, arg1);
    return ret;
}
obfuscated_dispatch pass 16 lines
// glaurung: obfuscated_dispatch @ 0x1150
int32_t obfuscated_dispatch(int32_t arg0, int32_t arg1, int32_t arg2) {
    extern void jt148_op_add(void);
    extern void jt148_op_pick(void);
    extern void jt148_op_sub(void);
    extern void jt148_op_xor(void);
    static void (*JT148_HANDLERS[4])(void) = {
        (void (*)(void))jt148_op_add,
        (void (*)(void))jt148_op_sub,
        (void (*)(void))jt148_op_xor,
        (void (*)(void))jt148_op_pick,
    };
    int ret;
    ret = ((int (*)(long, long))(JT148_HANDLERS[(unsigned long)((unsigned int)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 2 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 2 : *(char *)((0x2030 + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))))))))))))))) & 3)))]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
    return ret;
}
permuted_switch pass 26 lines
// glaurung: permuted_switch @ 0x11a0
int32_t permuted_switch(int32_t arg0, int32_t arg1) {
    long var2;
    var2 = (unsigned long)((unsigned int)((arg0 & 7)));
    if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned char)(((var2 == 0) ? 5 : ((var2 == 1) ? 3 : ((var2 == 2) ? 7 : ((var2 == 3) ? 1 : ((var2 == 4) ? 6 : ((var2 == 5) ? 0 : ((var2 == 6) ? 4 : ((var2 == 7) ? 2 : *(char *)((0x2028 + var2))))))))))))))) {
        return 0;
    }
    switch ((unsigned int)((unsigned char)(((var2 == 0) ? 5 : ((var2 == 1) ? 3 : ((var2 == 2) ? 7 : ((var2 == 3) ? 1 : ((var2 == 4) ? 6 : ((var2 == 5) ? 0 : ((var2 == 6) ? 4 : ((var2 == 7) ? 2 : *(char *)((0x2028 + var2))))))))))))) {
        case 0:
            return (unsigned int)((arg1 + 1));
        case 1:
            return (unsigned int)((arg1 + arg1));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 1));
        case 4:
            return (unsigned int)((arg1 - 7));
        case 5:
            return (unsigned int)((unsigned char)((arg1 & 255)));
        case 6:
            return (((unsigned long)((unsigned int)(arg1)) & -0xff01LL) | ((((((unsigned long)((unsigned int)(arg1)) >> 8) & 255) | 1) & 255) << 8));
        case 7:
            return (-(unsigned long)((unsigned int)(arg1)));
    }
}

← 213 fixtures