mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
@@ -132,6 +132,11 @@ includes sectionless PIE images with metadata in a later data segment. Named
|
||||
sections retain priority, and alignment, file mapping and record validation
|
||||
remain required.
|
||||
|
||||
A named ELF build-info section selects the record's discovery range. For inline
|
||||
encoding, the following version and module strings may continue past that
|
||||
section into its file-backed load mapping; their bytes are still checked
|
||||
against the mapping and the existing aggregate string limit.
|
||||
|
||||
Historical moduleless Go binaries can store their empty module-string header in
|
||||
declared zero-initialized virtual memory rather than file-backed bytes. The reader
|
||||
recognizes this only when the complete optional module header lies in that memory
|
||||
|
||||
+24
-1
@@ -104,10 +104,10 @@ export const readGoBinaryImage = (bytes: Buffer): GoBinaryImage => {
|
||||
const encoding = (flags & 2) !== 0 ? "inline" : "pointer";
|
||||
const readInline = (offset: number, remaining: number) =>
|
||||
readInlineString(reader, offset, remaining);
|
||||
const sourceEnd = search.offset + search.size;
|
||||
let version: LocatedString;
|
||||
let module: LocatedString;
|
||||
if (encoding === "inline") {
|
||||
const sourceEnd = inlineDataEnd(header, search, container);
|
||||
version = readInline(header + 32, sourceEnd - header - 32);
|
||||
const next = version.location.offset + version.location.bytes;
|
||||
module = readInline(next, sourceEnd - next);
|
||||
@@ -178,6 +178,29 @@ export const readGoBinaryImage = (bytes: Buffer): GoBinaryImage => {
|
||||
};
|
||||
};
|
||||
|
||||
/** Inline strings follow the header in mapped Go build-info data, even when
|
||||
* the named ELF discovery section ends immediately after the header.
|
||||
*/
|
||||
const inlineDataEnd = (
|
||||
header: number,
|
||||
search: FileMapping,
|
||||
container: BinaryContainer,
|
||||
): number => {
|
||||
if (container.format !== "elf") return search.offset + search.size;
|
||||
const address = search.address + BigInt(header - search.offset);
|
||||
let end = search.offset + search.size;
|
||||
for (const { mapping } of container.load_regions ?? []) {
|
||||
const delta = address - mapping.address;
|
||||
if (
|
||||
delta >= 0n &&
|
||||
delta < BigInt(mapping.size) &&
|
||||
mapping.offset + Number(delta) === header
|
||||
)
|
||||
end = Math.max(end, mapping.offset + mapping.size);
|
||||
}
|
||||
return Math.min(end, header + 32 + MAX_BUILD_INFO_BYTES);
|
||||
};
|
||||
|
||||
const observedText = (bytes: Buffer): string | null => {
|
||||
try {
|
||||
return observedUtf8(bytes, "Go build string");
|
||||
|
||||
@@ -191,6 +191,24 @@ it.each([
|
||||
},
|
||||
);
|
||||
|
||||
it("reads inline build strings past the ELF build-info discovery section", () => {
|
||||
const fixture = createGoBinaryFixture();
|
||||
const buildInfoSection = 256 + 64 * 2;
|
||||
fixture.bytes.writeBigUInt64LE(32n, buildInfoSection + 32);
|
||||
|
||||
expect(readGoBinaryImage(fixture.bytes).build_info).toMatchObject({
|
||||
header_offset: fixture.headerOffset,
|
||||
encoding: "inline",
|
||||
go_version: "go1.26.0",
|
||||
module_text: GO_MODULE_TEXT,
|
||||
version_location: { offset: fixture.versionOffset, bytes: 8 },
|
||||
module_location: {
|
||||
offset: fixture.moduleOffset,
|
||||
bytes: fixture.moduleBytes.length,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each(["elf", "pe", "macho"] as const)(
|
||||
"follows mapped pointers in legacy %s metadata",
|
||||
(format) => {
|
||||
|
||||
Reference in New Issue
Block a user