mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-22 14:33:51 +00:00
arm: parse ELF
This commit is contained in:
parent
c0909bef1b
commit
8bdbae36e3
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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
37
src/test/testelfparser.ts
Normal 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);
|
||||
*/
|
||||
});
|
||||
});
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user