[PR #7141] fix(ghidra): explain building the native decompiler on macOS and Linux ARM64 #7669

Open
opened 2026-11-03 13:39:31 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/7141
Author: @Navneet14-cmd
Created: 10/25/2026
Status: 🔄 Open

Base: main ← Head: docs/ghidra-macos-native-decompiler


📝 Commits (8)

  • 4f43ae0 docs(ghidra): explain building the native decompiler on macOS and Linux ARM64
  • 6995fec fix(ghidra): limit buildNatives hint to the missing-decompiler case
  • 0907ba5 test(ghidra): cover a foreign decompiler selected from build/os
  • 223c996 fix(ghidra): advise rebuilding a foreign build/os decompiler
  • 798b5c2 Merge remote-tracking branch 'upstream/main' into docs/ghidra-macos-native-decompiler
  • f940ddb fix(ghidra): name the JVM architecture before buildNatives when the decompiler is missing
  • 683f96f Merge remote-tracking branch 'upstream/main' into docs/ghidra-macos-native-decompiler
  • 67d964d docs(ghidra): cross-reference the buildNatives route from the manual macOS recipes

📊 Changes

3 files changed (+266 additions, -26 deletions)

View changed files

📝 docs/installation.md (+14 -4)
📝 src/ghidra/GhidraInstallation.test.ts (+154 -4)
📝 src/ghidra/GhidraInstallation.ts (+98 -18)

📄 Description

Summary

Fixes #7018

On macOS, the official Ghidra release archive has no native decompiler. The installation docs only mentioned this for Linux ARM64, and the native_decompiler doctor remediation named the expected path but not how to produce it. This PR documents Ghidra's buildNatives task for macOS and Linux ARM64 hosts and names it in the remediation.

Also fixes the incompatible-executable case: REA selects os/<platform>/ before build/os/<platform>/, so a foreign binary there keeps winning after buildNatives. The remediation now names the selected path and says to remove or replace it first. Discovery logic is unchanged; skipping an incompatible os/ candidate when build/os/ has a compatible one would be the root fix, which I can do as a follow-up PR if you'd like.

Problem and expected behavior

On macOS arm64 with the official Ghidra 12.1.4 archive, rea doctor / rea providers reports:

native_decompiler: Matching native decompiler for darwin/arm64 was not found
remediation: Provide an executable Ghidra/Features/Decompiler/os/mac_arm_64/decompile, or its build/os equivalent. REA does not build or install native tools.

The user can't tell from this that the binary is missing from every official macOS install, or that Ghidra ships its own task to build it.

Change and scope

  • docs/installation.md: says that official release archives include native binaries only for Windows and Linux x86-64. On macOS (mac_arm_64 / mac_x86_64) and Linux ARM64 (linux_arm_64), users build them once with ./gradlew buildNatives (or gradle buildNatives) from <GhidraInstallDir>/support/gradle/. This writes Ghidra/Features/Decompiler/build/os/<platform>/, which REA already accepts.
  • docs/installation.md also notes that REA selects os/<platform>/ before build/os/<platform>/, so an incompatible os/ executable must be removed before relying on buildNatives.
  • The manual macOS recipes (make ghidra_opt, which installs into os/<platform>/) now say where buildNatives writes (build/os/<platform>/) and link to the Ghidra section for the Gradle route. This follows a coverage check against #7019.
  • src/ghidra/GhidraInstallation.ts: the duplicated native_decompiler remediation strings now come from two helpers that reuse the existing NATIVE_PLATFORMS host-to-directory mapping. "REA does not build or install native tools." is kept in both.
    • Missing decompiler (nativeDecompilerRemediation): for directories that release archives don't ship, adds the buildNatives hint. Other platforms get the same text as before.
    • Incompatible header (foreignNativeDecompilerRemediation) names the selected path and branches on which directory it came from:
      • Selected under os/: says os/ takes precedence over build/os/ and to remove or replace the file. On macOS and Linux ARM64 it adds that the file must be removed before a buildNatives output can be selected.
      • Selected under build/os/ (any platform): says to remove the file and rerun buildNatives to rebuild it for this host.
    • installationCoordinates records which candidate it selected (os/ or build/os/) as an internal field, so the remediation doesn't re-parse the path. Candidate order and the public inspection output are unchanged.
    • Missing decompiler with a JVM-architecture mismatch (for example an x86_64 JDK under Rosetta on Apple silicon): buildNatives under that JDK can't produce the host's directory, so the check now reports the JVM's os.arch and the directory it would select, and the remediation points at JAVA_HOME first instead of buildNatives. It matches the existing advice when a decompiler is present. Check order, passed, code and discovery are unchanged.
  • Not included: REA still does not build or install native tools, and admission rules are unchanged. The Homebrew 12.2 gate (#6921) is a related but separate issue.

Follow the contribution scope: ordinary documentation/diagnostic fix within existing boundaries; maintainer scope decision not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: GhidraInstallation.ts remediation text for the native_decompiler check (and its detail text in the JVM-mismatch case); the two inline copies were replaced by two helpers (missing and incompatible). Discovery now also records which candidate directory it selected; selection order is unchanged.

  • Contribution scope fit and linked maintainer decision: not applicable (docs and diagnostic text).

  • CLI and MCP/tool-catalog contract: for a missing decompiler, the remediation changes only on darwin/x64, darwin/arm64 and linux/arm64. When the JVM's os.arch also selects a different directory than the host, both detail and remediation change on any platform. For an incompatible decompiler, the remediation changes on every platform. Check names, codes, statuses, selection order and native_decompiler_path are unchanged.

  • Provider, bridge, target-format, or platform compatibility: none.

  • Evidence, artifact, provenance, or reconstruction contract: none.

  • Target selection, authority, upstream behavior, and cleanup details: not applicable.

  • Generated metadata, package, or installation impact: none.

  • Documentation or usability only; no target interaction.

Evidence and regression coverage

  • Tests added or updated: src/ghidra/GhidraInstallation.test.ts, "Ghidra native decompiler remediation". It checks the exact remediation for a missing decompiler on darwin/arm64, darwin/x64 and linux/arm64, and checks that linux/x64 keeps the old text with no buildNatives hint. In "Ghidra native decompiler compatibility", the incompatible-header test now asserts the exact remediation (including built for mach-o arm64 and the remove/replace advice) and that the missing-file buildNatives wording is absent. A new test covers an incompatible executable selected from build/os/mac_arm_64/ and checks that the remediation names that path and advises removing it and rerunning buildNatives. A new "Ghidra JVM native directory remediation" test covers darwin/arm64 with an x86_64 JDK and no decompiler. It checks that detail names os.arch=x86_64, the remediation names JAVA_HOME, and the remediation doesn't mention buildNatives.
  • Base reproduction (the first three results were run on the earlier 115-test version of the file): with src/ghidra/GhidraInstallation.ts reverted to main, the three missing-file platform cases fail and the linux/x64 case passes. With the source reverted to the first PR commit (4f43ae056), the two incompatible-header tests fail. With the source reverted to 0907ba57e (before the build/os/ branch), only the build/os/ test fails. With the source reverted to 798b5c29c (before the JVM-mismatch fix), only the new JVM test fails. With the change, all 119 tests in the file pass.
  • Upstream source: Ghidra 12.1.4 docs/GettingStarted.md, "Building Native Components". Official releases include native binaries for Windows x86-64, Windows ARM64 (via x86 emulation) and Linux x86-64. macOS x86/ARM64 and Linux ARM64 need user-built binaries via gradle buildNatives.
  • Observed (reported by @danieljustus in #7018 / #7019, macOS arm64, ghidra_12.1.4_PUBLIC_20260921.zip): Ghidra/Features/Decompiler/os/ contained only linux_x86_64 and win_x86_64. After support/gradle/gradlew --no-daemon buildNatives, the provider reported build/os/mac_arm_64/decompile ... matches darwin/arm64 and availability: available.
  • Remaining proof gaps: macOS x64 and Linux ARM64 buildNatives output was not run here; those cases rest on the upstream docs. The JVM-mismatch case (an x86_64 JDK under Rosetta on Apple silicon with no decompiler) is covered by the unit test only; it was not observed on a real host. I did not re-run the macOS arm64 build myself; that observation is from #7019.

Validation performed

  • npm run test:local -- src/ghidra/GhidraInstallation.test.ts: 115 passed
  • Same command with the source file reverted to main (114-test version of the file): 3 failed (the new platform cases), 111 passed
  • Same command with the source file reverted to 4f43ae056: 2 failed (the incompatible-header cases), 113 passed
  • Same command with the source file reverted to 0907ba57e: 1 failed (the build/os/ case), 114 passed
  • npm run check:fast (typecheck + lint): passed
  • npm run docs:check: passed
  • npm run verify:test-discovery: passed (2495 test files discovered exactly once)
  • Run on f940ddb85, Node 24.22.1:
    • Merged upstream/main into the branch (798b5c29c, no conflicts, Ghidra files untouched).
    • npm run test:focused -- src/ghidra/GhidraInstallation.test.ts: 116 passed
    • Same command with the source file reverted to 798b5c29c: 1 failed (the JVM-mismatch case), 115 passed
    • npm run build:cached, npm run check:fast, npm run docs:check: passed
    • npm run verify:test-discovery: passed (2495 test files discovered exactly once)
  • Run on 67d964d31 (after merging the current upstream/main), Node 24.22.1:
    • Merged upstream/main again (683f96f12). One conflict in installationCoordinates: kept the {path, built} candidates and used upstream's new appendCallerPath(...) for path construction. The branch diff against upstream/main is still only docs/installation.md, src/ghidra/GhidraInstallation.ts and src/ghidra/GhidraInstallation.test.ts.
    • npm run test:focused -- src/ghidra/GhidraInstallation.test.ts: 119 passed
    • npm run check:fast, npm run docs:check: passed
    • npm run verify:test-discovery: passed (2676 test files discovered exactly once)

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 with Ghidra 12.1.4 (observed by @danieljustus in #7019; see above).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; text only.

Author readiness

Human owner: @Navneet14-cmd

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

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.

🔄 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/7141 **Author:** [@Navneet14-cmd](https://github.com/Navneet14-cmd) **Created:** 10/25/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `docs/ghidra-macos-native-decompiler` --- ### 📝 Commits (8) - [`4f43ae0`](https://github.com/morluto/rea/commit/4f43ae0560c41590d66626dbc45b1c6bb375fc6f) docs(ghidra): explain building the native decompiler on macOS and Linux ARM64 - [`6995fec`](https://github.com/morluto/rea/commit/6995fecd7430edec52a96d6bbbc13283a5f60dcc) fix(ghidra): limit buildNatives hint to the missing-decompiler case - [`0907ba5`](https://github.com/morluto/rea/commit/0907ba57ec1b0797b6d8b7917138cf3bc1363a1e) test(ghidra): cover a foreign decompiler selected from build/os - [`223c996`](https://github.com/morluto/rea/commit/223c996caba091004ee843b9f9271f530393e68b) fix(ghidra): advise rebuilding a foreign build/os decompiler - [`798b5c2`](https://github.com/morluto/rea/commit/798b5c29ce12566722dafc47079a321a9f63c917) Merge remote-tracking branch 'upstream/main' into docs/ghidra-macos-native-decompiler - [`f940ddb`](https://github.com/morluto/rea/commit/f940ddb85658630e4fe52ce6d0f71adba864024d) fix(ghidra): name the JVM architecture before buildNatives when the decompiler is missing - [`683f96f`](https://github.com/morluto/rea/commit/683f96f12c7ad7743f4689145b6268dbcc54b69a) Merge remote-tracking branch 'upstream/main' into docs/ghidra-macos-native-decompiler - [`67d964d`](https://github.com/morluto/rea/commit/67d964d31c82d76868139b2c69ecc1625f78e556) docs(ghidra): cross-reference the buildNatives route from the manual macOS recipes ### 📊 Changes **3 files changed** (+266 additions, -26 deletions) <details> <summary>View changed files</summary> 📝 `docs/installation.md` (+14 -4) 📝 `src/ghidra/GhidraInstallation.test.ts` (+154 -4) 📝 `src/ghidra/GhidraInstallation.ts` (+98 -18) </details> ### 📄 Description ## Summary Fixes #7018 On macOS, the official Ghidra release archive has no native decompiler. The installation docs only mentioned this for Linux ARM64, and the `native_decompiler` doctor remediation named the expected path but not how to produce it. This PR documents Ghidra's `buildNatives` task for macOS and Linux ARM64 hosts and names it in the remediation. Also fixes the incompatible-executable case: REA selects `os/<platform>/` before `build/os/<platform>/`, so a foreign binary there keeps winning after `buildNatives`. The remediation now names the selected path and says to remove or replace it first. Discovery logic is unchanged; skipping an incompatible `os/` candidate when `build/os/` has a compatible one would be the root fix, which I can do as a follow-up PR if you'd like. ## Problem and expected behavior On macOS arm64 with the official Ghidra 12.1.4 archive, `rea doctor` / `rea providers` reports: ``` native_decompiler: Matching native decompiler for darwin/arm64 was not found remediation: Provide an executable Ghidra/Features/Decompiler/os/mac_arm_64/decompile, or its build/os equivalent. REA does not build or install native tools. ``` The user can't tell from this that the binary is missing from every official macOS install, or that Ghidra ships its own task to build it. ## Change and scope - `docs/installation.md`: says that official release archives include native binaries only for Windows and Linux x86-64. On macOS (`mac_arm_64` / `mac_x86_64`) and Linux ARM64 (`linux_arm_64`), users build them once with `./gradlew buildNatives` (or `gradle buildNatives`) from `<GhidraInstallDir>/support/gradle/`. This writes `Ghidra/Features/Decompiler/build/os/<platform>/`, which REA already accepts. - `docs/installation.md` also notes that REA selects `os/<platform>/` before `build/os/<platform>/`, so an incompatible `os/` executable must be removed before relying on `buildNatives`. - The manual macOS recipes (`make ghidra_opt`, which installs into `os/<platform>/`) now say where `buildNatives` writes (`build/os/<platform>/`) and link to the Ghidra section for the Gradle route. This follows a coverage check against #7019. - `src/ghidra/GhidraInstallation.ts`: the duplicated `native_decompiler` remediation strings now come from two helpers that reuse the existing `NATIVE_PLATFORMS` host-to-directory mapping. "REA does not build or install native tools." is kept in both. - Missing decompiler (`nativeDecompilerRemediation`): for directories that release archives don't ship, adds the `buildNatives` hint. Other platforms get the same text as before. - Incompatible header (`foreignNativeDecompilerRemediation`) names the selected path and branches on which directory it came from: - Selected under `os/`: says `os/` takes precedence over `build/os/` and to remove or replace the file. On macOS and Linux ARM64 it adds that the file must be removed before a `buildNatives` output can be selected. - Selected under `build/os/` (any platform): says to remove the file and rerun `buildNatives` to rebuild it for this host. - `installationCoordinates` records which candidate it selected (`os/` or `build/os/`) as an internal field, so the remediation doesn't re-parse the path. Candidate order and the public inspection output are unchanged. - Missing decompiler with a JVM-architecture mismatch (for example an x86_64 JDK under Rosetta on Apple silicon): `buildNatives` under that JDK can't produce the host's directory, so the check now reports the JVM's `os.arch` and the directory it would select, and the remediation points at `JAVA_HOME` first instead of `buildNatives`. It matches the existing advice when a decompiler is present. Check order, `passed`, `code` and discovery are unchanged. - Not included: REA still does not build or install native tools, and admission rules are unchanged. The Homebrew 12.2 gate (#6921) is a related but separate issue. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope): ordinary documentation/diagnostic fix within existing boundaries; maintainer scope decision not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: `GhidraInstallation.ts` remediation text for the `native_decompiler` check (and its `detail` text in the JVM-mismatch case); the two inline copies were replaced by two helpers (missing and incompatible). Discovery now also records which candidate directory it selected; selection order is unchanged. - Contribution scope fit and linked maintainer decision: not applicable (docs and diagnostic text). - CLI and MCP/tool-catalog contract: for a missing decompiler, the `remediation` changes only on darwin/x64, darwin/arm64 and linux/arm64. When the JVM's `os.arch` also selects a different directory than the host, both `detail` and `remediation` change on any platform. For an incompatible decompiler, the `remediation` changes on every platform. Check names, codes, statuses, selection order and `native_decompiler_path` are unchanged. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none. - [x] Documentation or usability only; no target interaction. ## Evidence and regression coverage - Tests added or updated: `src/ghidra/GhidraInstallation.test.ts`, "Ghidra native decompiler remediation". It checks the exact remediation for a missing decompiler on darwin/arm64, darwin/x64 and linux/arm64, and checks that linux/x64 keeps the old text with no `buildNatives` hint. In "Ghidra native decompiler compatibility", the incompatible-header test now asserts the exact remediation (including `built for mach-o arm64` and the remove/replace advice) and that the missing-file `buildNatives` wording is absent. A new test covers an incompatible executable selected from `build/os/mac_arm_64/` and checks that the remediation names that path and advises removing it and rerunning `buildNatives`. A new "Ghidra JVM native directory remediation" test covers darwin/arm64 with an x86_64 JDK and no decompiler. It checks that `detail` names `os.arch=x86_64`, the remediation names `JAVA_HOME`, and the remediation doesn't mention `buildNatives`. - Base reproduction (the first three results were run on the earlier 115-test version of the file): with `src/ghidra/GhidraInstallation.ts` reverted to `main`, the three missing-file platform cases fail and the linux/x64 case passes. With the source reverted to the first PR commit (`4f43ae056`), the two incompatible-header tests fail. With the source reverted to `0907ba57e` (before the `build/os/` branch), only the `build/os/` test fails. With the source reverted to `798b5c29c` (before the JVM-mismatch fix), only the new JVM test fails. With the change, all 119 tests in the file pass. - Upstream source: Ghidra 12.1.4 `docs/GettingStarted.md`, "Building Native Components". Official releases include native binaries for Windows x86-64, Windows ARM64 (via x86 emulation) and Linux x86-64. macOS x86/ARM64 and Linux ARM64 need user-built binaries via `gradle buildNatives`. - Observed (reported by @danieljustus in #7018 / #7019, macOS arm64, `ghidra_12.1.4_PUBLIC_20260921.zip`): `Ghidra/Features/Decompiler/os/` contained only `linux_x86_64` and `win_x86_64`. After `support/gradle/gradlew --no-daemon buildNatives`, the provider reported `build/os/mac_arm_64/decompile ... matches darwin/arm64` and `availability: available`. - Remaining proof gaps: macOS x64 and Linux ARM64 `buildNatives` output was not run here; those cases rest on the upstream docs. The JVM-mismatch case (an x86_64 JDK under Rosetta on Apple silicon with no decompiler) is covered by the unit test only; it was not observed on a real host. I did not re-run the macOS arm64 build myself; that observation is from #7019. ## Validation performed - `npm run test:local -- src/ghidra/GhidraInstallation.test.ts`: 115 passed - Same command with the source file reverted to `main` (114-test version of the file): 3 failed (the new platform cases), 111 passed - Same command with the source file reverted to `4f43ae056`: 2 failed (the incompatible-header cases), 113 passed - Same command with the source file reverted to `0907ba57e`: 1 failed (the `build/os/` case), 114 passed - `npm run check:fast` (typecheck + lint): passed - `npm run docs:check`: passed - `npm run verify:test-discovery`: passed (2495 test files discovered exactly once) - Run on `f940ddb85`, Node 24.22.1: - Merged `upstream/main` into the branch (`798b5c29c`, no conflicts, Ghidra files untouched). - `npm run test:focused -- src/ghidra/GhidraInstallation.test.ts`: 116 passed - Same command with the source file reverted to `798b5c29c`: 1 failed (the JVM-mismatch case), 115 passed - `npm run build:cached`, `npm run check:fast`, `npm run docs:check`: passed - `npm run verify:test-discovery`: passed (2495 test files discovered exactly once) - Run on `67d964d31` (after merging the current `upstream/main`), Node 24.22.1: - Merged `upstream/main` again (`683f96f12`). One conflict in `installationCoordinates`: kept the `{path, built}` candidates and used upstream's new `appendCallerPath(...)` for path construction. The branch diff against `upstream/main` is still only `docs/installation.md`, `src/ghidra/GhidraInstallation.ts` and `src/ghidra/GhidraInstallation.test.ts`. - `npm run test:focused -- src/ghidra/GhidraInstallation.test.ts`: 119 passed - `npm run check:fast`, `npm run docs:check`: passed - `npm run verify:test-discovery`: passed (2676 test files discovered exactly once) ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 with Ghidra 12.1.4 (observed by @danieljustus in #7019; see above). - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; text only. ## Author readiness Human owner: @Navneet14-cmd - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## 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. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#7669
No description provided.