233 lines
8.5 KiB
TypeScript
233 lines
8.5 KiB
TypeScript
/*
|
|
* Copyright (c) 2024 Steven E. Hugg
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
function getASCII(view: DataView, offset: number): string {
|
|
let s = '';
|
|
let i = offset;
|
|
while (view.getUint8(i) !== 0) {
|
|
s += String.fromCharCode(view.getUint8(i));
|
|
i++;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// https://blog.k3170makan.com/2018/09/introduction-to-elf-format-elf-header.html
|
|
// https://dwarfstd.org/doc/DWARF5.pdf
|
|
|
|
export class ELFParser {
|
|
readonly dataView: DataView;
|
|
readonly sectionHeaders: ElfSectionHeader[];
|
|
readonly symbolTable: ElfSymbolTableEntry[];
|
|
readonly entry: number;
|
|
|
|
constructor(data: Uint8Array) {
|
|
this.dataView = new DataView(data.buffer);
|
|
this.sectionHeaders = [];
|
|
this.symbolTable = [];
|
|
|
|
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 sectionNameView = sectionNameSection.contents;
|
|
for (let i = 0; i < sectionHeaderCount; i++) {
|
|
this.sectionHeaders[i].stringView = sectionNameView;
|
|
}
|
|
}
|
|
|
|
// Extract the string table
|
|
const stringTableSection = this.getSection('.strtab', ElfSectionType.STRTAB);
|
|
if (stringTableSection) {
|
|
const stringView = stringTableSection.contents;
|
|
// Find the symbol table section and string table section
|
|
const symbolTableSection = this.getSection('.symtab', ElfSectionType.SYMTAB);
|
|
console.log('symbolTableSection', symbolTableSection);
|
|
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;
|
|
}
|
|
|
|
getSection(name: string, type?: number): ElfSectionHeader | null {
|
|
if (typeof type === 'number') {
|
|
return this.sectionHeaders.find((section) => section.name === name && section.type === type) || null;
|
|
} else {
|
|
return this.sectionHeaders.find((section) => section.name === name) || null;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 flags(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0x8, true);
|
|
}
|
|
get vmaddr(): number {
|
|
return this.dataView.getUint32(this.headerOffset + 0xc, 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 getASCII(this.stringView!, this.nameOffset);
|
|
}
|
|
get contents(): DataView {
|
|
return new DataView(this.dataView.buffer, this.offset, this.size);
|
|
}
|
|
}
|
|
|
|
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 getASCII(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);
|
|
}
|
|
}
|
|
|
|
// https://dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf
|
|
// https://dwarfstd.org/doc/DWARF5.pdf
|
|
|
|
export class DWARFParser {
|
|
|
|
invo: DWARFDebugInfo;
|
|
abbrev: ElfSectionHeader;
|
|
line: ElfSectionHeader;
|
|
str: ElfSectionHeader;
|
|
line_str: ElfSectionHeader;
|
|
aranges: ElfSectionHeader;
|
|
|
|
constructor(readonly elf: ELFParser) {
|
|
// fetch sections
|
|
this.invo = new DWARFDebugInfo(elf.getSection('.debug_info'));
|
|
this.abbrev = elf.getSection('.debug_abbrev', ElfSectionType.STRTAB);
|
|
this.line = elf.getSection('.debug_line', ElfSectionType.STRTAB);
|
|
this.str = elf.getSection('.debug_str', ElfSectionType.STRTAB);
|
|
this.line_str = elf.getSection('.debug_line_str', ElfSectionType.STRTAB);
|
|
this.aranges = elf.getSection('.debug_aranges', ElfSectionType.STRTAB);
|
|
}
|
|
/*
|
|
getCompilationUnits(): DWARFCompilationUnit[] {
|
|
const compilationUnits: DWARFCompilationUnit[] = [];
|
|
let offset = this.debugInfoSection.offset;
|
|
while (offset < this.debugInfoSection.offset + this.debugInfoSection.size) {
|
|
const compilationUnit = new DWARFCompilationUnit(this.debugInfoSection.contents, offset);
|
|
compilationUnits.push(compilationUnit);
|
|
offset += compilationUnit.size;
|
|
}
|
|
return compilationUnits;
|
|
}
|
|
*/
|
|
}
|
|
|
|
class DWARFDebugInfo {
|
|
contents: DataView;
|
|
|
|
constructor(readonly section: ElfSectionHeader) {
|
|
this.contents = section.contents;
|
|
}
|
|
}
|