Skip to content

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 26 Sep 12:48
· 22 commits to main since this release
v0.1.3
6917b5c

Highlights

  • Field names where they can be proved — the snapshot's surviving Field objects plus implicit accessor symbols, rendered as attributed comments: x0 = mem((local_0), 0x17); /* _FutureListener.result (off 0x18) */. The two sources are independent and agree on 40 of 41 entries (39/39 on x64), with zero conflicts.
  • The decompiler's output is valid Dart — dart analyze errors went 680,515 → 0 on a real Flutter app (412 files, 10,245 functions), and 0 across all 27 corpora. 87–92% of functions come out with structured control flow; the rest keep an honest gotoLabel + NOTE header.
  • Progressive mode — list first (dae libs / classes / functions / strings / fields / largest / callers / disasm), then decompile one class, method or library. dae getclass Foo takes 0.03 s against 1.9 s for a full export; stdout is the data channel, so it pipes.

What's changed

Decompiler (--decompile)

  • Structure, not goto. Dominators find the natural loops (back edge = header dominates its tail), diamonds become if/else, loop headers while, exits break/continue. Dart AOT merges identical function bodies, so those shared chunks are adopted; forward shared tails are duplicated; backward jumps to a non-header are genuinely irreducible loops and keep gotoLabel.
  • The output compiles. Machine syntax is rewritten (mem/memSet/memRead2/callIndirect/gotoLabel), names are sanitised into identifiers (mixin-application class names contain &), and each file opens with a pseudo-runtime preamble that states where the machine layer ends and Dart begins.
  • Object-pool constants are inlined: ldr x0, [PP, #0x17f8] becomes x0 = "Hello" /* pp+0x17f8 */ (1,922 literals on the Flutter app).
  • Field names, when provable. host_offset_or_field_id_ is a Smi and Smis are merged into the Mint cluster, so the Mint integer is the field's word index → byte offset = word × word_size → machine displacement = offset − 1. Pinned down four ways (four _FutureListener getters, _Uri.path as the 5th declared field, a generic class's unboxed bitmap, Error._stackTrace at word 1). The second route reads implicit getter/setter names (kind 6/7) whose body touches exactly one field; hand-written accessors are excluded because their names lie (get:_ignoreError reads _state).
  • Address fixes for appended snapshots (Mach-O LC_NOTE, and the 2.12–2.14 dart compile exe trailer → inner ELF). These had been decompiling the wrong bytes while every name-based metric stayed green; the gate now carries a prologue-rate floor (51–58% broken vs 91–100% correct).
  • Instructions: csel folds through cmp, brk is a terminator, frame/barrier instructions become // frame: comments rather than pretending to be data flow; unmapped lines dropped to single digits on most corpora and the count is printed in the run summary.

New outputs and commands

  • text/fields.txt + dae fields <binary> [pattern] — class, field, source (rec = snapshot, accessor = symbol-derived) and byte offset.
  • text/stubs.txt — instruction-table entries with no Code object (1,982 on the app, 1,295 named from their prologues), and allocation-stub names used at call sites.
  • call_edges.txt + callgraph.dot — direct call edges and indirect call sites.
  • dae info / libs / classes / functions / strings / fields / largest / callers / disasm / getclass / getmethod / getlib; --lib/--class/--func also produce a filtered export (object-layer dumps stay complete).

Gates (run on every change)

  • tests/dart_valid.rs — real dart analyze, zero errors expected.
  • tests/decompiler_shape.rs — brace balance per file, statement termination, structured-rate floor, and address self-consistency.
  • tests/field_names.rs — the two field-name routes must agree, zero conflicts, and every annotation in the output must exist in the recovered table (the no-fabrication rule).
  • tests/cli.rs — stdout purity and ASCII-only artifacts; tests/ground_truth.rs — differential against the binaries' own .symtab.

Numbers

arm64 sample Flutter app (412 files)
dart analyze errors 0 0 (was 680,515)
functions fully structured 89.2% 92.7%
named fields recovered 61 367
annotated field accesses 218 438

All 25 SDK-version regression samples still match their archived object-layer output byte for byte.

Install

cargo install dae-rs                             # crates.io
brew install ejfkdev/tap/dae                     # macOS / Linux (Homebrew)
scoop install dae                                # Windows (scoop bucket)

Or download the binary for your platform below (Windows/macOS/Linux × x64/arm64; x64 builds are UPX-compressed).