8bitworkshop/src/common/elfparser.ts

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;
}