From 6ca01ce546211cfcb03877102ef7ebdc8eff3fed Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 19 Jun 2019 07:58:02 +0200 Subject: [PATCH] Add wasmtime as default runtime for integration tests --- Cargo.toml | 11 + tests/misc_tests.rs | 54 ++ tests/runtime/mod.rs | 111 ++++ tests/runtime/wasi.rs | 1157 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1333 insertions(+) create mode 100644 tests/misc_tests.rs create mode 100644 tests/runtime/mod.rs create mode 100644 tests/runtime/wasi.rs diff --git a/Cargo.toml b/Cargo.toml index 84b5643cd4..8b1796c65e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,17 @@ nix = "0.13" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features=["std", "handleapi", "processthreadsapi", "winbase", "ws2def", "fileapi"] } +[dev-dependencies] +wasmtime-runtime = { git = "https://github.com/cranestation/wasmtime" } +wasmtime-environ = { git = "https://github.com/cranestation/wasmtime" } +wasmtime-jit = { git = "https://github.com/cranestation/wasmtime" } +cranelift-codegen = "0.30.0" +cranelift-entity = "0.30.0" +cranelift-wasm = "0.30.0" +cranelift-native = "0.30.0" +target-lexicon = "0.3.0" +pretty_env_logger = "0.3" + [lib] name = "wasi_common" crate-type = ["rlib", "staticlib", "cdylib"] diff --git a/tests/misc_tests.rs b/tests/misc_tests.rs new file mode 100644 index 0000000000..85b99f2d78 --- /dev/null +++ b/tests/misc_tests.rs @@ -0,0 +1,54 @@ +mod runtime; + +use std::sync::{Once, ONCE_INIT}; + +static INIT: Once = ONCE_INIT; + +fn setup() { + INIT.call_once(|| { + std::env::set_var("RUST_LOG", "wasi_common=trace"); + pretty_env_logger::init_custom_env("RUST_LOG"); + }); +} + +#[test] +fn sched_yield() { + setup(); + runtime::run_wasm("tests/misc-testsuite/sched_yield.wasm") +} + +#[test] +fn truncation_rights() { + setup(); + runtime::run_wasm("tests/misc-testsuite/truncation_rights.wasm") +} + +#[test] +fn unlink_dir() { + setup(); + runtime::run_wasm("tests/misc-testsuite/unlink_dir.wasm") +} + +#[test] +fn remove_nonempty_dir() { + setup(); + runtime::run_wasm("tests/misc-testsuite/remove_nonempty_dir.wasm") +} + +#[test] +fn interesting_paths() { + setup(); + runtime::run_wasm("tests/misc-testsuite/interesting_paths.wasm") +} + +#[test] +fn nofollow_errors() { + setup(); + runtime::run_wasm("tests/misc-testsuite/nofollow_errors.wasm") +} + +#[test] +fn symlink_loop() { + setup(); + runtime::run_wasm("tests/misc-testsuite/symlink_loop.wasm") +} diff --git a/tests/runtime/mod.rs b/tests/runtime/mod.rs new file mode 100644 index 0000000000..abd49a6720 --- /dev/null +++ b/tests/runtime/mod.rs @@ -0,0 +1,111 @@ +mod wasi; + +use cranelift_codegen::settings; +use cranelift_native; +use std::env; +use std::ffi::OsStr; +use std::fs::{self, File}; +use std::io; +use std::io::prelude::*; +use std::path::{Component, Path, PathBuf}; +use std::process::exit; +use std::time::SystemTime; +use wasmtime_jit::Context; + +fn read_to_end(path: PathBuf) -> Result, io::Error> { + let mut buf: Vec = Vec::new(); + let mut file = File::open(path)?; + file.read_to_end(&mut buf)?; + Ok(buf) +} + +fn read_wasm(path: PathBuf) -> Result, String> { + let data = read_to_end(path).map_err(|err| err.to_string())?; + if data.starts_with(&[b'\0', b'a', b's', b'm']) { + Ok(data) + } else { + Err("Invalid Wasm file encountered".to_owned()) + } +} + +fn handle_module(context: &mut Context, path: &Path) -> Result<(), String> { + // Read the wasm module binary. + let data = read_wasm(path.to_path_buf())?; + + // Compile and instantiating a wasm module. + context + .instantiate_module(None, &data) + .map_err(|e| e.to_string())?; + + Ok(()) +} + +fn prepare_workspace(exe_name: &str) -> Result { + let mut workspace = env::temp_dir(); + let time_now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_err(|err| err.to_string())?; + let subdir = format!("wasi_common_tests_{}_{}", exe_name, time_now.as_secs()); + workspace.push(subdir); + fs::create_dir(workspace.as_path()).map_err(|err| err.to_string())?; + + Ok(workspace + .as_os_str() + .to_str() + .expect("could convert to string") + .to_string()) +} + +fn preopen_workspace(workspace: String) -> (String, File) { + let preopen_dir = wasi_common::preopen_dir(&workspace).unwrap_or_else(|err| { + println!("error while preopening directory {}: {}", workspace, err); + exit(1); + }); + (".".to_owned(), preopen_dir) +} + +pub fn run_wasm>(path: P) { + let isa_builder = cranelift_native::builder().unwrap_or_else(|_| { + panic!("host machine is not a supported target"); + }); + let flag_builder = settings::builder(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + let mut context = Context::with_isa(isa); + + let global_exports = context.get_global_exports(); + + // extract exe name from path + let exe_name = Path::new(path.as_ref()) + .components() + .next_back() + .map(Component::as_os_str) + .and_then(OsStr::to_str) + .unwrap_or("unknown") + .to_owned(); + + let workspace = match prepare_workspace(&exe_name) { + Ok(workspace) => workspace, + Err(message) => { + println!("error while processing preopen dirs: {}", message); + exit(1); + } + }; + let preopen_dirs = &[preopen_workspace(workspace)]; + let argv = vec![exe_name, ".".to_owned()]; + + context.name_instance( + "wasi_unstable".to_owned(), + wasi::instantiate_wasi("", global_exports, preopen_dirs, &argv, &[]) + .expect("instantiating wasi"), + ); + + // Load the main wasm module. + match handle_module(&mut context, path.as_ref()) { + Ok(()) => {} + Err(message) => { + let name = path.as_ref().as_os_str().to_string_lossy(); + println!("error while processing main module {}: {}", name, message); + exit(1); + } + } +} diff --git a/tests/runtime/wasi.rs b/tests/runtime/wasi.rs new file mode 100644 index 0000000000..e4345d95e3 --- /dev/null +++ b/tests/runtime/wasi.rs @@ -0,0 +1,1157 @@ +use cranelift_codegen::ir::types; +use cranelift_codegen::ir::types::{Type, I32, I64}; +use cranelift_codegen::{ir, isa}; +use cranelift_entity::PrimaryMap; +use cranelift_wasm::DefinedFuncIndex; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs::File; +use std::rc::Rc; +use target_lexicon::HOST; +use wasi_common::memory::enc_errno; +use wasi_common::{host, hostcalls, wasm32, WasiCtx, WasiCtxBuilder}; +use wasmtime_environ::{translate_signature, Module}; +use wasmtime_runtime::VMContext; +use wasmtime_runtime::{Imports, InstanceHandle, InstantiationError, VMFunctionBody}; + +fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&mut WasiCtx, host::__wasi_errno_t> { + unsafe { + vmctx.host_state().downcast_mut::().ok_or_else(|| { + println!("!!! no host state named WasiCtx available"); + host::__WASI_EINVAL + }) + } +} + +fn get_memory(vmctx: &mut VMContext) -> Result<&mut [u8], host::__wasi_errno_t> { + unsafe { + match vmctx.lookup_global_export("memory") { + Some(wasmtime_runtime::Export::Memory { + definition, + vmctx: _, + memory: _, + }) => Ok(std::slice::from_raw_parts_mut( + (*definition).base, + (*definition).current_length, + )), + x => { + println!( + "!!! no export named __wasi_memory, or the export isn't a mem: {:?}", + x + ); + Err(host::__WASI_EINVAL) + } + } + } +} + +pub trait AbiRet { + type Abi; + fn convert(self) -> Self::Abi; + fn codegen_tys() -> Vec; +} + +pub trait AbiParam { + type Abi; + fn convert(arg: Self::Abi) -> Self; + fn codegen_ty() -> Type; +} + +macro_rules! cast32 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i32; + + fn convert(self) -> Self::Abi { + self as i32 + } + + fn codegen_tys() -> Vec { vec![I32] } + } + + impl AbiParam for $i { + type Abi = i32; + + fn convert(param: i32) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I32 } + } + )*) +} + +macro_rules! cast64 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i64; + + fn convert(self) -> Self::Abi { + self as i64 + } + + fn codegen_tys() -> Vec { vec![I64] } + } + + impl AbiParam for $i { + type Abi = i64; + + fn convert(param: i64) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I64 } + } + )*) +} + +cast32!(i8 i16 i32 u8 u16 u32); +cast64!(i64 u64); + +impl AbiRet for () { + type Abi = (); + fn convert(self) {} + fn codegen_tys() -> Vec { + Vec::new() + } +} + +macro_rules! syscalls { + ($(pub unsafe extern "C" fn $name:ident($ctx:ident: *mut VMContext $(, $arg:ident: $ty:ty)*,) -> $ret:ty { + $($body:tt)* + })*) => ($( + pub mod $name { + use super::*; + + /// Returns the codegen types of all the parameters to the shim + /// generated + pub fn params() -> Vec { + vec![$(<$ty as AbiParam>::codegen_ty()),*] + } + + /// Returns the codegen types of all the results of the shim + /// generated + pub fn results() -> Vec { + <$ret as AbiRet>::codegen_tys() + } + + /// The actual function pointer to the shim for a syscall. + pub static SHIM: unsafe extern "C" fn( + *mut VMContext, + $(<$ty as AbiParam>::Abi),* + ) -> <$ret as AbiRet>::Abi = shim; + + unsafe extern "C" fn shim( + $ctx: *mut VMContext, + $($arg: <$ty as AbiParam>::Abi,)* + ) -> <$ret as AbiRet>::Abi { + let r = super::$name($ctx, $(<$ty as AbiParam>::convert($arg),)*); + <$ret as AbiRet>::convert(r) + } + } + + pub unsafe extern "C" fn $name($ctx: *mut VMContext, $($arg: $ty,)*) -> $ret { + $($body)* + } + )*) +} + +syscalls! { + pub unsafe extern "C" fn args_get( + vmctx: *mut VMContext, + argv: wasm32::uintptr_t, + argv_buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::args_get(wasi_ctx, memory, argv, argv_buf) + } + + pub unsafe extern "C" fn args_sizes_get( + vmctx: *mut VMContext, + argc: wasm32::uintptr_t, + argv_buf_size: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::args_sizes_get(wasi_ctx, memory, argc, argv_buf_size) + } + + pub unsafe extern "C" fn clock_res_get( + vmctx: *mut VMContext, + clock_id: wasm32::__wasi_clockid_t, + resolution: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::clock_res_get(memory, clock_id, resolution) + } + + pub unsafe extern "C" fn clock_time_get( + vmctx: *mut VMContext, + clock_id: wasm32::__wasi_clockid_t, + precision: wasm32::__wasi_timestamp_t, + time: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::clock_time_get(memory, clock_id, precision, time) + } + + pub unsafe extern "C" fn environ_get( + vmctx: *mut VMContext, + environ: wasm32::uintptr_t, + environ_buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::environ_get(wasi_ctx, memory, environ, environ_buf) + } + + pub unsafe extern "C" fn environ_sizes_get( + vmctx: *mut VMContext, + environ_count: wasm32::uintptr_t, + environ_buf_size: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::environ_sizes_get(wasi_ctx, memory, environ_count, environ_buf_size) + } + + pub unsafe extern "C" fn fd_prestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_prestat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_prestat_dir_name( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_prestat_dir_name(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn fd_close( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_close(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_datasync( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_datasync(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_pread( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nread: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_pread( + wasi_ctx, + memory, + fd, + iovs, + iovs_len, + offset, + nread + ) + } + + pub unsafe extern "C" fn fd_pwrite( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nwritten: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_pwrite( + wasi_ctx, + memory, + fd, + iovs, + iovs_len, + offset, + nwritten + ) + } + + pub unsafe extern "C" fn fd_read( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nread: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_read(wasi_ctx, memory, fd, iovs, iovs_len, nread) + } + + pub unsafe extern "C" fn fd_renumber( + vmctx: *mut VMContext, + from: wasm32::__wasi_fd_t, + to: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_renumber(wasi_ctx, from, to) + } + + pub unsafe extern "C" fn fd_seek( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filedelta_t, + whence: wasm32::__wasi_whence_t, + newoffset: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_seek(wasi_ctx, memory, fd, offset, whence, newoffset) + } + + pub unsafe extern "C" fn fd_tell( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + newoffset: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_tell(wasi_ctx, memory, fd, newoffset) + } + + pub unsafe extern "C" fn fd_fdstat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_fdstat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_fdstat_set_flags( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_fdflags_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_fdstat_set_flags(wasi_ctx, fd, flags) + } + + pub unsafe extern "C" fn fd_fdstat_set_rights( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_fdstat_set_rights( + wasi_ctx, + fd, + fs_rights_base, + fs_rights_inheriting + ) + } + + pub unsafe extern "C" fn fd_sync( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_sync(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_write( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nwritten: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_write(wasi_ctx, memory, fd, iovs, iovs_len, nwritten) + } + + pub unsafe extern "C" fn fd_advise( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + advice: wasm32::__wasi_advice_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_advise(wasi_ctx, fd, offset, len, advice) + } + + pub unsafe extern "C" fn fd_allocate( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_allocate(wasi_ctx, fd, offset, len) + } + + pub unsafe extern "C" fn path_create_directory( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_create_directory(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn path_link( + vmctx: *mut VMContext, + fd0: wasm32::__wasi_fd_t, + flags0: wasm32::__wasi_lookupflags_t, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd1: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_link( + wasi_ctx, + memory, + fd0, + flags0, + path0, + path_len0, + fd1, + path1, + path_len1 + ) + } + + pub unsafe extern "C" fn path_open( + vmctx: *mut VMContext, + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + oflags: wasm32::__wasi_oflags_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + fs_flags: wasm32::__wasi_fdflags_t, + fd: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_open( + wasi_ctx, + memory, + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + ) + } + + pub unsafe extern "C" fn fd_readdir( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + cookie: wasm32::__wasi_dircookie_t, + buf_used: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_readdir( + wasi_ctx, + memory, + fd, + buf, + buf_len, + cookie, + buf_used + ) + } + + pub unsafe extern "C" fn path_readlink( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + buf_used: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_readlink( + wasi_ctx, + memory, + fd, + path, + path_len, + buf, + buf_len, + buf_used + ) + } + + pub unsafe extern "C" fn path_rename( + vmctx: *mut VMContext, + fd0: wasm32::__wasi_fd_t, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd1: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_rename( + wasi_ctx, + memory, + fd0, + path0, + path_len0, + fd1, + path1, + path_len1 + ) + } + + pub unsafe extern "C" fn fd_filestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_filestat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_filestat_set_times( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fstflags: wasm32::__wasi_fstflags_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_filestat_set_times(wasi_ctx, fd, st_atim, st_mtim, fstflags) + } + + pub unsafe extern "C" fn fd_filestat_set_size( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + size: wasm32::__wasi_filesize_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + hostcalls::fd_filestat_set_size(wasi_ctx, fd, size) + } + + pub unsafe extern "C" fn path_filestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_lookupflags_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_filestat_get(wasi_ctx, memory, fd, flags, path, path_len, buf) + } + + pub unsafe extern "C" fn path_filestat_set_times( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_lookupflags_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fstflags: wasm32::__wasi_fstflags_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_filestat_set_times( + wasi_ctx, + memory, + fd, + flags, + path, + path_len, + st_atim, + st_mtim, + fstflags + ) + } + + pub unsafe extern "C" fn path_symlink( + vmctx: *mut VMContext, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_symlink( + wasi_ctx, + memory, + path0, + path_len0, + fd, + path1, + path_len1 + ) + } + + pub unsafe extern "C" fn path_unlink_file( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_unlink_file(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn path_remove_directory( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::path_remove_directory(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn poll_oneoff( + vmctx: *mut VMContext, + in_: wasm32::uintptr_t, + out: wasm32::uintptr_t, + nsubscriptions: wasm32::size_t, + nevents: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::poll_oneoff(memory, in_, out, nsubscriptions, nevents) + } + + pub unsafe extern "C" fn proc_exit(_vmctx: *mut VMContext, rval: u32,) -> () { + hostcalls::proc_exit(rval) + } + + pub unsafe extern "C" fn proc_raise( + vmctx: *mut VMContext, + sig: wasm32::__wasi_signal_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::proc_raise(wasi_ctx, memory, sig) + } + + pub unsafe extern "C" fn random_get( + vmctx: *mut VMContext, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::random_get(memory, buf, buf_len) + } + + pub unsafe extern "C" fn sched_yield(_vmctx: *mut VMContext,) -> wasm32::__wasi_errno_t { + hostcalls::sched_yield() + } + + pub unsafe extern "C" fn sock_recv( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + ri_data: wasm32::uintptr_t, + ri_data_len: wasm32::size_t, + ri_flags: wasm32::__wasi_riflags_t, + ro_datalen: wasm32::uintptr_t, + ro_flags: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::sock_recv( + wasi_ctx, + memory, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_datalen, + ro_flags + ) + } + + pub unsafe extern "C" fn sock_send( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + si_data: wasm32::uintptr_t, + si_data_len: wasm32::size_t, + si_flags: wasm32::__wasi_siflags_t, + so_datalen: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::sock_send( + wasi_ctx, + memory, + sock, + si_data, + si_data_len, + si_flags, + so_datalen + ) + } + + pub unsafe extern "C" fn sock_shutdown( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + how: wasm32::__wasi_sdflags_t, + ) -> wasm32::__wasi_errno_t { + let wasi_ctx = match get_wasi_ctx(&mut *vmctx) { + Ok(ctx) => ctx, + Err(e) => return enc_errno(e), + }; + + let memory = match get_memory(&mut *vmctx) { + Ok(memory) => memory, + Err(e) => return enc_errno(e), + }; + + hostcalls::sock_shutdown(wasi_ctx, memory, sock, how) + } +} + +pub fn instantiate_wasi( + prefix: &str, + global_exports: Rc>>>, + preopened_dirs: &[(String, File)], + argv: &[String], + environ: &[(String, String)], +) -> Result { + let pointer_type = types::Type::triple_pointer_type(&HOST); + let mut module = Module::new(); + let mut finished_functions: PrimaryMap = + PrimaryMap::new(); + let call_conv = isa::CallConv::triple_default(&HOST); + + macro_rules! signature { + ($name:ident) => {{ + let sig = module.signatures.push(translate_signature( + ir::Signature { + params: $name::params().into_iter().map(ir::AbiParam::new).collect(), + returns: $name::results() + .into_iter() + .map(ir::AbiParam::new) + .collect(), + call_conv, + }, + pointer_type, + )); + let func = module.functions.push(sig); + module.exports.insert( + prefix.to_owned() + stringify!($name), + wasmtime_environ::Export::Function(func), + ); + finished_functions.push($name::SHIM as *const VMFunctionBody); + }}; + } + + signature!(args_get); + signature!(args_sizes_get); + signature!(clock_res_get); + signature!(clock_time_get); + signature!(environ_get); + signature!(environ_sizes_get); + signature!(fd_prestat_get); + signature!(fd_prestat_dir_name); + signature!(fd_close); + signature!(fd_datasync); + signature!(fd_pread); + signature!(fd_pwrite); + signature!(fd_read); + signature!(fd_renumber); + signature!(fd_seek); + signature!(fd_tell); + signature!(fd_fdstat_get); + signature!(fd_fdstat_set_flags); + signature!(fd_fdstat_set_rights); + signature!(fd_sync); + signature!(fd_write); + signature!(fd_advise); + signature!(fd_allocate); + signature!(path_create_directory); + signature!(path_link); + signature!(path_open); + signature!(fd_readdir); + signature!(path_readlink); + signature!(path_rename); + signature!(fd_filestat_get); + signature!(fd_filestat_set_times); + signature!(fd_filestat_set_size); + signature!(path_filestat_get); + signature!(path_filestat_set_times); + signature!(path_symlink); + signature!(path_unlink_file); + signature!(path_remove_directory); + signature!(poll_oneoff); + signature!(proc_exit); + signature!(proc_raise); + signature!(random_get); + signature!(sched_yield); + signature!(sock_recv); + signature!(sock_send); + signature!(sock_shutdown); + + let imports = Imports::none(); + let data_initializers = Vec::new(); + let signatures = PrimaryMap::new(); + + let args: Vec<&str> = argv.iter().map(AsRef::as_ref).collect(); + let mut wasi_ctx_builder = WasiCtxBuilder::new().args(&args).inherit_stdio(); + + for (k, v) in environ { + wasi_ctx_builder = wasi_ctx_builder.env(k, v); + } + + for (dir, f) in preopened_dirs { + wasi_ctx_builder = wasi_ctx_builder.preopened_dir( + f.try_clone().map_err(|err| { + InstantiationError::Resource(format!( + "couldn't clone an instance handle to pre-opened dir: {}", + err + )) + })?, + dir, + ); + } + + let wasi_ctx = wasi_ctx_builder.build().map_err(|err| { + InstantiationError::Resource(format!("couldn't assemble WASI context object: {}", err)) + })?; + + InstanceHandle::new( + Rc::new(module), + global_exports, + finished_functions.into_boxed_slice(), + imports, + &data_initializers, + signatures.into_boxed_slice(), + None, + Box::new(wasi_ctx), + ) +}