fix(il2cpp): track unresolved ELF relocation widths

Fixes #6701
This commit is contained in:
morluto
2026-10-23 21:03:24 +08:00
parent d31e42699f
commit bdf13a63c4
5 changed files with 84 additions and 25 deletions
+37 -13
View File
@@ -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 };
+7 -7
View File
@@ -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;
};