Add a crate to interface with the WebAssembly spec interpreter
The WebAssembly spec interpreter is written in OCaml and the new crate uses `ocaml-interop` along with a small OCaml wrapper to interpret Wasm modules in-process. The build process for this crate is currently Linux-specific: it requires several OCaml packages (e.g. `apt install -y ocaml-nox ocamlbuild`) as well as `make`, `cp`, and `ar`.
This commit is contained in:
66
crates/fuzzing/wasm-spec-interpreter/ocaml/interpret.ml
Normal file
66
crates/fuzzing/wasm-spec-interpreter/ocaml/interpret.ml
Normal file
@@ -0,0 +1,66 @@
|
||||
(* This module exposes an [interpret] function to Rust. It wraps several different calls from the
|
||||
WebAssembly specification interpreter in a way that we can access across the FFI boundary. To
|
||||
understand this better, see:
|
||||
- the OCaml manual documentation re: calling OCaml from C, https://ocaml.org/manual/intfc.html#s%3Ac-advexample
|
||||
- the [ocaml-interop] example, https://github.com/tezedge/ocaml-interop/blob/master/testing/rust-caller/ocaml/callable.ml
|
||||
*)
|
||||
|
||||
(* Here we access the WebAssembly specification interpreter; this must be linked in. *)
|
||||
open Wasm
|
||||
|
||||
(** Enumerate the types of values we pass across the FFI boundary. This must match `Value` in
|
||||
`src/lib.rs` *)
|
||||
type ffi_value =
|
||||
| I32 of int32
|
||||
| I64 of int64
|
||||
| F32 of int32
|
||||
| F64 of int64
|
||||
|
||||
(** Helper for converting the FFI values to their spec interpreter type. *)
|
||||
let convert_to_wasm (v: ffi_value) : Values.value = match v with
|
||||
| I32 n -> Values.Num (I32 n)
|
||||
| I64 n -> Values.Num (I64 n)
|
||||
| F32 n -> Values.Num (F32 (F32.of_bits n))
|
||||
| F64 n -> Values.Num (F64 (F64.of_bits n))
|
||||
|
||||
(** Helper for converting the spec interpreter values to their FFI type. *)
|
||||
let convert_from_wasm (v: Values.value) : ffi_value = match v with
|
||||
| Values.Num (I32 n) -> I32 n
|
||||
| Values.Num (I64 n) -> I64 n
|
||||
| Values.Num (F32 n) -> F32 (F32.to_bits n)
|
||||
| Values.Num (F64 n) -> F64 (F64.to_bits n)
|
||||
| _ -> failwith "Unknown type"
|
||||
|
||||
(** Parse the given WebAssembly module binary into an Ast.module_. At some point in the future this
|
||||
should also be able to parse the textual form (TODO). *)
|
||||
let parse bytes =
|
||||
(* Optionally, use Bytes.unsafe_to_string here to avoid the copy *)
|
||||
let bytes_as_str = Bytes.to_string bytes in
|
||||
Decode.decode "default" bytes_as_str
|
||||
|
||||
(** Return true if an export is a function. *)
|
||||
let match_exported_func export = match export with
|
||||
| (_, Instance.ExternFunc(func)) -> true
|
||||
| _ -> false
|
||||
|
||||
(** Extract a function from its export or fail. *)
|
||||
let extract_exported_func export = match export with
|
||||
| (_, Instance.ExternFunc(func)) -> func
|
||||
| _ -> failwith ""
|
||||
|
||||
(** Interpret the first exported function with the given parameters and return the result. *)
|
||||
let interpret_exn module_bytes params =
|
||||
let params' = List.map convert_to_wasm params in
|
||||
let module_ = parse module_bytes in
|
||||
let instance = Eval.init module_ [] in
|
||||
let func = extract_exported_func (List.find match_exported_func instance.exports) in
|
||||
let returns = Eval.invoke func params' in
|
||||
let returns' = List.map convert_from_wasm returns in
|
||||
returns' (* TODO eventually we should hash the memory state and return the hash *)
|
||||
|
||||
let interpret module_bytes params =
|
||||
try Ok(interpret_exn module_bytes params) with
|
||||
| _ as e -> Error(Printexc.to_string e)
|
||||
|
||||
let () =
|
||||
Callback.register "interpret" interpret;
|
||||
Reference in New Issue
Block a user