Fixture 95
function pointer table
C · 2 functions · 4 lanes · 7 of 8 function-lanes behave identically
One lane has a function that returns a different result after decompilation: gcc-O2 (1/2).
An array of function pointers is an indirect call whose target is a loaded value, not a relocation. Recovering the dispatch means recovering the table's contents, its stride, and the bounds check that guards it.
#include <stdint.h>
/* An array of function pointers is an indirect call whose target is a loaded
* value, not a relocation. Recovering the dispatch means recovering the table's
* contents, its stride, and the bounds check that guards it. */
static int32_t op_add(int32_t a, int32_t b) { return (int32_t)((uint32_t)a + (uint32_t)b); }
static int32_t op_sub(int32_t a, int32_t b) { return (int32_t)((uint32_t)a - (uint32_t)b); }
static int32_t op_and(int32_t a, int32_t b) { return a & b; }
static int32_t op_xor(int32_t a, int32_t b) { return a ^ b; }
static int32_t op_max(int32_t a, int32_t b) { return a > b ? a : b; }
typedef int32_t (*BinaryOp)(int32_t, int32_t);
static BinaryOp const OPERATIONS[5] = {op_add, op_sub, op_and, op_xor, op_max};
__attribute__((noinline)) int32_t
dispatch_operation(int32_t which, int32_t a, int32_t b) {
if (which < 0 || which >= 5) {
return -1;
}
return OPERATIONS[which](a, b);
}
__attribute__((noinline)) int32_t
fold_operations(const int32_t *selectors, int32_t count, int32_t seed) {
int32_t accumulator = seed;
int32_t index;
if (selectors == 0 || count < 0 || count > 16) {
return -1;
}
for (index = 0; index < count; ++index) {
int32_t which = selectors[index];
if (which < 0 || which >= 5) {
continue;
}
accumulator = OPERATIONS[which](accumulator, index + 1);
}
return 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.
gcc -O2
1/2dispatch_operation pass 27 lines
// glaurung: dispatch_operation @ 0x1150
int32_t dispatch_operation(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int ret;
long var0;
long var1;
long var2;
var0 = (long)(arg0);
var1 = (unsigned long)((unsigned int)(arg1));
var2 = (unsigned long)((unsigned int)(arg2));
if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(4))) {
ret = ((int (*)(long, long))(OPERATIONS[var0]))(var1, var2);
return ret;
}
return 0xffffffff;
} fold_operations fail 66 lines
// glaurung: fold_operations @ 0x1180
int32_t fold_operations(const int32_t * arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int which;
int accumulator;
int index;
long local_10;
long local_20;
long local_8;
long var0;
long var1;
long var12;
long var14;
long var2;
long var3;
long var4;
long var6;
long var9;
if ((arg0 == 0)) {
return (unsigned int)(-1);
}
local_8 = var0;
local_10 = var1;
var2 = (unsigned long)((unsigned int)(arg1));
local_20 = var3;
if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
goto L_11ef;
}
if (((unsigned long)((unsigned int)(arg1)) == 0)) {
goto L_11e0;
}
var4 = (long)arg0;
var6 = 0;
var9 = (unsigned long)((unsigned int)(arg2));
do {
which = (long)((int)(*(int *)((var4))));
var6 = (unsigned long)((unsigned int)((var6 + 1)));
var12 = var9;
if (((unsigned long)((unsigned long)((unsigned int)(which))) <= (unsigned long)(4))) {
var14 = ((long (*)(void))(OPERATIONS[which]))();
var12 = (unsigned long)((unsigned int)(var14));
}
var4 = (var4 + 4);
var9 = var12;
} while (((unsigned int)(var2) != (unsigned int)(var6)));
L_11ce: ;
// x86-64 epilogue: tear down frame
return (unsigned int)(var12);
L_11e0: ;
// x86-64 epilogue: tear down frame
return (unsigned int)(arg2);
L_11ef: ;
var12 = 0xffffffff;
goto L_11ce;
} clang -O0
2/2dispatch_operation pass 32 lines
// glaurung: dispatch_operation @ 0x1100
int32_t dispatch_operation(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int local_4;
long var3;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg0) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if ((5 <= (long)(arg0))) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
var3 = ((long (*)(long, long))(OPERATIONS[(long)(arg0)]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
local_4 = var3;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} fold_operations pass 55 lines
// glaurung: fold_operations @ 0x1160
int32_t fold_operations(const int32_t * arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int accumulator;
int index;
int which;
int local_4;
long var10;
// x86-64 prologue: save rbp, frame 48 bytes
accumulator = arg2;
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);
}
index = 0;
while ((index < arg1)) {
which = arg0[(long)(index)];
if (((long)(which) < 0)) {
L_11d8: ;
} else {
if (((long)(which) < 5)) {
var10 = ((long (*)(long, long))(OPERATIONS[(long)(which)]))((unsigned long)((unsigned int)(accumulator)), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(index)) + 1))));
accumulator = var10;
} else {
goto L_11d8;
}
}
index = ((unsigned int)(index) + 1);
}
local_4 = accumulator;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} clang -O2
2/2dispatch_operation pass 21 lines
// glaurung: dispatch_operation @ 0x1100
int32_t dispatch_operation(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int ret;
if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(4))) {
ret = ((int (*)(long, long))(OPERATIONS[(unsigned long)((unsigned int)(arg0))]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
return ret;
}
return 0xffffffff;
} fold_operations pass 57 lines
// glaurung: fold_operations @ 0x1120
int32_t fold_operations(const int32_t * arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int index;
int accumulator;
long ret;
long var0;
long var10;
long var11;
long var12;
long var2;
long var3;
long var8;
// x86-64 prologue: save callee registers, frame 40 bytes
var0 = 0xffffffff;
ret = 0xffffffff;
if ((arg0 == 0)) {
// x86-64 epilogue: restore callee registers
return ret;
}
ret = var0;
if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
// x86-64 epilogue: restore callee registers
return ret;
}
if (((unsigned long)((unsigned int)(arg1)) == 0)) {
return (unsigned int)(arg2);
}
var2 = (long)arg0;
var3 = (unsigned long)((unsigned int)(arg1));
var8 = (unsigned long)((unsigned int)(arg2));
index = 0;
do {
var10 = (unsigned long)((unsigned int)(*(int *)((var2 + index * 4))));
index = (index + 1);
var11 = var8;
if (((unsigned long)(var10) < (unsigned long)(5))) {
var12 = ((long (*)(long, long))(OPERATIONS[var10]))((unsigned long)((unsigned int)(var8)), (unsigned long)((unsigned int)(index)));
var11 = var12;
}
var8 = var11;
ret = var11;
} while ((index != var3));
// x86-64 epilogue: restore callee registers
return ret;
} gcc -O0
2/2dispatch_operation pass 28 lines
// glaurung: dispatch_operation @ 0x1170
int32_t dispatch_operation(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int ret;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg0) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg0)) == 4) | ((long)(arg0) < 4)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
ret = ((int (*)(long, long))(OPERATIONS[(long)((int)((unsigned long)((unsigned int)(arg0))))]))((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
// x86-64 epilogue: restore rbp
return ret;
} fold_operations pass 43 lines
// glaurung: fold_operations @ 0x11be
int32_t fold_operations(const int32_t * arg0, int32_t arg1, int32_t arg2) {
extern void op_add(void);
extern void op_and(void);
extern void op_max(void);
extern void op_sub(void);
extern void op_xor(void);
static void (*OPERATIONS[5])(void) = {
(void (*)(void))op_add,
(void (*)(void))op_sub,
(void (*)(void))op_and,
(void (*)(void))op_xor,
(void (*)(void))op_max,
};
int accumulator;
int index;
int which;
long var16;
// x86-64 prologue: save rbp, frame 32 bytes
accumulator = arg2;
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++) {
which = arg0[(long)(index)];
if ((((long)(which) < 0) || ((((unsigned long)((unsigned int)(which)) == 4) | ((long)(which) < 4)) == 0))) {
} else {
var16 = ((long (*)(long, long))(OPERATIONS[(long)((int)((unsigned long)((unsigned int)(which))))]))((unsigned long)((unsigned int)(accumulator)), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(index)) + 1))));
accumulator = var16;
}
}
// x86-64 epilogue: restore rbp
return (unsigned int)(accumulator);
}