Add elf parser
This commit is contained in:
parent
fc671d6ae1
commit
0f2f096f50
3
Makefile
3
Makefile
|
@ -76,7 +76,8 @@ TRANSPILE_ES6_FLAGS=\
|
|||
CORE_FILES=src/const.js src/io.js src/main.js src/lib.js src/fpu.js src/ide.js src/pci.js src/floppy.js src/memory.js\
|
||||
src/dma.js src/pit.js src/vga.js src/ps2.js src/pic.js src/rtc.js src/uart.js src/hpet.js src/acpi.js src/apic.js\
|
||||
src/state.js src/ne2k.js src/virtio.js src/bus.js src/log.js src/config.js\
|
||||
src/cpu.js src/translate.js src/modrm.js src/string.js src/arith.js src/misc_instr.js src/instructions.js src/debug.js
|
||||
src/cpu.js src/translate.js src/modrm.js src/string.js src/arith.js src/misc_instr.js src/instructions.js src/debug.js \
|
||||
src/elf.js
|
||||
LIB_FILES=lib/9p.js lib/filesystem.js lib/jor1k.js lib/marshall.js lib/utf8.js
|
||||
BROWSER_FILES=src/browser/screen.js\
|
||||
src/browser/keyboard.js src/browser/mouse.js src/browser/serial.js\
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"const.js config.js log.js cpu.js debug.js translate.js modrm.js string.js arith.js misc_instr.js instructions.js " +
|
||||
"io.js main.js lib.js ide.js fpu.js pci.js floppy.js " +
|
||||
"memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js apic.js hpet.js " +
|
||||
"ne2k.js state.js virtio.js bus.js";
|
||||
"ne2k.js state.js virtio.js bus.js elf.js";
|
||||
|
||||
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js serial.js lib.js network.js starter.js worker_bus.js";
|
||||
//var LIB_FILES = "esprima.js walk.js";
|
||||
|
|
222
src/elf.js
Normal file
222
src/elf.js
Normal file
|
@ -0,0 +1,222 @@
|
|||
"use strict";
|
||||
|
||||
// A minimal elf parser for loading 32 bit, x86, little endian, executable elf files
|
||||
|
||||
const ELF_MAGIC = 0x464C457F;
|
||||
|
||||
let types = DataView.prototype;
|
||||
let U8 = { size: 1, get: types.getUint8, set: types.setUint8, };
|
||||
let U16 = { size: 2, get: types.getUint16, set: types.setUint16, };
|
||||
let U32 = { size: 4, get: types.getUint32, set: types.setUint32, };
|
||||
let pad = function(size)
|
||||
{
|
||||
return {
|
||||
size,
|
||||
get: offset => -1,
|
||||
};
|
||||
}
|
||||
|
||||
let Header = create_struct([
|
||||
{ magic: U32, },
|
||||
|
||||
{ class: U8, },
|
||||
{ data: U8, },
|
||||
{ version0: U8, },
|
||||
{ osabi: U8, },
|
||||
|
||||
{ abiversion: U8, },
|
||||
{ pad0: pad(7) },
|
||||
|
||||
{ type: U16, },
|
||||
{ machine: U16, },
|
||||
|
||||
{ version1: U32, },
|
||||
{ entry: U32, },
|
||||
{ phoff: U32, },
|
||||
{ shoff: U32, },
|
||||
{ flags: U32, },
|
||||
|
||||
{ ehsize: U16, },
|
||||
{ phentsize: U16, },
|
||||
{ phnum: U16, },
|
||||
{ shentsize: U16, },
|
||||
{ shnum: U16, },
|
||||
{ shstrndx: U16, },
|
||||
]);
|
||||
console.assert(Header.reduce((a, entry) => a + entry.size, 0) === 52);
|
||||
|
||||
let ProgramHeader = create_struct([
|
||||
{ type: U32, },
|
||||
{ offset: U32, },
|
||||
{ vaddr: U32, },
|
||||
{ paddr: U32, },
|
||||
{ filesz: U32, },
|
||||
{ memsz: U32, },
|
||||
{ flags: U32, },
|
||||
{ align: U32, },
|
||||
]);
|
||||
console.assert(ProgramHeader.reduce((a, entry) => a + entry.size, 0) === 32);
|
||||
|
||||
let SectionHeader = create_struct([
|
||||
{ name: U32, },
|
||||
{ type: U32, },
|
||||
{ flags: U32, },
|
||||
{ addr: U32, },
|
||||
{ offset: U32, },
|
||||
{ size: U32, },
|
||||
{ link: U32, },
|
||||
{ info: U32, },
|
||||
{ addralign: U32, },
|
||||
{ entsize: U32, },
|
||||
]);
|
||||
console.assert(SectionHeader.reduce((a, entry) => a + entry.size, 0) === 40);
|
||||
|
||||
|
||||
// From [{ name: type }, ...] to [{ name, type, size, get, set }, ...]
|
||||
function create_struct(struct)
|
||||
{
|
||||
return struct.map(function(entry)
|
||||
{
|
||||
let keys = Object.keys(entry);
|
||||
console.assert(keys.length === 1);
|
||||
let name = keys[0];
|
||||
let type = entry[name];
|
||||
|
||||
console.assert(type.size > 0);
|
||||
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
size: type.size,
|
||||
get: type.get,
|
||||
set: type.set,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {ArrayBuffer} buffer */
|
||||
function read_elf(buffer)
|
||||
{
|
||||
let view = new DataView(buffer);
|
||||
|
||||
let [header, offset] = read_struct(view, Header);
|
||||
console.assert(offset === 52);
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
for(let key in header)
|
||||
{
|
||||
console.log(key + ": 0x" + header[key].toString(16));
|
||||
}
|
||||
console.log(header);
|
||||
}
|
||||
|
||||
console.assert(header.magic === ELF_MAGIC, "Bad magic");
|
||||
console.assert(header.class === 1, "Unimplemented: 64 bit elf");
|
||||
console.assert(header.data === 1, "Unimplemented: big endian");
|
||||
console.assert(header.version0 === 1, "Bad version0");
|
||||
|
||||
// 1, 2, 3, 4 specify whether the object is relocatable, executable,
|
||||
// shared, or core, respectively.
|
||||
console.assert(header.type === 2, "Unimplemented type");
|
||||
|
||||
console.assert(header.version1 === 1, "Bad version1");
|
||||
|
||||
// these are different in 64 bit
|
||||
console.assert(header.ehsize === 52, "Bad header size");
|
||||
console.assert(header.phentsize === 32, "Bad program header size");
|
||||
console.assert(header.shentsize === 40, "Bad section header size");
|
||||
|
||||
let [program_headers, ph_offset] = read_structs(
|
||||
view_slice(view, header.phoff, header.phentsize * header.phnum),
|
||||
ProgramHeader,
|
||||
header.phnum);
|
||||
|
||||
let [sections_headers, sh_offset] = read_structs(
|
||||
view_slice(view, header.shoff, header.shentsize * header.shnum),
|
||||
SectionHeader,
|
||||
header.shnum);
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
console.log("%d program headers:", program_headers.length);
|
||||
for(let program of program_headers)
|
||||
{
|
||||
console.log(
|
||||
"type=%s offset=%s vaddr=%s paddr=%s " +
|
||||
"filesz=%s memsz=%s flags=%s align=%s",
|
||||
program.type.toString(16),
|
||||
program.offset.toString(16),
|
||||
program.vaddr.toString(16),
|
||||
program.paddr.toString(16),
|
||||
program.filesz.toString(16),
|
||||
program.memsz.toString(16),
|
||||
program.flags.toString(16),
|
||||
program.align.toString(16)
|
||||
);
|
||||
}
|
||||
|
||||
console.log("%d program headers:", sections_headers.length);
|
||||
for(let section of sections_headers)
|
||||
{
|
||||
console.log(
|
||||
"name=%s type=%s flags=%s addr=%s offset=%s " +
|
||||
"size=%s link=%s info=%s addralign=%s entsize=%s",
|
||||
section.name.toString(16),
|
||||
section.type.toString(16),
|
||||
section.flags.toString(16),
|
||||
section.addr.toString(16),
|
||||
section.offset.toString(16),
|
||||
section.size.toString(16),
|
||||
section.link.toString(16),
|
||||
section.info.toString(16),
|
||||
section.addralign.toString(16),
|
||||
section.entsize.toString(16)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
header,
|
||||
program_headers,
|
||||
sections_headers,
|
||||
};
|
||||
}
|
||||
|
||||
function read_struct(view, Struct)
|
||||
{
|
||||
let result = {};
|
||||
let offset = 0;
|
||||
const LITTLE_ENDIAN = true; // big endian not supported yet
|
||||
|
||||
for(let entry of Struct)
|
||||
{
|
||||
let value = entry.get.call(view, offset, LITTLE_ENDIAN);
|
||||
console.assert(result[entry.name] === undefined);
|
||||
result[entry.name] = value;
|
||||
offset += entry.size;
|
||||
}
|
||||
|
||||
return [result, offset];
|
||||
}
|
||||
|
||||
function read_structs(view, Struct, count)
|
||||
{
|
||||
let result = [];
|
||||
let offset = 0;
|
||||
|
||||
for(var i = 0; i < count; i++)
|
||||
{
|
||||
let [s, size] = read_struct(view_slice(view, offset), Struct);
|
||||
result.push(s);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return [result, offset];
|
||||
}
|
||||
|
||||
/** @param {number=} length */
|
||||
function view_slice(view, offset, length)
|
||||
{
|
||||
return new DataView(view.buffer, view.byteOffset + offset, length);
|
||||
}
|
Loading…
Reference in a new issue