mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
6b8f4f7528
* fix(native): admit universal Mach-O files without a host slice FAT Mach-O admission selected only the host's slice and rejected a valid container whose slices are all foreign, so list-architectures returned target_unavailable instead of the container's inventory. A thin foreign image was already admitted. Prefer the host's slice and otherwise select the first declared supported slice. Remove the Ghidra installation probe's re-parse under other host architectures, which existed only to work around the rejection. Fixes #5404 * fix(native): select the first valid foreign FAT slice Without a host slice, FAT selection took the first declared slice even when it was too short for its Mach-O header, rejecting a container whose later foreign slice is valid. The Ghidra decompiler probe then reported such a foreign-only decompiler as unknown rather than incompatible. Select the first foreign slice that can hold its header; a malformed host slice still fails. * fix(native): select foreign FAT slices from every declaration Foreign-slice fallback chose among architecture families after keeping only each family's first declaration, so an undersized first arm64 subtype hid a valid later arm64 subtype. Select from all declarations, and report the deduplicated families separately. The host-slice rule is unchanged. Move the FAT selection tests into their own block. * fix(native): admit only unambiguous foreign FAT families With no host slice, a family declared more than once, such as arm64 and arm64e, has no provider-neutral choice: IDA analyzes the recorded slice while Hopper prefers its own subtype. Select only families declared exactly once and keep ambiguous foreign-only containers rejected, as on main. * fix(ghidra): decide FAT decompiler compatibility from validated families Slice selection now refuses a foreign-only FAT table that declares one family more than once, such as arm64 and arm64e, and the decompiler probe reported that refusal as unknown, so an x64 host accepted an arm64-only decompiler. When selection fails, read the table's validated CPU families and report the decompiler incompatible when they exclude the host. Selection and host exclusion now have separate owners in binaryTarget.ts.
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
|
|
import { expect } from "vitest";
|
|
import { z } from "zod";
|
|
|
|
import { fat } from "../../../src/domain/binaryTarget.fixture.js";
|
|
import { listArchitecturesSchema } from "../../../src/domain/native/nativeInspection.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
const environment = { REA_LOG_LEVEL: "silent" } as const;
|
|
|
|
// The one slice of each container below is never the host's architecture.
|
|
const foreign =
|
|
process.arch === "arm64"
|
|
? { name: "x86_64", cpuType: 0x01000007 }
|
|
: { name: "arm64", cpuType: 0x0100000c };
|
|
|
|
cliTest.skipIf(process.platform !== "darwin")(
|
|
"inventories a universal Mach-O whose only slice is foreign to the host",
|
|
async ({ cli, processes }) => {
|
|
const directory = await createTestTempDirectory("rea-foreign-fat-cli-");
|
|
const thin = join(directory, "thin");
|
|
const universal = join(directory, "universal");
|
|
const compiled = await processes.run(
|
|
"/usr/bin/xcrun",
|
|
[
|
|
"clang",
|
|
"-arch",
|
|
foreign.name,
|
|
resolve("tests/conformance/c/fixture.c"),
|
|
"-o",
|
|
thin,
|
|
],
|
|
{ timeoutMs: 30_000 },
|
|
);
|
|
expect(compiled.exitCode, compiled.stderr).toBe(0);
|
|
const created = await processes.run(
|
|
"/usr/bin/xcrun",
|
|
["lipo", "-create", thin, "-output", universal],
|
|
{ timeoutMs: 30_000 },
|
|
);
|
|
expect(created.exitCode, created.stderr).toBe(0);
|
|
|
|
const result = await cli.run({
|
|
arguments: ["list-architectures", universal, "--json"],
|
|
environment,
|
|
});
|
|
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
const evidence = z
|
|
.object({
|
|
subject: z.object({ format: z.string() }),
|
|
normalized_result: listArchitecturesSchema,
|
|
})
|
|
.parse(result.json);
|
|
expect(evidence.subject.format).toBe("mach-o-universal");
|
|
expect(evidence.normalized_result.architectures).toMatchObject({
|
|
total: 1,
|
|
exhaustive: true,
|
|
items: [{ name: foreign.name }],
|
|
});
|
|
expect(
|
|
evidence.normalized_result.architectures.items[0]?.file_offset,
|
|
).toBeGreaterThan(0);
|
|
},
|
|
60_000,
|
|
);
|
|
|
|
cliTest.skipIf(process.platform === "darwin")(
|
|
"admits a universal Mach-O whose only slice is foreign to the host",
|
|
async ({ cli }) => {
|
|
const directory = await createTestTempDirectory("rea-foreign-fat-cli-");
|
|
const universal = join(directory, "universal");
|
|
await writeFile(universal, fat([foreign.cpuType]));
|
|
|
|
const result = await cli.run({
|
|
arguments: ["list-architectures", universal, "--json"],
|
|
environment,
|
|
});
|
|
|
|
// The target is admitted; only the macOS-only lipo inventory is refused.
|
|
expect(result.exitCode).not.toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
code: "capability_unavailable",
|
|
details: { provider_id: "native-macos", operation: "list_architectures" },
|
|
});
|
|
},
|
|
);
|