From 62e238a6ef151d56b79e1f076a57463f2e1fb020 Mon Sep 17 00:00:00 2001 From: Quentin Carbonneaux Date: Fri, 25 Mar 2016 14:02:43 -0400 Subject: great renaming campain! --- src/tools/abi.ml | 532 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 src/tools/abi.ml (limited to 'src/tools/abi.ml') diff --git a/src/tools/abi.ml b/src/tools/abi.ml new file mode 100644 index 0000000..d845c74 --- /dev/null +++ b/src/tools/abi.ml @@ -0,0 +1,532 @@ +(* fuzzer *) + +type _ bty = + | Char: int bty + | Short: int bty + | Int: int bty + | Long: int bty + | Float: float bty + | Double: float bty + +type _ sty = + | Field: 'a bty * 'b sty -> ('a * 'b) sty + | Empty: unit sty + +type _ aty = + | Base: 'a bty -> 'a aty + | Struct: 'a sty -> 'a aty + +type anyb = AB: _ bty -> anyb (* kinda boring... *) +type anys = AS: _ sty -> anys +type anya = AA: _ aty -> anya +type testb = TB: 'a bty * 'a -> testb +type testa = TA: 'a aty * 'a -> testa + + +let align a x = + let m = x mod a in + if m <> 0 then x + (a-m) else x + +let btysize: type a. a bty -> int = function + | Char -> 1 + | Short -> 2 + | Int -> 4 + | Long -> 8 + | Float -> 4 + | Double -> 8 + +let btyalign = btysize + +let styempty: type a. a sty -> bool = function + | Field _ -> false + | Empty -> true + +let stysize s = + let rec f: type a. int -> a sty -> int = + fun sz -> function + | Field (b, s) -> + let a = btyalign b in + f (align a sz + btysize b) s + | Empty -> sz in + f 0 s + +let rec styalign: type a. a sty -> int = function + | Field (b, s) -> max (btyalign b) (styalign s) + | Empty -> 1 + + +(* Generate types and test vectors. *) +module Gen = struct + module R = Random + + let init = function + | None -> + let f = open_in "/dev/urandom" in + let seed = + Char.code (input_char f) lsl 8 + + Char.code (input_char f) in + close_in f; + R.init seed; + seed + | Some seed -> + R.init seed; + seed + + let int sz = + let bound = 1 lsl (8 * min sz 3 - 1) in + let i = R.int bound in + if R.bool () then - i else i + + let float () = + let f = R.float 1000. in + if R.bool () then -. f else f + + let testv: type a. a aty -> a = + let tb: type a. a bty -> a = function (* eh, dry... *) + | Float -> float () + | Double -> float () + | Char -> int (btysize Char) + | Short -> int (btysize Short) + | Int -> int (btysize Int) + | Long -> int (btysize Long) in + let rec ts: type a. a sty -> a = function + | Field (b, s) -> (tb b, ts s) + | Empty -> () in + function + | Base b -> tb b + | Struct s -> ts s + + let b () = (* uniform *) + match R.int 6 with + | 0 -> AB Char + | 1 -> AB Short + | 2 -> AB Int + | 3 -> AB Long + | 4 -> AB Float + | _ -> AB Double + + let smax = 5 (* max elements in structs *) + let structp = 0.3 (* odds of having a struct type *) + let amax = 8 (* max function arguments *) + + let s () = + let rec f n = + if n = 0 then AS Empty else + let AB bt = b () in + let AS st = f (n-1) in + AS (Field (bt, st)) in + f (1 + R.int (smax-1)) + + let a () = + if R.float 1.0 > structp then + let AB bt = b () in + AA (Base bt) + else + let AB bt = b () in + let AS st = s () in + AA (Struct (Field (bt, st))) + + let test () = + let AA ty = a () in + let t = testv ty in + TA (ty, t) + + let tests () = + let rec f n = + if n = 0 then [] else + test () :: f (n-1) in + f (R.int amax) + +end + + +(* Code generation for C *) +module OutC = struct + open Printf + + let ctypelong oc name = + let cb: type a. a bty -> unit = function + | Char -> fprintf oc "char" + | Short -> fprintf oc "short" + | Int -> fprintf oc "int" + | Long -> fprintf oc "long" + | Float -> fprintf oc "float" + | Double -> fprintf oc "double" in + let rec cs: type a. int -> a sty -> unit = + fun i -> function + | Field (b, s) -> + cb b; + fprintf oc " f%d; " i; + cs (i+1) s; + | Empty -> () in + function + | Base b -> + cb b; + | Struct s -> + fprintf oc "struct %s { " name; + cs 1 s; + fprintf oc "}"; + () + + let ctype: type a. out_channel -> string -> a aty -> unit = + fun oc name -> function + | Struct _ -> fprintf oc "struct %s" name + | t -> ctypelong oc "" t + + let base: type a. out_channel -> a bty * a -> unit = + fun oc -> function + | Char, i -> fprintf oc "%d" i + | Short, i -> fprintf oc "%d" i + | Int, i -> fprintf oc "%d" i + | Long, i -> fprintf oc "%d" i + | Float, f -> fprintf oc "%ff" f + | Double, f -> fprintf oc "%f" f + + let init oc name (TA (ty, t)) = + let inits s = + let rec f: type a. a sty * a -> unit = function + | Field (b, s), (tb, ts) -> + base oc (b, tb); + fprintf oc ", "; + f (s, ts) + | Empty, () -> () in + fprintf oc "{ "; + f s; + fprintf oc "}"; in + ctype oc name ty; + fprintf oc " %s = " name; + begin match (ty, t) with + | Base b, tb -> base oc (b, tb) + | Struct s, ts -> inits (s, ts) + end; + fprintf oc ";\n"; + () + + let extension = ".c" + + let comment oc s = + fprintf oc "/* %s */\n" s + + let prelude oc = List.iter (fprintf oc "%s\n") + [ "#include " + ; "#include " + ; "" + ; "static void fail(char *chk)" + ; "{" + ; "\tfprintf(stderr, \"fail: checking %s\\n\", chk);" + ; "\tabort();" + ; "}" + ; "" + ] + + let typedef oc name = function + | TA (Struct ts, _) -> + ctypelong oc name (Struct ts); + fprintf oc ";\n"; + | _ -> () + + let check oc name = + let chkbase: type a. string -> a bty * a -> unit = + fun name t -> + fprintf oc "\tif (%s != " name; + base oc t; + fprintf oc ")\n\t\tfail(%S);\n" name; in + function + | TA (Base b, tb) -> chkbase name (b, tb) + | TA (Struct s, ts) -> + let rec f: type a. int -> a sty * a -> unit = + fun i -> function + | Field (b, s), (tb, ts) -> + chkbase (Printf.sprintf "%s.f%d" name i) (b, tb); + f (i+1) (s, ts); + | Empty, () -> () in + f 1 (s, ts) + + let argname i = "arg" ^ string_of_int (i+1) + + let proto oc (TA (tret, _)) args = + ctype oc "ret" tret; + fprintf oc " f("; + let narg = List.length args in + List.iteri (fun i (TA (targ, _)) -> + ctype oc (argname i) targ; + fprintf oc " %s" (argname i); + if i <> narg-1 then + fprintf oc ", "; + ) args; + fprintf oc ")"; + () + + let caller oc ret args = + let narg = List.length args in + prelude oc; + typedef oc "ret" ret; + List.iteri (fun i arg -> + typedef oc (argname i) arg; + ) args; + proto oc ret args; + fprintf oc ";\n\nint main()\n{\n"; + List.iteri (fun i arg -> + fprintf oc "\t"; + init oc (argname i) arg; + ) args; + fprintf oc "\t"; + let TA (tret, _) = ret in + ctype oc "ret" tret; + fprintf oc " ret;\n\n"; + fprintf oc "\tret = f("; + List.iteri (fun i _ -> + fprintf oc "%s" (argname i); + if i <> narg-1 then + fprintf oc ", "; + ) args; + fprintf oc ");\n"; + check oc "ret" ret; + fprintf oc "\n\treturn 0;\n}\n"; + () + + let callee oc ret args = + prelude oc; + typedef oc "ret" ret; + List.iteri (fun i arg -> + typedef oc (argname i) arg; + ) args; + fprintf oc "\n"; + proto oc ret args; + fprintf oc "\n{\n\t"; + init oc "ret" ret; + fprintf oc "\n"; + List.iteri (fun i arg -> + check oc (argname i) arg; + ) args; + fprintf oc "\n\treturn ret;\n}\n"; + () + +end + +(* Code generation for QBE *) +module OutIL = struct + open Printf + + let comment oc s = + fprintf oc "# %s\n" s + + let tmp, lbl = + let next = ref 0 in + (fun () -> incr next; "%t" ^ (string_of_int !next)), + (fun () -> incr next; "@l" ^ (string_of_int !next)) + + let bvalue: type a. a bty * a -> string = function + | Char, i -> sprintf "%d" i + | Short, i -> sprintf "%d" i + | Int, i -> sprintf "%d" i + | Long, i -> sprintf "%d" i + | Float, f -> sprintf "s_%f" f + | Double, f -> sprintf "d_%f" f + + let btype: type a. a bty -> string = function + | Char -> "w" + | Short -> "w" + | Int -> "w" + | Long -> "l" + | Float -> "s" + | Double -> "d" + + let extension = ".ssa" + + let argname i = "arg" ^ string_of_int (i+1) + + let siter oc base s g = + let rec f: type a. int -> int -> a sty * a -> unit = + fun id off -> function + | Field (b, s), (tb, ts) -> + let off = align (btyalign b) off in + let addr = tmp () in + fprintf oc "\t%s =l add %d, %s\n" addr off base; + g id addr (TB (b, tb)); + f (id + 1) (off + btysize b) (s, ts); + | Empty, () -> () in + f 0 0 s + + let bmemtype b = + if AB b = AB Char then "b" else + if AB b = AB Short then "h" else + btype b + + let init oc = function + | TA (Base b, tb) -> bvalue (b, tb) + | TA (Struct s, ts) -> + let base = tmp () in + fprintf oc "\t%s =l alloc%d %d\n" + base (styalign s) (stysize s); + siter oc base (s, ts) + begin fun _ addr (TB (b, tb)) -> + fprintf oc "\tstore%s %s, %s\n" + (bmemtype b) (bvalue (b, tb)) addr; + end; + base + + let check oc id name = + let bcheck = fun id name (b, tb) -> + let tcmp = tmp () in + let nxtl = lbl () in + fprintf oc "\t%s =w ceq%s %s, %s\n" + tcmp (btype b) name (bvalue (b, tb)); + fprintf oc "\tstorew %d, %%failcode\n" id; + fprintf oc "\tjnz %s, %s, @fail\n" tcmp nxtl; + fprintf oc "%s\n" nxtl; in + function + | TA (Base Char, i) -> + let tval = tmp () in + fprintf oc "\t%s =w extsb %s\n" tval name; + bcheck id tval (Int, i) + | TA (Base Short, i) -> + let tval = tmp () in + fprintf oc "\t%s =w extsh %s\n" tval name; + bcheck id tval (Int, i) + | TA (Base b, tb) -> + bcheck id name (b, tb) + | TA (Struct s, ts) -> + siter oc name (s, ts) + begin fun id' addr (TB (b, tb)) -> + let tval = tmp () in + let lsuffix = + if AB b = AB Char then "sb" else + if AB b = AB Short then "sh" else + "" in + fprintf oc "\t%s =%s load%s %s\n" + tval (btype b) lsuffix addr; + bcheck (100*id + id'+1) tval (b, tb); + end; + () + + let ttype name = function + | TA (Base b, _) -> btype b + | TA (Struct _, _) -> ":" ^ name + + let typedef oc name = + let rec f: type a. a sty -> unit = function + | Field (b, s) -> + fprintf oc "%s" (bmemtype b); + if not (styempty s) then + fprintf oc ", "; + f s; + | Empty -> () in + function + | TA (Struct ts, _) -> + fprintf oc "type :%s = { " name; + f ts; + fprintf oc " }\n"; + | _ -> () + + let postlude oc = List.iter (fprintf oc "%s\n") + [ "@fail" + ; "# failure code" + ; "\t%fcode =w loadw %failcode" + ; "\t%f0 =w call $printf(l $failstr, w %fcode)" + ; "\t%f1 =w call $abort()" + ; "\tret 0" + ; "}" + ; "" + ; "data $failstr = { b \"fail on check %d\\n\", b 0 }" + ] + + let caller oc ret args = + let narg = List.length args in + List.iteri (fun i arg -> + typedef oc (argname i) arg; + ) args; + typedef oc "ret" ret; + fprintf oc "\nfunction w $main() {\n"; + fprintf oc "@start\n"; + fprintf oc "\t%%failcode =l alloc4 4\n"; + let targs = List.mapi (fun i arg -> + comment oc ("define argument " ^ (string_of_int (i+1))); + (ttype (argname i) arg, init oc arg) + ) args in + comment oc "call test function"; + fprintf oc "\t%%ret =%s call $f(" (ttype "ret" ret); + List.iteri (fun i (ty, tmp) -> + fprintf oc "%s %s" ty tmp; + if i <> narg-1 then + fprintf oc ", "; + ) targs; + fprintf oc ")\n"; + comment oc "check the return value"; + check oc 0 "%ret" ret; + fprintf oc "\tret 0\n"; + postlude oc; + () + + let callee oc ret args = + let narg = List.length args in + List.iteri (fun i arg -> + typedef oc (argname i) arg; + ) args; + typedef oc "ret" ret; + fprintf oc "\nfunction %s $f(" (ttype "ret" ret); + List.iteri (fun i arg -> + let a = argname i in + fprintf oc "%s %%%s" (ttype a arg) a; + if i <> narg-1 then + fprintf oc ", "; + ) args; + fprintf oc ") {\n"; + fprintf oc "@start\n"; + fprintf oc "\t%%failcode =l alloc4 4\n"; + List.iteri (fun i arg -> + comment oc ("checking argument " ^ (string_of_int (i+1))); + check oc (i+1) ("%" ^ argname i) arg; + ) args; + comment oc "define the return value"; + let rettmp = init oc ret in + fprintf oc "\tret %s\n" rettmp; + postlude oc; + () + +end + + +module type OUT = sig + val extension: string + val comment: out_channel -> string -> unit + val caller: out_channel -> testa -> testa list -> unit + val callee: out_channel -> testa -> testa list -> unit +end + +let _ = + let usage code = + Printf.eprintf "usage: abi.ml [-s SEED] DIR {c,ssa} {c,ssa}\n"; + exit code in + + let outmod = function + | "c" -> (module OutC : OUT) + | "ssa" -> (module OutIL: OUT) + | _ -> usage 1 in + + let seed, dir, mcaller, mcallee = + match Sys.argv with + | [| _; "-s"; seed; dir; caller; callee |] -> + let seed = + try Some (int_of_string seed) with + Failure _ -> usage 1 in + seed, dir, outmod caller, outmod callee + | [| _; dir; caller; callee |] -> + None, dir, outmod caller, outmod callee + | [| _; "-h" |] -> + usage 0 + | _ -> + usage 1 in + + let seed = Gen.init seed in + let tret = Gen.test () in + let targs = Gen.tests () in + let module OCaller = (val mcaller : OUT) in + let module OCallee = (val mcallee : OUT) in + let ocaller = open_out (dir ^ "/caller" ^ OCaller.extension) in + let ocallee = open_out (dir ^ "/callee" ^ OCallee.extension) in + OCaller.comment ocaller (Printf.sprintf "seed %d" seed); + OCallee.comment ocallee (Printf.sprintf "seed %d" seed); + OCaller.caller ocaller tret targs; + OCallee.callee ocallee tret targs; + () -- cgit 1.4.1