mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(native): preserve slice identity when selecting UUID rows
Carry the selected slice name and the existing tool owner's dwarfdump spelling separately. Compare observed row labels exactly, preserving explicit ABI versions and uncertainty for missing rows. Correct the production-boundary fixture and retain wrong-version and mixed-row controls. Real Apple multi-version selection behavior remains unverified. Refs: #4195, #4288 Co-authored-by: Henglie <ebhenglie@gmail.com>
This commit is contained in:
1 parent
5f0d18a534
commit
2f6b9ecdc6
3 files changed
+97
-10
No files matched your search
@@ -237,7 +237,15 @@ const normalizeMacho = (
|
||||
load.commands,
|
||||
dwarfdump.stdout,
|
||||
artifactPath,
|
||||
selectedArchitecture.name,
|
||||
selectedArchitecture.name === null
|
||||
? null
|
||||
: {
|
||||
name: selectedArchitecture.name,
|
||||
dwarfdumpName: toolArchitectureName(
|
||||
"dwarfdump",
|
||||
selectedArchitecture.name,
|
||||
),
|
||||
},
|
||||
);
|
||||
const provenance = captures.map(toInvocation);
|
||||
// A thread-state command carries the entrypoint in architecture-specific
|
||||
|
||||
@@ -7,12 +7,17 @@ type UuidObservation =
|
||||
| { readonly kind: "valid"; readonly value: string }
|
||||
| { readonly kind: "invalid" | "conflict"; readonly reason: string };
|
||||
|
||||
interface UuidArchitectureSelection {
|
||||
readonly name: string;
|
||||
readonly dwarfdumpName: string;
|
||||
}
|
||||
|
||||
/** Reconcile the selected slice's UUID observations from otool and dwarfdump. */
|
||||
export const reconcileMachoUuid = (
|
||||
commands: ReturnType<typeof parseOtoolLoadCommands>["commands"],
|
||||
dwarfdumpOutput: string,
|
||||
artifactPath: string,
|
||||
selectedArchitecture: string | null,
|
||||
selectedArchitecture: UuidArchitectureSelection | null,
|
||||
): {
|
||||
readonly value: string | null;
|
||||
readonly limitations: readonly string[];
|
||||
@@ -25,7 +30,7 @@ export const reconcileMachoUuid = (
|
||||
const parseDwarfdumpUuid = (
|
||||
output: string,
|
||||
artifactPath: string,
|
||||
selectedArchitecture: string | null,
|
||||
selectedArchitecture: UuidArchitectureSelection | null,
|
||||
): UuidObservation => {
|
||||
const parsed = parseDwarfdumpRows(output, artifactPath);
|
||||
return parsed.kind === "invalid"
|
||||
@@ -94,14 +99,15 @@ const parseDwarfdumpRows = (
|
||||
|
||||
const selectDwarfdumpRow = (
|
||||
rows: readonly DwarfdumpUuidRow[],
|
||||
selectedArchitecture: string | null,
|
||||
selectedArchitecture: UuidArchitectureSelection | null,
|
||||
): UuidObservation => {
|
||||
if (rows.length === 0) return { kind: "absent" };
|
||||
const selectedRows =
|
||||
selectedArchitecture === null
|
||||
? rows
|
||||
: rows.filter(
|
||||
({ architecture }) => architecture === selectedArchitecture,
|
||||
({ architecture }) =>
|
||||
architecture === selectedArchitecture.dwarfdumpName,
|
||||
);
|
||||
const validRows = selectedRows.filter(({ value }) => MACHO_UUID.test(value));
|
||||
const distinctValid = new Set(
|
||||
@@ -110,15 +116,15 @@ const selectDwarfdumpRow = (
|
||||
if (distinctValid.size > 1)
|
||||
return {
|
||||
kind: "conflict",
|
||||
reason: `dwarfdump --uuid returned conflicting UUIDs for selected architecture ${selectedArchitecture ?? "unknown"}`,
|
||||
reason: `dwarfdump --uuid returned conflicting UUIDs for selected architecture ${selectedArchitecture?.name ?? "unknown"}`,
|
||||
};
|
||||
if (selectedRows.length !== 1)
|
||||
return {
|
||||
kind: "invalid",
|
||||
reason:
|
||||
selectedRows.length > 1
|
||||
? `dwarfdump --uuid returned multiple rows for selected architecture ${selectedArchitecture}`
|
||||
: `dwarfdump --uuid returned no row for selected architecture ${selectedArchitecture}`,
|
||||
? `dwarfdump --uuid returned multiple rows for selected architecture ${selectedArchitecture?.name ?? "unknown"}`
|
||||
: `dwarfdump --uuid returned no row for selected architecture ${selectedArchitecture?.name ?? "unknown"}`,
|
||||
};
|
||||
const [row] = selectedRows;
|
||||
if (row === undefined) return { kind: "absent" };
|
||||
|
||||
@@ -85,31 +85,41 @@ it.each([
|
||||
architectures: ["x86_64", "arm64e", "arm64e.x1"],
|
||||
selected: "arm64e",
|
||||
dyldArchitecture: "arm64e",
|
||||
dwarfdumpArchitecture: "arm64e",
|
||||
},
|
||||
{
|
||||
architectures: ["x86_64", "arm64e.v1"],
|
||||
selected: "arm64e",
|
||||
dyldArchitecture: "arm64e.v1",
|
||||
dwarfdumpArchitecture: "arm64e",
|
||||
},
|
||||
{
|
||||
architectures: ["arm64v8", "arm64e.v1"],
|
||||
selected: "arm64e",
|
||||
dyldArchitecture: "arm64e.v1",
|
||||
dwarfdumpArchitecture: "arm64e",
|
||||
},
|
||||
{
|
||||
architectures: ["arm64e.v1", "arm64v8"],
|
||||
selected: "arm64e",
|
||||
dyldArchitecture: "arm64e.v1",
|
||||
dwarfdumpArchitecture: "arm64e",
|
||||
},
|
||||
// arm64e.x1 is its own subtype: otool and nm cannot reach it as arm64e.
|
||||
{
|
||||
architectures: ["x86_64", "arm64e.x1"],
|
||||
selected: "arm64e.x1",
|
||||
dyldArchitecture: "arm64e.x1",
|
||||
dwarfdumpArchitecture: "arm64e.x1",
|
||||
},
|
||||
] as const)(
|
||||
"selects the $dyldArchitecture slice of a universal arm64e target",
|
||||
async ({ architectures, selected, dyldArchitecture }) => {
|
||||
async ({
|
||||
architectures,
|
||||
selected,
|
||||
dyldArchitecture,
|
||||
dwarfdumpArchitecture,
|
||||
}) => {
|
||||
const calls: {
|
||||
readonly tool: string;
|
||||
readonly arguments_: readonly string[];
|
||||
@@ -133,7 +143,7 @@ it.each([
|
||||
.join("\n");
|
||||
const fixtures = new NativeFixtureRunner({
|
||||
lipo,
|
||||
dwarfdump: `UUID: 01234567-89AB-CDEF-0123-456789ABCDEF (${dyldArchitecture}) /owned/universal\n`,
|
||||
dwarfdump: `UUID: 01234567-89AB-CDEF-0123-456789ABCDEF (${dwarfdumpArchitecture}) /owned/universal\n`,
|
||||
});
|
||||
const client = new NativeMacOSProvider(
|
||||
{},
|
||||
@@ -160,6 +170,14 @@ it.each([
|
||||
.filter(({ tool }) => tool === "dyld_info")
|
||||
.every(({ arguments_ }) => arguments_.includes(dyldArchitecture)),
|
||||
).toBe(true);
|
||||
expect(
|
||||
calls.find(({ tool }) => tool === "dwarfdump")?.arguments_,
|
||||
).toContain(`--arch=${dwarfdumpArchitecture}`);
|
||||
expect(
|
||||
result.value.limitations.some((limitation) =>
|
||||
limitation.includes("dwarfdump"),
|
||||
),
|
||||
).toBe(false);
|
||||
// vtool has no spelling for a variant subtype, so it is skipped rather
|
||||
// than pointed at another slice.
|
||||
const variant = selected.includes(".");
|
||||
@@ -172,6 +190,61 @@ it.each([
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{
|
||||
description: "a row explicitly naming another ABI version",
|
||||
rows: "UUID: FEDCBA98-7654-3210-FEDC-BA9876543210 (arm64e.v2) /owned/universal\n",
|
||||
uuid: "01234567-89AB-CDEF-0123-456789ABCDEF",
|
||||
limitations: [
|
||||
"dwarfdump --uuid returned no row for selected architecture arm64e.v1; using the unambiguous otool LC_UUID.",
|
||||
],
|
||||
},
|
||||
{
|
||||
description: "a matching family row alongside another ABI version",
|
||||
rows: "UUID: 01234567-89ab-cdef-0123-456789abcdef (arm64e) /owned/universal\nUUID: FEDCBA98-7654-3210-FEDC-BA9876543210 (arm64e.v2) /owned/universal\n",
|
||||
uuid: "01234567-89ab-cdef-0123-456789abcdef",
|
||||
limitations: [],
|
||||
},
|
||||
])(
|
||||
"preserves selected slice identity with $description",
|
||||
async ({ rows, uuid, limitations }) => {
|
||||
const calls: {
|
||||
readonly tool: string;
|
||||
readonly arguments_: readonly string[];
|
||||
}[] = [];
|
||||
const fixtures = new NativeFixtureRunner({
|
||||
lipo: `architecture arm64e.v1
|
||||
cputype ${CPU.arm64.type}
|
||||
cpusubtype CPU_SUBTYPE_ARM64E
|
||||
capabilities PTR_AUTH_VERSION USERSPACE 1
|
||||
offset 16384
|
||||
size 8192
|
||||
align 2^14 (16384)`,
|
||||
dwarfdump: rows,
|
||||
});
|
||||
const client = new NativeMacOSProvider(
|
||||
{},
|
||||
sliceSelectingRunner(fixtures, "arm64e", "arm64e.v1", calls),
|
||||
"darwin",
|
||||
).createClient(nativeMachoTarget("/owned/universal"));
|
||||
const result = await client.execute("inspect_macho", {});
|
||||
if (!result.ok) throw result.error;
|
||||
expect(result.value.result).toMatchObject({
|
||||
uuid,
|
||||
selected_architecture: {
|
||||
name: "arm64e.v1",
|
||||
container_file_offset: 16384,
|
||||
},
|
||||
});
|
||||
expect(
|
||||
result.value.limitations.filter((value) => value.includes("dwarfdump")),
|
||||
).toEqual(limitations);
|
||||
expect(
|
||||
calls.find(({ tool }) => tool === "dwarfdump")?.arguments_,
|
||||
).toContain("--arch=arm64e");
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
["ordinary", "/owned/universal", "\n"],
|
||||
["LF Non-fat text", "/owned/\nNon-fat file: forged", "\n"],
|
||||
|
||||
Reference in new issue
Block a user