UNPKG

elflib

Version:
188 lines (187 loc) 7.99 kB
import assert from 'assert'; import { RPLFileInfo } from './rplfileinfo.js'; import { Structs } from './structs.js'; import * as Enums from './enums.js'; import { isStringSection, getString, packStringSection, packSymbolSection, packRelocationSection } from '../sections.js'; import { trimBuffer } from '../encoding.js'; import hashwasm from 'hash-wasm'; import zlib from 'zlib'; export class Section extends Structs.Section { constructor() { super(); } /** Get the section's name. */ getName(elf) { if (elf.header.shstrIndex >= elf.sections.length) throw new Error('Invalid ELF file. Section header string table index is invalid.'); if (elf.header.shstrIndex === 0) return 'SECTION' + this.index; const shstrtab = elf.sections[elf.header.shstrIndex]; if (!isStringSection(shstrtab)) throw new Error('Invalid ELF file. Section header string table index is not a string table.'); if (shstrtab.flags & Enums.SectionFlags.Compressed) return '<compressed>'; if (this.nameOffset === 0) return this.type ? 'SECTION' + this.index : '<null>'; else return getString(shstrtab.strings, this.nameOffset); } /** The index of this section */ index = -1; /** The raw binary data of this section */ _data = new Uint8Array(0); /** The raw binary data of this section */ get data() { return this._data; } set data(data) { this._data = data; } /** The uncompressed size of this section in bytes, if it's compressed. * If the section is not compressed, this is identical to {@link Section.size}. */ get sizeUncompressed() { if (!(this.flags & Enums.SectionFlags.Compressed)) return this.size; if (this.data.byteLength < 4) throw new Error('Invalid or corrupt ELF section. Section is flagged as compressed, but is too small.'); return new DataView(this.data.buffer).getUint32(0); } get crc32Hash() { return (async () => { if (this.type === Enums.SectionType.RPLCrcs || this.offset === 0 || this.data.length === 0) return 0x00000000; let data = this.data; if (this.flags & Enums.SectionFlags.Compressed) data = new Uint8Array(trimBuffer(zlib.inflateSync(this.data.slice(4)))); return Number('0x' + await hashwasm.crc32(data)); })(); } /** Offset from the start of the {@link Header.shstrIndex section headers string table} * to the address of this section's name in said table, if any. */ get nameOffset() { return this._nameOffset; } /** The type of this section. */ get type() { return this._type; } /** The flags for this section. */ get flags() { return this._flags; } /** The virtual address of this section. */ get addr() { return this._addr; } /** The absolute offset of the section in the file. */ get offset() { return this._offset; } /** The size of this section, in bytes. */ get size() { return this.offset === 0 ? this._size : this.data.byteLength; } /** A section linked to this section. For example for a symbol section the * linked section is a string table section providing names for symbols. */ get link() { return this._link; } /** Section type specific info for this section. */ get info() { return this._info; } /** The alignment requirement of this section. */ get addrAlign() { return this._addrAlign; } /** The size of each "entity" in this section, if applicable. * For example, if this is a symbol table section, this is the size of a symbol entry. */ get entSize() { return this._entSize; } set nameOffset(nameOffset) { assert(nameOffset >= 0x00 && 0xFFFFFFFF >= nameOffset, `${nameOffset} does not fit inside a uint32.`); this._nameOffset = nameOffset; } set type(type) { assert(type in Enums.SectionType, `${type} is not a valid ELF.SectionType value.`); this._type = type; } set flags(flags) { assert(flags >= 0x00 && 0xFFFFFFFF >= flags, `${flags} does not fit inside a uint32.`); this._flags = flags; } set addr(addr) { assert(addr >= 0x00 && 0xFFFFFFFF >= addr, `${addr} does not fit inside a uint32.`); this._addr = addr; } set offset(offset) { assert(offset >= 0x00 && 0xFFFFFFFF >= offset, `${offset} does not fit inside a uint32.`); this._offset = offset; } set size(size) { assert(size >= 0x00 && 0xFFFFFFFF >= size, `${size} does not fit inside a uint32.`); if (this.offset === 0) { this._size = size; return; } if (size === this.data.byteLength) return; if (size > this.data.byteLength) this.data = new Uint8Array(trimBuffer(Buffer.concat([new Uint8Array(this.data), new Uint8Array(size - this.data.byteLength)]))); else this.data = new Uint8Array(this.data.slice(0, size)); } set link(link) { assert(link >= 0x00 && 0xFFFFFFFF >= link, `${link} does not fit inside a uint32.`); this._link = link; } set info(info) { assert(info >= 0x00 && 0xFFFFFFFF >= info, `${info} does not fit inside a uint32.`); this._info = info; } set addrAlign(addrAlign) { assert(addrAlign >= 0x00 && 0xFFFFFFFF >= addrAlign, `${addrAlign} does not fit inside a uint32.`); this._addrAlign = addrAlign; } set entSize(entSize) { assert(entSize >= 0x00 && 0xFFFFFFFF >= entSize, `${entSize} does not fit inside a uint32.`); this._entSize = entSize; } } /** A string table section. */ export class StringSection extends Section { get size() { if (this.flags & Enums.SectionFlags.Compressed) return this._data.byteLength; const lastStringOffset = Number(Object.keys(this.strings).sort((a, b) => Number(a) - Number(b)).at(-1)); if (!lastStringOffset) return 0; return lastStringOffset + new TextEncoder().encode(this.strings[lastStringOffset]).byteLength + 1; } get data() { if (this.flags & Enums.SectionFlags.Compressed) return this._data; this._data = new Uint8Array(packStringSection(this, this.size)); return this._data; } set data(data) { this._data = data; } /** The strings parsed from this section in the case of a string table section. */ strings = {}; } /** A symbol table section. */ export class SymbolSection extends Section { get size() { if (this.flags & Enums.SectionFlags.Compressed) return this._data.byteLength; return this.symbols.length * this.entSize; } get data() { if (this.flags & Enums.SectionFlags.Compressed) return this._data; this._data = new Uint8Array(packSymbolSection(this, this.size)); return this._data; } set data(data) { this._data = data; } /** The symbols parsed from this section. */ symbols = []; } /** A relocation table section. */ export class RelocationSection extends Section { get size() { if (this.flags & Enums.SectionFlags.Compressed) return this._data.byteLength; return this._size = this.relocations.length * this.entSize; } get data() { if (this.flags & Enums.SectionFlags.Compressed) return this._data; this._data = new Uint8Array(packRelocationSection(this, this.size)); return this._data; } set data(data) { this._data = data; } /** The relocations parsed from this section. */ relocations = []; } /** RPL-exclusive CRC hashes section. */ export class RPLCrcSection extends Section { } /** RPL-exclusive file information section. */ export class RPLFileInfoSection extends Section { /** The parsed RPL file information. */ fileinfo = new RPLFileInfo(); }