Prototype pie executable

This commit is contained in:
Roman Venediktov 2024-03-14 09:01:50 +01:00
parent 9fa02845cb
commit 8df129b518
2 changed files with 68 additions and 32 deletions

View file

@ -22,7 +22,7 @@ remake_runtime:
copy_to_build: all remake_runtime
mkdir -p $(BUILDDIR)
cp runtime/Std.i runtime/runtime.a stdlib/* $(BUILDDIR)
cp runtime/Std.i runtime/runtime.a stdlib/* src/lamac $(BUILDDIR)
install: all

View file

@ -88,11 +88,21 @@ end
(* We need to know the word size to calculate offsets correctly *)
let word_size = 8
type externality = I (**Internal*) | E (**External*)
type data_kind = F (**Function*) | D (**Data*)
type addressed = A (**Address*) | V (**Value*)
(* We need to distinguish the following operand types: *)
type opnd =
| R of Register.t (* hard register *)
| S of int (* a position on the hardware stack *)
| M of string (* a named memory location *)
| M of
(* a named memory location *)
data_kind
* externality
* addressed
* string
| C of string (* a named constant *)
| L of int (* an immediate operand *)
| I of int * opnd (* an indirect operand with offset *)
@ -104,9 +114,15 @@ type argument_location = Register of opnd | Stack
let rec show_opnd = function
| R r -> Printf.sprintf "R %s" (Register.show r)
| S i -> Printf.sprintf "S %d" i
| M s -> Printf.sprintf "M %s" s
| L i -> Printf.sprintf "L %d" i
| I (i, o) -> Printf.sprintf "I %d %s" i (show_opnd o)
| C s -> Printf.sprintf "C %s" s
| M (e, d, a, s) ->
Printf.sprintf "M %s %s %s %s"
(match e with F -> "Function" | D -> "Data")
(match d with I -> "Internal" | E -> "External")
(match a with A -> "Address" | V -> "Value")
s
(* We need to know the word size to calculate offsets correctly *)
@ -127,15 +143,18 @@ let r13 = R Registers.r13
let r14 = R Registers.r14
let r15 = R Registers.r15
(* Value that could be used to fill unused stack locations *)
let filler = M (D, I, A, "filler")
(* Now x86 instruction (we do not need all of them): *)
type instr =
(* copies a value from the first to the second operand *)
| Mov of opnd * opnd
(* loads an address of the first operand into the second *)
| Lea of opnd * opnd
(* makes a binary operation; note, the first operand *)
(* makes a binary operation; note, the first operand
designates x86 operator, not the source language one *)
| Binop of string * opnd * opnd
(* designates x86 operator, not the source language one *)
(* x86 integer division, see instruction set reference *)
| IDiv of opnd
(* see instruction set reference *)
@ -184,7 +203,10 @@ let show instr =
| S i ->
if i >= 0 then Printf.sprintf "-%d(%%rbp)" (stack_offset i)
else Printf.sprintf "%d(%%rbp)" (stack_offset i)
| M x -> x
| M (_, I, _, s) -> Printf.sprintf "%s(%%rip)" s
| M (F, E, _, s) -> Printf.sprintf "%s@plt(%%rip)" s
| M (D, E, _, s) -> Printf.sprintf "%s@GOTPCREL(%%rip)" s
| C s -> Printf.sprintf "$%s" s
| L i -> Printf.sprintf "$%d" i
| I (0, x) -> Printf.sprintf "(%s)" (opnd x)
| I (n, x) -> Printf.sprintf "%d(%s)" n (opnd x)
@ -207,8 +229,11 @@ let show instr =
| IDiv s1 -> Printf.sprintf "\tidivq\t%s" (opnd s1)
| Binop (op, s1, s2) ->
Printf.sprintf "\t%s\t%s,\t%s" (binop op) (opnd s1) (opnd s2)
| Mov ((M (_, _, A, _) as x), y) | Lea (x, y) ->
(* TODO: It looks like a bad design.
Maybe we should introduce eopnd with the boolean if we referenceing an address but not a value *)
Printf.sprintf "\tleaq\t%s,\t%s" (opnd x) (opnd y)
| Mov (s1, s2) -> Printf.sprintf "\tmovq\t%s,\t%s" (opnd s1) (opnd s2)
| Lea (x, y) -> Printf.sprintf "\tlea\t%s,\t%s" (opnd x) (opnd y)
| Push s -> Printf.sprintf "\tpushq\t%s" (opnd s)
| Pop s -> Printf.sprintf "\tpopq\t%s" (opnd s)
| Ret -> "\tret"
@ -225,7 +250,7 @@ let show instr =
| Sar1 s -> Printf.sprintf "\tsarq\t%s" (opnd s)
| Repmovsl -> Printf.sprintf "\trep movsq\t"
let in_memory = function M _ | S _ | I _ -> true | R _ | L _ -> false
let in_memory = function M _ | S _ | I _ -> true | C _ | R _ | L _ -> false
let big_numeric_literal = function L num -> num > 0xFFFFFFFF | _ -> false
let mov x s =
@ -482,7 +507,7 @@ let compile_call env ?fname nargs tail =
else if aligned then
([], [ Binop ("+", L (word_size * stack_arguments), rsp) ])
else
( [ Push (M "$filler") ],
( [ Push filler ],
[ Binop ("+", L (word_size * (1 + stack_arguments)), rsp) ] )
in
let call env fname =
@ -553,7 +578,7 @@ let compile_call env ?fname nargs tail =
else if aligned then
([], [ Binop ("+", L (word_size * stack_arguments), rsp) ])
else
( [ Push (M "$filler") ],
( [ Push filler ],
[ Binop ("+", L (word_size * (1 + stack_arguments)), rsp) ] )
in
let call env fname = (env, [ Call fname ]) in
@ -609,6 +634,8 @@ let compile cmd env imports code =
| EXTERN name -> (env#register_extern name, [])
| IMPORT _ -> (env, [])
| CLOSURE (name, closure) ->
let ext = if env#is_external name then E else I in
let address = M (F, ext, A, name) in
let l, env = env#allocate in
let env, push_closure_code =
List.fold_left
@ -622,15 +649,15 @@ let compile cmd env imports code =
(1 + List.length closure)
false
in
(env, push_closure_code @ (Mov (M ("$" ^ name), l) :: call_code))
(env, push_closure_code @ mov address l @ call_code)
| CONST n ->
let s, env' = env#allocate in
(env', [ Mov (L (box n), s) ])
| STRING s ->
let s, env = env#string s in
let addr, env = env#string s in
let l, env = env#allocate in
let env, call = compile_call env ~fname:".string" 1 false in
(env, Mov (M ("$" ^ s), l) :: call)
(env, mov addr l @ call)
| LDA x ->
let s, env' = (env#variable x)#allocate in
let s', env'' = env'#allocate in
@ -732,7 +759,7 @@ let compile cmd env imports code =
@ [ Meta "\t.cfi_startproc" ]
@ (if f = cmd#topname then
[
Mov (M "_init", rax);
Mov (M (D, I, V, "_init"), rax);
Binop ("test", rax, rax);
CJmp ("z", "_continue");
Ret;
@ -743,7 +770,7 @@ let compile cmd env imports code =
Call (label "binoperror2");
Ret;
Label "_continue";
Mov (L 1, M "_init");
Mov (L 1, M (D, I, V, "_init"));
]
else [])
@ [
@ -752,13 +779,13 @@ let compile cmd env imports code =
Meta "\t.cfi_offset 5, -8";
Mov (rsp, rbp);
Meta "\t.cfi_def_cfa_register\t5";
Binop ("-", M ("$" ^ env#lsize), rsp);
Binop ("-", C env#lsize, rsp);
Mov (rdi, r12);
Mov (rsi, r13);
Mov (rcx, r14);
Mov (rsp, rdi);
Mov (M "$filler", rsi);
Mov (M ("$" ^ env#allocated_size), rcx);
Mov (filler, rsi);
Mov (C env#allocated_size, rcx);
Repmovsl;
Mov (r12, rdi);
Mov (r13, rsi);
@ -766,11 +793,11 @@ let compile cmd env imports code =
]
@ (if f = "main" then
[
(* Align stack as main function is the only function that could be called without alignment *)
Mov (M "$0xF", rax);
(* Align stack as main function is the only function that could be called without alignment. TODO *)
Mov (L 0xF, rax);
Binop ("test", rsp, rax);
CJmp ("z", "_ALIGNED");
Push (M "$filler");
Push filler;
Label "_ALIGNED";
(* Initialize gc and arguments *)
Push (R Registers.rdi);
@ -863,7 +890,7 @@ let compile cmd env imports code =
| LINE line -> env#gen_line line
| FAIL ((line, col), value) ->
let v, env = if value then (env#peek, env) else env#pop in
let s, env = env#string cmd#get_infile in
let msg_addr, env = env#string cmd#get_infile in
let vr, env = env#allocate in
let sr, env = env#allocate in
let liner, env = env#allocate in
@ -876,7 +903,7 @@ let compile cmd env imports code =
[
Mov (L col, colr);
Mov (L line, liner);
Mov (M ("$" ^ s), sr);
Mov (msg_addr, sr);
Mov (v, vr);
]
@ code )
@ -1032,7 +1059,7 @@ class env prg =
object (self)
inherit SM.indexer prg
val globals = S.empty (* a set of global variables *)
val stringm = M.empty (* a string map *)
val stringm : string M.t = M.empty (* a string map *)
val scount = 0 (* string count *)
val stack_slots = 0 (* maximal number of stack positions *)
val static_size = 0 (* static data size *)
@ -1104,11 +1131,18 @@ class env prg =
(*Printf.printf "Retrieving stack for %s\n" l;*)
M.mem l stackmap
method is_external name = S.mem name externs
(* gets a name for a global variable *)
method loc x =
match x with
| Value.Global name -> M ("global_" ^ name)
| Value.Fun name -> M ("$" ^ name)
| Value.Global name ->
let loc_name = "global_" ^ name in
let ext = if self#is_external name then E else I in
M (D, ext, V, loc_name)
| Value.Fun name ->
let ext = if self#is_external name then E else I in
M (F, ext, A, name)
| Value.Local i -> S i
| Value.Arg i when i < num_of_argument_registers -> argument_registers.(i)
| Value.Arg i -> S (-(i - num_of_argument_registers) - 1)
@ -1178,11 +1212,13 @@ class env prg =
Buffer.contents buf
in
let x = escape x in
try (M.find x stringm, self)
with Not_found ->
let y = Printf.sprintf "string_%d" scount in
let m = M.add x y stringm in
(y, {<scount = scount + 1; stringm = m>})
let name = M.find_opt x stringm in
match name with
| Some name -> (M (D, I, A, name), self)
| None ->
let name = Printf.sprintf "string_%d" scount in
let m = M.add x name stringm in
(M (D, I, A, name), {<scount = scount + 1; stringm = m>})
(* gets number of arguments in the current function *)
method nargs = nargs
@ -1314,7 +1350,7 @@ let build cmd prog =
cmd#dump_file "i" (Interface.gen prog);
let inc = get_std_path () in
let compiler = "gcc" in
let flags = "-no-pie" in
let flags = "-pie" in
match cmd#get_mode with
| `Default ->
let objs = find_objects (fst @@ fst prog) cmd#get_include_paths in