fix(macho): infer legacy Intel simulator platforms

Fixes #6649
This commit is contained in:
morluto
2026-10-23 19:23:38 +08:00
parent 5c7755636d
commit 1280e7e513
2 changed files with 58 additions and 3 deletions
+26 -3
View File
@@ -20,6 +20,7 @@ interface ParsedSegment {
}
interface ParsedLoadCommands {
readonly cpuType: number | null;
readonly commands: Array<{
index: number;
kind: string;
@@ -135,7 +136,7 @@ export const parseOtoolLoadCommands = (raw: string) => {
const header = parseHeader(output);
if (header === undefined)
throw new TypeError("otool output is missing the Mach header");
const state = createLoadCommandState(header.flags);
const state = createLoadCommandState(header.flags, header.cpuType);
for (const block of output.split(/(?=^Load command \d+$)/mu))
parseLoadCommand(block, state);
validateLoadCommandFraming(header, state.commands);
@@ -264,7 +265,9 @@ const validateLoadCommandFraming = (
const createLoadCommandState = (
headerFlags: readonly string[] | undefined,
cpuType: number | null,
): ParsedLoadCommands => ({
cpuType,
commands: [],
segments: [],
dependencies: [],
@@ -403,13 +406,33 @@ const collectBuild = (
});
return;
}
if (kind.startsWith("LC_VERSION_MIN_"))
if (kind.startsWith("LC_VERSION_MIN_")) {
const platform = kind.slice("LC_VERSION_MIN_".length) || null;
state.builds.push({
platform: kind.slice("LC_VERSION_MIN_".length) || null,
platform: legacySimulatorPlatform(platform, state.cpuType),
minimum_os: stringField(fields, "version"),
sdk: stringField(fields, "sdk"),
tools: [],
});
}
};
// Apple dyld interprets legacy iOS/tvOS/watchOS minimum-version commands on
// Intel slices as simulator platforms. Newer commands encode this directly.
const legacySimulatorPlatform = (
platform: string | null,
cpuType: number | null,
): string | null => {
if (![7, 0x01000007].includes(cpuType ?? -1)) return platform;
return (
(
{
IPHONEOS: "IOSSIMULATOR",
TVOS: "TVOSSIMULATOR",
WATCHOS: "WATCHOSSIMULATOR",
} as const
)[platform as "IPHONEOS" | "TVOS" | "WATCHOS"] ?? platform
);
};
const collectDependency = (
@@ -267,3 +267,35 @@ Section
const truncatedBytes = truncatedCount.replace("2 2 48", "2 1 32");
expect(() => parseOtoolLoadCommands(truncatedBytes)).toThrow(/sizes/u);
});
it.each([
["LC_VERSION_MIN_IPHONEOS", "IOSSIMULATOR"],
["LC_VERSION_MIN_TVOS", "TVOSSIMULATOR"],
["LC_VERSION_MIN_WATCHOS", "WATCHOSSIMULATOR"],
])("classifies legacy Intel simulator command %s", (command, platform) => {
const text = [
"Mach header",
" magic cputype cpusubtype caps filetype ncmds sizeofcmds flags",
" 0xfeedfacf 16777223 3 0x00 2 1 16 0x0",
"Load command 0",
` cmd ${command}`,
" cmdsize 16",
" version 12.0",
" sdk 12.1",
].join("\n");
expect(parseOtoolLoadCommands(text).builds[0]?.platform).toBe(platform);
});
it("keeps legacy device platforms on arm64 slices", () => {
const text = [
"Mach header",
" magic cputype cpusubtype caps filetype ncmds sizeofcmds flags",
" 0xfeedfacf 16777228 0 0x00 2 1 16 0x0",
"Load command 0",
" cmd LC_VERSION_MIN_IPHONEOS",
" cmdsize 16",
" version 12.0",
" sdk 12.1",
].join("\n");
expect(parseOtoolLoadCommands(text).builds[0]?.platform).toBe("IPHONEOS");
});