[GH-ISSUE #4842] [Feature] Admit SPARC V9 ELF objects (EM_SPARCV9, 43) through the header-driven Ghidra lane #3007

Open
opened 2026-10-31 00:34:46 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @Zhouyang66639 on GitHub (Oct 17, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4842

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal. Searching this repository for sparc, EM_SPARCV9, and e_machine 43 returns no issue and no open pull request; #4781 / merged PR #4819 admitted EM_HEXAGON at the same switch, which is the pattern this request follows.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

open_binary refuses an ELF64 SPARC object (e_machine = 43, EM_SPARCV9) with

Cannot open artifact: unsupported ELF architecture.

The refusal is REA's own admission layer, not the engine. elfArchitecture() in src/domain/elfFixedHeader.ts maps eight ELF machine values (3, 8, 20, 40, 62, 164, 183, 243) and has no case for 43, so parseElf returns err("unsupported ELF architecture") before any provider is consulted.

Use cases

Solaris/illumos and NetBSD SPARC64 binaries: service daemons, proprietary agents, and firmware images delivered as ELF64 SPARC. Today REA cannot classify them at all, so inventory, analyze_function, and call-path work are unreachable for those targets even when rea doctor --provider ghidra is green.

Desired outcome and success criteria

REA classifies e_machine = 43 as a sparc executable family so the existing header-driven Ghidra lane imports it, and Hopper and IDA decline it by name with a Ghidra-directed reason — the same observable contract PR #4819 established for hexagon.

Expected interaction with targets

Read-only static analysis of one local file through a Ghidra headless import. No process execution, injection, mutation, network contact, or protection recovery.

Verification available so far

Source-derived. No SPARC binary was analyzed and no Ghidra process was launched for this report.

  • REA: src/domain/elfFixedHeader.ts elfArchitecture() has no case 43; parseElf fails when the map returns null.
  • Ghidra at the tag REA pins (Ghidra_12.1.4_build, Ghidra/Processors/Sparc/data/languages/): Sparc.opinion binds the ELF loader primary value (e_machine) 2 -> 32-bit big, 18 -> 32-bit big, 43 -> 64-bit big, and SparcV9.ldefs offers exactly two ids, sparc:BE:32:default and sparc:BE:64:default. So e_machine 43 resolves to one language with no caller choice and no REA_GHIDRA_LANGUAGE_ID override needed.

Verification details (optional)

Header shape under test: ELFCLASS64, big-endian, e_machine = 43 (elf(2, 2, 43) with the repository's own src/domain/binaryTarget.fixture.ts helper).

Alternatives considered

  • Admit EM_SPARC (2) and EM_SPARC32PLUS (18) here too. Both resolve to Ghidra's 32-bit language whose sla is V9, while machine 2 also covers pre-V9 V8 objects, so the family name alone would not establish the ISA. That is what the ELF-metadata route plus a pinned profile is for (mips, riscv, powerpc), so 32-bit SPARC is deliberately outside this request.
  • Add a pinned GhidraSparcProfile and conformance fixture now. That would advertise a verified lane nobody has run against a real SPARC target.
  • Leave the refusal in place.

Possible direction

One case 43 in elfArchitecture(), a sparc ELF member in BinaryTarget and ExecutableMetadata, and one shared owner for the family label the Hopper and IDA decline reasons print, so adding a family does not add a per-architecture conditional in each adapter.

References

#4781, merged PR #4819, Ghidra/Processors/Sparc/data/languages/Sparc.opinion, SparcV9.ldefs at tag Ghidra_12.1.4_build.

Originally created by @Zhouyang66639 on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4842 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. Searching this repository for `sparc`, `EM_SPARCV9`, and `e_machine 43` returns no issue and no open pull request; #4781 / merged PR #4819 admitted `EM_HEXAGON` at the same switch, which is the pattern this request follows. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation `open_binary` refuses an ELF64 SPARC object (`e_machine = 43`, `EM_SPARCV9`) with ``` Cannot open artifact: unsupported ELF architecture. ``` The refusal is REA's own admission layer, not the engine. `elfArchitecture()` in `src/domain/elfFixedHeader.ts` maps eight ELF machine values (3, 8, 20, 40, 62, 164, 183, 243) and has no case for 43, so `parseElf` returns `err("unsupported ELF architecture")` before any provider is consulted. ### Use cases Solaris/illumos and NetBSD SPARC64 binaries: service daemons, proprietary agents, and firmware images delivered as ELF64 SPARC. Today REA cannot classify them at all, so inventory, `analyze_function`, and call-path work are unreachable for those targets even when `rea doctor --provider ghidra` is green. ### Desired outcome and success criteria REA classifies `e_machine = 43` as a `sparc` executable family so the existing header-driven Ghidra lane imports it, and Hopper and IDA decline it by name with a Ghidra-directed reason — the same observable contract PR #4819 established for `hexagon`. ### Expected interaction with targets Read-only static analysis of one local file through a Ghidra headless import. No process execution, injection, mutation, network contact, or protection recovery. ### Verification available so far **Source-derived. No SPARC binary was analyzed and no Ghidra process was launched for this report.** - REA: `src/domain/elfFixedHeader.ts` `elfArchitecture()` has no case 43; `parseElf` fails when the map returns `null`. - Ghidra at the tag REA pins (`Ghidra_12.1.4_build`, `Ghidra/Processors/Sparc/data/languages/`): `Sparc.opinion` binds the ELF loader `primary` value (e_machine) `2` -> 32-bit big, `18` -> 32-bit big, `43` -> **64-bit big**, and `SparcV9.ldefs` offers exactly two ids, `sparc:BE:32:default` and `sparc:BE:64:default`. So e_machine 43 resolves to one language with no caller choice and no `REA_GHIDRA_LANGUAGE_ID` override needed. ### Verification details (optional) Header shape under test: ELFCLASS64, big-endian, `e_machine = 43` (`elf(2, 2, 43)` with the repository's own `src/domain/binaryTarget.fixture.ts` helper). ### Alternatives considered - Admit `EM_SPARC` (2) and `EM_SPARC32PLUS` (18) here too. Both resolve to Ghidra's 32-bit language whose `sla` is V9, while machine 2 also covers pre-V9 V8 objects, so the family name alone would not establish the ISA. That is what the ELF-metadata route plus a pinned profile is for (`mips`, `riscv`, `powerpc`), so 32-bit SPARC is deliberately outside this request. - Add a pinned `GhidraSparcProfile` and conformance fixture now. That would advertise a verified lane nobody has run against a real SPARC target. - Leave the refusal in place. ### Possible direction One `case 43` in `elfArchitecture()`, a `sparc` ELF member in `BinaryTarget` and `ExecutableMetadata`, and one shared owner for the family label the Hopper and IDA decline reasons print, so adding a family does not add a per-architecture conditional in each adapter. ### References #4781, merged PR #4819, `Ghidra/Processors/Sparc/data/languages/Sparc.opinion`, `SparcV9.ldefs` at tag `Ghidra_12.1.4_build`.
Author
Owner

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

The admission-layer gap is clear: elfArchitecture() simply has no case for 43, so parseElf bails before any provider is even consulted. Given that Ghidra 12.1.4 already binds e_machine 43 to SparcV9 64-bit big-endian with no language-choice ambiguity, the change surface is tiny and the contract you propose (Ghidra accepts, Hopper/IDA decline by name with a Ghidra-directed reason) mirrors #4819 nicely. One suggestion: add a unit test with a synthetic ELF64 header (or a minimal real .o from a cross toolchain) asserting elfArchitecture(43) resolves and parseElf succeeds, so this class of architecture-gap regressions is caught by CI rather than by users hitting 'unsupported ELF architecture'.

<!-- gh-comment-id:6248245738 --> @XinwangTao commented on GitHub (Oct 18, 2026): The admission-layer gap is clear: elfArchitecture() simply has no case for 43, so parseElf bails before any provider is even consulted. Given that Ghidra 12.1.4 already binds e_machine 43 to SparcV9 64-bit big-endian with no language-choice ambiguity, the change surface is tiny and the contract you propose (Ghidra accepts, Hopper/IDA decline by name with a Ghidra-directed reason) mirrors #4819 nicely. One suggestion: add a unit test with a synthetic ELF64 header (or a minimal real .o from a cross toolchain) asserting elfArchitecture(43) resolves and parseElf succeeds, so this class of architecture-gap regressions is caught by CI rather than by users hitting 'unsupported ELF architecture'.
Author
Owner

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

Thanks — the synthetic-header unit test you describe is in #4843, and it is the shape that catches this class of gap:

  • src/domain/binaryTarget.test.ts adds elf(2, 2, 43) (ELFCLASS64, big-endian, e_machine 43) to the classification table, so readElfFixedHeader → elfArchitecture() → parseElf must produce format: "elf", architecture: "sparc".
  • src/ghidra/GhidraProvider.behavior.test.ts asserts the resolved profile keeps loader and language_id at auto-from-header, so the admission does not smuggle in a pinned-language claim.
  • src/hopper/HopperProvider.test.ts and src/ida/IdaProvider.test.ts assert both adapters decline the family by name with a Ghidra-directed reason.

Failure check on the base tree: reverting only the seven source files and keeping those tests turns npm run test:focused on the five files into 10 failed / 165 passed; on the branch the same command reports 175 passed, and npm run test:fast reports 607 files / 9678 passed / 13 skipped / 0 failed.

Still open on my side, and recorded in the PR rather than hidden: no real Ghidra import of a SPARC64 binary and no verify:ghidra:sparc lane. This machine has no SPARC cross target in clang and no SPARC object, which is also why 32-bit SPARC (EM_SPARC 2, EM_SPARC32PLUS 18) is deliberately not admitted here — the 32-bit machine values do not pin down V8 versus V9 by themselves, so they need the metadata plus pinned-profile route instead of this header-only one.

<!-- gh-comment-id:6250628497 --> @Zhouyang66639 commented on GitHub (Oct 18, 2026): Thanks — the synthetic-header unit test you describe is in #4843, and it is the shape that catches this class of gap: - `src/domain/binaryTarget.test.ts` adds `elf(2, 2, 43)` (ELFCLASS64, big-endian, e_machine 43) to the classification table, so `readElfFixedHeader` → `elfArchitecture()` → `parseElf` must produce `format: "elf"`, `architecture: "sparc"`. - `src/ghidra/GhidraProvider.behavior.test.ts` asserts the resolved profile keeps `loader` and `language_id` at `auto-from-header`, so the admission does not smuggle in a pinned-language claim. - `src/hopper/HopperProvider.test.ts` and `src/ida/IdaProvider.test.ts` assert both adapters decline the family by name with a Ghidra-directed reason. Failure check on the base tree: reverting only the seven source files and keeping those tests turns `npm run test:focused` on the five files into 10 failed / 165 passed; on the branch the same command reports 175 passed, and `npm run test:fast` reports 607 files / 9678 passed / 13 skipped / 0 failed. Still open on my side, and recorded in the PR rather than hidden: no real Ghidra import of a SPARC64 binary and no `verify:ghidra:sparc` lane. This machine has no SPARC cross target in clang and no SPARC object, which is also why 32-bit SPARC (`EM_SPARC` 2, `EM_SPARC32PLUS` 18) is deliberately not admitted here — the 32-bit machine values do not pin down V8 versus V9 by themselves, so they need the metadata plus pinned-profile route instead of this header-only one.
Author
Owner

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

Cross-referencing with sibling architecture lane admission proposals in Cluster AN: #2338 (Ghidra MIPS: admit plain MIPS32 PIC shared libraries (ET_DYN) at their link base), #2734, #4487, and #5243. All propose expanding Ghidra header-driven binary admission for non-standard ELF architectures and PIE binaries (ET_DYN).

Consolidated under Cluster AN (Ghidra Header-Driven Architecture Lane Admission: ET_DYN, MIPS, RISC-V, SPARC) in tracker #4330.

<!-- gh-comment-id:6291455270 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with sibling architecture lane admission proposals in **Cluster AN**: #2338 (`Ghidra MIPS: admit plain MIPS32 PIC shared libraries (ET_DYN) at their link base`), #2734, #4487, and #5243. All propose expanding Ghidra header-driven binary admission for non-standard ELF architectures and PIE binaries (`ET_DYN`). Consolidated under **Cluster AN (Ghidra Header-Driven Architecture Lane Admission: ET_DYN, MIPS, RISC-V, SPARC)** in tracker #4330.
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#3007
No description provided.