diff --git a/src/common/elfparser.ts b/src/common/binutils.ts similarity index 61% rename from src/common/elfparser.ts rename to src/common/binutils.ts index 308cf281..c5b96c3f 100644 --- a/src/common/elfparser.ts +++ b/src/common/binutils.ts @@ -1,18 +1,29 @@ + +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[]; - entry: number; + readonly 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); @@ -55,26 +66,19 @@ export class ELFParser { 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); + const sectionNameView = sectionNameSection.contents; 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' - ); + const stringTableSection = this.getSection('.strtab', ElfSectionType.STRTAB); if (stringTableSection) { - const stringTableOffset = stringTableSection.offset; - const stringTableSize = stringTableSection.size; - const stringView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize); + const stringView = stringTableSection.contents; // Find the symbol table section and string table section - const symbolTableSection = this.sectionHeaders.find( - (section) => section.name === '.symtab' - ); + const symbolTableSection = this.getSection('.symtab', ElfSectionType.SYMTAB); + console.log('symbolTableSection', symbolTableSection); if (symbolTableSection) { // Extract the symbol table const symbolTableOffset = symbolTableSection.offset; @@ -93,6 +97,14 @@ export class ELFParser { 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 { @@ -107,6 +119,12 @@ class ElfSectionHeader { 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); } @@ -117,10 +135,10 @@ class ElfSectionHeader { return this.dataView.getUint32(this.headerOffset + 0x0, true); } get name(): string { - return getUTF8(this.stringView!, this.nameOffset); + return getASCII(this.stringView!, this.nameOffset); } - get vaddr(): number { - return this.dataView.getUint32(this.headerOffset + 0xc, true); + get contents(): DataView { + return new DataView(this.dataView.buffer, this.offset, this.size); } } @@ -134,7 +152,7 @@ class ElfSymbolTableEntry { return this.dataView.getUint32(this.entryOffset, true); } get name(): string { - return getUTF8(this.stringView, this.nameOffset); + return getASCII(this.stringView, this.nameOffset); } get value(): number { return this.dataView.getUint32(this.entryOffset + 4, true); @@ -150,10 +168,45 @@ class ElfSymbolTableEntry { } } -function getUTF8(view: DataView, offset: number): string { - let str = ''; - for (let i = offset; view.getUint8(i) !== 0; i++) { - str += String.fromCharCode(view.getUint8(i)); +// 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); } - return str; -} \ No newline at end of file + /* + 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; + } +} diff --git a/src/ide/views/debugviews.ts b/src/ide/views/debugviews.ts index a91e7867..a7a72625 100644 --- a/src/ide/views/debugviews.ts +++ b/src/ide/views/debugviews.ts @@ -23,6 +23,7 @@ export class MemoryView implements ProjectView { dumplines; maindiv : HTMLElement; recreateOnResize = true; + hibits = 0; totalRows = 0x1400; createDiv(parent : HTMLElement) { @@ -101,7 +102,7 @@ export class MemoryView implements ProjectView { for (var i=0; i 8) s += ' '; for (var i=n1; i { // TODO: what if memory browser does not exist? var memview = projectWindows.createOrShow('#memory') as MemoryView; - memview.scrollToAddress(seg.start); + memview.hibits = seg.start & 0xffff0000; + memview.scrollToAddress(seg.start & 0xffff); }); } diff --git a/src/machine/arm32.ts b/src/machine/arm32.ts index f036c25b..c2af5ca3 100644 --- a/src/machine/arm32.ts +++ b/src/machine/arm32.ts @@ -20,8 +20,7 @@ var GBA_KEYCODE_MAP = makeKeycodeMap([ const RAM_START = 0x0; const RAM_SIZE = 0x100000; -const ROM_BASE = 0x10000; -const ENTRY_POINT = 0x20000; +const ROM_BASE = 0x0; const IO_START = 0x4000000; const IO_SIZE = 0x100; const MAX_SERIAL_CHARS = 1000000; @@ -78,18 +77,7 @@ export class ARM32Machine extends BasicScanlineMachine reset() { if (this.rom) { this.ram.set(this.rom, this.rombase); - // set ARM vectors - const obj32 = new Uint32Array(this.ram.buffer); - const start = ENTRY_POINT; - obj32[0] = start; // set reset vector - obj32[1] = start; // set undefined vector - obj32[2] = start; // set swi vector - obj32[3] = start; // set prefetch abort vector - obj32[4] = start; // set data abort vector - obj32[5] = start; // set reserved vector - obj32[6] = start; // set irq vector - obj32[7] = start; // set fiq vector - } + } super.reset(); this.serialOut = []; this.serialIn = []; diff --git a/src/test/testelfparser.ts b/src/test/testelfparser.ts new file mode 100644 index 00000000..22229820 --- /dev/null +++ b/src/test/testelfparser.ts @@ -0,0 +1,37 @@ +import assert from "assert"; +import { DWARFParser, ELFParser } from "../common/binutils"; + +describe('test ELFParser', () => { + + const fs = require('fs'); + const data = fs.readFileSync('./test/exes/arm32.elf'); + const elfParser = new ELFParser(new Uint8Array(data)); + + it('should parse sections and symbols', () => { + /* + elfParser.sectionHeaders.forEach((section, index) => { + console.log('section', index, section.name, section.type, section.vaddr.toString(16), section.size.toString(16)); + }); + elfParser.getSymbols().forEach((symbol, index) => { + console.log('symbol', index, symbol.info, symbol.other, symbol.name, symbol.value.toString(16)); + }); + */ + assert.strictEqual(22, elfParser.sectionHeaders.length); + assert.strictEqual(31, elfParser.getSymbols().length); + assert.ok(elfParser.sectionHeaders.find((section) => section.name === '.text') != null); + assert.ok(elfParser.getSymbols().find((symbol) => symbol.name === 'main') != null); + }); + + it('should parse DWARF info', () => { + const dwarf = new DWARFParser(elfParser); + /* + const info = dwarf.getCompilationUnits()[0]; + assert.ok(info != null); + assert.ok(info!.lineNumberProgram != null); + assert.ok(info!.lineNumberProgram!.length > 0); + assert.ok(info!.lineNumberProgram![0].file != null); + assert.ok(info!.lineNumberProgram![0].file!.name != null); + assert.ok(info!.lineNumberProgram![0].file!.name!.length > 0); + */ + }); +}); diff --git a/src/worker/tools/arm.ts b/src/worker/tools/arm.ts index 5ae12296..0d2ef63d 100644 --- a/src/worker/tools/arm.ts +++ b/src/worker/tools/arm.ts @@ -1,4 +1,5 @@ +import { ELFParser } from "../../common/binutils"; import { hex } from "../../common/util"; import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes"; import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString, populateExtraFiles } from "../builder"; @@ -292,7 +293,7 @@ export async function linkARMTCC(step: BuildStep): Promise { const params = step.params; const errors = []; gatherFiles(step, { mainFilePath: "main.c" }); - const objpath = "main.bin"; + const objpath = "main.elf"; const error_fn = tccErrorMatcher(errors, step.path); if (staleFiles(step, [objpath])) { @@ -304,7 +305,7 @@ export async function linkARMTCC(step: BuildStep): Promise { }); var args = ['-L.', '-nostdlib', '-nostdinc', - '-Wl,--oformat=binary', + '-Wl,--oformat=elf32-arm', //'-Wl,-section-alignment=0x100000', '-gdwarf', '-o', objpath]; @@ -335,12 +336,54 @@ export async function linkARMTCC(step: BuildStep): Promise { if (!anyTargetChanged(step, [objpath])) return; + // parse ELF and create ROM + const elfparser = new ELFParser(objout); + let maxaddr = 0; + elfparser.sectionHeaders.forEach((section, index) => { + maxaddr = Math.max(maxaddr, section.vmaddr + section.size); + }); + let rom = new Uint8Array(maxaddr); + elfparser.sectionHeaders.forEach((section, index) => { + if (section.flags & 0x2) { + let data = objout.slice(section.offset, section.offset + section.size); + console.log(section.name, section.vmaddr.toString(16), data); + rom.set(data, section.vmaddr); + } + }); + // set vectors, entry point etc + const obj32 = new Uint32Array(rom.buffer); + const start = elfparser.entry; + obj32[0] = start; // set reset vector + obj32[1] = start; // set undefined vector + obj32[2] = start; // set swi vector + obj32[3] = start; // set prefetch abort vector + obj32[4] = start; // set data abort vector + obj32[5] = start; // set reserved vector + obj32[6] = start; // set irq vector + obj32[7] = start; // set fiq vector + + let symbolmap = {}; + elfparser.getSymbols().forEach((symbol, index) => { + symbolmap[symbol.name] = symbol.value; + }); + let segments = []; + elfparser.sectionHeaders.forEach((section, index) => { + if ((section.flags & 0x2) && section.size) { + segments.push({ + name: section.name, + start: section.vmaddr, + size: section.size, + type: section.type, + }); + } + }); + return { - output: objout, //.slice(0x34), + output: rom, //.slice(0x34), //listings: listings, errors: errors, - //symbolmap: symbolmap, - //segments: segments + symbolmap: symbolmap, + segments: segments }; } }