mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(ghidra): verify dense jump tables on an optimized fixture (#1374)
The dense jump-table oracles (inspect-native-api CLI and the analyze_function dossier) ran against the -O0 debug fixture, which assumes the host compiler lowers dense switches to tables at -O0. GCC 16 and later expand them as compare chains instead, so on those hosts the lane rejected a correct provider and Ghidra build with empty jump_tables. Build the same inventory source once more at -O2 as a dedicated jump-table variant, run both strict dense oracles there, and keep the debug dossier at its complete generic shape. The dedicated switch lane already compiles at -O2 for the same reason.
This commit is contained in:
1 parent
355a292198
commit
73a664bc78
2 files changed
+53
-20
No files matched your search
@@ -153,14 +153,15 @@ export async function verifyDebugFunctionOperations(
|
||||
const denseSwitchDossier = await functionCall(client, "analyze_function", {
|
||||
procedure: denseSwitchProcedure.address,
|
||||
});
|
||||
if (process.platform === "linux" && process.arch === "x64")
|
||||
assertDenseSwitchDossier(denseSwitchDossier, denseSwitchProcedure.address);
|
||||
else
|
||||
assertDossier(denseSwitchDossier, {
|
||||
address: denseSwitchProcedure.address,
|
||||
requireAssembly: true,
|
||||
requireMultiBlock: true,
|
||||
});
|
||||
// The debug fixture is compiled -O0, and GCC 16 and later lower dense
|
||||
// switches to compare chains at -O0, so this dossier asserts only its
|
||||
// complete shape; the optimized jump-table variant proves dense jump-table
|
||||
// completeness through assertDenseSwitchDossier.
|
||||
assertDossier(denseSwitchDossier, {
|
||||
address: denseSwitchProcedure.address,
|
||||
requireAssembly: true,
|
||||
requireMultiBlock: true,
|
||||
});
|
||||
assertIndirectDossier(indirectDossier, leaf.address);
|
||||
|
||||
const { cancellation, timeout } = await verifyRequestCancellationAndTimeout(
|
||||
@@ -372,6 +373,19 @@ async function verifyConcurrentRequests(client, entry, leaf) {
|
||||
return "serialized-two-results";
|
||||
}
|
||||
|
||||
/** Prove dense jump-table completeness on the optimized jump-table fixture. */
|
||||
export async function verifyJumpTableDenseSwitch(client, procedures) {
|
||||
const denseSwitchProcedure = requireProcedure(
|
||||
procedures,
|
||||
"rea_ghidra_inventory_dense_switch",
|
||||
);
|
||||
const dossier = await functionCall(client, "analyze_function", {
|
||||
procedure: denseSwitchProcedure.address,
|
||||
});
|
||||
assertDenseSwitchDossier(dossier, denseSwitchProcedure.address);
|
||||
return { dense_switch: dossierSummary(dossier) };
|
||||
}
|
||||
|
||||
export async function verifyStrippedFunctionOperations(client, entryString) {
|
||||
const stringXrefs = await functionCall(client, "xrefs", {
|
||||
document: null,
|
||||
|
||||
@@ -35,6 +35,7 @@ import { hasTypedSwitchEvidence } from "./verify-real-ghidra-switch-assertions.m
|
||||
|
||||
import {
|
||||
verifyDenseDefaultReturn,
|
||||
verifyJumpTableDenseSwitch,
|
||||
verifyNativeTypeLayout,
|
||||
verifyRelativeSwitch,
|
||||
verifyNativeValueTrace,
|
||||
@@ -80,6 +81,7 @@ const crossFormatSourcePath = fileURLToPath(
|
||||
const debugPath = join(fixtureRoot, "rea-ghidra-inventory-debug");
|
||||
const layoutPath = join(fixtureRoot, "rea-ghidra-layout.o");
|
||||
const strippedPath = join(fixtureRoot, "rea-ghidra-inventory-stripped");
|
||||
const jumpTablePath = join(fixtureRoot, "rea-ghidra-inventory-jump");
|
||||
const arm64ElfPath = join(fixtureRoot, "rea-ghidra-cross-arm64");
|
||||
const peObjectPath = join(fixtureRoot, "rea-ghidra-cross-x86_64.obj");
|
||||
const pePath = join(fixtureRoot, "rea-ghidra-cross-x86_64.exe");
|
||||
@@ -134,6 +136,14 @@ try {
|
||||
if (expectedNativeTarget.format === "mach-o")
|
||||
strippedFlags.push("-fvisibility=hidden");
|
||||
await exec(compiler, [...strippedFlags, sourcePath, "-o", strippedPath]);
|
||||
// The dense jump-table oracle needs the switch lowered to a table: GCC 16 and
|
||||
// later expand dense switches as compare chains at -O0, so the CLI oracle
|
||||
// runs against an optimized build while the debug fixture keeps its -O0
|
||||
// stack-local and DWARF shape.
|
||||
const jumpTableFlags = ["-O2", "-g", "-fno-inline"];
|
||||
if (expectedNativeTarget.format === "elf")
|
||||
jumpTableFlags.push("-fno-pie", "-no-pie");
|
||||
await exec(compiler, [...jumpTableFlags, sourcePath, "-o", jumpTablePath]);
|
||||
const crossTargets = [];
|
||||
if (aarch64JumpTableOnly) {
|
||||
const relativeSource = fileURLToPath(
|
||||
@@ -253,6 +263,11 @@ try {
|
||||
expectedNativeTarget,
|
||||
);
|
||||
assertStrippedFixture(stripped);
|
||||
const jumpTable = await verifyTarget(
|
||||
jumpTablePath,
|
||||
"jump-table",
|
||||
expectedNativeTarget,
|
||||
);
|
||||
const crossResults = [];
|
||||
for (const [targetPath, variant, expectedTarget] of crossTargets) {
|
||||
const result = await verifyTarget(targetPath, variant, expectedTarget);
|
||||
@@ -277,11 +292,13 @@ try {
|
||||
crossFormat || aarch64JumpTableOnly
|
||||
? [sourcePath, crossFormatSourcePath]
|
||||
: [sourcePath],
|
||||
fixtures: [debug, stripped, layout, ...crossResults].map(summary),
|
||||
fixtures: [debug, stripped, layout, jumpTable, ...crossResults].map(
|
||||
summary,
|
||||
),
|
||||
malformed_target: "rejected-before-provider-start",
|
||||
native_api_cli: aarch64JumpTableOnly
|
||||
? (crossResults[0]?.native_api_cli ?? null)
|
||||
: debug.native_api_cli,
|
||||
: jumpTable.native_api_cli,
|
||||
native_value_e2e: debug.native_values,
|
||||
custom_target: custom === null ? null : summary(custom),
|
||||
cleanup: "complete",
|
||||
@@ -493,7 +510,7 @@ async function verifyTarget(targetPath, variant, expectedTarget = null) {
|
||||
const crossArm64Elf =
|
||||
variant === "cross-arm64-elf" || variant === "aarch64-jump-table";
|
||||
const nativeApiCli =
|
||||
variant === "debug" || crossArm64Elf
|
||||
variant === "jump-table" || crossArm64Elf
|
||||
? await verifyNativeApiCli(parsedTarget.value.path, procedures, {
|
||||
requireDenseJumpTable:
|
||||
crossArm64Elf ||
|
||||
@@ -513,15 +530,17 @@ async function verifyTarget(targetPath, variant, expectedTarget = null) {
|
||||
)
|
||||
: variant === "type-layout"
|
||||
? await verifyNativeTypeLayout(client, names)
|
||||
: variant === "custom" || variant === "aarch64-jump-table"
|
||||
? null
|
||||
: await verifyInventoryOperations({
|
||||
client,
|
||||
variant,
|
||||
procedures,
|
||||
names,
|
||||
strings,
|
||||
});
|
||||
: variant === "jump-table"
|
||||
? await verifyJumpTableDenseSwitch(client, procedures)
|
||||
: variant === "custom" || variant === "aarch64-jump-table"
|
||||
? null
|
||||
: await verifyInventoryOperations({
|
||||
client,
|
||||
variant,
|
||||
procedures,
|
||||
names,
|
||||
strings,
|
||||
});
|
||||
const nativeValues =
|
||||
variant === "debug"
|
||||
? await verifyNativeValueTrace(client, procedures, parsedTarget.value)
|
||||
|
||||
Reference in new issue
Block a user