From cb6c83a95465bb2a32c66fbff7408ac783a9e974 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 01:08:07 +0100 Subject: [PATCH 01/18] add `owi c` command --- dune-project | 13 +- owi.opam | 6 + owi.opam.template | 3 + src/bin/dune | 2 +- src/bin/owi.ml | 51 +++- src/c_instrumentor.ml | 12 + src/c_instrumentor.mli | 1 + src/c_share.ml | 14 ++ src/c_share.mli | 7 + src/cmd_c.ml | 240 +++++++++++++++++++ src/cmd_c.mli | 11 + src/cmd_fmt.mli | 3 + src/cmd_opt.mli | 1 + src/cmd_run.mli | 1 + src/cmd_sym.mli | 2 + src/dune | 13 + src/libc/LICENSE_dietlibc | 341 ++++++++++++++++++++++++++ src/libc/dune | 131 ++++++++++ src/libc/include/alloca.h | 8 + src/libc/include/assert.h | 12 + src/libc/include/ctype.h | 27 +++ src/libc/include/dune | 36 +++ src/libc/include/endian.h | 4 + src/libc/include/errno.h | 147 ++++++++++++ src/libc/include/fcntl.h | 73 ++++++ src/libc/include/inttypes.h | 230 ++++++++++++++++++ src/libc/include/limits.h | 85 +++++++ src/libc/include/math.h | 7 + src/libc/include/netinet/in.h | 11 + src/libc/include/owi.h | 18 ++ src/libc/include/pthread.h | 163 +++++++++++++ src/libc/include/sched.h | 24 ++ src/libc/include/setjmp.h | 8 + src/libc/include/signal.h | 49 ++++ src/libc/include/stdarg-cruft.h | 286 ++++++++++++++++++++++ src/libc/include/stdarg.h | 19 ++ src/libc/include/stdbool.h | 13 + src/libc/include/stddef.h | 22 ++ src/libc/include/stdint.h | 135 +++++++++++ src/libc/include/stdio.h | 85 +++++++ src/libc/include/stdlib.h | 35 +++ src/libc/include/string.h | 36 +++ src/libc/include/sys/cdefs.h | 142 +++++++++++ src/libc/include/sys/param.h | 4 + src/libc/include/sys/resource.h | 92 +++++++ src/libc/include/sys/time.h | 52 ++++ src/libc/include/sys/types.h | 27 +++ src/libc/include/time.h | 24 ++ src/libc/include/unistd.h | 14 ++ src/libc/instrumentor.py | 388 ++++++++++++++++++++++++++++++ src/libc/script.ml | 34 +++ src/libc/src/assert_fail.c | 6 + src/libc/src/ctype.c | 50 ++++ src/libc/src/errno.c | 2 + src/libc/src/math.c | 5 + src/libc/src/memccpy.c | 15 ++ src/libc/src/memchr.c | 9 + src/libc/src/memcmp.c | 21 ++ src/libc/src/memcpy.c | 11 + src/libc/src/memmove.c | 19 ++ src/libc/src/memset.c | 12 + src/libc/src/netinet_in.c | 13 + src/libc/src/owi.c | 55 +++++ src/libc/src/pthread.c | 67 ++++++ src/libc/src/setjmp.c | 3 + src/libc/src/start.c | 3 + src/libc/src/stderr.c | 25 ++ src/libc/src/stdio.c | 40 ++++ src/libc/src/stdlib.c | 408 ++++++++++++++++++++++++++++++++ src/libc/src/stdout.c | 26 ++ src/libc/src/strchr.c | 15 ++ src/libc/src/strcmp.c | 7 + src/libc/src/strcpy.c | 8 + src/libc/src/strdup.c | 10 + src/libc/src/strerror.c | 8 + src/libc/src/strftime.c | 199 ++++++++++++++++ src/libc/src/strlen.c | 10 + src/libc/src/strncmp.c | 20 ++ src/libc/src/strncpy.c | 9 + src/libc/src/strrchr.c | 16 ++ src/libc/src/strstr.c | 17 ++ src/libc/src/sys_resources.c | 5 + src/libc/src/test-comp.c | 45 ++++ src/libc/src/time.c | 14 ++ src/libc/src/unistd.c | 10 + src/thread.ml | 2 +- 86 files changed, 4334 insertions(+), 13 deletions(-) create mode 100644 src/c_instrumentor.ml create mode 100644 src/c_instrumentor.mli create mode 100644 src/c_share.ml create mode 100644 src/c_share.mli create mode 100644 src/cmd_c.ml create mode 100644 src/cmd_c.mli create mode 100644 src/cmd_fmt.mli create mode 100644 src/cmd_opt.mli create mode 100644 src/cmd_run.mli create mode 100644 src/cmd_sym.mli create mode 100644 src/libc/LICENSE_dietlibc create mode 100644 src/libc/dune create mode 100644 src/libc/include/alloca.h create mode 100644 src/libc/include/assert.h create mode 100644 src/libc/include/ctype.h create mode 100644 src/libc/include/dune create mode 100644 src/libc/include/endian.h create mode 100644 src/libc/include/errno.h create mode 100644 src/libc/include/fcntl.h create mode 100644 src/libc/include/inttypes.h create mode 100644 src/libc/include/limits.h create mode 100644 src/libc/include/math.h create mode 100644 src/libc/include/netinet/in.h create mode 100644 src/libc/include/owi.h create mode 100644 src/libc/include/pthread.h create mode 100644 src/libc/include/sched.h create mode 100644 src/libc/include/setjmp.h create mode 100644 src/libc/include/signal.h create mode 100644 src/libc/include/stdarg-cruft.h create mode 100644 src/libc/include/stdarg.h create mode 100644 src/libc/include/stdbool.h create mode 100644 src/libc/include/stddef.h create mode 100644 src/libc/include/stdint.h create mode 100644 src/libc/include/stdio.h create mode 100644 src/libc/include/stdlib.h create mode 100644 src/libc/include/string.h create mode 100644 src/libc/include/sys/cdefs.h create mode 100644 src/libc/include/sys/param.h create mode 100644 src/libc/include/sys/resource.h create mode 100644 src/libc/include/sys/time.h create mode 100644 src/libc/include/sys/types.h create mode 100644 src/libc/include/time.h create mode 100644 src/libc/include/unistd.h create mode 100644 src/libc/instrumentor.py create mode 100644 src/libc/script.ml create mode 100644 src/libc/src/assert_fail.c create mode 100644 src/libc/src/ctype.c create mode 100644 src/libc/src/errno.c create mode 100644 src/libc/src/math.c create mode 100644 src/libc/src/memccpy.c create mode 100644 src/libc/src/memchr.c create mode 100644 src/libc/src/memcmp.c create mode 100644 src/libc/src/memcpy.c create mode 100644 src/libc/src/memmove.c create mode 100644 src/libc/src/memset.c create mode 100644 src/libc/src/netinet_in.c create mode 100644 src/libc/src/owi.c create mode 100644 src/libc/src/pthread.c create mode 100644 src/libc/src/setjmp.c create mode 100644 src/libc/src/start.c create mode 100644 src/libc/src/stderr.c create mode 100644 src/libc/src/stdio.c create mode 100644 src/libc/src/stdlib.c create mode 100644 src/libc/src/stdout.c create mode 100644 src/libc/src/strchr.c create mode 100644 src/libc/src/strcmp.c create mode 100644 src/libc/src/strcpy.c create mode 100644 src/libc/src/strdup.c create mode 100644 src/libc/src/strerror.c create mode 100644 src/libc/src/strftime.c create mode 100644 src/libc/src/strlen.c create mode 100644 src/libc/src/strncmp.c create mode 100644 src/libc/src/strncpy.c create mode 100644 src/libc/src/strrchr.c create mode 100644 src/libc/src/strstr.c create mode 100644 src/libc/src/sys_resources.c create mode 100644 src/libc/src/test-comp.c create mode 100644 src/libc/src/time.c create mode 100644 src/libc/src/unistd.c diff --git a/dune-project b/dune-project index 5660ff498..a8de983fb 100644 --- a/dune-project +++ b/dune-project @@ -1,26 +1,28 @@ (lang dune 3.0) (using menhir 2.1) - +(using dune_site 0.1) (using mdx 0.2) +(using directory-targets 0.1) (cram enable) +(generate_opam_files true) + (name owi) (license AGPL-3.0-or-later) (authors "Léo Andrès " - "Pierre Chambart ") + "Pierre Chambart " + "Filipe Marques") (maintainers "Léo Andrès ") (source (github ocamlpro/owi)) -(generate_opam_files true) - (package (name owi) (synopsis @@ -62,4 +64,5 @@ (graphics :dev) (tiny_httpd :dev) (encoding (>= 0.0.3)) - xmlm)) + xmlm) + (sites (share pyc) (share binc) (share libc))) diff --git a/owi.opam b/owi.opam index d091c3edb..48e365326 100644 --- a/owi.opam +++ b/owi.opam @@ -8,6 +8,7 @@ maintainer: ["Léo Andrès "] authors: [ "Léo Andrès " "Pierre Chambart " + "Filipe Marques" ] license: "AGPL-3.0-or-later" tags: ["owi" "ocaml" "webassembly" "wasm" "interpreter" "compiler"] @@ -42,10 +43,15 @@ build: [ name "-j" jobs + "--promote-install-files=false" "@install" "@runtest" {with-test} "@doc" {with-doc} ] + ["dune" "install" "-p" name "--create-install-files" name] ] dev-repo: "git+https://github.com/ocamlpro/owi.git" available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" +depexts: [ + ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} +] diff --git a/owi.opam.template b/owi.opam.template index b7b455db0..0ed3fabde 100644 --- a/owi.opam.template +++ b/owi.opam.template @@ -1 +1,4 @@ available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" +depexts: [ + ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} +] diff --git a/src/bin/dune b/src/bin/dune index 9cbf44d66..c856afaa2 100644 --- a/src/bin/dune +++ b/src/bin/dune @@ -3,5 +3,5 @@ (modules owi) (package owi) (libraries sedlex owi cmdliner) - (instrumentation + (instrumentation (backend bisect_ppx))) diff --git a/src/bin/owi.ml b/src/bin/owi.ml index 868c77f62..de0f615b4 100644 --- a/src/bin/owi.ml +++ b/src/bin/owi.ml @@ -10,10 +10,7 @@ let file = let files = let doc = "source files" in - let parse s = Ok s in - Cmdliner.Arg.( - value - & pos 0 (list ~sep:' ' (conv (parse, Format.pp_string))) [] (info [] ~doc) ) + Cmdliner.Arg.(value & pos_all non_dir_file [] (info [] ~doc)) let no_exhaustion = let doc = "no exhaustion tests" in @@ -37,7 +34,10 @@ let unsafe = let workers = let doc = "number of workers for symbolic execution" in - Cmdliner.Arg.(value & opt int (Domain.recommended_domain_count ()) & info [ "workers"; "w" ] ~doc) + Cmdliner.Arg.( + value + & opt int (Domain.recommended_domain_count ()) + & info [ "workers"; "w" ] ~doc ) let workspace = let doc = "path to the workspace directory" in @@ -52,6 +52,44 @@ let shared_man = let version = "%%VERSION%%" +let c_cmd = + let open Cmdliner in + let info = + let doc = + "Compile a C file to Wasm and run the symbolic interpreter on it" + in + let man = [] @ shared_man in + Cmd.info "c" ~version ~doc ~sdocs ~man + in + let arch = + let doc = "data model" in + Arg.(value & opt int 32 & info [ "arch"; "m" ] ~doc) + in + let property = + let doc = "property file" in + Arg.(value & opt (some string) None & info [ "property"; "p" ] ~doc) + in + let includes = + let doc = "headers path" in + Arg.(value & opt_all dir [] & info [ "I" ] ~doc) + in + let opt_lvl = + let doc = "specify which optimization level to use" in + Arg.(value & opt string "0" & info [ "O" ] ~doc) + in + let testcomp = + let doc = "test-comp mode" in + Arg.(value & flag & info [ "testcomp" ] ~doc) + in + let output = + let doc = "write results to dir" in + Arg.(value & opt string "owi-out" & info [ "output"; "o" ] ~doc) + in + Cmd.v info + Term.( + const Cmd_c.cmd $ debug $ arch $ property $ testcomp $ output $ workers + $ opt_lvl $ includes $ files ) + let fmt_cmd = let open Cmdliner in let info = @@ -117,7 +155,8 @@ let cli = Cmd.info "owi" ~version ~doc ~sdocs ~man in let default = Term.(ret (const (fun _ -> `Help (`Pager, None)) $ copts_t)) in - Cmd.group info ~default [ fmt_cmd; opt_cmd; run_cmd; script_cmd; sym_cmd ] + Cmd.group info ~default + [ c_cmd; fmt_cmd; opt_cmd; run_cmd; script_cmd; sym_cmd ] let main () = exit @@ Cmdliner.Cmd.eval cli diff --git a/src/c_instrumentor.ml b/src/c_instrumentor.ml new file mode 100644 index 000000000..96e4e656f --- /dev/null +++ b/src/c_instrumentor.ml @@ -0,0 +1,12 @@ +let py_module = lazy (Py.Import.import_module "instrumentor") + +let import_module () = Lazy.force py_module + +let instrument file includes = + let callable = Py.Module.get (import_module ()) "instrument" in + let kwargs = + [ ("file", Py.String.of_string file) + ; ("includes", Py.List.of_list @@ List.map Py.String.of_string includes) + ] + in + ignore @@ Py.Callable.to_function_with_keywords callable [||] kwargs diff --git a/src/c_instrumentor.mli b/src/c_instrumentor.mli new file mode 100644 index 000000000..e5f9ea216 --- /dev/null +++ b/src/c_instrumentor.mli @@ -0,0 +1 @@ +val instrument : string -> string list -> unit diff --git a/src/c_share.ml b/src/c_share.ml new file mode 100644 index 000000000..f048c4589 --- /dev/null +++ b/src/c_share.ml @@ -0,0 +1,14 @@ +let py_location = C_share_site.Sites.pyc + +let bin_location = C_share_site.Sites.binc + +let lib_location = C_share_site.Sites.libc + +let find location file = + List.find_map + (fun dir -> + let filename = Filename.concat dir file in + if Sys.file_exists filename then Some filename else None ) + location + +let get_libc () = find bin_location "libc.wasm" diff --git a/src/c_share.mli b/src/c_share.mli new file mode 100644 index 000000000..83b4cb4bb --- /dev/null +++ b/src/c_share.mli @@ -0,0 +1,7 @@ +val py_location : string list + +val bin_location : string list + +val lib_location : string list + +val get_libc : unit -> string option diff --git a/src/cmd_c.ml b/src/cmd_c.ml new file mode 100644 index 000000000..01a691182 --- /dev/null +++ b/src/cmd_c.ml @@ -0,0 +1,240 @@ +open Bos + +let ( let* ) = Result.bind + +let list_map f lst = + let exception E of Rresult.R.msg in + try + Ok + (List.map + (fun x -> match f x with Ok x' -> x' | Error e -> raise (E e)) + lst ) + with E e -> Error e + +type deps = + { clang : flags:Cmd.t -> out:Fpath.t -> Fpath.t -> Cmd.t + ; opt : Fpath.t -> Cmd.t + ; llc : bc:Fpath.t -> obj:Fpath.t -> Cmd.t + ; ld : flags:Cmd.t -> out:Fpath.t -> Fpath.t list -> Cmd.t + ; wasm2wat : out:Fpath.t -> Fpath.t -> Cmd.t + } + +type metadata = + { arch : int + ; property : string option + ; files : string list + } + +let clang bin ~flags ~out file = Cmd.(bin %% flags % "-o" % p out % p file) + +let opt bin file = Cmd.(bin % "-O1" % "-o" % p file % p file) + +let llc bin ~bc ~obj = + let flags = Cmd.of_list [ "-O1"; "-march=wasm32"; "-filetype=obj"; "-o" ] in + Cmd.(bin %% flags % p obj % p bc) + +let ld bin ~flags ~out files = + let libc = C_share.get_libc () |> Option.get in + let files = List.fold_left (fun acc f -> Cmd.(acc % p f)) Cmd.empty files in + Cmd.(bin %% flags % "-o" % p out %% files % libc) + +let wasm2wat bin0 ~out bin = Cmd.(bin0 % "-o" % p out % p bin) + +let check_dependencies () = + let* clang_bin = OS.Cmd.resolve @@ Cmd.v "clang" in + let* opt_bin = OS.Cmd.resolve @@ Cmd.v "opt" in + let* llc_bin = OS.Cmd.resolve @@ Cmd.v "llc" in + let* ld_bin = OS.Cmd.resolve @@ Cmd.v "wasm-ld" in + let* wasm2wat_bin = OS.Cmd.resolve @@ Cmd.v "wasm2wat" in + Ok + { clang = clang clang_bin + ; opt = opt opt_bin + ; llc = llc llc_bin + ; ld = ld ld_bin + ; wasm2wat = wasm2wat wasm2wat_bin + } + +let pre_patterns : (Re2.t * string) array = + Array.map + (fun (regex, template) -> (Re2.create_exn regex, template)) + [| ( "void\\s+reach_error\\(\\)\\s*\\{.*\\}" + , "void reach_error() { owi_assert(0); }" ) + (* ugly: Hack to solve duplicate errors on compilation *) + (* ; ("void\\s+(assert|assume)\\(", "void old_\\1(") *) + |] + +let patch_with_regex ~patterns (data : string) : string = + Array.fold_left + (fun data (regex, template) -> Re2.rewrite_exn regex ~template data) + data patterns + +let patch ~(src : Fpath.t) ~(dst : Fpath.t) = + let* data = OS.File.read src in + let data = patch_with_regex ~patterns:pre_patterns data in + let data = + String.concat "\n" + [ "#define __attribute__(x)" + ; "#define __extension__" + ; "#define __restrict" + ; "#define __inline" + ; "#include " + ; data + ] + in + let* () = OS.File.write dst data in + Ok () + +let copy ~src ~dst = + let* data = OS.File.read src in + let* () = OS.File.write dst data in + Ok dst + +let instrument_file ?(skip = false) ~(includes : string list) + ~(workspace : Fpath.t) (file : string) = + let file = Fpath.v file in + let dst = Fpath.(workspace // base (file -+ ".c")) in + if skip then copy ~src:file ~dst + else begin + Logs.app (fun m -> m "instrumenting %a" Fpath.pp file); + let* () = patch ~src:file ~dst in + let pypath = String.concat ":" C_share.py_location in + let* () = OS.Env.set_var "PYTHONPATH" (Some pypath) in + begin + try + Py.initialize (); + C_instrumentor.instrument (Fpath.to_string dst) includes; + Py.finalize () + with Py.E (errtype, errvalue) -> + let pp = Py.Object.format in + Logs.warn (fun m -> m "instrumentor: %a: %a" pp errtype pp errvalue) + end; + Ok dst + end + +let compile ~(deps : deps) ~(includes : string list) ~(opt_lvl : string) + (file : Fpath.t) = + Logs.app (fun m -> m "compiling %a" Fpath.pp file); + let cflags = + let includes = Cmd.of_list ~slip:"-I" includes in + let warnings = + Cmd.of_list + [ "-Wno-int-conversion" + ; "-Wno-pointer-sign" + ; "-Wno-string-plus-int" + ; "-Wno-implicit-function-declaration" + ; "-Wno-incompatible-library-redeclaration" + ; "-Wno-incompatible-function-pointer-types" + ; "-Wno-incompatible-pointer-types" + ] + in + Cmd.( + of_list + [ "-O" ^ opt_lvl; "-g"; "-emit-llvm"; "--target=wasm32"; "-m32"; "-c" ] + %% warnings %% includes ) + in + let bc = Fpath.(file -+ ".bc") in + let obj = Fpath.(file -+ ".o") in + let* () = OS.Cmd.run @@ deps.clang ~flags:cflags ~out:bc file in + let* () = OS.Cmd.run @@ deps.opt bc in + let* () = OS.Cmd.run @@ deps.llc ~bc ~obj in + Ok obj + +let link ~deps ~workspace (files : Fpath.t list) = + let ldflags ~entry = + let stack_size = 8 * (1024 * 1024) in + Cmd.( + of_list + [ "-z"; "stack-size=" ^ string_of_int stack_size; "--export=" ^ entry ] ) + in + let wasm = Fpath.(workspace / "a.out.wasm") in + let wat = Fpath.(workspace / "a.out.wat") in + let* () = + OS.Cmd.run @@ deps.ld ~flags:(ldflags ~entry:"_start") ~out:wasm files + in + let* () = OS.Cmd.run @@ deps.wasm2wat ~out:wat wasm in + Ok wat + +let cleanup dir = + OS.Path.fold ~elements:`Files + (fun path _acc -> + if not @@ Fpath.has_ext ".wat" path then + match OS.Path.delete path with + | Ok () -> () + | Error (`Msg e) -> Logs.warn (fun m -> m "%s" e) ) + () [ dir ] + |> Logs.on_error_msg ~level:Logs.Warning ~use:Fun.id + +let owi ~workspace ~workers flags module_ = + let flags = Cmd.of_list flags in + let w = string_of_int workers in + Cmd.( + v "owi" % "sym" % "-w" % w % "--workspace" %% flags % p workspace + % p module_ ) + +let run ~workspace ~workers module_ = + Logs.app (fun m -> m "running %a" Fpath.pp module_); + let workspace = Fpath.(workspace / "test-suite") in + let* () = OS.Cmd.run @@ owi ~workspace ~workers [] module_ in + Ok 0 + +let pp_tm fmt Unix.{ tm_year; tm_mon; tm_mday; tm_hour; tm_min; tm_sec; _ } = + Format.pp fmt "%04d-%02d-%02dT%02d:%02d:%02dZ" (tm_year + 1900) tm_mon tm_mday + tm_hour tm_min tm_sec + +let metadata ~workspace arch property files = + let out_metadata chan { arch; property; files } = + let o = Xmlm.make_output ~nl:true ~indent:(Some 2) (`Channel chan) in + let tag n = (("", n), []) in + let el n d = `El (tag n, [ `Data d ]) in + let* spec = + match property with None -> Ok "" | Some f -> OS.File.read @@ Fpath.v f + in + let file = String.concat " " files in + let hash = List.fold_left (fun _ f -> Sha256.file f) Sha256.zero files in + let time = Unix.time () |> Unix.localtime in + let test_metadata = + `El + ( tag "test-metadata" + , [ el "sourcecodelang" "C" + ; el "producer" "owic" + ; el "specification" (String.trim spec) + ; el "programfile" file + ; el "programhash" (Sha256.to_hex hash) + ; el "entryfunction" "main" + ; el "architecture" (Format.sprintf "%dbit" arch) + ; el "creationtime" (Format.asprintf "%a" pp_tm time) + ] ) + in + let dtd = + "" + in + Xmlm.output o (`Dtd (Some dtd)); + Xmlm.output_tree Fun.id o test_metadata; + Ok () + in + let fpath = Fpath.(workspace / "test-suite" / "metadata.xml") in + let* _ = OS.Dir.create ~path:true (Fpath.parent fpath) in + let* res = OS.File.with_oc fpath out_metadata { arch; property; files } in + res + +let cmd debug arch property testcomp output workers opt_lvl includes files = + if debug then Logs.set_level (Some Debug); + let workspace = Fpath.v output in + let includes = C_share.lib_location @ includes in + let res = + let* deps = check_dependencies () in + let* _ = OS.Dir.create ~path:true workspace in + (* skip instrumentation if not in test-comp mode *) + let skip = not testcomp in + let* nfiles = list_map (instrument_file ~skip ~includes ~workspace) files in + let* objects = list_map (compile ~deps ~includes ~opt_lvl) nfiles in + let* module_ = link ~deps ~workspace objects in + cleanup workspace; + let* () = metadata ~workspace arch property files in + run ~workspace ~workers module_ + in + match Logs.on_error_msg ~level:Logs.Error ~use:(fun _ -> 1) res with + | 0 -> () + | n -> exit n diff --git a/src/cmd_c.mli b/src/cmd_c.mli new file mode 100644 index 000000000..0f01359f1 --- /dev/null +++ b/src/cmd_c.mli @@ -0,0 +1,11 @@ +val cmd : + bool + -> int + -> string option + -> bool + -> string + -> int + -> string + -> string list + -> string list + -> unit diff --git a/src/cmd_fmt.mli b/src/cmd_fmt.mli new file mode 100644 index 000000000..d9f156a08 --- /dev/null +++ b/src/cmd_fmt.mli @@ -0,0 +1,3 @@ +val cmd : bool -> string -> unit + +val format_file_to_string : string -> string Result.t diff --git a/src/cmd_opt.mli b/src/cmd_opt.mli new file mode 100644 index 000000000..8b45dede8 --- /dev/null +++ b/src/cmd_opt.mli @@ -0,0 +1 @@ +val cmd : bool -> bool -> string -> unit diff --git a/src/cmd_run.mli b/src/cmd_run.mli new file mode 100644 index 000000000..d301a42e3 --- /dev/null +++ b/src/cmd_run.mli @@ -0,0 +1 @@ +val cmd : bool -> bool -> bool -> bool -> string list -> unit diff --git a/src/cmd_sym.mli b/src/cmd_sym.mli new file mode 100644 index 000000000..8ace5cce1 --- /dev/null +++ b/src/cmd_sym.mli @@ -0,0 +1,2 @@ +val cmd : + bool -> bool -> bool -> bool -> int -> bool -> string -> string list -> unit diff --git a/src/dune b/src/dune index 8a978dfd3..2be343c3f 100644 --- a/src/dune +++ b/src/dune @@ -2,9 +2,13 @@ (public_name owi) (modules assigned + c_instrumentor + c_share + c_share_site check choice_monad choice_monad_intf + cmd_c cmd_fmt cmd_opt cmd_run @@ -67,11 +71,16 @@ wutf8) (private_modules convert lexer menhir_parser spectest wutf8 tracing) (libraries + bos + dune-site encoding integers menhirLib ocaml_intrinsics ppxlib + pyml + re2 + sha sedlex uutf runtime_events @@ -90,3 +99,7 @@ (_ (flags (-w +a-4-40-41-42-44-45-70-73 -warn-error +a)))) + +(generate_sites_module + (module c_share_site) + (sites owi)) diff --git a/src/libc/LICENSE_dietlibc b/src/libc/LICENSE_dietlibc new file mode 100644 index 000000000..b8306825e --- /dev/null +++ b/src/libc/LICENSE_dietlibc @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/src/libc/dune b/src/libc/dune new file mode 100644 index 000000000..0dc448ffa --- /dev/null +++ b/src/libc/dune @@ -0,0 +1,131 @@ +(executable + (name script) + (modules script)) + +(rule + (target libc.wasm) + (deps + assert_fail.o + stdlib.o + ctype.o + stdout.o + errno.o + strchr.o + math.o + strcmp.o + memccpy.o + strcpy.o + memchr.o + strdup.o + memcmp.o + strerror.o + memcpy.o + strftime.o + memmove.o + strlen.o + memset.o + strncmp.o + netinet_in.o + strncpy.o + owi.o + strrchr.o + pthread.o + strstr.o + setjmp.o + sys_resources.o + start.o + test-comp.o + stderr.o + time.o + stdio.o + unistd.o) + (action + (run wasm-ld %{deps} -o %{target} --no-entry --export-all --relocatable))) + +(rule + (targets + assert_fail.o + stdlib.o + ctype.o + stdout.o + errno.o + strchr.o + math.o + strcmp.o + memccpy.o + strcpy.o + memchr.o + strdup.o + memcmp.o + strerror.o + memcpy.o + strftime.o + memmove.o + strlen.o + memset.o + strncmp.o + netinet_in.o + strncpy.o + owi.o + strrchr.o + pthread.o + strstr.o + setjmp.o + sys_resources.o + start.o + test-comp.o + stderr.o + time.o + stdio.o + unistd.o) + (deps + src/assert_fail.c + src/stdlib.c + src/ctype.c + src/stdout.c + src/errno.c + src/strchr.c + src/math.c + src/strcmp.c + src/memccpy.c + src/strcpy.c + src/memchr.c + src/strdup.c + src/memcmp.c + src/strerror.c + src/memcpy.c + src/strftime.c + src/memmove.c + src/strlen.c + src/memset.c + src/strncmp.c + src/netinet_in.c + src/strncpy.c + src/owi.c + src/strrchr.c + src/pthread.c + src/strstr.c + src/setjmp.c + src/sys_resources.c + src/start.c + src/test-comp.c + src/stderr.c + src/time.c + src/stdio.c + src/unistd.c) + (action + (run ./script.exe %{deps}))) + +(install + (package owi) + (section + (site + (owi binc))) + (files libc.wasm)) + +(install + (package owi) + (section + (site + (owi pyc))) + (files instrumentor.py)) diff --git a/src/libc/include/alloca.h b/src/libc/include/alloca.h new file mode 100644 index 000000000..6a1dfd0c9 --- /dev/null +++ b/src/libc/include/alloca.h @@ -0,0 +1,8 @@ +#ifndef ALLOCA_H +#define ALLOCA_H + +#include + +void *alloca(size_t size); + +#endif diff --git a/src/libc/include/assert.h b/src/libc/include/assert.h new file mode 100644 index 000000000..6693d5aa8 --- /dev/null +++ b/src/libc/include/assert.h @@ -0,0 +1,12 @@ +#ifndef ASSERT_H +#define ASSERT_H + +extern void __assert_fail(const char *__assertion, const char *__file, + unsigned int __line, const char *__function); +#define assert(expr) \ + ((void)((expr) \ + ? 0 \ + : (__assert_fail(#expr, __FILE__, __LINE__, __ASSERT_FUNCTION), \ + 0))) + +#endif diff --git a/src/libc/include/ctype.h b/src/libc/include/ctype.h new file mode 100644 index 000000000..913679010 --- /dev/null +++ b/src/libc/include/ctype.h @@ -0,0 +1,27 @@ +#ifndef CTYPE_H +#define CTYPE_H + +extern int isascii(int c); +extern int isblank(int c); +extern int isalnum(int c); +extern int isalpha(int c); +extern int isdigit(int c); +extern int isspace(int c); + +extern int isupper(int c); +extern int islower(int c); + +extern int toascii(int c); +extern int tolower(int c); +extern int toupper(int c); + +extern int isprint(int c); +extern int ispunct(int c); +extern int iscntrl(int c); + +/* fscking GNU extensions! */ +extern int isxdigit(int c); + +extern int isgraph(int c); + +#endif diff --git a/src/libc/include/dune b/src/libc/include/dune new file mode 100644 index 000000000..9a447658a --- /dev/null +++ b/src/libc/include/dune @@ -0,0 +1,36 @@ +(install + (package owi) + (section + (site + (owi libc))) + (files + (netinet/in.h as netinet/in.h) + (sys/cdefs.h as sys/cdefs.h) + (sys/param.h as sys/param.h) + (sys/resource.h as sys/resource.h) + (sys/time.h as sys/time.h) + (sys/types.h as sys/types.h) + alloca.h + assert.h + ctype.h + endian.h + errno.h + fcntl.h + inttypes.h + limits.h + math.h + pthread.h + sched.h + setjmp.h + signal.h + stdarg-cruft.h + stdarg.h + stdbool.h + stddef.h + stdint.h + stdio.h + stdlib.h + string.h + time.h + unistd.h + owi.h)) diff --git a/src/libc/include/endian.h b/src/libc/include/endian.h new file mode 100644 index 000000000..9c53ea537 --- /dev/null +++ b/src/libc/include/endian.h @@ -0,0 +1,4 @@ +#ifndef ENDIAN_H +#define ENDIAN_H + +#endif diff --git a/src/libc/include/errno.h b/src/libc/include/errno.h new file mode 100644 index 000000000..c51e38670 --- /dev/null +++ b/src/libc/include/errno.h @@ -0,0 +1,147 @@ +#ifndef ERRNO_H +#define ERRNO_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ +#define EDEADLOCK EDEADLK +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define ENOTSUP EOPNOTSUPP /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + +#define ERFKILL 132 /* Operation not possible due to RF-kill */ + +#define EHWPOISON 133 /* Memory page has hardware error */ + +#define __SYS_NERR ((EHWPOISON) + 1) + +static int errno; + +#endif diff --git a/src/libc/include/fcntl.h b/src/libc/include/fcntl.h new file mode 100644 index 000000000..4cc5e3e0a --- /dev/null +++ b/src/libc/include/fcntl.h @@ -0,0 +1,73 @@ +#ifndef FCNTL_H +#define FCNTL_H + +#define F_LINUX_SPECIFIC_BASE 1024 + +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_DSYNC 010000 +#define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_DIRECT 040000 /* direct disk access hint - currently ignored */ +#define O_LARGEFILE 0100000 +#define O_DIRECTORY 0200000 /* must be a directory */ +#define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 +#define O_CLOEXEC 02000000 +#define O_SYNC (O_DSYNC | 04000000) +#define O_PATH 010000000 +#define __O_TMPFILE 020000000 + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 +#define F_SETOWN 8 /* for sockets. */ +#define F_GETOWN 9 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +#define F_GETLK64 12 /* using 'struct flock64' */ +#define F_SETLK64 13 +#define F_SETLKW64 14 + +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB \ + 4 /* or'd with one of the above to prevent \ + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock */ +#define LOCK_READ 64 /* ... Which allows concurrent read operations */ +#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ +#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ + +#endif diff --git a/src/libc/include/inttypes.h b/src/libc/include/inttypes.h new file mode 100644 index 000000000..05bfb7b6b --- /dev/null +++ b/src/libc/include/inttypes.h @@ -0,0 +1,230 @@ +#ifndef INTTYPES_H +#define INTTYPES_H + +#define __PRI64_PREFIX "ll" +#define __PRIPTR_PREFIX + +/* Decimal notation. */ +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 __PRI64_PREFIX "d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "d" +#define PRIdLEAST32 "d" +#define PRIdLEAST64 __PRI64_PREFIX "d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 "d" +#define PRIdFAST32 "d" +#define PRIdFAST64 __PRI64_PREFIX "d" + +#define PRIi8 "i" +#define PRIi16 "i" +#define PRIi32 "i" +#define PRIi64 __PRI64_PREFIX "i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "i" +#define PRIiLEAST32 "i" +#define PRIiLEAST64 __PRI64_PREFIX "i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 "i" +#define PRIiFAST32 "i" +#define PRIiFAST64 __PRI64_PREFIX "i" + +/* Octal notation. */ +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 __PRI64_PREFIX "o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "o" +#define PRIoLEAST32 "o" +#define PRIoLEAST64 __PRI64_PREFIX "o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 "o" +#define PRIoFAST32 "o" +#define PRIoFAST64 __PRI64_PREFIX "o" + +/* Unsigned integers. */ +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __PRI64_PREFIX "u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "u" +#define PRIuLEAST32 "u" +#define PRIuLEAST64 __PRI64_PREFIX "u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 "u" +#define PRIuFAST32 "u" +#define PRIuFAST64 __PRI64_PREFIX "u" + +/* lowercase hexadecimal notation. */ +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 __PRI64_PREFIX "x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "x" +#define PRIxLEAST32 "x" +#define PRIxLEAST64 __PRI64_PREFIX "x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 "x" +#define PRIxFAST32 "x" +#define PRIxFAST64 __PRI64_PREFIX "x" + +/* UPPERCASE hexadecimal notation. */ +#define PRIX8 "X" +#define PRIX16 "X" +#define PRIX32 "X" +#define PRIX64 __PRI64_PREFIX "X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "X" +#define PRIXLEAST32 "X" +#define PRIXLEAST64 __PRI64_PREFIX "X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 "X" +#define PRIXFAST32 "X" +#define PRIXFAST64 __PRI64_PREFIX "X" + +/* Macros for printing `intmax_t' and `uintmax_t'. */ +#define PRIdMAX __PRI64_PREFIX "d" +#define PRIiMAX __PRI64_PREFIX "i" +#define PRIoMAX __PRI64_PREFIX "o" +#define PRIuMAX __PRI64_PREFIX "u" +#define PRIxMAX __PRI64_PREFIX "x" +#define PRIXMAX __PRI64_PREFIX "X" + +/* Macros for printing `intptr_t' and `uintptr_t'. */ +#define PRIdPTR __PRIPTR_PREFIX "d" +#define PRIiPTR __PRIPTR_PREFIX "i" +#define PRIoPTR __PRIPTR_PREFIX "o" +#define PRIuPTR __PRIPTR_PREFIX "u" +#define PRIxPTR __PRIPTR_PREFIX "x" +#define PRIXPTR __PRIPTR_PREFIX "X" + +/* Macros for scanning format specifiers. */ + +/* Signed decimal notation. */ +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "d" +#define SCNd64 __PRI64_PREFIX "d" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "d" +#define SCNdLEAST64 __PRI64_PREFIX "d" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 __PRIPTR_PREFIX "d" +#define SCNdFAST32 __PRIPTR_PREFIX "d" +#define SCNdFAST64 __PRI64_PREFIX "d" + +/* Signed decimal notation. */ +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "i" +#define SCNi64 __PRI64_PREFIX "i" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "i" +#define SCNiLEAST64 __PRI64_PREFIX "i" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 __PRIPTR_PREFIX "i" +#define SCNiFAST32 __PRIPTR_PREFIX "i" +#define SCNiFAST64 __PRI64_PREFIX "i" + +/* Unsigned decimal notation. */ +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "u" +#define SCNu64 __PRI64_PREFIX "u" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "u" +#define SCNuLEAST64 __PRI64_PREFIX "u" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 __PRIPTR_PREFIX "u" +#define SCNuFAST32 __PRIPTR_PREFIX "u" +#define SCNuFAST64 __PRI64_PREFIX "u" + +/* Octal notation. */ +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "o" +#define SCNo64 __PRI64_PREFIX "o" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "o" +#define SCNoLEAST64 __PRI64_PREFIX "o" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 __PRIPTR_PREFIX "o" +#define SCNoFAST32 __PRIPTR_PREFIX "o" +#define SCNoFAST64 __PRI64_PREFIX "o" + +/* Hexadecimal notation. */ +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "x" +#define SCNx64 __PRI64_PREFIX "x" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "x" +#define SCNxLEAST64 __PRI64_PREFIX "x" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 __PRIPTR_PREFIX "x" +#define SCNxFAST32 __PRIPTR_PREFIX "x" +#define SCNxFAST64 __PRI64_PREFIX "x" + +/* Macros for scanning `intmax_t' and `uintmax_t'. */ +#define SCNdMAX __PRI64_PREFIX "d" +#define SCNiMAX __PRI64_PREFIX "i" +#define SCNoMAX __PRI64_PREFIX "o" +#define SCNuMAX __PRI64_PREFIX "u" +#define SCNxMAX __PRI64_PREFIX "x" + +/* Macros for scanning `intptr_t' and `uintptr_t'. */ +#define SCNdPTR __PRIPTR_PREFIX "d" +#define SCNiPTR __PRIPTR_PREFIX "i" +#define SCNoPTR __PRIPTR_PREFIX "o" +#define SCNuPTR __PRIPTR_PREFIX "u" +#define SCNxPTR __PRIPTR_PREFIX "x" + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +typedef signed long int64_t; +typedef unsigned long uint64_t; +typedef signed long intmax_t; +typedef unsigned long uintmax_t; + +typedef long int intptr_t; +typedef unsigned long int uintptr_t; + +#endif diff --git a/src/libc/include/limits.h b/src/libc/include/limits.h new file mode 100644 index 000000000..fec9984e6 --- /dev/null +++ b/src/libc/include/limits.h @@ -0,0 +1,85 @@ +#ifndef _LIMITS_H +#define _LIMITS_H + +#include + +#ifndef __SCHAR_MAX__ +#define __SCHAR_MAX__ 0x7f +#endif +#ifndef __SHRT_MAX__ +#define __SHRT_MAX__ 0x7fff +#endif +#ifndef __INT_MAX__ +#define __INT_MAX__ 0x7fffffff +#endif +#ifndef __LONG_MAX__ +#define __LONG_MAX__ 0x7fffffffl +#endif + +#define CHAR_BIT 8 + +#define SCHAR_MIN (-1 - SCHAR_MAX) +#define SCHAR_MAX (__SCHAR_MAX__) +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) + +#ifdef __CHAR_UNSIGNED__ +#undef CHAR_MIN +#define CHAR_MIN 0 +#undef CHAR_MAX +#define CHAR_MAX UCHAR_MAX +#else +#undef CHAR_MIN +#define CHAR_MIN SCHAR_MIN +#undef CHAR_MAX +#define CHAR_MAX SCHAR_MAX +#endif + +#define SHRT_MIN (-1 - SHRT_MAX) +#define SHRT_MAX (__SHRT_MAX__) +#define USHRT_MAX (SHRT_MAX * 2 + 1) + +#define INT_MIN (-1 - INT_MAX) +#define INT_MAX (__INT_MAX__) +#define UINT_MAX (INT_MAX * 2u + 1) + +#define LONG_MIN (-1l - LONG_MAX) +#define LONG_MAX (__LONG_MAX__) +#define ULONG_MAX (LONG_MAX * 2ul + 1) + +#define LLONG_MAX 0x7fffffffffffffffll +#define LLONG_MIN (-1ll - LLONG_MAX) + +/* Maximum value an `unsigned long long int' can hold. (Minimum is 0.) */ +#define ULLONG_MAX (~0ull) + +#define SSIZE_MIN LONG_MIN +#define SSIZE_MAX LONG_MAX + +#define PASS_MAX 256 + +#define NR_OPEN 1024 + +#define NGROUPS_MAX 32 /* supplemental group IDs are available */ +#define ARG_MAX 131072 /* # bytes of args + environ for exec() */ +#define CHILD_MAX 999 /* no limit :-) */ +#define OPEN_MAX 256 /* # open files a process may have */ +#define LINK_MAX 127 /* # links a file may have */ +#define MAX_CANON 255 /* size of the canonical input queue */ +#define MAX_INPUT 255 /* size of the type-ahead buffer */ +#define NAME_MAX 255 /* # chars in a file name */ +#define PATH_MAX 4095 /* # chars in a path name */ +#define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */ + +#define RTSIG_MAX 32 + +#define LINE_MAX 2048 + +/* mutt demanded these */ +#define _POSIX_PATH_MAX PATH_MAX +#define MB_LEN_MAX 16 + +#ifdef _XOPEN_SOURCE +#define IOV_MAX 1024 +#endif + +#endif diff --git a/src/libc/include/math.h b/src/libc/include/math.h new file mode 100644 index 000000000..642f5f771 --- /dev/null +++ b/src/libc/include/math.h @@ -0,0 +1,7 @@ +#ifndef _MATH_H_ +#define _MATH_H_ + +double sin(double); +double cos(double); + +#endif diff --git a/src/libc/include/netinet/in.h b/src/libc/include/netinet/in.h new file mode 100644 index 000000000..9c71f8e34 --- /dev/null +++ b/src/libc/include/netinet/in.h @@ -0,0 +1,11 @@ +#ifndef NETINET_IN_H +#define NETINET_IN_H + +#include + +uint32_t htonl(uint32_t hostlong); +uint16_t htons(uint16_t hostshort); +uint32_t ntohl(uint32_t netlong); +uint16_t ntohs(uint16_t netshort); + +#endif diff --git a/src/libc/include/owi.h b/src/libc/include/owi.h new file mode 100644 index 000000000..68e6ab43a --- /dev/null +++ b/src/libc/include/owi.h @@ -0,0 +1,18 @@ +#ifndef _OWI_H +#define _OWI_H + +void *owi_malloc(void *base, unsigned int size); +void owi_free(void *); + +int owi_i32(void); +long long owi_i64(void); +float owi_f32(void); +double owi_f64(void); + +void owi_assume(int c); +void owi_assert(int c); + +int and_(int, int); +int or_(int, int); + +#endif diff --git a/src/libc/include/pthread.h b/src/libc/include/pthread.h new file mode 100644 index 000000000..d3446d60a --- /dev/null +++ b/src/libc/include/pthread.h @@ -0,0 +1,163 @@ +#ifndef PTHREAD_H +#define PTHREAD_H + +#include + +#define PTHREAD_STACK_MIN 16384 +#define PTHREAD_STACK_MAX (8 << 20) + +struct _pthread_descr_struct { + int empty; +}; + +typedef struct _pthread_descr_struct *_pthread_descr; +typedef int pthread_t; + +/* Fast locks */ +struct _pthread_fastlock { + int __spinlock; +}; + +#define PTHREAD_SPIN_LOCKED 1 +#define PTHREAD_SPIN_UNLOCKED 0 + +/* Mutexes */ +typedef struct { + struct _pthread_fastlock lock; + _pthread_descr owner; + int kind; + unsigned int count; +} pthread_mutex_t; + +enum { + PTHREAD_MUTEX_FAST_NP, +#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_FAST_NP +#define PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_FAST_NP + PTHREAD_MUTEX_RECURSIVE_NP, +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE_NP + PTHREAD_MUTEX_ERRORCHECK_NP, +#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK_NP +}; + +enum { + PTHREAD_PROCESS_PRIVATE, +#define PTHREAD_PROCESS_PRIVATE PTHREAD_PROCESS_PRIVATE + PTHREAD_PROCESS_SHARED +#define PTHREAD_PROCESS_SHARED PTHREAD_PROCESS_SHARED +}; + +#define PTHREAD_MUTEX_INITIALIZER \ + { {PTHREAD_SPIN_UNLOCKED}, 0, PTHREAD_MUTEX_FAST_NP, 0 } + +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ + { {PTHREAD_SPIN_UNLOCKED}, 0, PTHREAD_MUTEX_RECURSIVE_NP, 0 } + +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \ + { {PTHREAD_SPIN_UNLOCKED}, 0, PTHREAD_MUTEX_ERRORCHECK_NP, 0 } + +typedef struct { + int __mutexkind; +} pthread_mutexattr_t; + +int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); + +int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind); +int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind); + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); + +typedef void *pthread_condattr_t; + +typedef struct { + struct _pthread_fastlock lock; + _pthread_descr wait_chain; +} pthread_cond_t; + +#define PTHREAD_COND_INITIALIZER \ + { {PTHREAD_SPIN_UNLOCKED}, 0 } + +int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); +int pthread_cond_destroy(pthread_cond_t *cond); +int pthread_cond_signal(pthread_cond_t *cond); +int pthread_cond_broadcast(pthread_cond_t *cond); +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime); +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +typedef int pthread_once_t; + +/* Attributes for threads. */ +typedef struct { + int __detachstate; + int __schedpolicy; + struct sched_param __schedparam; + int __inheritsched; + int __scope; + void *__stackaddr; + unsigned long __stacksize; +} pthread_attr_t; + +enum { + PTHREAD_CREATE_JOINABLE, +#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_JOINABLE + PTHREAD_CREATE_DETACHED +#define PTHREAD_CREATE_DETACHED PTHREAD_CREATE_DETACHED +}; + +enum { + PTHREAD_EXPLICIT_SCHED, +#define PTHREAD_EXPLICIT_SCHED PTHREAD_EXPLICIT_SCHED + PTHREAD_INHERIT_SCHED +#define PTHREAD_INHERIT_SCHED PTHREAD_INHERIT_SCHED +}; + +enum { /* for completeness */ + PTHREAD_SCOPE_SYSTEM, +#define PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM + PTHREAD_SCOPE_PROCESS +#define PTHREAD_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS +}; + +typedef struct { + unsigned int n; +} pthread_rwlock_t; + +typedef struct { + int dummy; +} pthread_rwlockattr_t; + +#define PTHREAD_RWLOCK_INITIALIZER \ + { 0 } + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); +// pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER; +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); + +int pthread_detach(pthread_t __th); +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_setstacksize(pthread_attr_t *attr, const size_t stacksize); +int pthread_create(pthread_t *__threadarg, const pthread_attr_t *__attr, + void *(*__start_routine)(void *), void *__arg); +int pthread_join(pthread_t __th, void **__thread_return); +pthread_t pthread_self(void); +int pthread_equal(pthread_t __thread1, pthread_t __thread2); + +#endif diff --git a/src/libc/include/sched.h b/src/libc/include/sched.h new file mode 100644 index 000000000..ab19da226 --- /dev/null +++ b/src/libc/include/sched.h @@ -0,0 +1,24 @@ +#ifndef SCHED_H +#define SCHED_H + +#include +#include + +/* + * Scheduling policies + */ +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 + +/* + * This is an additional bit set when we want to + * yield the CPU for one re-schedule.. + */ +#define SCHED_YIELD 0x10 + +struct sched_param { + int sched_priority; +}; + +#endif diff --git a/src/libc/include/setjmp.h b/src/libc/include/setjmp.h new file mode 100644 index 000000000..614333a91 --- /dev/null +++ b/src/libc/include/setjmp.h @@ -0,0 +1,8 @@ +#ifndef _SETJMP_H_ +#define _SETJMP_H_ + +struct __jmp_buf_tag {}; + +int setjmp(struct __jmp_buf_tag *); + +#endif diff --git a/src/libc/include/signal.h b/src/libc/include/signal.h new file mode 100644 index 000000000..c74febeed --- /dev/null +++ b/src/libc/include/signal.h @@ -0,0 +1,49 @@ +#ifndef SIGNAL_H +#define SIGNAL_H + +#define NSIG 32 +#define _NSIG 65 + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGSEGV 11 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGUNUSED 31 +#define SIGBUS 7 +#define SIGUSR1 10 +#define SIGUSR2 12 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPWR 30 +#define SIGSYS 31 + +#define SIGCLD SIGCHLD +#define SIGPOLL SIGIO + +/* These should not be considered constants from userland. */ +#define SIGLOST SIGPWR +#define SIGRTMIN 32 +#define SIGRTMAX (_NSIG - 1) + +#endif diff --git a/src/libc/include/stdarg-cruft.h b/src/libc/include/stdarg-cruft.h new file mode 100644 index 000000000..a29086132 --- /dev/null +++ b/src/libc/include/stdarg-cruft.h @@ -0,0 +1,286 @@ +#include + +#if defined(__sparc__) || defined(__alpha__) || defined(__mips__) +enum { + __no_type_class = -1, + __void_type_class, + __integer_type_class, + __char_type_class, + __enumeral_type_class, + __boolean_type_class, + __pointer_type_class, + __reference_type_class, + __offset_type_class, + __real_type_class, + __complex_type_class, + __function_type_class, + __method_type_class, + __record_type_class, + __union_type_class, + __array_type_class, + __string_type_class, + __set_type_class, + __file_type_class, + __lang_type_class +}; +#endif + +#if defined(__sparc__) + +typedef char *va_list; +#define va_end(ap) ap = 0 + +#define va_start(AP, LASTARG) \ + (__builtin_next_arg(LASTARG), AP = (char *)__builtin_saveregs()) + +#define __va_rounded_size(TYPE) \ + (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) + +/* We don't declare the union member `d' to have type TYPE + because that would lose in C++ if TYPE has a constructor. */ +/* We cast to void * and then to TYPE * because this avoids + a warning about increasing the alignment requirement. + The casts to char * avoid warnings about invalid pointer arithmetic. */ +#define va_arg(pvar, TYPE) \ + __extension__(*({ \ + ((__builtin_classify_type(*(TYPE *)0) >= __record_type_class || \ + (__builtin_classify_type(*(TYPE *)0) == __real_type_class && \ + sizeof(TYPE) == 16)) \ + ? ((pvar) = (char *)(pvar) + __va_rounded_size(TYPE *), \ + *(TYPE **)(void *)((char *)(pvar)-__va_rounded_size(TYPE *))) \ + : __va_rounded_size(TYPE) == 8 \ + ? ({ \ + union { \ + char __d[sizeof(TYPE)]; \ + int __i[2]; \ + } __u; \ + __u.__i[0] = ((int *)(void *)(pvar))[0]; \ + __u.__i[1] = ((int *)(void *)(pvar))[1]; \ + (pvar) = (char *)(pvar) + 8; \ + (TYPE *)(void *)__u.__d; \ + }) \ + : ((pvar) = (char *)(pvar) + __va_rounded_size(TYPE), \ + ((TYPE *)(void *)((char *)(pvar)-__va_rounded_size(TYPE))))); \ + })) + +#elif defined(__mips__) + +typedef char *va_list; + +#ifdef __mips64__ +#define __va_rounded_size(__TYPE) (((sizeof(__TYPE) + 8 - 1) / 8) * 8) +#else +#define __va_rounded_size(__TYPE) \ + (((sizeof(__TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) +#endif +#ifdef __mips64__ +#define __va_reg_size 8 +#else +#define __va_reg_size 4 +#endif + +#define va_start(__AP, __LASTARG) \ + (__AP = (va_list)__builtin_next_arg(__LASTARG)) + +#ifdef __mips64__ +#ifdef __MIPSEB__ +#define va_arg(__AP, __type) \ + ((__type *)(void *)(__AP = \ + (char *)((((__PTRDIFF_TYPE__)__AP + 8 - 1) & -8) + \ + __va_rounded_size(__type))))[-1] +#else +#define va_arg(__AP, __type) \ + ((__AP = (char *)((((__PTRDIFF_TYPE__)__AP + 8 - 1) & -8) + \ + __va_rounded_size(__type))), \ + *(__type *)(void *)(__AP - __va_rounded_size(__type))) +#endif + +#else /* not __mips64 */ + +#ifdef __MIPSEB__ +/* For big-endian machines. */ +#define va_arg(__AP, __type) \ + ((__AP = (char *)((__alignof__(__type) > 4 \ + ? ((__PTRDIFF_TYPE__)__AP + 8 - 1) & -8 \ + : ((__PTRDIFF_TYPE__)__AP + 4 - 1) & -4) + \ + __va_rounded_size(__type))), \ + *(__type *)(void *)(__AP - __va_rounded_size(__type))) +#else +/* For little-endian machines. */ +#define va_arg(__AP, __type) \ + ((__type *)(void *)(__AP = \ + (char *)((__alignof__(__type) > 4 \ + ? ((__PTRDIFF_TYPE__)__AP + 8 - 1) & \ + -8 \ + : ((__PTRDIFF_TYPE__)__AP + 4 - 1) & \ + -4) + \ + __va_rounded_size(__type))))[-1] +#endif +#endif + +#elif defined(__powerpc__) + +typedef struct __va_list_tag { + unsigned char gpr; /* index into the array of 8 GPRs stored in the + register save area gpr=0 corresponds to r3, + gpr=1 to r4, etc. */ + unsigned char fpr; /* index into the array of 8 FPRs stored in the + register save area fpr=0 corresponds to f1, + fpr=1 to f2, etc. */ + char *overflow_arg_area; /* location on stack that holds the next + overflow argument */ + char *reg_save_area; /* where r3:r10 and f1:f8, if saved are stored */ +} va_list[1]; + +#define __va_overflow(AP) (AP)->overflow_arg_area +#ifdef __OPTIMIZE__ +extern void __va_arg_type_violation(void) __attribute__((__noreturn__)); +#else +#define __va_arg_type_violation() +#endif + +typedef struct { + long __gp_save[8]; /* save area for GP registers */ + double __fp_save[8]; /* save area for FP registers */ +} __va_regsave_t; + +/* Macros to access the register save area */ +/* We cast to void * and then to TYPE * because this avoids + a warning about increasing the alignment requirement. */ +#define __VA_FP_REGSAVE(AP, OFS, TYPE) \ + ((TYPE *)(void *)(&(((__va_regsave_t *)(AP)->reg_save_area)->__fp_save[OFS]))) + +#define __VA_GP_REGSAVE(AP, OFS, TYPE) \ + ((TYPE *)(void *)(&(((__va_regsave_t *)(AP)->reg_save_area)->__gp_save[OFS]))) + +#define __va_start_common(AP, FAKE) \ + __builtin_memcpy((AP), __builtin_saveregs(), sizeof(va_list)) + +#define va_start(AP, LASTARG) \ + (__builtin_next_arg(LASTARG), __va_start_common(AP, 0)) + +#ifdef _SOFT_FLOAT +#define __va_float_p(TYPE) 0 +#else +#define __va_float_p(TYPE) (__builtin_classify_type(*(TYPE *)0) == 8) +#endif + +#define __va_aggregate_p(TYPE) (__builtin_classify_type(*(TYPE *)0) >= 12) +#define __va_size(TYPE) ((sizeof(TYPE) + sizeof(long) - 1) / sizeof(long)) + +#define va_arg(AP, TYPE) \ + __extension__(*({ \ + register TYPE *__ptr; \ + \ + if (__va_float_p(TYPE) && sizeof(TYPE) < 16) { \ + unsigned char __fpr = (AP)->fpr; \ + if (__fpr < 8) { \ + __ptr = __VA_FP_REGSAVE(AP, __fpr, TYPE); \ + (AP)->fpr = __fpr + 1; \ + } else if (sizeof(TYPE) == 8) { \ + unsigned long __addr = (unsigned long)(__va_overflow(AP)); \ + __ptr = (TYPE *)((__addr + 7) & -8); \ + __va_overflow(AP) = (char *)(__ptr + 1); \ + } else { \ + /* float is promoted to double. */ \ + __va_arg_type_violation(); \ + } \ + } \ + \ + /* Aggregates and long doubles are passed by reference. */ \ + else if (__va_aggregate_p(TYPE) || __va_float_p(TYPE)) { \ + unsigned char __gpr = (AP)->gpr; \ + if (__gpr < 8) { \ + __ptr = *__VA_GP_REGSAVE(AP, __gpr, TYPE *); \ + (AP)->gpr = __gpr + 1; \ + } else { \ + TYPE **__pptr = (TYPE **)(__va_overflow(AP)); \ + __ptr = *__pptr; \ + __va_overflow(AP) = (char *)(__pptr + 1); \ + } \ + } \ + \ + /* Only integrals remaining. */ \ + else { \ + /* longlong is aligned. */ \ + if (sizeof(TYPE) == 8) { \ + unsigned char __gpr = (AP)->gpr; \ + if (__gpr < 7) { \ + __gpr += __gpr & 1; \ + __ptr = __VA_GP_REGSAVE(AP, __gpr, TYPE); \ + (AP)->gpr = __gpr + 2; \ + } else { \ + unsigned long __addr = (unsigned long)(__va_overflow(AP)); \ + __ptr = (TYPE *)((__addr + 7) & -8); \ + (AP)->gpr = 8; \ + __va_overflow(AP) = (char *)(__ptr + 1); \ + } \ + } else if (sizeof(TYPE) == 4) { \ + unsigned char __gpr = (AP)->gpr; \ + if (__gpr < 8) { \ + __ptr = __VA_GP_REGSAVE(AP, __gpr, TYPE); \ + (AP)->gpr = __gpr + 1; \ + } else { \ + __ptr = (TYPE *)__va_overflow(AP); \ + __va_overflow(AP) = (char *)(__ptr + 1); \ + } \ + } else { \ + /* Everything else was promoted to int. */ \ + __va_arg_type_violation(); \ + } \ + } \ + __ptr; \ + })) + +#define va_end(AP) ((void)0) + +/* Copy va_list into another variable of this type. */ +#define __va_copy(dest, src) *(dest) = *(src) + +#elif defined(__alpha__) + +typedef struct { + char *__base; /* Pointer to first integer register. */ + int __offset; /* Byte offset of args so far. */ +} va_list; + +#define va_start(pvar, firstarg) \ + (__builtin_next_arg(firstarg), (pvar) = *(va_list *)__builtin_saveregs()) +#define va_end(__va) ((void)0) + +#define __va_tsize(__type) \ + (((sizeof(__type) + __extension__ sizeof(long long) - 1) / \ + __extension__ sizeof(long long)) * \ + __extension__ sizeof(long long)) + +#define va_arg(__va, __type) \ + (*(((__va).__offset += __va_tsize(__type)), \ + (__type *)(void *)((__va).__base + (__va).__offset - \ + (((__builtin_classify_type(*(__type *)0) == \ + __real_type_class) && \ + (__va).__offset <= (6 * 8)) \ + ? (6 * 8) + 8 \ + : __va_tsize(__type))))) + +#else /* !__sparc__ && !__powerpc__ && !__mips__ && !__alpha__*/ + +typedef char *va_list; + +/* this only works when everything is passed on the stack (i.e. x86) */ +#if __WORDSIZE == 64 +#define va_start(ap, argn) ap = ((char *)&argn) + 8 +#else +#define va_start(ap, argn) ap = ((char *)&argn) + 4 +#endif +#define va_arg(ap, type) \ + (ap += sizeof(type), *(type *)((void *)ap - sizeof(type))) + +#endif + +#ifndef __va_copy +#define __va_copy(x, y) x = y +#endif + +#ifndef va_end +#define va_end(ap) ((void)0) +#endif diff --git a/src/libc/include/stdarg.h b/src/libc/include/stdarg.h new file mode 100644 index 000000000..5e4bdc4ca --- /dev/null +++ b/src/libc/include/stdarg.h @@ -0,0 +1,19 @@ +#ifndef _STDARG_H +#define _STDARG_H + +#include + +typedef char *va_list; +#define va_start(ap, parmn) (void)((ap) = (char *)(&(parmn) + 1)) +#define va_end(ap) (void)((ap) = 0) +#define va_arg(ap, type) (((type *)((ap) = ((ap) + sizeof(type))))[-1]) + +#ifndef va_end +#include +#endif + +#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L +#define va_copy(d, s) __va_copy(d, s) +#endif + +#endif diff --git a/src/libc/include/stdbool.h b/src/libc/include/stdbool.h new file mode 100644 index 000000000..5faf6e899 --- /dev/null +++ b/src/libc/include/stdbool.h @@ -0,0 +1,13 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* believe it or not but the Single Unix Specification actually + * specifies this header, see + * http://www.opengroup.org/onlinepubs/007904975/basedefs/stdbool.h.html */ + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif diff --git a/src/libc/include/stddef.h b/src/libc/include/stddef.h new file mode 100644 index 000000000..a98a6d6e2 --- /dev/null +++ b/src/libc/include/stddef.h @@ -0,0 +1,22 @@ +#ifndef STDDEF_H +#define STDDEF_H + +typedef signed long ptrdiff_t; +typedef unsigned long size_t; +typedef int wchar_t; + +#undef NULL +#if defined(__cplusplus) +#define NULL 0 +#else +#define NULL (void *)0 +#endif + +#undef offsetof +#if defined(__GNUC__) && (__GNUC >= 3) +#define offsetof(type, member) __builtin_offsetof(type, member) +#else +#define offsetof(type, member) ((size_t) & ((type *)0)->member) +#endif + +#endif diff --git a/src/libc/include/stdint.h b/src/libc/include/stdint.h new file mode 100644 index 000000000..0351ed849 --- /dev/null +++ b/src/libc/include/stdint.h @@ -0,0 +1,135 @@ +#ifndef STDINT_H +#define STDINT_H + +#include + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +typedef signed char int_fast8_t; +typedef signed long int int_fast16_t; +typedef signed long int int_fast32_t; +typedef int64_t int_fast64_t; + +typedef unsigned char uint_fast8_t; +typedef unsigned long int uint_fast16_t; +typedef unsigned long int uint_fast32_t; +typedef uint64_t uint_fast64_t; + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ + +#define INT8_MAX (127) +#define INT16_MAX (32767) +#define INT32_MAX (2147483647) +#define INT64_MAX (9223372036854775807ll) + +#define INT8_MIN (-1 - INT8_MAX) +#define INT16_MIN (-1 - INT16_MAX) +#define INT32_MIN (-1 - INT32_MAX) +#define INT64_MIN (-1 - INT64_MAX) + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MAX INT64_MAX +#define INT_LEAST64_MIN INT64_MIN + +#define UINT8_MAX 0xff +#define UINT16_MAX 0xffff +#define UINT32_MAX 0xfffffffful +#define UINT64_MAX 0xffffffffffffffffull + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#if __WORDSIZE == 64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif + +#define SIZE_MAX UINTPTR_MAX +#define PTRDIFF_MIN INTPTR_MIN +#define PTRDIFF_MAX INTPTR_MAX + +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#define INT_FAST16_MIN INTPTR_MIN +#define INT_FAST16_MAX INTPTR_MAX +#define UINT_FAST16_MAX UINTPTR_MAX + +#define INT_FAST32_MIN INTPTR_MIN +#define INT_FAST32_MAX INTPTR_MAX +#define UINT_FAST32_MAX UINTPTR_MAX + +#define SIG_ATOMIC_MAX ((int)(~0u << sizeof(int) * 8 - 1)) +#define SIG_ATOMIC_MIN ((int)((~0u << sizeof(int) * 8 - 1) - 1)) + +#ifndef WCHAR_MIN +#define WCHAR_MIN ((int)(~0u << sizeof(int) * 8 - 1)) +#define WCHAR_MAX ((int)((~0u << sizeof(int) * 8 - 1) - 1)) +#endif + +#define WINT_MIN 0 +#define WINT_MAX (~(wint_t)0) + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ + +/* Signed. */ +#define INT8_C(c) c +#define INT16_C(c) c +#define INT32_C(c) c +#if __WORDSIZE == 64 +#define INT64_C(c) c##L +#else +#define INT64_C(c) c##LL +#endif + +/* Unsigned. */ +#define UINT8_C(c) c +#define UINT16_C(c) c +#define UINT32_C(c) c##U +#if __WORDSIZE == 64 +#define UINT64_C(c) c##UL +#else +#define UINT64_C(c) c##ULL +#endif + +/* Maximal type. */ +#if __WORDSIZE == 64 +#define INTMAX_C(c) c##L +#define UINTMAX_C(c) c##UL +#else +#define INTMAX_C(c) c##LL +#define UINTMAX_C(c) c##ULL +#endif + +#endif diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h new file mode 100644 index 000000000..cd19c779d --- /dev/null +++ b/src/libc/include/stdio.h @@ -0,0 +1,85 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include + +#define BUFSIZE 128 + +struct __stdio_file { + int fd; + int flags; + uint32_t bs; /* read: bytes in buffer */ + uint32_t bm; /* position in buffer */ + uint32_t buflen; /* length of buf */ + char *buf; + struct __stdio_file *next; /* for fflush */ + pid_t popen_kludge; + unsigned char ungetbuf; + char ungotten; +}; + +typedef struct __stdio_file FILE; + +extern FILE *stdin, *stdout, *stderr; + +#define ERRORINDICATOR 1 +#define EOFINDICATOR 2 +#define BUFINPUT 4 +#define BUFLINEWISE 8 +#define NOBUF 16 +#define STATICBUF 32 +#define FDPIPE 64 +#define CANREAD 128 +#define CANWRITE 256 +#define CHECKLINEWISE 512 + +#define _IONBF 0 +#define _IOLBF 1 +#define _IOFBF 2 + +int puts(const char *); +int fputs(const char *s, FILE *stream); +int putchar(int); +int printf(const char *format, ...); +int fprintf(FILE *stream, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int swprintf(wchar_t *, wchar_t const *, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int asprintf(char **ptr, const char *format, ...); + +int scanf(const char *format, ...); +int fscanf(FILE *stream, const char *format, ...); +int sscanf(const char *str, const char *format, ...); + +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + +char getchar(); + +int fflush(FILE *stream); +/* +int vprintf(const char *format, va_list ap); +int vfprintf(FILE *stream, const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); + +int fdprintf(int fd, const char *format, ...) __THROW +__attribute__((__format__(__printf__,2,3))); int vfdprintf(int fd, const char +*format, va_list ap) __THROW __attribute__((__format__(__printf__,2,0))); + +int vscanf(const char *format, va_list ap) __THROW +__attribute__((__format__(__scanf__,1,0))); int vsscanf(const char *str, const +char *format, va_list ap) __THROW __attribute__((__format__(__scanf__,2,0))); +int vfscanf(FILE *stream, const char *format, va_list ap) __THROW +__attribute__((__format__(__scanf__,2,0))); + +*/ + +int fclose(FILE *stream); +FILE *fopen(const char *path, const char *mode); +int feof(FILE *stream); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +char *fgets(char *s, int size, FILE *stream); + +FILE *popen(const char *command, const char *type); +int pclose(FILE *stream); +#endif diff --git a/src/libc/include/stdlib.h b/src/libc/include/stdlib.h new file mode 100644 index 000000000..ca4e03cfc --- /dev/null +++ b/src/libc/include/stdlib.h @@ -0,0 +1,35 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include +#include + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + +void abort(void); +void exit(int); + +void *alloca(size_t); + +void *calloc(size_t, size_t); +void *malloc(size_t); +void free(void *); +void *realloc(void *, size_t); + +char *getenv(const char *name); +int setenv(const char *name, const char *value, int overwrite); +int unsetenv(const char *name); + +int atoi(const char *nptr); + +double strtod(const char *str, char **endptr); +long int strtol(const char *nptr, char **endptr, int base); +long long int strtoll(const char *nptr, char **endptr, int base); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +unsigned long long int strtoull(const char *nptr, char **endptr, int base); + +void qsort(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); + +#endif diff --git a/src/libc/include/string.h b/src/libc/include/string.h new file mode 100644 index 000000000..247364a72 --- /dev/null +++ b/src/libc/include/string.h @@ -0,0 +1,36 @@ +#ifndef STRING_H +#define STRING_H + +#include +#include +#include + +char *strcpy(char *, const char *); + +void *memccpy(void *__restrict__ dest, const void *__restrict__ src, int c, + size_t n); +void *memmove(void *dest, const void *src, size_t n); + +void *memset(void *, int, size_t); +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *, const void *, size_t); + +char *strncpy(char *, const char *, size_t); +int strncmp(const char *s1, const char *s2, size_t n); + +int strcmp(const char *s1, const char *s2); + +size_t strlen(const char *s); + +char *strstr(const char *hatstack, const char *needle); + +char *strdup(const char *s); + +char *strchr(const char *str, int c); +char *strrchr(const char *str, int c); + +void *memchr(const void *s, int c, size_t n); + +char *strerror(int errnum); + +#endif diff --git a/src/libc/include/sys/cdefs.h b/src/libc/include/sys/cdefs.h new file mode 100644 index 000000000..b78712b9d --- /dev/null +++ b/src/libc/include/sys/cdefs.h @@ -0,0 +1,142 @@ +#ifndef _SYS_CDEFS_H +#define _SYS_CDEFS_H + +#ifndef __cplusplus +#define __THROW +#define __BEGIN_DECLS +#define __END_DECLS +#else +#define __THROW throw() +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#endif + +#ifndef __GNUC__ +#define __attribute__(xyz) +#define __extension__ +#endif + +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) +#define __leaf , __leaf__ +#else +#define __leaf +#endif + +#if !defined(__cplusplus) && \ + ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) +#undef __THROW +#define __THROW __attribute__((__nothrow__ __leaf)) +#define __THROWNL __attribute__((__nothrow__)) +#endif + +#ifndef __THROWNL +#define __THROWNL __THROW +#endif + +#if (__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 96)) +#define __pure __attribute__((__pure__)) +#else +#define __pure +#endif + +#if (__GNUC__ == 2) && (__GNUC_MINOR__ < 95) +#define __restrict__ +#endif + +#ifndef __STRICT_ANSI__ +#define restrict __restrict__ +#if __GNUC__ < 3 +#define __builtin_expect(foo, bar) (foo) +#define __expect(foo, bar) (foo) +#define __malloc__ +#else +#define __expect(foo, bar) __builtin_expect((long)(foo), bar) +#define __attribute_malloc__ __attribute__((__malloc__)) +#endif +#endif + +/* idea for these macros taken from Linux kernel */ +#define __likely(foo) __expect((foo), 1) +#define __unlikely(foo) __expect((foo), 0) + +#ifndef __attribute_malloc__ +#define __attribute_malloc__ +#endif + +#define __P(x) x + +#define __ptr_t void * + +#if defined(__STRICT_ANSI__) && __STDC_VERSION__ + 0 < 199900L +#define inline +#endif + +#ifndef __i386__ +#define __regparm__(x) +#endif + +#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2)) +#define __attribute_dontuse__ __attribute__((__deprecated__)) +#else +#define __attribute_dontuse__ +#define __deprecated__ +#endif + +#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) +#define __nonnull(params) __attribute__((__nonnull__ params)) +#else +#define __nonnull(params) +#endif + +#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)) +#define __attribute_used __attribute__((__used__)) +#else +#define __attribute_used +#define __warn_unused_result__ +#endif + +#if (__GNUC__ >= 4) +#define __needsNULL__(x) __sentinel__(x) +#else +#define __needsNULL__(x) +#define __sentinel__ +#endif + +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) +#define __cold__ +#define __hot__ +#endif + +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) +#define __attribute_alloc__(x) +#define __attribute_alloc2__(x, y) +#else +#define __attribute_alloc__(x) __attribute__((alloc_size(x))) +#define __attribute_alloc2__(x, y) __attribute__((alloc_size(x, y))) +#endif + +#if (__GNUC__ < 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ < 5)) +#define __attribute_const__ +#else +#define __attribute_const__ __attribute__((const)) +#endif + +#if (__GNUC__ < 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ < 8)) +#define __attribute_formatarg__(x) +#else +#define __attribute_formatarg__(x) __attribute__((format_arg(x))) +#endif + +#if (__GNUC__ < 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ < 1)) +#define __noinline__ +#else +#define __noinline__ __attribute__((noinline)) +#endif + +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 6)) +#define __hidden__ +#else +#define __hidden__ __attribute__((visibility("hidden"))) +#endif + +#endif diff --git a/src/libc/include/sys/param.h b/src/libc/include/sys/param.h new file mode 100644 index 000000000..7b0e5af6c --- /dev/null +++ b/src/libc/include/sys/param.h @@ -0,0 +1,4 @@ +#ifndef PARAM_H +#define PARAM_H + +#endif diff --git a/src/libc/include/sys/resource.h b/src/libc/include/sys/resource.h new file mode 100644 index 000000000..d4404a759 --- /dev/null +++ b/src/libc/include/sys/resource.h @@ -0,0 +1,92 @@ +#ifndef _SYS_RESOURCE_H +#define _SYS_RESOURCE_H + +#include + +#define RUSAGE_SELF 0 +#define RUSAGE_CHILDREN (-1) +#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */ + +struct rusage { + struct timeval ru_utime; /* user time used */ + struct timeval ru_stime; /* system time used */ + long ru_maxrss; /* maximum resident set size */ + long ru_ixrss; /* integral shared memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary " */ +}; + +struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +#define PRIO_MIN (-20) +#define PRIO_MAX 20 + +#define PRIO_PROCESS 0 +#define PRIO_PGRP 1 +#define PRIO_USER 2 + +#define RLIMIT_CPU 0 /* CPU time in ms */ +#define RLIMIT_FSIZE 1 /* Maximum filesize */ +#define RLIMIT_DATA 2 /* max data size */ +#define RLIMIT_STACK 3 /* max stack size */ +#define RLIMIT_CORE 4 /* max core file size */ +#if defined(__alpha__) +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 8 /* max number of processes */ +#define RLIMIT_NOFILE 6 /* max number of open files */ +#define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */ +#define RLIMIT_AS 7 /* address space limit */ +#elif defined(__mips__) +#define RLIMIT_RSS 7 /* max resident set size */ +#define RLIMIT_NPROC 8 /* max number of processes */ +#define RLIMIT_NOFILE 5 /* max number of open files */ +#define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */ +#define RLIMIT_AS 6 /* address space limit */ +#elif defined(__sparc__) +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 7 /* max number of processes */ +#define RLIMIT_NOFILE 6 /* max number of open files */ +#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit */ +#else +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 6 /* max number of processes */ +#define RLIMIT_NOFILE 7 /* max number of open files */ +#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit */ +#endif +#define RLIMIT_LOCKS 10 /* maximum file locks held */ +#define RLIMIT_SIGPENDING 11 /* max number of pending signals */ +#define RLIMIT_MSGQUEUE 12 /* maximum bytes in POSIX mqueues */ +#define RLIMIT_NICE \ + 13 /* max nice prio allowed to raise to \ + 0-39 for nice level 19 .. -20 */ +#define RLIMIT_RTPRIO 14 /* maximum realtime priority */ +#define RLIMIT_RTTIME 15 /* timeout for RT tasks in us */ +#define RLIM_NLIMITS 16 + +#define RLIM_INFINITY (~0UL) + +int getpriority(int which, int who); +int setpriority(int which, int who, int prio); + +int getrlimit(int resource, struct rlimit *rlim); +int getrusage(int who, struct rusage *usage); +int setrlimit(int resource, const struct rlimit *rlim); + +typedef unsigned long rlim_t; + +#endif diff --git a/src/libc/include/sys/time.h b/src/libc/include/sys/time.h new file mode 100644 index 000000000..51de6fcf6 --- /dev/null +++ b/src/libc/include/sys/time.h @@ -0,0 +1,52 @@ +#ifndef SYS_TIME_H +#define SYS_TIME_H + +#include + +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* microseconds */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct itimerspec { + struct timespec it_interval; /* timer period */ + struct timespec it_value; /* timer expiration */ +}; + +struct itimerval { + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; + +typedef struct timezone *__timezone_ptr_t; + +struct tm { + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + + long int tm_gmtoff; /* Seconds east of UTC. */ + const char *tm_zone; /* Timezone abbreviation. */ +}; + +#endif diff --git a/src/libc/include/sys/types.h b/src/libc/include/sys/types.h new file mode 100644 index 000000000..eeb46f231 --- /dev/null +++ b/src/libc/include/sys/types.h @@ -0,0 +1,27 @@ +#ifndef TYPES_H +#define TYPES_H + +#include +#include + +typedef signed long ssize_t; + +typedef signed long suseconds_t; /* Used for time in microseconds. */ +typedef signed long useconds_t; /* Used for time in microseconds. */ + +typedef signed long long time_t; + +typedef int32_t pid_t; + +typedef uint32_t uid32_t; +typedef uint32_t gid32_t; + +typedef int32_t clockid_t; +typedef int32_t timer_t; + +typedef long int fpos_t; + +typedef uint32_t socklen_t; +typedef uint16_t sa_family_t; + +#endif diff --git a/src/libc/include/time.h b/src/libc/include/time.h new file mode 100644 index 000000000..9f30d830c --- /dev/null +++ b/src/libc/include/time.h @@ -0,0 +1,24 @@ +#ifndef TIME_H +#define TIME_H + +#include + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 +#define CLOCK_REALTIME_HR 4 +#define CLOCK_MONOTONIC_HR 5 + +int clock_gettime(clockid_t clock_id, struct timespec *tp); + +time_t time(time_t *t); +time_t timegm(struct tm *timeptr); +time_t mktime(struct tm *timeptr); + +char *ctime(const time_t *timep); + +struct tm *localtime_r(const time_t *t, struct tm *r); +struct tm *gmtime_r(const time_t *t, struct tm *r); + +#endif diff --git a/src/libc/include/unistd.h b/src/libc/include/unistd.h new file mode 100644 index 000000000..d8380d227 --- /dev/null +++ b/src/libc/include/unistd.h @@ -0,0 +1,14 @@ +#ifndef UNISTD_H +#define UNISTD_H + +#include + +extern int optind, opterr, optopt; +extern char *optarg; +/* int getopt(int argc, char *const argv[], const char *options); */ + +/* int open(const char* pathname,int flags, ...); */ +int close(int fd); +ssize_t read(int fd, void *buf, size_t len); + +#endif diff --git a/src/libc/instrumentor.py b/src/libc/instrumentor.py new file mode 100644 index 000000000..f25f1db1c --- /dev/null +++ b/src/libc/instrumentor.py @@ -0,0 +1,388 @@ +import sys +import tempfile +import logging + +from pycparser import c_ast, parse_file +from pycparser.c_generator import CGenerator +from pycparser.plyparser import ParseError + +sys.setrecursionlimit(10000) + +log = logging.getLogger(__name__) + +class MethodNotImplemented(Exception): + """Exception raised when visit method not available""" + + def __init__(self, name): + self.name = name + self.message = f"visit_{name}: method not implemented" + super().__init__(self.message) + +class ParsingError(Exception): + + def __init__(self, message): + self.message = message + super().__init__(self.message) + +def instrument(file : str, includes : list[str]) -> None: + visitor = BinopVisitor() + + includes = list(map(lambda inc: f"-I{inc}", includes)) + try: + ast = parse_file( + file, + use_cpp=True, + cpp_path="clang", + cpp_args=[r"-E"] + includes + ) + except ParseError as e: + raise ParsingError(str(e)) + + n_ast = visitor.to_string(visitor.visit(ast)) + with open(file, 'w') as f: + f.write(n_ast) + +class BinopVisitor(c_ast.NodeVisitor): + + def __init__(self, rm_boolops=True): + self.counter = 0 + self.rm_boolops = rm_boolops + self.ctx = [ "" ] + + def _safe_visit(self, node): + return self.visit(node) if node is not None else node + + def _safe_map(self, f, lst): + return map(f, lst) if lst is not None else lst + + def _fresh_int(self): + fresh = self.counter + self.counter += 1 + return fresh + + def _get_binop_func(self, op): + if op == "&&": + return "and_" + elif op == "||": + return "or_" + else: + raise RuntimeError + + def to_string(self, node): + return CGenerator().visit(node) + + # def visit_ArrayDecl(self, node): + # return node + + # def visit_ArrayRef(self, node): + # return node + + def visit_Assignment(self, node): + return c_ast.Assignment( + node.op, + self._safe_visit(node.lvalue), + self._safe_visit(node.rvalue), + node.coord + ) + + def visit_BinaryOp(self, node): + if (node.op in ["&&", "||"]) and self.rm_boolops: + return c_ast.FuncCall( + c_ast.ID(self._get_binop_func(node.op)), + c_ast.ExprList([ + self._safe_visit(node.left), + self._safe_visit(node.right) + ]), + node.coord + ) + return c_ast.BinaryOp( + node.op, + self._safe_visit(node.left), + self._safe_visit(node.right), + node.coord + ) + + # def visit_Break(self, node): + # return node + + def visit_Case(self, node): + return c_ast.Case( + self._safe_visit(node.expr), + map(self._safe_visit, node.stmts), + node.coord + ) + + def visit_Cast(self, node): + return c_ast.Cast( + node.to_type, + self._safe_visit(node.expr), + node.coord + ) + + def visit_Compound(self, node): + return c_ast.Compound( + self._safe_map(self._safe_visit, node.block_items), + node.coord + ) + + def visit_CompoundLiteral(self, node): + return c_ast.CompoundLiteral( + node.type, + self._safe_visit(node.init), + ) + + # def visit_Constant(self, node): + # return node + + # def visit_Continue(self, node): + # return node + + def visit_Decl(self, node): + return c_ast.Decl( + node.name, + node.quals, + node.align, + node.storage, + node.funcspec, + self._safe_visit(node.type), + node.init, + node.bitsize, + node.coord + ) + + def visit_DeclList(self, node): + return c_ast.DeclList( + map(self._safe_visit, node.decls), + node.coord + ) + + def visit_Default(self, node): + return c_ast.Default( + map(self._safe_visit, node.stmts), + node.coord + ) + + def visit_DoWhile(self, node): + # _ = c_ast.FuncCall( + # c_ast.ID("IFG"), + # c_ast.ExprList([ + # self._safe_visit(node.cond), + # c_ast.Constant("int", str(self._fresh_int())) + # ]), + # node.coord + # ) + return c_ast.DoWhile( + self._safe_visit(node.cond), + self._safe_visit(node.stmt), + node.coord + ) + + # def visit_EllipsisParam(self, node): + # return node + + # def visit_EmptyStatement(self, node): + # return node + + # def visit_Enum(self, node): + # return node + + # def visit_Enumerator(self, node): + # return node + + # def visit_EnumeratorList(self, node): + # return node + + def visit_ExprList(self, node): + return c_ast.ExprList( + map(self._safe_visit, node.exprs), + node.coord + ) + + def visit_FileAST(self, node): + return c_ast.FileAST( + map(self._safe_visit, node.ext), + node.coord + ) + + def visit_For(self, node): + # _ = c_ast.FuncCall( + # c_ast.ID("IFG"), + # c_ast.ExprList([ + # self._safe_visit(node.cond), + # c_ast.Constant("int", str(self._fresh_int())) + # ]), + # node.coord + # ) + return c_ast.For( + self._safe_visit(node.init), + self._safe_visit(node.cond), + self._safe_visit(node.next), + self._safe_visit(node.stmt), + node.coord + ) + + def visit_FuncCall(self, node): + tmp = self.rm_boolops + self.rm_boolops = True + n_args = self._safe_visit(node.args) + self.rm_boolops = tmp + return c_ast.FuncCall( + node.name, + n_args, + node.coord + ) + + def visit_FuncDecl(self, node): + n_args = node.args + if self.ctx[-1] == "main" and n_args: + n_args = None + return c_ast.FuncDecl(n_args, node.type, node.coord) + + def visit_FuncDef(self, node): + self.ctx.append(node.decl.name) + n_decl = self._safe_visit(node.decl) + self.ctx.pop() + return c_ast.FuncDef( + n_decl, + node.param_decls, + self._safe_visit(node.body), + node.coord + ) + + # def visit_Goto(self, node): + # return node + + # def visit_ID(self, node): + # return node + + # def visit_IdentifierType(self, node): + # return node + + def visit_If(self, node): + # _ = c_ast.FuncCall( + # c_ast.ID("IFG"), + # c_ast.ExprList([ + # self._safe_visit(node.cond), + # c_ast.Constant("int", str(self._fresh_int())) + # ]), + # node.coord + # ) + return c_ast.If( + self._safe_visit(node.cond), + self._safe_visit(node.iftrue), + self._safe_visit(node.iffalse), + node.coord + ) + + def visit_InitList(self, node): + return c_ast.InitList( + map(self._safe_visit, node.exprs), + node.coord + ) + + def visit_Label(self, node): + return c_ast.Label( + node.name, + self.visit(node.stmt), + node.coord, + ) + + def visit_NamedInitializer(self, node): + return c_ast.NamedInitializer( + map(self._safe_visit, node.name), + self._safe_visit(node.expr), + node.coord + ) + + def visit_ParamList(self, node): + return c_ast.ParamList( + map(self._safe_visit, node.params), + node.coord + ) + + # def visit_PtrDecl(self, node): + # return node + + def visit_Return(self, node): + return c_ast.Return( + self._safe_visit(node.expr), + node.coord + ) + + # def visit_Struct(self, node): + # return node + + # def visit_StructRef(self, node): + # return node + + def visit_Switch(self, node): + # _ = c_ast.FuncCall( + # c_ast.ID("IFG"), + # c_ast.ExprList([ + # self._safe_visit(node.cond), + # c_ast.Constant("int", str(self._fresh_int())) + # ]), + # node.coord + # ) + return c_ast.Switch( + self._safe_visit(node.cond), + self._safe_visit(node.stmt), + node.coord + ) + + def visit_TernaryOp(self, node): +# return c_ast.FuncCall( +# c_ast.ID("ite"), +# c_ast.ExprList([ +# self._safe_visit(node.cond), +# self._safe_visit(node.iftrue), +# self._safe_visit(node.iffalse) +# ]), +# node.coord +# ) + return c_ast.TernaryOp( + self._safe_visit(node.cond), + self._safe_visit(node.iftrue), + self._safe_visit(node.iffalse), + node.coord + ) + + # def visit_TypeDecl(self, node): + # return node + + # def visit_Typedef(self, node): + # return node + + # def visit_Typename(self, node): + # return node + + def visit_UnaryOp(self, node): + return c_ast.UnaryOp( + node.op, + self._safe_visit(node.expr), + node.coord + ) + + # def visit_Union(self, node): + # return node + + def visit_While(self, node): + # _ = c_ast.FuncCall( + # c_ast.ID("IFG"), + # c_ast.ExprList([ + # self._safe_visit(node.cond), + # c_ast.Constant("int", str(self._fresh_int())) + # ]), + # node.coord + # ) + return c_ast.While( + self._safe_visit(node.cond), + self._safe_visit(node.stmt), + node.coord + ) + + # def visit_Pragma(self, node): + # return node + + def generic_visit(self, node): + return node diff --git a/src/libc/script.ml b/src/libc/script.ml new file mode 100644 index 000000000..0b7f6a6cd --- /dev/null +++ b/src/libc/script.ml @@ -0,0 +1,34 @@ +let () = + if Array.length Sys.argv < 2 then + Format.ksprintf failwith "usage: %s " Sys.argv.(0) + +let () = + for i = 1 to Array.length Sys.argv - 1 do + let c_filename = Sys.argv.(i) in + if Filename.extension c_filename = ".c" then begin + let filename = Filename.basename c_filename |> Filename.chop_extension in + let bc_filename = Format.sprintf "%s.bc" filename in + let n = + Format.ksprintf Sys.command + "clang -emit-llvm -g -O3 -ffreestanding --target=wasm32 -c -m32 \ + -Iinclude -Wno-parentheses-equality -Wno-attributes \ + -Wno-return-type -Wno-int-conversion \ + -Wno-incompatible-pointer-types \ + -Wno-incompatible-function-pointer-types -Wno-pointer-sign \ + -Wno-bitfield-constant-conversion \ + -Wno-implicit-function-declaration -fbracket-depth=512 -o %s %s" + bc_filename c_filename + in + if n <> 0 then exit n; + let n = + Format.ksprintf Sys.command "opt -O3 %s -o %s" bc_filename bc_filename + in + if n <> 0 then exit n; + let o_filename = Format.sprintf "%s.o" filename in + let n = + Format.ksprintf Sys.command + "llc -O3 -march=wasm32 -filetype=obj %s -o %s" bc_filename o_filename + in + if n <> 0 then exit n + end + done diff --git a/src/libc/src/assert_fail.c b/src/libc/src/assert_fail.c new file mode 100644 index 000000000..4794c9abb --- /dev/null +++ b/src/libc/src/assert_fail.c @@ -0,0 +1,6 @@ +#include + +void __assert_fail(const char *assertion, const char *file, unsigned int line, + const char *function) { + owi_assert(0); +} diff --git a/src/libc/src/ctype.c b/src/libc/src/ctype.c new file mode 100644 index 000000000..59a7e0bbf --- /dev/null +++ b/src/libc/src/ctype.c @@ -0,0 +1,50 @@ +#include + +int isascii(int ch) { return (unsigned int)ch < 128u; } + +int isblank(int ch) { return ch == ' ' || ch == '\t'; } + +int isalnum(int ch) { + return (unsigned int)((ch | 0x20) - 'a') < 26u || + (unsigned int)(ch - '0') < 10u; +} + +int isalpha(int ch) { return (unsigned int)((ch | 0x20) - 'a') < 26u; } + +int isdigit(int ch) { return (unsigned int)(ch - '0') < 10u; } + +int isspace(int ch) { return (unsigned int)(ch - 9) < 5u || ch == ' '; } + +int isupper(int ch) { return (unsigned int)(ch - 'A') < 26u; } + +int islower(int ch) { return (unsigned int)(ch - 'a') < 26u; } + +int tolower(int ch) { + if ((unsigned int)(ch - 'A') < 26u) + ch += 'a' - 'A'; + return ch; +} + +inline int toupper(int ch) { + if ((unsigned int)(ch - 'a') < 26u) + ch += 'A' - 'a'; + return ch; +} + +int toascii(int c) { return (c & 0x7f); } + +int isprint(int ch) { + ch &= 0x7f; + return (ch >= 32 && ch < 127); +} + +int ispunct(int ch) { return isprint(ch) && !isalnum(ch) && !isspace(ch); } + +int iscntrl(int ch) { return (unsigned int)ch < 32u || ch == 127; } + +int isxdigit(int ch) { + return (unsigned int)(ch - '0') < 10u || + (unsigned int)((ch | 0x20) - 'a') < 6u; +} + +int isgraph(int ch) { return (unsigned int)(ch - '!') < 127u - '!'; } diff --git a/src/libc/src/errno.c b/src/libc/src/errno.c new file mode 100644 index 000000000..043c6693a --- /dev/null +++ b/src/libc/src/errno.c @@ -0,0 +1,2 @@ +#include + diff --git a/src/libc/src/math.c b/src/libc/src/math.c new file mode 100644 index 000000000..fbdee445e --- /dev/null +++ b/src/libc/src/math.c @@ -0,0 +1,5 @@ +#include + +// TODO: External to OWI +double sin(double x) { return x; } +double cos(double x) { return x; } diff --git a/src/libc/src/memccpy.c b/src/libc/src/memccpy.c new file mode 100644 index 000000000..cd6fe91f9 --- /dev/null +++ b/src/libc/src/memccpy.c @@ -0,0 +1,15 @@ +#include +#include + +void *memccpy(void *dst, const void *src, int c, size_t count) { + char *a = dst; + const char *b = src; + while (count--) { + *a++ = *b; + if (*b == c) { + return (void *)a; + } + b++; + } + return 0; +} diff --git a/src/libc/src/memchr.c b/src/libc/src/memchr.c new file mode 100644 index 000000000..20b2a6321 --- /dev/null +++ b/src/libc/src/memchr.c @@ -0,0 +1,9 @@ +#include + +void *memchr(const void *s, int c, size_t n) { + const unsigned char *pc = (unsigned char *)s; + for (; n--; pc++) + if (*pc == c) + return ((void *)pc); + return 0; +} diff --git a/src/libc/src/memcmp.c b/src/libc/src/memcmp.c new file mode 100644 index 000000000..22bb62c31 --- /dev/null +++ b/src/libc/src/memcmp.c @@ -0,0 +1,21 @@ +#include +#include + +/* gcc is broken and has a non-SUSv2 compliant internal prototype. + * This causes it to warn about a type mismatch here. Ignore it. */ +int memcmp(const void *dst, const void *src, size_t count) { + register int r; + register const unsigned char *d = dst; + register const unsigned char *s = src; + ++count; + while (--count) { + // TODO: check this change ! + r = (*d - *s); + if (r) { + return r; + } + ++d; + ++s; + } + return 0; +} diff --git a/src/libc/src/memcpy.c b/src/libc/src/memcpy.c new file mode 100644 index 000000000..b8be62ef4 --- /dev/null +++ b/src/libc/src/memcpy.c @@ -0,0 +1,11 @@ +#include + +void *memcpy(void *dst, const void *src, size_t n) { + void *res = dst; + unsigned char *c1, *c2; + c1 = (unsigned char *)dst; + c2 = (unsigned char *)src; + while (n--) + *c1++ = *c2++; + return (res); +} diff --git a/src/libc/src/memmove.c b/src/libc/src/memmove.c new file mode 100644 index 000000000..5fe665703 --- /dev/null +++ b/src/libc/src/memmove.c @@ -0,0 +1,19 @@ +#include +#include + +void *memmove(void *dst, const void *src, size_t count) { + char *a = dst; + const char *b = src; + if (src != dst) { + if (src > dst) { + while (count--) + *a++ = *b++; + } else { + a += count - 1; + b += count - 1; + while (count--) + *a-- = *b--; + } + } + return dst; +} diff --git a/src/libc/src/memset.c b/src/libc/src/memset.c new file mode 100644 index 000000000..8a3998f86 --- /dev/null +++ b/src/libc/src/memset.c @@ -0,0 +1,12 @@ +#include +#include + +/* gcc is broken and has a non-SUSv2 compliant internal prototype. + * This causes it to warn about a type mismatch here. Ignore it. */ +void *memset(void *dst, int s, size_t count) { + register char *a = dst; + count++; /* this actually creates smaller code than using count-- */ + while (--count) + *a++ = s; + return dst; +} diff --git a/src/libc/src/netinet_in.c b/src/libc/src/netinet_in.c new file mode 100644 index 000000000..e0c66163e --- /dev/null +++ b/src/libc/src/netinet_in.c @@ -0,0 +1,13 @@ +#include + +uint32_t htonl(uint32_t hostlong) { + return (hostlong >> 24) | ((hostlong & 0xff0000) >> 8) | + ((hostlong & 0xff00) << 8) | (hostlong << 24); +} + +uint16_t htons(uint16_t hostshort) { + return ((hostshort >> 8) & 0xff) | (hostshort << 8); +} + +uint32_t ntohl(uint32_t hostlong) __attribute__((weak, alias("htonl"))); +uint16_t ntohs(uint16_t hostshort) __attribute__((weak, alias("htons"))); diff --git a/src/libc/src/owi.c b/src/libc/src/owi.c new file mode 100644 index 000000000..23745a9ab --- /dev/null +++ b/src/libc/src/owi.c @@ -0,0 +1,55 @@ +#include + +__attribute__((import_module("summaries"), import_name("alloc"))) void * +__owi_alloc(void *, unsigned int); +__attribute__((import_module("summaries"), import_name("dealloc"))) void +__owi_dealloc(void *); + +__attribute__((import_module("symbolic"), import_name("i32_symbol"))) int +__i32(); +__attribute__((import_module("symbolic"), import_name("i64_symbol"))) long long +__i64(); +__attribute__((import_module("symbolic"), import_name("f32_symbol"))) float +__f32(); +__attribute__((import_module("symbolic"), import_name("f64_symbol"))) double +__f64(); + +__attribute__((import_module("symbolic"), import_name("assume"))) void +__owi_assume(int); +__attribute__((import_module("symbolic"), import_name("assert"))) void +__owi_assert(int); + +void *owi_malloc(void *base, unsigned int size) { return __owi_alloc(base, size); } +void owi_free(void *base) { __owi_dealloc(base); } + +int owi_i32() { return __i32(); } +long long owi_i64() { return __i64(); } +float owi_f32() { return __f32(); } +double owi_f64() { return __f64(); } + +void owi_assume(int c) { __owi_assume(c); } +void owi_assert(int c) { __owi_assert(c); } + +void assume(int) __attribute__((weak, alias("owi_assume"))); + +int and_(int a, int b) { + __asm__ __volatile__("local.get 0;" + "i32.const 0;" + "i32.ne;" + "local.get 1;" + "i32.const 0;" + "i32.ne;" + "i32.and;" + "return;"); +} + +int or_(int a, int b) { + __asm__ __volatile__("local.get 0;" + "i32.const 0;" + "i32.ne;" + "local.get 1;" + "i32.const 0;" + "i32.ne;" + "i32.or;" + "return;"); +} diff --git a/src/libc/src/pthread.c b/src/libc/src/pthread.c new file mode 100644 index 000000000..3cd10e1c3 --- /dev/null +++ b/src/libc/src/pthread.c @@ -0,0 +1,67 @@ +#include + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) { return 0; } +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { return 0; } + +int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind) { + return 0; +} +int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind) { + return 0; +} + +int pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) { + return 0; +} +int pthread_mutex_lock(pthread_mutex_t *mutex) { return 0; } +int pthread_mutex_unlock(pthread_mutex_t *mutex) { return 0; } +int pthread_mutex_trylock(pthread_mutex_t *mutex) { return 0; } +int pthread_mutex_destroy(pthread_mutex_t *mutex) { return 0; } + +int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr) { + return 0; +} +int pthread_cond_destroy(pthread_cond_t *cond) { return 0; } +int pthread_cond_signal(pthread_cond_t *cond) { return 0; } +int pthread_cond_broadcast(pthread_cond_t *cond) { return 0; } +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) { + return 0; +} +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { return 0; } + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) { + return 0; +} +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { return 0; } + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { return 0; } +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { return 0; } +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { return 0; } +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { return 0; } +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { return 0; } +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { return 0; } +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { return 0; } + +int pthread_detach(pthread_t __th) { return 0; } +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { + return 0; +} +int pthread_attr_init(pthread_attr_t *attr) { return 0; } +int pthread_attr_destroy(pthread_attr_t *attr) { return 0; } +int pthread_attr_setstacksize(pthread_attr_t *attr, const size_t stacksize) { + return 0; +} +int pthread_create(pthread_t *__threadarg, const pthread_attr_t *__attr, + void *(*__start_routine)(void *), void *__arg) { + return 0; +} +int pthread_join(pthread_t __th, void **__thread_return) { return 0; } +pthread_t pthread_self(void) { return 0; } +int pthread_equal(pthread_t __thread1, pthread_t __thread2) { return 0; } diff --git a/src/libc/src/setjmp.c b/src/libc/src/setjmp.c new file mode 100644 index 000000000..86f4c039c --- /dev/null +++ b/src/libc/src/setjmp.c @@ -0,0 +1,3 @@ +#include + +int setjmp(struct __jmp_buf_tag *tag) { return 0; } diff --git a/src/libc/src/start.c b/src/libc/src/start.c new file mode 100644 index 000000000..4d1deafb5 --- /dev/null +++ b/src/libc/src/start.c @@ -0,0 +1,3 @@ +extern int main(); + +void _start() { main(); } diff --git a/src/libc/src/stderr.c b/src/libc/src/stderr.c new file mode 100644 index 000000000..6273ec86a --- /dev/null +++ b/src/libc/src/stderr.c @@ -0,0 +1,25 @@ +#include + +static FILE __stderr = { + .fd = 2, + .flags = NOBUF | CANWRITE, + .bs = 0, + .bm = 0, + .buflen = 0, + .buf = 0, + .next = 0, + .popen_kludge = 0, + .ungetbuf = 0, + .ungotten = 0, +#ifdef WANT_THREAD_SAFE + .m = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +#endif +}; + +FILE *stderr = &__stderr; + +/* +int __fflush_stderr(void) { + return fflush(stderr); +} +*/ diff --git a/src/libc/src/stdio.c b/src/libc/src/stdio.c new file mode 100644 index 000000000..fed8b5777 --- /dev/null +++ b/src/libc/src/stdio.c @@ -0,0 +1,40 @@ +#include + +int puts(const char *str) { return 0; } + +int fputs(const char *s, FILE *stream) { return 0; } + +int putchar(int c) { return 0; } + +int printf(const char *format, ...) { return 0; } + +int fprintf(FILE *stream, const char *format, ...) { return 0; } + +int sprintf(char *str, const char *format, ...) { return 0; } + +int swprintf(wchar_t *ptr1, const wchar_t *ptr2, ...) { return 0; } + +int snprintf(char *str, size_t size, const char *format, ...) { return 0; } + +int asprintf(char **ptr, const char *format, ...) { return 0; } + +int scanf(const char *format, ...) { return 0; } +int fscanf(FILE *stream, const char *format, ...) { return 0; } +int sscanf(const char *str, const char *format, ...) { return 0; } + +int fflush(FILE *stream) { return 0; } + +int fclose(FILE *stream) { return 0; } +FILE *fopen(const char *path, const char *mode) { return 0; } +int feof(FILE *stream) { return 0; } +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { + return 0; +} +char *fgets(char *s, int size, FILE *stream) { return 0; } + +FILE *popen(const char *command, const char *type) { return 0; } +int pclose(FILE *stream) { return 0; } + +int vsnprintf(char *str, size_t size, const char *format, va_list ap) { + return 0; +} diff --git a/src/libc/src/stdlib.c b/src/libc/src/stdlib.c new file mode 100644 index 000000000..5a7501cd9 --- /dev/null +++ b/src/libc/src/stdlib.c @@ -0,0 +1,408 @@ +#include +#include +#include +#include +#include + +#define ABS_LONG_MIN 2147483648UL + +#ifndef WANT_STRTOD_WITHOUT_LONG_DOUBLE +#define ldbltype long double +#else +#define ldbltype double +#endif + +__attribute__((import_module("summaries"), import_name("abort"))) void +owi_abort(void); +__attribute__((import_module("summaries"), import_name("exit"))) void +owi_exit(int); + +void abort() { owi_abort(); } +void exit(int status) { owi_exit(status); } + +extern unsigned char __heap_base; +unsigned int bump_pointer = &__heap_base; + +void *malloc(size_t size) { + unsigned int r = bump_pointer; + /* for (int i = 0; i < size; ++i) */ + /* *((unsigned char *)bump_pointer + i) = 'i'; */ + bump_pointer += size; + return (void *)owi_malloc(r, size); +} + +void *alloca(size_t size) { return malloc(size); } + +void *calloc(size_t nmemb, size_t size) { + unsigned int r = bump_pointer; + /* for (int i = 0; i < nmemb * size; ++i) */ + /* *((unsigned int *)(bump_pointer + i)) = 0; */ + bump_pointer += (nmemb * size); + return (void *)owi_malloc(r, nmemb * size); +} + +void *realloc(void *ptr, size_t size) { + owi_free(ptr); + return (void *)owi_malloc(ptr, size); +} + +void free(void *ptr) { owi_free(ptr); } + +char *getenv(const char *name) { return (char *)0; } +int setenv(const char *name, const char *value, int overwrite) { return 0; } +int unsetenv(const char *name) { return 0; } + +int atoi(const char *s) { + long int v = 0; + int sign = 1; + while (*s == ' ' || (unsigned int)(*s - 9) < 5u) + s++; + switch (*s) { + case '-': + sign = -1; /* fall through */ + case '+': + ++s; + } + while ((unsigned int)(*s - '0') < 10u) { + v = v * 10 + *s - '0'; + ++s; + } + return sign == -1 ? -v : v; +} + +double strtod(const char *s, char **endptr) { + register const char *p = s; + register ldbltype value = 0.; + int sign = +1; + ldbltype factor; + unsigned int expo; + + while (isspace(*p)) + p++; + + switch (*p) { + case '-': + sign = -1; /* fall through */ + case '+': + p++; + default: + break; + } + + while ((unsigned int)(*p - '0') < 10u) + value = value * 10 + (*p++ - '0'); + + if (*p == '.') { + factor = 1.; + + p++; + while ((unsigned int)(*p - '0') < 10u) { + factor *= 0.1; + value += (*p++ - '0') * factor; + } + } + + if ((*p | 32) == 'e') { + expo = 0; + factor = 10.; + + switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem 'E' + // folgenden MUSS. + case '-': + factor = 0.1; /* fall through */ + case '+': + p++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + value = 0.; + p = s; + goto done; + } + + while ((unsigned int)(*p - '0') < 10u) + expo = 10 * expo + (*p++ - '0'); + + while (1) { + if (expo & 1) + value *= factor; + if ((expo >>= 1) == 0) + break; + factor *= factor; + } + } + +done: + if (endptr != NULL) + *endptr = (char *)p; + + return value * sign; +} + +long int strtol(const char *nptr, char **endptr, int base) { + int neg = 0; + unsigned long int v; + const char *orig = nptr; + + while (isspace(*nptr)) + nptr++; + + if (*nptr == '-' && isalnum(nptr[1])) { + neg = -1; + ++nptr; + } + v = strtoul(nptr, endptr, base); + if (endptr && *endptr == nptr) + *endptr = (char *)orig; + if (v >= ABS_LONG_MIN) { + if (v == ABS_LONG_MIN && neg) { + errno = 0; + return v; + } + errno = ERANGE; + return (neg ? LONG_MIN : LONG_MAX); + } + return (neg ? -v : v); +} + +long long int strtoll(const char *nptr, char **endptr, int base) { + int neg = 0; + unsigned long long int v; + const char *orig = nptr; + + while (isspace(*nptr)) + nptr++; + + if (*nptr == '-' && isalnum(nptr[1])) { + neg = -1; + nptr++; + } + v = strtoull(nptr, endptr, base); + if (endptr && *endptr == nptr) + *endptr = (char *)orig; + if (v > LLONG_MAX) { + if (v == 0x8000000000000000ull && neg) { + errno = 0; + return v; + } + errno = ERANGE; + return (neg ? LLONG_MIN : LLONG_MAX); + } + return (neg ? -v : v); +} + +unsigned long int strtoul(const char *ptr, char **endptr, int base) { + int neg = 0, overflow = 0; + unsigned long int v = 0; + const char *orig; + const char *nptr = ptr; + + while (isspace(*nptr)) + ++nptr; + + if (*nptr == '-') { + neg = 1; + nptr++; + } else if (*nptr == '+') + ++nptr; + orig = nptr; + if (base == 16 && nptr[0] == '0') + goto skip0x; + if (base) { + register unsigned int b = base - 2; + if (b > 34) { + errno = EINVAL; + return 0; + } + } else { + if (*nptr == '0') { + base = 8; + skip0x: + if ((nptr[1] == 'x' || nptr[1] == 'X') && isxdigit(nptr[2])) { + nptr += 2; + base = 16; + } + } else + base = 10; + } + while (*nptr) { + register unsigned char c = *nptr; + c = (c >= 'a' ? c - 'a' + 10 + : c >= 'A' ? c - 'A' + 10 + : c <= '9' ? c - '0' + : 0xff); + if (c >= base) + break; /* out of base */ + { + register unsigned long x = (v & 0xff) * base + c; + register unsigned long w = (v >> 8) * base + (x >> 8); + if (w > (ULONG_MAX >> 8)) + overflow = 1; + v = (w << 8) + (x & 0xff); + } + ++nptr; + } + if (nptr == orig) { /* no conversion done */ + nptr = ptr; + errno = EINVAL; + v = 0; + } + if (endptr) + *endptr = (char *)nptr; + if (overflow) { + errno = ERANGE; + return ULONG_MAX; + } + return (neg ? -v : v); +} + +unsigned long long int strtoull(const char *ptr, char **endptr, int base) { + int neg = 0, overflow = 0; + long long int v = 0; + const char *orig; + const char *nptr = ptr; + + while (isspace(*nptr)) + ++nptr; + + if (*nptr == '-') { + neg = 1; + nptr++; + } else if (*nptr == '+') + ++nptr; + orig = nptr; + if (base == 16 && nptr[0] == '0') + goto skip0x; + if (base) { + register unsigned int b = base - 2; + if ((b > 34)) { + errno = EINVAL; + return 0; + } + } else { + if (*nptr == '0') { + base = 8; + skip0x: + if (((*(nptr + 1) == 'x') || (*(nptr + 1) == 'X')) && isxdigit(nptr[2])) { + nptr += 2; + base = 16; + } + } else + base = 10; + } + while ((*nptr)) { + register unsigned char c = *nptr; + c = (c >= 'a' ? c - 'a' + 10 + : c >= 'A' ? c - 'A' + 10 + : c <= '9' ? c - '0' + : 0xff); + if ((c >= base)) + break; /* out of base */ + { + register unsigned long x = (v & 0xff) * base + c; + register unsigned long long w = (v >> 8) * base + (x >> 8); + if (w > (ULLONG_MAX >> 8)) + overflow = 1; + v = (w << 8) + (x & 0xff); + } + ++nptr; + } + if ((nptr == orig)) { /* no conversion done */ + nptr = ptr; + errno = EINVAL; + v = 0; + } + if (endptr) + *endptr = (char *)nptr; + if (overflow) { + errno = ERANGE; + return ULLONG_MAX; + } + return (neg ? -v : v); +} + +static void exch(char *base, size_t size, size_t a, size_t b) { + char *x = base + a * size; + char *y = base + b * size; + while (size) { + char z = *x; + *x = *y; + *y = z; + --size; + ++x; + ++y; + } +} + +/* Quicksort with 3-way partitioning, ala Sedgewick */ +/* Blame him for the scary variable names */ +/* http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf */ +static void quicksort(char *base, size_t size, ssize_t l, ssize_t r, + int (*compar)(const void *, const void *)) { + ssize_t i = l - 1, j = r, p = l - 1, q = r, k; + char *v = base + r * size; + if (r <= l) + return; + /* + We chose the rightmost element in the array to be sorted as pivot, + which is OK if the data is random, but which is horrible if the + data is already sorted. Try to improve by chosing the middle + element instead. + */ + exch(base, size, l + (r - l) / 2, r); + + for (;;) { + while (++i != r && compar(base + i * size, v) < 0) + ; + while (compar(v, base + (--j) * size) < 0) + if (j == l) + break; + if (i >= j) + break; + exch(base, size, i, j); + if (compar(base + i * size, v) == 0) + exch(base, size, ++p, i); + if (compar(v, base + j * size) == 0) + exch(base, size, j, --q); + } + exch(base, size, i, r); + j = i - 1; + ++i; + for (k = l; k < p; k++, j--) + exch(base, size, k, j); + for (k = r - 1; k > q; k--, i++) + exch(base, size, i, k); + quicksort(base, size, l, j, compar); + quicksort(base, size, i, r, compar); +} + +void qsort(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)) { + /* check for integer overflows */ + if (nmemb >= (((size_t)-1) >> 1) || size >= (((size_t)-1) >> 1)) + return; +#if 0 + if (sizeof(size_t) < sizeof(unsigned long long)) { + if ((unsigned long long)size * nmemb > (size_t)-1) return; + } else { + if (size*nmemb/nmemb != size) return; + } +#endif + if (nmemb > 1) + quicksort(base, size, 0, nmemb - 1, compar); +} + +int posix_memalign(void **memptr, size_t alignment, size_t size) { return 0; } + +// TODO: OWI external +char getchar() { return ' '; } diff --git a/src/libc/src/stdout.c b/src/libc/src/stdout.c new file mode 100644 index 000000000..f055946aa --- /dev/null +++ b/src/libc/src/stdout.c @@ -0,0 +1,26 @@ +#include + +static char __stdout_buf[BUFSIZE]; +static FILE __stdout = { + .fd = 1, + .flags = BUFLINEWISE | STATICBUF | CANWRITE | CHECKLINEWISE, + .bs = 0, + .bm = 0, + .buflen = BUFSIZE, + .buf = __stdout_buf, + .next = 0, + .popen_kludge = 0, + .ungetbuf = 0, + .ungotten = 0, +#ifdef WANT_THREAD_SAFE + .m = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +#endif +}; + +FILE *stdout = &__stdout; + +/* +int __fflush_stdout(void) { + return fflush(stdout); +} +*/ diff --git a/src/libc/src/strchr.c b/src/libc/src/strchr.c new file mode 100644 index 000000000..41577bd63 --- /dev/null +++ b/src/libc/src/strchr.c @@ -0,0 +1,15 @@ +#include + +char *strchr(register const char *t, int c) { + register char ch; + + ch = c; + for (;;) { + if (*t == ch) + break; + if (!*t) + return 0; + ++t; + } + return (char *)t; +} diff --git a/src/libc/src/strcmp.c b/src/libc/src/strcmp.c new file mode 100644 index 000000000..17e5cb9dc --- /dev/null +++ b/src/libc/src/strcmp.c @@ -0,0 +1,7 @@ +#include + +int strcmp(const char *s1, const char *s2) { + while (*s1 && *s1 == *s2) + s1++, s2++; + return (*s1 - *s2); +} diff --git a/src/libc/src/strcpy.c b/src/libc/src/strcpy.c new file mode 100644 index 000000000..802b2164a --- /dev/null +++ b/src/libc/src/strcpy.c @@ -0,0 +1,8 @@ +#include + +char *strcpy(char *s1, const char *s2) { + char *res = s1; + while ((*s1++ = *s2++)) + ; + return (res); +} diff --git a/src/libc/src/strdup.c b/src/libc/src/strdup.c new file mode 100644 index 000000000..7b1f7b698 --- /dev/null +++ b/src/libc/src/strdup.c @@ -0,0 +1,10 @@ +#include +#include + +char *strdup(const char *s) { + size_t l = strlen(s) + 1; + char *tmp = (char *)malloc(l); + if (!tmp) + return 0; + return memcpy(tmp, s, l); +} diff --git a/src/libc/src/strerror.c b/src/libc/src/strerror.c new file mode 100644 index 000000000..8725f5e5d --- /dev/null +++ b/src/libc/src/strerror.c @@ -0,0 +1,8 @@ +#include +#include +#include + +extern const char __sys_err_unknown[]; + +// TODO +char *strerror(int errnum) { return NULL; } diff --git a/src/libc/src/strftime.c b/src/libc/src/strftime.c new file mode 100644 index 000000000..ef002e462 --- /dev/null +++ b/src/libc/src/strftime.c @@ -0,0 +1,199 @@ +#include +#include + +static const char sweekdays[7][4] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; +static const char weekdays[7][10] = {"Sunday", "Monday", "Tuesday", + "Wednesday", "Thursday", "Friday", + "Saturday"}; +static const char smonths[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static const char *months[12] = { + "January", "February", "March", "April", smonths[5 - 1], "June", + "July", "August", "September", "October", "November", "December"}; +static const char ampm[4][3] = {"am", "pm", "AM", "PM"}; + +static void i2a(char *dest, unsigned long x) { + int div = 10; + *dest++ = x / div + '0'; + *dest++ = x % div + '0'; + *dest++ = '\0'; +} + +size_t strftime(char *dst, size_t max, const char *format, + const struct tm *tm) { + char *p = dst; + const char *src; + unsigned long no; + char buf[5]; + + if (!max) + return 0; + for (; *format != '\0'; format++) { + if (*format == '%') { + if (*++format == '%') { + *p++ = '%'; + } else + again: + switch (*format) { + // case '%': *p++ = '%'; break; // reduce size + // of jump table + case 'n': + *p++ = '\n'; + break; + case 't': + *p++ = '\t'; + break; + case 'O': + case 'E': + ++format; + goto again; + case 'c': + src = "%b %a %d %k:%M:%S %Z %Y"; + goto _strf; + case 'r': + src = "%I:%M:%S %p"; + goto _strf; + case 'R': + src = "%H:%M"; + goto _strf; + case 'x': + src = "%b %a %d"; + goto _strf; + case 'X': + src = "%k:%M:%S"; + goto _strf; + case 'D': + src = "%m/%d/%y"; + goto _strf; + case 'F': + src = "%Y-%m-%d"; + goto _strf; + case 'T': + src = "%H:%M:%S"; + _strf: + p += strftime(p, (size_t)(dst + max - p), src, tm); + break; + case 'a': + src = sweekdays[tm->tm_wday]; + goto _str; + case 'A': + src = weekdays[tm->tm_wday]; + goto _str; + case 'h': + case 'b': + src = smonths[tm->tm_mon]; + goto _str; + case 'B': + src = months[tm->tm_mon]; + goto _str; + case 'p': + src = ampm[tm->tm_hour > 12 ? 3 : 2]; + goto _str; + case 'P': + src = ampm[tm->tm_hour > 12 ? 1 : 0]; + goto _str; + case 'C': + no = tm->tm_year / 100 + 19; + goto _no; + case 'd': + no = tm->tm_mday; + goto _no; + case 'e': + no = tm->tm_mday; + goto _nos; + case 'H': + no = tm->tm_hour; + goto _no; + case 'I': + no = tm->tm_hour % 12; + goto _no; + case 'j': + no = tm->tm_yday; + goto _no; + case 'k': + no = tm->tm_hour; + goto _nos; + case 'l': + no = tm->tm_hour % 12; + goto _nos; + case 'm': + no = tm->tm_mon + 1; + goto _no; + case 'M': + no = tm->tm_min; + goto _no; + case 'S': + no = tm->tm_sec; + goto _no; + case 'u': + no = tm->tm_wday ? tm->tm_wday : 7; + goto _no; + case 'w': + no = tm->tm_wday; + goto _no; + case 'U': + no = (tm->tm_yday - tm->tm_wday + 7) / 7; + goto _no; + case 'W': + no = (tm->tm_yday - (tm->tm_wday - 1 + 7) % 7 + 7) / 7; + goto _no; + case 's': { + time_t t = mktime((struct tm *)tm); + char sbuf[101]; + char *c; + sbuf[100] = 0; + for (c = sbuf + 99; c > sbuf; --c) { + *c = (t % 10) + '0'; + t /= 10; + if (!t) + break; + } + src = c; + goto _str; + } + case 'Z': +#ifdef WANT_TZFILE_PARSER + tzset(); + src = tzname[0]; +#else + src = "[unknown timezone]"; +#endif + goto _str; + case 'Y': + i2a(buf + 0, (unsigned int)(tm->tm_year / 100 + 19)); + i2a(buf + 2, (unsigned int)(tm->tm_year % 100)); + src = buf; + goto _str; + case 'y': + no = tm->tm_year % 100; + goto _no; + _no: + i2a(buf, no); /* append number 'no' */ + src = buf; + goto _str; + _nos: + i2a(buf, no); /* the same, but '0'->' ' */ + if (buf[0] == '0') + buf[0] = ' '; + src = buf; + _str: + while (*src && p < dst + max) /* append string */ + *p++ = *src++; + break; + }; + } else { + *p++ = *format; + } + + if (p >= dst + max) + break; + } + + if ((size_t)(p - dst) >= max) { + if (max) + p[-1] = 0; + } else + *p = '\0'; + return p - dst; +} diff --git a/src/libc/src/strlen.c b/src/libc/src/strlen.c new file mode 100644 index 000000000..7ff51a8ed --- /dev/null +++ b/src/libc/src/strlen.c @@ -0,0 +1,10 @@ +#include + +size_t strlen(const char *s) { + register size_t i; + if (!s) + return 0; + for (i = 0; *s; ++s) + ++i; + return i; +} diff --git a/src/libc/src/strncmp.c b/src/libc/src/strncmp.c new file mode 100644 index 000000000..2bffb57cb --- /dev/null +++ b/src/libc/src/strncmp.c @@ -0,0 +1,20 @@ +#include +#include + +/* gcc is broken and has a non-SUSv2 compliant internal prototype. + * This causes it to warn about a type mismatch here. Ignore it. */ +int strncmp(const char *s1, const char *s2, size_t n) { + register const unsigned char *a = (const unsigned char *)s1; + register const unsigned char *b = (const unsigned char *)s2; + register const unsigned char *fini = a + n; + while (a != fini) { + register int res = *a - *b; + if (res) + return res; + if (!*a) + return 0; + ++a; + ++b; + } + return 0; +} diff --git a/src/libc/src/strncpy.c b/src/libc/src/strncpy.c new file mode 100644 index 000000000..0ac92f51a --- /dev/null +++ b/src/libc/src/strncpy.c @@ -0,0 +1,9 @@ +#include +#include + +/* gcc is broken and has a non-SUSv2 compliant internal prototype. + * This causes it to warn about a type mismatch here. Ignore it. */ +char *strncpy(char *dest, const char *src, size_t n) { + memccpy(dest,src,0,n); + return dest; +} diff --git a/src/libc/src/strrchr.c b/src/libc/src/strrchr.c new file mode 100644 index 000000000..e7ed84f9f --- /dev/null +++ b/src/libc/src/strrchr.c @@ -0,0 +1,16 @@ +#include + +char *strrchr(const char *t, int c) { + register char ch; + register const char *l = 0; + + ch = c; + for (;;) { + if (*t == ch) + l = t; + if (!*t) + return (char *)l; + ++t; + } + return (char *)l; +} diff --git a/src/libc/src/strstr.c b/src/libc/src/strstr.c new file mode 100644 index 000000000..9031af071 --- /dev/null +++ b/src/libc/src/strstr.c @@ -0,0 +1,17 @@ +#include +#include + +char *strstr(const char *haystack, const char *needle) { + size_t nl=strlen(needle); + size_t hl=strlen(haystack); + size_t i; + if (!nl) goto found; + if (nl>hl) return 0; + for (i=hl-nl+1; i; --i) { + if (*haystack==*needle && !memcmp(haystack,needle,nl)) +found: + return (char*)haystack; + ++haystack; + } + return 0; +} diff --git a/src/libc/src/sys_resources.c b/src/libc/src/sys_resources.c new file mode 100644 index 000000000..a87b9214d --- /dev/null +++ b/src/libc/src/sys_resources.c @@ -0,0 +1,5 @@ +#include + +int getrlimit(int resource, struct rlimit *rlim) { return 0; } +int getrusage(int who, struct rusage *usage) { return 0; } +int setrlimit(int resource, const struct rlimit *rlim) { return 0; } diff --git a/src/libc/src/test-comp.c b/src/libc/src/test-comp.c new file mode 100644 index 000000000..3b992553c --- /dev/null +++ b/src/libc/src/test-comp.c @@ -0,0 +1,45 @@ +#include + +_Bool __VERIFIER_nondet_bool() { + _Bool var = owi_i32(); + owi_assume(or_(var == 0, var == 1)); + return var; +} + +char __VERIFIER_nondet_char() { return (char)owi_i32(); } + +unsigned char __VERIFIER_nondet_uchar() { return (unsigned char)owi_i32(); } + +short __VERIFIER_nondet_short() { return (short)owi_i32(); } + +unsigned short __VERIFIER_nondet_ushort() { + return (unsigned short)owi_i32(); +} + +int __VERIFIER_nondet_int() { return owi_i32(); } + +unsigned int __VERIFIER_nondet_uint() { return (unsigned int)owi_i32(); } + +__int128 __VERIFIER_nondet_int128() { return owi_i32(); } + +unsigned __int128 __VERIFIER_nondet_uint128() { + return (unsigned __int128)owi_i32(); +} + +unsigned int __VERIFIER_nondet_charp() { + return (unsigned int)owi_i32(); +} + +long __VERIFIER_nondet_long() { return (long)owi_i32(); } + +unsigned long __VERIFIER_nondet_ulong() { return (unsigned long)owi_i32(); } + +long long __VERIFIER_nondet_longlong() { return owi_i64(); } + +unsigned long long __VERIFIER_nondet_ulonglong() { + return (unsigned long long)owi_i64(); +} + +float __VERIFIER_nondet_float() { return owi_f32(); } + +double __VERIFIER_nondet_double() { return owi_f64(); } diff --git a/src/libc/src/time.c b/src/libc/src/time.c new file mode 100644 index 000000000..ec356c336 --- /dev/null +++ b/src/libc/src/time.c @@ -0,0 +1,14 @@ +#include + +int clock_gettime(clockid_t clock_id, struct timespec *tp) { return 0; } + +time_t time(time_t *t) { return 0; } +time_t timegm(struct tm *timeptr) { return 0; } +time_t mktime(struct tm *timeptr) { return 0; } + +struct tm *localtime_r(const time_t *t, struct tm *r) { return (struct tm *)0; } +struct tm *gmtime_r(const time_t *t, struct tm *r) { return (struct tm *)0; } + +int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { return 0; } + +char *ctime(const time_t *timep) { return 0; } diff --git a/src/libc/src/unistd.c b/src/libc/src/unistd.c new file mode 100644 index 000000000..57ba08281 --- /dev/null +++ b/src/libc/src/unistd.c @@ -0,0 +1,10 @@ +#include + +int optind = 0; +char *optarg = NULL; + +/* int getopt(int argc, char *const *argv, const char *options) { return 0; } */ +/* int open(const char* pathname,int flags, ...) { return 0; } */ +int close(int fd) { return 0; } +ssize_t read(int fd, void *buf, size_t len) { return 0; } +int getpid(void) { return 0; } diff --git a/src/thread.ml b/src/thread.ml index c26c5c5f5..32cf40886 100644 --- a/src/thread.ml +++ b/src/thread.ml @@ -29,7 +29,7 @@ let globals t = t.globals let solver_mod : Solver.t solver_module = (module Solver) let create () = - let solver = S (solver_mod, Solver.create ~logic:QF_BVFP ()) in + let solver = S (solver_mod, Solver.create ~logic:QF_BV ()) in { solver ; pc = [] ; memories = Symbolic_memory.init () From 9c48af2de55c8d7330a648275ee9677dc722af15 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 01:44:53 +0100 Subject: [PATCH 02/18] enable -Wall --- src/libc/include/stdio.h | 2 +- src/libc/script.ml | 2 +- src/libc/src/owi.c | 16 ++++++++-------- src/libc/src/start.c | 4 ++-- src/libc/src/stdlib.c | 6 +++--- src/libc/src/test-comp.c | 32 ++++++++++++++++---------------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h index cd19c779d..d5e684201 100644 --- a/src/libc/include/stdio.h +++ b/src/libc/include/stdio.h @@ -54,7 +54,7 @@ int sscanf(const char *str, const char *format, ...); int vsnprintf(char *str, size_t size, const char *format, va_list ap); -char getchar(); +char getchar(void); int fflush(FILE *stream); /* diff --git a/src/libc/script.ml b/src/libc/script.ml index 0b7f6a6cd..114a13747 100644 --- a/src/libc/script.ml +++ b/src/libc/script.ml @@ -11,7 +11,7 @@ let () = let n = Format.ksprintf Sys.command "clang -emit-llvm -g -O3 -ffreestanding --target=wasm32 -c -m32 \ - -Iinclude -Wno-parentheses-equality -Wno-attributes \ + -Iinclude -Wno-parentheses-equality -Wall -Wno-attributes \ -Wno-return-type -Wno-int-conversion \ -Wno-incompatible-pointer-types \ -Wno-incompatible-function-pointer-types -Wno-pointer-sign \ diff --git a/src/libc/src/owi.c b/src/libc/src/owi.c index 23745a9ab..6542b78de 100644 --- a/src/libc/src/owi.c +++ b/src/libc/src/owi.c @@ -6,13 +6,13 @@ __attribute__((import_module("summaries"), import_name("dealloc"))) void __owi_dealloc(void *); __attribute__((import_module("symbolic"), import_name("i32_symbol"))) int -__i32(); +__i32(void); __attribute__((import_module("symbolic"), import_name("i64_symbol"))) long long -__i64(); +__i64(void); __attribute__((import_module("symbolic"), import_name("f32_symbol"))) float -__f32(); +__f32(void); __attribute__((import_module("symbolic"), import_name("f64_symbol"))) double -__f64(); +__f64(void); __attribute__((import_module("symbolic"), import_name("assume"))) void __owi_assume(int); @@ -22,10 +22,10 @@ __owi_assert(int); void *owi_malloc(void *base, unsigned int size) { return __owi_alloc(base, size); } void owi_free(void *base) { __owi_dealloc(base); } -int owi_i32() { return __i32(); } -long long owi_i64() { return __i64(); } -float owi_f32() { return __f32(); } -double owi_f64() { return __f64(); } +int owi_i32(void) { return __i32(); } +long long owi_i64(void) { return __i64(); } +float owi_f32(void) { return __f32(); } +double owi_f64(void) { return __f64(); } void owi_assume(int c) { __owi_assume(c); } void owi_assert(int c) { __owi_assert(c); } diff --git a/src/libc/src/start.c b/src/libc/src/start.c index 4d1deafb5..c2dd79f62 100644 --- a/src/libc/src/start.c +++ b/src/libc/src/start.c @@ -1,3 +1,3 @@ -extern int main(); +extern int main(void); -void _start() { main(); } +void _start(void) { main(); } diff --git a/src/libc/src/stdlib.c b/src/libc/src/stdlib.c index 5a7501cd9..db0ccd281 100644 --- a/src/libc/src/stdlib.c +++ b/src/libc/src/stdlib.c @@ -17,7 +17,7 @@ owi_abort(void); __attribute__((import_module("summaries"), import_name("exit"))) void owi_exit(int); -void abort() { owi_abort(); } +void abort(void) { owi_abort(); } void exit(int status) { owi_exit(status); } extern unsigned char __heap_base; @@ -317,7 +317,7 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base) { } ++nptr; } - if ((nptr == orig)) { /* no conversion done */ + if (nptr == orig) { /* no conversion done */ nptr = ptr; errno = EINVAL; v = 0; @@ -405,4 +405,4 @@ void qsort(void *base, size_t nmemb, size_t size, int posix_memalign(void **memptr, size_t alignment, size_t size) { return 0; } // TODO: OWI external -char getchar() { return ' '; } +char getchar(void) { return ' '; } diff --git a/src/libc/src/test-comp.c b/src/libc/src/test-comp.c index 3b992553c..31cf52e70 100644 --- a/src/libc/src/test-comp.c +++ b/src/libc/src/test-comp.c @@ -1,45 +1,45 @@ #include -_Bool __VERIFIER_nondet_bool() { +_Bool __VERIFIER_nondet_bool(void) { _Bool var = owi_i32(); owi_assume(or_(var == 0, var == 1)); return var; } -char __VERIFIER_nondet_char() { return (char)owi_i32(); } +char __VERIFIER_nondet_char(void) { return (char)owi_i32(); } -unsigned char __VERIFIER_nondet_uchar() { return (unsigned char)owi_i32(); } +unsigned char __VERIFIER_nondet_uchar(void) { return (unsigned char)owi_i32(); } -short __VERIFIER_nondet_short() { return (short)owi_i32(); } +short __VERIFIER_nondet_short(void) { return (short)owi_i32(); } -unsigned short __VERIFIER_nondet_ushort() { +unsigned short __VERIFIER_nondet_ushort(void) { return (unsigned short)owi_i32(); } -int __VERIFIER_nondet_int() { return owi_i32(); } +int __VERIFIER_nondet_int(void) { return owi_i32(); } -unsigned int __VERIFIER_nondet_uint() { return (unsigned int)owi_i32(); } +unsigned int __VERIFIER_nondet_uint(void) { return (unsigned int)owi_i32(); } -__int128 __VERIFIER_nondet_int128() { return owi_i32(); } +__int128 __VERIFIER_nondet_int128(void) { return owi_i32(); } -unsigned __int128 __VERIFIER_nondet_uint128() { +unsigned __int128 __VERIFIER_nondet_uint128(void) { return (unsigned __int128)owi_i32(); } -unsigned int __VERIFIER_nondet_charp() { +unsigned int __VERIFIER_nondet_charp(void) { return (unsigned int)owi_i32(); } -long __VERIFIER_nondet_long() { return (long)owi_i32(); } +long __VERIFIER_nondet_long(void) { return (long)owi_i32(); } -unsigned long __VERIFIER_nondet_ulong() { return (unsigned long)owi_i32(); } +unsigned long __VERIFIER_nondet_ulong(void) { return (unsigned long)owi_i32(); } -long long __VERIFIER_nondet_longlong() { return owi_i64(); } +long long __VERIFIER_nondet_longlong(void) { return owi_i64(); } -unsigned long long __VERIFIER_nondet_ulonglong() { +unsigned long long __VERIFIER_nondet_ulonglong(void) { return (unsigned long long)owi_i64(); } -float __VERIFIER_nondet_float() { return owi_f32(); } +float __VERIFIER_nondet_float(void) { return owi_f32(); } -double __VERIFIER_nondet_double() { return owi_f64(); } +double __VERIFIER_nondet_double(void) { return owi_f64(); } From 0c9764fd3a32e9addc93f096c3763caed5a85529 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 01:49:23 +0100 Subject: [PATCH 03/18] fix ~logic --- src/thread.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thread.ml b/src/thread.ml index 32cf40886..c26c5c5f5 100644 --- a/src/thread.ml +++ b/src/thread.ml @@ -29,7 +29,7 @@ let globals t = t.globals let solver_mod : Solver.t solver_module = (module Solver) let create () = - let solver = S (solver_mod, Solver.create ~logic:QF_BV ()) in + let solver = S (solver_mod, Solver.create ~logic:QF_BVFP ()) in { solver ; pc = [] ; memories = Symbolic_memory.init () From fb78292f48846bde89a1e99772f29b78da261589 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 01:50:52 +0100 Subject: [PATCH 04/18] add sha dependency --- dune-project | 1 + owi.opam | 1 + 2 files changed, 2 insertions(+) diff --git a/dune-project b/dune-project index a8de983fb..3df502a97 100644 --- a/dune-project +++ b/dune-project @@ -64,5 +64,6 @@ (graphics :dev) (tiny_httpd :dev) (encoding (>= 0.0.3)) + sha xmlm) (sites (share pyc) (share binc) (share libc))) diff --git a/owi.opam b/owi.opam index 48e365326..fa6d7cae5 100644 --- a/owi.opam +++ b/owi.opam @@ -32,6 +32,7 @@ depends: [ "graphics" {dev} "tiny_httpd" {dev} "encoding" {>= "0.0.3"} + "sha" "xmlm" ] build: [ From 50095d47ff117348ea1a95ebb9ba4e5f2c6fa67e Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 01:53:34 +0100 Subject: [PATCH 05/18] add missings deps --- dune-project | 7 +++++-- owi.opam | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dune-project b/dune-project index 3df502a97..4b9f012ba 100644 --- a/dune-project +++ b/dune-project @@ -55,7 +55,7 @@ (>= "0.1") :dev)) (odoc :with-doc) - (bos :with-test) + bos (mdx (and :with-test @@ -65,5 +65,8 @@ (tiny_httpd :dev) (encoding (>= 0.0.3)) sha - xmlm) + xmlm + pyml + re2 + dune-site) (sites (share pyc) (share binc) (share libc))) diff --git a/owi.opam b/owi.opam index fa6d7cae5..4cce93bda 100644 --- a/owi.opam +++ b/owi.opam @@ -26,7 +26,7 @@ depends: [ "bisect_ppx" {with-test & >= "2.5" & dev} "ocb" {with-test & >= "0.1" & dev} "odoc" {with-doc} - "bos" {with-test} + "bos" "mdx" {with-test & >= "2.1"} "crowbar" {with-test} "graphics" {dev} @@ -34,6 +34,9 @@ depends: [ "encoding" {>= "0.0.3"} "sha" "xmlm" + "pyml" + "re2" + "dune-site" ] build: [ ["dune" "subst"] {dev} From 42192cd1faabeb475e2cccaff85ecb93c60a4807 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 02:54:29 +0100 Subject: [PATCH 06/18] write more doc --- README.md | 22 ++- example/c/README.md | 154 ++++++++++++++++++ example/c/dune | 7 + example/c/poly.c | 18 ++ example/c/poly2.c | 22 +++ example/{ => define_host_function}/README.md | 38 +---- example/{ => define_host_function}/dune | 3 +- example/{ => define_host_function}/extern.ml | 2 +- .../extern.wat} | 0 .../life_game/README.md | 8 +- .../life_game/content/dune | 5 + .../life_game/content/index.html | 0 .../life_game/content/life_browser.js | 0 .../{ => define_host_function}/life_game/dune | 0 .../life_game/life.wat} | 0 .../life_game/life_console.ml | 4 +- .../life_game/life_graphics.ml | 4 +- .../life_game/life_loop.wat} | 0 .../life_game/runweb.ml | 0 example/fmt/README.md | 46 ++++++ example/fmt/dune | 5 + example/life_game/content/dune | 5 - example/opt/README.md | 49 ++++++ example/opt/dune | 5 + example/run/42.wat | 9 + example/run/README.md | 105 ++++++++++++ example/run/dune | 5 + example/script/README.md | 19 +++ example/script/dune | 6 + example/{ => script}/print.wast | 0 example/sym/README.md | 64 ++++++++ example/sym/dune | 5 + src/choice_monad.ml | 5 +- 33 files changed, 567 insertions(+), 48 deletions(-) create mode 100644 example/c/README.md create mode 100644 example/c/dune create mode 100644 example/c/poly.c create mode 100644 example/c/poly2.c rename example/{ => define_host_function}/README.md (76%) rename example/{ => define_host_function}/dune (77%) rename example/{ => define_host_function}/extern.ml (96%) rename example/{extern.wast => define_host_function/extern.wat} (100%) rename example/{ => define_host_function}/life_game/README.md (90%) create mode 100644 example/define_host_function/life_game/content/dune rename example/{ => define_host_function}/life_game/content/index.html (100%) rename example/{ => define_host_function}/life_game/content/life_browser.js (100%) rename example/{ => define_host_function}/life_game/dune (100%) rename example/{life_game/life.wast => define_host_function/life_game/life.wat} (100%) rename example/{ => define_host_function}/life_game/life_console.ml (96%) rename example/{ => define_host_function}/life_game/life_graphics.ml (96%) rename example/{life_game/life_loop.wast => define_host_function/life_game/life_loop.wat} (100%) rename example/{ => define_host_function}/life_game/runweb.ml (100%) create mode 100644 example/fmt/README.md create mode 100644 example/fmt/dune delete mode 100644 example/life_game/content/dune create mode 100644 example/opt/README.md create mode 100644 example/opt/dune create mode 100644 example/run/42.wat create mode 100644 example/run/README.md create mode 100644 example/run/dune create mode 100644 example/script/README.md create mode 100644 example/script/dune rename example/{ => script}/print.wast (100%) create mode 100644 example/sym/README.md create mode 100644 example/sym/dune diff --git a/README.md b/README.md index acbb84837..981519d2a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # [🐌] Owi [![build-badge]][build status] [![coverage-badge]][coverage percentage] -[Owi] is an [OCaml] toolchain to work with WebAssembly. It provides an interpreter as an executable and a library. +[Owi] is a toolchain to work with WebAssembly. It is written in [OCaml]. It provides a binary with many subcommands: + +- [`owi c`]: a bug finding tool for C code that performs symbolic execution by compiling to Wasm and using our symbolic Wasm interpreter; +- [`owi fmt`]: a formatter for Wasm; +- [`owi opt`]: an optimizer for Wasm; +- [`owi run`]: a concrete Wasm interpreter; +- [`owi script`]: an interpreter for [Wasm specification tests]; +- [`owi sym`]: a symbolic Wasm interpreter. + +It also provides an OCaml library which allows for instance to [import OCaml functions in a Wasm module] in a type-safe way! ## Installation @@ -250,8 +259,9 @@ This project was funded through the [NGI0 Core] Fund, a fund established by [NLn [Owi project on NLnet]: https://nlnet.nl/project/OWI [reference test suite script]: https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#scripts [september 2023]: https://invidious.zapashcanon.fr/watch?v=IM76cMP3Eqo -[spectest module]: https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#spectest-host-module +[spectest module]: https://github.com/webassembly/spec/blob/main/interpreter/readme.md#spectest-host-module [video of our talk]: https://invidious.zapashcanon.fr/watch?v=os_pknmiqmU +[Wasm specification tests]: https://github.com/webassembly/spec/blob/main/interpreter/readme.md#spectest-host-module [WebAssembly]: https://webassembly.org [WebAssembly Research Center]: https://www.cs.cmu.edu/wrc @@ -278,4 +288,12 @@ This project was funded through the [NGI0 Core] Fund, a fund established by [NLn [Relaxed SIMD]: https://github.com/WebAssembly/relaxed-simd [JS Promise Integration]: https://github.com/WebAssembly/js-promise-integration +[`owi c`]: ./example/c +[`owi fmt`]: ./example/fmt +[`owi opt`]: ./example/opt +[`owi run`]: ./example/run +[`owi script`]: ./example/script +[`owi sym`]: ./example/sym +[import OCaml functions in a Wasm module]: ./example/define_host_function + [🐌]: https://invidious.zapashcanon.fr/watch?v=XgK9Fd8ikxk diff --git a/example/c/README.md b/example/c/README.md new file mode 100644 index 000000000..8cb24815c --- /dev/null +++ b/example/c/README.md @@ -0,0 +1,154 @@ +# Performing symbolic execution on C programs + +## Solving polynomials example + +Given the following `poly.c` file: + + +```wast +#include + +int main() { + int x = owi_i32(); + int x2 = x * x; + int x3 = x * x * x; + + int a = 1; + int b = -7; + int c = 14; + int d = -8; + + int poly = a * x3 + b * x2 + c * x + d; + + owi_assert(poly != 0); + + return 0; +} +``` + +We are defining one symbolic variable `x` using the function `owi_i32(void)`. Then we build a polynomial `poly` equal to $x^3 - 7x^2 + 14x - 8$. + +Then we use `owi_assert(poly != 0)`. Which should fail as this polynomial has multiple roots. Let's see what owi says about it: + +```sh +$ dune exec owi -- c ./poly.c +... +Model: + (model + (symbol_0 i32 (i32 1))) +Reached problem! +``` + +Indeed, `1` is a root of the polynomial and thus it is expected to be equal to `0` in this case. We know the three roots are `1`, `2` and `4`, so let's inform owi that we are not interested in this cases. + +We can do so by assuming that `x` is not equal to any of these with the function `owi_assume(bool)`: + + +```wast +#include + +int main() { + int x = owi_i32(); + int x2 = x * x; + int x3 = x * x * x; + + int a = 1; + int b = -7; + int c = 14; + int d = -8; + + int poly = a * x3 + b * x2 + c * x + d; + + owi_assume(x != 1); + owi_assume(x != 2); + owi_assume(x != 4); + + owi_assert(poly != 0); + + return 0; +} +``` + +Let's run owi on this new input: + + +```sh +$ dune exec owi -- c ./poly2.c +... +Model: + (model + (symbol_0 i32 (i32 -2147483644))) +Reached problem! +``` + +And indeed, `-2147483644` is a root of the polynomial! Well, not quite… + +Remember that we are working on 32 bits integers here. Thus *overflows* are a thing we have to think about. And indeed when `x` is equal to `-2147483644`, because of overflows, the polynomial will be equal to zero. + +Exercise: can you find another "root" of the polynomial ? :-) + +## Man page + +```sh +$ dune exec owi -- c --help +OWI-C(1) Owi Manual OWI-C(1) + +NAME + owi-c - Compile a C file to Wasm and run the symbolic interpreter on it + +SYNOPSIS + owi c [OPTION]… [ARG]… + +OPTIONS + -d, --debug + debug mode + + -I VAL + headers path + + -m VAL, --arch=VAL (absent=32) + data model + + -o VAL, --output=VAL (absent=owi-out) + write results to dir + + -O VAL (absent=0) + specify which optimization level to use + + -p VAL, --property=VAL + property file + + --testcomp + test-comp mode + + -w VAL, --workers=VAL (absent=4) + number of workers for symbolic execution + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi c exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +SEE ALSO + owi(1) + +Owi 11VERSION11 OWI-C(1) +``` diff --git a/example/c/dune b/example/c/dune new file mode 100644 index 000000000..92903e8c6 --- /dev/null +++ b/example/c/dune @@ -0,0 +1,7 @@ +(mdx + (libraries owi) + (deps + %{bin:owi} + (file poly.c) + (file poly2.c)) + (files README.md)) diff --git a/example/c/poly.c b/example/c/poly.c new file mode 100644 index 000000000..c353e45a8 --- /dev/null +++ b/example/c/poly.c @@ -0,0 +1,18 @@ +#include + +int main() { + int x = owi_i32(); + int x2 = x * x; + int x3 = x * x * x; + + int a = 1; + int b = -7; + int c = 14; + int d = -8; + + int poly = a * x3 + b * x2 + c * x + d; + + owi_assert(poly != 0); + + return 0; +} diff --git a/example/c/poly2.c b/example/c/poly2.c new file mode 100644 index 000000000..fb35766f1 --- /dev/null +++ b/example/c/poly2.c @@ -0,0 +1,22 @@ +#include + +int main() { + int x = owi_i32(); + int x2 = x * x; + int x3 = x * x * x; + + int a = 1; + int b = -7; + int c = 14; + int d = -8; + + int poly = a * x3 + b * x2 + c * x + d; + + owi_assume(x != 1); + owi_assume(x != 2); + owi_assume(x != 4); + + owi_assert(poly != 0); + + return 0; +} diff --git a/example/README.md b/example/define_host_function/README.md similarity index 76% rename from example/README.md rename to example/define_host_function/README.md index 589dc4598..3dd6d33b0 100644 --- a/example/README.md +++ b/example/define_host_function/README.md @@ -1,31 +1,9 @@ -# Examples +# Using and defining external functions (host functions) -## Using the `spectest` module +Given the following `extern.wat` file: -Given the following `print.wast` file: - - -```wast -(module - (func $print_i32 (import "spectest" "print_i32") (param i32)) - (func $main (call $print_i32 (i32.const 42))) - (start $main) -) -``` - -You can print the value thanks to the `print_i32` function imported from the `spectest` module: - -```sh -$ dune exec owi -- script ./print.wast -42 -``` - -## Using and defining external functions (host functions) - -Given the following `extern.wast` file: - - -```wast + +```wat (module $extern (import "sausage" "fresh" @@ -98,7 +76,7 @@ let link_state = (* a pure wasm module refering to `sausage` *) let pure_wasm_module = - match Parse.Module.from_file ~filename:"extern.wast" with + match Parse.Module.from_file ~filename:"extern.wat" with | Error e -> failwith e | Ok modul -> modul @@ -126,5 +104,7 @@ $ dune exec -- ./extern.exe 13 ``` -To learn more, follow the link [life_game/](./life_game/) for a more advanced example -based on the Conway's Game of Life that links several modules from different .wast files. +To learn more, see our advanced [Game of Live] example +based on the famous cellular automaton by Conway. It show how to link several modules from different `.wat` files. + +[Game of Life]: ./life_game diff --git a/example/dune b/example/define_host_function/dune similarity index 77% rename from example/dune rename to example/define_host_function/dune index 3c431e6db..696818251 100644 --- a/example/dune +++ b/example/define_host_function/dune @@ -8,6 +8,5 @@ %{bin:owi} (file extern.exe) (file extern.ml) - (file extern.wast) - (file print.wast)) + (file extern.wat)) (files README.md)) diff --git a/example/extern.ml b/example/define_host_function/extern.ml similarity index 96% rename from example/extern.ml rename to example/define_host_function/extern.ml index 7d80ab3ec..bc3213b81 100644 --- a/example/extern.ml +++ b/example/define_host_function/extern.ml @@ -32,7 +32,7 @@ let link_state = (* a pure wasm module refering to `sausage` *) let pure_wasm_module = - match Parse.Module.from_file ~filename:"extern.wast" with + match Parse.Module.from_file ~filename:"extern.wat" with | Error e -> failwith e | Ok modul -> modul diff --git a/example/extern.wast b/example/define_host_function/extern.wat similarity index 100% rename from example/extern.wast rename to example/define_host_function/extern.wat diff --git a/example/life_game/README.md b/example/define_host_function/life_game/README.md similarity index 90% rename from example/life_game/README.md rename to example/define_host_function/life_game/README.md index 5cd72eba6..8263f153c 100644 --- a/example/life_game/README.md +++ b/example/define_host_function/life_game/README.md @@ -4,7 +4,7 @@ This example is a [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%2 3 outputs are proposed (console, [OCaml Graphics](https://github.com/ocaml/graphics) and HTML/JavaScript), each one using the same WebAssembly modules. -## Console et OCaml Graphics +## Console and OCaml Graphics Console and Graphics implementations use *external functions* technique as the [standard example](../). Here are the command lines to run in this directory to execute the programs: @@ -12,9 +12,9 @@ Console and Graphics implementations use *external functions* technique as the * Console mode: `dune exec -- ./life_graphics.exe` * Graphics mode: `dune exec -- ./life_console.exe` -Several modules (defined in `life.wast` and `life_loop.wast` files) are linked. +Several modules (defined in `life.wat` and `life_loop.wat` files) are linked. -**Why are two `.wast` files required ?** +**Why are two `.wat` files required ?** Because, in JavaScript, the `Sleep` function uses the [Promise system](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) @@ -30,7 +30,7 @@ Here is a standard example of a wasm module called in a web page using the [JavaScript API](https://webassembly.org/getting-started/js-api/). First of all, a compiled version of the WebAssembly module is needed, obtained by the -command line `wat2wasm life.wast`. wat2wasm is part of the +command line `wat2wasm life.wat`. wat2wasm is part of the [WABT: The WebAssembly Binary Toolkit](https://github.com/WebAssembly/wabt). As in OCaml in the previous contexts, external functions are defined in the environment execution diff --git a/example/define_host_function/life_game/content/dune b/example/define_host_function/life_game/content/dune new file mode 100644 index 000000000..de495552e --- /dev/null +++ b/example/define_host_function/life_game/content/dune @@ -0,0 +1,5 @@ +(rule + (deps ../life.wat) + (targets life.wasm) + (action + (run %{bin:wat2wasm} ../life.wat))) diff --git a/example/life_game/content/index.html b/example/define_host_function/life_game/content/index.html similarity index 100% rename from example/life_game/content/index.html rename to example/define_host_function/life_game/content/index.html diff --git a/example/life_game/content/life_browser.js b/example/define_host_function/life_game/content/life_browser.js similarity index 100% rename from example/life_game/content/life_browser.js rename to example/define_host_function/life_game/content/life_browser.js diff --git a/example/life_game/dune b/example/define_host_function/life_game/dune similarity index 100% rename from example/life_game/dune rename to example/define_host_function/life_game/dune diff --git a/example/life_game/life.wast b/example/define_host_function/life_game/life.wat similarity index 100% rename from example/life_game/life.wast rename to example/define_host_function/life_game/life.wat diff --git a/example/life_game/life_console.ml b/example/define_host_function/life_game/life_console.ml similarity index 96% rename from example/life_game/life_console.ml rename to example/define_host_function/life_game/life_console.ml index 70c5487de..d6d240bfb 100644 --- a/example/life_game/life_console.ml +++ b/example/define_host_function/life_game/life_console.ml @@ -57,7 +57,7 @@ let link_state = (* first pure wasm module refering to `life_ext` *) let pure_wasm_module_1 = - match Parse.Module.from_file ~filename:"life.wast" with + match Parse.Module.from_file ~filename:"life.wat" with | Error msg -> failwith msg | Ok modul -> modul @@ -78,7 +78,7 @@ let () = (* second pure wasm module refering to `life_ext` *) let pure_wasm_module_2 = - match Parse.Module.from_file ~filename:"life_loop.wast" with + match Parse.Module.from_file ~filename:"life_loop.wat" with | Error msg -> failwith msg | Ok modul -> modul diff --git a/example/life_game/life_graphics.ml b/example/define_host_function/life_game/life_graphics.ml similarity index 96% rename from example/life_game/life_graphics.ml rename to example/define_host_function/life_game/life_graphics.ml index 606f53bf2..386db9fce 100644 --- a/example/life_game/life_graphics.ml +++ b/example/define_host_function/life_game/life_graphics.ml @@ -69,7 +69,7 @@ let link_state = (* first pure wasm module refering to `life_ext` *) let pure_wasm_module_1 = - match Parse.Module.from_file ~filename:"life.wast" with + match Parse.Module.from_file ~filename:"life.wat" with | Error msg -> failwith msg | Ok modul -> modul @@ -90,7 +90,7 @@ let () = (* second pure wasm module refering to `life_ext` *) let pure_wasm_module_2 = - match Parse.Module.from_file ~filename:"life_loop.wast" with + match Parse.Module.from_file ~filename:"life_loop.wat" with | Error msg -> failwith msg | Ok modul -> modul diff --git a/example/life_game/life_loop.wast b/example/define_host_function/life_game/life_loop.wat similarity index 100% rename from example/life_game/life_loop.wast rename to example/define_host_function/life_game/life_loop.wat diff --git a/example/life_game/runweb.ml b/example/define_host_function/life_game/runweb.ml similarity index 100% rename from example/life_game/runweb.ml rename to example/define_host_function/life_game/runweb.ml diff --git a/example/fmt/README.md b/example/fmt/README.md new file mode 100644 index 000000000..5a9ee27d0 --- /dev/null +++ b/example/fmt/README.md @@ -0,0 +1,46 @@ +# Formatter + +## Man page + +```sh +$ dune exec owi -- fmt --help +OWI-FMT(1) Owi Manual OWI-FMT(1) + +NAME + owi-fmt - Format a .wat or .wast file + +SYNOPSIS + owi fmt [--inplace] [OPTION]… ARG + +OPTIONS + -i, --inplace + Format in-place, overwriting input file + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi fmt exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +SEE ALSO + owi(1) + +Owi 11VERSION11 OWI-FMT(1) +``` diff --git a/example/fmt/dune b/example/fmt/dune new file mode 100644 index 000000000..9c38e0219 --- /dev/null +++ b/example/fmt/dune @@ -0,0 +1,5 @@ +(mdx + (libraries owi) + (deps + %{bin:owi}) + (files README.md)) diff --git a/example/life_game/content/dune b/example/life_game/content/dune deleted file mode 100644 index bb26bc310..000000000 --- a/example/life_game/content/dune +++ /dev/null @@ -1,5 +0,0 @@ -(rule - (deps ../life.wast) - (targets life.wasm) - (action - (run %{bin:wat2wasm} ../life.wast))) diff --git a/example/opt/README.md b/example/opt/README.md new file mode 100644 index 000000000..97fd71e94 --- /dev/null +++ b/example/opt/README.md @@ -0,0 +1,49 @@ +# Optimizer + +## Man page + +```sh +$ dune exec owi -- opt --help +OWI-OPT(1) Owi Manual OWI-OPT(1) + +NAME + owi-opt - Optimize a module + +SYNOPSIS + owi opt [--debug] [--unsafe] [OPTION]… ARG + +OPTIONS + -d, --debug + debug mode + + -u, --unsafe + skip typechecking pass + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi opt exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +SEE ALSO + owi(1) + +Owi 11VERSION11 OWI-OPT(1) +``` diff --git a/example/opt/dune b/example/opt/dune new file mode 100644 index 000000000..9c38e0219 --- /dev/null +++ b/example/opt/dune @@ -0,0 +1,5 @@ +(mdx + (libraries owi) + (deps + %{bin:owi}) + (files README.md)) diff --git a/example/run/42.wat b/example/run/42.wat new file mode 100644 index 000000000..3717ddeb5 --- /dev/null +++ b/example/run/42.wat @@ -0,0 +1,9 @@ +(module $quickstart + (func $f + i32.const 20 + i32.const 22 + i32.add + drop + ) + (start $f) +) diff --git a/example/run/README.md b/example/run/README.md new file mode 100644 index 000000000..5e894a21f --- /dev/null +++ b/example/run/README.md @@ -0,0 +1,105 @@ +# Concrete interpreter + +## Run a simple program + +Given a file `42.wat` with the following content: + + +```wat +(module $quickstart + (func $f + i32.const 20 + i32.const 22 + i32.add + drop + ) + (start $f) +) +``` + +Running the interpreter is as simple as: + +```sh +$ dune exec owi -- run ./42.wat +``` + +Nothing is happening, so you can add the `--debug` option to print an execution trace: + +```sh +$ dune exec owi -- run ./42.wat --debug +parsing ... +checking ... +grouping ... +assigning ... +rewriting ... +typechecking ... +linking ... +interpreting ... +stack : [ ] +running instr: call 0 +calling func : func f +stack : [ ] +running instr: i32.const 20 +stack : [ i32.const 20 ] +running instr: i32.const 22 +stack : [ i32.const 22 ; i32.const 20 ] +running instr: i32.add +stack : [ i32.const 42 ] +running instr: drop +stack : [ ] +stack : [ ] +``` + +## Man page + +```sh +$ dune exec owi -- run --help +OWI-RUN(1) Owi Manual OWI-RUN(1) + +NAME + owi-run - Run the concrete interpreter + +SYNOPSIS + owi run [OPTION]… [ARG]… + +OPTIONS + -d, --debug + debug mode + + --optimize + optimize mode + + -p, --profiling + profiling mode + + -u, --unsafe + skip typechecking pass + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi run exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +SEE ALSO + owi(1) + +Owi 11VERSION11 OWI-RUN(1) +``` diff --git a/example/run/dune b/example/run/dune new file mode 100644 index 000000000..887f23858 --- /dev/null +++ b/example/run/dune @@ -0,0 +1,5 @@ +(mdx + (libraries owi) + (deps + %{bin:owi} 42.wat) + (files README.md)) diff --git a/example/script/README.md b/example/script/README.md new file mode 100644 index 000000000..8a3df1ebb --- /dev/null +++ b/example/script/README.md @@ -0,0 +1,19 @@ +# Using the `spectest` module + +Given the following `print.wast` file: + + +```wast +(module + (func $print_i32 (import "spectest" "print_i32") (param i32)) + (func $main (call $print_i32 (i32.const 42))) + (start $main) +) +``` + +You can print the value thanks to the `print_i32` function imported from the `spectest` module: + +```sh +$ dune exec owi -- script ./print.wast +42 +``` diff --git a/example/script/dune b/example/script/dune new file mode 100644 index 000000000..7bef01654 --- /dev/null +++ b/example/script/dune @@ -0,0 +1,6 @@ +(mdx + (libraries owi) + (deps + %{bin:owi} + (file print.wast)) + (files README.md)) diff --git a/example/print.wast b/example/script/print.wast similarity index 100% rename from example/print.wast rename to example/script/print.wast diff --git a/example/sym/README.md b/example/sym/README.md new file mode 100644 index 000000000..8ef9ebe97 --- /dev/null +++ b/example/sym/README.md @@ -0,0 +1,64 @@ +# Symbolic interpreter + +## Man page + +```sh +$ dune exec owi -- sym --help +OWI-SYM(1) Owi Manual OWI-SYM(1) + +NAME + owi-sym - Run the symbolic interpreter + +SYNOPSIS + owi sym [OPTION]… [ARG]… + +OPTIONS + -d, --debug + debug mode + + --no-stop-at-failure + do not stop when a program failure is encountered + + --optimize + optimize mode + + -p, --profiling + profiling mode + + -u, --unsafe + skip typechecking pass + + -w VAL, --workers=VAL (absent=4) + number of workers for symbolic execution + + --workspace=VAL (absent=owi-out) + path to the workspace directory + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi sym exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +SEE ALSO + owi(1) + +Owi 11VERSION11 OWI-SYM(1) +``` diff --git a/example/sym/dune b/example/sym/dune new file mode 100644 index 000000000..9c38e0219 --- /dev/null +++ b/example/sym/dune @@ -0,0 +1,5 @@ +(mdx + (libraries owi) + (deps + %{bin:owi}) + (files README.md)) diff --git a/src/choice_monad.ml b/src/choice_monad.ml index 4d6c5d677..6a3069a1d 100644 --- a/src/choice_monad.ml +++ b/src/choice_monad.ml @@ -23,13 +23,16 @@ let check (sym_bool : vbool) (state : Thread.t) : bool = | Val False -> true | _ -> let check = no :: pc in + (* Format.pp_std "CHECK:@.%a" (Format.pp_list ~pp_sep:Format.pp_newline Expr.pp) - check; + check; *) let module Solver = (val solver_module) in let r = Solver.check solver check in + (* let msg = if r then "KO" else "OK" in Format.pp_std "@./CHECK %s@." msg; + *) not r (* TODO: make this a CLI flag ? *) From 22be1c4cae5b45faa51e23e998cdce0f81e0abd7 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 02:56:42 +0100 Subject: [PATCH 07/18] color --- example/c/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/c/README.md b/example/c/README.md index 8cb24815c..504464e9a 100644 --- a/example/c/README.md +++ b/example/c/README.md @@ -5,7 +5,7 @@ Given the following `poly.c` file: -```wast +```c #include int main() { @@ -44,7 +44,7 @@ Indeed, `1` is a root of the polynomial and thus it is expected to be equal to ` We can do so by assuming that `x` is not equal to any of these with the function `owi_assume(bool)`: -```wast +```c #include int main() { From a4c4c8c6cfeaf6bb61f6bce019c884b2cd6abda9 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:00:56 +0100 Subject: [PATCH 08/18] update dead link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 981519d2a..eedba34d2 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ This project was funded through the [NGI0 Core] Fund, a fund established by [NLn [Owi project on NLnet]: https://nlnet.nl/project/OWI [reference test suite script]: https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#scripts [september 2023]: https://invidious.zapashcanon.fr/watch?v=IM76cMP3Eqo -[spectest module]: https://github.com/webassembly/spec/blob/main/interpreter/readme.md#spectest-host-module +[spectest module]: https://github.com/WebAssembly/spec/tree/main/interpreter#scripts [video of our talk]: https://invidious.zapashcanon.fr/watch?v=os_pknmiqmU [Wasm specification tests]: https://github.com/webassembly/spec/blob/main/interpreter/readme.md#spectest-host-module [WebAssembly]: https://webassembly.org From 3c4b112a625c7776740b01dd188b901d9346fd0f Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:32:04 +0100 Subject: [PATCH 09/18] more doc --- README.md | 129 +------------ example/README.md | 24 +++ example/fmt/README.md | 96 ++++++++++ example/fmt/horrible.wat | 30 +++ example/lib/README.md | 44 +++++ example/lib/dune | 6 + example/lib/quickstart.wat | 9 + example/opt/README.md | 361 +++++++++++++++++++++++++++++++++++++ example/opt/dune | 3 +- example/opt/useless.wat | 326 +++++++++++++++++++++++++++++++++ example/sym/README.md | 38 ++++ example/sym/dune | 2 +- example/sym/mini.wat | 11 ++ 13 files changed, 955 insertions(+), 124 deletions(-) create mode 100644 example/README.md create mode 100644 example/fmt/horrible.wat create mode 100644 example/lib/README.md create mode 100644 example/lib/dune create mode 100644 example/lib/quickstart.wat create mode 100644 example/opt/useless.wat create mode 100644 example/sym/mini.wat diff --git a/README.md b/README.md index eedba34d2..3653d4079 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,11 @@ - [`owi script`]: an interpreter for [Wasm specification tests]; - [`owi sym`]: a symbolic Wasm interpreter. -It also provides an OCaml library which allows for instance to [import OCaml functions in a Wasm module] in a type-safe way! +It also provides an [OCaml library] which allows for instance to [import OCaml functions in a Wasm module] in a type-safe way! + +We also have [a fuzzer] that is able to generate random *valid* Wasm programs. For now it has not been made available as a subcommand so you'll have to hack the code a little bit to play with it. + +⚠️ For now, the optimizer and the formatter are quite experimental. The optimizer is well tested but only performs basic optimizations in an inefficient way. The formatter is mainly used for debugging purpose and is probably incorrect on some cases. ## Installation @@ -35,127 +39,6 @@ $ dune build -p owi @install $ dune install ``` -## Quickstart - -### Concrete interpretation - -Given a file `test/passing/quickstart.wast` with the following content: - - -```wast -(module $quickstart - (func $f - i32.const 24 - i32.const 24 - i32.add - drop - ) - (start $f) -) -``` - -Running the executable interpreter is as simple as: -```sh -$ dune exec owi -- run --debug test/passing/quickstart.wast -parsing ... -checking ... -grouping ... -assigning ... -rewriting ... -typechecking ... -linking ... -interpreting ... -stack : [ ] -running instr: call 0 -calling func : func f -stack : [ ] -running instr: i32.const 24 -stack : [ i32.const 24 ] -running instr: i32.const 24 -stack : [ i32.const 24 ; i32.const 24 ] -running instr: i32.add -stack : [ i32.const 48 ] -running instr: drop -stack : [ ] -stack : [ ] -``` - -If you want to run the file as a [reference test suite script], you can use the `script` command instead of the `run` one. This will allow you to add constructs like assertions and will also link the [spectest module], which provides function for e.g. printing. - -If you're interested in the library part of Owi, here's how to get started: - -```ocaml -# open Owi;; -# let filename = "test/passing/quickstart.wast";; -val filename : string = "test/passing/quickstart.wast" -# let m = - match Parse.Module.from_file ~filename with - | Ok script -> script - | Error e -> failwith e;; -val m : Text.modul = -... -# let module_to_run, link_state = - match Compile.until_link Link.empty_state ~unsafe:false ~optimize:false ~name:None m with - | Ok v -> v - | Error e -> failwith e;; -val module_to_run : '_weak1 Link.module_to_run = -... -val link_state : '_weak1 Link.state = -... -# let () = - Log.debug_on := true; - match Interpret.Concrete.modul link_state.envs module_to_run with - | Ok () -> () - | Error e -> failwith e;; -interpreting ... -stack : [ ] -running instr: call 0 -calling func : func f -stack : [ ] -running instr: i32.const 24 -stack : [ i32.const 24 ] -running instr: i32.const 24 -stack : [ i32.const 24 ; i32.const 24 ] -running instr: i32.add -stack : [ i32.const 48 ] -running instr: drop -stack : [ ] -stack : [ ] -``` - -For more, have a look at the [example] folder, at the [documentation] or at the [test suite]. - -### Symbolic interpretation - -The interpreter can also be used in symbolic mode. This allows to find which input values are leading to a trap. - -Given a file `test/sym/mini_test.wast` with the following content: - - -```wast -(module - (import "symbolic" "i32" (func $gen_i32 (param i32) (result i32))) - - (func $start (local $x i32) - (local.set $x (call $gen_i32 (i32.const 42))) - (if (i32.lt_s (i32.const 5) (local.get $x)) (then - unreachable - ))) - - (start $start) -) -``` - -You can run the symbolic interpreter through the `sym` command: -```sh -$ dune exec owi -- sym test/sym/mini_test.wast -Trap: unreachable -Model: - (model - (x_0 i32 (i32 6))) -Reached problem! -``` - ## Supported proposals The 🐌 status means the proposal is not applicable to Owi. @@ -295,5 +178,7 @@ This project was funded through the [NGI0 Core] Fund, a fund established by [NLn [`owi script`]: ./example/script [`owi sym`]: ./example/sym [import OCaml functions in a Wasm module]: ./example/define_host_function +[OCaml library]: ./example/lib +[a fuzzer]: ./test/fuzz [🐌]: https://invidious.zapashcanon.fr/watch?v=XgK9Fd8ikxk diff --git a/example/README.md b/example/README.md new file mode 100644 index 000000000..fbeb07ab6 --- /dev/null +++ b/example/README.md @@ -0,0 +1,24 @@ +# Examples + +## Owi binary + +- [`owi c`] +- [`owi fmt`] +- [`owi opt`] +- [`owi run`] +- [`owi script`] +- [`owi sym`] + +## Owi library + +- [OCaml library] +- [import OCaml functions in a Wasm module] + +[`owi c`]: ./c +[`owi fmt`]: ./fmt +[`owi opt`]: ./opt +[`owi run`]: ./run +[`owi script`]: ./script +[`owi sym`]: ./sym +[import OCaml functions in a Wasm module]: ./define_host_function +[OCaml library]: ./lib diff --git a/example/fmt/README.md b/example/fmt/README.md index 5a9ee27d0..2196e5837 100644 --- a/example/fmt/README.md +++ b/example/fmt/README.md @@ -1,5 +1,101 @@ # Formatter +## Quickstart + +Given a file `horrible.wat`: + + + +```wat +(module (memory +10) (func +$f (param + $n i32) (result +i32) (if + ( i32.lt_s + ( +local.get $n) + ( + i32.const + + + 0)) + ( then +( + + unreachable))) + + ( + if +( +i32.lt_s + (local.get $n) + (i32.const 2)) + (then (return (local.get $n)))) (if + (i32.eqz +(i32.load (i32.mul (i32.const 4) (local.get $n)))) (then local.get $n i32.const 4 i32.mul + (call $f (i32.sub (local.get $n) (i32.const 1))) + (call $f (i32.sub (local.get $n) +(i32.const 2))) i32.add i32.store )) local.get $n i32.const 4 i32.mul i32.load return)) +``` + +Owi will format it like this: + +```sh +$ dune exec -- owi fmt horrible.wat +(module + (memory 10) + (func $f (param $n i32) (result i32) + local.get $n + i32.const 0 + i32.lt_s + (if + (then + unreachable + ) + ) + local.get $n + i32.const 2 + i32.lt_s + (if + (then + local.get $n + return + ) + ) + i32.const 4 + local.get $n + i32.mul + i32.load align=1 + i32.eqz + (if + (then + local.get $n + i32.const 4 + i32.mul + local.get $n + i32.const 1 + i32.sub + call $f + local.get $n + i32.const 2 + i32.sub + call $f + i32.add + i32.store align=1 + ) + ) + local.get $n + i32.const 4 + i32.mul + i32.load align=1 + return + ) +) +``` + +Are you able to recognize the program now? + ## Man page ```sh diff --git a/example/fmt/horrible.wat b/example/fmt/horrible.wat new file mode 100644 index 000000000..4348daef0 --- /dev/null +++ b/example/fmt/horrible.wat @@ -0,0 +1,30 @@ +(module (memory +10) (func +$f (param + $n i32) (result +i32) (if + ( i32.lt_s + ( +local.get $n) + ( + i32.const + + + 0)) + ( then +( + + unreachable))) + + ( + if +( +i32.lt_s + (local.get $n) + (i32.const 2)) + (then (return (local.get $n)))) (if + (i32.eqz +(i32.load (i32.mul (i32.const 4) (local.get $n)))) (then local.get $n i32.const 4 i32.mul + (call $f (i32.sub (local.get $n) (i32.const 1))) + (call $f (i32.sub (local.get $n) +(i32.const 2))) i32.add i32.store )) local.get $n i32.const 4 i32.mul i32.load return)) diff --git a/example/lib/README.md b/example/lib/README.md new file mode 100644 index 000000000..40161ff3e --- /dev/null +++ b/example/lib/README.md @@ -0,0 +1,44 @@ +# Using the library + +## Quickstart + +Given a file `quickstart.wat`, here's how to parse and run this file: + +```ocaml +# open Owi;; +# let filename = "quickstart.wat";; +val filename : string = "quickstart.wat" +# let m = + match Parse.Module.from_file ~filename with + | Ok script -> script + | Error e -> failwith e;; +val m : Text.modul = +... +# let module_to_run, link_state = + match Compile.until_link Link.empty_state ~unsafe:false ~optimize:false ~name:None m with + | Ok v -> v + | Error e -> failwith e;; +val module_to_run : '_weak1 Link.module_to_run = +... +val link_state : '_weak1 Link.state = +... +# let () = + Log.debug_on := true; + match Interpret.Concrete.modul link_state.envs module_to_run with + | Ok () -> () + | Error e -> failwith e;; +interpreting ... +stack : [ ] +running instr: call 0 +calling func : func f +stack : [ ] +running instr: i32.const 24 +stack : [ i32.const 24 ] +running instr: i32.const 24 +stack : [ i32.const 24 ; i32.const 24 ] +running instr: i32.add +stack : [ i32.const 48 ] +running instr: drop +stack : [ ] +stack : [ ] +``` diff --git a/example/lib/dune b/example/lib/dune new file mode 100644 index 000000000..c6459a3fc --- /dev/null +++ b/example/lib/dune @@ -0,0 +1,6 @@ +(mdx + (libraries owi) + (deps + %{bin:owi} + quickstart.wat) + (files README.md)) diff --git a/example/lib/quickstart.wat b/example/lib/quickstart.wat new file mode 100644 index 000000000..2bfb8b56d --- /dev/null +++ b/example/lib/quickstart.wat @@ -0,0 +1,9 @@ +(module $quickstart + (func $f + i32.const 24 + i32.const 24 + i32.add + drop + ) + (start $f) +) diff --git a/example/opt/README.md b/example/opt/README.md index 97fd71e94..d5c6a0e89 100644 --- a/example/opt/README.md +++ b/example/opt/README.md @@ -1,5 +1,366 @@ # Optimizer +## Usage + +Given the useless program `useless.wat`: + + +```wat +(module + (func $i32binop + i32.const 21 + i32.const 21 + i32.add + i32.const 0 + drop + drop + + i32.const 63 + i32.const 21 + i32.sub + i32.const 0 + drop + drop + + i32.const 21 + i32.const 2 + i32.mul + i32.const 0 + drop + drop + + i32.const 84 + i32.const 2 + i32.div_s + i32.const 0 + drop + drop + + i32.const 84 + i32.const 2 + i32.div_u + i32.const 0 + drop + drop + + i32.const -42 + i32.const 4 + i32.rem_s ;; 4*10 + 2 > =-2 + i32.const 0 + drop + drop + + i32.const -42 + i32.const 4 + i32.rem_u ;; 4*10 + 2 > =2 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.and ;; bitwise ope > =1 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.or ;; bitwise ope > =3 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.xor ;; bitwise ope > =2 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.shl ;; shift left ope > =8 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.shr_u ;; shift right ope > =1 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.shr_s ;; shift right ope > =1 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.rotl ;; rotation left ope > =8 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.rotr ;; rotation left ope > =1 + i32.const 0 + drop + drop + ) + + (func $i64binop + i64.const 21 + i64.const 21 + i64.add + i32.const 0 + drop + drop + + i64.const 63 + i64.const 21 + i64.sub + i32.const 0 + drop + drop + + i64.const 21 + i64.const 2 + i64.mul + i32.const 0 + drop + drop + + i64.const 84 + i64.const 2 + i64.div_s + i32.const 0 + drop + drop + + i64.const 84 + i64.const 2 + i64.div_u + i32.const 0 + drop + drop + + i64.const -42 + i64.const 4 + i64.rem_s ;; 4*10 + 2 > =-2 + i32.const 0 + drop + drop + + i64.const -42 + i64.const 4 + i64.rem_u ;; 4*10 + 2 > =2 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.and ;; bitwise ope > =1 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.or ;; bitwise ope > =3 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.xor ;; bitwise ope > =2 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.shl ;; shift left ope > =8 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.shr_u ;; shift right ope > =1 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.shr_s ;; shift right ope > =1 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.rotl ;; rotation left ope > =8 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.rotr ;; rotation left ope > =1 + i32.const 0 + drop + drop + ) + + (func $f32binop + f32.const 21 + f32.const 21 + f32.add + i32.const 0 + drop + drop + + f32.const 63 + f32.const 21 + f32.sub + i32.const 0 + drop + drop + + f32.const 21 + f32.const 2 + f32.mul + i32.const 0 + drop + drop + + f32.const 84 + f32.const 2 + f32.div + i32.const 0 + drop + drop + + f32.const 42 + f32.const 21 + f32.max + i32.const 0 + drop + drop + + f32.const 42 + f32.const 84 + f32.min + i32.const 0 + drop + drop + + f32.const -42 + f32.const 21 + f32.copysign + i32.const 0 + drop + drop + ) + + (func $f64binop + f64.const 21 + f64.const 21 + f64.add + i32.const 0 + drop + drop + + f64.const 63 + f64.const 21 + f64.sub + i32.const 0 + drop + drop + + f64.const 21 + f64.const 2 + f64.mul + i32.const 0 + drop + drop + + f64.const 84 + f64.const 2 + f64.div + i32.const 0 + drop + drop + + f64.const 42 + f64.const 21 + f64.max + i32.const 0 + drop + drop + + f64.const 42 + f64.const 84 + f64.min + i32.const 0 + drop + drop + + f64.const -42 + f64.const 21 + f64.copysign + i32.const 0 + drop + drop + ) + + (func $start + call $i32binop + call $i64binop + call $f32binop + call $f64binop + ) + + (start $start) +) +``` + +Owi is able to get rid of most of the code: + +```sh +$ dune exec -- owi opt useless.wat +(module + (func $i32binop + + ) + (func $i64binop + + ) + (func $f32binop + + ) + (func $f64binop + + ) + (func $start + call 0 + call 1 + call 2 + call 3 + ) + (start 4) +) +``` + ## Man page ```sh diff --git a/example/opt/dune b/example/opt/dune index 9c38e0219..5f8091b55 100644 --- a/example/opt/dune +++ b/example/opt/dune @@ -1,5 +1,6 @@ (mdx (libraries owi) (deps - %{bin:owi}) + %{bin:owi} + useless.wat) (files README.md)) diff --git a/example/opt/useless.wat b/example/opt/useless.wat new file mode 100644 index 000000000..04e22f507 --- /dev/null +++ b/example/opt/useless.wat @@ -0,0 +1,326 @@ +(module + (func $i32binop + i32.const 21 + i32.const 21 + i32.add + i32.const 0 + drop + drop + + i32.const 63 + i32.const 21 + i32.sub + i32.const 0 + drop + drop + + i32.const 21 + i32.const 2 + i32.mul + i32.const 0 + drop + drop + + i32.const 84 + i32.const 2 + i32.div_s + i32.const 0 + drop + drop + + i32.const 84 + i32.const 2 + i32.div_u + i32.const 0 + drop + drop + + i32.const -42 + i32.const 4 + i32.rem_s ;; 4*10 + 2 > =-2 + i32.const 0 + drop + drop + + i32.const -42 + i32.const 4 + i32.rem_u ;; 4*10 + 2 > =2 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.and ;; bitwise ope > =1 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.or ;; bitwise ope > =3 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.xor ;; bitwise ope > =2 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.shl ;; shift left ope > =8 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.shr_u ;; shift right ope > =1 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.shr_s ;; shift right ope > =1 + i32.const 0 + drop + drop + + i32.const 1 + i32.const 3 + i32.rotl ;; rotation left ope > =8 + i32.const 0 + drop + drop + + i32.const 8 + i32.const 3 + i32.rotr ;; rotation left ope > =1 + i32.const 0 + drop + drop + ) + + (func $i64binop + i64.const 21 + i64.const 21 + i64.add + i32.const 0 + drop + drop + + i64.const 63 + i64.const 21 + i64.sub + i32.const 0 + drop + drop + + i64.const 21 + i64.const 2 + i64.mul + i32.const 0 + drop + drop + + i64.const 84 + i64.const 2 + i64.div_s + i32.const 0 + drop + drop + + i64.const 84 + i64.const 2 + i64.div_u + i32.const 0 + drop + drop + + i64.const -42 + i64.const 4 + i64.rem_s ;; 4*10 + 2 > =-2 + i32.const 0 + drop + drop + + i64.const -42 + i64.const 4 + i64.rem_u ;; 4*10 + 2 > =2 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.and ;; bitwise ope > =1 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.or ;; bitwise ope > =3 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.xor ;; bitwise ope > =2 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.shl ;; shift left ope > =8 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.shr_u ;; shift right ope > =1 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.shr_s ;; shift right ope > =1 + i32.const 0 + drop + drop + + i64.const 1 + i64.const 3 + i64.rotl ;; rotation left ope > =8 + i32.const 0 + drop + drop + + i64.const 8 + i64.const 3 + i64.rotr ;; rotation left ope > =1 + i32.const 0 + drop + drop + ) + + (func $f32binop + f32.const 21 + f32.const 21 + f32.add + i32.const 0 + drop + drop + + f32.const 63 + f32.const 21 + f32.sub + i32.const 0 + drop + drop + + f32.const 21 + f32.const 2 + f32.mul + i32.const 0 + drop + drop + + f32.const 84 + f32.const 2 + f32.div + i32.const 0 + drop + drop + + f32.const 42 + f32.const 21 + f32.max + i32.const 0 + drop + drop + + f32.const 42 + f32.const 84 + f32.min + i32.const 0 + drop + drop + + f32.const -42 + f32.const 21 + f32.copysign + i32.const 0 + drop + drop + ) + + (func $f64binop + f64.const 21 + f64.const 21 + f64.add + i32.const 0 + drop + drop + + f64.const 63 + f64.const 21 + f64.sub + i32.const 0 + drop + drop + + f64.const 21 + f64.const 2 + f64.mul + i32.const 0 + drop + drop + + f64.const 84 + f64.const 2 + f64.div + i32.const 0 + drop + drop + + f64.const 42 + f64.const 21 + f64.max + i32.const 0 + drop + drop + + f64.const 42 + f64.const 84 + f64.min + i32.const 0 + drop + drop + + f64.const -42 + f64.const 21 + f64.copysign + i32.const 0 + drop + drop + ) + + (func $start + call $i32binop + call $i64binop + call $f32binop + call $f64binop + ) + + (start $start) +) diff --git a/example/sym/README.md b/example/sym/README.md index 8ef9ebe97..6bd87d289 100644 --- a/example/sym/README.md +++ b/example/sym/README.md @@ -1,5 +1,43 @@ # Symbolic interpreter +## Basic example + +The symbolic interpreter allows to define *symbolic variables* whose values is considered to be any possible value. Then, we collect informations during the execution and when an error is reached, we try to see if there's a possible value for all the symbolic variables by taking all our informations into account. + +In the following file, we define `x` as a symbolic variable. Then if `5 < x`, we fail. + + +```wat +(module + (import "symbolic" "i32" (func $gen_i32 (param i32) (result i32))) + + (func $start (local $x i32) + (local.set $x (call $gen_i32 (i32.const 42))) + (if (i32.lt_s (i32.const 5) (local.get $x)) (then + unreachable + ))) + + (start $start) +) +``` + +Let's see if owi is able to find a value for `x` that lead to an error: + +```sh +$ dune exec owi -- sym ./mini.wat +Trap: unreachable +Model: + (model + (x_0 i32 (i32 6))) +Reached problem! +``` + +Indeed, if `x` is equal to `6` then, the `unreachable` instruction will be reached. + +## Complicated example + +TODO + ## Man page ```sh diff --git a/example/sym/dune b/example/sym/dune index 9c38e0219..56c783211 100644 --- a/example/sym/dune +++ b/example/sym/dune @@ -1,5 +1,5 @@ (mdx (libraries owi) (deps - %{bin:owi}) + %{bin:owi} mini.wat) (files README.md)) diff --git a/example/sym/mini.wat b/example/sym/mini.wat new file mode 100644 index 000000000..20a0818a9 --- /dev/null +++ b/example/sym/mini.wat @@ -0,0 +1,11 @@ +(module + (import "symbolic" "i32" (func $gen_i32 (param i32) (result i32))) + + (func $start (local $x i32) + (local.set $x (call $gen_i32 (i32.const 42))) + (if (i32.lt_s (i32.const 5) (local.get $x)) (then + unreachable + ))) + + (start $start) +) From 74ad930bb32b5fa165048223da138c2f7dc16997 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:42:07 +0100 Subject: [PATCH 10/18] dead link again --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3653d4079..e3eb05a4a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - [`owi fmt`]: a formatter for Wasm; - [`owi opt`]: an optimizer for Wasm; - [`owi run`]: a concrete Wasm interpreter; -- [`owi script`]: an interpreter for [Wasm specification tests]; +- [`owi script`]: an interpreter for [Wasm scripts]; - [`owi sym`]: a symbolic Wasm interpreter. It also provides an [OCaml library] which allows for instance to [import OCaml functions in a Wasm module] in a type-safe way! @@ -142,9 +142,8 @@ This project was funded through the [NGI0 Core] Fund, a fund established by [NLn [Owi project on NLnet]: https://nlnet.nl/project/OWI [reference test suite script]: https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#scripts [september 2023]: https://invidious.zapashcanon.fr/watch?v=IM76cMP3Eqo -[spectest module]: https://github.com/WebAssembly/spec/tree/main/interpreter#scripts +[Wasm scripts]: https://github.com/WebAssembly/spec/tree/main/interpreter#scripts [video of our talk]: https://invidious.zapashcanon.fr/watch?v=os_pknmiqmU -[Wasm specification tests]: https://github.com/webassembly/spec/blob/main/interpreter/readme.md#spectest-host-module [WebAssembly]: https://webassembly.org [WebAssembly Research Center]: https://www.cs.cmu.edu/wrc From 16bdb7e72787532540c0b7c785ce1949caf3c1c0 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:43:54 +0100 Subject: [PATCH 11/18] more doc --- example/README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++ example/dune | 5 +++++ 2 files changed, 62 insertions(+) create mode 100644 example/dune diff --git a/example/README.md b/example/README.md index fbeb07ab6..aa0b04973 100644 --- a/example/README.md +++ b/example/README.md @@ -14,6 +14,63 @@ - [OCaml library] - [import OCaml functions in a Wasm module] +## Man page + +```sh +$ dune exec -- owi --help +OWI(1) Owi Manual OWI(1) + +NAME + owi - OCaml WebAssembly Interpreter + +SYNOPSIS + owi [COMMAND] … + +COMMANDS + c [OPTION]… [ARG]… + Compile a C file to Wasm and run the symbolic interpreter on it + + fmt [--inplace] [OPTION]… ARG + Format a .wat or .wast file + + opt [--debug] [--unsafe] [OPTION]… ARG + Optimize a module + + run [OPTION]… [ARG]… + Run the concrete interpreter + + script [OPTION]… [ARG]… + Run a reference test suite script + + sym [OPTION]… [ARG]… + Run the symbolic interpreter + +COMMON OPTIONS + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --version + Show version information. + +EXIT STATUS + owi exits with: + + 0 on success. + + 123 on indiscriminate errors reported on standard error. + + 124 on command line parsing errors. + + 125 on unexpected internal errors (bugs). + +BUGS + Email them to . + +Owi 11VERSION11 OWI(1) +``` + [`owi c`]: ./c [`owi fmt`]: ./fmt [`owi opt`]: ./opt diff --git a/example/dune b/example/dune new file mode 100644 index 000000000..9c38e0219 --- /dev/null +++ b/example/dune @@ -0,0 +1,5 @@ +(mdx + (libraries owi) + (deps + %{bin:owi}) + (files README.md)) From 6d5be74d1603c11be4de87549d819682de51c01d Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:45:52 +0100 Subject: [PATCH 12/18] typo --- example/define_host_function/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/define_host_function/README.md b/example/define_host_function/README.md index 3dd6d33b0..8d147fdc5 100644 --- a/example/define_host_function/README.md +++ b/example/define_host_function/README.md @@ -104,7 +104,7 @@ $ dune exec -- ./extern.exe 13 ``` -To learn more, see our advanced [Game of Live] example +To learn more, see our advanced [Game of Life] example based on the famous cellular automaton by Conway. It show how to link several modules from different `.wat` files. [Game of Life]: ./life_game From df0bc913b313a0f217dc30c6b8968a9a39e8d79d Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 03:59:44 +0100 Subject: [PATCH 13/18] add osx depexts --- owi.opam | 1 + owi.opam.template | 1 + 2 files changed, 2 insertions(+) diff --git a/owi.opam b/owi.opam index 4cce93bda..785f05a7a 100644 --- a/owi.opam +++ b/owi.opam @@ -58,4 +58,5 @@ dev-repo: "git+https://github.com/ocamlpro/owi.git" available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" depexts: [ ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} + ["llvm" "wabt"] {os-family = "osx"} ] diff --git a/owi.opam.template b/owi.opam.template index 0ed3fabde..3fcf43a62 100644 --- a/owi.opam.template +++ b/owi.opam.template @@ -1,4 +1,5 @@ available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" depexts: [ ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} + ["llvm" "wabt"] {os-family = "osx"} ] From dad8bd6618578192229da658087cdf0296691312 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 13:03:02 +0100 Subject: [PATCH 14/18] Update dune-project Co-authored-by: filipeom <33165733+filipeom@users.noreply.github.com> --- dune-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dune-project b/dune-project index 4b9f012ba..5ed8962c3 100644 --- a/dune-project +++ b/dune-project @@ -16,7 +16,7 @@ (authors "Léo Andrès " "Pierre Chambart " - "Filipe Marques") + "Filipe Marques ") (maintainers "Léo Andrès ") From 965d80943a8531bf98ed7c1d983421fe37be7f35 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 13:04:10 +0100 Subject: [PATCH 15/18] update owi.opam --- owi.opam | 4 ++-- owi.opam.template | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/owi.opam b/owi.opam index 785f05a7a..094b579ee 100644 --- a/owi.opam +++ b/owi.opam @@ -8,7 +8,7 @@ maintainer: ["Léo Andrès "] authors: [ "Léo Andrès " "Pierre Chambart " - "Filipe Marques" + "Filipe Marques " ] license: "AGPL-3.0-or-later" tags: ["owi" "ocaml" "webassembly" "wasm" "interpreter" "compiler"] @@ -58,5 +58,5 @@ dev-repo: "git+https://github.com/ocamlpro/owi.git" available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" depexts: [ ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} - ["llvm" "wabt"] {os-family = "osx"} + ["llvm" "wabt"] {os-family = "homebrew"} ] diff --git a/owi.opam.template b/owi.opam.template index 3fcf43a62..5875f7469 100644 --- a/owi.opam.template +++ b/owi.opam.template @@ -1,5 +1,5 @@ available: (arch = "x86_32" | arch = "x86_64" | arch = "arm64") & os != "win32" depexts: [ ["llvm" "clang" "lld" "wabt"] {os-family = "debian"} - ["llvm" "wabt"] {os-family = "osx"} + ["llvm" "wabt"] {os-family = "homebrew"} ] From 1d6447d4d356ef8a292c27b554c5dc15378cb152 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 15:29:53 +0100 Subject: [PATCH 16/18] fix manpage rendering --- example/README.md | 2 +- example/c/README.md | 2 +- example/fmt/README.md | 2 +- example/opt/README.md | 2 +- example/run/README.md | 2 +- example/sym/README.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/README.md b/example/README.md index aa0b04973..48d52f314 100644 --- a/example/README.md +++ b/example/README.md @@ -17,7 +17,7 @@ ## Man page ```sh -$ dune exec -- owi --help +$ dune exec -- owi --help=plain OWI(1) Owi Manual OWI(1) NAME diff --git a/example/c/README.md b/example/c/README.md index 504464e9a..ad1fb7fa7 100644 --- a/example/c/README.md +++ b/example/c/README.md @@ -90,7 +90,7 @@ Exercise: can you find another "root" of the polynomial ? :-) ## Man page ```sh -$ dune exec owi -- c --help +$ dune exec owi -- c --help=plain OWI-C(1) Owi Manual OWI-C(1) NAME diff --git a/example/fmt/README.md b/example/fmt/README.md index 2196e5837..8d7bbca46 100644 --- a/example/fmt/README.md +++ b/example/fmt/README.md @@ -99,7 +99,7 @@ Are you able to recognize the program now? ## Man page ```sh -$ dune exec owi -- fmt --help +$ dune exec owi -- fmt --help=plain OWI-FMT(1) Owi Manual OWI-FMT(1) NAME diff --git a/example/opt/README.md b/example/opt/README.md index d5c6a0e89..63e430d6b 100644 --- a/example/opt/README.md +++ b/example/opt/README.md @@ -364,7 +364,7 @@ $ dune exec -- owi opt useless.wat ## Man page ```sh -$ dune exec owi -- opt --help +$ dune exec owi -- opt --help=plain OWI-OPT(1) Owi Manual OWI-OPT(1) NAME diff --git a/example/run/README.md b/example/run/README.md index 5e894a21f..db25a9d89 100644 --- a/example/run/README.md +++ b/example/run/README.md @@ -53,7 +53,7 @@ stack : [ ] ## Man page ```sh -$ dune exec owi -- run --help +$ dune exec owi -- run --help=plain OWI-RUN(1) Owi Manual OWI-RUN(1) NAME diff --git a/example/sym/README.md b/example/sym/README.md index 6bd87d289..ad285f6fb 100644 --- a/example/sym/README.md +++ b/example/sym/README.md @@ -41,7 +41,7 @@ TODO ## Man page ```sh -$ dune exec owi -- sym --help +$ dune exec owi -- sym --help=plain OWI-SYM(1) Owi Manual OWI-SYM(1) NAME From 32af3ef4695099ecf0f5bf76b50fafce5c5e94a0 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 16 Jan 2024 15:48:36 +0100 Subject: [PATCH 17/18] promote tests --- example/README.md | 7 ++----- example/c/README.md | 10 ++++------ example/fmt/README.md | 7 ++----- example/opt/README.md | 7 ++----- example/run/README.md | 7 ++----- example/sym/README.md | 7 ++----- 6 files changed, 14 insertions(+), 31 deletions(-) diff --git a/example/README.md b/example/README.md index 48d52f314..bc27444f7 100644 --- a/example/README.md +++ b/example/README.md @@ -18,8 +18,6 @@ ```sh $ dune exec -- owi --help=plain -OWI(1) Owi Manual OWI(1) - NAME owi - OCaml WebAssembly Interpreter @@ -47,8 +45,8 @@ COMMANDS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -68,7 +66,6 @@ EXIT STATUS BUGS Email them to . -Owi 11VERSION11 OWI(1) ``` [`owi c`]: ./c diff --git a/example/c/README.md b/example/c/README.md index ad1fb7fa7..0fc9d053b 100644 --- a/example/c/README.md +++ b/example/c/README.md @@ -91,10 +91,9 @@ Exercise: can you find another "root" of the polynomial ? :-) ```sh $ dune exec owi -- c --help=plain -OWI-C(1) Owi Manual OWI-C(1) - NAME - owi-c - Compile a C file to Wasm and run the symbolic interpreter on it + owi-c - Compile a C file to Wasm and run the symbolic interpreter on + it SYNOPSIS owi c [OPTION]… [ARG]… @@ -126,8 +125,8 @@ OPTIONS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -150,5 +149,4 @@ BUGS SEE ALSO owi(1) -Owi 11VERSION11 OWI-C(1) ``` diff --git a/example/fmt/README.md b/example/fmt/README.md index 8d7bbca46..c3f74caeb 100644 --- a/example/fmt/README.md +++ b/example/fmt/README.md @@ -100,8 +100,6 @@ Are you able to recognize the program now? ```sh $ dune exec owi -- fmt --help=plain -OWI-FMT(1) Owi Manual OWI-FMT(1) - NAME owi-fmt - Format a .wat or .wast file @@ -114,8 +112,8 @@ OPTIONS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -138,5 +136,4 @@ BUGS SEE ALSO owi(1) -Owi 11VERSION11 OWI-FMT(1) ``` diff --git a/example/opt/README.md b/example/opt/README.md index 63e430d6b..dd20462bf 100644 --- a/example/opt/README.md +++ b/example/opt/README.md @@ -365,8 +365,6 @@ $ dune exec -- owi opt useless.wat ```sh $ dune exec owi -- opt --help=plain -OWI-OPT(1) Owi Manual OWI-OPT(1) - NAME owi-opt - Optimize a module @@ -382,8 +380,8 @@ OPTIONS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -406,5 +404,4 @@ BUGS SEE ALSO owi(1) -Owi 11VERSION11 OWI-OPT(1) ``` diff --git a/example/run/README.md b/example/run/README.md index db25a9d89..00ba1ff41 100644 --- a/example/run/README.md +++ b/example/run/README.md @@ -54,8 +54,6 @@ stack : [ ] ```sh $ dune exec owi -- run --help=plain -OWI-RUN(1) Owi Manual OWI-RUN(1) - NAME owi-run - Run the concrete interpreter @@ -77,8 +75,8 @@ OPTIONS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -101,5 +99,4 @@ BUGS SEE ALSO owi(1) -Owi 11VERSION11 OWI-RUN(1) ``` diff --git a/example/sym/README.md b/example/sym/README.md index ad285f6fb..fc707734d 100644 --- a/example/sym/README.md +++ b/example/sym/README.md @@ -42,8 +42,6 @@ TODO ```sh $ dune exec owi -- sym --help=plain -OWI-SYM(1) Owi Manual OWI-SYM(1) - NAME owi-sym - Run the symbolic interpreter @@ -74,8 +72,8 @@ OPTIONS COMMON OPTIONS --help[=FMT] (default=auto) - Show this help in format FMT. The value FMT must be one of auto, - pager, groff or plain. With auto, the format is pager or plain + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain whenever the TERM env var is dumb or undefined. --version @@ -98,5 +96,4 @@ BUGS SEE ALSO owi(1) -Owi 11VERSION11 OWI-SYM(1) ``` From b06116a22b5a2b3f52d2a5adbbfec2884fb2a5f9 Mon Sep 17 00:00:00 2001 From: Pierre Chambart Date: Tue, 16 Jan 2024 13:51:05 +0100 Subject: [PATCH 18/18] Stops producer when reader dies --- src/choice_monad.ml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/choice_monad.ml b/src/choice_monad.ml index 6a3069a1d..804d3efc3 100644 --- a/src/choice_monad.ml +++ b/src/choice_monad.ml @@ -584,9 +584,14 @@ module MT = struct | None -> () in Domain.spawn (fun () -> - WQ.with_produce global.r - ~started:(fun () -> Counter.incr global.start_counter) - (fun () -> WQ.produce global.w producer) ) + try + WQ.with_produce global.r + ~started:(fun () -> Counter.incr global.start_counter) + (fun () -> WQ.produce global.w producer) + with e -> + let bt = Printexc.get_raw_backtrace () in + WQ.fail global.w; + Printexc.raise_with_backtrace e bt ) let rec loop_and_do (s : 'a Seq.t) f : 'a Seq.t = fun () ->