Performance Guide
Repository: https://github.com/ECLS-Studio/rerius
Typical Analysis Times#
The table below is a real measurement (3 runs each, Analysis completed in N seconds self-reported by the tool), not a simulated or estimated figure, but it's from one x86-64 Linux container, not the ARM64 Android device the original draft of this doc claimed. Absolute numbers will differ by CPU, compiler optimization level, and binary; treat the relative shape (which passes cost more) as the more portable takeaway.
Test binary: /bin/ls from this environment: 142 KB, stripped x86-64 ELF, 162 functions detected.
Build: gcc -std=c99 -O2 (see BUILDING.md for the full source list).
| Analysis | Flag | Time |
|---|---|---|
| Disassembly only | (default) | ~20-30 ms |
| Standard full | -x |
~120 ms |
| + Entropy | -x -e |
~120 ms |
| + Recursive descent | -x -R |
~190 ms |
| + Symbolic execution | -x -P |
~130 ms |
| + Decompile | -x -D |
~155 ms |
| + Emulate | -x -I |
~115 ms |
| Full everything | -X |
~760 ms |
-X includes every pass (-P -Q -D -I -e -R -V --poly --aire --vm-trace --dsa on top of -x), which is why it's noticeably more than the sum of any two individual passes above: it's paying for all of them together, plus AIRE's cross-referencing pass over everything else's output. It also drops into the interactive shell afterward (see CLI_REFERENCE.md); the timing above was captured with stdin closed (< /dev/null) so the shell exits immediately on EOF.
These numbers are for a single small, non-obfuscated binary. Larger or heavily obfuscated binaries will scale differently, particularly for the modules noted as bottlenecks below (CFG block lookup, recursive-descent visited-set scan).
Performance Characteristics by Module#
Loader (loader.c)#
O(sections + symbols). Dominated by mmap/read: purely I/O bound. Scales linearly with file size.
Xref Builder (analysis.c)#
O(instructions). Single pass through all code sections. Very fast: typically under 2 ms even for large binaries.
CFG Builder (cfg.c)#
O(instructions x 2) for the two-pass algorithm. The pre-pass and main pass each walk instructions once. Block boundary lookup is a linear scan O(nblocks): this could be O(log n) with a sorted array, but nblocks is typically under 8192.
Entropy (entropy.c)#
O(sections x size / step). For a 1 MB .text section with a 64-byte step: roughly 15,000 window evaluations, each O(256). Fast in practice.
Recursive Descent (entropy.c)#
O(reachable_instructions x queue_size). BFS with a visited set: each instruction is decoded at most once. Dominates runtime for large binaries with many functions.
Symbolic Execution (symexec.c)#
O(function_instructions x pool_depth). The expression pool is bounded at 1024 nodes per function. Fast for small functions, slower for functions with many nested operations.
Emulator (emulate.c)#
O(steps). Hard limit of 8192 instructions per function. Page allocation is O(npages) on first write, bounded at 64 pages.
JS API Performance#
The N-API boundary adds negligible overhead even for large return values: arrays of structs are returned by value. The dominant cost is always the C-side analysis, not the JS/C boundary.
Repeated analysis on the same binary:
withBinary opens and closes the binary on every call. For a service that analyzes the same binary repeatedly, cache the loaded handle:
const cache = new Map();
function getCached(filePath) {
const bin = rerius.load(filePath);
const key = bin.sha256;
if (cache.has(key)) { bin.close(); return cache.get(key); }
cache.set(key, bin);
return bin;
}
This avoids re-parsing ELF headers, re-loading symbols, and re-building xrefs on every request.
Memory Usage#
Struct sizes below are sizeof() measured directly (via a small probe compiled against include/core/dax.h), not estimated: the original draft of this table understated several of these by 2-20x, most notably the base struct.
| Component | Actual size | Notes |
|---|---|---|
Base dax_binary_t struct |
174,592 bytes (~171 KB) | Dominated by the embedded sections[128] array and other fixed-size fields: this is allocated once per binary regardless of file size |
Raw file data (bin->data) |
= file size | |
| Symbols array | nsymbols × 1056 bytes |
|
| Xrefs array | nxrefs × 24 bytes |
|
| Functions array | nfunctions × 544 bytes |
|
| Blocks array | nblocks × 240 bytes |
|
| Unicode strings | nustrings × 528 bytes |
Practical implication: because the base struct alone is ~171 KB, a service holding many dax_binary_t handles open concurrently (e.g. an unbounded cache in a long-running REST server) uses meaningfully more baseline memory per handle than the file size alone would suggest: budget for it if you're caching a large number of binaries.
For /bin/ls (142 KB, 8 symbols, 162 functions, 2928 blocks, 4293 xrefs) with -X (every pass): peak RSS measured via /proc/<pid>/status (VmHWM) in this environment was ~2 MB. Static struct sizes account for roughly 171 KB (base) + 142 KB (file) + 101 KB (xrefs) + 86 KB (functions) + 686 KB (blocks) ≈ 1.2 MB, so an additional ~0.8 MB comes from decoder/analysis scratch buffers, the C library, and allocator overhead not accounted for by the arrays above.
Profiling#
# Linux with perf (redirect stdin - -X drops into the interactive shell otherwise)
perf stat ./rerius -X /bin/ls < /dev/null
# Linux with Valgrind callgrind
valgrind --tool=callgrind ./rerius -x /bin/ls
callgrind_annotate callgrind.out.*
# Time individual modules
time ./rerius -x /bin/ls > /dev/null
time ./rerius -x -R /bin/ls > /dev/null
Known Bottlenecks#
Block boundary lookup in CFG: find_block_by_addr() is an O(n) linear scan. For binaries with more than 8000 blocks this becomes noticeable. A hash map or sorted array with binary search would reduce this to O(1) or O(log n).
Recursive descent visited set: rda_is_visited() is an O(n) linear scan over up to 65,536 entries. A hash set would reduce this to O(1).
Large .dynstr UTF-16LE scan: The unicode scanner skips known-bad sections, but for binaries with many rodata sections the scan is still O(section_size). Pre-filtering by section type reduces this significantly for most binaries.
These are tracked for improvement in future releases.
docs/PERFORMANCE.md · Rerius v1.0.0