arm: parse ELF

This commit is contained in:
Steven Hugg 2023-12-28 17:40:24 -05:00
parent c0909bef1b
commit 8bdbae36e3
5 changed files with 170 additions and 47 deletions

View File

@ -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;
}
/*
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;
}
}

View File

@ -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<n1; i++) s += ' ';
if (n1 > 8) s += ' ';
for (var i=n1; i<n2; i++) {
var read = this.readAddress(offset+i);
var read = this.readAddress((offset+i) | this.hibits);
if (i==8) s += ' ';
s += ' ' + (typeof read == 'number' ? hex(read,2) : '??');
}
@ -279,7 +280,8 @@ export class MemoryMapView implements ProjectView {
segdiv.click(() => {
// 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);
});
}

View File

@ -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 = [];

37
src/test/testelfparser.ts Normal file
View File

@ -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);
*/
});
});

View File

@ -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<WorkerResult> {
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<WorkerResult> {
});
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<WorkerResult> {
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
};
}
}