1 parent
d31e42699f
commit
bdf13a63c4
5 files changed
+84
-25
No files matched your search
@@ -298,7 +298,7 @@ Format and selection failures name `binary_path`; capacity refusals remain typed
|
||||
|
||||
Scans yield cooperatively in bounded batches. Name-address retention and reverse-trie construction are each limited to65,536 entries/nodes and fallback module-array candidates to64 (`native-index-capacity`); exceeding capacity does not prove absence. Rendered type text has a64MiB UTF-8 cache-workspace guard, separate from the64MiB encoded-report admission, and64 nested rendering frames (`native-type-text-capacity` / `type-nesting-capacity`). Genuine cycles are malformed; unsupported type kinds are explicit coverage failures. Native row/text admission runs before allocation or retention. Unused generic parameter names are read on demand from the same stable metadata snapshot. There is no cross-inspection file cache; validated immutable Evidence is reused for session views.
|
||||
|
||||
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown; stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
|
||||
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown only for the bytes their ELF ABI relocation type covers; an adjacent 32-bit relocation does not make a separate 64-bit method slot unknown. Stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
|
||||
|
||||
Native address queries batch exact64-bit hexadecimal `addresses` and `states` (`code`, `no-body`, `unknown`), intersecting indices and pagination. Numerically equal address spellings match; original row indices and every shared-body alternative survive:
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const unsupported = (message: string) =>
|
||||
|
||||
interface RelocatedImage {
|
||||
readonly bytes: Buffer;
|
||||
readonly unresolved: Set<bigint>;
|
||||
readonly unresolved: Map<bigint, number>;
|
||||
readonly zeroFillPointers: Map<bigint, bigint>;
|
||||
}
|
||||
interface RelocationSource {
|
||||
@@ -29,7 +29,7 @@ interface RelocationSource {
|
||||
readonly mappings: readonly FileMapping[];
|
||||
readonly zeroFills: readonly ZeroFillMapping[];
|
||||
readonly relative: number;
|
||||
readonly unknownPointerKinds: ReadonlySet<number>;
|
||||
readonly unknownPointerWidths: ReadonlyMap<number, number>;
|
||||
}
|
||||
|
||||
/** Standard dynamic relocations whose destination spans at most eight bytes.
|
||||
@@ -41,8 +41,19 @@ const RELOCATION_KINDS = new Map([
|
||||
183,
|
||||
{
|
||||
relative: 1027,
|
||||
unknownPointerKinds: new Set([
|
||||
257, 258, 259, 260, 261, 262, 1025, 1026, 1028, 1029, 1030, 1032,
|
||||
unknownPointerWidths: new Map([
|
||||
[257, 8],
|
||||
[258, 4],
|
||||
[259, 2],
|
||||
[260, 8],
|
||||
[261, 4],
|
||||
[262, 2],
|
||||
[1025, 8],
|
||||
[1026, 8],
|
||||
[1028, 8],
|
||||
[1029, 8],
|
||||
[1030, 8],
|
||||
[1032, 8],
|
||||
]),
|
||||
},
|
||||
],
|
||||
@@ -50,7 +61,19 @@ const RELOCATION_KINDS = new Map([
|
||||
62,
|
||||
{
|
||||
relative: 8,
|
||||
unknownPointerKinds: new Set([1, 2, 6, 7, 10, 11, 16, 17, 18, 24, 37]),
|
||||
unknownPointerWidths: new Map([
|
||||
[1, 8],
|
||||
[2, 4],
|
||||
[6, 8],
|
||||
[7, 8],
|
||||
[10, 4],
|
||||
[11, 4],
|
||||
[16, 8],
|
||||
[17, 8],
|
||||
[18, 8],
|
||||
[24, 8],
|
||||
[37, 8],
|
||||
]),
|
||||
},
|
||||
],
|
||||
]);
|
||||
@@ -148,19 +171,20 @@ function* applyRelaTable(
|
||||
kind = Number(info & 0xffffffffn);
|
||||
if (kind === 0) continue;
|
||||
const destination = source.image.readBigUInt64LE(at);
|
||||
const target = fileOffset(source.mappings, destination, 8);
|
||||
if (kind !== source.relative || info >> 32n !== 0n) {
|
||||
if (!source.unknownPointerKinds.has(kind))
|
||||
const width = source.unknownPointerWidths.get(kind);
|
||||
if (width === undefined)
|
||||
throw unsupported(`ELF relocation kind ${kind} is not decoded`);
|
||||
if (!output.unresolved.has(destination)) {
|
||||
admitOverlay(output);
|
||||
output.unresolved.add(destination);
|
||||
}
|
||||
const priorWidth = output.unresolved.get(destination);
|
||||
if (priorWidth === undefined) admitOverlay(output);
|
||||
output.unresolved.set(destination, Math.max(priorWidth ?? 0, width));
|
||||
// Registration scans read this private image directly. Clear unknown
|
||||
// fields so their stored bytes cannot masquerade as resolved pointers.
|
||||
if (target !== null) output.bytes.fill(0, target, target + 8);
|
||||
const target = fileOffset(source.mappings, destination, width);
|
||||
if (target !== null) output.bytes.fill(0, target, target + width);
|
||||
continue;
|
||||
}
|
||||
const target = fileOffset(source.mappings, destination, 8);
|
||||
const value = source.image.readBigInt64LE(at + 16) & 0xffffffffffffffffn;
|
||||
if (target !== null) output.bytes.writeBigUInt64LE(value, target);
|
||||
else if (
|
||||
@@ -196,7 +220,7 @@ export function* applyElfRelativeRelocationsSteps(
|
||||
yield;
|
||||
const output: RelocatedImage = {
|
||||
bytes: Buffer.from(image),
|
||||
unresolved: new Set(),
|
||||
unresolved: new Map(),
|
||||
zeroFillPointers: new Map(),
|
||||
};
|
||||
const source = { image, mappings, zeroFills, ...kinds };
|
||||
|
||||
@@ -66,7 +66,7 @@ export interface NativeImage {
|
||||
readonly bytes: Buffer;
|
||||
readonly container: BinaryContainer;
|
||||
readonly imageBase: bigint;
|
||||
readonly unresolvedPointerSlots: ReadonlySet<bigint>;
|
||||
readonly unresolvedPointerSlots: ReadonlyMap<bigint, number>;
|
||||
readonly zeroFillPointers: ReadonlyMap<bigint, bigint>;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function* openImageSteps(
|
||||
let relocated:
|
||||
| {
|
||||
bytes: Buffer;
|
||||
unresolved: ReadonlySet<bigint>;
|
||||
unresolved: ReadonlyMap<bigint, number>;
|
||||
zeroFillPointers: ReadonlyMap<bigint, bigint>;
|
||||
}
|
||||
| undefined;
|
||||
@@ -151,7 +151,7 @@ export function* openImageSteps(
|
||||
bytes: image,
|
||||
container,
|
||||
imageBase,
|
||||
unresolvedPointerSlots: relocated?.unresolved ?? new Set<bigint>(),
|
||||
unresolvedPointerSlots: relocated?.unresolved ?? new Map<bigint, number>(),
|
||||
zeroFillPointers: relocated?.zeroFillPointers ?? new Map<bigint, bigint>(),
|
||||
};
|
||||
}
|
||||
@@ -295,10 +295,10 @@ const hasUnresolvedPointer = (
|
||||
size = 8,
|
||||
): boolean => {
|
||||
if (image.unresolvedPointerSlots.size === 0) return false;
|
||||
// Unsupported relocation widths are unknown; conservatively cover any
|
||||
// overlap with the pointer rather than infer a zero or trust stored bytes.
|
||||
for (let delta = -7; delta < size; delta++)
|
||||
if (image.unresolvedPointerSlots.has(address + BigInt(delta))) return true;
|
||||
for (let delta = -7; delta < size; delta++) {
|
||||
const width = image.unresolvedPointerSlots.get(address + BigInt(delta));
|
||||
if (width !== undefined && delta + width > 0) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -511,6 +511,29 @@ it("reports a symbol-relocated method slot as unknown rather than no-body", () =
|
||||
expect(linked.method_addresses[0]).toEqual(expectedAddresses[0]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["PC32", 2],
|
||||
["ABS32", 10],
|
||||
] as const)(
|
||||
"keeps a method pointer beside a non-overlapping %s relocation",
|
||||
(_, kind) => {
|
||||
const binary = createIl2cppBinaryFixture();
|
||||
const adjacent = createElfFixture(binary, {
|
||||
machine: 62,
|
||||
unresolvedRelocations: [{ offset: 0x2fc, kind }],
|
||||
});
|
||||
expect(link(adjacent).method_addresses).toEqual(expectedAddresses);
|
||||
|
||||
const overlapping = createElfFixture(binary, {
|
||||
machine: 62,
|
||||
unresolvedRelocations: [{ offset: 0x300, kind }],
|
||||
});
|
||||
expect(link(overlapping).method_addresses[1]).toEqual(
|
||||
expectedAddress("unknown"),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it("reads a relative relocation into a zero-filled pointer array as a known value", () => {
|
||||
const bytes = createElfFixture(createIl2cppBinaryFixture(), {
|
||||
zeroFilledTable: true,
|
||||
|
||||
Vendored
+16
-4
@@ -223,6 +223,11 @@ export interface ElfFixtureOptions {
|
||||
readonly dynamic?: boolean;
|
||||
/** Give every relocation a symbol index, so none is RELATIVE-only. */
|
||||
readonly symbolRelocations?: boolean;
|
||||
/** Add unresolved dynamic relocations at offsets relative to the load base. */
|
||||
readonly unresolvedRelocations?: readonly {
|
||||
readonly offset: number;
|
||||
readonly kind: number;
|
||||
}[];
|
||||
/** Write DT_RELA twice. */
|
||||
readonly duplicateRela?: boolean;
|
||||
/** Override DT_RELASZ or DT_RELAENT. */
|
||||
@@ -243,7 +248,7 @@ export const createElfFixture = (
|
||||
): Buffer => {
|
||||
const body = Buffer.from(image);
|
||||
body.fill(0, 0, 0x100);
|
||||
const relocations: [slot: bigint, value: bigint][] = [];
|
||||
const relocations: [slot: bigint, value: bigint, kind?: number][] = [];
|
||||
for (let at = 0x100; at + 8 <= SIZE; at += 8) {
|
||||
const value = body.readBigUInt64LE(at);
|
||||
if (
|
||||
@@ -254,6 +259,12 @@ export const createElfFixture = (
|
||||
relocations.push([FIXTURE_BINARY_BASE + BigInt(at), value]);
|
||||
body.writeBigUInt64LE(0n, at);
|
||||
}
|
||||
for (const relocation of options.unresolvedRelocations ?? [])
|
||||
relocations.push([
|
||||
FIXTURE_BINARY_BASE + BigInt(relocation.offset),
|
||||
0n,
|
||||
relocation.kind,
|
||||
]);
|
||||
const tagCount =
|
||||
4 +
|
||||
(options.duplicateRela === true ? 1 : 0) +
|
||||
@@ -266,11 +277,12 @@ export const createElfFixture = (
|
||||
entry[1] = FIXTURE_BINARY_BASE + BigInt(fileSize);
|
||||
}
|
||||
const rela = Buffer.alloc(relocations.length * 24);
|
||||
relocations.forEach(([slot, value], index) => {
|
||||
relocations.forEach(([slot, value, kind], index) => {
|
||||
rela.writeBigUInt64LE(slot, index * 24);
|
||||
rela.writeBigUInt64LE(
|
||||
(options.symbolRelocations === true ? 1n << 32n : 0n) |
|
||||
(options.machine === 62 ? 8n : 1027n),
|
||||
(options.symbolRelocations === true || kind !== undefined
|
||||
? 1n << 32n
|
||||
: 0n) | BigInt(kind ?? (options.machine === 62 ? 8 : 1027)),
|
||||
index * 24 + 8,
|
||||
);
|
||||
rela.writeBigInt64LE(value, index * 24 + 16);
|
||||
|
||||
Reference in new issue
Block a user