00d655187a
Three small functions extracted from the v1.19 conservative blob with
ground-truth C and per-tool (Ghidra / retdec / decomp.me) docs:
01_memset — byte memset, 28 B
02_memcpy32 — word-aligned memcpy, 36 B
03_magic_memset — magic check + tail-call to memset, 40 B
04_train_phy_block — first real poll-site function (104 B, 26 insts),
contains poll sites 12-15
Results in RESULTS.md:
- Ghidra: A on all four. Auto-decompile is close to final.
- retdec: A on #3, F on #1 and #2 (no register-arg inference on raw),
C on #4 (mistakes & 0xF0000000 for < 0x10000000).
GRIND_LOG.md (in 04_train_phy_block/) records the matching-decomp
iteration: 116-byte candidate.c at -Os vs vendor 104 bytes = 89.7%
size match on first real iteration. Remaining gap is GCC's choice of
`cmp w, w_const; b.ls` over vendor's `tst w, #imm; b.eq` for the
mask tests.
gdb_debug/ holds a native-aarch64 GDB single-stepper for the three
benchmark functions — boltzmann smoke test passed (memset:
buf[10] 0x00→0xab).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.7 KiB
C
75 lines
2.7 KiB
C
/* Generic harness for single-stepping one of the benchmark functions under GDB.
|
|
* Copies the raw bytes of funcNN.bin into an RWX buffer and calls through
|
|
* a function pointer. GDB stepi from the call site drops you right into the
|
|
* target function's first instruction. No QEMU needed — boltzmann is aarch64.
|
|
*
|
|
* Build: run `make` in this dir (native aarch64 only, for now).
|
|
* Run: ./gdb_debug.elf {1|2|3} (1=memset 2=memcpy32 3=magic_memset)
|
|
*
|
|
* Under GDB: see README.md.
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
|
|
extern uint8_t _binary_func_01_bin_start[], _binary_func_01_bin_end[];
|
|
extern uint8_t _binary_func_02_bin_start[], _binary_func_02_bin_end[];
|
|
extern uint8_t _binary_func_03_bin_start[], _binary_func_03_bin_end[];
|
|
|
|
typedef void (*f1_t)(void *, uint8_t, uint64_t);
|
|
typedef void (*f2_t)(uint32_t *, const uint32_t *, uint64_t);
|
|
typedef void (*f3_t)(void);
|
|
|
|
static void *rwx_copy(const void *src, size_t len) {
|
|
void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (p == MAP_FAILED) { perror("mmap"); exit(1); }
|
|
memcpy(p, src, len);
|
|
__builtin___clear_cache(p, (char *)p + len);
|
|
return p;
|
|
}
|
|
|
|
static void __attribute__((noinline))
|
|
call_func(void (*fn)(void), int which) {
|
|
switch (which) {
|
|
case 1: {
|
|
char buf[64] = {0};
|
|
printf("pre: buf[10]=0x%02x\n", (uint8_t)buf[10]);
|
|
((f1_t)fn)(buf, 0xAB, 16);
|
|
printf("post: buf[10]=0x%02x (expect 0xab)\n", (uint8_t)buf[10]);
|
|
break;
|
|
}
|
|
case 2: {
|
|
uint32_t dst[8] = {0}, src[8];
|
|
for (int i = 0; i < 8; i++) src[i] = 0xDEAD0000U | i;
|
|
((f2_t)fn)(dst, src, sizeof dst);
|
|
printf("dst[3]=0x%08x (expect 0xdead0003)\n", dst[3]);
|
|
break;
|
|
}
|
|
case 3:
|
|
printf("calling magic_memset — SIGSEGVs on LDR of 0x1fe004 in user mode.\n");
|
|
((f3_t)fn)();
|
|
break;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc != 2) { fprintf(stderr, "usage: %s {1|2|3}\n", argv[0]); return 2; }
|
|
int which = atoi(argv[1]);
|
|
void (*fn)(void);
|
|
switch (which) {
|
|
case 1: fn = rwx_copy(_binary_func_01_bin_start,
|
|
_binary_func_01_bin_end - _binary_func_01_bin_start); break;
|
|
case 2: fn = rwx_copy(_binary_func_02_bin_start,
|
|
_binary_func_02_bin_end - _binary_func_02_bin_start); break;
|
|
case 3: fn = rwx_copy(_binary_func_03_bin_start,
|
|
_binary_func_03_bin_end - _binary_func_03_bin_start); break;
|
|
default: fprintf(stderr, "unknown index %d\n", which); return 2;
|
|
}
|
|
printf("function %d loaded at %p\n", which, fn);
|
|
call_func(fn, which);
|
|
return 0;
|
|
}
|