aboutsummaryrefslogtreecommitdiffstats
path: root/test/abi/generator.ml
blob: 529352e081043bbdcffe3c52f5de324c02ab436f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
open Printf

type ty =
  | Int8u  | Int8s
  | Int16u | Int16s
  | Int32
  | Int64
  | Float32
  | Float64
  | String
  | Struct of int * (string * ty) list

type funsig = {
    args: ty list;
    varargs: ty list;    (* empty list if fixed-argument function *)
    res: ty option
  }

type value =
  | VInt of int
  | VInt32 of int32
  | VInt64 of int64
  | VFloat of float
  | VString of string
  | VStruct of value list

(* Print a value.  If [norm] is true, re-normalize values of
   small numerical types. *)

let zero_ext n k =
  n land ((1 lsl k) - 1)

let sign_ext n k =
  (n lsl (Sys.int_size - k)) asr (Sys.int_size - k)

let normalize_float32 n =
  Int32.float_of_bits (Int32.bits_of_float n)

let rec print_value ~norm oc (ty, v) =
  match (ty, v) with
  | (Int8u, VInt n) ->
      fprintf oc "%d" (if norm then zero_ext n 8 else n)
  | (Int8s, VInt n) ->
      fprintf oc "%d" (if norm then sign_ext n 8 else n)
  | (Int16u, VInt n) ->
      fprintf oc "%d" (if norm then zero_ext n 16 else n)
  | (Int16s, VInt n) ->
      fprintf oc "%d" (if norm then sign_ext n 16 else n)
  | (Int32, VInt32 n) ->
      fprintf oc "%ld" n
  | (Int64, VInt64 n) ->
      fprintf oc "%Ld" n
  | (Float32, VFloat f) ->
      if norm
      then fprintf oc "%hF" (normalize_float32 f)
      else fprintf oc "%h" f
  | (Float64, VFloat f) ->
      fprintf oc "%h" f
  | (String, VString s) ->
      fprintf oc "%S" s
  | (Struct(id, (fld1, ty1) :: members), VStruct (v1 :: vl)) ->
      fprintf oc "(struct s%d){" id;
      print_value ~norm oc (ty1, v1);
      List.iter2
        (fun (fld, ty) v -> fprintf oc ", %a" (print_value ~norm) (ty, v))
        members vl;
      fprintf oc "}"
  | _, _ ->
      assert false

(* Generate random values of the given type *)

let random_char () = Char.chr (Char.code 'a' + Random.int 26)

let random_string () =
  let len = Random.int 3 in
  String.init len (fun _ -> random_char ())

let random_int () =
  Random.bits() - (1 lsl 29)

let random_int32 () =
  Int32.(logxor (of_int (Random.bits()))
                (shift_left (of_int (Random.bits())) 30))

let random_int64 () =
  Int64.(logxor (of_int (Random.bits()))
                (logxor (shift_left (of_int (Random.bits())) 30)
                        (shift_left (of_int (Random.bits())) 60)))

let random_float64 () =
  Random.float 100.0 -. 50.0

(* Returns a random value.  Small numerical types are not normalized. *)

let rec random_value = function
  | Int8u | Int8s | Int16u | Int16s ->
      VInt (random_int())
  | Int32 ->
      VInt32 (random_int32())
  | Int64 ->
      VInt64 (random_int64())
  | Float32 | Float64 ->
      VFloat (random_float64())
  | String ->
      VString (random_string())
  | Struct(id, members) ->
      VStruct (List.map (fun (fld, ty) -> random_value ty) members)

let random_retvalue = function
  | None -> VInt 0 (* meaningless *)
  | Some ty -> random_value ty

(* Generate function declaration, definition, and call *)

let string_of_ty = function
  | Int8u -> "unsigned char"
  | Int8s -> "signed char"
  | Int16u -> "unsigned short"
  | Int16s -> "short"
  | Int32 -> "int"
  | Int64 -> "long long"
  | Float32 -> "float"
  | Float64 -> "double"
  | String -> "char *"
  | Struct(id, _) -> sprintf "struct s%d" id

