Fission

Fission - reverse engineering workspace [![CI](https://github.com/sjkim1127/Fission/actions/workflows/ci.yml/badge.svg)](https://github.com/sjkim1127/Fission/actions/workflows/ci.yml) [![Rust](https://img.shields.io/badge/Rust-1.85%2B-orange.svg)](https://www.rust-lang.org/) [![License: AGPL-3.0-or-later](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.html)

Fission

Fission is a Rust-native reverse-engineering and binary decompilation workspace. It is built around a Fission-owned intermediate-representation pipeline, with Ghidra-style Sleigh semantics feeding Rust-owned NIR, HIR, structuring, rendering, automation, and quality gates.

The project goal is not only to decode instructions. The goal is to produce decompiler output that is mechanically traceable, semantically defensible, and eventually readable enough to be useful in day-to-day reverse engineering.

This README is intentionally long. It is a practical orientation document for contributors, operators, and future agents working in the repository. The shorter source-of-truth references remain in docs/, AGENTS.md, and the crate-local AGENTS.md files.

Table of Contents

Project Status

Fission should be treated as a system with strict ownership boundaries. If a semantic issue is discovered in the final pseudocode, the fix should land where the behavior is owned, not in the renderer or README-level presentation.

Quick Start

Clone

git clone https://github.com/sjkim1127/Fission.git
cd Fission

Install Requirements

rustup update
cargo install cargo-nextest --locked

Pull Required Runtime Assets

The Rust workspace can build without every large resource, but real decompilation needs Sleigh specifications and some quality lanes need signature or support data. utils/ is not checked into git; pull the bundled release asset instead:

mkdir -p utils
curl -L --fail --show-error \
  "https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
  -o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1

Build the CLI

cargo build -p fission-cli --release
./target/release/fission_cli --help

For local iteration, prefer --profile quick-release over --release: [profile.release] uses fat LTO + codegen-units = 1 for shipping-quality runtime perf, which serializes codegen/linking and dominates rebuild time. [profile.quick-release] (workspace Cargo.toml) drops those two knobs but keeps opt-level = 3, so decomp-speed/output checks stay representative — measured ~2.9x faster on a touch-one-core-crate rebuild (44s → 15s), with byte-identical output on the real-binary regression set and ~9% slower runtime on the heaviest measured function. Binaries land in target/quick-release/. Use plain --release for anything going into a benchmark run, a release build, or a perf measurement that needs to match production codegen.

cargo build -p fission-cli --profile quick-release
./target/quick-release/fission_cli --help

Run Common Checks

cargo nextest run -p fission-pcode
cargo check -p fission-pcode
cargo check -p fission-decompiler
cargo check -p fission-automation

Repository Tour

The repository is organized as a Cargo workspace plus documentation, resource bundles, scripts, CI workflows, and reference-only vendor trees.

Path Purpose
crates/ Rust workspace crates.
docs/ Architecture, CLI, evaluation, release, versioning, onboarding, roadmap, and ADR documents.
utils/ Checked-in resource bundle material such as Sleigh specs and Ghidra data manifests.
vendor/ Reference-only third-party source trees and datasets.
scripts/ Local helper scripts for testing, corpus work, and maintenance.
.github/workflows/ CI, reusable workflow jobs, release tag workflow, fuzzing, and heavy gates.
image/ Project logo and icon assets.
target/ Local Cargo build output; not source.

Primary Documentation

Architecture in One Page

The shortest useful architecture summary is: binary bytes are loaded by fission-loader, instruction semantics are lifted through fission-sleigh, canonical IR and decompiler semantics are owned by fission-pcode, orchestration is performed by fission-decompiler, and user or automation surfaces consume those results without inventing new semantic policy.

Binary bytes
  -> fission-loader
  -> fission-static facts and provenance
  -> fission-sleigh decode and p-code lift
  -> fission-pcode NIR
  -> fission-pcode HIR
  -> structuring, cleanup, rendering
  -> fission-decompiler result contracts
  -> CLI, TUI, GUI, automation, reports

Core Pipeline

  1. Load bytes from a supported binary format.
  2. Classify format, architecture, sections, symbols, imports, exports, and executable regions.
  3. Attach provenance and identity hints without changing IR semantics.
  4. Prepare static facts needed by the decompiler.
  5. Decode instructions through Sleigh language definitions.
  6. Emit raw p-code in a form that can be compared against parity expectations.
  7. Lower p-code into Fission NIR.
  8. Normalize NIR while preserving semantics.
  9. Recover type, calling convention, stack, pointer, and data-flow hints when evidence supports them.
  10. Build HIR as a human-readable representation.
  11. Apply structuring passes using CFG, dominance, post-dominance, SCC, and proof evidence.
  12. Render pseudocode without inventing missing semantics.
  13. Report telemetry through canonical counters.
  14. Use automation lanes to compare quality over time.

Crate Guide

Crate Role Editing rule
fission-script Script-facing helpers and experiments for automation or user scripting. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-automation Quality lanes, reports, summaries, and go/stop automation signals. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-core Shared core types, path configuration, resource roots, and utilities. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-loader Binary loading, sections, symbols, relocations, virtual types, and identity reports. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-pcode Canonical IR, NIR, HIR, structuring, CFG analysis, type hints, and printer. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-decompiler Decompilation orchestration, request/result contracts, Rust-Sleigh bridge, and render routing. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-signatures Signature datasets and lookup logic. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-static Static analysis facts, native preparation, discovery, xrefs, patches, and strings. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-dynamic Dynamic-analysis support surfaces. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-ttd Time-travel and trace-adjacent support. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-emulator Pure-Rust P-Code execution engine, OS HLE, TTD recording, and taint-aware concolic execution. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-solver Pure-Rust SMT/constraint engine: SymExpr AST, Solver node registry, path condition management. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-plugin Plugin contracts, manager, loader, and runtime hooks. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-cli Command-line product surface. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-sleigh Sleigh decode and p-code lift runtime. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-ai AI-facing assistance surfaces and integration points. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-tui Terminal UI with ratatui-based interaction. Keep behavior inside its owner; do not leak semantic policy to callers.
fission-dioxus Pure Rust desktop GUI surface. Keep behavior inside its owner; do not leak semantic policy to callers.

fission-script

Script-facing helpers and experiments for automation or user scripting.

fission-automation

Quality lanes, reports, summaries, and go/stop automation signals.

fission-core

Shared core types, path configuration, resource roots, and utilities.

fission-loader

Binary loading, sections, symbols, relocations, virtual types, and identity reports.

fission-pcode

Canonical IR, NIR, HIR, structuring, CFG analysis, type hints, and printer.

fission-decompiler

Decompilation orchestration, request/result contracts, Rust-Sleigh bridge, and render routing.

fission-signatures

Signature datasets and lookup logic.

fission-static

Static analysis facts, native preparation, discovery, xrefs, patches, and strings.

fission-dynamic

Dynamic-analysis support surfaces.

fission-ttd

Time-travel and trace-adjacent support.

fission-plugin

Plugin contracts, manager, loader, and runtime hooks.

fission-cli

Command-line product surface.

fission-sleigh

Sleigh decode and p-code lift runtime.

fission-ai

AI-facing assistance surfaces and integration points.

fission-tui

Terminal UI with ratatui-based interaction.

fission-dioxus

Pure Rust desktop GUI surface.

CLI Guide

The CLI is the fastest product surface for validating loader and decompiler behavior locally. It should expose capabilities, not own semantic fixes.

fission_cli --help
fission_cli info <binary>
fission_cli list <binary>
fission_cli decomp <binary> --addr 0x1400010a0
fission_cli decomp <binary> --all --json

Resource Bundle

Fission keeps large or operationally specific data out of git and ships it as a standalone utils/ resource bundle (fission-utils.tar.gz) attached to the assets-v1 GitHub Release.

mkdir -p utils
curl -L --fail --show-error \
  "https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
  -o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1
fission_cli resources status

Decompiler Quality Loop

Quality work starts from a concrete function or row, not a vague aggregate. The loop below is the standard operating model for decompiler-quality changes.

  1. Anchor the exact row, binary, address, function name, behavior status, and quality scores.
  2. Record stdout, stderr, line count, byte count, and static feature gaps.
  3. Find the canonical owner: Sleigh, NIR, type recovery, structuring, cleanup, printer, benchmark, or automation.
  4. Add focused coverage for the invariant.
  5. Make the smallest invariant-based production change.
  6. Run the targeted test first.
  7. Run relevant crate-level tests and checks.
  8. Run the focused source-semantic row with stale caches disabled when available.
  9. Inspect artifacts, not only aggregate numbers.
  10. Run broader smoke or automation checks after a focused improvement.
  11. Report whether behavior mechanically changed and whether quality improved.

Quality Evidence Rules

NIR and HIR Policy

NIR and HIR have different contracts.

Layer Contract Consequence
NIR Semantically identical to the source behavior. Correctness and parity are the highest priorities.
HIR Human-readable pseudocode derived from correct semantics. Readability can be prioritized when the underlying semantics remain traceable.

Sleigh Runtime

The Sleigh runtime is the decode and raw p-code lift path. It should execute .sla ConstructTpl semantics rather than accumulating manual opcode mappings.

Loader Policy

The loader owns binary format parsing and metadata provenance. It does not own decompiler repair.

  1. detect stage in the loader pipeline.
  2. probe/load-spec stage in the loader pipeline.
  3. map stage in the loader pipeline.
  4. symbols stage in the loader pipeline.
  5. finalize stage in the loader pipeline.

Static Facts

Static facts help the decompiler, but they are not the final semantic owner. The fact layer should provide evidence, provenance, and analysis services.

Dynamic Analysis and Emulator

Fission includes a pure-Rust dynamic analysis engine (fission-emulator) capable of executing arbitrary x86/x86-64 P-Code sequences produced by Sleigh, without requiring any external runtime like QEMU or Unicorn.

The emulator is a core pillar for future dynamic reasoning capabilities:

Architecture

fission-emulator
  ├── core.rs          — Emulator state, main run loop, register I/O, TTD hooks
  ├── pcode/
  │   ├── eval.rs      — P-Code opcode evaluator (taint-aware)
  │   └── state.rs     — MachineState: address spaces + shadow memory
  ├── arch/            — Architecture descriptors, calling conventions
  ├── os/
  │   ├── linux/       — Linux syscall HLE (read, write, mmap, brk, exit, ...)
  │   ├── windows/     — Windows HLE stubs
  │   └── bare_metal/  — No-OS / embedded HLE
  ├── sym/
  │   └── mod.rs       — SymbolicExecutor: TTD-backed concolic path exploration
  ├── trace.rs         — TraceLog: per-instruction audit trail
  ├── snapshot.rs      — Lightweight snapshot helpers
  └── loader.rs        — Binary loading bridge for the emulator

CLI Integration

# Emulate a binary (default: auto-detect OS, no limits)
fission_cli run <binary>

# Limit to N instructions
fission_cli run <binary> --max-inst 10000

# Provide stdin mock data
fission_cli run <binary> --stdin "hello\n"

# Emit full instruction trace (JSON)
fission_cli run <binary> --trace --json

# Enable TTD recording (snapshot every 1000 instructions)
fission_cli run <binary> --ttd-record 1000

# Seek TTD to step N (rewind and replay from that point)
fission_cli run <binary> --ttd-record 1000 --ttd-seek 5000

# Enable concolic path exploration
fission_cli run <binary> --ttd-record 500 --sym-explore

Operating System HLE

The emulator uses High-Level Emulation (HLE) rather than emulating an actual OS kernel. Each architecture+OS pair has its own HLE handler:

OS HLE Handler Key syscalls
Linux x86-64 os/linux/mod.rs read, write, mmap, brk, exit_group, open, close, fstat, stat
Windows x86-64 os/windows/hle.rs VirtualAlloc, HeapAlloc, WriteFile, ExitProcess, and common NT stubs
Bare Metal os/bare_metal/mod.rs Minimal: semihosting stubs only

HLE handlers can intercept function calls by name (via PLT/IAT hooks) or by recognized calling patterns.

Calling Convention Support

The arch/ subsystem provides architecture-agnostic calling convention helpers:

Currently supported: System V AMD64 ABI (Linux x86-64), Microsoft x64 (Windows), and a stub for ARM AArch64.


Time-Travel Debugging (TTD)

The fission-ttd crate provides a deterministic execution recorder and replayer. It is the backbone for all forms of non-linear execution analysis.

How TTD Works

  1. Recording Phase — As the Emulator executes, every N instructions (configurable via --ttd-record <N>) it captures:
    • Complete CPU register state (RegisterState)
    • Memory write deltas: (address, old_bytes, new_bytes)
    • Shadow state (taint) deltas: (space_id, address, old_node_id, new_node_id)
  2. Snapshot Storage — Snapshots are stored in a bounded ring buffer (TTDRecorder) with configurable capacity. Older snapshots are evicted when capacity is reached.

  3. Seeking / RewindingEmulator::ttd_seek(step) locates the most recent snapshot at or before step, then restores:
    • All GP registers from RegisterState
    • All memory bytes from MemoryDelta records
    • All symbolic taint mappings from ShadowDelta records
  4. Replay — After rewinding, emulator.run() continues forward from the restored state.

TTD Data Structures

Type Crate Purpose
TTDRecorder fission-ttd Manages the ring buffer of ExecutionSnapshots
ExecutionSnapshot fission-ttd Single-point snapshot: registers + memory + shadow deltas
MemoryDelta fission-ttd Before/after memory diff for one address range
ShadowDelta fission-ttd Before/after taint AST node diff for one byte position
RegisterState fission-ttd Full x86-64 GP register snapshot

Symbolic Execution and Taint Engine

Fission’s taint and symbolic engine is a pure-Rust implementation — no Z3 bindings, no C++ FFI, no external SMT dependencies. It is designed for long-term maintainability and architecture-agnostic reasoning.

Two-Layer Architecture

fission-solver            — Pure-Rust SMT/Constraint engine
  ├── ast.rs              — SymExpr: the symbolic expression AST
  └── solver.rs           — Solver: node registry, path conditions, SAT stub

fission-emulator          — Concrete + Symbolic execution
  └── pcode/
      ├── state.rs        — MachineState: shadow_memory (taint map)
      └── eval.rs         — Evaluator: taint propagation per P-Code opcode

SymExpr AST Nodes

Every symbolic value is represented as a node in the SymExpr tree:

Variant Description
Const { val, size } Concrete bitvector constant
Var { id, name, size } Named symbolic variable (e.g. stdin_0x4000)
Add(a, b) / Sub(a, b) / Mul(a, b) Integer arithmetic
And(a, b) / Or(a, b) / Xor(a, b) Bitwise operations
Shl(a, b) / Lshr(a, b) Shift operations
Eq(a, b) / Neq(a, b) / Ult(a, b) / Ule(a, b) Comparisons (return 1-bit)
Ite { cond, t, f } If-then-else
Extract { expr, lsb, size } Bit extraction
Concat(a, b) Bitvector concatenation

Shadow Memory (Taint State)

The MachineState holds a parallel “shadow” layer alongside the concrete memory:

// shadow_memory: (space_id, byte_address) -> SymNodeId
pub shadow_memory: HashMap<(u64, u64), u32>

When a concrete byte is written, its shadow entry is cleared. When a symbolic value is written, its shadow entry is updated with the corresponding AST node ID.

Taint Propagation Table

P-Code Op Taint Behavior
COPY Propagate source shadow to destination
LOAD Propagate shadow from RAM byte to output varnode
STORE Propagate shadow from source varnode to RAM byte
INT_ADD If either input is tainted, build SymExpr::Add(a, b) and store new node
INT_SUB Similar: build SymExpr::Sub
Other ops Currently concrete-only (no taint propagation yet)

Taint Sources

The primary taint source is stdin. In os/linux/mod.rs, the sys_read(fd=0, ...) handler:

  1. Reads bytes from stdin_buffer (the --stdin mock).
  2. Writes them into RAM as concrete bytes.
  3. For each byte, calls solver.register_var("stdin_<addr>", 1) to create a SymExpr::Var.
  4. Tags the corresponding shadow_memory entries with the new node ID.

From that point forward, any P-Code operation that reads those bytes will propagate the taint forward into new SymExpr constraint trees.


Concolic Path Exploration

Concolic (concrete + symbolic) execution combines real execution with symbolic state to explore multiple code paths automatically.

How It Works

  1. The emulator runs normally in concrete mode, recording TTD snapshots along the way.
  2. Every CBranch (conditional branch) P-Code instruction emits a SymBranch event containing:
    • The TTD step index at the branch point
    • The current PC value
    • Whether the branch was taken or not
    • The alternate target (address or relative P-Code index)
  3. After the current path terminates, the SymbolicExecutor pops unexplored branches from a queue.
  4. It rewinds the emulator to the snapshot closest to the branch step via ttd_seek().
  5. It forces the PC (or P-Code index) to the alternate target and resumes execution.
  6. This continues until the exploration queue is empty.
Path 1:  [A] → [B] → [D] → halt        (branch at B taken = true)
Rewind to B
Path 2:  [A] → [B] → [C] → [E] → halt  (branch at B taken = false)

SymbolicExecutor

The SymbolicExecutor (sym/mod.rs) is the exploration driver:

pub struct SymbolicExecutor {
    pub emu: Emulator,
    pub queue: Vec<SymBranch>,  // unexplored branch events
}

It calls emu.run() in a loop, drains emu.sym_events into the queue, and rewinds to the next unexplored branch. Each new execution path may reveal additional branches, which are added to the queue.

Future: Full Symbolic Mode

The current implementation is concolic scaffolding: it explores paths by forcing PC values, but does not yet invert branch conditions symbolically via the Solver. The planned evolution:

  1. When a CBranch is encountered and a taint variable is used in the branch condition, add the negated constraint !condition to solver.assertions.
  2. Call solver.check_sat() to verify the alternate path is feasible.
  3. Use solver.get_value(var_id) to obtain a concrete input that triggers the alternate path.
  4. Replay with that concrete input instead of forcing PC.

This requires implementing the DPLL/CDCL bit-blasting core inside fission-solver, which is the next development milestone.

Structuring Model

The active structuring path is graph-oriented and proof-driven. It should use deterministic collapse rules and explicit fallback when legality is incomplete.

Pass Pipeline Rules

There are two independent pass-orchestration tracks; neither subsumes the other, because they operate on different IR shapes. Current migration status and the full per-stage backlog live in PROJECT.md.

Determinism

Decompiler output must be identical across separate process runs of the same binary (AGENTS.md Core Rule 4). std::collections::HashMap/HashSet use a per-process-random RandomState by default; any unsorted iteration over one that feeds a .first()/.find_map()-style pick (not just a .contains()/.get() membership check) is a real nondeterminism bug, not a style nit — two were found and fixed this way in fission-pcode::midend::structuring. When adding a new HashMap/HashSet in fission-pcode::midend or fission-midend-structuring, prefer the crate-local fixed-seed alias (rustc_hash::FxBuildHasher, already the default there) over std::collections::HashMap directly, and never iterate either collection to pick a specific value without sorting first.

Telemetry and Reports

Telemetry is useful only if the same counter means the same thing everywhere. Fission keeps canonical decompiler counters in NirBuildStats and projects them outward.

Testing Matrix

Scope Default command Use when
pcode tests cargo nextest run -p fission-pcode NIR, HIR, structuring, printer, and type-hint work.
pcode check cargo check -p fission-pcode Compile validation after semantic changes.
decompiler check cargo check -p fission-decompiler Orchestration or Rust-Sleigh glue changes.
automation check cargo check -p fission-automation Telemetry or reporting changes.
core tests cargo nextest run -p fission-core Resource path and shared core changes.
CLI build cargo build -p fission-cli --release Product and benchmark validation requiring the release CLI.
workspace check cargo check --workspace Broad compile confidence before larger handoff.

CI and Release

CI source of truth lives in .github/workflows/. Reusable workflows keep the main pipelines smaller and make heavy checks explicit.

Release tags should be shipped through Release Tag (CI green), which tags only a commit whose push run has already passed the required CI path.

Development Workflow

  1. Read the nearest AGENTS.md before editing a scoped area.
  2. Confirm the owner layer before changing behavior.
  3. Inspect existing tests and local patterns.
  4. Add focused coverage for new invariants.
  5. Make scoped production changes.
  6. Run targeted validation.
  7. Run crate-level validation.
  8. Inspect Git status and stage only intended hunks.
  9. Report both mechanical change and quality impact.

Dirty Worktree Discipline

Security and Malware Sample Policy

Reverse-engineering repositories often touch untrusted binaries. Treat samples and externally sourced executables as hostile inputs.

Contributor Notes

Troubleshooting

Symptom First check
Missing Sleigh specs Pull fission-utils.tar.gz from the assets-v1 release (see Resource Bundle) and check resource status.
CLI cannot find resources Check FISSION_RESOURCE_ROOT, --resource-root, and PathConfig::detect behavior.
Raw p-code mismatch Start in fission-sleigh before interpreting NIR output.
NIR is wrong but p-code is right Investigate NIR materialization, normalization, or type hint application.
HIR is unreadable but NIR is right Investigate structuring, cleanup, or printer consume behavior.
Report counters disagree Trace the counter back to NirBuildStats.
Loader identifies unsupported container Extract an executable child explicitly instead of raw-loading the container.
A test passes locally but CI fails Check LFS pulls, OS-specific paths, feature flags, and reusable workflow inputs.

Glossary

Term Meaning
CFG Control-flow graph.
HIR High-level intermediate representation for readable pseudocode.
NIR Normalized intermediate representation with strict semantic requirements.
P-code Ghidra-style low-level instruction semantics representation.
Sleigh Language specification system used for instruction decode and semantics.
Dominance Graph relation used to reason about control-flow ownership.
Post-dominance Graph relation used to reason about exits and structured regions.
SCC Strongly connected component, often used for loop analysis.
RegionProof Evidence that a region can be safely promoted during structuring.
NirBuildStats Canonical NIR telemetry contract.
FactStore Aggregated facts and provenance consumed by decompilation contexts.
FID Function identification through signatures.
LFS Git Large File Storage.
Taint A label on data indicating it originated from a symbolic (untrusted) source.
Shadow Memory Parallel memory map tracking symbolic AST node IDs alongside concrete bytes.
Concolic Execution that combines concrete runs with symbolic state to explore multiple paths.
HLE High-Level Emulation: OS syscall interception without full kernel emulation.
TTD Time-Travel Debugging: record/replay execution via memory and register snapshots.
SymExpr Symbolic expression AST node in fission-solver.
SAT Boolean satisfiability problem. Used to check if a path constraint can be fulfilled.

Roadmap

Appendix A: Commands

Build

cargo build -p fission-cli --release
cargo check --workspace

Test

cargo nextest run -p fission-pcode
cargo nextest run -p fission-core

Format and lint

cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings

CLI smoke

./target/release/fission_cli --help
./target/release/fission_cli info <binary>
./target/release/fission_cli list <binary>

Resources

mkdir -p utils
curl -L --fail --show-error \
  "https://github.com/sjkim1127/Fission/releases/download/assets-v1/fission-utils.tar.gz" \
  -o /tmp/fission-utils.tar.gz
tar -xzf /tmp/fission-utils.tar.gz -C utils --strip-components=1
fission_cli resources status

Appendix B: Review Checklists

Semantic change

Loader change

Sleigh change

Automation change

Documentation change

Appendix C: File Ownership Index

Path Owner meaning
AGENTS.md Repository-level contributor instructions.
Cargo.toml Workspace members and profile settings.
crates/fission-pcode/src/nir/ Core NIR/HIR implementation.
crates/fission-pcode/src/nir/structuring/ Structuring algorithms and region collapse.
crates/fission-pcode/src/nir/types/ NIR/HIR type contracts and build stats.
crates/fission-decompiler/ Decompiler orchestration and Rust-Sleigh bridge.
crates/fission-sleigh/ Sleigh runtime.
crates/fission-static/src/analysis/ Static facts and analysis services.
crates/fission-loader/src/loader/ Binary loader implementations.
crates/fission-automation/src/report/ Automation report implementation.
crates/fission-cli/src/cli/ CLI command ownership.
docs/architecture/ARCHITECTURE.md Architecture source of truth.
docs/adr/ Architectural decision records.
utils/ Checked-in resource bundle material.
vendor/ Reference-only third-party material.

Appendix D: Quality Investigation Playbook

Raw p-code suspect

  1. Compare raw p-code against a known-good reference.
  2. Check Sleigh token cursor placement.
  3. Check dynamic memory output materialization.
  4. Add a row-level canary before broad benchmark runs.

NIR suspect

  1. Inspect p-code to NIR lowering.
  2. Check temporary, register, stack, and memory varnode handling.
  3. Inspect normalization rules and wave stats.
  4. Validate semantic preservation before HIR cleanup.

Type recovery suspect

  1. Check calling convention source.
  2. Check stack slot and parameter hints.
  3. Check import and function hints.
  4. Avoid output-only substitution.

Structuring suspect

  1. Inspect CFG shape.
  2. Inspect dominance and post-dominance.
  3. Inspect SCC and loop facts.
  4. Check RegionProof and collapse readiness.
  5. Prefer explicit fallback over invalid structure.

Printer suspect

  1. Confirm HIR is already correct.
  2. Keep printer consume-only.
  3. Avoid reconstructing missing control flow.
  4. Add snapshot coverage if formatting changes.

Appendix E: Documentation Index

Field Guide: Practical Rules by Area

01. P-code parity

Verify instruction semantics before diagnosing decompiler output.

02. NIR materialization

Preserve source behavior even when output is temporarily verbose.

03. HIR cleanup

Improve readability only after the semantic basis is correct.

04. Type hints

Promote evidence-backed types and keep provenance inspectable.

05. Stack recovery

Model stack slots consistently across calls, locals, and spills.

06. Pointer recovery

Prefer data-flow-backed pointer reasoning over text substitution.

07. Array recovery

Recover indexed forms when stride and base evidence are present.

08. Struct recovery

Recover field access only when layout evidence supports it.

09. Calling convention

Derive parameters and returns from ABI facts and observed uses.

10. Loop structuring

Use SCC and dominance facts before emitting structured loops.

11. Switch recovery

Use jump-table evidence and bounds before emitting switch syntax.

12. Goto fallback

Use explicit fallback when legal structure is not proven.

13. Printer formatting

Render the model; do not create semantic facts.

14. Loader identity

Attach evidence without changing parse semantics.

15. Resource lookup

Route through resource roots and path config.

16. Automation reports

Project canonical metrics and keep outputs deterministic.

17. CI gates

Prefer focused reusable jobs with explicit inputs.

18. Release tags

Tag only after successful push CI for the exact commit.

19. Vendor reference

Consult for invariants without copying or depending on code.

20. Documentation

Keep claims grounded in current source and docs.

Contributor Playbooks

This section expands the repository rules into concrete scenarios. It is intentionally operational: each playbook describes where to start, what to avoid, and what evidence should exist before claiming the work is done.

01. Fixing a raw p-code mismatch

Start in fission-sleigh; compare emitted p-code before touching NIR or HIR.

02. Fixing an NIR materialization bug

Start in fission-pcode/src/nir; preserve exact source behavior before readability work.

03. Improving HIR readability

Start from correct NIR; prefer cleanup passes over printer substitutions.

04. Changing structuring logic

Start in fission-pcode/src/nir/structuring; require CFG evidence and proof completeness.

05. Adding a new NIR pass

Implement a pass with declared analysis dependencies and accurate changed status.

06. Debugging an if-else recovery failure

Inspect dominance, post-dominance, and region exits before modifying emitted syntax.

07. Debugging a loop recovery failure

Inspect SCCs, loop headers, latches, exits, and break or continue candidates.

08. Debugging a switch recovery failure

Inspect jump table evidence, bounds, case targets, and default target handling.

09. Cleaning unnecessary temporaries

Confirm the temporary has no semantic or ordering role before removing it.

10. Recovering function parameters

Use ABI facts, call uses, stack/register evidence, and type context together.

11. Recovering local variables

Tie stack slots, stores, loads, and lifetimes to stable local names.

12. Recovering return values

Check accumulator registers, call sites, and observed return uses.

13. Recovering pointer arithmetic

Prefer base plus offset or indexed forms only when data-flow evidence supports them.

14. Recovering arrays

Require stable stride, base object, and index expression evidence.

15. Recovering struct fields

Require layout evidence before rendering field access.

16. Changing loader format detection

Fail closed for unknown or unsupported families; never hide uncertainty as raw bytes.

17. Adding loader provenance

Attach evidence without changing parsing semantics or decompiler behavior.

18. Changing import handling

Keep true imports, import thunks, undefined externals, and debug-only symbols distinct.

19. Changing export handling

Preserve symbol provenance and loader-owned function views.

20. Changing resource lookup

Route through path config and resource roots; do not embed local absolute paths.

21. Changing utility manifests

Keep manifests deterministic and explain what data is required at runtime.

22. Changing signature lookup

Keep signature hits evidence-backed and separate from semantic repair.

23. Changing automation reports

Project NirBuildStats and other canonical counters; do not redefine metrics.

24. Changing JSON report contracts

Version or document the contract and keep output deterministic.

25. Changing CLI output

Keep CLI as a surface; do not fix semantics in formatting code.

26. Changing CLI command parsing

Separate compatibility shims from command ownership and behavior.

27. Changing TUI behavior

Preserve backend contracts and avoid UI-specific semantic rules.

28. Changing Dioxus GUI behavior

Consume shared contracts and avoid duplicate function filtering rules.

29. Changing AI integration surfaces

Keep AI assistance advisory and preserve deterministic core behavior.

30. Changing plugin contracts

Keep contracts explicit, stable, and separated from core crate internals.

31. Changing dynamic-analysis support

Keep dynamic evidence labeled and do not blur it with static facts.

32. Changing time-travel support

Keep trace-derived facts explicit and reproducible.

33. Adding a new test fixture

Document provenance, architecture, compiler, and why the fixture is useful.

34. Adding a regression test

Name the invariant, not just the failing sample.

35. Updating snapshots

Inspect semantic meaning before accepting changed text.

36. Investigating benchmark movement

Compare exact rows, artifacts, scores, stdout, stderr, and feature gaps.

37. Investigating a pass-count change

Trace whether the change is semantic, presentational, telemetry-only, or noise.

38. Investigating a size change

Inspect line count and byte count together with readability and semantics.

39. Investigating a CI-only failure

Check OS, LFS resources, workflow inputs, feature flags, and rust version.

40. Investigating a resource-missing failure

Check LFS pull scope, resource roots, and CLI resource status.

41. Investigating a panic

Capture command, input, backtrace, crate owner, and minimal reproducer.

42. Investigating non-determinism

Check map iteration, filesystem order, random seeds, timestamps, and local paths.

43. Changing dependencies

Justify long-term maintenance value and avoid dependency shortcuts for core semantics.

44. Consulting Ghidra

Use it for invariants and expected behavior, not copied implementation.

45. Consulting RetDec

Use it as reference material without creating runtime dependency.

46. Touching vendor trees

Do not add production links, shell-outs, bindings, or copied shortcuts.

47. Touching utils/

Use existing loaders and manifests instead of bypassing resource configuration.

48. Writing architecture docs

State owner boundaries and avoid implying surface layers own semantics.

49. Writing user docs

Prefer commands and observed behavior over aspiration.

50. Writing troubleshooting docs

Map symptom to first owner and first command.

51. Writing release notes

Separate features, fixes, quality movement, and known limitations.

52. Preparing a commit

Stage intended hunks only and keep unrelated dirty work untouched.

53. Preparing a PR

Lead with behavior change, validation, and residual risk.

54. Reviewing a PR

Prioritize bugs, regressions, missing tests, and ownership drift.

55. Refactoring shared code

Keep behavior stable unless the refactor explicitly includes a measured semantic change.

56. Adding an abstraction

Add it only when it removes real duplication or encodes a real invariant.

57. Deleting legacy code

Prove the active path no longer depends on it and keep compatibility expectations visible.

58. Changing telemetry names

Check every consumer and avoid parallel meanings.

59. Changing public structs

Consider CLI JSON, GUI, automation, and downstream compatibility.

60. Changing error types

Keep errors typed enough for users and automation to act on.

61. Changing logging

Keep logs useful for debugging without making tests flaky.

62. Changing performance-sensitive paths

Measure before and after when the change affects common loops.

63. Changing memory-heavy paths

Check large binaries and avoid unbounded accumulation.

64. Changing parser code

Use structured readers and bounds checks rather than ad hoc slicing.

65. Changing graph algorithms

Prefer explicit graph facts over lexical ordering or sample-specific assumptions.

66. Changing dataflow analysis

Document convergence, lattice meaning, and budget behavior.

67. Changing fixed-point loops

Make termination, changed status, and budget behavior inspectable.

68. Changing type inference

Keep confidence and provenance visible; avoid overconfident names.

69. Changing ABI handling

Keep architecture and calling convention boundaries explicit.

70. Changing x86 behavior

Validate exact sample first, then the broader x86/x86-64 family.

71. Changing non-x86 behavior

Do not regress x86/x86-64 priority while expanding breadth.

72. Changing docs only

Do not claim semantic improvement from documentation changes.

73. Changing logo or README assets

Keep icon and README logo responsibilities separate.

Review Question Bank

Use these questions during code review or before handing off a quality cycle.

Maintainer Handoff Template

Use this template when handing off a substantial decompiler-quality change.