159 lines
5.7 KiB
TypeScript
159 lines
5.7 KiB
TypeScript
|
|
export class ELFParser {
|
|
readonly dataView: DataView;
|
|
readonly sectionHeaders: ElfSectionHeader[];
|
|
readonly symbolTable: ElfSymbolTableEntry[];
|
|
entry: number;
|
|
|
|
constructor(data: Uint8Array) {
|
|
this.dataView = new DataView(data.buffer);
|
|
this.sectionHeaders = [];
|
|
this.symbolTable = [];
|
|
this.parse();
|
|
}
|
|
|
|
private parse() {
|
|
const elfHeader = new DataView(this.dataView.buffer, 0, 52);
|
|
// check magic #
|
|
const magic = elfHeader.getInt32(0, true);
|
|
if (magic !== 0x464c457f) {
|
|
throw new Error('Invalid ELF header');
|
|
}
|
|
// only 32 bit supported
|
|
if (elfHeader.getUint8(4) !== 1) {
|
|
throw new Error('Only 32-bit ELF supported');
|
|
}
|
|
// check version = 1
|
|
if (elfHeader.getUint8(6) !== 1) {
|
|
throw new Error('Invalid ELF version');
|
|
}
|
|
// get endianness
|
|
const endian = elfHeader.getUint8(5) === 1;
|
|
if (!endian) {
|
|
throw new Error('Big endian not supported');
|
|
}
|
|
// get entryPoint
|
|
this.entry = elfHeader.getUint32(24, endian);
|
|
|
|
// Parse ELF header and extract section header offset
|
|
const sectionHeaderOffset = this.dataView.getUint32(32, endian);
|
|
// get section header size
|
|
const sectionHeaderSize = this.dataView.getUint16(46, endian);
|
|
// get # of section headers
|
|
const sectionHeaderCount = this.dataView.getUint16(48, endian);
|
|
// get index of section with names
|
|
const sectionNameIndex = this.dataView.getUint16(50, endian);
|
|
|
|
// Parse section headers
|
|
for (let i = 0; i < sectionHeaderCount; i++) {
|
|
const offset = sectionHeaderOffset + i * sectionHeaderSize; // Each section header is 40 bytes
|
|
//const sectionView = new DataView(this.dataView.buffer, offset, sectionHeaderSize);
|
|
const section = new ElfSectionHeader(this.dataView, offset);
|
|
this.sectionHeaders.push(section);
|
|
}
|
|
const sectionNameSection = this.sectionHeaders[sectionNameIndex];
|
|
if (!sectionNameSection) {
|
|
throw new Error('Invalid ELF section name table');
|
|
} else {
|
|
const stringTableOffset = sectionNameSection.offset;
|
|
const stringTableSize = sectionNameSection.size;
|
|
const sectionNameView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
|
|
for (let i = 0; i < sectionHeaderCount; i++) {
|
|
this.sectionHeaders[i].stringView = sectionNameView;
|
|
}
|
|
}
|
|
|
|
// Extract the string table
|
|
const stringTableSection = this.sectionHeaders.find(
|
|
(section) => section.type === ElfSectionType.STRTAB && section.name == '.strtab'
|
|
);
|
|
if (stringTableSection) {
|
|
const stringTableOffset = stringTableSection.offset;
|
|
const stringTableSize = stringTableSection.size;
|
|
const stringView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
|
|
// Find the symbol table section and string table section
|
|
const symbolTableSection = this.sectionHeaders.find(
|
|
(section) => section.name === '.symtab'
|
|
);
|
|
if (symbolTableSection) {
|
|
// Extract the symbol table
|
|
const symbolTableOffset = symbolTableSection.offset;
|
|
const symbolTableSize = symbolTableSection.size;
|
|
const symbolTableEntryCount = symbolTableSize / 16;
|
|
//const symbolTable = new DataView(this.dataView.buffer, symbolTableOffset, symbolTableSize);
|
|
for (let i = 0; i < symbolTableEntryCount; i++) {
|
|
const offset = symbolTableOffset + i * 16;
|
|
const entry = new ElfSymbolTableEntry(this.dataView, offset, stringView);
|
|
this.symbolTable.push(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getSymbols(): ElfSymbolTableEntry[] {
|
|
return this.symbolTable;
|
|
}
|
|
}
|
|
|
|
enum ElfSectionType {
|
|
SYMTAB = 2,
|
|
STRTAB = 3,
|
|
}
|
|
|
|
class ElfSectionHeader {
|
|
readonly type: number;
|
|
stringView: DataView | null = null;
|
|
|
|
constructor(readonly dataView: DataView, readonly headerOffset: number) {
|
|
this.type = this.dataView.getUint32(this.headerOffset + 0x4, true);
|
|
}
|
|
get offset(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0x10, true);
|
|
}
|
|
get size(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0x14, true);
|
|
}
|
|
get nameOffset(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0x0, true);
|
|
}
|
|
get name(): string {
|
|
return getUTF8(this.stringView!, this.nameOffset);
|
|
}
|
|
get vaddr(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0xc, true);
|
|
}
|
|
}
|
|
|
|
class ElfSymbolTableEntry {
|
|
constructor(readonly dataView: DataView,
|
|
readonly entryOffset: number,
|
|
readonly stringView: DataView) {
|
|
}
|
|
|
|
get nameOffset(): number {
|
|
return this.dataView.getUint32(this.entryOffset, true);
|
|
}
|
|
get name(): string {
|
|
return getUTF8(this.stringView, this.nameOffset);
|
|
}
|
|
get value(): number {
|
|
return this.dataView.getUint32(this.entryOffset + 4, true);
|
|
}
|
|
get size(): number {
|
|
return this.dataView.getUint32(this.entryOffset + 8, true);
|
|
}
|
|
get info(): number {
|
|
return this.dataView.getUint8(this.entryOffset + 12);
|
|
}
|
|
get other(): number {
|
|
return this.dataView.getUint8(this.entryOffset + 13);
|
|
}
|
|
}
|
|
|
|
function getUTF8(view: DataView, offset: number): string {
|
|
let str = '';
|
|
for (let i = offset; view.getUint8(i) !== 0; i++) {
|
|
str += String.fromCharCode(view.getUint8(i));
|
|
}
|
|
return str;
|
|
} |