From 22db10e6438175a418d8e3f3a02642a3854fe0b6 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Fri, 11 Dec 2020 15:00:19 -0800 Subject: [PATCH] add wasmtime adapter and stdio wrappers the stdio wrappers will not work on windows, but thats a whole other can of worms anyway --- crates/wasi-c2/Cargo.toml | 5 ++ crates/wasi-c2/build.rs | 2 + crates/wasi-c2/src/ctx.rs | 38 +++++++++ crates/wasi-c2/src/lib.rs | 3 + crates/wasi-c2/src/stdio.rs | 124 +++++++++++++++++++++++++++++ crates/wasi-c2/wasmtime/Cargo.toml | 23 ++++++ crates/wasi-c2/wasmtime/build.rs | 6 ++ crates/wasi-c2/wasmtime/src/lib.rs | 37 +++++++++ 8 files changed, 238 insertions(+) create mode 100644 crates/wasi-c2/src/stdio.rs create mode 100644 crates/wasi-c2/wasmtime/Cargo.toml create mode 100644 crates/wasi-c2/wasmtime/build.rs create mode 100644 crates/wasi-c2/wasmtime/src/lib.rs diff --git a/crates/wasi-c2/Cargo.toml b/crates/wasi-c2/Cargo.toml index 14324dd229..c2c59f7e7f 100644 --- a/crates/wasi-c2/Cargo.toml +++ b/crates/wasi-c2/Cargo.toml @@ -13,6 +13,11 @@ include = ["src/**/*", "LICENSE", "build.rs"] build = "build.rs" publish = false +# This doesn't actually link to a native library, but it allows us to set env +# vars like `DEP_WASI_C2_19_*` for crates that have build scripts and depend +# on this crate, allowing other crates to use the same witx files. +links = "wasi-c2-19" + [dependencies] anyhow = "1.0" thiserror = "1.0" diff --git a/crates/wasi-c2/build.rs b/crates/wasi-c2/build.rs index fdb3f82111..a83d4831ad 100644 --- a/crates/wasi-c2/build.rs +++ b/crates/wasi-c2/build.rs @@ -3,6 +3,8 @@ fn main() { let cwd = std::env::current_dir().unwrap(); let wasi = cwd.join("..").join("wasi-common").join("WASI"); + // this will be available to dependent crates via the DEP_WASI_C2_19_WASI env var: println!("cargo:wasi={}", wasi.display()); + // and available to our own crate as WASI_ROOT: println!("cargo:rustc-env=WASI_ROOT={}", wasi.display()); } diff --git a/crates/wasi-c2/src/ctx.rs b/crates/wasi-c2/src/ctx.rs index b4febbb5a8..acfd8271a6 100644 --- a/crates/wasi-c2/src/ctx.rs +++ b/crates/wasi-c2/src/ctx.rs @@ -1,6 +1,7 @@ use crate::dir::{DirCaps, DirEntry, WasiDir}; use crate::file::{FileCaps, FileEntry, WasiFile}; use crate::table::Table; +use crate::Error; use std::cell::{RefCell, RefMut}; use std::path::PathBuf; use std::rc::Rc; @@ -10,6 +11,10 @@ pub struct WasiCtx { } impl WasiCtx { + pub fn builder() -> WasiCtxBuilder { + WasiCtxBuilder(WasiCtx::new()) + } + pub fn new() -> Self { WasiCtx { table: Rc::new(RefCell::new(Table::new())), @@ -52,3 +57,36 @@ impl WasiCtx { self.table.borrow_mut() } } + +pub struct WasiCtxBuilder(WasiCtx); + +impl WasiCtxBuilder { + pub fn build(self) -> Result { + Ok(self.0) + } + pub fn arg(&mut self, _arg: &str) -> &mut Self { + // Intentionally left blank. We do not handle arguments yet. + self + } + pub fn inherit_stdio(&mut self) -> &mut Self { + self.0.insert_file( + 0, + Box::new(crate::stdio::stdin()), + FileCaps::READ, + FileCaps::READ, + ); + self.0.insert_file( + 1, + Box::new(crate::stdio::stdout()), + FileCaps::WRITE, + FileCaps::WRITE, + ); + self.0.insert_file( + 2, + Box::new(crate::stdio::stderr()), + FileCaps::WRITE, + FileCaps::WRITE, + ); + self + } +} diff --git a/crates/wasi-c2/src/lib.rs b/crates/wasi-c2/src/lib.rs index 462c1c52e9..44710a5372 100644 --- a/crates/wasi-c2/src/lib.rs +++ b/crates/wasi-c2/src/lib.rs @@ -5,7 +5,10 @@ mod dir; mod error; mod file; pub mod snapshots; +pub mod stdio; pub mod table; pub use ctx::WasiCtx; +pub use dir::{DirCaps, WasiDir}; pub use error::Error; +pub use file::{FileCaps, WasiFile}; diff --git a/crates/wasi-c2/src/stdio.rs b/crates/wasi-c2/src/stdio.rs new file mode 100644 index 0000000000..a53c56596f --- /dev/null +++ b/crates/wasi-c2/src/stdio.rs @@ -0,0 +1,124 @@ +use crate::file::{FdFlags, Filestat, Filetype, OFlags, WasiFile}; +use crate::Error; +#[cfg(unix)] +use std::os::unix::io::{AsRawFd, RawFd}; + +pub struct Stdin(std::io::Stdin); + +pub fn stdin() -> Stdin { + Stdin(std::io::stdin()) +} + +#[cfg(unix)] +impl AsRawFd for Stdin { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl WasiFile for Stdin { + fn datasync(&self) -> Result<(), Error> { + Ok(()) + } + fn sync(&self) -> Result<(), Error> { + Ok(()) + } + fn get_filetype(&self) -> Result { + Ok(Filetype::CharacterDevice) + } + fn get_fdflags(&self) -> Result { + todo!() + } + fn get_oflags(&self) -> Result { + todo!() + } + fn set_oflags(&self, _flags: OFlags) -> Result<(), Error> { + todo!() + } + fn get_filestat(&self) -> Result { + todo!() + } + fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { + todo!() + } +} + +pub struct Stdout(std::io::Stdout); + +pub fn stdout() -> Stdout { + Stdout(std::io::stdout()) +} + +#[cfg(unix)] +impl AsRawFd for Stdout { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl WasiFile for Stdout { + fn datasync(&self) -> Result<(), Error> { + Ok(()) + } + fn sync(&self) -> Result<(), Error> { + Ok(()) + } + fn get_filetype(&self) -> Result { + Ok(Filetype::CharacterDevice) + } + fn get_fdflags(&self) -> Result { + todo!() + } + fn get_oflags(&self) -> Result { + todo!() + } + fn set_oflags(&self, _flags: OFlags) -> Result<(), Error> { + todo!() + } + fn get_filestat(&self) -> Result { + todo!() + } + fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { + todo!() + } +} + +pub struct Stderr(std::io::Stderr); + +pub fn stderr() -> Stderr { + Stderr(std::io::stderr()) +} + +#[cfg(unix)] +impl AsRawFd for Stderr { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl WasiFile for Stderr { + fn datasync(&self) -> Result<(), Error> { + Ok(()) + } + fn sync(&self) -> Result<(), Error> { + Ok(()) + } + fn get_filetype(&self) -> Result { + Ok(Filetype::CharacterDevice) + } + fn get_fdflags(&self) -> Result { + todo!() + } + fn get_oflags(&self) -> Result { + todo!() + } + fn set_oflags(&self, _flags: OFlags) -> Result<(), Error> { + todo!() + } + fn get_filestat(&self) -> Result { + todo!() + } + fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { + todo!() + } +} diff --git a/crates/wasi-c2/wasmtime/Cargo.toml b/crates/wasi-c2/wasmtime/Cargo.toml new file mode 100644 index 0000000000..ea8f4b37b3 --- /dev/null +++ b/crates/wasi-c2/wasmtime/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "wasi-c2-wasmtime" +version = "0.21.0" +authors = ["The Wasmtime Project Developers"] +description = "WASI implementation in Rust" +license = "Apache-2.0 WITH LLVM-exception" +categories = ["wasm"] +keywords = ["webassembly", "wasm"] +repository = "https://github.com/bytecodealliance/wasmtime" +readme = "README.md" +edition = "2018" +include = ["src/**/*", "LICENSE", "build.rs"] +build = "build.rs" +publish = false + +[dependencies] +wasi-c2 = { path = "../" } +wiggle = { path = "../../wiggle", default-features = false, version = "0.21.0" } +wasmtime-wiggle = { path = "../../wiggle/wasmtime", default-features = false, version = "0.21.0" } +cap-std = "0.7" +anyhow = "*" +wasmtime = { path = "../../wasmtime" } + diff --git a/crates/wasi-c2/wasmtime/build.rs b/crates/wasi-c2/wasmtime/build.rs new file mode 100644 index 0000000000..726bbddd30 --- /dev/null +++ b/crates/wasi-c2/wasmtime/build.rs @@ -0,0 +1,6 @@ +fn main() { + // wasi-c2's links & build.rs ensure this variable points to the wasi root: + let wasi_root = std::env::var("DEP_WASI_C2_19_WASI").unwrap(); + // Make it available as WASI_ROOT: + println!("cargo:rustc-env=WASI_ROOT={}", wasi_root); +} diff --git a/crates/wasi-c2/wasmtime/src/lib.rs b/crates/wasi-c2/wasmtime/src/lib.rs new file mode 100644 index 0000000000..f7eb355d9a --- /dev/null +++ b/crates/wasi-c2/wasmtime/src/lib.rs @@ -0,0 +1,37 @@ +pub use wasi_c2::WasiCtx; + +// Defines a `struct Wasi` with member fields and appropriate APIs for dealing +// with all the various WASI exports. +wasmtime_wiggle::wasmtime_integration!({ + // The wiggle code to integrate with lives here: + target: wasi_c2::snapshots::preview_1, + // This must be the same witx document as used above. This should be ensured by + // the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`. + witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], + // This must be the same ctx type as used for the target: + ctx: WasiCtx, + // This macro will emit a struct to represent the instance, + // with this name and docs: + modules: { wasi_snapshot_preview1 => + { name: Wasi, + docs: "An instantiated instance of the wasi exports. + +This represents a wasi module which can be used to instantiate other wasm +modules. This structure exports all that various fields of the wasi instance +as fields which can be used to implement your own instantiation logic, if +necessary. Additionally [`Wasi::get_export`] can be used to do name-based +resolution.", + // Don't use the wiggle generated code to implement proc_exit, we need + // to hook directly into the runtime there: + function_override: { + proc_exit => wasi_proc_exit + } + }, + }, + // Error to return when caller module is missing memory export: + missing_memory: { wasi_c2::snapshots::preview_1::types::Errno::Inval }, +}); + +fn wasi_proc_exit(code: i32) { + panic!("stubbed out: wasi proc exit with code {}", code) +}