Disassembler Backends¶
Hexana disassembles native binaries (ELF, Mach-O, PE) through a bundled Capstone WASM module. The module itself is the same regardless of how it runs — the difference is how the WASM bytecode is executed inside the JVM.
Two backends are supported:
| Backend | Default | Execution path | Distribution |
|---|---|---|---|
| Bytecode AOT | yes | Capstone WASM is lowered to JVM bytecode at build time and shipped inside the plugin. Runs on the JVM with no native components. | Single jar slice, every platform. |
| Cranelift / Redline | experimental | Capstone WASM is compiled to native machine code at build time, one binary per (OS, architecture), and loaded at runtime via Project Panama FFM (Java 25+) or jffi. | One slice per host: macOS arm64 / x86-64, Linux x86-64 / aarch64, Windows x86-64. |
Both backends produce identical disassembly output. The choice is a performance trade-off, not a feature trade-off.
When each backend is faster¶
The AOT backend is the safe default — it runs on every platform the IDE itself runs on, with no extra files to deploy and no native-loader surprises.
The Redline backend has lower overhead per decode call. On large .text sections (typically several MB and above), or when scrubbing quickly through a virtualised disassembly view, the difference becomes visible. On small or already-disassembled binaries (everything is cached) the two backends are indistinguishable.
If no native slice is available for the host platform, Redline transparently falls back to the AOT path — selecting it never breaks the disassembly view, it simply has no effect.
Switching backends¶
Backend selection is a Registry key.
- Open Help → Find Action… (
Cmd+Shift+Aon macOS,Ctrl+Shift+Aon Linux / Windows). - Type
Registry…and open the Registry dialog. - Filter for
hexana.disassembly. - Toggle the keys you want.
| Registry key | Default | Effect |
|---|---|---|
hexana.disassembly.backend.redline |
false |
When true, use the Cranelift native backend; when false, use the bytecode AOT backend. |
hexana.disassembly.perf.logging |
false |
When true, emit [disasm-perf] timing lines to idea.log for every decoded chunk. Useful when comparing the two backends on a specific binary. |
The change takes effect on the next disassembly panel open, or via the panel's Retry button — there is no restart required.
Observing in-flight decode work¶
The Capstone library itself is not thread-safe. Hexana serialises all decode calls through a single-slot dispatcher, so several panels (or several virtualised windows in the same panel) queue rather than running in parallel.
The number of decode requests currently in flight is surfaced as a status-bar widget:
Disassembling: 2
When the queue drains, the widget hides itself. This is also the easiest way to spot whether the disassembly view is genuinely busy or whether a UI freeze has another cause.
Reading [disasm-perf] logs¶
With hexana.disassembly.perf.logging = true, each decoded chunk emits a line like:
[disasm-perf] backend=redline arch=x86-64 bytes=131072 instr=24871 wall=83ms
backend—aotorredline.arch— the ISA the decoder ran.bytes— input length passed to Capstone (an instruction window, not the whole section).instr— instructions returned.wall— wall-clock time spent inside the decoder, excluding queue wait.
To compare backends on the same binary:
- Open the binary, scroll through the disassembly tab to populate the cache.
- Note the
wallvalues. - Toggle
hexana.disassembly.backend.redline, close and reopen the panel. - Repeat — chunks are decoded fresh, so the new
wallvalues are directly comparable.
Caveats¶
- Redline is experimental in 0.10. If you hit a crash or a decoder mismatch, switch back to AOT and file an issue with the
[disasm-perf]lines and the binary's architecture, OS, and platform. - The native slice is loaded once per IDE session. Toggling the Registry key while a disassembly is in flight is safe but cosmetically inconsistent — the in-flight chunk finishes on the old backend, subsequent chunks use the new one.
- Both backends share the same on-disk decode cache (an LRU keyed by
(section offset, length, ISA)). Switching backends does not invalidate cached results — and because the output is byte-for-byte identical, there is no need to.