mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
* fix(native): select versioned arm64e slices for codesign by family name Current clang marks arm64e slices with pointer-authentication ABI version 1 (subtype 0x81000002, lipo name arm64e.v1). The codesign selector table accepted only ABI version 0, so inspect-signature failed with capability_unavailable whenever such a slice was selected, for example on any universal arm64e + x86_64 binary built today. codesign has no versioned name: -a arm64e.v1 is "unknown architecture name", and -a arm64e resolves to the first FAT slice with the arm64e base subtype, whatever its ABI version. A selector is therefore exact only for that first slice. Accept any versioned pointer-authentication ABI for arm64e, and return a selector only when the slice is the first in table order to carry that name; a later slice sharing it stays unknown instead of being inspected through the earlier one. This also removes the separate duplicate-name check from signature coverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(native): keep arm64e selectors unknown behind unmapped variants codesign resolves a name to the first FAT slice with the same CPU type and base subtype, so an earlier capability variant whose own name is unknown may shadow a later arm64e slice. Compare identities rather than successful name mappings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
144 lines
4.8 KiB
TypeScript
144 lines
4.8 KiB
TypeScript
import { machoCpuSubtypeBase } from "../domain/apple/machoCpuIdentity.js";
|
|
import type { LipoArchitecture } from "./parsers/lipo.js";
|
|
|
|
interface MachoArchitecturePolicy {
|
|
readonly cpuType: number;
|
|
readonly subtypes: readonly {
|
|
readonly subtype: number;
|
|
readonly name: string;
|
|
}[];
|
|
readonly variantName?: RegExp;
|
|
}
|
|
|
|
/** One preference order for FAT subtype identities and Apple tool slice names. */
|
|
export const MACHO_ARCHITECTURE_POLICIES: Readonly<
|
|
Record<string, MachoArchitecturePolicy>
|
|
> = {
|
|
x86: { cpuType: 7, subtypes: [{ subtype: 3, name: "i386" }] },
|
|
x86_64: {
|
|
cpuType: 0x01000007,
|
|
subtypes: [{ subtype: 3, name: "x86_64" }],
|
|
},
|
|
arm: { cpuType: 12, subtypes: [], variantName: /^arm$/u },
|
|
arm64: {
|
|
cpuType: 0x0100000c,
|
|
subtypes: [
|
|
{ subtype: 0, name: "arm64" },
|
|
{ subtype: 2, name: "arm64e" },
|
|
{ subtype: 1, name: "arm64v8" },
|
|
],
|
|
variantName: /^arm64e\.[A-Za-z0-9_]+$/u,
|
|
},
|
|
arm64_32: {
|
|
cpuType: 0x0200000c,
|
|
subtypes: [{ subtype: 1, name: "arm64_32" }],
|
|
},
|
|
};
|
|
|
|
/** Unlisted subtypes follow the preferred slices in FAT table order. */
|
|
export const machoSubtypeRank = (
|
|
policy: MachoArchitecturePolicy,
|
|
subtype: number,
|
|
): number => {
|
|
const rank = policy.subtypes.findIndex(
|
|
(candidate) => candidate.subtype === machoCpuSubtypeBase(subtype),
|
|
);
|
|
return rank < 0 ? policy.subtypes.length : rank;
|
|
};
|
|
|
|
/** codesign's architecture name for one FAT CPU identity, when known. */
|
|
const codeSignArchitectureName = (
|
|
cpuType: number,
|
|
cpuSubtype: number,
|
|
): string | undefined => {
|
|
const capabilities = (cpuSubtype >>> 24) & 0xff;
|
|
const base = machoCpuSubtypeBase(cpuSubtype);
|
|
// codesign names arm64e by its base subtype whatever its versioned
|
|
// pointer-authentication ABI (CPU_SUBTYPE_PTRAUTH_ABI plus a version), and
|
|
// x86_64 LIB64 as x86_64. Other capability variants may have distinct
|
|
// names; do not collapse them.
|
|
if (
|
|
capabilities !== 0 &&
|
|
!(cpuType === 0x0100000c && base === 2 && (capabilities & 0xf0) === 0x80) &&
|
|
!(cpuType === 0x01000007 && base === 3 && capabilities === 0x80)
|
|
)
|
|
return undefined;
|
|
return Object.values(MACHO_ARCHITECTURE_POLICIES)
|
|
.find((policy) => policy.cpuType === cpuType)
|
|
?.subtypes.find(({ subtype }) => subtype === base)?.name;
|
|
};
|
|
|
|
/**
|
|
* Return the codesign selector that names exactly the FAT slice at `index`.
|
|
* codesign resolves a name to the first slice in table order with that CPU
|
|
* type and base subtype, whatever its capability bits. A later slice sharing
|
|
* them has no exact selector, even behind an earlier variant whose own name
|
|
* is unknown.
|
|
*/
|
|
export const machoKnownCodeSignSelector = (
|
|
slices: readonly { readonly cpuType: number; readonly cpuSubtype: number }[],
|
|
index: number,
|
|
): string | undefined => {
|
|
const slice = slices[index];
|
|
if (slice === undefined) return undefined;
|
|
const base = machoCpuSubtypeBase(slice.cpuSubtype);
|
|
return slices.findIndex(
|
|
(other) =>
|
|
other.cpuType === slice.cpuType &&
|
|
machoCpuSubtypeBase(other.cpuSubtype) === base,
|
|
) === index
|
|
? codeSignArchitectureName(slice.cpuType, slice.cpuSubtype)
|
|
: undefined;
|
|
};
|
|
|
|
/** Keep the selected lipo record, including its container coordinates. */
|
|
export const selectMachoArchitecture = (
|
|
policy: MachoArchitecturePolicy,
|
|
available: readonly LipoArchitecture[],
|
|
): LipoArchitecture | undefined => {
|
|
let selected: LipoArchitecture | undefined;
|
|
let bestRank = Number.POSITIVE_INFINITY;
|
|
for (const architecture of available) {
|
|
if (
|
|
architecture.cpu_type_code !== null &&
|
|
architecture.cpu_type_code !== policy.cpuType
|
|
)
|
|
continue;
|
|
const namedSubtype = policy.subtypes.find(
|
|
({ name }) => name === architecture.name,
|
|
);
|
|
const recognizedName =
|
|
namedSubtype !== undefined || policy.variantName?.test(architecture.name);
|
|
if (
|
|
!recognizedName &&
|
|
(architecture.cpu_type_code === null ||
|
|
architecture.cpu_subtype_base === null)
|
|
)
|
|
continue;
|
|
// Parsed identity is authoritative. Names support thin/partial lipo records.
|
|
const subtype = architecture.cpu_subtype_base ?? namedSubtype?.subtype;
|
|
const rank =
|
|
subtype === undefined
|
|
? policy.subtypes.length
|
|
: machoSubtypeRank(policy, subtype);
|
|
if (rank >= bestRank) continue;
|
|
selected = architecture;
|
|
bestRank = rank;
|
|
}
|
|
return selected;
|
|
};
|
|
|
|
/** lipo may label ARM64 V8 as arm64 even though tools select it as arm64v8. */
|
|
export const machoToolArchitectureName = (
|
|
policy: MachoArchitecturePolicy,
|
|
architecture: LipoArchitecture,
|
|
): string => {
|
|
const named = policy.subtypes.find(({ name }) => name === architecture.name);
|
|
const actual = policy.subtypes.find(
|
|
({ subtype }) => subtype === architecture.cpu_subtype_base,
|
|
);
|
|
return named !== undefined && actual !== undefined && named !== actual
|
|
? actual.name
|
|
: architecture.name;
|
|
};
|