let string_of_optty = function
  | None -> "void"
  | Some t -> string_of_ty t

let declare_struct oc id members =
  fprintf oc "struct s%d {\n" id;
  List.iter
    (fun (fld, ty) -> fprintf oc "  %s %s;\n" (string_of_ty ty) fld)
    members;
  fprintf oc "};\n"

let declare_function oc name sg =
  fprintf oc "%s %s(" (string_of_optty sg.res) name;
  begin match sg.args with
  | [] -> fprintf oc "void"
  | t0 :: tl ->
      fprintf oc "%s x0" (string_of_ty t0);
      List.iteri (fun n t -> fprintf oc ", %s x%d" (string_of_ty t) (n + 1)) tl;
      if sg.varargs <> [] then fprintf oc ", ..."
  end;
  fprintf oc ")"

let rec compare_value oc variable value ty =
  match ty with
  | Struct(id, members) ->
      begin match value with
      | VStruct vl ->
          List.iter2
            (fun (fld, ty) v ->
              compare_value oc (sprintf "%s.%s" variable fld) v ty)
            members vl
      | _ ->
          assert false
      end
  | String ->
      fprintf oc "  check (strcmp(%s, %a) == 0);\n"
                 variable (print_value ~norm:true) (ty, value)
  | _ ->
      fprintf oc "  check (%s == %a);\n"
                 variable (print_value ~norm:true) (ty, value)

let define_function oc name sg vargs vres =
  declare_function oc name sg;
  fprintf oc "\n{\n";
  if sg.varargs <> [] then begin
    fprintf oc "  va_list l;\n";
    fprintf oc "  va_start(l, x%d);\n" (List.length sg.args - 1);
    List.iteri
      (fun n t ->
        fprintf oc "  %s x%d = va_arg(l, %s);\n"
                   (string_of_ty t) (n + List.length sg.args) (string_of_ty t))
      sg.varargs;
    fprintf oc "  va_end(l);\n";
  end;
  List.iteri
    (fun n (t, v) -> compare_value oc (sprintf "x%d" n) v t)
    (List.combine (sg.args @ sg.varargs) vargs);
  begin match sg.res with
    | None -> ()
    | Some tres ->
        fprintf oc "  return %a;\n" (print_value ~norm:false) (tres, vres)
  end;
  fprintf oc "}\n\n"

let call_function oc name sg vargs vres =
  fprintf oc "void call_%s(void)\n" name;
  fprintf oc "{\n";
  begin match sg.res with
  | None -> fprintf oc "  %s(" name
  | Some t -> fprintf oc "  %s r = %s(" (string_of_ty t) name
  end;
  begin match (sg.args @ sg.varargs), vargs with
  | [], [] -> ()
  | ty1 :: tyl, v1 :: vl ->
      print_value ~norm:false oc (ty1, v1);
      List.iter2
        (fun ty v -> fprintf oc ", %a" (print_value ~norm:false) (ty, v))
        tyl vl
  | _, _ ->
      assert false
  end;
  fprintf oc ");\n";
  begin match sg.res with
  | None -> ()
  | Some tyres -> compare_value oc "r" vres tyres
  end;
  fprintf oc "}\n\n"

let function_counter = ref 0

let generate_one_test oc0 oc1 oc2 sg =
  incr function_counter;
  let num = !function_counter in
  let vargs = List.map random_value (sg.args @ sg.varargs) in
  let vres = random_retvalue sg.res in
  let name = "f" ^ string_of_int num in
  fprintf oc0 "extern ";
  declare_function oc0 name sg;
  fprintf oc0 ";\n";
  define_function oc1 name sg vargs vres;
  call_function oc2 name sg vargs vres

let call_all_test oc =
  fprintf oc "int main(void)\n";
  fprintf oc "{\n";
  fprintf oc "  alarm(60);\n";
  for i = 1 to !function_counter do
    fprintf oc "  call_f%d();\n" i
  done;
  fprintf oc "  return failed;\n";
  fprintf oc "}\n"

