[PR #3163] [MERGED] fix(native): derive Mach-O word size and byte order from the selected slice #5652

Closed
opened 2026-10-31 01:08:17 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3163
Author: @kaoru0822-kitauji
Created: 10/14/2026
Status: ✅ Merged
Merged: 10/14/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/macho-header-endian-word-size


📝 Commits (2)

  • 1de72e7 fix(native): derive Mach-O word size and byte order from the selected slice
  • f4a497f test(native): select a 32-bit slice beside a 64-bit slice through inspect_macho

📊 Changes

7 files changed (+84 additions, -23 deletions)

View changed files

📝 src/native/NativeMachoInspection.test.ts (+3 -4)
📝 src/native/NativeMachoInspection.ts (+3 -16)
📝 src/native/parsers/otool.ts (+22 -0)
📝 tests/boundary/providers/native/nativeInventory.golden.test.ts (+1 -1)
📝 tests/boundary/providers/native/nativeMachoHeader.golden.test.ts (+18 -0)
📝 tests/boundary/providers/native/nativeSelectedArchitecture.test.ts (+36 -1)
📝 tests/fixtures/nativeCommands.ts (+1 -1)

📄 Description

Problem

inspect_macho read endian and word_size from file -b, which describes the whole file:

  • macOS file never prints a Mach-O byte order, so endian was null for every target.
  • For a universal binary, word_size matched 64-bit anywhere in the summary (for example [x86_64:Mach-O 64-bit …] [i386] [ppc] [arm64]) rather than describing the selected slice.
  • For a thin 32-bit slice (Mach-O executable i386), file prints no bit width, so word_size was null.

The test fixture's fake file output, Mach-O 64-bit executable arm64, little-endian, contains a byte-order phrase that real macOS file never prints. That is why the unit tests never caught this.

Fix

The selected slice's otool -h -l header, already captured and parsed, now supplies both values:

  • word_size: from the magic (0xfeedface/MH_MAGIC = 32, 0xfeedfacf/MH_MAGIC_64 = 64). Any other magic stays null.
  • endian: from the CPU family with the ABI bits masked. x86 and ARM (including ARM64 and ARM64_32) are little-endian, PowerPC is big-endian, and other families stay null.

inspect_macho no longer runs file, so it launches one fewer process and lists one fewer provenance entry. file stays in the tool allowlist because inspect_plist still uses it. The fixture's file text is now realistic.

Validation

Real data: XLD's universal binary, with thin slices extracted by lipo -thin, run through rea inspect-macho:

slice before (endian, word_size) after
i386 null, null little, 32
x86_64 null, 64 little, 64
arm64 null, 64 little, 64

Provenance tool count went from 8 to 7, and wall time per inspection stayed about 1.0 s (977–994 ms after vs. 1009–1027 ms before). The ppc slice is refused at target admission before and after (unsupported Mach-O architecture), so the big-endian path is covered by the parser test only.

  • New parser test with real otool header values: arm64 and x86_64 (64-bit, little), i386 (32-bit, little), ppc (32-bit, big), an unknown CPU family (null byte order) and an unknown magic (null word size).
  • npm run test:focused on the 26 test files that reference inspect_macho, the otool parser or the native fixtures: 349 passed.
  • npm run check:fast passes.

🤖 Generated with Claude Code


🔄 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/3163 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/14/2026 **Status:** ✅ Merged **Merged:** 10/14/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/macho-header-endian-word-size` --- ### 📝 Commits (2) - [`1de72e7`](https://github.com/morluto/rea/commit/1de72e79984e08b90af2429a9de9c189115839dc) fix(native): derive Mach-O word size and byte order from the selected slice - [`f4a497f`](https://github.com/morluto/rea/commit/f4a497f98a0e12c1d23863cf6ec919708c2c795b) test(native): select a 32-bit slice beside a 64-bit slice through inspect_macho ### 📊 Changes **7 files changed** (+84 additions, -23 deletions) <details> <summary>View changed files</summary> 📝 `src/native/NativeMachoInspection.test.ts` (+3 -4) 📝 `src/native/NativeMachoInspection.ts` (+3 -16) 📝 `src/native/parsers/otool.ts` (+22 -0) 📝 `tests/boundary/providers/native/nativeInventory.golden.test.ts` (+1 -1) 📝 `tests/boundary/providers/native/nativeMachoHeader.golden.test.ts` (+18 -0) 📝 `tests/boundary/providers/native/nativeSelectedArchitecture.test.ts` (+36 -1) 📝 `tests/fixtures/nativeCommands.ts` (+1 -1) </details> ### 📄 Description ## Problem `inspect_macho` read `endian` and `word_size` from `file -b`, which describes the whole file: - macOS `file` never prints a Mach-O byte order, so `endian` was `null` for every target. - For a universal binary, `word_size` matched `64-bit` anywhere in the summary (for example `[x86_64:Mach-O 64-bit …] [i386] [ppc] [arm64]`) rather than describing the selected slice. - For a thin 32-bit slice (`Mach-O executable i386`), `file` prints no bit width, so `word_size` was `null`. The test fixture's fake `file` output, `Mach-O 64-bit executable arm64, little-endian`, contains a byte-order phrase that real macOS `file` never prints. That is why the unit tests never caught this. ## Fix The selected slice's `otool -h -l` header, already captured and parsed, now supplies both values: - `word_size`: from the magic (`0xfeedface`/`MH_MAGIC` = 32, `0xfeedfacf`/`MH_MAGIC_64` = 64). Any other magic stays `null`. - `endian`: from the CPU family with the ABI bits masked. x86 and ARM (including ARM64 and ARM64_32) are little-endian, PowerPC is big-endian, and other families stay `null`. `inspect_macho` no longer runs `file`, so it launches one fewer process and lists one fewer provenance entry. `file` stays in the tool allowlist because `inspect_plist` still uses it. The fixture's `file` text is now realistic. ## Validation Real data: XLD's universal binary, with thin slices extracted by `lipo -thin`, run through `rea inspect-macho`: | slice | before (`endian`, `word_size`) | after | |---|---|---| | i386 | `null`, `null` | `little`, `32` | | x86_64 | `null`, `64` | `little`, `64` | | arm64 | `null`, `64` | `little`, `64` | Provenance tool count went from 8 to 7, and wall time per inspection stayed about 1.0 s (977–994 ms after vs. 1009–1027 ms before). The ppc slice is refused at target admission before and after (`unsupported Mach-O architecture`), so the big-endian path is covered by the parser test only. - New parser test with real `otool` header values: arm64 and x86_64 (64-bit, little), i386 (32-bit, little), ppc (32-bit, big), an unknown CPU family (`null` byte order) and an unknown magic (`null` word size). - `npm run test:focused` on the 26 test files that reference `inspect_macho`, the `otool` parser or the native fixtures: 349 passed. - `npm run check:fast` passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:08:17 +00:00
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#5652
No description provided.