[PR #1747] [MERGED] feat(ghidra): admit 32-bit big-endian PowerPC ELF #4792

Closed
opened 2026-10-31 00:55:07 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1747
Author: @arielfikru
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: feat/ghidra-powerpc


📝 Commits (6)

  • 80f93ce feat(ghidra): admit 32-bit big-endian PowerPC ELF
  • 311961a fix(ghidra): reconcile PowerPC with shared ELF profile ownership
  • c631c9e style(ghidra): format shared PowerPC profile integration
  • 43557da fix(ghidra): preserve caller-selected PowerPC languages
  • 8783377 chore: sync captured review base for PR 1747
  • ad135cb Merge branch 'main' into feat/ghidra-powerpc

📊 Changes

22 files changed (+1258 additions, -4 deletions)

View changed files

📝 docs/.vitepress/config.ts (+1 -0)
➕ docs/ghidra-ppc.md (+90 -0)
📝 package.json (+1 -0)
➕ scripts/verify-real-ghidra-ppc.mjs (+282 -0)
➕ src/domain/binaryTarget.powerpc.test.ts (+58 -0)
📝 src/domain/binaryTarget.ts (+21 -1)
📝 src/domain/binaryTargetTypes.ts (+14 -0)
📝 src/ghidra/GhidraAnalysisProfile.ts (+9 -1)
➕ src/ghidra/GhidraPowerPcProfile.test.ts (+166 -0)
➕ src/ghidra/GhidraPowerPcProfile.ts (+67 -0)
📝 src/ghidra/GhidraProvider.ts (+1 -0)
📝 src/ghidra/GhidraPspProfile.ts (+3 -0)
📝 src/hopper/HopperAnalysisProfile.ts (+1 -0)
📝 src/hopper/HopperProvider.ts (+5 -1)
📝 src/ida/IdaProvider.ts (+5 -1)
➕ tests/composition/analysis-sessions/powerpc-identity.test.ts (+93 -0)
➕ tests/conformance/c/ppc.c (+14 -0)
➕ tests/conformance/ghidra/ppc-checks.d.mts (+29 -0)
➕ tests/conformance/ghidra/ppc-checks.mjs (+132 -0)
➕ tests/conformance/ghidra/ppc-checks.test.ts (+187 -0)

...and 2 more files

📄 Description

Summary

Milestone 1 of #1741: 32-bit big-endian PowerPC ELF executables can reach REA's normal Ghidra workflow. Before this change EM_PPC was "unsupported ELF architecture", so no PowerPC target could be opened.

Opened as a draft while #1741 waits for maintainer input on direction; happy to reshape it.

Problem and expected behavior

A legally obtained PowerPC ELF (embedded/firmware code, or reconstruction work on PowerPC programs) should give functions, instructions, pseudocode, references and Evidence through the existing tools, with the same admission discipline as the MIPS lane.

One PowerPC-specific trap shapes the design: for a plain EM_PPC header with e_flags 0, Ghidra 12.1.4's ELF loader picks PowerPC:BE:32:e500:default. e500 is the Book-E/SPE variant without the classic FPU, so it misreads 750-family code, and nothing in the header distinguishes the two. Observed headless import of the fixture:

INFO  Using Loader: Executable and Linking Format (ELF) (ProgramLoader)
INFO  Using Language/Compiler: PowerPC:BE:32:e500:default (ProgramLoader)

Change and scope

  • Domain: powerpc joins BINARY_ARCHITECTURES. EM_PPC (20) resolves to an ELF target that carries PowerPcElfMetadata (class, byte order, e_type, raw e_flags), mirroring how MIPS keeps its header facts. EM_PPC64 (21) is not mapped to this family. ElfHeaderArchitecture = "mips" | "powerpc" replaces the Exclude<…, "mips"> spellings.
  • Ghidra admission (src/ghidra/GhidraPowerPcProfile.ts): ELF32, big endian, ET_EXEC. ELF64, little endian, relocatable/shared objects and EF_PPC_EMB are refused with a specific reason. Other e_flags bits only declare relocation conventions.
  • Bound language: the profile commits language_id: "PowerPC:BE:32:default", compiler_spec_id: "default", language_selection: "admitted-lane-v1", plus powerpc_elf header facts and powerpc_support_lane: "elf32-be-exec-generic-v1". The client reads that back (ghidraProfileLaneLanguage) and uses the existing launcher/handshake path: -processor … -cspec … on import and expectedLanguageId checked against the bridge's reported language. A caller's REA_GHIDRA_LANGUAGE_ID still wins (configured-v1, admitted against the installed catalog as before), e.g. PowerPC:BE:32:e500 for real e500 code.
  • Other adapters: Hopper and IDA refuse PowerPC explicitly; Windows Ghidra P0 is unchanged.

Not included: GameCube/Wii DOL, Gekko/Broadway paired singles, PPC64, little-endian PowerPC, REL/RSO, VLE. Unlike the MIPS lane there is no carved-section case, because PowerPC admission reads no section-borne ABI record.

Contract and boundary impact

  • Semantic owner and earliest changed stage: ELF header parsing in src/domain/binaryTarget.ts; lane admission in src/ghidra/GhidraPowerPcProfile.ts.
  • CLI and MCP/tool-catalog contract: no new tools or options. The architecture enum in target/Evidence/snapshot schemas gains "powerpc".
  • Provider, bridge, target-format, or platform compatibility: Ghidra (Linux/macOS) gains the lane; Hopper, IDA and Windows Ghidra P0 refuse PowerPC. No bridge changes.
  • Evidence, artifact, provenance, or reconstruction contract: Evidence and snapshots preserve powerpc; profile-bound snapshots are invalidated when header facts change.
  • Process execution, authorization, cleanup, or containment impact: none. The fixture is never executed.
  • Generated metadata, package, or installation impact: none committed; docs:check passes.

Evidence and regression coverage

  • Tests added or updated:
    • src/domain/binaryTarget.powerpc.test.ts: header facts for both byte orders, ELF64 flag offset, EM_PPC64 not recognized.
    • src/ghidra/GhidraPowerPcProfile.test.ts: bound language and admitted-lane-v1, configured language replaces it (real temporary catalog), header flags change the profile digest, refusals (ELF64, little endian, ET_REL, ET_DYN, EF_PPC_EMB), Windows P0, Hopper/IDA refusal.
    • tests/composition/analysis-sessions/powerpc-identity.test.ts: identity through Evidence, snapshot and open_binary schema.
    • tests/conformance/ghidra/ppc-checks.test.ts: GNU readelf parser against real 2.47 output; rejects wrong class/order/machine/type/flags, ambiguous fields, an e500 loaded language, and wrong probe bytes/immediate/destination.
  • Base reproduction or other evidence: on the base revision the fixture fails with unsupported ELF architecture; a direct analyzeHeadless -import picks e500 (log above).
  • User-visible CLI/MCP output: open_binary on the fixture returns architecture: "powerpc"; inspect_native_load_image reports language_id: "PowerPC:BE:32:default".
  • Remaining proof gaps: Linux host not run; floating-point and paired-single semantics not exercised; only Clang/LLD-produced ELF tested.

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

Host: macOS 26 arm64, Node 24.18.0, npm 11.16.0, Ghidra 12.1.4, OpenJDK 21, Homebrew clang/LLD 23.1.3, GNU readelf 2.47.

  • npm run check:fast: passed
  • npm run verify:test-discovery: "Discovered all 817 test files exactly once."
  • npm run test:changed: 379 files, 4296 passed, 7 skipped
  • npm run docs:check: passed
  • GHIDRA_INSTALL_DIR=… REA_PPC_CLANG=… REA_PPC_READELF=… npm run verify:ghidra:ppc: passed (effective_language: "PowerPC:BE:32:default", 3 procedures, process lineage verified)
  • npm run verify:ghidra:mips on this branch: passed for both byte orders (shared ELF parsing and language plumbing unchanged for MIPS)

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Previously rejected targets are now admitted; no existing target changes interpretation.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on macOS arm64 for the PowerPC and MIPS lanes; Linux not run; Hopper/IDA only via refusal unit tests.
  • Package or release metadata impact: new verify:ghidra:ppc script; no package contents change.
  • Security, privacy, process, or containment review: the fixture is source-owned and compiled locally; no third-party or commercial binaries; nothing is executed.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • 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.

Refs #1741


🔄 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/1747 **Author:** [@arielfikru](https://github.com/arielfikru) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `feat/ghidra-powerpc` --- ### 📝 Commits (6) - [`80f93ce`](https://github.com/morluto/rea/commit/80f93ceed8c29cf328d5c31b60d40838a4c2f5db) feat(ghidra): admit 32-bit big-endian PowerPC ELF - [`311961a`](https://github.com/morluto/rea/commit/311961ae6e3ad089cb185d2a8b5047d0ff66decc) fix(ghidra): reconcile PowerPC with shared ELF profile ownership - [`c631c9e`](https://github.com/morluto/rea/commit/c631c9e9dd70d4f4c6a03810a85a191e2bb6721e) style(ghidra): format shared PowerPC profile integration - [`43557da`](https://github.com/morluto/rea/commit/43557dac777f5c54fcf2844b8d65fdd879d5c117) fix(ghidra): preserve caller-selected PowerPC languages - [`8783377`](https://github.com/morluto/rea/commit/878337765766ad8c3962765dd252fe084dadc17a) chore: sync captured review base for PR 1747 - [`ad135cb`](https://github.com/morluto/rea/commit/ad135cba77aa3714c89576d81f1d790e20cb4ccd) Merge branch 'main' into feat/ghidra-powerpc ### 📊 Changes **22 files changed** (+1258 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `docs/.vitepress/config.ts` (+1 -0) ➕ `docs/ghidra-ppc.md` (+90 -0) 📝 `package.json` (+1 -0) ➕ `scripts/verify-real-ghidra-ppc.mjs` (+282 -0) ➕ `src/domain/binaryTarget.powerpc.test.ts` (+58 -0) 📝 `src/domain/binaryTarget.ts` (+21 -1) 📝 `src/domain/binaryTargetTypes.ts` (+14 -0) 📝 `src/ghidra/GhidraAnalysisProfile.ts` (+9 -1) ➕ `src/ghidra/GhidraPowerPcProfile.test.ts` (+166 -0) ➕ `src/ghidra/GhidraPowerPcProfile.ts` (+67 -0) 📝 `src/ghidra/GhidraProvider.ts` (+1 -0) 📝 `src/ghidra/GhidraPspProfile.ts` (+3 -0) 📝 `src/hopper/HopperAnalysisProfile.ts` (+1 -0) 📝 `src/hopper/HopperProvider.ts` (+5 -1) 📝 `src/ida/IdaProvider.ts` (+5 -1) ➕ `tests/composition/analysis-sessions/powerpc-identity.test.ts` (+93 -0) ➕ `tests/conformance/c/ppc.c` (+14 -0) ➕ `tests/conformance/ghidra/ppc-checks.d.mts` (+29 -0) ➕ `tests/conformance/ghidra/ppc-checks.mjs` (+132 -0) ➕ `tests/conformance/ghidra/ppc-checks.test.ts` (+187 -0) _...and 2 more files_ </details> ### 📄 Description ## Summary Milestone 1 of #1741: 32-bit big-endian PowerPC ELF executables can reach REA's normal Ghidra workflow. Before this change `EM_PPC` was "unsupported ELF architecture", so no PowerPC target could be opened. Opened as a draft while #1741 waits for maintainer input on direction; happy to reshape it. ## Problem and expected behavior A legally obtained PowerPC ELF (embedded/firmware code, or reconstruction work on PowerPC programs) should give functions, instructions, pseudocode, references and Evidence through the existing tools, with the same admission discipline as the MIPS lane. One PowerPC-specific trap shapes the design: for a plain `EM_PPC` header with `e_flags` 0, Ghidra 12.1.4's ELF loader picks `PowerPC:BE:32:e500:default`. e500 is the Book-E/SPE variant without the classic FPU, so it misreads 750-family code, and nothing in the header distinguishes the two. Observed headless import of the fixture: ``` INFO Using Loader: Executable and Linking Format (ELF) (ProgramLoader) INFO Using Language/Compiler: PowerPC:BE:32:e500:default (ProgramLoader) ``` ## Change and scope - **Domain**: `powerpc` joins `BINARY_ARCHITECTURES`. `EM_PPC` (20) resolves to an ELF target that carries `PowerPcElfMetadata` (class, byte order, `e_type`, raw `e_flags`), mirroring how MIPS keeps its header facts. `EM_PPC64` (21) is not mapped to this family. `ElfHeaderArchitecture = "mips" | "powerpc"` replaces the `Exclude<…, "mips">` spellings. - **Ghidra admission** (`src/ghidra/GhidraPowerPcProfile.ts`): ELF32, big endian, `ET_EXEC`. ELF64, little endian, relocatable/shared objects and `EF_PPC_EMB` are refused with a specific reason. Other `e_flags` bits only declare relocation conventions. - **Bound language**: the profile commits `language_id: "PowerPC:BE:32:default"`, `compiler_spec_id: "default"`, `language_selection: "admitted-lane-v1"`, plus `powerpc_elf` header facts and `powerpc_support_lane: "elf32-be-exec-generic-v1"`. The client reads that back (`ghidraProfileLaneLanguage`) and uses the existing launcher/handshake path: `-processor … -cspec …` on import and `expectedLanguageId` checked against the bridge's reported language. A caller's `REA_GHIDRA_LANGUAGE_ID` still wins (`configured-v1`, admitted against the installed catalog as before), e.g. `PowerPC:BE:32:e500` for real e500 code. - **Other adapters**: Hopper and IDA refuse PowerPC explicitly; Windows Ghidra P0 is unchanged. Not included: GameCube/Wii DOL, Gekko/Broadway paired singles, PPC64, little-endian PowerPC, REL/RSO, VLE. Unlike the MIPS lane there is no carved-section case, because PowerPC admission reads no section-borne ABI record. ## Contract and boundary impact - Semantic owner and earliest changed stage: ELF header parsing in `src/domain/binaryTarget.ts`; lane admission in `src/ghidra/GhidraPowerPcProfile.ts`. - CLI and MCP/tool-catalog contract: no new tools or options. The `architecture` enum in target/Evidence/snapshot schemas gains `"powerpc"`. - Provider, bridge, target-format, or platform compatibility: Ghidra (Linux/macOS) gains the lane; Hopper, IDA and Windows Ghidra P0 refuse PowerPC. No bridge changes. - Evidence, artifact, provenance, or reconstruction contract: Evidence and snapshots preserve `powerpc`; profile-bound snapshots are invalidated when header facts change. - Process execution, authorization, cleanup, or containment impact: none. The fixture is never executed. - Generated metadata, package, or installation impact: none committed; `docs:check` passes. ## Evidence and regression coverage - Tests added or updated: - `src/domain/binaryTarget.powerpc.test.ts`: header facts for both byte orders, ELF64 flag offset, `EM_PPC64` not recognized. - `src/ghidra/GhidraPowerPcProfile.test.ts`: bound language and `admitted-lane-v1`, configured language replaces it (real temporary catalog), header flags change the profile digest, refusals (ELF64, little endian, `ET_REL`, `ET_DYN`, `EF_PPC_EMB`), Windows P0, Hopper/IDA refusal. - `tests/composition/analysis-sessions/powerpc-identity.test.ts`: identity through Evidence, snapshot and `open_binary` schema. - `tests/conformance/ghidra/ppc-checks.test.ts`: GNU readelf parser against real 2.47 output; rejects wrong class/order/machine/type/flags, ambiguous fields, an e500 loaded language, and wrong probe bytes/immediate/destination. - Base reproduction or other evidence: on the base revision the fixture fails with `unsupported ELF architecture`; a direct `analyzeHeadless -import` picks e500 (log above). - User-visible CLI/MCP output: `open_binary` on the fixture returns `architecture: "powerpc"`; `inspect_native_load_image` reports `language_id: "PowerPC:BE:32:default"`. - Remaining proof gaps: Linux host not run; floating-point and paired-single semantics not exercised; only Clang/LLD-produced ELF tested. 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 Host: macOS 26 arm64, Node 24.18.0, npm 11.16.0, Ghidra 12.1.4, OpenJDK 21, Homebrew clang/LLD 23.1.3, GNU readelf 2.47. - `npm run check:fast`: passed - `npm run verify:test-discovery`: "Discovered all 817 test files exactly once." - `npm run test:changed`: 379 files, 4296 passed, 7 skipped - `npm run docs:check`: passed - `GHIDRA_INSTALL_DIR=… REA_PPC_CLANG=… REA_PPC_READELF=… npm run verify:ghidra:ppc`: passed (`effective_language: "PowerPC:BE:32:default"`, 3 procedures, process lineage verified) - `npm run verify:ghidra:mips` on this branch: passed for both byte orders (shared ELF parsing and language plumbing unchanged for MIPS) ## Compatibility, safety, and release - Breaking changes or migration steps: none. Previously rejected targets are now admitted; no existing target changes interpretation. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra 12.1.4 on macOS arm64 for the PowerPC and MIPS lanes; Linux not run; Hopper/IDA only via refusal unit tests. - Package or release metadata impact: new `verify:ghidra:ppc` script; no package contents change. - Security, privacy, process, or containment review: the fixture is source-owned and compiled locally; no third-party or commercial binaries; nothing is executed. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [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. Refs #1741 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:55:07 +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#4792
No description provided.