fix(elf): reuse recognized machine architecture facts

Preserve the shared EM_HEXAGON classification in Go and Rust image reports.

Fixes #6629
This commit is contained in:
morluto
2026-10-23 18:45:44 +08:00
parent 48cd33af18
commit ef05713d86
4 changed files with 142 additions and 13 deletions
+24 -13
View File
@@ -8,6 +8,7 @@ import {
type LoadRegion,
type ZeroFillMapping,
} from "./BinaryContainer.js";
import { elfArchitecture } from "../../domain/elfFixedHeader.js";
import { readNativeDirectoryFromBuffer } from "./NativeSectionDirectory.js";
/** Read ELF sections and file-backed program mappings without interpreting language metadata. */
@@ -105,17 +106,27 @@ export const readElfImage = (bytes: Buffer): BinaryContainer => {
/** Map ELF machine identifiers to stable architecture names. */
export const architectureForElfMachine = (machine: number): string => {
const names = new Map([
[3, "386"],
[8, "mips"],
[20, "ppc"],
[21, "ppc64"],
[22, "s390"],
[40, "arm"],
[62, "amd64"],
[183, "arm64"],
[243, "riscv"],
[258, "loongarch"],
]);
return names.get(machine) ?? `elf-machine-${machine}`;
const architecture = elfArchitecture(machine);
if (architecture !== undefined) {
switch (architecture) {
case "x86":
return "386";
case "x86_64":
return "amd64";
case "powerpc":
return "ppc";
default:
return architecture;
}
}
switch (machine) {
case 21:
return "ppc64";
case 22:
return "s390";
case 258:
return "loongarch";
default:
return `elf-machine-${machine}`;
}
};
@@ -0,0 +1,92 @@
import { goBinarySchema } from "../../src/domain/go/goBinary.js";
import { rustBinarySchema } from "../../src/domain/rust/rustBinary.js";
import { expect } from "vitest";
import { toolContract } from "../../src/contracts/toolContracts.js";
import { createGoBinaryFixture } from "../fixtures/go/image.js";
import { createRustImage } from "../fixtures/rust/image.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { join } from "node:path";
import { writeFile } from "node:fs/promises";
import { cliTest } from "../support/cli/cliFixture.js";
cliTest(
"preserves classifier-known Hexagon ELF identity through Go and Rust CLI/MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-elf-machine-identity-");
const { client, call } = await connectLocalToolsMcp();
for (const language of ["go", "rust"] as const) {
const operation =
language === "go" ? "inspect_go_binary" : "inspect_rust_binary";
const command =
language === "go" ? "inspect-go-binary" : "inspect-rust-binary";
for (const item of [
{ machine: 164, architecture: "hexagon", subject: "hexagon" },
{ machine: 62, architecture: "amd64", subject: "x86_64" },
]) {
const bytes =
language === "go"
? createGoBinaryFixture({ omitBuildInfo: true }).bytes
: createRustImage({
format: "elf",
omitSection: true,
includeRustc: true,
});
bytes.writeUInt16LE(item.machine, 18);
const path = join(root, `${language}-${item.machine}.elf`);
await writeFile(path, bytes);
const cliResponse = await cli.run({
arguments: [command, path, "--json"],
});
expect(
cliResponse.exitCode,
`${cliResponse.stderr}\n${JSON.stringify(cliResponse.json)}`,
).toBe(0);
const cliEvidence = toolContract(operation).outputSchema.parse(
cliResponse.json,
);
const cliReport =
language === "go"
? goBinarySchema.parse(cliEvidence.normalized_result)
: rustBinarySchema.parse(cliEvidence.normalized_result);
expect(cliReport.architecture).toBe(item.architecture);
expect(cliEvidence.subject?.architecture).toBe(item.subject);
const mcpResponse = await call(operation, { path });
expect(mcpResponse.isError, JSON.stringify(mcpResponse)).not.toBe(true);
const mcpEvidence = toolContract(operation).outputSchema.parse(
mcpResponse.structuredContent,
);
const mcpReport =
language === "go"
? goBinarySchema.parse(mcpEvidence.normalized_result)
: rustBinarySchema.parse(mcpEvidence.normalized_result);
expect(mcpReport.architecture).toBe(item.architecture);
expect(mcpEvidence.subject?.architecture).toBe(item.subject);
}
const malformed =
language === "go"
? createGoBinaryFixture({ omitBuildInfo: true }).bytes
: createRustImage({
format: "elf",
omitSection: true,
includeRustc: true,
});
malformed[0] = 0;
const path = join(root, `${language}-malformed.elf`);
await writeFile(path, malformed);
const cliFailure = await cli.run({
arguments: [command, path, "--json"],
});
expect(cliFailure.exitCode).not.toBe(0);
expect(cliFailure.json).not.toHaveProperty("normalized_result");
const mcpFailure = await call(operation, { path });
expect(mcpFailure.isError).toBe(true);
expect(mcpFailure.structuredContent).toBeUndefined();
}
await client.ping();
},
30000,
);
+10
View File
@@ -17,6 +17,16 @@ import {
GO_MODULE_TEXT,
} from "../../fixtures/go/image.js";
it("uses the shared ELF classifier for Go image architecture", () => {
const fixture = createGoBinaryFixture({ omitBuildInfo: true });
fixture.bytes.writeUInt16LE(164, 18); // ELF EM_HEXAGON.
expect(readGoBinaryImage(fixture.bytes)).toMatchObject({
format: "elf",
architecture: "hexagon",
build_info: null,
});
});
it("preserves exact Mach-O protection masks for Go fallback selection", () => {
const { bytes } = createGoBinaryFixture({
format: "macho",
@@ -19,6 +19,7 @@ const packages = [
source: "crates.io",
kind: null,
dependencies: [],
features: [],
root: false,
},
{
@@ -27,6 +28,7 @@ const packages = [
source: "local",
kind: null,
dependencies: [0],
features: [],
root: true,
},
];
@@ -87,6 +89,20 @@ it("preserves the arm64_32 Mach-O CPU family when dependency metadata is absent"
});
});
it("uses the shared ELF classifier for Rust image architecture", () => {
const bytes = createRustImage({
format: "elf",
omitSection: true,
includeRustc: true,
});
bytes.writeUInt16LE(164, 18); // ELF EM_HEXAGON.
expect(readRustBinaryImage(bytes)).toMatchObject({
format: "elf",
architecture: "hexagon",
auditable_metadata: null,
});
});
it("ignores PE file-alignment padding after the dependency content", () => {
const bytes = createRustImage({ format: "pe", includeRustc: false });
const payloadBytes = rustDependencyPayload().length;