[PR #1922] feat(ghidra): verify and pin the 32-bit ARM (armhf) lane #4912

Open
opened 2026-10-31 00:56:42 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1922
Author: @BaaRai
Created: 10/11/2026
Status: 🔄 Open

Base: main ← Head: feat/armhf-lane


📝 Commits (10+)

  • 156b53a feat(ghidra): verify and pin the 32-bit ARM (armhf) lane
  • 48946c7 feat(ghidra): verify the ARM ISA through .ARM.attributes before the v7 pin
  • 5b3539f fix(verify): build the armv8 refusal variant from the conformance sources
  • d9024b3 fix(ghidra): verify the ARMv7 variant and scope-bounded attribute reading
  • 7c8c89e Merge origin/main into feat/ghidra-armhf-lane
  • ee40681 fix(ghidra): tighten AEABI tag-zero handling and drop the unused admission export
  • e0e25e9 fix(ghidra): reconcile scoped AEABI declarations and refuse partial scans
  • 56bff4e fix(ghidra): parse scoped AEABI index lists and reconcile every aeabi subsection
  • c1858eb Merge remote-tracking branch 'origin/main' into feat/armhf-lane
  • 231763a fix(ghidra): preserve ARM attribute uncertainty and pin ownership

📊 Changes

26 files changed (+2484 additions, -65 deletions)

View changed files

➕ docs/ghidra-arm.md (+64 -0)
📝 docs/ghidra-provider.md (+3 -1)
📝 package.json (+1 -0)
➕ scripts/lib/arm-objcopy.d.mts (+16 -0)
➕ scripts/lib/arm-objcopy.mjs (+89 -0)
➕ scripts/verify-real-ghidra-arm.mjs (+491 -0)
➕ src/application/ArmElfAttributes.ts (+433 -0)
📝 src/application/BinaryTargetResolver.ts (+50 -26)
📝 src/domain/binaryTarget.ts (+32 -9)
📝 src/domain/binaryTargetTypes.ts (+43 -1)
📝 src/ghidra/GhidraAnalysisProfile.ts (+14 -0)
➕ src/ghidra/GhidraArmProfile.test.ts (+271 -0)
➕ src/ghidra/GhidraArmProfile.ts (+169 -0)
📝 src/ghidra/GhidraMipsProfile.ts (+5 -1)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+17 -5)
📝 src/ghidra/GhidraProvider.ts (+9 -0)
📝 src/ghidra/GhidraProviderClient.ts (+26 -4)
📝 src/hopper/HopperProviderDiscovery.test.ts (+1 -1)
➕ tests/boundary/filesystem/armElfAttributes.test.ts (+374 -0)
➕ tests/boundary/filesystem/armVerifierPrerequisites.test.ts (+99 -0)

...and 6 more files

📄 Description

Summary

Fixes #1745.

Backs the arm entry in the provider-neutral architecture set with a verified 32-bit ARM lane and pins ARM:LE:32:v7 for the admitted shape instead of Ghidra header auto-selection, which has several plausible wrong answers for ARM/Thumb-2 interworked images.

Problem and expected behavior

BINARY_ARCHITECTURES admitted arm with no verified fixture or statement covering armhf Linux ELF, so the entry was an allowlist guess. The issue asks to confirm or refute that, add the lane in the shape of the other architecture work (admit ELF32 EM_ARM, little-endian, ET_EXEC; select ARM:LE:32:v7; record the observed language/compiler spec in Evidence; refuse the rest with a specific unsupported constraint), and verify with a source-owned cross-compiled armhf fixture including a Thumb-2 interworking case, with no third-party binaries.

Change and scope

  • EM_ARM ELF targets now retain class, byte order, e_type and raw e_flags as provider-neutral facts (an arm metadata member mirroring the MIPS one). Mach-O 32-bit ARM slices and PE ARM images keep their existing classification; only ELF ARM carries the metadata.
  • Admission: little-endian ELF32 ET_EXEC with EABI5 is admitted; hard-float (armhf) and unspecified float conventions are covered. ELF64, big-endian armeb, EF_ARM_BE8 mixed-endian images, legacy or older ABIs, EF_ARM_ABI_FLOAT_SOFT, PIE (ET_DYN) and relocatable images are refused with their specific constraint rather than a guessed profile.
  • Pinning: the profile pins -processor ARM:LE:32:v7 with the default compiler spec through a provider-pinned-v1 selection; the bridge-reported language_id is checked against it after import (the DOS expectedLanguageId mechanism). A caller-selected REA_GHIDRA_LANGUAGE_ID/REA_GHIDRA_COMPILER_SPEC_ID still wins; combining the pin with an environment override is a typed refusal.
  • Evidence: the profile records the lane identity elf32-exec-le-eabi5-v7-p1... elf32-exec-le-eabi5-v7-pinned-v1, the raw header facts and the declared float ABI basis without interpreting it into a calling-convention claim. Sessions carry the limitation that the ELF float ABI declaration does not change the modeled calling convention.

