8bitworkshop/src/common/binutils.ts

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