mirror of
https://github.com/Noratrieb/jompiler.git
synced 2026-01-14 14:05:01 +01:00
elf
This commit is contained in:
parent
b2ed872ea7
commit
076d8c3d6e
2 changed files with 304 additions and 13 deletions
315
index.js
315
index.js
|
|
@ -333,12 +333,17 @@ function lower(ast) {
|
|||
strategy: every expression returns its result in rax.
|
||||
*/
|
||||
|
||||
function littleEndian16(number) {
|
||||
assertDefined(number);
|
||||
return [number & 0xff, (number >> 8) & 0xff];
|
||||
}
|
||||
function littleEndian32(number) {
|
||||
assertDefined(number);
|
||||
return [
|
||||
number & 0xf,
|
||||
(number >> 8) & 0xf,
|
||||
(number >> 16) & 0xf,
|
||||
(number >> 24) & 0xf,
|
||||
number & 0xff,
|
||||
(number >> 8) & 0xff,
|
||||
(number >> 16) & 0xff,
|
||||
(number >> 24) & 0xff,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -357,10 +362,9 @@ function lower(ast) {
|
|||
}
|
||||
|
||||
class InstBuilder {
|
||||
#out;
|
||||
#stackSize;
|
||||
constructor() {
|
||||
this.#out = new Uint8Array();
|
||||
this.out = new Uint8Array();
|
||||
this.#stackSize = 0;
|
||||
}
|
||||
|
||||
|
|
@ -372,14 +376,19 @@ function lower(ast) {
|
|||
movEaxImm32(imm) {
|
||||
// mov eax, imm
|
||||
this.#append([
|
||||
0xc7,
|
||||
0xC7,
|
||||
modRm(MOD_REG, RM_A, REG_IGNORED),
|
||||
...littleEndian32(imm),
|
||||
]);
|
||||
}
|
||||
|
||||
ret() {
|
||||
// ret ; near return to calling prodecude
|
||||
this.#append([0xc3]);
|
||||
}
|
||||
|
||||
#append(code) {
|
||||
this.#out += code;
|
||||
this.out = Buffer.concat([this.out, new Uint8Array(code)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,20 +435,294 @@ function lower(ast) {
|
|||
}
|
||||
}
|
||||
|
||||
ib.movEaxImm32(0);
|
||||
ib.ret();
|
||||
|
||||
return ib;
|
||||
}
|
||||
|
||||
for (const func of ast) {
|
||||
const ib = codegenFunction(func);
|
||||
class BufferBuilder {
|
||||
constructor() {
|
||||
this.buffer = new Uint8Array();
|
||||
}
|
||||
append(array) {
|
||||
assertDefined(array);
|
||||
this.buffer = Buffer.concat([this.buffer, new Uint8Array(array)]);
|
||||
}
|
||||
get currentPos() {
|
||||
return this.buffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
function compile(input) {
|
||||
function generateObjectFile(funcs) {
|
||||
if (funcs.length !== 1) {
|
||||
throw new Error("bad");
|
||||
}
|
||||
|
||||
const textContent = funcs[0].code;
|
||||
const textRelativeSymbols = [
|
||||
{
|
||||
name: funcs[0].name,
|
||||
offset: 0,
|
||||
size: funcs[0].code.length,
|
||||
},
|
||||
];
|
||||
|
||||
let out = new BufferBuilder();
|
||||
// ident
|
||||
out.append([0x7f, "E".charCodeAt(0), "L".charCodeAt(0), "F".charCodeAt(0)]);
|
||||
out.append([
|
||||
/*ELFCLASS64*/ 2, /*ELFDATA2LSB*/ 1, /*EV_CURRENT*/ 1,
|
||||
/*ELFOSABI_SYSV*/ 0, /*EI_ABIVERSION*/ 0, /*EI_PAD*/ 0, 0, 0, 0, 0, 0, 0,
|
||||
]);
|
||||
|
||||
let shoffRef;
|
||||
let shnumRef;
|
||||
let shstrndxRef;
|
||||
let sectionOffsetRefs = {};
|
||||
|
||||
// type
|
||||
out.append([/*ET_REL*/ 1, 0]);
|
||||
// machine
|
||||
out.append([/*EM_X86_64*/ 62, 0]);
|
||||
// version
|
||||
out.append([/*EV_CURRENT*/ 1, 0, 0, 0]);
|
||||
// entry
|
||||
out.append([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
// phoff
|
||||
out.append([0, 0, 0, 0, 0, 0, 0, 0]); // no ph
|
||||
// shoff
|
||||
shoffRef = out.currentPos;
|
||||
out.append([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
// flags
|
||||
out.append([0, 0, 0, 0]);
|
||||
// ehsize
|
||||
out.append([64, 0]);
|
||||
// phentsize
|
||||
out.append([0, 0]); // no ph
|
||||
// phnum
|
||||
out.append([0, 0]);
|
||||
// shentsize
|
||||
out.append([64, 0]);
|
||||
// shnum
|
||||
shnumRef = out.currentPos;
|
||||
out.append([0, 0]);
|
||||
// shstrndx
|
||||
shstrndxRef = out.currentPos;
|
||||
out.append([0, 0]);
|
||||
|
||||
// Let's write some section headers.
|
||||
|
||||
const shoff = littleEndian32(out.currentPos);
|
||||
out.buffer[shoffRef] = shoff[0];
|
||||
out.buffer[shoffRef + 1] = shoff[1];
|
||||
out.buffer[shoffRef + 2] = shoff[2];
|
||||
out.buffer[shoffRef + 3] = shoff[3];
|
||||
|
||||
class NullTerminatedStringStore {
|
||||
#offsets;
|
||||
constructor() {
|
||||
this.#offsets = new Map();
|
||||
this.out = new BufferBuilder();
|
||||
}
|
||||
pushAndGet(str) {
|
||||
if (this.#offsets.has(str)) {
|
||||
return this.#offsets.get(str);
|
||||
}
|
||||
const offset = this.out.buffer.length;
|
||||
this.#offsets.set(str, offset);
|
||||
this.out.append(new TextEncoder("utf-8").encode(str));
|
||||
this.out.append([0]);
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
const shstrs = new NullTerminatedStringStore();
|
||||
shstrs.pushAndGet("");
|
||||
shstrs.pushAndGet(".shstrtab"); // ensure that this is already present so it doesn't get added afterwards when we already got the length
|
||||
const strs = new NullTerminatedStringStore();
|
||||
strs.pushAndGet("");
|
||||
let sectionCount = 0;
|
||||
|
||||
const writeSectionHeader = (name, sh) => {
|
||||
sectionCount++;
|
||||
const nameIndex = shstrs.pushAndGet(name);
|
||||
out.append([
|
||||
...littleEndian32(nameIndex),
|
||||
...littleEndian32(sh.type),
|
||||
...littleEndian32(sh.flags),
|
||||
...[0, 0, 0, 0], // flag pad
|
||||
...littleEndian32(sh.addr),
|
||||
...[0, 0, 0, 0],
|
||||
]);
|
||||
sectionOffsetRefs[name] = out.currentPos;
|
||||
out.append([
|
||||
...littleEndian32(sh.offset),
|
||||
...[0, 0, 0, 0],
|
||||
...littleEndian32(sh.size),
|
||||
...[0, 0, 0, 0],
|
||||
...littleEndian32(sh.link),
|
||||
...littleEndian32(sh.info),
|
||||
...littleEndian32(sh.addralign),
|
||||
...[0, 0, 0, 0],
|
||||
...littleEndian32(sh.entsize),
|
||||
...[0, 0, 0, 0],
|
||||
]);
|
||||
};
|
||||
|
||||
// null section
|
||||
writeSectionHeader("", {
|
||||
type: 0,
|
||||
flags: 0,
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
size: 0,
|
||||
link: 0,
|
||||
info: 0,
|
||||
addralign: 0,
|
||||
entsize: 0,
|
||||
});
|
||||
|
||||
// text section
|
||||
writeSectionHeader(".text", {
|
||||
type: /*SHT_PROGBITS*/ 1,
|
||||
flags: /*SHF_ALLOC*/ (1 << 1) | /*SHF_EXECINSTR*/ (1 << 2),
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
size: textContent.length,
|
||||
link: 0,
|
||||
info: 0,
|
||||
addralign: 16,
|
||||
entsize: 0,
|
||||
});
|
||||
|
||||
const symtab = new BufferBuilder();
|
||||
const nameToSymIdx = new Map();
|
||||
let symIdx = 0;
|
||||
for (const sym of textRelativeSymbols) {
|
||||
const nameIdx = strs.pushAndGet(sym.name);
|
||||
|
||||
symtab.append([
|
||||
...littleEndian32(nameIdx),
|
||||
/*STT_FUNC*/ 2 | /*STB_GLOBAL*/ (1 << 4),
|
||||
/*STV_DEFAULT*/ 0,
|
||||
/*shndx .text*/ ...littleEndian16(1),
|
||||
/*value*/ ...littleEndian32(sym.offset),
|
||||
...[0, 0, 0, 0],
|
||||
/*size*/ ...littleEndian32(sym.size),
|
||||
...[0, 0, 0, 0],
|
||||
]);
|
||||
nameToSymIdx.set(sym.name, symIdx);
|
||||
symIdx++;
|
||||
}
|
||||
|
||||
console.log(symtab);
|
||||
|
||||
// symtab section
|
||||
const strTableIndex = sectionCount + 1;
|
||||
writeSectionHeader(".symtab", {
|
||||
type: /*SHT_SYMTAB*/ 2,
|
||||
flags: 0,
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
size: symtab.buffer.length,
|
||||
link: strTableIndex,
|
||||
info: 0,
|
||||
addralign: 8,
|
||||
entsize: 24,
|
||||
});
|
||||
|
||||
// strtab section
|
||||
writeSectionHeader(".strtab", {
|
||||
type: /*SHT_STRTAB*/ 3,
|
||||
flags: 0,
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
size: strs.out.buffer.length,
|
||||
link: 0,
|
||||
info: 0,
|
||||
addralign: 1,
|
||||
entsize: 0,
|
||||
});
|
||||
|
||||
const shstrndx = littleEndian32(sectionCount);
|
||||
out.buffer[shstrndxRef] = shstrndx[0];
|
||||
out.buffer[shstrndxRef + 1] = shstrndx[1];
|
||||
|
||||
const totalSectionCount = littleEndian32(sectionCount + 1);
|
||||
out.buffer[shnumRef] = totalSectionCount[0];
|
||||
out.buffer[shnumRef + 1] = totalSectionCount[1];
|
||||
|
||||
// shstrtab section
|
||||
writeSectionHeader(".shstrtab", {
|
||||
type: /*SHT_STRTAB*/ 3,
|
||||
flags: 0,
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
size: shstrs.out.buffer.length,
|
||||
link: 0,
|
||||
info: 0,
|
||||
addralign: 1,
|
||||
entsize: 0,
|
||||
});
|
||||
|
||||
const alignTo = (align) => {
|
||||
assertDefined(align);
|
||||
const up = align - (out.buffer.length % align);
|
||||
out.append(Array(up).fill(0));
|
||||
};
|
||||
|
||||
const patch32 = (baseOffset, value) => {
|
||||
assertDefined(baseOffset, value);
|
||||
const encoded = littleEndian32(value);
|
||||
out.buffer[baseOffset] = encoded[0];
|
||||
out.buffer[baseOffset + 1] = encoded[1];
|
||||
out.buffer[baseOffset + 2] = encoded[2];
|
||||
out.buffer[baseOffset + 3] = encoded[3];
|
||||
};
|
||||
|
||||
alignTo(16);
|
||||
patch32(sectionOffsetRefs[".text"], out.currentPos);
|
||||
out.append(textContent);
|
||||
|
||||
patch32(sectionOffsetRefs[".strtab"], out.currentPos);
|
||||
out.append(strs.out.buffer);
|
||||
|
||||
alignTo(8);
|
||||
patch32(sectionOffsetRefs[".symtab"], out.currentPos);
|
||||
out.append(symtab.buffer);
|
||||
|
||||
patch32(sectionOffsetRefs[".shstrtab"], out.currentPos);
|
||||
out.append(shstrs.out.buffer);
|
||||
|
||||
return out.buffer;
|
||||
}
|
||||
|
||||
const funcs = [];
|
||||
|
||||
for (const func of ast) {
|
||||
const ib = codegenFunction(func);
|
||||
funcs.push({
|
||||
name: func.name.ident,
|
||||
code: ib.out,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(funcs);
|
||||
|
||||
const obj = generateObjectFile(funcs);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
async function compile(input) {
|
||||
const tokens = lex(input);
|
||||
console.log(tokens);
|
||||
const ast = parse(tokens);
|
||||
console.dir(ast, { depth: 20 });
|
||||
lower(ast);
|
||||
const object = lower(ast);
|
||||
|
||||
fs.writeFile("output.o", object);
|
||||
}
|
||||
|
||||
const fileName = process.argv[2];
|
||||
|
|
@ -447,7 +730,7 @@ const input = await fs.readFile(fileName, "utf-8");
|
|||
console.log(input);
|
||||
|
||||
try {
|
||||
compile(input);
|
||||
await compile(input);
|
||||
} catch (e) {
|
||||
if (e instanceof CompilerError) {
|
||||
console.error(e.render(fileName, input));
|
||||
|
|
@ -455,3 +738,9 @@ try {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function assertDefined(...values) {
|
||||
if (values.some((value) => value === undefined || value === null)) {
|
||||
throw new Error(`assertion failed, value undefined or null`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
input.c
4
input.c
|
|
@ -1,4 +1,6 @@
|
|||
//#include<elf.h>
|
||||
|
||||
int main(int argc)
|
||||
{
|
||||
//exit(45);
|
||||
// exit(42);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue