[PR #4018] [MERGED] feat(ghidra): support raw big-endian m68k analysis #5991

Closed
opened 2026-10-31 01:11:01 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4018
Author: @icedac
Created: 10/15/2026
Status: ✅ Merged
Merged: 10/16/2026
Merged by: @morluto

Base: main ← Head: feat/ghidra-raw-m68k


📝 Commits (2)

  • bf68478 feat(ghidra): support raw big-endian m68k analysis
  • 8807530 test(ghidra): verify raw m68k analysis with real Ghidra

📊 Changes

23 files changed (+742 additions, -187 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md (+8 -6)
📝 docs/raw-binary.md (+45 -13)
📝 docs/testing.md (+10 -0)
📝 package.json (+1 -0)
➕ scripts/verify-real-ghidra-m68k.mjs (+296 -0)
📝 src/contracts/toolOutputReferences.test.ts (+11 -4)
📝 src/contracts/toolOutputSchemaGroups.ts (+8 -2)
📝 src/domain/analysisSnapshot.test.ts (+67 -58)
📝 src/domain/analysisSnapshot.ts (+7 -2)
📝 src/domain/binaryTarget.ts (+2 -1)
📝 src/domain/binaryTargetTypes.ts (+12 -1)
📝 src/domain/evidence.test.ts (+33 -27)
📝 src/domain/evidence.ts (+5 -1)
📝 src/domain/rawBinary.test.ts (+54 -0)
📝 src/domain/rawBinary.ts (+13 -3)
📝 src/ghidra/GhidraLoadImageValues.ts (+2 -2)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+31 -0)
📝 src/ghidra/GhidraProvider.ts (+2 -1)
📝 src/ghidra/GhidraProviderClient.ts (+1 -1)
📝 src/ghidra/GhidraRawBinaryLanguage.ts (+13 -4)

...and 3 more files

📄 Description

Summary

Support explicitly selected headerless big-endian Motorola 68000 images through REA's existing local Ghidra raw-binary lane. This follows the shape of #2079 (raw NMOS 6502): the caller supplies architecture: "m68k" and an entry address, and Ghidra imports the file at that address with 68000:BE:32:default.

No issue is linked. The change extends the existing static, read-only raw-binary admission (#1868, #2079) with one more CPU family. It adds no provider, target execution, injection, mutation, or protection recovery, so a separate maintainer scope decision does not apply under CONTRIBUTING's fail-closed rule. I am happy to open an issue first if you prefer to track it there.

Problem and expected behavior

Before this change, open_binary and the CLI reject architecture: "m68k", so headerless 68000-family code (Amiga/Atari ST/Mac 68k code extracted from its container, cartridge ROM dumps, embedded firmware) cannot reach REA's Ghidra workflow at all, although Ghidra ships a 68000 language.

After this change, format: "raw-binary", architecture: "m68k", entry_point: "0x…" maps the complete file at the entry and every existing Ghidra operation works on it. Admission requires a 2-byte aligned entry and a mapping inside 0x0..0xffffffff; both are rejected before provider startup.

Change and scope

  • BINARY_ARCHITECTURES admits m68k. 6502 and m68k now share one RAW_ONLY_ARCHITECTURES owner, so detected targets, Evidence subjects, analysis snapshots, and lifecycle output contracts exclude both from header-based formats instead of repeating a 6502 literal.
  • GhidraRawBinaryLanguage maps m68k to 68000:BE:32:default with the default compiler specification. Raw-load verification and the load-image limitation text state that m68k is the one big-endian family.
  • scripts/verify-real-ghidra-m68k.mjs + npm run verify:ghidra:m68k: a real Ghidra lane with a source-owned, annotated 22-byte fixture (no assembler dependency).
  • Docs: docs/raw-binary.md (language table, alignment and address-width rules, a Motorola 68000 section), docs/testing.md, and the authored skill reference.

Not included: separate 68020/68030/CPU32/ColdFire languages, Amiga hunk / Atari ST / other container parsing, reset-vector discovery, the MC68000 24-bit address mask, and Windows Ghidra P0 admission (Windows P0 still admits raw 6502 only).

Contract and boundary impact

  • Semantic owner and earliest changed stage: domain raw-binary admission (src/domain/rawBinary.ts, binaryTargetTypes.ts), then the Ghidra language selection.
  • Contribution scope fit and linked maintainer decision: extension of the existing static, read-only raw-binary lane; no new authority. Separate decision: not applicable.
  • CLI and MCP/tool-catalog contract: the architecture enum of the raw-binary inputs gains m68k; output schemas that already excluded 6502 from non-raw subjects now exclude m68k too. No operation is added or removed.
  • Provider, bridge, target-format, or platform compatibility: Ghidra only (Hopper does not claim raw-binary targets). No bridge script change. Windows Ghidra P0 admission is unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: Evidence keeps architecture: "m68k", the selected language, entry, file digest, and the verified source mapping. Header-format subjects reject m68k like 6502.
  • Target selection, authority, upstream behavior, and cleanup details: caller-selected file; Ghidra analyzes a private snapshot; the source bytes are never written; existing runtime cleanup is reused.
  • Generated metadata, package, or installation impact: none beyond the regenerated tool catalog; no dependency, installer, or native ABI change.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process. (Only the existing owned Ghidra/JVM analysis process; the target is never executed.)
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated: scripts/verify-real-ghidra-m68k.mjs (real Ghidra lane), src/domain/rawBinary.test.ts (alignment, 32-bit span), src/ghidra/GhidraRawBinaryLoadImage.test.ts (big-endian load-image verification), src/ghidra/GhidraProvider.behavior.test.ts, src/domain/analysisSnapshot.test.ts, src/domain/evidence.test.ts, src/contracts/toolOutputReferences.test.ts, tests/boundary/cli/rawBinaryCli.test.ts (unaligned entry rejected before provider startup), tests/boundary/mcp/rawBinaryMcp.test.ts.
  • Base reproduction or other evidence: source-derived. On the base revision BINARY_ARCHITECTURES (src/domain/rawBinary.ts) does not contain m68k, so architecture: "m68k" fails input validation before any provider starts. The new tests were not executed against the base revision.
  • User-visible CLI/MCP output: see the real run below.
  • Remaining proof gaps: real Linux Ghidra was not run; Windows P0 does not admit m68k by design.

Real run (npm run verify:ghidra:m68k, Ghidra 12.1.4, JDK 21, macOS arm64), final line abridged to the result fields:

{
 "ok": true,
 "mocked": false,
 "lane": "ghidra-m68k",
 "host": "darwin",
 "provider": {
  "id": "ghidra",
  "name": "Ghidra",
  "version": "12.1.4"
 },
 "target": {
  "sha256": "7679358409efee4c5c2465f9a47158c4d49e0415ba12b3170898440db7ea0bc9",
  "bytes": 22,
  "architecture": "m68k",
  "entry": "0x1000"
 },
 "cli_mcp_load_image_parity": true,
 "instructions_and_call_reference": true,
 "decompilation": true,
 "file_offset_decompilation_cli_mcp": true,
 "misaligned_entry_preserves_session": true,
 "invalid_mapping_preserves_session": true,
 "source_unchanged": true,
 "runtime_cleanup": "complete"
}

The fixture is 22 self-authored bytes (moveq #7,d0; bsr.b add_three; cmpi.w #10,d0; bne.b mismatch; moveq #42,d0; rts; mismatch: moveq #0,d0; rts; add_three: addq.l #3,d0; rts at 0x1000). Observed Ghidra output through REA:

0x1002  bsr.b 0x00001012   flow: call -> 0x1012 (UNCONDITIONAL_CALL)

undefined4 entry(void) {
  short sVar1;
  sVar1 = FUN_00001012();
  if (sVar1 == 10) { return 0x2a; }
  return 0;
}

int FUN_00001012(void) {
  int in_D0;
  return in_D0 + 3;
}

Rejections keep the active session (same run_id and entry): raw-binary m68k entry_point 0x1001 must be 2-byte aligned, and raw-binary mapping from 0xfffffff0 must fit in the m68k address width.

Outside the verifier, this branch was also used on a real 68000 program of about 1,400 functions after flattening its container to a raw image. That program is not included.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run verify:ghidra:m68k (Ghidra 12.1.4, JDK 21, macOS arm64) — ok: true, runtime_cleanup: complete
  • npm run check (typecheck, lint:built, format:check, knip, metadata:check, architecture guards) — passed, 6/6 tasks
  • npm run docs:check — passed
  • npm run verify:test-discovery — Discovered all 1321 test files exactly once.
  • npm run test:focused -- the 17 test files that mention 6502, m68k, or raw binaries — 279 passed, 8 skipped (Windows-only skipIf(platform !== "win32") and a root-uid guard)
  • Node 26.9.0 (within engines); not run: Linux real Ghidra, Windows, check:pr

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Existing architectures and their profiles are unchanged.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on macOS (arm64), JDK 21. Linux and Windows real runs not performed.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new process, network, or mutation path; the fixture is self-authored bytes in the verifier source.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4018 **Author:** [@icedac](https://github.com/icedac) **Created:** 10/15/2026 **Status:** ✅ Merged **Merged:** 10/16/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `feat/ghidra-raw-m68k` --- ### 📝 Commits (2) - [`bf68478`](https://github.com/morluto/rea/commit/bf68478099d778bceafdbd8ca592c048fb8e6deb) feat(ghidra): support raw big-endian m68k analysis - [`8807530`](https://github.com/morluto/rea/commit/8807530264b4e56084eb4cc0cf8a1a96570ca9fe) test(ghidra): verify raw m68k analysis with real Ghidra ### 📊 Changes **23 files changed** (+742 additions, -187 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` (+8 -6) 📝 `docs/raw-binary.md` (+45 -13) 📝 `docs/testing.md` (+10 -0) 📝 `package.json` (+1 -0) ➕ `scripts/verify-real-ghidra-m68k.mjs` (+296 -0) 📝 `src/contracts/toolOutputReferences.test.ts` (+11 -4) 📝 `src/contracts/toolOutputSchemaGroups.ts` (+8 -2) 📝 `src/domain/analysisSnapshot.test.ts` (+67 -58) 📝 `src/domain/analysisSnapshot.ts` (+7 -2) 📝 `src/domain/binaryTarget.ts` (+2 -1) 📝 `src/domain/binaryTargetTypes.ts` (+12 -1) 📝 `src/domain/evidence.test.ts` (+33 -27) 📝 `src/domain/evidence.ts` (+5 -1) 📝 `src/domain/rawBinary.test.ts` (+54 -0) 📝 `src/domain/rawBinary.ts` (+13 -3) 📝 `src/ghidra/GhidraLoadImageValues.ts` (+2 -2) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+31 -0) 📝 `src/ghidra/GhidraProvider.ts` (+2 -1) 📝 `src/ghidra/GhidraProviderClient.ts` (+1 -1) 📝 `src/ghidra/GhidraRawBinaryLanguage.ts` (+13 -4) _...and 3 more files_ </details> ### 📄 Description ## Summary Support explicitly selected headerless big-endian Motorola 68000 images through REA's existing local Ghidra raw-binary lane. This follows the shape of #2079 (raw NMOS 6502): the caller supplies `architecture: "m68k"` and an entry address, and Ghidra imports the file at that address with `68000:BE:32:default`. No issue is linked. The change extends the existing static, read-only raw-binary admission (#1868, #2079) with one more CPU family. It adds no provider, target execution, injection, mutation, or protection recovery, so a separate maintainer scope decision does not apply under CONTRIBUTING's fail-closed rule. I am happy to open an issue first if you prefer to track it there. ## Problem and expected behavior Before this change, `open_binary` and the CLI reject `architecture: "m68k"`, so headerless 68000-family code (Amiga/Atari ST/Mac 68k code extracted from its container, cartridge ROM dumps, embedded firmware) cannot reach REA's Ghidra workflow at all, although Ghidra ships a 68000 language. After this change, `format: "raw-binary", architecture: "m68k", entry_point: "0x…"` maps the complete file at the entry and every existing Ghidra operation works on it. Admission requires a 2-byte aligned entry and a mapping inside `0x0..0xffffffff`; both are rejected before provider startup. ## Change and scope - `BINARY_ARCHITECTURES` admits `m68k`. `6502` and `m68k` now share one `RAW_ONLY_ARCHITECTURES` owner, so detected targets, Evidence subjects, analysis snapshots, and lifecycle output contracts exclude both from header-based formats instead of repeating a `6502` literal. - `GhidraRawBinaryLanguage` maps `m68k` to `68000:BE:32:default` with the `default` compiler specification. Raw-load verification and the load-image limitation text state that m68k is the one big-endian family. - `scripts/verify-real-ghidra-m68k.mjs` + `npm run verify:ghidra:m68k`: a real Ghidra lane with a source-owned, annotated 22-byte fixture (no assembler dependency). - Docs: `docs/raw-binary.md` (language table, alignment and address-width rules, a Motorola 68000 section), `docs/testing.md`, and the authored skill reference. Not included: separate 68020/68030/CPU32/ColdFire languages, Amiga hunk / Atari ST / other container parsing, reset-vector discovery, the MC68000 24-bit address mask, and Windows Ghidra P0 admission (Windows P0 still admits raw 6502 only). ## Contract and boundary impact - Semantic owner and earliest changed stage: domain raw-binary admission (`src/domain/rawBinary.ts`, `binaryTargetTypes.ts`), then the Ghidra language selection. - Contribution scope fit and linked maintainer decision: extension of the existing static, read-only raw-binary lane; no new authority. Separate decision: not applicable. - CLI and MCP/tool-catalog contract: the `architecture` enum of the raw-binary inputs gains `m68k`; output schemas that already excluded `6502` from non-raw subjects now exclude `m68k` too. No operation is added or removed. - Provider, bridge, target-format, or platform compatibility: Ghidra only (Hopper does not claim raw-binary targets). No bridge script change. Windows Ghidra P0 admission is unchanged. - Evidence, artifact, provenance, or reconstruction contract: Evidence keeps `architecture: "m68k"`, the selected language, entry, file digest, and the verified source mapping. Header-format subjects reject `m68k` like `6502`. - Target selection, authority, upstream behavior, and cleanup details: caller-selected file; Ghidra analyzes a private snapshot; the source bytes are never written; existing runtime cleanup is reused. - Generated metadata, package, or installation impact: none beyond the regenerated tool catalog; no dependency, installer, or native ABI change. Expected effects: - [x] Read existing files or captured data. - [x] Launch or attach to a process. (Only the existing owned Ghidra/JVM analysis process; the target is never executed.) - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: `scripts/verify-real-ghidra-m68k.mjs` (real Ghidra lane), `src/domain/rawBinary.test.ts` (alignment, 32-bit span), `src/ghidra/GhidraRawBinaryLoadImage.test.ts` (big-endian load-image verification), `src/ghidra/GhidraProvider.behavior.test.ts`, `src/domain/analysisSnapshot.test.ts`, `src/domain/evidence.test.ts`, `src/contracts/toolOutputReferences.test.ts`, `tests/boundary/cli/rawBinaryCli.test.ts` (unaligned entry rejected before provider startup), `tests/boundary/mcp/rawBinaryMcp.test.ts`. - Base reproduction or other evidence: source-derived. On the base revision `BINARY_ARCHITECTURES` (`src/domain/rawBinary.ts`) does not contain `m68k`, so `architecture: "m68k"` fails input validation before any provider starts. The new tests were not executed against the base revision. - User-visible CLI/MCP output: see the real run below. - Remaining proof gaps: real Linux Ghidra was not run; Windows P0 does not admit m68k by design. Real run (`npm run verify:ghidra:m68k`, Ghidra 12.1.4, JDK 21, macOS arm64), final line abridged to the result fields: ```json { "ok": true, "mocked": false, "lane": "ghidra-m68k", "host": "darwin", "provider": { "id": "ghidra", "name": "Ghidra", "version": "12.1.4" }, "target": { "sha256": "7679358409efee4c5c2465f9a47158c4d49e0415ba12b3170898440db7ea0bc9", "bytes": 22, "architecture": "m68k", "entry": "0x1000" }, "cli_mcp_load_image_parity": true, "instructions_and_call_reference": true, "decompilation": true, "file_offset_decompilation_cli_mcp": true, "misaligned_entry_preserves_session": true, "invalid_mapping_preserves_session": true, "source_unchanged": true, "runtime_cleanup": "complete" } ``` The fixture is 22 self-authored bytes (`moveq #7,d0; bsr.b add_three; cmpi.w #10,d0; bne.b mismatch; moveq #42,d0; rts; mismatch: moveq #0,d0; rts; add_three: addq.l #3,d0; rts` at `0x1000`). Observed Ghidra output through REA: ``` 0x1002 bsr.b 0x00001012 flow: call -> 0x1012 (UNCONDITIONAL_CALL) undefined4 entry(void) { short sVar1; sVar1 = FUN_00001012(); if (sVar1 == 10) { return 0x2a; } return 0; } int FUN_00001012(void) { int in_D0; return in_D0 + 3; } ``` Rejections keep the active session (same `run_id` and entry): `raw-binary m68k entry_point 0x1001 must be 2-byte aligned`, and `raw-binary mapping from 0xfffffff0 must fit in the m68k address width`. Outside the verifier, this branch was also used on a real 68000 program of about 1,400 functions after flattening its container to a raw image. That program is not included. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run verify:ghidra:m68k` (Ghidra 12.1.4, JDK 21, macOS arm64) — `ok: true`, `runtime_cleanup: complete` - `npm run check` (typecheck, lint:built, format:check, knip, metadata:check, architecture guards) — passed, 6/6 tasks - `npm run docs:check` — passed - `npm run verify:test-discovery` — `Discovered all 1321 test files exactly once.` - `npm run test:focused --` the 17 test files that mention `6502`, `m68k`, or raw binaries — 279 passed, 8 skipped (Windows-only `skipIf(platform !== "win32")` and a root-uid guard) - Node 26.9.0 (within `engines`); not run: Linux real Ghidra, Windows, `check:pr` ## Compatibility, safety, and release - Breaking changes or migration steps: none. Existing architectures and their profiles are unchanged. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on macOS (arm64), JDK 21. Linux and Windows real runs not performed. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new process, network, or mutation path; the fixture is self-authored bytes in the verifier source. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:11:01 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5991
No description provided.