Files
rea/tests/acceptance/applications/nativeExportsDynamicSegment.test.ts
郭立 Guo Li 9fd78a74f8 fix(native): preserve unknown ELF export coverage without a dynsym section
Closes #6792.

ELF images with `PT_DYNAMIC` but no `SHT_DYNSYM` section incorrectly reported a complete, empty export inventory. The shared ELF directory reader now owns the dynamic-segment observation; CLI/MCP report `unsupported` with a reason. Existing symbol-table results remain intact. Read-only format correction; no new execution authority or public schema.

Validation on the current main integration:

- `npm run check`, test discovery, and 166 selected Linux tests pass, including actual CLI/stdio MCP cases and PE/IL2CPP/GPU/Rust reader consumers. Original author validation: 113 tests on macOS, including regressions that failed before the fix; test discovery passed.
- Real Debian libc (1,926,232 bytes) and libLLVM 14 (109,967,296 bytes), with only section-header-count/name-index bytes changed, reproduce the old false absence in both public adapters. The reviewed implementation returns unsupported in both adapters. Independent Python maps unchanged `PT_DYNAMIC` declarations and selected symbol bytes; original `readelf` agrees. The reduced files are not loaded or executed; `readelf -D` itself cannot enumerate these reduced layouts, and that failed oracle attempt is retained.
- Unmodified libc still returns all 3,025 exports, byte-identical complete Evidence and the same Evidence ID as the baseline. Resource-guarded public probes completed without stops or payload leftovers. No speed, memory or model-token improvement is claimed for this coverage fix.

Codex review completed with no findings. CI failed: all 61 observed failures (including 47 omitted from the aggregate report by canceled shards) reproduce with identical complete failure diagnostics on the exact CI base and merge trees. Baseline-only bypass is explicitly authorized for this maintenance session; canceled/skipped coverage remains unverified. See the final CI attribution comment. The original author's readiness declaration below is preserved.

## Author readiness

Human owner: @leeguooooo

- [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
- [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
- [x] I declare this PR ready for initial maintainer review.
2026-10-24 03:14:34 +08:00

70 lines
2.7 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { toolContract } from "../../../src/contracts/toolContracts.js";
import { nativeExportsSchema } from "../../../src/domain/native/nativeExports.js";
import { elfExportImage } from "../../fixtures/binaryDiagnostics/nativeExports.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.for(["CLI", "stdio MCP"] as const)(
"keeps unenumerated ELF dynamic exports unsupported through %s",
async (surface, { cli }) => {
const root = await createTestTempDirectory("rea-elf-dynamic-exports-");
const mcp =
surface === "stdio MCP" ? await connectLocalToolsMcp() : undefined;
for (const encoding of [
{ bits: 64, little: true },
{ bits: 32, little: false },
] as const) {
for (const sectionHeaders of ["null-only", false, true] as const) {
const path = join(
root,
`exports-${encoding.bits}-${sectionHeaders}.so`,
);
const bytes = elfExportImage({
...encoding,
symbols: [{ name: "exported_answer" }],
dynamicSegment: true,
sectionHeaders,
});
await writeFile(path, bytes);
let document: unknown;
if (surface === "CLI") {
const response = await cli.run({
arguments: ["inspect-native-exports", path, "--json"],
});
expect(response.exitCode, response.stderr).toBe(0);
document = response.json;
} else {
const response = await mcp!.call("inspect_native_exports", { path });
expect(response.isError, JSON.stringify(response)).not.toBe(true);
document = response.structuredContent;
}
const evidence = toolContract(
"inspect_native_exports",
).outputSchema.parse(document);
const result = nativeExportsSchema.parse(evidence.normalized_result);
expect(result).toMatchObject({
format: "elf",
coverage: {
status: sectionHeaders === true ? "complete" : "unsupported",
exports: sectionHeaders === true ? 1 : 0,
},
issues: [],
});
if (sectionHeaders === true) {
expect(result.exports).toMatchObject([{ name: "exported_answer" }]);
} else {
expect(result.exports).toEqual([]);
expect(result.coverage.reason).toContain("dynamic segment");
expect(result.limitations.join(" ")).not.toContain(
"so it exports no dynamic symbols",
);
}
}
}
},
);