129 lines
4.3 KiB
C
129 lines
4.3 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "io.h"
|
|
#include "ir.h"
|
|
#include "lex.h"
|
|
|
|
#define ELF_HEADER_SIZE 0xb0
|
|
|
|
size_t compile(void) {
|
|
var argc, argv, env;
|
|
init(&argc, &argv, &env);
|
|
var a = lit(52);
|
|
var b = lit(10);
|
|
var exit_code = sub(a, b);
|
|
var sys_exit = lit(60);
|
|
var args[2] = { sys_exit, exit_code };
|
|
syscall(2, args);
|
|
return ELF_HEADER_SIZE;
|
|
}
|
|
|
|
static void write_elf(uint64_t entry_point) {
|
|
uint64_t file_len = here;
|
|
|
|
// Hardcoded ELF header for statically-linked position-independent executable.
|
|
// Since we only support Linux amd64 static PIE, there's no need to abstract over this for now.
|
|
uint8_t elf_header[ELF_HEADER_SIZE] = {
|
|
// ELF header
|
|
0x7F, 'E', 'L', 'F', // ELF magic
|
|
2, 1, 1, 3, 0, // 64-bit little-endian Linux, ELF version 1
|
|
0, 0, 0, 0, 0, 0, 0, // padding
|
|
3, 0, 0x3E, 0, 1, 0, 0, 0, // dynamic executable, amd64, ELF version 1 again
|
|
0, 0, 0, 0, 0, 0, 0, 0, // PATCHME: entry point address
|
|
0x40, 0, 0, 0, 0, 0, 0, 0, // program header table offset (immediately after ELF)
|
|
0, 0, 0, 0, 0, 0, 0, 0, // section eader table offset (none)
|
|
0, 0, 0, 0, 0x40, 0, 0x38, 0, // flags (none), header sizes
|
|
2, 0, 0, 0, 0, 0, 0, 0, // 2 segments, no sections
|
|
|
|
// program header segment
|
|
6, 0, 0, 0, 4, 0, 0, 0, // program header segment, readable
|
|
0x40, 0, 0, 0, 0, 0, 0, 0, // immediately after ELF header
|
|
0x40, 0, 0, 0, 0, 0, 0, 0, // virtual address
|
|
0, 0, 0, 0, 0, 0, 0, 0, // physical address
|
|
0x70, 0, 0, 0, 0, 0, 0, 0, // size in file (2 * size of program header)
|
|
0x70, 0, 0, 0, 0, 0, 0, 0, // size in memory
|
|
8, 0, 0, 0, 0, 0, 0, 0, // alignment
|
|
|
|
// executable segment
|
|
1, 0, 0, 0, 5, 0, 0, 0, // loadable segment, readable and executable
|
|
0, 0, 0, 0, 0, 0, 0, 0, // whole file
|
|
0, 0, 0, 0, 0, 0, 0, 0, // virtual address
|
|
0, 0, 0, 0, 0, 0, 0, 0, // physical address
|
|
0, 0, 0, 0, 0, 0, 0, 0, // PATCHME: size in file
|
|
0, 0, 0, 0, 0, 0, 0, 0, // PATCHME: size in memory
|
|
0, 0x10, 0, 0, 0, 0, 0, 0, // alignment (4K)
|
|
};
|
|
uint64_t ep = (uint64_t) entry_point;
|
|
uint64_t fl = (uint64_t) file_len;
|
|
memcpy(&elf_header[0x18], &entry_point, sizeof(uint64_t));
|
|
memcpy(&elf_header[0x98], &file_len, sizeof(uint64_t));
|
|
memcpy(&elf_header[0x98 + sizeof(uint64_t)], &file_len, sizeof(uint64_t));
|
|
|
|
patch(0, elf_header, ELF_HEADER_SIZE);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc != 3) {
|
|
fprintf(stderr, "usage: %s <output file> <source file>\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
open_files(argv[2], argv[1]);
|
|
|
|
struct token tok;
|
|
do {
|
|
tok = lex();
|
|
switch (tok.type) {
|
|
case TOK_NAME:
|
|
fprintf(stdout, "%s\n", tok.data.name);
|
|
break;
|
|
case TOK_LABEL:
|
|
fprintf(stdout, "'%s\n", tok.data.label);
|
|
break;
|
|
case TOK_INTEGER:
|
|
fprintf(stdout, "%zi\n", tok.data.int_);
|
|
break;
|
|
case TOK_STRING:
|
|
fprintf(stdout, "\"%s\"\n", tok.data.string);
|
|
break;
|
|
case TOK_OPEN_GROUP:
|
|
fprintf(stdout, "(\n");
|
|
break;
|
|
case TOK_CLOSE_GROUP:
|
|
fprintf(stdout, ")\n");
|
|
break;
|
|
case TOK_OPEN_BLOCK:
|
|
fprintf(stdout, "{\n");
|
|
break;
|
|
case TOK_CLOSE_BLOCK:
|
|
fprintf(stdout, "}\n");
|
|
break;
|
|
case TOK_TERMINATOR:
|
|
fprintf(stdout, ";\n");
|
|
break;
|
|
case TOK_SEPARATOR:
|
|
fprintf(stdout, ",\n");
|
|
break;
|
|
case TOK_OPERATOR:
|
|
fprintf(stdout, "OP: %i\n", tok.data.op);
|
|
break;
|
|
case TOK_EOF:
|
|
fprintf(stdout, "EOF\n");
|
|
break;
|
|
}
|
|
} while (tok.type != TOK_EOF);
|
|
|
|
reserve(ELF_HEADER_SIZE);
|
|
size_t entry_point = compile();
|
|
write_elf((uint64_t) entry_point);
|
|
|
|
close_files();
|
|
return 0;
|
|
}
|