Link original DEX native declarations to per-ABI function exports and relocated static JNI table candidates through shared CLI/MCP workflows. Preserve global attribution ambiguity, exact provenance, partial coverage and retained Evidence; default MCP delivery to bounded summaries with filtered/batch pages. Use one immutable ELF relocation decoder with streaming IL2CPP materialization, cooperative bounded export/version parsing and bounded JNI state. Reuse validated session inventories without loading target libraries or executing JNI code. Validation: relevant type/lint/build and parser/domain/public CLI/MCP regressions; genuine four-ABI NDK fixtures and independent library/slot oracles; actual JDK/JADX Conscrypt workflows with retained-source removal. Full measurements and limitations are recorded in docs/verification/android-jni-bindings-2026-10-27.md. Closes #6403.
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
import type { JadxToolPort } from "./JadxAnalysis.js";
|
|
import type { AndroidNativeDeclarations } from "../domain/android/androidNativeDeclarations.js";
|
|
import { jadxNativeDeclarationsSchema } from "./JadxProtocol.js";
|
|
import { AnalysisOutputError } from "../domain/analysisErrorCore.js";
|
|
|
|
/** Validate the native-only batch at its producing boundary, preserving raw facts. */
|
|
export const readAndroidNativeInventory = async (
|
|
tools: JadxToolPort,
|
|
snapshot: string,
|
|
): Promise<AndroidNativeDeclarations> => {
|
|
const raw = await tools.json("rea_native_declarations", { path: snapshot });
|
|
const parsed = jadxNativeDeclarationsSchema.safeParse(raw);
|
|
if (!parsed.success)
|
|
throw new AnalysisOutputError(
|
|
"inspect_android_jni_bindings",
|
|
"JADX native declaration inventory was malformed",
|
|
{ cause: parsed.error },
|
|
);
|
|
const inventory = parsed.data;
|
|
const origins = new Map<number, string>();
|
|
const inconsistent =
|
|
inventory.coverage.examined_dex_entries > inventory.coverage.dex_entries ||
|
|
inventory.methods.length > inventory.coverage.examined_methods ||
|
|
(inventory.coverage.status === "complete" &&
|
|
(inventory.coverage.examined_dex_entries !==
|
|
inventory.coverage.dex_entries ||
|
|
inventory.unknowns.length !== 0)) ||
|
|
inventory.methods.some((method, index) => {
|
|
const previous = origins.get(method.dex_entry_index);
|
|
origins.set(method.dex_entry_index, method.dex_entry);
|
|
return (
|
|
method.source_index !== index ||
|
|
(previous !== undefined && previous !== method.dex_entry)
|
|
);
|
|
});
|
|
if (inconsistent)
|
|
throw new AnalysisOutputError(
|
|
"inspect_android_jni_bindings",
|
|
"JADX native declaration counts or source identities were inconsistent",
|
|
);
|
|
return inventory;
|
|
};
|