diff --git a/Cargo.toml b/Cargo.toml index f404a19b2..efa0bec15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ glob = "0.2.11" [features] default = ["fast-tests"] - +vfs = ["wasmer-runtime-abi", "wasmer-runtime-core/vfs", "wasmer-emscripten/vfs", "wasmer-clif-backend/vfs"] debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index cd691e566..b410582f3 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -10,6 +10,8 @@ build = "build/mod.rs" [dependencies] wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } +wasmer-runtime-abi = { path = "../runtime-abi", optional = true } lazy_static = "1.2.0" libc = "0.2.49" byteorder = "1" @@ -19,7 +21,6 @@ time = "0.1.41" rand = "0.6" [dev-dependencies] -wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" } wabt = "0.7.2" [target.'cfg(not(windows))'.dev-dependencies] @@ -30,4 +31,5 @@ glob = "0.2.11" [features] clif = [] -llvm = [] \ No newline at end of file +llvm = [] +vfs = ["wasmer-runtime-core/vfs", "wasmer-clif-backend/vfs", "wasmer-runtime-abi"] diff --git a/lib/emscripten/emtests/test_vfs.c b/lib/emscripten/emtests/test_vfs.c new file mode 100644 index 000000000..e7fc5c129 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int main() { + char data[256] = {0}; + ssize_t fd = open("data.txt", 0); + ssize_t result = read((int)fd, &data, 255); + printf("content: %s", data); + printf("fd: %zd\n", fd); + return 0; +} diff --git a/lib/emscripten/emtests/test_vfs.md b/lib/emscripten/emtests/test_vfs.md new file mode 100644 index 000000000..e399c8138 --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.md @@ -0,0 +1,6 @@ +The wasm file `test_vfs.wasm` is generated by compiling the `test_vfs.c` and writing a tar.zst blob with a single file +named `data.txt`. + +The program expects to find a file named `data.txt` and reads the contents and the file descriptor. + +The runtime should mount the virtual filesystem and expose the file. \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.out b/lib/emscripten/emtests/test_vfs.out new file mode 100644 index 000000000..5d8528aff --- /dev/null +++ b/lib/emscripten/emtests/test_vfs.out @@ -0,0 +1 @@ +content: wasmer is awesomer \ No newline at end of file diff --git a/lib/emscripten/emtests/test_vfs.wasm b/lib/emscripten/emtests/test_vfs.wasm new file mode 100644 index 000000000..8456c3f04 Binary files /dev/null and b/lib/emscripten/emtests/test_vfs.wasm differ diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 66d9b75ff..fd82f07ca 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -54,6 +54,11 @@ pub use self::utils::{ get_emscripten_table_size, is_emscripten_module, }; +use std::fs::File; +use std::io::Write; +#[cfg(feature = "vfs")] +use wasmer_runtime_abi::vfs::vfs::VfsBacking; + // TODO: Magic number - how is this calculated? const TOTAL_STACK: u32 = 5_242_880; // TODO: Magic number - how is this calculated? @@ -135,6 +140,9 @@ pub struct EmscriptenData<'a> { pub dyn_call_viji: Option>, pub dyn_call_vijiii: Option>, pub dyn_call_vijj: Option>, + + #[cfg(feature = "vfs")] + pub vfs: Option, } impl<'a> EmscriptenData<'a> { @@ -231,17 +239,33 @@ impl<'a> EmscriptenData<'a> { dyn_call_viji, dyn_call_vijiii, dyn_call_vijj, + #[cfg(feature = "vfs")] + vfs: None, } } } pub fn run_emscripten_instance( - _module: &Module, + module: &Module, instance: &mut Instance, path: &str, args: Vec<&str>, ) -> CallResult<()> { let mut data = EmscriptenData::new(instance); + + // Construct a new virtual filesystem and inject it into the emscripten data + // This is behind a feature flag for now, but will be default in the future + #[cfg(feature = "vfs")] + { + data.vfs = match module.info().custom_sections.get("wasmer_fs") { + Some(bytes) => match VfsBacking::from_tar_zstd_bytes(&bytes[..]) { + Ok(vfs_backing) => Some(vfs_backing), + Err(_) => None, + }, + None => None, + }; + } + let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 39bd2750d..fe16295f5 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -29,7 +29,7 @@ use libc::{ // iovec, lseek, // open, - read, + // read, // readv, rmdir, // writev, @@ -40,7 +40,6 @@ use libc::{ use wasmer_runtime_core::vm::Ctx; use super::env; -use std::cell::Cell; use std::slice; // use std::sys::fd::FileDesc; @@ -67,6 +66,7 @@ pub fn ___syscall1(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) { } /// read +#[cfg(not(feature = "vfs"))] pub fn ___syscall3(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { // -> ssize_t debug!("emscripten::___syscall3 (read) {}", which); @@ -75,7 +75,27 @@ pub fn ___syscall3(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { let count: i32 = varargs.get(ctx); debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_void; - let ret = unsafe { read(fd, buf_addr, count as _) }; + let ret = unsafe { libc::read(fd, buf_addr, count as _) }; + debug!("=> ret: {}", ret); + ret as _ +} + +/// read +#[cfg(feature = "vfs")] +pub fn ___syscall3(ctx: &mut Ctx, which: i32, mut varargs: VarArgs) -> i32 { + // -> ssize_t + debug!("emscripten::___syscall3 (read - vfs) {}", which); + let fd: i32 = varargs.get(ctx); + let buf: u32 = varargs.get(ctx); + let count: i32 = varargs.get(ctx); + debug!("=> fd: {}, buf_offset: {}, count: {}", fd, buf, count); + let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut u8; + let mut buf_slice = unsafe { slice::from_raw_parts_mut(buf_addr, count as _) }; + let emscripten_data = get_emscripten_data(ctx); + let ret = match &mut emscripten_data.vfs { + Some(vfs) => vfs.read_file(fd as _, &mut buf_slice).unwrap(), + None => 0, + }; debug!("=> ret: {}", ret); ret as _ } @@ -91,6 +111,23 @@ pub fn ___syscall4(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { unsafe { write(fd, buf_addr, count as _) as i32 } } +/// open +#[cfg(feature = "vfs")] +pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall5 (open vfs) {}", which); + let pathname: u32 = varargs.get(ctx); + let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8; + let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; + let emscripten_data = get_emscripten_data(ctx); + let fd = if let Some(vfs) = &mut emscripten_data.vfs { + vfs.open_file(path_str).unwrap_or(-1) + } else { + -1 + }; + debug!("=> fd: {}", fd); + return fd as _; +} + /// close pub fn ___syscall6(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall6 (close) {}", which); @@ -307,7 +344,7 @@ pub fn ___syscall145(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> i32 { as *mut c_void; let iov_len = (*guest_iov_addr).iov_len as _; // debug!("=> iov_addr: {:?}, {:?}", iov_base, iov_len); - let curr = read(fd, iov_base, iov_len); + let curr = libc::read(fd, iov_base, iov_len); if curr < 0 { return -1; } diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index a73456d09..5d4aff3c9 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -78,6 +78,7 @@ use libc::SO_NOSIGPIPE; const SO_NOSIGPIPE: c_int = 0; /// open +#[cfg(not(feature = "vfs"))] pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall5 (open) {}", which); let pathname: u32 = varargs.get(ctx); diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index d2584bbd4..c797a3d43 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -1,3 +1,4 @@ +use crate::env::get_emscripten_data; use crate::utils::copy_cstr_into_wasm; use crate::utils::read_string_from_wasm; use crate::varargs::VarArgs; @@ -15,6 +16,7 @@ use wasmer_runtime_core::vm::Ctx; type pid_t = c_int; /// open +#[cfg(not(feature = "vfs"))] pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall5 (open) {}", which); let pathname: u32 = varargs.get(ctx); diff --git a/lib/emscripten/tests/emtests/mod.rs b/lib/emscripten/tests/emtests/mod.rs index 89a66bf2b..76983c05c 100644 --- a/lib/emscripten/tests/emtests/mod.rs +++ b/lib/emscripten/tests/emtests/mod.rs @@ -176,6 +176,8 @@ mod test_unary_literal; mod test_utf; mod test_varargs; mod test_varargs_multi; +#[cfg(feature = "vfs")] +mod test_vfs; mod test_vprintf; mod test_vsnprintf; mod test_wprintf; diff --git a/lib/emscripten/tests/emtests/test_vfs.rs b/lib/emscripten/tests/emtests/test_vfs.rs new file mode 100644 index 000000000..091086e19 --- /dev/null +++ b/lib/emscripten/tests/emtests/test_vfs.rs @@ -0,0 +1,9 @@ +#[test] +fn test_vfs() { + assert_emscripten_output!( + "../../emtests/test_vfs.wasm", + "test_vfs", + vec![], + "../../emtests/test_vfs.out" + ); +}