mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-17 03:29:55 +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 {
|
export class ELFParser {
|
||||||
readonly dataView: DataView;
|
readonly dataView: DataView;
|
||||||
readonly sectionHeaders: ElfSectionHeader[];
|
readonly sectionHeaders: ElfSectionHeader[];
|
||||||
readonly symbolTable: ElfSymbolTableEntry[];
|
readonly symbolTable: ElfSymbolTableEntry[];
|
||||||
entry: number;
|
readonly entry: number;
|
||||||
|
|
||||||
constructor(data: Uint8Array) {
|
constructor(data: Uint8Array) {
|
||||||
this.dataView = new DataView(data.buffer);
|
this.dataView = new DataView(data.buffer);
|
||||||
this.sectionHeaders = [];
|
this.sectionHeaders = [];
|
||||||
this.symbolTable = [];
|
this.symbolTable = [];
|
||||||
this.parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
private parse() {
|
|
||||||
const elfHeader = new DataView(this.dataView.buffer, 0, 52);
|
const elfHeader = new DataView(this.dataView.buffer, 0, 52);
|
||||||
// check magic #
|
// check magic #
|
||||||
const magic = elfHeader.getInt32(0, true);
|
const magic = elfHeader.getInt32(0, true);
|
||||||
|
@ -55,26 +66,19 @@ export class ELFParser {
|
||||||
if (!sectionNameSection) {
|
if (!sectionNameSection) {
|
||||||
throw new Error('Invalid ELF section name table');
|
throw new Error('Invalid ELF section name table');
|
||||||
} else {
|
} else {
|
||||||
const stringTableOffset = sectionNameSection.offset;
|
const sectionNameView = sectionNameSection.contents;
|
||||||
const stringTableSize = sectionNameSection.size;
|
|
||||||
const sectionNameView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
|
|
||||||
for (let i = 0; i < sectionHeaderCount; i++) {
|
for (let i = 0; i < sectionHeaderCount; i++) {
|
||||||
this.sectionHeaders[i].stringView = sectionNameView;
|
this.sectionHeaders[i].stringView = sectionNameView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the string table
|
// Extract the string table
|
||||||
const stringTableSection = this.sectionHeaders.find(
|
const stringTableSection = this.getSection('.strtab', ElfSectionType.STRTAB);
|
||||||
(section) => section.type === ElfSectionType.STRTAB && section.name == '.strtab'
|
|
||||||
);
|
|
||||||
if (stringTableSection) {
|
if (stringTableSection) {
|
||||||
const stringTableOffset = stringTableSection.offset;
|
const stringView = stringTableSection.contents;
|
||||||
const stringTableSize = stringTableSection.size;
|
|
||||||
const stringView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
|
|
||||||
// Find the symbol table section and string table section
|
// Find the symbol table section and string table section
|
||||||
const symbolTableSection = this.sectionHeaders.find(
|
const symbolTableSection = this.getSection('.symtab', ElfSectionType.SYMTAB);
|
||||||
(section) => section.name === '.symtab'
|
console.log('symbolTableSection', symbolTableSection);
|
||||||
);
|
|
||||||
if (symbolTableSection) {
|
if (symbolTableSection) {
|
||||||
// Extract the symbol table
|
// Extract the symbol table
|
||||||
const symbolTableOffset = symbolTableSection.offset;
|
const symbolTableOffset = symbolTableSection.offset;
|
||||||
|
@ -93,6 +97,14 @@ export class ELFParser {
|
||||||
getSymbols(): ElfSymbolTableEntry[] {
|
getSymbols(): ElfSymbolTableEntry[] {
|
||||||
return this.symbolTable;
|
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 {
|
enum ElfSectionType {
|
||||||
|
@ -107,6 +119,12 @@ class ElfSectionHeader {
|
||||||
constructor(readonly dataView: DataView, readonly headerOffset: number) {
|
constructor(readonly dataView: DataView, readonly headerOffset: number) {
|
||||||
this.type = this.dataView.getUint32(this.headerOffset + 0x4, true);
|
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 {
|
get offset(): number {
|
||||||
return this.dataView.getUint32(this.headerOffset + 0x10, true);
|
return this.dataView.getUint32(this.headerOffset + 0x10, true);
|
||||||
}
|
}
|
||||||
|
@ -117,10 +135,10 @@ class ElfSectionHeader {
|
||||||
return this.dataView.getUint32(this.headerOffset + 0x0, true);
|
return this.dataView.getUint32(this.headerOffset + 0x0, true);
|
||||||
}
|
}
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return getUTF8(this.stringView!, this.nameOffset);
|
return getASCII(this.stringView!, this.nameOffset);
|
||||||
}
|
}
|
||||||
get vaddr(): number {
|
get contents(): DataView {
|
||||||
return this.dataView.getUint32(this.headerOffset + 0xc, true);
|
return new DataView(this.dataView.buffer, this.offset, this.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +152,7 @@ class ElfSymbolTableEntry {
|
||||||
return this.dataView.getUint32(this.entryOffset, true);
|
return this.dataView.getUint32(this.entryOffset, true);
|
||||||
}
|
}
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return getUTF8(this.stringView, this.nameOffset);
|
return getASCII(this.stringView, this.nameOffset);
|
||||||
}
|
}
|
||||||
get value(): number {
|
get value(): number {
|
||||||
return this.dataView.getUint32(this.entryOffset + 4, true);
|
return this.dataView.getUint32(this.entryOffset + 4, true);
|
||||||
|
@ -150,10 +168,45 @@ class ElfSymbolTableEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUTF8(view: DataView, offset: number): string {
|
// https://dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf
|
||||||
let str = '';
|
// https://dwarfstd.org/doc/DWARF5.pdf
|
||||||
for (let i = offset; view.getUint8(i) !== 0; i++) {
|
|
||||||
str += String.fromCharCode(view.getUint8(i));
|
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;
|
||||||
}
|
}
|
||||||
return str;
|
|
||||||
}
|
}
|
|
@ -23,6 +23,7 @@ export class MemoryView implements ProjectView {
|
||||||
dumplines;
|
dumplines;
|
||||||
maindiv : HTMLElement;
|
maindiv : HTMLElement;
|
||||||
recreateOnResize = true;
|
recreateOnResize = true;
|
||||||
|
hibits = 0;
|
||||||
totalRows = 0x1400;
|
totalRows = 0x1400;
|
||||||
|
|
||||||
createDiv(parent : HTMLElement) {
|
createDiv(parent : HTMLElement) {
|
||||||
|
@ -101,7 +102,7 @@ export class MemoryView implements ProjectView {
|
||||||
for (var i=0; i<n1; i++) s += ' ';
|
for (var i=0; i<n1; i++) s += ' ';
|
||||||
if (n1 > 8) s += ' ';
|
if (n1 > 8) s += ' ';
|
||||||
for (var i=n1; i<n2; i++) {
|
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 += ' ';
|
if (i==8) s += ' ';
|
||||||
s += ' ' + (typeof read == 'number' ? hex(read,2) : '??');
|
s += ' ' + (typeof read == 'number' ? hex(read,2) : '??');
|
||||||
}
|
}
|
||||||
|
@ -279,7 +280,8 @@ export class MemoryMapView implements ProjectView {
|
||||||
segdiv.click(() => {
|
segdiv.click(() => {
|
||||||
// TODO: what if memory browser does not exist?
|
// TODO: what if memory browser does not exist?
|
||||||
var memview = projectWindows.createOrShow('#memory') as MemoryView;
|
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_START = 0x0;
|
||||||
const RAM_SIZE = 0x100000;
|
const RAM_SIZE = 0x100000;
|
||||||
const ROM_BASE = 0x10000;
|
const ROM_BASE = 0x0;
|
||||||
const ENTRY_POINT = 0x20000;
|
|
||||||
const IO_START = 0x4000000;
|
const IO_START = 0x4000000;
|
||||||
const IO_SIZE = 0x100;
|
const IO_SIZE = 0x100;
|
||||||
const MAX_SERIAL_CHARS = 1000000;
|
const MAX_SERIAL_CHARS = 1000000;
|
||||||
|
@ -78,17 +77,6 @@ export class ARM32Machine extends BasicScanlineMachine
|
||||||
reset() {
|
reset() {
|
||||||
if (this.rom) {
|
if (this.rom) {
|
||||||
this.ram.set(this.rom, this.rombase);
|
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();
|
super.reset();
|
||||||
this.serialOut = [];
|
this.serialOut = [];
|
||||||
|
|
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 { hex } from "../../common/util";
|
||||||
import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes";
|
import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes";
|
||||||
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString, populateExtraFiles } from "../builder";
|
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 params = step.params;
|
||||||
const errors = [];
|
const errors = [];
|
||||||
gatherFiles(step, { mainFilePath: "main.c" });
|
gatherFiles(step, { mainFilePath: "main.c" });
|
||||||
const objpath = "main.bin";
|
const objpath = "main.elf";
|
||||||
const error_fn = tccErrorMatcher(errors, step.path);
|
const error_fn = tccErrorMatcher(errors, step.path);
|
||||||
|
|
||||||
if (staleFiles(step, [objpath])) {
|
if (staleFiles(step, [objpath])) {
|
||||||
|
@ -304,7 +305,7 @@ export async function linkARMTCC(step: BuildStep): Promise<WorkerResult> {
|
||||||
});
|
});
|
||||||
|
|
||||||
var args = ['-L.', '-nostdlib', '-nostdinc',
|
var args = ['-L.', '-nostdlib', '-nostdinc',
|
||||||
'-Wl,--oformat=binary',
|
'-Wl,--oformat=elf32-arm',
|
||||||
//'-Wl,-section-alignment=0x100000',
|
//'-Wl,-section-alignment=0x100000',
|
||||||
'-gdwarf',
|
'-gdwarf',
|
||||||
'-o', objpath];
|
'-o', objpath];
|
||||||
|
@ -335,12 +336,54 @@ export async function linkARMTCC(step: BuildStep): Promise<WorkerResult> {
|
||||||
if (!anyTargetChanged(step, [objpath]))
|
if (!anyTargetChanged(step, [objpath]))
|
||||||
return;
|
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 {
|
return {
|
||||||
output: objout, //.slice(0x34),
|
output: rom, //.slice(0x34),
|
||||||
//listings: listings,
|
//listings: listings,
|
||||||
errors: errors,
|
errors: errors,
|
||||||
//symbolmap: symbolmap,
|
symbolmap: symbolmap,
|
||||||
//segments: segments
|
segments: segments
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user