An --x86 host reloads an --x86 module, checked by running it
This commit is contained in:
parent
69ea66deca
commit
bd69f684ed
26
lib/x86.ml
26
lib/x86.ml
@ -611,9 +611,8 @@ let addr_into f ~reg (l : loc) =
|
|||||||
load_int f.b ~dst:reg ~mm:(Frame p) ~size:8 ~signed:false;
|
load_int f.b ~dst:reg ~mm:(Frame p) ~size:8 ~signed:false;
|
||||||
if a <> 0 then add_imm f.b ~dst:reg a
|
if a <> 0 then add_imm f.b ~dst:reg a
|
||||||
|
|
||||||
(* The three spellings of "name a symbol", each picking the pc-relative form
|
(* The spellings of "name a symbol", each picking the pc-relative form
|
||||||
for a symbol this object defines and the GOT form for one it does not. *)
|
for a symbol this object defines and the GOT form for one it does not. *)
|
||||||
let sym_mem f s = if f.ext s then Got s else Sym (s, 0)
|
|
||||||
let sym_loc f s = if f.ext s then Lgot (s, 0) else Lg (s, 0)
|
let sym_loc f s = if f.ext s then Lgot (s, 0) else Lg (s, 0)
|
||||||
|
|
||||||
(* [lea] of a symbol is an address; out of the GOT the address is already
|
(* [lea] of a symbol is an address; out of the GOT the address is already
|
||||||
@ -622,6 +621,19 @@ let addr_sym f ~dst s =
|
|||||||
if f.ext s then load_int f.b ~dst ~mm:(Got s) ~size:8 ~signed:false
|
if f.ext s then load_int f.b ~dst ~mm:(Got s) ~size:8 ~signed:false
|
||||||
else lea f.b ~dst ~mm:(Sym (s, 0))
|
else lea f.b ~dst ~mm:(Sym (s, 0))
|
||||||
|
|
||||||
|
(* Read what a global holds, as opposed to where it is -- an indirection cell
|
||||||
|
is the only caller. Out of the GOT that is two loads, not one: the slot
|
||||||
|
holds the cell's *address*. Collapsing them was this lane's one real bug,
|
||||||
|
and it looked exactly right in the disassembly: [mov r11, cell@GOTPCREL(%rip)]
|
||||||
|
beside [call *%r11] reads as "call through the cell" and in fact calls the
|
||||||
|
cell. DISCUSS.md item 15 said this is how hand-encoding fails. *)
|
||||||
|
let load_sym f ~dst s =
|
||||||
|
if f.ext s then begin
|
||||||
|
load_int f.b ~dst ~mm:(Got s) ~size:8 ~signed:false;
|
||||||
|
load_int f.b ~dst ~mm:(Reg (dst, 0)) ~size:8 ~signed:false
|
||||||
|
end
|
||||||
|
else load_int f.b ~dst ~mm:(Sym (s, 0)) ~size:8 ~signed:false
|
||||||
|
|
||||||
let scalar_size f (t : Types.t) =
|
let scalar_size f (t : Types.t) =
|
||||||
match t with Types.Bool -> 1 | _ -> max 1 (sizeof f.md t)
|
match t with Types.Bool -> 1 | _ -> max 1 (sizeof f.md t)
|
||||||
|
|
||||||
@ -1005,7 +1017,7 @@ and lower_at f (e : Tast.expr) (dst : loc) : unit =
|
|||||||
address is in a slot there is nothing left to re-resolve. *)
|
address is in a slot there is nothing left to re-resolve. *)
|
||||||
| Tast.FnAddr (Tast.Fnval n) ->
|
| Tast.FnAddr (Tast.Fnval n) ->
|
||||||
if f.md.Emit.dev then
|
if f.md.Emit.dev then
|
||||||
load_int f.b ~dst:rax ~mm:(sym_mem f (csym n)) ~size:8 ~signed:false
|
load_sym f ~dst:rax (csym n)
|
||||||
else addr_sym f ~dst:rax (fsym n);
|
else addr_sym f ~dst:rax (fsym n);
|
||||||
store_int f.b ~src:rax ~mm:(lmem f dst ~scratch:r11) ~size:8
|
store_int f.b ~src:rax ~mm:(lmem f dst ~scratch:r11) ~size:8
|
||||||
| Tast.FnAddr (Tast.Rtfn n) ->
|
| Tast.FnAddr (Tast.Rtfn n) ->
|
||||||
@ -1816,7 +1828,7 @@ and call_flan f ~target ~args ~rty dst =
|
|||||||
(match callee with
|
(match callee with
|
||||||
| `Sym s -> call_sym f.b s
|
| `Sym s -> call_sym f.b s
|
||||||
| `Cell s ->
|
| `Cell s ->
|
||||||
load_int f.b ~dst:r11 ~mm:(sym_mem f s) ~size:8 ~signed:false;
|
load_sym f ~dst:r11 s;
|
||||||
call_r f.b r11
|
call_r f.b r11
|
||||||
| `Loc o ->
|
| `Loc o ->
|
||||||
load_int f.b ~dst:r11 ~mm:(Frame o) ~size:8 ~signed:false;
|
load_int f.b ~dst:r11 ~mm:(Frame o) ~size:8 ~signed:false;
|
||||||
@ -2607,7 +2619,11 @@ let program ~checks ?(dev = false) (p : Tast.program) : string =
|
|||||||
Buffer.add_string rodata gr;
|
Buffer.add_string rodata gr;
|
||||||
(match List.find_opt (fun (f : Tast.fn) -> f.Tast.name = "main") p.Tast.fns with
|
(match List.find_opt (fun (f : Tast.fn) -> f.Tast.name = "main") p.Tast.fns with
|
||||||
| Some fn -> Buffer.add_string text (emit_main md fn)
|
| Some fn -> Buffer.add_string text (emit_main md fn)
|
||||||
| None -> unsupported "no main");
|
(* No [main] is not an error, and [emit.ml] treats it the same way: a
|
||||||
|
program can be linked against a C host that brings its own entry point,
|
||||||
|
which is what [reload_host.c] is. Refusing here made a --x86 host for the
|
||||||
|
reload tests impossible to build. *)
|
||||||
|
| None -> ());
|
||||||
let out = Buffer.create 65536 in
|
let out = Buffer.create 65536 in
|
||||||
Buffer.add_buffer out text;
|
Buffer.add_buffer out text;
|
||||||
(* The globals' initialiser runs before main, through the same constructor
|
(* The globals' initialiser runs before main, through the same constructor
|
||||||
|
|||||||
@ -200,6 +200,58 @@ let () =
|
|||||||
if code <> 0 || text <> want then
|
if code <> 0 || text <> want then
|
||||||
fail "reload\n got: %S (exit %d)\n wanted: %S" text code want;
|
fail "reload\n got: %S (exit %d)\n wanted: %S" text code want;
|
||||||
|
|
||||||
|
(* The same thing again, compiled by the dev backend end to end (x86.ml's
|
||||||
|
header, HANDOFF-x86-rt.md item 1). Both halves, host and module, because
|
||||||
|
the two backends' conventions agree on every scalar and disagree on
|
||||||
|
every aggregate: an LLVM-built module dlopened into an --x86 host would
|
||||||
|
be correct until the first redefined function took or returned a struct.
|
||||||
|
So an --x86 host gets --x86 modules and the two never meet.
|
||||||
|
|
||||||
|
Only v1 and v2, which is the whole of what X86.redefinition compiles:
|
||||||
|
every name they touch is one the host was built with. v3 and v4
|
||||||
|
introduce a function and a global at run time, and the flan_dev_cell /
|
||||||
|
flan_dev_global lookups that needs are refused there by name.
|
||||||
|
|
||||||
|
Read by running, not by reading. A disassembly reads correctly beside a
|
||||||
|
wrong answer often enough (DISCUSS.md item 15) that only the printed
|
||||||
|
transcript settles it: [outer] is compiled once into the host and never
|
||||||
|
rebuilt, so "after2 1204" can only mean its call site followed a body
|
||||||
|
that this backend emitted, published through a cell it reached via the
|
||||||
|
GOT. *)
|
||||||
|
let x86 = { dev with Build.x86 = true } in
|
||||||
|
let xhost = tmp "xhost" in
|
||||||
|
ignore
|
||||||
|
(Build.executable ~opts:x86 ~csrcs:[ "reload_host.c" ]
|
||||||
|
~lflags:[ "-ldl" ] p1 ~out:xhost);
|
||||||
|
let xmodule q fns name =
|
||||||
|
let o = tmp name in
|
||||||
|
let asm = X86.redefinition ~checks:true ~dev:true ~known q ~fns in
|
||||||
|
ignore (Build.shared_x86 ~opts:x86 ~asm ~out:o ());
|
||||||
|
o
|
||||||
|
in
|
||||||
|
let xso1 = xmodule p1 [ "bump" ] "xv1.so" in
|
||||||
|
let xso2 = xmodule p2 [ "bump" ] "xv2.so" in
|
||||||
|
let xout = tmp "xout" in
|
||||||
|
let xcode =
|
||||||
|
Sys.command
|
||||||
|
(Printf.sprintf "%s %s %s > %s 2> %s" (Filename.quote xhost)
|
||||||
|
(Filename.quote xso1) (Filename.quote xso2) (Filename.quote xout)
|
||||||
|
(Filename.quote (tmp "xerr")))
|
||||||
|
in
|
||||||
|
let xtext = In_channel.with_open_bin xout In_channel.input_all in
|
||||||
|
let xwant =
|
||||||
|
"v1\nhost 2\nv1\nafter1 4\n" ^ v2s ^ "after2 1204\ncounter 102\n"
|
||||||
|
in
|
||||||
|
if xcode <> 0 || xtext <> xwant then
|
||||||
|
fail "x86 reload\n got: %S (exit %d)\n wanted: %S" xtext xcode
|
||||||
|
xwant;
|
||||||
|
(* A name the host was never built with has no symbol to bind to, and the
|
||||||
|
registry path is not built here. It has to refuse rather than emit
|
||||||
|
something that links and then stores through a null. *)
|
||||||
|
(match X86.redefinition ~checks:true ~dev:true ~known p3 ~fns:[ "added" ] with
|
||||||
|
| _ -> fail "x86 redefinition accepted a name the host does not have"
|
||||||
|
| exception X86.Unsupported _ -> ());
|
||||||
|
|
||||||
(* The layout-drift guard, which needs a process of its own because what it
|
(* The layout-drift guard, which needs a process of its own because what it
|
||||||
does is abort one. [extra] does not exist in the host: v3 introduced it
|
does is abort one. [extra] does not exist in the host: v3 introduced it
|
||||||
at run time, so flan_dev.c allocated its storage and recorded its size,
|
at run time, so flan_dev.c allocated its storage and recorded its size,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user