[GH-ISSUE #1741] [Feature] Add PowerPC (32-bit big-endian) native analysis through Ghidra, with a GameCube/Wii DOL follow-up #584

Open
opened 2026-10-15 18:26:24 +00:00 by bsakaguchi · 14 comments
Owner

Originally created by @arielfikru on GitHub (Oct 11, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1741

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Current limitation

BINARY_ARCHITECTURES in src/domain/binaryTargetTypes.ts currently admits x86, x86_64, arm, arm64 and mips. A 32-bit big-endian PowerPC ELF (EM_PPC) therefore cannot reach REA's Ghidra workflow, even though Ghidra ships PowerPC language support. The same applies to Nintendo GameCube/Wii executables (DOL), which are PowerPC 750CL-family (Gekko/Broadway) code with paired-single floating-point instructions.

Use cases

Static investigation of PowerPC code through REA's ordinary native workflow: functions, instructions, pseudocode, references and Evidence. Concrete cases:

  • Embedded/firmware PowerPC ELF (big-endian, 32-bit), which also connects to the firmware roadmap in #718.
  • Game preservation and decompilation projects for GameCube/Wii titles (the decomp community around decomp-toolkit/objdiff), where an agent needs to inspect a function, its callers and its data before writing matching C.

I have been doing this on a Wii title with Ghidra headless scripts outside REA, which is where the notes below come from.

Desired outcome and success criteria

Milestone 1: generic PowerPC ELF, following the shape of the MIPS lane (docs/ghidra-mips.md):

  • Admit ELF32 EM_PPC, big-endian, ET_EXEC, and keep a provider-neutral powerpc family through target classification, analysis profile and Evidence.
  • Refuse what this lane does not cover with a specific unsupported constraint (ELF64/EM_PPC64, little-endian, relocatable/shared objects, VLE) rather than guessing a profile.
  • Select Ghidra's built-in PowerPC:BE:32:default language; record the observed language/compiler spec in Evidence.
  • Verify with a small source-owned freestanding fixture (for example tests/conformance/c/ppc.c plus a probe with fixed addi/bc encodings), cross-compiled for a generic 32-bit big-endian PPC target; no third-party binaries.

Milestone 2: GameCube/Wii DOL (could be a separate issue):

  • Admit a DOL by its fixed 0x100-byte header (7 text + 11 data sections, load addresses, BSS, entry), with the header facts kept in the profile.
  • Select the Gekko/Broadway language so paired-single instructions decode instead of becoming bad instructions.
  • Record the small-data bases (r2/r13, SDA2/SDA) when they can be derived from the entry stub, because without them most global accesses decompile as raw offsets.
  • Verify with a DOL built from the same source-owned fixture; no commercial game fixture is needed.

Authority boundaries and non-goals

No emulation (Dolphin), no disc image handling (ISO/RVZ/WBFS), no REL/RSO relocation in the first milestone, no matching-decompilation or compiler integration, no game-specific logic, no automatic extension installation. Analysis stays read-only and targets stay caller-supplied.

Alternatives considered

  • Ghidra-GameCube-Loader extension (https://github.com/Cuyler36/Ghidra-GameCube-Loader, latest release 1.3.1 for Ghidra 12.1): it supports DOL/REL/apploaders/RAM dumps and provides the PowerPC:BE:32:Gekko_Broadway language. In my use (an earlier release, not yet re-checked with 1.3.1) its loader opened a dialog during headless import and failed with HeadlessException. That matters because REA drives Ghidra headless. The workaround that worked: convert the DOL to an ELF with its section addresses and entry, then import it as ELF with -processor "PowerPC:BE:32:Gekko_Broadway"; the extension is still needed for that language.
  • decomp-toolkit (dtk) already splits DOLs and can emit ELF; it is a good reference for header parsing but is a separate tool with its own workflow.

Possible direction

Milestone 1 mirrors the MIPS lane: header parsing in the ELF resolver, a powerpc family in the domain types, a Ghidra profile entry, a verify:ghidra:ppc real-provider lane and docs/ghidra-ppc.md. Milestone 2 adds a DOL target kind; the open question for maintainers is whether REA should (a) parse the DOL itself and hand Ghidra an equivalent ELF/raw-binary import with explicit addresses and the Gekko/Broadway language, keeping the extension bring-your-own only for the language, or (b) rely on the extension's loader once its headless behaviour is confirmed.

The powerpc family from milestone 1 is meant to be reused by later, separate lanes (for example Wii U RPX, which is still 32-bit PowerPC, or PPC64 targets) without widening this issue's scope.

I'm happy to implement milestone 1 and open a PR if the direction fits. I can run the real-provider lane on macOS with Ghidra 12.1.4 and JDK 17/21.

References

Originally created by @arielfikru on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1741 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Current limitation `BINARY_ARCHITECTURES` in `src/domain/binaryTargetTypes.ts` currently admits `x86`, `x86_64`, `arm`, `arm64` and `mips`. A 32-bit big-endian PowerPC ELF (`EM_PPC`) therefore cannot reach REA's Ghidra workflow, even though Ghidra ships PowerPC language support. The same applies to Nintendo GameCube/Wii executables (DOL), which are PowerPC 750CL-family (Gekko/Broadway) code with paired-single floating-point instructions. ### Use cases Static investigation of PowerPC code through REA's ordinary native workflow: functions, instructions, pseudocode, references and Evidence. Concrete cases: - Embedded/firmware PowerPC ELF (big-endian, 32-bit), which also connects to the firmware roadmap in #718. - Game preservation and decompilation projects for GameCube/Wii titles (the decomp community around decomp-toolkit/objdiff), where an agent needs to inspect a function, its callers and its data before writing matching C. I have been doing this on a Wii title with Ghidra headless scripts outside REA, which is where the notes below come from. ### Desired outcome and success criteria **Milestone 1: generic PowerPC ELF**, following the shape of the MIPS lane (`docs/ghidra-mips.md`): - Admit ELF32 `EM_PPC`, big-endian, `ET_EXEC`, and keep a provider-neutral `powerpc` family through target classification, analysis profile and Evidence. - Refuse what this lane does not cover with a specific unsupported constraint (ELF64/`EM_PPC64`, little-endian, relocatable/shared objects, VLE) rather than guessing a profile. - Select Ghidra's built-in `PowerPC:BE:32:default` language; record the observed language/compiler spec in Evidence. - Verify with a small source-owned freestanding fixture (for example `tests/conformance/c/ppc.c` plus a probe with fixed `addi`/`bc` encodings), cross-compiled for a generic 32-bit big-endian PPC target; no third-party binaries. **Milestone 2: GameCube/Wii DOL** (could be a separate issue): - Admit a DOL by its fixed 0x100-byte header (7 text + 11 data sections, load addresses, BSS, entry), with the header facts kept in the profile. - Select the Gekko/Broadway language so paired-single instructions decode instead of becoming bad instructions. - Record the small-data bases (`r2`/`r13`, SDA2/SDA) when they can be derived from the entry stub, because without them most global accesses decompile as raw offsets. - Verify with a DOL built from the same source-owned fixture; no commercial game fixture is needed. ### Authority boundaries and non-goals No emulation (Dolphin), no disc image handling (ISO/RVZ/WBFS), no REL/RSO relocation in the first milestone, no matching-decompilation or compiler integration, no game-specific logic, no automatic extension installation. Analysis stays read-only and targets stay caller-supplied. ### Alternatives considered - **Ghidra-GameCube-Loader extension** (https://github.com/Cuyler36/Ghidra-GameCube-Loader, latest release 1.3.1 for Ghidra 12.1): it supports DOL/REL/apploaders/RAM dumps and provides the `PowerPC:BE:32:Gekko_Broadway` language. In my use (an earlier release, not yet re-checked with 1.3.1) its loader opened a dialog during headless import and failed with `HeadlessException`. That matters because REA drives Ghidra headless. The workaround that worked: convert the DOL to an ELF with its section addresses and entry, then import it as ELF with `-processor "PowerPC:BE:32:Gekko_Broadway"`; the extension is still needed for that language. - **decomp-toolkit (`dtk`)** already splits DOLs and can emit ELF; it is a good reference for header parsing but is a separate tool with its own workflow. ### Possible direction Milestone 1 mirrors the MIPS lane: header parsing in the ELF resolver, a `powerpc` family in the domain types, a Ghidra profile entry, a `verify:ghidra:ppc` real-provider lane and `docs/ghidra-ppc.md`. Milestone 2 adds a DOL target kind; the open question for maintainers is whether REA should (a) parse the DOL itself and hand Ghidra an equivalent ELF/raw-binary import with explicit addresses and the Gekko/Broadway language, keeping the extension bring-your-own only for the language, or (b) rely on the extension's loader once its headless behaviour is confirmed. The `powerpc` family from milestone 1 is meant to be reused by later, separate lanes (for example Wii U RPX, which is still 32-bit PowerPC, or PPC64 targets) without widening this issue's scope. I'm happy to implement milestone 1 and open a PR if the direction fits. I can run the real-provider lane on macOS with Ghidra 12.1.4 and JDK 17/21. ### References - REA MIPS lane: `docs/ghidra-mips.md`, #718 - REA PSP/Allegrex request (similar extension-backed shape): #1330 - REA architecture type: `src/domain/binaryTargetTypes.ts` - Ghidra-GameCube-Loader: https://github.com/Cuyler36/Ghidra-GameCube-Loader - decomp-toolkit: https://github.com/encounter/decomp-toolkit - DOL format: https://wiibrew.org/wiki/DOL
Author
Owner

@arielfikru commented on GitHub (Oct 11, 2026):

One data point that supports pinning the language in milestone 1, plus a small correction.

Ghidra's automatic choice for a plain PowerPC ELF is e500, not the generic 32-bit language. I built a freestanding fixture (the MIPS fixture with the names changed) and imported it headless:

clang --target=powerpc-unknown-linux-gnu -mcpu=750 -fno-pic -fuse-ld=lld \
  -nostdlib -static -O0 -g -Wl,-e,rea_ppc_entry -Wl,--build-id=none ppc.c -o ppc.elf
# readelf -h: ELF32, big endian, EXEC, Machine: PowerPC, Flags: 0x0
analyzeHeadless <project> t -import ppc.elf -deleteProject

Ghidra 12.1.4 log:

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

e500 is the embedded Book-E/SPE variant without the classic FPU, so for 750-family code (which uses ordinary FP instructions) it is the wrong interpretation. With e_flags 0 there is nothing in the header that distinguishes the two, so the lane should select PowerPC:BE:32:default explicitly and record it in the profile, the same way the MIPS lane binds its declared interpretation, rather than accept the loader's pick.

Correction: Ghidra 12.1.x requires JDK 21 (application.java.min=21), not 17. As a baseline, the existing npm run verify:ghidra:mips lane passes on this macOS (arm64) host with Ghidra 12.1.4 + JDK 21, Homebrew clang 23 + lld and GNU readelf 2.47.

<!-- gh-comment-id:6106511260 --> @arielfikru commented on GitHub (Oct 11, 2026): One data point that supports pinning the language in milestone 1, plus a small correction. **Ghidra's automatic choice for a plain PowerPC ELF is e500, not the generic 32-bit language.** I built a freestanding fixture (the MIPS fixture with the names changed) and imported it headless: ```sh clang --target=powerpc-unknown-linux-gnu -mcpu=750 -fno-pic -fuse-ld=lld \ -nostdlib -static -O0 -g -Wl,-e,rea_ppc_entry -Wl,--build-id=none ppc.c -o ppc.elf # readelf -h: ELF32, big endian, EXEC, Machine: PowerPC, Flags: 0x0 analyzeHeadless <project> t -import ppc.elf -deleteProject ``` Ghidra 12.1.4 log: ``` INFO Using Loader: Executable and Linking Format (ELF) (ProgramLoader) INFO Using Language/Compiler: PowerPC:BE:32:e500:default (ProgramLoader) ``` e500 is the embedded Book-E/SPE variant without the classic FPU, so for 750-family code (which uses ordinary FP instructions) it is the wrong interpretation. With `e_flags` 0 there is nothing in the header that distinguishes the two, so the lane should select `PowerPC:BE:32:default` explicitly and record it in the profile, the same way the MIPS lane binds its declared interpretation, rather than accept the loader's pick. **Correction:** Ghidra 12.1.x requires JDK 21 (`application.java.min=21`), not 17. As a baseline, the existing `npm run verify:ghidra:mips` lane passes on this macOS (arm64) host with Ghidra 12.1.4 + JDK 21, Homebrew clang 23 + lld and GNU readelf 2.47.
Author
Owner

@casesolved-co-uk commented on GitHub (Oct 12, 2026):

They just implemented the ability to supply a user defined architecture when using ghidra via #1868 which ties in with this feature.

Unfortunately they only added x86 and arm architectures.

I would also like to see PowerPC architectures added, but also all architectures supported by ghidra.

My own use case is 32bit PowerPC BE with VLE for the MPC5565 (Marelli 8F3 Fiat engine ECU)

@morluto @mvanhorn please?

<!-- gh-comment-id:6121018443 --> @casesolved-co-uk commented on GitHub (Oct 12, 2026): They just implemented the ability to supply a user defined architecture when using ghidra via #1868 which ties in with this feature. Unfortunately they only added x86 and arm architectures. I would also like to see PowerPC architectures added, but also all architectures supported by ghidra. My own use case is 32bit PowerPC BE with VLE for the MPC5565 (Marelli 8F3 Fiat engine ECU) @morluto @mvanhorn please?
Author
Owner

@mvanhorn commented on GitHub (Oct 15, 2026):

Agreed on both points. The PowerPC lane should explicitly select PowerPC:BE:32:default for this profile instead of relying on Ghidra's e500 default, and the prerequisite should be JDK 21 for Ghidra 12.1.x. Broader architecture coverage, including VLE and the other Ghidra-supported languages, is a worthwhile follow-up beyond the initial x86/ARM support.

<!-- gh-comment-id:6203943905 --> @mvanhorn commented on GitHub (Oct 15, 2026): Agreed on both points. The PowerPC lane should explicitly select `PowerPC:BE:32:default` for this profile instead of relying on Ghidra's e500 default, and the prerequisite should be JDK 21 for Ghidra 12.1.x. Broader architecture coverage, including VLE and the other Ghidra-supported languages, is a worthwhile follow-up beyond the initial x86/ARM support.
Author
Owner

@CassieFox23 commented on GitHub (Oct 18, 2026):

TL;DR: Part one of this request works on main now. I tested it on Linux. Part two, the GameCube/Wii DOL format, is not done yet.

Milestone 1, 32-bit big-endian PowerPC ELF, is fixed on main by c86b09a10 (#1747).

  • I took the PowerPC ELF handling back out of main 32f0e5120 by hand. Then 10 of 27 tests fail on Node 22.23.1, 24.18.0 and 26.11.1, with unsupported ELF architecture among them.
  • On main all 27 pass.

Not run: the real Ghidra lane (verify:ghidra:ppc) and a real PowerPC binary.

What was taken out, and the tests

Taken out: the EM_PPC case in elfFixedHeader.ts, the powerpc entry and dispatch in the Ghidra provider, and the PowerPC profile links.

Tests: src/domain/binaryTarget.powerpc.test.ts, src/ghidra/GhidraPowerPcProfile.test.ts, tests/composition/analysis-sessions/powerpc-identity.test.ts, tests/conformance/ghidra/ppc-checks.test.ts.

Thanks, arielfikru, for the clear report and the patch.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6255404438 --> @CassieFox23 commented on GitHub (Oct 18, 2026): **TL;DR:** Part one of this request works on main now. I tested it on Linux. Part two, the GameCube/Wii DOL format, is not done yet. Milestone 1, 32-bit big-endian PowerPC ELF, is fixed on main by c86b09a10 (#1747). - I took the PowerPC ELF handling back out of main 32f0e5120 by hand. Then 10 of 27 tests fail on Node 22.23.1, 24.18.0 and 26.11.1, with `unsupported ELF architecture` among them. - On main all 27 pass. Not run: the real Ghidra lane (`verify:ghidra:ppc`) and a real PowerPC binary. <details> <summary>What was taken out, and the tests</summary> Taken out: the EM_PPC case in `elfFixedHeader.ts`, the `powerpc` entry and dispatch in the Ghidra provider, and the PowerPC profile links. Tests: `src/domain/binaryTarget.powerpc.test.ts`, `src/ghidra/GhidraPowerPcProfile.test.ts`, `tests/composition/analysis-sessions/powerpc-identity.test.ts`, `tests/conformance/ghidra/ppc-checks.test.ts`. </details> Thanks, arielfikru, for the clear report and the patch. 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Author
Owner

@arielfikru commented on GitHub (Oct 19, 2026):

The real Ghidra lane passes on current main, so the gap noted above ("Not run: the real Ghidra lane") is closed.

npm run verify:ghidra:ppc on main at e4c3d7239: exit 0, about 26 s.

  • Effective language PowerPC:BE:32:default, compiler spec default (the pin holds; no e500 fallback).
  • 3 procedures; all six checks pass: independent ELF header and symbols, loaded Ghidra identity and bound language, fixed instruction bytes and immediate, conditional branch destination, global word and marker bytes, CLI/MCP and snapshot identity.

Host: macOS 26.6.2 arm64, Ghidra 12.1.4, OpenJDK 21.0.12, Homebrew clang 23.1.3 + lld, GNU readelf 2.47, Node 24.18.0.

So milestone 1 is verified with a real provider. I would like to start milestone 2 (GameCube/Wii DOL); a short design proposal follows in a separate comment.

<!-- gh-comment-id:6260200958 --> @arielfikru commented on GitHub (Oct 19, 2026): The real Ghidra lane passes on current main, so the gap noted above ("Not run: the real Ghidra lane") is closed. `npm run verify:ghidra:ppc` on main at e4c3d7239: exit 0, about 26 s. - Effective language `PowerPC:BE:32:default`, compiler spec `default` (the pin holds; no e500 fallback). - 3 procedures; all six checks pass: independent ELF header and symbols, loaded Ghidra identity and bound language, fixed instruction bytes and immediate, conditional branch destination, global word and marker bytes, CLI/MCP and snapshot identity. Host: macOS 26.6.2 arm64, Ghidra 12.1.4, OpenJDK 21.0.12, Homebrew clang 23.1.3 + lld, GNU readelf 2.47, Node 24.18.0. So milestone 1 is verified with a real provider. I would like to start milestone 2 (GameCube/Wii DOL); a short design proposal follows in a separate comment.
Author
Owner

@arielfikru commented on GitHub (Oct 19, 2026):

Proposal for milestone 2 (GameCube/Wii DOL). I would like agreement on the shape before writing it, because it touches the import path and not just a profile.

Why the existing lanes don't fit. A DOL has no magic, and its 7 text and 11 data sections each load at their own address, plus a BSS range. The raw-binary lane from #1868 maps the whole file contiguously at one base through BinaryLoader, so it would place every section at the wrong address. The stock Ghidra loaders have no DOL support. The GameCube-Loader extension (Cuyler36, Apache-2.0) does, but its loader opens a dialog and throws HeadlessException under analyzeHeadless (seen on Ghidra 12.1.4).

Proposed shape

  1. Target: format: "gamecube-dol", caller-supplied like raw-binary (never inferred, since there is no magic). A pure parser in src/domain reads the 0x100-byte header and rejects these cases: offsets or sizes outside the file, overlapping sections, unaligned text, addresses outside 0x80000000..0x81800000, and an entry point not inside a text section.
  2. Import: BinaryLoader plus a new ReaGhidraPrepareDol.java pre-script. It rebuilds the memory map from the parsed table (text r-x, data rw-, BSS uninitialized) and sets the entry point. This follows the PrepareRaw/PrepareCom pattern, with no third-party loader.
  3. Language: pinned to PowerPC:BE:32:default by default, because it ships with Ghidra. Known limit: Gekko/Broadway paired-single instructions (psq_l, ps_*) do not decode there. That is documented in docs/ghidra-dol.md. If the installed catalog has PowerPC:BE:32:Gekko_Broadway (from the GameCube-Loader extension, language only, loader unused), a caller can select it through REA_GHIDRA_LANGUAGE_ID. The lane tests that override path as well as the default.
  4. Fixture: generated from the existing ppc.c fixture. A small script turns its ELF segments into a DOL at 0x80003100, so no game binary is involved. The independent cross-check: the expected section table and bytes come from readelf on the source ELF and are compared with what Ghidra reports. The real lane is verify:ghidra:dol.

Out of scope for this milestone: small-data anchors. r13/r2 are not in the header; they come from lis/ori pairs in the startup code. I would propose that as a follow-up, either caller-supplied or detected, together with REL modules and Wii U RPX.

Questions:

  • Is gamecube-dol an acceptable format name, or would you prefer dol?
  • Is the optional extension language through the override acceptable, or should the lane stay stock-only?
<!-- gh-comment-id:6260202056 --> @arielfikru commented on GitHub (Oct 19, 2026): Proposal for milestone 2 (GameCube/Wii DOL). I would like agreement on the shape before writing it, because it touches the import path and not just a profile. **Why the existing lanes don't fit.** A DOL has no magic, and its 7 text and 11 data sections each load at their own address, plus a BSS range. The raw-binary lane from #1868 maps the whole file contiguously at one base through `BinaryLoader`, so it would place every section at the wrong address. The stock Ghidra loaders have no DOL support. The GameCube-Loader extension (Cuyler36, Apache-2.0) does, but its loader opens a dialog and throws `HeadlessException` under `analyzeHeadless` (seen on Ghidra 12.1.4). **Proposed shape** 1. Target: `format: "gamecube-dol"`, caller-supplied like `raw-binary` (never inferred, since there is no magic). A pure parser in `src/domain` reads the 0x100-byte header and rejects these cases: offsets or sizes outside the file, overlapping sections, unaligned text, addresses outside `0x80000000..0x81800000`, and an entry point not inside a text section. 2. Import: `BinaryLoader` plus a new `ReaGhidraPrepareDol.java` pre-script. It rebuilds the memory map from the parsed table (text r-x, data rw-, BSS uninitialized) and sets the entry point. This follows the `PrepareRaw`/`PrepareCom` pattern, with no third-party loader. 3. Language: pinned to `PowerPC:BE:32:default` by default, because it ships with Ghidra. Known limit: Gekko/Broadway paired-single instructions (`psq_l`, `ps_*`) do not decode there. That is documented in `docs/ghidra-dol.md`. If the installed catalog has `PowerPC:BE:32:Gekko_Broadway` (from the GameCube-Loader extension, language only, loader unused), a caller can select it through `REA_GHIDRA_LANGUAGE_ID`. The lane tests that override path as well as the default. 4. Fixture: generated from the existing `ppc.c` fixture. A small script turns its ELF segments into a DOL at `0x80003100`, so no game binary is involved. The independent cross-check: the expected section table and bytes come from `readelf` on the source ELF and are compared with what Ghidra reports. The real lane is `verify:ghidra:dol`. **Out of scope for this milestone:** small-data anchors. `r13`/`r2` are not in the header; they come from `lis`/`ori` pairs in the startup code. I would propose that as a follow-up, either caller-supplied or detected, together with REL modules and Wii U RPX. Questions: - Is `gamecube-dol` an acceptable format name, or would you prefer `dol`? - Is the optional extension language through the override acceptable, or should the lane stay stock-only?
Author
Owner

@mvanhorn commented on GitHub (Oct 19, 2026):

Agreed on this shape: gamecube-dol makes the explicit format selection clear, and rebuilding the section map through a pre-script fits the noncontiguous layout.

The optional extension language through REA_GHIDRA_LANGUAGE_ID is acceptable, with the stock language as the default and the paired-single limitation documented. Keep the stock verification lane runnable without the extension, and make override coverage conditional on that language being installed.

The parser should also validate address-plus-size bounds and BSS overlap with loaded sections. Small-data anchors, REL, and RPX make sense as separate follow-ups.

<!-- gh-comment-id:6269131074 --> @mvanhorn commented on GitHub (Oct 19, 2026): Agreed on this shape: `gamecube-dol` makes the explicit format selection clear, and rebuilding the section map through a pre-script fits the noncontiguous layout. The optional extension language through `REA_GHIDRA_LANGUAGE_ID` is acceptable, with the stock language as the default and the paired-single limitation documented. Keep the stock verification lane runnable without the extension, and make override coverage conditional on that language being installed. The parser should also validate address-plus-size bounds and BSS overlap with loaded sections. Small-data anchors, REL, and RPX make sense as separate follow-ups.
Author
Owner

@WyattTheSkid commented on GitHub (Oct 20, 2026):

Don't forget about IDA!

<!-- gh-comment-id:6283734618 --> @WyattTheSkid commented on GitHub (Oct 20, 2026): Don't forget about IDA!
Author
Owner

@CassieFox23 commented on GitHub (Oct 20, 2026):

TL;DR: Milestone 1 works on Linux with the real Ghidra lane. npm run verify:ghidra:ppc passed on main 90e433df0. They match the numbers arielfikru gave. The three PowerPC profile tests I worried about earlier pass on main.

Thanks, arielfikru, for running the real lane first. This is a second check on a different box. Milestone 2 is out of scope here.

Check Result on main 90e433df0
npm run verify:ghidra:ppc exit 0, 23.9 s (arielfikru: about 26 s)
Effective language PowerPC:BE:32:default
Compiler spec default
Procedures 3
Six lane checks all six listed, status passed
The four PowerPC test files, Node 22.23.1 27 passed
Same, Node 24.18.0 27 passed
Same, Node 26.11.1 27 passed

About the three tests: in my earlier check on the Refs commit c86b09a10, three tests in src/ghidra/GhidraPowerPcProfile.test.ts failed. They cover the e500 language pick, a configured language, and header flags. The full names are in the fold below. On main that file passes 10 of 10 on all three Node versions. They already passed on main on Oct 18, so this is not a new fix. I did not rerun the Refs commit.

arielfikru ran it at e4c3d7239. Main is now 137 commits past that. The PowerPC files I compared were identical between the two. I did not read the changes to GhidraInstallation.ts and GhidraPeProfile.ts.

Not run

  • Milestone 2, GameCube/Wii DOL. It is out of scope here.
  • A real-world PowerPC binary. The lane builds its own small test file with clang. It is not a third-party binary.
  • Windows and macOS, and the Hopper and IDA adapters.
  • A whole-suite run.
The three tests
  • binds the generic language instead of the loader's e500 choice
  • lets a configured language replace the lane default
  • commits header flags so a changed interpretation changes the profile
Commands
npm --prefix <tree> ci --ignore-scripts
npm --prefix <tree> run build:cached
npm --prefix <tree> run test:prepare
GHIDRA_INSTALL_DIR=~/.local/opt/ghidra_12.1.4_PUBLIC JAVA_HOME=~/.local/opt/jdk-21.0.12.1+1 npm --prefix <tree> run verify:ghidra:ppc
GHIDRA_INSTALL_DIR=... JAVA_HOME=... npm --prefix <tree> run test:focused -- src/domain/binaryTarget.powerpc.test.ts src/ghidra/GhidraPowerPcProfile.test.ts tests/composition/analysis-sessions/powerpc-identity.test.ts tests/conformance/ghidra/ppc-checks.test.ts

The 23.9 s covers the whole npm run, including a cached build and the clang build of the fixture. The box was shared with other jobs.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.11
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 22.23.1, 24.18.0, 26.11.1
  • Head: 90e433df0
  • Other: Ghidra 12.1.4, JDK 21.0.12.1, GNU readelf 2.46.1, clang 22.1.8

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6295121697 --> @CassieFox23 commented on GitHub (Oct 20, 2026): **TL;DR:** Milestone 1 works on Linux with the real Ghidra lane. `npm run verify:ghidra:ppc` passed on main 90e433df0. They match the numbers arielfikru gave. The three PowerPC profile tests I worried about earlier pass on main. Thanks, arielfikru, for running the real lane first. This is a second check on a different box. Milestone 2 is out of scope here. | Check | Result on main 90e433df0 | |---|---| | `npm run verify:ghidra:ppc` | exit 0, 23.9 s (arielfikru: about 26 s) | | Effective language | PowerPC:BE:32:default | | Compiler spec | default | | Procedures | 3 | | Six lane checks | all six listed, status passed | | The four PowerPC test files, Node 22.23.1 | 27 passed | | Same, Node 24.18.0 | 27 passed | | Same, Node 26.11.1 | 27 passed | About the three tests: in my earlier check on the Refs commit c86b09a10, three tests in `src/ghidra/GhidraPowerPcProfile.test.ts` failed. They cover the e500 language pick, a configured language, and header flags. The full names are in the fold below. On main that file passes 10 of 10 on all three Node versions. They already passed on main on Oct 18, so this is not a new fix. I did not rerun the Refs commit. arielfikru ran it at e4c3d7239. Main is now 137 commits past that. The PowerPC files I compared were identical between the two. I did not read the changes to `GhidraInstallation.ts` and `GhidraPeProfile.ts`. ### Not run - Milestone 2, GameCube/Wii DOL. It is out of scope here. - A real-world PowerPC binary. The lane builds its own small test file with clang. It is not a third-party binary. - Windows and macOS, and the Hopper and IDA adapters. - A whole-suite run. <details> <summary>The three tests</summary> - `binds the generic language instead of the loader's e500 choice` - `lets a configured language replace the lane default` - `commits header flags so a changed interpretation changes the profile` </details> <details> <summary>Commands</summary> ``` npm --prefix <tree> ci --ignore-scripts npm --prefix <tree> run build:cached npm --prefix <tree> run test:prepare GHIDRA_INSTALL_DIR=~/.local/opt/ghidra_12.1.4_PUBLIC JAVA_HOME=~/.local/opt/jdk-21.0.12.1+1 npm --prefix <tree> run verify:ghidra:ppc GHIDRA_INSTALL_DIR=... JAVA_HOME=... npm --prefix <tree> run test:focused -- src/domain/binaryTarget.powerpc.test.ts src/ghidra/GhidraPowerPcProfile.test.ts tests/composition/analysis-sessions/powerpc-identity.test.ts tests/conformance/ghidra/ppc-checks.test.ts ``` The 23.9 s covers the whole `npm run`, including a cached build and the clang build of the fixture. The box was shared with other jobs. </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.11 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB DDR4-3600 - Node: 22.23.1, 24.18.0, 26.11.1 - Head: 90e433df0 - Other: Ghidra 12.1.4, JDK 21.0.12.1, GNU readelf 2.46.1, clang 22.1.8 </details> 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Author
Owner

@arielfikru commented on GitHub (Oct 23, 2026):

A maintainer scope decision is needed before #6240 can be integrated. The earlier agreement in this thread came from a contributor. Could a maintainer confirm the shape below, or narrow it?

  • Format: a caller-selected gamecube-dol target format, never auto-detected.
  • Import: read-only, through Ghidra BinaryLoader plus a pre-script that rebuilds the section map.
  • Language: stock PowerPC:BE:32:default, with an optional REA_GHIDRA_LANGUAGE_ID override.
  • Out of scope: no target execution, injection or mutation. IDA/Hopper decline it.
  • Follow-ups: small-data anchors, REL and RPX.
<!-- gh-comment-id:6368653424 --> @arielfikru commented on GitHub (Oct 23, 2026): A maintainer scope decision is needed before #6240 can be integrated. The earlier agreement in this thread came from a contributor. Could a maintainer confirm the shape below, or narrow it? - **Format:** a caller-selected `gamecube-dol` target format, never auto-detected. - **Import:** read-only, through Ghidra `BinaryLoader` plus a pre-script that rebuilds the section map. - **Language:** stock `PowerPC:BE:32:default`, with an optional `REA_GHIDRA_LANGUAGE_ID` override. - **Out of scope:** no target execution, injection or mutation. IDA/Hopper decline it. - **Follow-ups:** small-data anchors, REL and RPX.
Author
Owner

@mvanhorn commented on GitHub (Oct 24, 2026):

Could a maintainer confirm or narrow the scope for #6240: caller-selected gamecube-dol, read-only import through Ghidra BinaryLoader plus a section-map pre-script, stock PowerPC:BE:32:default with an optional REA_GHIDRA_LANGUAGE_ID override, and IDA/Hopper declining the format? Target execution, injection, and mutation remain out of scope; small-data anchors, REL, and RPX remain follow-ups.

<!-- gh-comment-id:6379700597 --> @mvanhorn commented on GitHub (Oct 24, 2026): Could a maintainer confirm or narrow the scope for #6240: caller-selected `gamecube-dol`, read-only import through Ghidra BinaryLoader plus a section-map pre-script, stock `PowerPC:BE:32:default` with an optional `REA_GHIDRA_LANGUAGE_ID` override, and IDA/Hopper declining the format? Target execution, injection, and mutation remain out of scope; small-data anchors, REL, and RPX remain follow-ups.
Author
Owner

@arielfikru commented on GitHub (Oct 24, 2026):

Follow-up proposal for the small-data anchors listed in #6240's out-of-scope notes. I'm asking for a maintainer scope decision before implementing.

Problem: a DOL imported through gamecube-dol has r2/r13 unset. Every small-data access (lwz r3,-0x1234(r13)) stays an unresolved register offset, so decompiled code loses its globals.

Proposed shape:

  • Detection: after admission, detect the anchors read-only from the startup code. Look for exactly one lis rN,hi + ori/addi rN,rN,lo pair for r2 and for r13, in the code reached from the header entry. Report each value with the address of the instruction pair that produced it. Ambiguity or absence fails closed: the anchor is reported as unknown and left unset, never guessed.
  • Override: an optional caller-supplied value per register replaces detection. The profile records whether each value came from detection or from the caller.
  • Ghidra: the DOL pre-script sets r2/r13 as program context over the text sections before auto-analysis. The values become part of the analysis profile digest and of the load-image attestation.
  • Verification: the fixture DOL gets a synthetic __init_registers. The real lane checks the detected values and that a small-data load decompiles to its global's address.
  • Out of scope: no target execution, mutation or new providers. IDA/Hopper still decline DOL. REL/RPX remain separate.

Evidence from a commercial Wii main.dol: the pairs in __init_registers give r13 = .sdata + 0x8000 and r2 = .sdata2 + 0x8000, which matches the EABI convention.

<!-- gh-comment-id:6385484482 --> @arielfikru commented on GitHub (Oct 24, 2026): Follow-up proposal for the small-data anchors listed in #6240's out-of-scope notes. I'm asking for a maintainer scope decision before implementing. **Problem:** a DOL imported through `gamecube-dol` has r2/r13 unset. Every small-data access (`lwz r3,-0x1234(r13)`) stays an unresolved register offset, so decompiled code loses its globals. **Proposed shape:** - **Detection:** after admission, detect the anchors read-only from the startup code. Look for exactly one `lis rN,hi` + `ori/addi rN,rN,lo` pair for r2 and for r13, in the code reached from the header entry. Report each value with the address of the instruction pair that produced it. Ambiguity or absence fails closed: the anchor is reported as unknown and left unset, never guessed. - **Override:** an optional caller-supplied value per register replaces detection. The profile records whether each value came from detection or from the caller. - **Ghidra:** the DOL pre-script sets r2/r13 as program context over the text sections before auto-analysis. The values become part of the analysis profile digest and of the load-image attestation. - **Verification:** the fixture DOL gets a synthetic `__init_registers`. The real lane checks the detected values and that a small-data load decompiles to its global's address. - **Out of scope:** no target execution, mutation or new providers. IDA/Hopper still decline DOL. REL/RPX remain separate. **Evidence from a commercial Wii `main.dol`:** the pairs in `__init_registers` give r13 = `.sdata` + 0x8000 and r2 = `.sdata2` + 0x8000, which matches the EABI convention.
Author
Owner

@mvanhorn commented on GitHub (Oct 25, 2026):

@morluto, one scope question on arielfikru's small-data anchor proposal above, so it can start once #6240 settles. Which of these would you take as the follow-up?

  1. Detect r2/r13 from the startup lis + ori/addi pairs, allow a caller override, and set them as Ghidra context before analysis (the full proposal).
  2. Caller-supplied r2/r13 only for now, with no startup scanning. Detection could come later.
  3. Detect and record the values in the profile, but leave Ghidra context unset unless the caller opts in.

Either way it would be a separate PR from #6240, read-only, with REL and RPX still out of scope.

<!-- gh-comment-id:6395911777 --> @mvanhorn commented on GitHub (Oct 25, 2026): @morluto, one scope question on arielfikru's small-data anchor proposal above, so it can start once #6240 settles. Which of these would you take as the follow-up? 1. Detect r2/r13 from the startup `lis` + `ori`/`addi` pairs, allow a caller override, and set them as Ghidra context before analysis (the full proposal). 2. Caller-supplied r2/r13 only for now, with no startup scanning. Detection could come later. 3. Detect and record the values in the profile, but leave Ghidra context unset unless the caller opts in. Either way it would be a separate PR from #6240, read-only, with REL and RPX still out of scope.
Author
Owner

@morluto commented on GitHub (Oct 26, 2026):

The requested generic PowerPC ELF lane landed in #1747, and explicit DOL import landed in #6240. I am keeping this issue open for the distinct remaining DOL work: r2/r13 small-data anchors are not recovered, and Gekko/Broadway decoding requires a separately installed language rather than being the stock default. The merged DOL PR reports its real Linux Ghidra run and explicitly notes that the Gekko pass was skipped; no additional provider run was performed in this triage.

<!-- gh-comment-id:6428543427 --> @morluto commented on GitHub (Oct 26, 2026): The requested generic PowerPC ELF lane landed in #1747, and explicit DOL import landed in #6240. I am keeping this issue open for the distinct remaining DOL work: `r2`/`r13` small-data anchors are not recovered, and Gekko/Broadway decoding requires a separately installed language rather than being the stock default. The merged DOL PR reports its real Linux Ghidra run and explicitly notes that the Gekko pass was skipped; no additional provider run was performed in this triage.
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#584
No description provided.