diff --git a/CHANGELOG.md b/CHANGELOG.md index 4465a87..85afd95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed + +- **Imported programs are now durably analyzed** — the one-shot project + bootstrap (`analyzeHeadless -import`) no longer passes `-noanalysis`. The + import previously skipped analysis at launch and relied on the bridge's + best-effort `program.save()` after a TCP `analyze`; that save silently + swallows failures, so a fresh session saw the imported program as + un-analyzed (`analyzed:false`, `function_count:0`) even though import + reported "Analysis complete", and `function list`/`decompile` returned + nothing. Analyzing inside the one-shot import is persisted by + `analyzeHeadless` on exit, so query sessions see the functions. + ## [0.2.2] ### Added diff --git a/src/ghidra/bridge.rs b/src/ghidra/bridge.rs index 4970e23..9700fd4 100644 --- a/src/ghidra/bridge.rs +++ b/src/ghidra/bridge.rs @@ -310,9 +310,18 @@ fn apply_java_home(cmd: &mut Command, ghidra_install_dir: &Path) { /// the persistent bridge with `-import` (which holds the imported program inside /// HeadlessAnalyzer's transaction for the bridge's whole life and only commits /// it during teardown — a commit we then race by killing the JVM), this run -/// imports, saves, commits the project, and exits on its own. The persistent -/// bridge can then open the already-committed program in `-process` mode, where -/// saves are durable and no teardown commit is required. +/// imports, ANALYZES, saves, commits the project, and exits on its own. The +/// persistent bridge can then open the already-committed, already-analyzed +/// program in `-process` mode, where saves are durable and no teardown commit +/// is required. +/// +/// Analysis happens HERE — in the one-shot run — not over TCP after the bridge +/// starts. Previously the run passed `-noanalysis` and relied on the bridge's +/// `program.save()` after a TCP `analyze`; that save is best-effort and +/// silently swallows failures, so a fresh session saw an un-analyzed program +/// (0 functions) even though import reported "Analysis complete". Analyzing in +/// the one-shot import is persisted by `analyzeHeadless` on exit and is immune +/// to that failure mode. pub fn import_oneshot( project_path: &Path, binary_path: &Path, @@ -340,7 +349,6 @@ pub fn import_oneshot( .arg(&ghidra_project_name) .arg("-import") .arg(binary_path) - .arg("-noanalysis") .arg("-overwrite"); apply_java_home(&mut cmd, ghidra_install_dir); diff --git a/src/main.rs b/src/main.rs index fa4fe81..eea2db2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -525,9 +525,9 @@ fn run_with_bridge(cli: Cli) -> anyhow::Result<()> { // hang-proof cases (see docs/plans/prescript-fix.md §3.3): // 1. Bridge already running -> TCP import into the live project. // 2. Project exists, no bridge -> fast launch (Project mode), TCP import. - // 3. Brand-new project -> bootstrap via `-import -noanalysis` - // (only `-import` can create a project; analysis is skipped at - // launch and driven over TCP below). + // 3. Brand-new project -> bootstrap via `-import` + // (only `-import` can create a project; it analyzes and persists + // the program durably, so query sessions see the functions). // The launch is bounded (the bridge binds its socket before analysis); // analysis afterwards runs as an unbounded TCP operation. let (client, program_name) =