(* Generate interesting function signatures *)

let all_ty =
  [| Int8u; Int8s; Int16u; Int16s; Int32; Int64; Float32; Float64; String |]

let base_ty =
  [| Int32; Int64; Float32; Float64 |]

let makerun pat len =
  let rec make i l =
    if l <= 0
    then []
    else pat.(i) :: make ((i + 1) mod (Array.length pat)) (l - 1)
  in make 0 len

let gen_fixed_sigs f =
  (* All possible return types *)
  Array.iter
    (fun ty -> f { args = []; varargs = []; res = Some ty })
    all_ty;
  (* All possible argument types *)
  Array.iter
    (fun ty -> f { args = [ty]; varargs = []; res = None })
    all_ty;
  (* 2 arguments of base types *)
  Array.iter
    (fun ty1 ->
      Array.iter
        (fun ty2 -> f { args = [ty1; ty2]; varargs = []; res = None })
        base_ty)
    base_ty;
  (* 3 arguments of base types *)
  Array.iter
    (fun ty1 ->
      Array.iter
        (fun ty2 ->
          Array.iter
            (fun ty3 -> f { args = [ty1; ty2; ty3]; varargs = []; res = None })
            base_ty)
        base_ty)
    base_ty;
  (* 4 arguments of base types *)
  Array.iter
    (fun ty1 ->
      Array.iter
        (fun ty2 ->
          Array.iter
            (fun ty3 ->
              Array.iter
                (fun ty4 ->
                   f { args = [ty1; ty2; ty3; ty4]; varargs = []; res = None })
                base_ty)
            base_ty)
        base_ty)
    base_ty;
  (* Runs of 6, 8, 10, 12, 16, 32 arguments of various patterns *)
  Array.iter
    (fun pat ->
      Array.iter
        (fun len ->
          f { args = makerun pat len; varargs = []; res = None })
        [| 6;8;10;12;16;32 |])
    [| [|Int32|]; [|Int64|]; [|Float32|]; [|Float64|];
       [|Int32;Int64|]; [|Int32;Float32|]; [|Int32;Float64|]; 
       [|Int64;Float32|]; [|Int64;Float64|]; [|Float32;Float64|];
       [|Int32;Int64;Float32;Float64|]
    |]

let split_list l n =
  let rec split l n accu =
    if n <= 0 then (List.rev accu, l) else
      match l with
      | [] -> assert false
      | h :: t -> split t (n - 1) (h :: accu)
  in split l n []

let is_vararg_type = function
  | Int32 | Int64 | Float64 | String -> true
  | _ -> false

let gen_vararg_sigs f =
  let make_vararg sg n =
    if List.length sg.args > n then begin
      let (fixed, varia) = split_list sg.args n in
      if List.for_all is_vararg_type varia
      && is_vararg_type (List.nth fixed (n - 1)) then
        f { args = fixed; varargs = varia; res = sg.res }
    end
  in
    gen_fixed_sigs
     (fun sg -> make_vararg sg 2; make_vararg sg 6; make_vararg sg 14)

(* Generate interesting struct types *)

let struct_counter = ref 0

let mkstruct oc members =
  incr struct_counter;
  let id = !struct_counter in
  declare_struct oc id members;
  Struct(id, members)

let member_ty =
  [| Int8u; Int16u; Int32; Int64; Float32; Float64 |]

let gen_structs oc f =
  (* One field of any type *)
  Array.iter
    (fun ty -> f (mkstruct oc [("a", ty)]))
    all_ty;
  (* Two fields of interesting types *)
  Array.iter
    (fun ty1 ->
      Array.iter
        (fun ty2 -> f (mkstruct oc [("a", ty1); ("b", ty2)]))
        member_ty)
    member_ty;
  (* 3, 4, 6, 8 fields of identical interesting type *)
  Array.iter
    (fun ty ->
       f (mkstruct oc [("a", ty); ("b", ty); ("c", ty)]);
       f (mkstruct oc [("a", ty); ("b", ty); ("c", ty); ("d", ty)]);
       f (mkstruct oc [("a", ty); ("b", ty); ("c", ty); ("d", ty);
                       ("e", ty); ("f", ty)]);
       f (mkstruct oc [("a", ty); ("b", ty); ("c", ty); ("d", ty);
                       ("e", ty); ("f", ty); ("g", ty); ("h", ty)]))
    member_ty

