Files
rea/tests/composition/analysis-sessions/powerpc-identity.test.ts
T
Ariel Fikru Avicennaandmorluto c86b09a109 feat(ghidra): admit 32-bit big-endian PowerPC ELF (#1747)
* feat(ghidra): admit 32-bit big-endian PowerPC ELF

Recognize EM_PPC as a provider-neutral `powerpc` family that keeps its ELF
class, byte order, type and flags, and admit ELF32 big-endian ET_EXEC through
a bounded Ghidra lane. ELF64, little-endian, relocatable/shared objects and
EF_PPC_EMB are refused with a specific reason; Hopper and IDA do not gain
PowerPC support, and Windows Ghidra P0 is unchanged.

Ghidra's ELF loader selects PowerPC:BE:32:e500:default for a plain EM_PPC
header, which misreads 750-family code. The lane binds
PowerPC:BE:32:default/default in the profile (language_selection
"admitted-lane-v1"), imports with it and checks the reported language. A
language set through REA_GHIDRA_LANGUAGE_ID still replaces the lane default.

Add a source-owned fixture, fixed-encoding probe, GNU readelf cross-check and
the optional `verify:ghidra:ppc` real-provider lane, plus docs/ghidra-ppc.md.

Refs #1741

* style(ghidra): format shared PowerPC profile integration

* fix(ghidra): preserve caller-selected PowerPC languages

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:35:55 +08:00

94 lines
3.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { BinaryTarget } from "../../../src/domain/binaryTargetTypes.js";
import { createAnalysisProfile } from "../../../src/domain/analysisProfile.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
import {
createAnalysisSnapshotEntry,
parseAnalysisSnapshot,
serializeAnalysisSnapshot,
snapshotBinding,
snapshotTarget,
snapshotMatchesProfile,
} from "../../../src/domain/analysisSnapshot.js";
import { sessionOutputSchemas } from "../../../src/contracts/toolOutputSchemaGroups.js";
const target: BinaryTarget = {
path: "/source-owned.elf",
sha256: "a".repeat(64),
kind: "executable",
format: "elf",
architecture: "powerpc",
availableArchitectures: ["powerpc"],
powerpc: { elfClass: 32, byteOrder: "big", type: 2, flags: 0 },
};
const provider = { id: "ghidra", name: "Ghidra", version: "12.1.4" };
const profile = createAnalysisProfile(provider, {
powerpc_elf: { elf_class: 32, byte_order: "big", type: 2, flags: 0 },
});
describe("PowerPC identity through lifecycle, Evidence and snapshot consumers", () => {
it("round-trips query bindings without relaxing integrity", () => {
const result = [{ address: "0x100101b0", value: "rea_ppc_entry" }];
const parameters = {};
const evidence = createEvidence(target, provider, {
operation: "list_procedures",
parameters,
result,
analysisProfile: profile,
});
const binding = snapshotBinding(profile);
const entry = createAnalysisSnapshotEntry({
target: snapshotTarget(target),
binding,
operation: "list_procedures",
parameters,
execution: {
result,
rawResult: null,
provider,
analysisProfile: profile,
limitations: [],
locations: [],
subject: target,
},
});
const snapshot = {
target: snapshotTarget(target),
binding,
entries: [entry],
workflow_entries: [],
evidence_bundle: createEvidenceBundle([evidence]),
};
const restored = parseAnalysisSnapshot(
JSON.parse(serializeAnalysisSnapshot(snapshot)),
);
expect(restored.target.architecture).toBe("powerpc");
expect(restored.evidence_bundle.records[0]?.subject?.architecture).toBe(
"powerpc",
);
expect(restored.entries[0]?.execution.subject?.architecture).toBe(
"powerpc",
);
expect(
sessionOutputSchemas.open_binary.parse({ result: target }).result
.architecture,
).toBe("powerpc");
expect(() =>
parseEvidence({
...evidence,
subject: { ...evidence.subject, architecture: "x86" },
}),
).toThrow("semantic identifier");
const changed = createAnalysisProfile(provider, {
powerpc_elf: {
elf_class: 32,
byte_order: "big",
type: 2,
flags: 0x00010000,
},
});
expect(snapshotMatchesProfile(binding, changed)).toBe(false);
});
});