Repository navigation
Conversation
inspect_native_initializers, inspect_go_binary and inspect_rust_binary matched universal Mach-O slices by CPU type only and refused files whose arm64 and arm64e slices share a CPU type, such as /usr/bin/file. Move the subtype-preference choice that inspect_macho already uses into selectPreferredMachoSlice and use it from both selectors, so arm64 resolves to one slice. Slices that share a base subtype are still refused. Fixes morluto#9792
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8e9cc0914
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42baa4317b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
When a universal Mach-O holds several slices of the selected CPU family, the metadata readers now name the slices they did not read in limitations. Correct the guides' description of the preference order, add selector unit cases for x86_64h and 32-bit ARM ordering, and mention the preference in the architecture schema.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 77b203d6eb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Qualify the remaining refusal as a duplicated winning subtype, state that unlisted subtypes follow the listed ones, and say the limitation names the selected slice rather than a preferred one.
Summary
Fixes #9792.
Stock macOS tools such as
/usr/bin/fileship three slices:x86_64,arm64andarm64e.inspect_native_initializershad no input that reached either arm64 slice.--architecture arm64was refused as "several arm64 slices", andarm64eisn't an accepted value.inspect_machoreads the same file fine because it picks one slice by subtype preference.After this change, the metadata readers resolve
arm64by the subtype preference orderinspect_machoalso uses (arm64, thenarm64e, thenarm64v8). On the stock tools below, they now read the same sliceinspect_machoreads. When other slices of that family go unread,limitationsnames them ("Only the selected arm64 slice ... was read"), so the result doesn't read as covering the whole family.Problem and expected behavior
Smallest trigger:
rea inspect-native-initializers /usr/bin/file --architecture arm64. The broken invariant: anarchitecturerefusal should name an input that works, and one universal file should resolve to the same slice across REA tools.selectNativeImage(src/native/metadata/MachoSliceSelection.ts) matched slices by CPU type only and refused when more than one matched.selectMachoSlice(src/native/AppleMachoSelection.ts) already ranked same-family slices byMACHO_ARCHITECTURE_POLICIES. That gave the repo two different rules for one decision.Change and scope
selectPreferredMachoSliceinsrc/native/MachoArchitectureSelection.tsnow owns the choice. It applies the existing per-family subtype order. Equal ranks keep FAT table order. The choice is ambiguous only when the winning base subtype (capability bits cleared) appears more than once, so a file with onearm64slice and twoarm64eslices still selectsarm64. The loop is moved fromselectMachoSlice, not rewritten.selectMachoSliceandselectNativeImageboth call it.selectMachoSlicenow validates every FAT declaration before picking instead of interleaving the two. Every slice was already validated before it returned, so its results and errors are unchanged.machoSubtypeRankis no longer exported because nothing outside the module uses it.arm64(0x0) andarm64e(0x2) slices used to be refused as ambiguous. It now selects thearm64slice. A duplicated winning subtype, such as twoarm64subtype 0 slices, is still refused with the same message. feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers #6219 targeted Go and Rust binaries, which don't ship arm64e. The refusal started hitting stock macOS binaries onceinspect_native_initializersadopted the same selector.armslices with different subtypes, such asarmv7andarmv7s, now resolve to the first one in table order instead of being refused.inspect_machoalready behaves this way. Its lipo-basedselectMachoArchitecturekeeps the first equal-rank record, andselectArchitecturetreats the choice as ambiguous only when another record has the same tool name after a.vNsuffix is stripped.selectNativeImagealso returns alimitationsline naming the same-family slices it skipped, with their subtypes and file offsets.inspect_native_initializers,inspect_go_binaryandinspect_rust_binaryappend it to their ownlimitations. Files with one slice per family get no new line.docs/guides/native/go-binaries.md,docs/rust-binaries.mdanddocs/guides/native/native-initializers.mdnow describe the preference order and the remaining refusal of a duplicated preferred subtype. Thearchitectureschema description also mentions both.arm64eslice of a file that also holdsarm64. That needs a new selector value. I asked about it on [Bug] inspect-native-initializers can't read either arm64 slice of a universal Mach-O that holds both arm64 and arm64e #9792 and kept it out of this PR.selectImage(src/il2cpp/Il2cppBinaryImage.ts) still refuses distinct ARM subtypes that both map toarm64.docs/il2cpp-metadata.mddocuments that refusal, and the selector has its own public vocabulary (amd64) and failure codes. feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers #6219 also left it as a follow-up, so this PR doesn't touch it. Moving it ontoselectPreferredMachoSlicewould need the name mapping and the docs updated together. Separately,inspect_machoranks lipo records (names plus parsed subtypes) inselectMachoArchitectureand judges ambiguity by tool name inNativeMachoInspection.selectArchitecture, rather than reading FAT declarations. It applies the sameMACHO_ARCHITECTURE_POLICIESorder, but it works on a different input representation, so this PR leaves it as is.arm64subtype-0 slices) are still refused onarchitecturewith the existing message, as before this PR.Contribution scope: not applicable. This is an ordinary fix inside existing boundaries.
Contract and boundary impact
selectPreferredMachoSlice(MachoArchitectureSelection.ts) chooses the slice. Consumers areselectNativeImage(inspect_native_initializers,inspect_go_binary,inspect_rust_binary) andselectMachoSliceviaparseMachoLayout. Of theparseMachoLayoutcallers, only dispatch metadata passes a universal file with an architecture. The Mach-O initializer parser and the IL2CPP layout reader receive slices that are already thinned, so their selection doesn't change. The duplicated selection loop inAppleMachoSelection.tswas removed.architecturedescription gains one sentence. Successful results on files with several same-family slices gain onelimitationsentry. Calls that used to fail with anarchitecturerefusal on files with several slices of one family now succeed.cpu_subtype, offset and container table digest are already reported.Expected effects:
Evidence and regression coverage
Tests added or updated:
tests/boundary/mcp/nativeInitializersMcp.test.ts: anx86_64+arm64+arm64euniversal file (the/usr/bin/filelayout) built from the existing arm64 initializer fixture.architecture: "arm64"reads the subtype-0 slice at its file offset.tests/boundary/mcp/universalSliceMcp.test.ts: witharm64elisted beforearm64in the FAT table,inspect_go_binarystill selects thearm64slice. Two subtype-0 slices are still refused with the existing "several arm64 slices" message. Thearm64/arm64erefusal assertion was removed.limitationsline naming the unreadarm64eslice and its offset.src/native/MachoArchitectureSelection.test.ts(new):x86_64is preferred overx86_64hin both table orders, 32-bitarmv7s/armv7keeps FAT table order, a duplicated winning subtype is ambiguous (including when only capability bits differ), onearm64slice plus twoarm64eslices still selectsarm64, and an unknown family is absent.src/reverted, 2 tests fail and the rest pass.Base reproduction (built CLI,
mainat55b6d2ca2, macOS 26.5.2 arm64):/usr/bin/ktrace,/usr/bin/traceand/usr/bin/stzgave the sameinvalid_request.User-visible CLI output after this change (built from this branch, same host).
inspect-native-initializers <binary> --architecture <arch>, showingslice.file_offset,slice.cpu_subtype,coverage.statusandcoverage.total_entries:The
/usr/bin/fileresult now ends with this limitation:/usr/bin/file --architecture arm64now reads offset 180224, the same sliceinspect-machoselects. Two-slice files (/bin/ls, TextEdit, ShortcutDroplet) resolve to the same slice as before.Remaining proof gaps: Go and Rust are covered by fixtures only. I had no real Go or Rust universal binary that carries both
arm64andarm64e. 32-bitarmmulti-subtype ordering is covered by a selector unit test, not by a real binary. Linux and Windows hosts were not run locally.Boundary regressions covered: malformed and truncated FAT input (existing damaged-slice and CPU-disagreement cases still pass), plus target identity (selected slice offset and subtype). No lifecycle stages are involved.
For evidence-bearing changes:
Validation performed
npm run check:fast: passed (typecheck and lint, after splitting the new Go case into its own test to stay undermax-lines-per-function).npm run test:focused -- <48 test files that reference the Mach-O slice selectors,parseMachoLayout, dispatch metadata, universal fixtures, initializers, the Go/Rust providers, or the architecture schema, plus tests/acceptance/tools/localToolContracts.test.ts and tests/boundary/mcp/serverIdentity.test.ts>: 49 files, 856 tests passed (on the final head).npm run docs:check: passed.npm run test:focused -- tests/boundary/mcp/universalSliceMcp.test.ts tests/boundary/mcp/nativeInitializersMcp.test.tswithsrc/reverted to base: 2 failed (the new cases), 5 passed.npm run build:cached, then the CLI runs shown above on macOS 26.5.2 arm64, Node 24.22.1.Compatibility, safety, and release
Author readiness
Human owner: @chordpli
Before merge
type(scope): outcome.