* 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>
94 lines
3.0 KiB
TypeScript
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);
|
|
});
|
|
});
|