mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
* feat(elf): decode Linux core dump process and memory mapping notes Inspect Linux core dump note tables and decode structured metadata without requiring external tools or debuggers: - NT_PRPSINFO (type 3): decode process ID, parent ID, process group, session, UID, GID, execution state, filename, and command line arguments bounded by the text budget. - NT_FILE (type 0x46494c45): decode mapped memory ranges (start_address, end_address, file_offset_pages) and mapped file paths with page size. - NT_SIGINFO (type 0x53494749): decode faulting signal number, errno, and code. - NT_AUXV (type 6): decode auxiliary vector pairs with known AT_* tag names. - Support both 32-bit and 64-bit, little-endian and big-endian targets. - Retain unknown core note types gracefully without partial success errors. - Add comprehensive unit tests, fixtures, acceptance tests, and documentation. * fix(elf): preserve raw path bytes and enforce auxv termination * fix(elf): account for path hex text budget and enforce auxv trailing padding * fix(elf): account for row and scan limits, ABI signal order, and lossless source IDs * fix(elf): cache auxv padding scans, pre-charge entries, and isolate page-size test
556 lines
18 KiB
TypeScript
556 lines
18 KiB
TypeScript
import { deflateSync, zstdCompressSync } from "node:zlib";
|
|
|
|
const concat = (...parts: Buffer[]): Buffer => Buffer.concat(parts);
|
|
|
|
const alignTo = (value: number, alignment: number): number =>
|
|
Math.ceil(value / alignment) * alignment;
|
|
|
|
/** Effective namesz/descsz padding: 8 stays 8; 0, 1, 4, and other values use 4. */
|
|
const notePadding = (declared: number): number => (declared === 8 ? 8 : 4);
|
|
|
|
const writeWord = (
|
|
bytes: Buffer,
|
|
offset: number,
|
|
value: bigint | number,
|
|
bits: 32 | 64,
|
|
little: boolean,
|
|
): void => {
|
|
if (bits === 64) {
|
|
if (little) bytes.writeBigUInt64LE(BigInt(value), offset);
|
|
else bytes.writeBigUInt64BE(BigInt(value), offset);
|
|
return;
|
|
}
|
|
if (little) bytes.writeUInt32LE(Number(value), offset);
|
|
else bytes.writeUInt32BE(Number(value), offset);
|
|
};
|
|
|
|
const write16 = (
|
|
bytes: Buffer,
|
|
offset: number,
|
|
value: number,
|
|
little: boolean,
|
|
): void => {
|
|
if (little) bytes.writeUInt16LE(value, offset);
|
|
else bytes.writeUInt16BE(value, offset);
|
|
};
|
|
|
|
const write32 = (
|
|
bytes: Buffer,
|
|
offset: number,
|
|
value: number,
|
|
little: boolean,
|
|
): void => {
|
|
if (little) bytes.writeUInt32LE(Number(value), offset);
|
|
else bytes.writeUInt32BE(Number(value), offset);
|
|
};
|
|
|
|
/** Encode one ELF note, padding name/descriptor to the table alignment. */
|
|
export const elfNote = (
|
|
owner: string,
|
|
type: number,
|
|
descriptor: Buffer,
|
|
little = true,
|
|
alignment = 4,
|
|
): Buffer => {
|
|
const name = Buffer.from(`${owner}\0`, "latin1");
|
|
const pad = notePadding(alignment);
|
|
const descOff = alignTo(12 + name.length, pad);
|
|
const bytes = Buffer.alloc(alignTo(descOff + descriptor.length, pad));
|
|
write32(bytes, 0, name.length, little);
|
|
write32(bytes, 4, descriptor.length, little);
|
|
write32(bytes, 8, type, little);
|
|
name.copy(bytes, 12);
|
|
descriptor.copy(bytes, descOff);
|
|
return bytes;
|
|
};
|
|
|
|
/** GNU NT_GNU_BUILD_ID note with a 20-byte descriptor. */
|
|
export const gnuBuildIdNote = (
|
|
hex = "0123456789abcdef0123456789abcdef01234567",
|
|
little = true,
|
|
): Buffer => elfNote("GNU", 3, Buffer.from(hex, "hex"), little);
|
|
|
|
/** GNU NT_GNU_ABI_TAG note: Linux 3.2.0. */
|
|
export const gnuAbiTagNote = (little = true): Buffer => {
|
|
const descriptor = Buffer.alloc(16);
|
|
write32(descriptor, 0, 0, little);
|
|
write32(descriptor, 4, 3, little);
|
|
write32(descriptor, 8, 2, little);
|
|
write32(descriptor, 12, 0, little);
|
|
return elfNote("GNU", 1, descriptor, little);
|
|
};
|
|
|
|
/** GNU NT_GNU_PROPERTY_TYPE_0 note with 8-byte table padding. */
|
|
export const gnuPropertyNote = (little = true): Buffer => {
|
|
const descriptor = Buffer.alloc(32);
|
|
write32(descriptor, 0, 0xc0000002, little);
|
|
write32(descriptor, 4, 4, little);
|
|
write32(descriptor, 8, 3, little);
|
|
write32(descriptor, 16, 0xc0008002, little);
|
|
write32(descriptor, 20, 4, little);
|
|
write32(descriptor, 24, 1, little);
|
|
return elfNote("GNU", 5, descriptor, little, 8);
|
|
};
|
|
|
|
export interface ElfNoteImageOptions {
|
|
readonly bits?: 32 | 64;
|
|
readonly little?: boolean;
|
|
readonly notes?: Buffer;
|
|
readonly ownership?: "program" | "section" | "both" | "none";
|
|
readonly machine?: number;
|
|
readonly type?: number;
|
|
readonly alignment?: number;
|
|
readonly sectionName?: string;
|
|
}
|
|
|
|
/** Minimal ELF32/ELF64 image declaring PT_NOTE and/or SHT_NOTE. */
|
|
export const elfNoteFixture = (options: ElfNoteImageOptions = {}): Buffer => {
|
|
const bits = options.bits ?? 64;
|
|
const little = options.little ?? true;
|
|
const ownership = options.ownership ?? "both";
|
|
const notes =
|
|
options.notes ??
|
|
concat(gnuBuildIdNote(undefined, little), gnuAbiTagNote(little));
|
|
const declaredAlign = options.alignment ?? 4;
|
|
const pad = notePadding(declaredAlign);
|
|
const header = bits === 64 ? 64 : 52;
|
|
const phent = bits === 64 ? 56 : 32;
|
|
const shent = bits === 64 ? 64 : 40;
|
|
const program = ownership === "program" || ownership === "both";
|
|
const section = ownership === "section" || ownership === "both";
|
|
const phnum = program ? 1 : 0;
|
|
const sectionName = options.sectionName ?? ".note.gnu.build-id";
|
|
const shstrtab = Buffer.from(`\0${sectionName}\0.shstrtab\0`, "latin1");
|
|
const shnum = section ? 3 : 0;
|
|
const phoff = header;
|
|
const shoff = phoff + phnum * phent;
|
|
const noteOff = alignTo(shoff + shnum * shent, pad);
|
|
const strOff = noteOff + notes.length;
|
|
const bytes = Buffer.alloc(strOff + (section ? shstrtab.length : 0));
|
|
bytes.writeUInt32LE(0x464c457f, 0);
|
|
bytes[4] = bits === 64 ? 2 : 1;
|
|
bytes[5] = little ? 1 : 2;
|
|
bytes[6] = 1;
|
|
write16(bytes, 0x10, options.type ?? 3, little);
|
|
write16(bytes, 0x12, options.machine ?? 62, little);
|
|
write32(bytes, 0x14, 1, little);
|
|
write16(bytes, bits === 64 ? 0x34 : 0x28, header, little);
|
|
writeWord(
|
|
bytes,
|
|
bits === 64 ? 0x20 : 0x1c,
|
|
phnum === 0 ? 0 : phoff,
|
|
bits,
|
|
little,
|
|
);
|
|
write16(bytes, bits === 64 ? 0x36 : 0x2a, phent, little);
|
|
write16(bytes, bits === 64 ? 0x38 : 0x2c, phnum, little);
|
|
writeWord(
|
|
bytes,
|
|
bits === 64 ? 0x28 : 0x20,
|
|
shnum === 0 ? 0 : shoff,
|
|
bits,
|
|
little,
|
|
);
|
|
write16(bytes, bits === 64 ? 0x3a : 0x2e, shent, little);
|
|
write16(bytes, bits === 64 ? 0x3c : 0x30, shnum, little);
|
|
write16(bytes, bits === 64 ? 0x3e : 0x32, section ? 2 : 0, little);
|
|
if (program) {
|
|
const ph = phoff;
|
|
write32(bytes, ph, 4, little);
|
|
if (bits === 64) {
|
|
write32(bytes, ph + 4, 4, little);
|
|
writeWord(bytes, ph + 8, noteOff, bits, little);
|
|
writeWord(bytes, ph + 32, notes.length, bits, little);
|
|
writeWord(bytes, ph + 40, notes.length, bits, little);
|
|
writeWord(bytes, ph + 48, declaredAlign, bits, little);
|
|
} else {
|
|
writeWord(bytes, ph + 4, noteOff, bits, little);
|
|
writeWord(bytes, ph + 16, notes.length, bits, little);
|
|
writeWord(bytes, ph + 20, notes.length, bits, little);
|
|
write32(bytes, ph + 24, 4, little);
|
|
writeWord(bytes, ph + 28, declaredAlign, bits, little);
|
|
}
|
|
}
|
|
if (section) {
|
|
const noteName = 1;
|
|
const strName = `${sectionName}\0`.length + 1;
|
|
const noteSh = shoff + shent;
|
|
const strSh = shoff + 2 * shent;
|
|
write32(bytes, noteSh, noteName, little);
|
|
write32(bytes, noteSh + 4, 7, little);
|
|
writeWord(bytes, noteSh + (bits === 64 ? 24 : 16), noteOff, bits, little);
|
|
writeWord(
|
|
bytes,
|
|
noteSh + (bits === 64 ? 32 : 20),
|
|
notes.length,
|
|
bits,
|
|
little,
|
|
);
|
|
writeWord(
|
|
bytes,
|
|
noteSh + (bits === 64 ? 48 : 32),
|
|
declaredAlign,
|
|
bits,
|
|
little,
|
|
);
|
|
write32(bytes, strSh, strName, little);
|
|
write32(bytes, strSh + 4, 3, little);
|
|
writeWord(bytes, strSh + (bits === 64 ? 24 : 16), strOff, bits, little);
|
|
writeWord(
|
|
bytes,
|
|
strSh + (bits === 64 ? 32 : 20),
|
|
shstrtab.length,
|
|
bits,
|
|
little,
|
|
);
|
|
shstrtab.copy(bytes, strOff);
|
|
}
|
|
notes.copy(bytes, noteOff);
|
|
return bytes;
|
|
};
|
|
|
|
/** A valid GNU note whose SHT_NOTE section-name lookup cannot be completed. */
|
|
export const elfNoteWithUnreadableSectionNameFixture = (
|
|
reason: "outside" | "unterminated",
|
|
): Buffer => {
|
|
const bytes = elfNoteFixture({
|
|
ownership: "section",
|
|
notes: gnuBuildIdNote(),
|
|
});
|
|
const noteSection = 64 + 64;
|
|
const namesSection = 64 + 2 * 64;
|
|
const namesOffset = Number(bytes.readBigUInt64LE(namesSection + 24));
|
|
const namesSize = Number(bytes.readBigUInt64LE(namesSection + 32));
|
|
if (reason === "outside") bytes.writeUInt32LE(namesSize, noteSection);
|
|
else bytes.fill(0x41, namesOffset + 1, namesOffset + namesSize);
|
|
return bytes;
|
|
};
|
|
|
|
/** A non-allocated SHT_NOTE whose stored bytes include an ELF compression header. */
|
|
export const compressedElfNoteSectionFixture = (
|
|
bits: 32 | 64 = 64,
|
|
little = true,
|
|
): Buffer => {
|
|
const notes = concat(
|
|
gnuBuildIdNote(undefined, little),
|
|
gnuAbiTagNote(little),
|
|
);
|
|
const header = Buffer.alloc(bits === 64 ? 24 : 12);
|
|
write32(header, 0, 1, little); // ELFCOMPRESS_ZLIB
|
|
writeWord(header, bits === 64 ? 8 : 4, notes.length, bits, little);
|
|
writeWord(header, bits === 64 ? 16 : 8, 4, bits, little);
|
|
const bytes = elfNoteFixture({
|
|
bits,
|
|
little,
|
|
type: 1,
|
|
ownership: "section",
|
|
sectionName: ".debug.notes",
|
|
alignment: bits === 64 ? 8 : 4,
|
|
notes: concat(header, deflateSync(notes)),
|
|
});
|
|
const noteSection = (bits === 64 ? 64 : 52) + (bits === 64 ? 64 : 40);
|
|
writeWord(bytes, noteSection + 8, 0x800, bits, little);
|
|
return bytes;
|
|
};
|
|
|
|
/** Valid ELF64 section count stored in section header zero per the gABI. */
|
|
export const elfExtendedSectionCountFixture = (): Buffer => {
|
|
const count = 0xff00;
|
|
const sectionOffset = 0x200;
|
|
const noteOffset = 0x100;
|
|
const namesOffset = 0x180;
|
|
const note = gnuBuildIdNote("12345678");
|
|
const names = Buffer.from("\0.note.gnu.build-id\0.shstrtab\0", "latin1");
|
|
const bytes = Buffer.alloc(sectionOffset + count * 64);
|
|
bytes.set([0x7f, 0x45, 0x4c, 0x46, 2, 1, 1]);
|
|
bytes.writeUInt16LE(1, 16);
|
|
bytes.writeUInt16LE(62, 18);
|
|
bytes.writeUInt32LE(1, 20);
|
|
bytes.writeBigUInt64LE(BigInt(sectionOffset), 40);
|
|
bytes.writeUInt16LE(64, 52);
|
|
bytes.writeUInt16LE(56, 54);
|
|
bytes.writeUInt16LE(64, 58);
|
|
bytes.writeUInt16LE(0, 60);
|
|
bytes.writeUInt16LE(2, 62);
|
|
bytes.writeBigUInt64LE(BigInt(count), sectionOffset + 32);
|
|
|
|
const noteSection = sectionOffset + 64;
|
|
bytes.writeUInt32LE(1, noteSection);
|
|
bytes.writeUInt32LE(7, noteSection + 4);
|
|
bytes.writeBigUInt64LE(BigInt(noteOffset), noteSection + 24);
|
|
bytes.writeBigUInt64LE(BigInt(note.length), noteSection + 32);
|
|
bytes.writeBigUInt64LE(4n, noteSection + 48);
|
|
|
|
const namesSection = sectionOffset + 2 * 64;
|
|
bytes.writeUInt32LE(20, namesSection);
|
|
bytes.writeUInt32LE(3, namesSection + 4);
|
|
bytes.writeBigUInt64LE(BigInt(namesOffset), namesSection + 24);
|
|
bytes.writeBigUInt64LE(BigInt(names.length), namesSection + 32);
|
|
bytes.writeBigUInt64LE(1n, namesSection + 48);
|
|
|
|
note.copy(bytes, noteOffset);
|
|
names.copy(bytes, namesOffset);
|
|
return bytes;
|
|
};
|
|
|
|
/** ELF note fixture with a compressed section-name string table. */
|
|
export const compressedElfNoteFixture = (
|
|
bits: 32 | 64 = 64,
|
|
little = true,
|
|
compressionType = 1,
|
|
sectionName = ".note.gnu.build-id",
|
|
): Buffer => {
|
|
const image = elfNoteFixture({
|
|
bits,
|
|
little,
|
|
ownership: "section",
|
|
sectionName,
|
|
});
|
|
const wide = bits === 64;
|
|
const u16 = (offset: number) =>
|
|
little ? image.readUInt16LE(offset) : image.readUInt16BE(offset);
|
|
const word = (offset: number) =>
|
|
wide
|
|
? Number(
|
|
little
|
|
? image.readBigUInt64LE(offset)
|
|
: image.readBigUInt64BE(offset),
|
|
)
|
|
: little
|
|
? image.readUInt32LE(offset)
|
|
: image.readUInt32BE(offset);
|
|
const shoff = word(wide ? 40 : 32);
|
|
const shentsize = u16(wide ? 58 : 46);
|
|
const namesIndex = u16(wide ? 62 : 50);
|
|
const namesHeader = shoff + namesIndex * shentsize;
|
|
const namesOffset = word(namesHeader + (wide ? 24 : 16));
|
|
const namesSize = word(namesHeader + (wide ? 32 : 20));
|
|
const names = image.subarray(namesOffset, namesOffset + namesSize);
|
|
const compressed =
|
|
compressionType === 2 ? zstdCompressSync(names) : deflateSync(names);
|
|
const compressionHeaderBytes = wide ? 24 : 12;
|
|
const replacement = Buffer.alloc(compressionHeaderBytes);
|
|
if (little) replacement.writeUInt32LE(compressionType, 0);
|
|
else replacement.writeUInt32BE(compressionType, 0);
|
|
if (wide) {
|
|
if (little) {
|
|
replacement.writeBigUInt64LE(BigInt(names.length), 8);
|
|
replacement.writeBigUInt64LE(1n, 16);
|
|
} else {
|
|
replacement.writeBigUInt64BE(BigInt(names.length), 8);
|
|
replacement.writeBigUInt64BE(1n, 16);
|
|
}
|
|
} else {
|
|
if (little) {
|
|
replacement.writeUInt32LE(names.length, 4);
|
|
replacement.writeUInt32LE(1, 8);
|
|
} else {
|
|
replacement.writeUInt32BE(names.length, 4);
|
|
replacement.writeUInt32BE(1, 8);
|
|
}
|
|
}
|
|
const result = concat(
|
|
image.subarray(0, namesOffset),
|
|
replacement,
|
|
compressed,
|
|
image.subarray(namesOffset + namesSize),
|
|
);
|
|
const writeWord = (offset: number, value: number) => {
|
|
if (wide) {
|
|
if (little) result.writeBigUInt64LE(BigInt(value), offset);
|
|
else result.writeBigUInt64BE(BigInt(value), offset);
|
|
} else if (little) result.writeUInt32LE(value, offset);
|
|
else result.writeUInt32BE(value, offset);
|
|
};
|
|
const flagsAt = namesHeader + 8;
|
|
writeWord(flagsAt, word(flagsAt) | 0x800);
|
|
writeWord(
|
|
namesHeader + (wide ? 32 : 20),
|
|
compressionHeaderBytes + compressed.length,
|
|
);
|
|
writeWord(namesHeader + (wide ? 48 : 32), 1);
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* ELF64 whose only PT_NOTE declares 20 bytes. The file holds the 12-byte
|
|
* header, 4-byte name, and 1-byte descriptor; the 3 alignment bytes are absent.
|
|
*/
|
|
export const elfNoteTablePastEnd = (): Buffer => {
|
|
const note = Buffer.alloc(17);
|
|
write32(note, 0, 4, true);
|
|
write32(note, 4, 1, true);
|
|
write32(note, 8, 1, true);
|
|
note.write("GNU\0", 12, "latin1");
|
|
note[16] = 0x2a;
|
|
const header = Buffer.alloc(64);
|
|
header.writeUInt32LE(0x464c457f, 0);
|
|
header[4] = 2;
|
|
header[5] = 1;
|
|
header[6] = 1;
|
|
write16(header, 0x10, 3, true);
|
|
write16(header, 0x12, 62, true);
|
|
write32(header, 0x14, 1, true);
|
|
write16(header, 0x34, 64, true);
|
|
writeWord(header, 0x20, 64, 64, true);
|
|
write16(header, 0x36, 56, true);
|
|
write16(header, 0x38, 1, true);
|
|
const program = Buffer.alloc(56);
|
|
write32(program, 0, 4, true);
|
|
writeWord(program, 8, 120, 64, true);
|
|
writeWord(program, 32, 20, 64, true);
|
|
writeWord(program, 48, 4, 64, true);
|
|
return concat(header, program, note);
|
|
};
|
|
|
|
export interface CorePrpsinfoOptions {
|
|
readonly bits?: 32 | 64;
|
|
readonly little?: boolean;
|
|
readonly state?: number;
|
|
readonly sname?: string;
|
|
readonly zomb?: number;
|
|
readonly nice?: number;
|
|
readonly flag?: number;
|
|
readonly uid?: number;
|
|
readonly gid?: number;
|
|
readonly pid?: number;
|
|
readonly ppid?: number;
|
|
readonly pgrp?: number;
|
|
readonly sid?: number;
|
|
readonly fname?: string;
|
|
readonly psargs?: string;
|
|
}
|
|
|
|
/** Linux CORE NT_PRPSINFO note (type 3). */
|
|
export const corePrpsinfoNote = (options: CorePrpsinfoOptions = {}): Buffer => {
|
|
const bits = options.bits ?? 64;
|
|
const little = options.little ?? true;
|
|
const desc = Buffer.alloc(bits === 64 ? 136 : 124);
|
|
desc[0] = options.state ?? 0;
|
|
desc[1] = (options.sname ?? "R").charCodeAt(0);
|
|
desc[2] = options.zomb ?? 0;
|
|
desc[3] = options.nice ?? 0;
|
|
if (bits === 64) {
|
|
if (little) desc.writeBigUInt64LE(BigInt(options.flag ?? 0), 8);
|
|
else desc.writeBigUInt64BE(BigInt(options.flag ?? 0), 8);
|
|
write32(desc, 16, options.uid ?? 1000, little);
|
|
write32(desc, 20, options.gid ?? 1000, little);
|
|
if (little) {
|
|
desc.writeInt32LE(options.pid ?? 1234, 24);
|
|
desc.writeInt32LE(options.ppid ?? 1, 28);
|
|
desc.writeInt32LE(options.pgrp ?? 1234, 32);
|
|
desc.writeInt32LE(options.sid ?? 1234, 36);
|
|
} else {
|
|
desc.writeInt32BE(options.pid ?? 1234, 24);
|
|
desc.writeInt32BE(options.ppid ?? 1, 28);
|
|
desc.writeInt32BE(options.pgrp ?? 1234, 32);
|
|
desc.writeInt32BE(options.sid ?? 1234, 36);
|
|
}
|
|
const fname = Buffer.from(`${options.fname ?? "app"}\0`, "latin1");
|
|
fname.copy(desc, 40, 0, Math.min(fname.length, 16));
|
|
const psargs = Buffer.from(
|
|
`${options.psargs ?? "./app --flag"}\0`,
|
|
"latin1",
|
|
);
|
|
psargs.copy(desc, 56, 0, Math.min(psargs.length, 80));
|
|
} else {
|
|
write32(desc, 4, options.flag ?? 0, little);
|
|
write16(desc, 8, options.uid ?? 1000, little);
|
|
write16(desc, 10, options.gid ?? 1000, little);
|
|
if (little) {
|
|
desc.writeInt32LE(options.pid ?? 1234, 12);
|
|
desc.writeInt32LE(options.ppid ?? 1, 16);
|
|
desc.writeInt32LE(options.pgrp ?? 1234, 20);
|
|
desc.writeInt32LE(options.sid ?? 1234, 24);
|
|
} else {
|
|
desc.writeInt32BE(options.pid ?? 1234, 12);
|
|
desc.writeInt32BE(options.ppid ?? 1, 16);
|
|
desc.writeInt32BE(options.pgrp ?? 1234, 20);
|
|
desc.writeInt32BE(options.sid ?? 1234, 24);
|
|
}
|
|
const fname = Buffer.from(`${options.fname ?? "app"}\0`, "latin1");
|
|
fname.copy(desc, 28, 0, Math.min(fname.length, 16));
|
|
const psargs = Buffer.from(
|
|
`${options.psargs ?? "./app --flag"}\0`,
|
|
"latin1",
|
|
);
|
|
psargs.copy(desc, 44, 0, Math.min(psargs.length, 80));
|
|
}
|
|
return elfNote("CORE", 3, desc, little);
|
|
};
|
|
|
|
export interface CoreFileMapping {
|
|
readonly start: bigint | number;
|
|
readonly end: bigint | number;
|
|
readonly file_ofs: bigint | number;
|
|
readonly path: string;
|
|
}
|
|
|
|
/** Linux CORE NT_FILE note (type 0x46494c45). */
|
|
export const coreFileNote = (
|
|
mappings: readonly CoreFileMapping[],
|
|
pageSize = 4096,
|
|
bits: 32 | 64 = 64,
|
|
little = true,
|
|
): Buffer => {
|
|
const wordBytes = bits === 64 ? 8 : 4;
|
|
const headerBytes = 2 * wordBytes;
|
|
const mapEntryBytes = 3 * wordBytes;
|
|
const filenames = Buffer.concat(
|
|
mappings.map((m) => Buffer.from(`${m.path}\0`, "latin1")),
|
|
);
|
|
const desc = Buffer.alloc(
|
|
headerBytes + mappings.length * mapEntryBytes + filenames.length,
|
|
);
|
|
writeWord(desc, 0, mappings.length, bits, little);
|
|
writeWord(desc, wordBytes, pageSize, bits, little);
|
|
let offset = headerBytes;
|
|
for (const m of mappings) {
|
|
writeWord(desc, offset, m.start, bits, little);
|
|
writeWord(desc, offset + wordBytes, m.end, bits, little);
|
|
writeWord(desc, offset + 2 * wordBytes, m.file_ofs, bits, little);
|
|
offset += mapEntryBytes;
|
|
}
|
|
filenames.copy(desc, offset);
|
|
return elfNote("CORE", 0x46494c45, desc, little);
|
|
};
|
|
|
|
/** Linux CORE NT_SIGINFO note (type 0x53494749). */
|
|
export const coreSiginfoNote = (
|
|
signo = 11,
|
|
code = 1,
|
|
errno = 0,
|
|
little = true,
|
|
): Buffer => {
|
|
const desc = Buffer.alloc(128);
|
|
if (little) {
|
|
desc.writeInt32LE(signo, 0);
|
|
desc.writeInt32LE(errno, 4);
|
|
desc.writeInt32LE(code, 8);
|
|
} else {
|
|
desc.writeInt32BE(signo, 0);
|
|
desc.writeInt32BE(errno, 4);
|
|
desc.writeInt32BE(code, 8);
|
|
}
|
|
return elfNote("CORE", 0x53494749, desc, little);
|
|
};
|
|
|
|
/** Linux CORE NT_AUXV note (type 6). */
|
|
export const coreAuxvNote = (
|
|
entries: readonly [key: number, value: bigint | number][],
|
|
bits: 32 | 64 = 64,
|
|
little = true,
|
|
): Buffer => {
|
|
const wordBytes = bits === 64 ? 8 : 4;
|
|
const desc = Buffer.alloc((entries.length + 1) * 2 * wordBytes);
|
|
let offset = 0;
|
|
for (const [key, value] of entries) {
|
|
writeWord(desc, offset, key, bits, little);
|
|
writeWord(desc, offset + wordBytes, value, bits, little);
|
|
offset += 2 * wordBytes;
|
|
}
|
|
writeWord(desc, offset, 0, bits, little);
|
|
writeWord(desc, offset + wordBytes, 0, bits, little);
|
|
return elfNote("CORE", 6, desc, little);
|
|
};
|