From 2b7bb3bb6a87704c18469768bc82d87e97b5c99c Mon Sep 17 00:00:00 2001 From: Rudy Mizrahi Celekli <47457359+rudycelekli@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:08:33 -0400 Subject: [PATCH] fix(native): inspect the selected universal Mach-O architecture (#601) Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> --- src/native/NativeMachoInspection.ts | 30 ++++++++++++- .../native/nativeSelectedArchitecture.test.ts | 42 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/boundary/providers/native/nativeSelectedArchitecture.test.ts diff --git a/src/native/NativeMachoInspection.ts b/src/native/NativeMachoInspection.ts index e82c61ad3c..29d909b094 100644 --- a/src/native/NativeMachoInspection.ts +++ b/src/native/NativeMachoInspection.ts @@ -54,7 +54,11 @@ export const inspectNativeMacho = async ( for (const [tool, prefix] of REQUIRED_COMMANDS) { const captured = await context.run( tool, - [...prefix, context.target.path], + [ + ...prefix, + ...selectedArchitectureArguments(tool, context.target), + context.target.path, + ], context.signal, ); if (!captured.ok) return err(captured.error); @@ -64,7 +68,11 @@ export const inspectNativeMacho = async ( const [tool, prefix] = OPTIONAL_VTOOL_COMMAND; const optional = await context.run( tool, - [...prefix, context.target.path], + [ + ...prefix, + ...selectedArchitectureArguments(tool, context.target), + context.target.path, + ], context.signal, ); if (optional.ok) captures.push(optional.value); @@ -251,3 +259,21 @@ export const architectureLocations = ( } return locations; }; + +const selectedArchitectureArguments = ( + tool: string, + target: BinaryTarget, +): string[] => { + if ( + target.kind !== "executable" || + target.availableArchitectures.length < 2 || + tool === "file" || + tool === "lipo" + ) + return []; + const architecture = + target.architecture === "x86" ? "i386" : target.architecture; + return tool === "dwarfdump" + ? [`--arch=${architecture}`] + : ["-arch", architecture]; +}; diff --git a/tests/boundary/providers/native/nativeSelectedArchitecture.test.ts b/tests/boundary/providers/native/nativeSelectedArchitecture.test.ts new file mode 100644 index 0000000000..a57b5ba8a3 --- /dev/null +++ b/tests/boundary/providers/native/nativeSelectedArchitecture.test.ts @@ -0,0 +1,42 @@ +import { expect, it } from "vitest"; +import { inspectMachoSchema } from "../../../../src/domain/nativeInspection.js"; +import { NativeMacOSProvider } from "../../../../src/native/NativeMacOSProvider.js"; +import { + NativeFixtureRunner, + nativeFixture, + nativeMachoTarget, +} from "../../../fixtures/nativeCommands.js"; + +it("does not attribute the first universal slice's UUID and segments to the selected target", async () => { + const singleSlice = await nativeFixture("otool-load.txt"); + const selected = new NativeFixtureRunner(); + const universal = new NativeFixtureRunner({ + "otool:-h": `${singleSlice}\n${singleSlice}`, + dwarfdump: + "UUID: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA (x86_64) fixture\nUUID: 01234567-89AB-CDEF-0123-456789ABCDEF (arm64) fixture\n", + }); + const client = new NativeMacOSProvider( + { + async run(tool, arguments_) { + const isSelected = + arguments_.includes("arm64") || arguments_.includes("--arch=arm64"); + return (isSelected ? selected : universal).run(tool, arguments_); + }, + }, + "darwin", + ).createClient(nativeMachoTarget("/owned/universal")); + const result = await client.execute("inspect_macho", {}); + expect(result.ok).toBe(true); + if (!result.ok) return; + expect(result.value.result).toMatchObject({ + uuid: "01234567-89AB-CDEF-0123-456789ABCDEF", + architectures: { total: 2 }, + }); + const normalized = inspectMachoSchema.parse(result.value.result); + expect( + normalized.segments.items.filter((segment) => segment.name === "__TEXT"), + ).toHaveLength(1); + expect(JSON.stringify(normalized)).not.toContain( + "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA", + ); +});