Merge pull request #1172 from morluto/fix/macho-arm64e-variant-slices

fix(artifacts): match lipo arm64e variant names to their FAT slices
This commit is contained in:
Kaoru0822 2026-10-08 19:42:05 -07:00 committed by GitHub
commit de5bfec79f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 1 deletions

View File

@ -255,7 +255,7 @@ const lipoMatchesSlice = (
reported.cpu_subtype_code !== null;
return (
(hasPhysicalIdentity || structurallyUnique) &&
reported.name === observed.architecture &&
lipoNameMatches(reported.name, observed.architecture) &&
(reported.file_offset === null ||
reported.file_offset === observed.slice_offset) &&
(reported.size === null || reported.size === observed.slice_size) &&
@ -269,6 +269,15 @@ const lipoMatchesSlice = (
);
};
/**
* Newer lipo names an arm64e ABI variant `arm64e.<variant>` (for example
* `arm64e.v1` for pointer-auth ABI version 1). The FAT table names that slice
* `arm64e`; its physical identity is still compared field by field.
*/
const lipoNameMatches = (reported: string, observed: string): boolean =>
reported === observed ||
(observed === "arm64e" && /^arm64e\.[A-Za-z0-9_]+$/u.test(reported));
const parseSliceKeyInteger = (value: string | undefined): number | null => {
if (value === undefined || !/^\d+$/u.test(value)) return null;
const parsed = Number(value);

View File

@ -186,3 +186,72 @@ const lipoRunner = (stdout: string): NativeCommandRunner => ({
}),
),
});
// Newer lipo names arm64e ABI variants `arm64e.<variant>`; the FAT table names
// the same slice `arm64e`, and its physical identity must still match.
describe("artifact Mach-O arm64e variant slices", () => {
const ARM64E_V1 = { type: 0x0100000c, subtype: 0x81000002 } as const;
const variantSlices = async (
cpu: { readonly type: number; readonly subtype: number },
lipo: (size: number) => string,
) => {
const root = await createTestTempDirectory("rea-slices-arm64e-");
const binary = join(root, "fat");
const x86 = machoImage({ cpu: CPU.x86_64 });
const arm = machoImage({ cpu });
await writeFile(
binary,
fatImage([
{ cpu: CPU.x86_64, bytes: x86 },
{ cpu, bytes: arm },
]),
);
const reader = new MachOSliceArtifactReader(
binary,
lipoRunner(
`architecture x86_64\n cputype CPU_TYPE_X86_64\n cpusubtype CPU_SUBTYPE_X86_64_ALL\n offset 4096\n size ${String(x86.length)}\n align 2^12 (4096)\n${lipo(arm.length)}`,
),
);
const entries = [];
for await (const entry of reader.entries()) entries.push(entry);
return entries.map(({ path, byteOffset }) => [path, byteOffset]);
};
it.each([
[
"arm64e.v1",
" cputype CPU_TYPE_ARM64\n cpusubtype CPU_SUBTYPE_ARM64E\n capabilities PTR_AUTH_VERSION USERSPACE 1",
],
[
"arm64e.x1",
" cputype CPU_TYPE_ARM64\n cpusubtype unknown arm64 cpusubtype",
],
])("accepts lipo's %s name for the FAT arm64e slice", async (name, cpu) => {
expect(
await variantSlices(
ARM64E_V1,
(size) =>
`architecture ${name}\n${cpu}\n offset 8192\n size ${String(size)}\n align 2^12 (4096)\n`,
),
).toEqual([
["slices/x86_64", 4096],
[`slices/${name}`, 8192],
]);
});
it.each([
["a plain arm64 slice", CPU.arm64, "8192"],
["a different offset", ARM64E_V1, "8193"],
])(
"still rejects an arm64e variant name for %s",
async (_label, cpu, offset) => {
await expect(
variantSlices(
cpu,
(size) =>
`architecture arm64e.v1\n cputype CPU_TYPE_ARM64\n cpusubtype unknown arm64 cpusubtype\n offset ${offset}\n size ${String(size)}\n align 2^12 (4096)\n`,
),
).rejects.toMatchObject({ reason: "integrity" });
},
);
});