Verification

Locally verified on Linux with Ghidra 12.1.4 PUBLIC, clang 21.1.8 (Debian) and GNU readelf 2.47; fork PRs currently receive no CI check runs in this repository, so results below are from this machine:

  • New npm run verify:ghidra:arm: builds the source-owned fixture with arm-linux-gnueabihf (ARMv7-A, hard float) containing an ARM-mode probe (mov r0, #42, e3a0002a) and a Thumb-2 interworked probe (movs r0, #42, 202a); checks REA's ELF interpretation against GNU readelf; asserts the pinned language after import; decodes both instruction-set states under ARM:LE:32:v7; recovers the direct call edge, pseudocode, global and marker bytes; walks a snapshot round-trip asserting the pinned profile parameters; proves typed MCP refusals for legacy-EABI and BE8 header variants; and closes with CLI/MCP parity and source-byte immutability.
  • vitest src/ghidra/GhidraArmProfile.test.ts (admission, refusal matrix, pin read-back, caller-override precedence), src/domain/binaryTarget.test.ts, and the Ghidra suite pass; tsc --noEmit clean; oxlint reports no new warnings on the touched files.

Author readiness

Human owner: @BaaRai

  • 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.

🔄 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/1922 **Author:** [@BaaRai](https://github.com/BaaRai) **Created:** 10/11/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/armhf-lane` --- ### 📝 Commits (10+) - [`156b53a`](https://github.com/morluto/rea/commit/156b53a34d0c5a7f53af7f78080445cb2b952bc3) feat(ghidra): verify and pin the 32-bit ARM (armhf) lane - [`48946c7`](https://github.com/morluto/rea/commit/48946c7169c4d5dfd629877d1e05070a5e535f93) feat(ghidra): verify the ARM ISA through .ARM.attributes before the v7 pin - [`5b3539f`](https://github.com/morluto/rea/commit/5b3539f5e3b7586b79673fb4bdbacf113173505b) fix(verify): build the armv8 refusal variant from the conformance sources - [`d9024b3`](https://github.com/morluto/rea/commit/d9024b31ad1de494c0b84aa7e021f87425c7dd8b) fix(ghidra): verify the ARMv7 variant and scope-bounded attribute reading - [`7c8c89e`](https://github.com/morluto/rea/commit/7c8c89ef37416e66b44d80a5389f57014efeb8ce) Merge origin/main into feat/ghidra-armhf-lane - [`ee40681`](https://github.com/morluto/rea/commit/ee40681478702122db47f4b71d1d1eb9b4fba413) fix(ghidra): tighten AEABI tag-zero handling and drop the unused admission export - [`e0e25e9`](https://github.com/morluto/rea/commit/e0e25e9d223e20e1a36ab4d3f1242aa113103473) fix(ghidra): reconcile scoped AEABI declarations and refuse partial scans - [`56bff4e`](https://github.com/morluto/rea/commit/56bff4e2eb66969a9f86cd89a1181539644d4739) fix(ghidra): parse scoped AEABI index lists and reconcile every aeabi subsection - [`c1858eb`](https://github.com/morluto/rea/commit/c1858eb0b2711d363a933011720b47adea5d117a) Merge remote-tracking branch 'origin/main' into feat/armhf-lane - [`231763a`](https://github.com/morluto/rea/commit/231763a31e6c7880d3f385986b1af9beaf9c9bdd) fix(ghidra): preserve ARM attribute uncertainty and pin ownership ### 📊 Changes **26 files changed** (+2484 additions, -65 deletions) <details> <summary>View changed files</summary> ➕ `docs/ghidra-arm.md` (+64 -0) 📝 `docs/ghidra-provider.md` (+3 -1) 📝 `package.json` (+1 -0) ➕ `scripts/lib/arm-objcopy.d.mts` (+16 -0) ➕ `scripts/lib/arm-objcopy.mjs` (+89 -0) ➕ `scripts/verify-real-ghidra-arm.mjs` (+491 -0) ➕ `src/application/ArmElfAttributes.ts` (+433 -0) 📝 `src/application/BinaryTargetResolver.ts` (+50 -26) 📝 `src/domain/binaryTarget.ts` (+32 -9) 📝 `src/domain/binaryTargetTypes.ts` (+43 -1) 📝 `src/ghidra/GhidraAnalysisProfile.ts` (+14 -0) ➕ `src/ghidra/GhidraArmProfile.test.ts` (+271 -0) ➕ `src/ghidra/GhidraArmProfile.ts` (+169 -0) 📝 `src/ghidra/GhidraMipsProfile.ts` (+5 -1) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+17 -5) 📝 `src/ghidra/GhidraProvider.ts` (+9 -0) 📝 `src/ghidra/GhidraProviderClient.ts` (+26 -4) 📝 `src/hopper/HopperProviderDiscovery.test.ts` (+1 -1) ➕ `tests/boundary/filesystem/armElfAttributes.test.ts` (+374 -0) ➕ `tests/boundary/filesystem/armVerifierPrerequisites.test.ts` (+99 -0) _...and 6 more files_ </details> ### 📄 Description ## Summary Fixes #1745. Backs the `arm` entry in the provider-neutral architecture set with a verified 32-bit ARM lane and pins `ARM:LE:32:v7` for the admitted shape instead of Ghidra header auto-selection, which has several plausible wrong answers for ARM/Thumb-2 interworked images. ## Problem and expected behavior `BINARY_ARCHITECTURES` admitted `arm` with no verified fixture or statement covering armhf Linux ELF, so the entry was an allowlist guess. The issue asks to confirm or refute that, add the lane in the shape of the other architecture work (admit ELF32 `EM_ARM`, little-endian, `ET_EXEC`; select `ARM:LE:32:v7`; record the observed language/compiler spec in Evidence; refuse the rest with a specific unsupported constraint), and verify with a source-owned cross-compiled armhf fixture including a Thumb-2 interworking case, with no third-party binaries. ## Change and scope - `EM_ARM` ELF targets now retain class, byte order, `e_type` and raw `e_flags` as provider-neutral facts (an `arm` metadata member mirroring the MIPS one). Mach-O 32-bit ARM slices and PE ARM images keep their existing classification; only ELF ARM carries the metadata. - Admission: little-endian ELF32 `ET_EXEC` with EABI5 is admitted; hard-float (armhf) and unspecified float conventions are covered. ELF64, big-endian `armeb`, `EF_ARM_BE8` mixed-endian images, legacy or older ABIs, `EF_ARM_ABI_FLOAT_SOFT`, PIE (`ET_DYN`) and relocatable images are refused with their specific constraint rather than a guessed profile. - Pinning: the profile pins `-processor ARM:LE:32:v7` with the default compiler spec through a `provider-pinned-v1` selection; the bridge-reported `language_id` is checked against it after import (the DOS `expectedLanguageId` mechanism). A caller-selected `REA_GHIDRA_LANGUAGE_ID`/`REA_GHIDRA_COMPILER_SPEC_ID` still wins; combining the pin with an environment override is a typed refusal. - Evidence: the profile records the lane identity `elf32-exec-le-eabi5-v7-p1`... `elf32-exec-le-eabi5-v7-pinned-v1`, the raw header facts and the declared float ABI basis without interpreting it into a calling-convention claim. Sessions carry the limitation that the ELF float ABI declaration does not change the modeled calling convention. ## Verification Locally verified on Linux with Ghidra 12.1.4 PUBLIC, clang 21.1.8 (Debian) and GNU readelf 2.47; fork PRs currently receive no CI check runs in this repository, so results below are from this machine: - New `npm run verify:ghidra:arm`: builds the source-owned fixture with `arm-linux-gnueabihf` (ARMv7-A, hard float) containing an ARM-mode probe (`mov r0, #42`, `e3a0002a`) and a Thumb-2 interworked probe (`movs r0, #42`, `202a`); checks REA's ELF interpretation against GNU readelf; asserts the pinned language after import; decodes both instruction-set states under `ARM:LE:32:v7`; recovers the direct call edge, pseudocode, global and marker bytes; walks a snapshot round-trip asserting the pinned profile parameters; proves typed MCP refusals for legacy-EABI and BE8 header variants; and closes with CLI/MCP parity and source-byte immutability. - `vitest` `src/ghidra/GhidraArmProfile.test.ts` (admission, refusal matrix, pin read-back, caller-override precedence), `src/domain/binaryTarget.test.ts`, and the Ghidra suite pass; `tsc --noEmit` clean; `oxlint` reports no new warnings on the touched files. ## Author readiness Human owner: @BaaRai - [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. --- <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#4912
No description provided.