fix(mips): retain unexamined RELR declaration coverage

This commit is contained in:
morluto committed 2026-10-24 04:06:57 +08:00
1 parent 0a9bcbed42
commit 22d3236c31
4 files changed
+86 -2

No files matched your search

+6
View File
@@ -261,3 +261,9 @@ next match. Coverage can overlap between pages; do not sum it as unique memory.
page. These blocks are never searched. Cancellation returns a typed error,
discards the active Ghidra database, and does not return a successful partial page.
Hopper byte search remains unavailable until its implementation is verified.
MIPS16 shared-image admission requires examined dynamic REL declarations.
RELR dynamic tags or nonempty `SHT_RELR` sections leave relocation coverage
unavailable and the profile unsupported; REA does not infer an empty relocation
set from the absence of REL declarations. RELR loader semantics have not been
verified for this lane.
+5 -1
View File
@@ -179,7 +179,11 @@ export const readMipsElfMetadata = async (
gnuAttributes = parsed.value;
continue;
}
if (metadata.type === 3 && table.optional && (type === 4 || type === 9))
if (
metadata.type === 3 &&
table.optional &&
(type === 4 || type === 9 || type === 19)
)
relocationSections.push({
type,
offset: u32(contents.value, position + 16),
+5 -1
View File
@@ -103,7 +103,7 @@ export const readMipsElfSharedLoad = async (
// Bound temporary storage, not admitted artifact size. ELF32 records are
// exact eight-byte structures; every declared record is examined or refused.
const chunkBytes = 64 * 1024;
const relevant = new Set([2, 7, 8, 9, 17, 18, 19, 20, 23]);
const relevant = new Set([2, 7, 8, 9, 17, 18, 19, 20, 23, 35, 36, 37]);
const tags = new Map<number, number>();
let terminated = false;
for (
@@ -130,6 +130,10 @@ export const readMipsElfSharedLoad = async (
}
}
if (!terminated) return err("MIPS dynamic table has no DT_NULL terminator");
if ([35, 36, 37].some((tag) => tags.has(tag)))
return err(
"MIPS shared RELR relocation declarations require separate verification",
);
if ([2, 7, 8, 9, 23].some((tag) => (tags.get(tag) ?? 0) !== 0))
return err(
"MIPS shared RELA or PLT relocation declarations require separate verification",
@@ -110,6 +110,76 @@ for (const little of [true, false]) {
/Symbol-bearing R_MIPS_REL32/u,
);
});
it.each([false, true])(
`leaves RELR dynamic declarations unverified with existing REL=%s (${little ? "LE" : "BE"})`,
async (withRel) => {
const bytes = fixture(little);
const write = (value: number, offset: number) =>
little
? bytes.writeUInt32LE(value, offset)
: bytes.writeUInt32BE(value, offset);
const start = withRel ? 280 : 256;
write(35, start); // DT_RELRSZ
write(4, start + 4);
write(36, start + 8); // DT_RELR
write(2048, start + 12);
write(37, start + 16); // DT_RELRENT
write(4, start + 20);
write(0, start + 24);
write(64, 100);
write(64, 104);
const target = await resolve(bytes);
assert.equal(target.mips.sharedLoad?.status, "unavailable");
assert.match(ghidraMipsUnsupportedReason(target) ?? "", /RELR/u);
},
);
it(`does not ignore an isolated zero-valued RELR declaration (${little ? "LE" : "BE"})`, async () => {
const bytes = fixture(little);
if (little) bytes.writeUInt32LE(37, 280);
else bytes.writeUInt32BE(37, 280);
if (little) {
bytes.writeUInt32LE(40, 100);
bytes.writeUInt32LE(40, 104);
} else {
bytes.writeUInt32BE(40, 100);
bytes.writeUInt32BE(40, 104);
}
const target = await resolve(bytes);
assert.equal(target.mips.sharedLoad?.status, "unavailable");
assert.match(ghidraMipsUnsupportedReason(target) ?? "", /RELR/u);
});
it.each([false, true])(
`includes SHT_RELR in source coverage with existing REL=%s (${little ? "LE" : "BE"})`,
async (withRel) => {
const bytes = fixture(little);
const write = (value: number, offset: number) =>
little
? bytes.writeUInt32LE(value, offset)
: bytes.writeUInt32BE(value, offset);
if (!withRel) write(0, 256);
write(1024, 32);
if (little) {
bytes.writeUInt16LE(40, 46);
bytes.writeUInt16LE(2, 48);
} else {
bytes.writeUInt16BE(40, 46);
bytes.writeUInt16BE(2, 48);
}
write(19, 1068); // SHT_RELR
write(2048, 1080);
write(4, 1084);
write(4, 1100);
const target = await resolve(bytes);
assert.equal(target.mips.sharedLoad?.status, "unavailable");
assert.match(
ghidraMipsUnsupportedReason(target) ?? "",
/section relocation declarations/u,
);
},
);
}
it("examines relocation records beyond a read chunk before deciding completeness", async () => {