let gen_struct_sigs oc f =
  let make ty =
    (* Struct return *)
    f { args = []; varargs = []; res = Some ty };
    (* Struct passing (once, twice) *)
    f { args = [ty]; varargs = []; res = None };
    f { args = [ty;ty]; varargs = []; res = None };
    (* Struct passing mixed with scalar arguments *)
    f { args = [Int32;ty]; varargs = []; res = None };
    f { args = [Float64;ty]; varargs = []; res = None }
  in
    gen_structs oc make

(* Random generation *)

let pick arr =
  arr.(Random.int (Array.length arr))

let big_ty = [| Int32; Int64; Float32; Float64; String |]

let vararg_ty =  [| Int32; Int64; Float64; String |]

let random_funsig vararg =
  let res = if Random.bool() then Some (pick all_ty) else None in
  let numargs = Random.int 12 in
  let args = List.init numargs (fun _ -> pick big_ty) in
  let numvarargs = 
    if vararg && numargs > 0 && is_vararg_type (List.nth args (numargs - 1))
    then 1 + Random.int 12
    else 0 in
  let varargs = List.init numvarargs (fun _ -> pick vararg_ty) in
  { args; varargs; res }

let header =
{|#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
|}

let checking_code = {|
extern int failed;

static void failure(const char * assertion, const char * file,
                    int line, const char * fn)
{
  fprintf(stderr, "%s:%d:%s: assertion %s failed\n", file, line, fn, assertion);
  failed = 1;
}

#define check(expr) ((expr) ? (void)0 : failure(#expr,__FILE__,__LINE__,__func__))
|}

let output_prefix = ref "abifuzz"
let gen_vararg = ref false
let gen_struct = ref false
let num_random = ref 0

let _ =
  Arg.parse [
     "-plain", Arg.Unit (fun () -> gen_vararg := false; gen_struct := false),
       " generate fixed-argument functions without structs";
     "-vararg", Arg.Set gen_vararg,
       " generate variable-argument functions";
     "-structs", Arg.Set gen_struct,
       " generate functions that exchange structs";
     "-o", Arg.String (fun s -> output_prefix := s),
       " <prefix> produce <prefix>.h, <prefix>def.c and <prefix>use.c files";
     "-rnd", Arg.Int (fun n -> num_random := n),
       " <num> produce <num> extra functions with random signatures";
     "-seed", Arg.Int Random.init,
       " <seed> use the given seed for randomization"
  ]
  (fun s -> raise (Arg.Bad ("don't know what to do with " ^ s)))
  "Usage: generator [options]\n\nOptions are:";
  let oc0 = open_out (!output_prefix ^ "_decl.h")
  and oc1 = open_out (!output_prefix ^ "_def.c")
  and oc2 = open_out (!output_prefix ^ "_use.c") in
  fprintf oc0 "%s\n%s\n" header checking_code;
  fprintf oc1 "%s#include \"%s_decl.h\"\n\n" header !output_prefix;
  fprintf oc2 "%s#include \"%s_decl.h\"\n\nint failed = 0;\n\n"
              header !output_prefix;
  let cont = generate_one_test oc0 oc1 oc2 in
  if !gen_vararg then gen_vararg_sigs cont
  else if !gen_struct then gen_struct_sigs oc0 cont
  else gen_fixed_sigs cont;
  for i = 1 to !num_random do
    cont (random_funsig !gen_vararg)
  done;
  call_all_test oc2;
  close_out oc0; close_out oc1; close_out oc2