[GH-ISSUE #3282] [Feature] Ghidra provider: support universal (FAT) Mach-O by importing the selected slice #1172

Open
opened 2026-10-15 18:31:44 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @lifetose on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3282

Problem

With REA_ANALYSIS_PROVIDER=ghidra (or provider_id: "ghidra"), open_binary rejects every universal Mach-O:

code: architecture_unsupported
reason: Ghidra v1 cannot enforce the selected architecture when importing a universal Mach-O; analyze a thinned Mach-O slice instead.

Most macOS targets are universal: system binaries (/bin/ls), and the main executable of most .app bundles (e.g. /System/Applications/Calculator.app). This makes Ghidra unusable as the default provider on macOS without manual lipo -thin first, and with provider_id pinned there is no fallback.

I understand this guard was added after #656 (Ghidra analyzed the x86_64 slice while reporting arm64), so the refusal is the safe behavior — this request is for slice selection rather than removing the guard.

Proposal

When the target is a universal Mach-O and the provider is Ghidra:

  1. Use the existing MachOSliceArtifactReader (lipo-backed slice ranges) to materialize the selected architecture's slice bytes into a private, content-addressed temp file.
  2. Import that single-arch file into Ghidra, so architecture is enforced by construction.
  3. Keep subject / Evidence pointing at the original universal file and its digest, and record the slice (arch, offset, size, slice sha256) in the analysis profile so results stay traceable.

IDA already handles universal inputs by selecting a slice (#2139, #2285), so this would bring Ghidra to parity.

Workaround today

lipo <file> -thin arm64e -output <slice> and open the slice. Works (Ghidra 12.1.4, REA 6.4.0), but loses the link to the original artifact and must be done by hand for every target.

Environment

  • REA 6.4.0 (npm latest; same check present on main as of 2026-10-14)
  • Ghidra 12.1.4, Temurin JDK 27
  • macOS 26.6.2, Apple Silicon (arm64)
Originally created by @lifetose on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3282 ### Problem With `REA_ANALYSIS_PROVIDER=ghidra` (or `provider_id: "ghidra"`), `open_binary` rejects every universal Mach-O: ``` code: architecture_unsupported reason: Ghidra v1 cannot enforce the selected architecture when importing a universal Mach-O; analyze a thinned Mach-O slice instead. ``` Most macOS targets are universal: system binaries (`/bin/ls`), and the main executable of most `.app` bundles (e.g. `/System/Applications/Calculator.app`). This makes Ghidra unusable as the default provider on macOS without manual `lipo -thin` first, and with `provider_id` pinned there is no fallback. I understand this guard was added after #656 (Ghidra analyzed the x86_64 slice while reporting arm64), so the refusal is the safe behavior — this request is for slice selection rather than removing the guard. ### Proposal When the target is a universal Mach-O and the provider is Ghidra: 1. Use the existing `MachOSliceArtifactReader` (lipo-backed slice ranges) to materialize the selected architecture's slice bytes into a private, content-addressed temp file. 2. Import that single-arch file into Ghidra, so architecture is enforced by construction. 3. Keep `subject` / Evidence pointing at the original universal file and its digest, and record the slice (arch, offset, size, slice sha256) in the analysis profile so results stay traceable. IDA already handles universal inputs by selecting a slice (#2139, #2285), so this would bring Ghidra to parity. ### Workaround today `lipo <file> -thin arm64e -output <slice>` and open the slice. Works (Ghidra 12.1.4, REA 6.4.0), but loses the link to the original artifact and must be done by hand for every target. ### Environment - REA 6.4.0 (npm latest; same check present on `main` as of 2026-10-14) - Ghidra 12.1.4, Temurin JDK 27 - macOS 26.6.2, Apple Silicon (arm64)
Author
Owner

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

Still reproduces on REA 6.8.0 (Ghidra 12.1.4, Temurin JDK 21.0.13, macOS 27.0.1, Apple Silicon): open_binary with provider_id: "ghidra" on /bin/ls and on /System/Applications/Calculator.app/Contents/MacOS/Calculator returns architecture_unsupported with the "analyze a thinned Mach-O slice instead" reason.

Linking #6392, which implements this and is in review.

A provenance-preserving workaround already ships, so lipo isn't needed. #6392's description says REA has no tool that thins a slice, but extract_artifact does this through MachOSliceArtifactReader:

open_binary      { "path": "/bin/ls" }                     # no provider_id
extract_artifact {}                                        # writes <output_root>/slices/{arm64e,arm64e.x1,x86_64}
open_binary      { "path": "<output_root>/slices/arm64e", "provider_id": "ghidra" }

CLI equivalent: rea extract-artifact /bin/ls --format json, then rea analyze <output_root>/slices/arm64e --provider ghidra. Verified on 6.8.0: /bin/ls arm64e → 218 procedures, x86_64 → 220; Calculator arm64e → 7,594 procedures. The extraction Evidence records the source digest and manifest, so the slice stays traceable to the universal file, unlike a manual lipo.

Small ask until #6392 lands: have the architecture_unsupported refusal name this route in its remediation (and in the skill's native-and-artifacts.md). Right now the message says to use a thinned slice without naming the tool that makes one, so agents stop at the refusal or fall back to lipo.

<!-- gh-comment-id:6400823818 --> @udomiayetony commented on GitHub (Oct 25, 2026): Still reproduces on REA 6.8.0 (Ghidra 12.1.4, Temurin JDK 21.0.13, macOS 27.0.1, Apple Silicon): `open_binary` with `provider_id: "ghidra"` on `/bin/ls` and on `/System/Applications/Calculator.app/Contents/MacOS/Calculator` returns `architecture_unsupported` with the "analyze a thinned Mach-O slice instead" reason. Linking #6392, which implements this and is in review. **A provenance-preserving workaround already ships, so `lipo` isn't needed.** #6392's description says REA has no tool that thins a slice, but `extract_artifact` does this through `MachOSliceArtifactReader`: ```text open_binary { "path": "/bin/ls" } # no provider_id extract_artifact {} # writes <output_root>/slices/{arm64e,arm64e.x1,x86_64} open_binary { "path": "<output_root>/slices/arm64e", "provider_id": "ghidra" } ``` CLI equivalent: `rea extract-artifact /bin/ls --format json`, then `rea analyze <output_root>/slices/arm64e --provider ghidra`. Verified on 6.8.0: `/bin/ls` arm64e → 218 procedures, x86_64 → 220; Calculator arm64e → 7,594 procedures. The extraction Evidence records the source digest and manifest, so the slice stays traceable to the universal file, unlike a manual `lipo`. **Small ask until #6392 lands:** have the `architecture_unsupported` refusal name this route in its `remediation` (and in the skill's `native-and-artifacts.md`). Right now the message says to use a thinned slice without naming the tool that makes one, so agents stop at the refusal or fall back to `lipo`.
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#1172
No description provided.