elflib
Version:
ELF file reader and writer
188 lines (187 loc) • 7.99 kB
JavaScript
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();
}