diff --git a/Cargo.lock b/Cargo.lock index b7320baa0bd..2f1f6b5cc32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1411,6 +1411,7 @@ version = "0.4.2" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index c9209eb8f43..3d9e5df713d 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -9,14 +9,15 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" } +byteorder = "1" +hashbrown = "0.1" lazy_static = "1.2.0" libc = "0.2.49" -byteorder = "1" time = "0.1.41" wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 400bcd94f97..70d62d95084 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -3,8 +3,10 @@ #[macro_use] extern crate wasmer_runtime_core; +use hashbrown::HashMap; use lazy_static::lazy_static; use std::cell::UnsafeCell; +use std::path::PathBuf; use std::{f64, ffi::c_void}; use wasmer_runtime_core::{ error::CallResult, @@ -141,10 +143,14 @@ pub struct EmscriptenData<'a> { pub stack_save: Option>, pub stack_restore: Option>, pub set_threw: Option>, + pub mapped_dirs: HashMap, } impl<'a> EmscriptenData<'a> { - pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> { + pub fn new( + instance: &'a mut Instance, + mapped_dirs: HashMap, + ) -> EmscriptenData<'a> { let malloc = instance.func("_malloc").unwrap(); let free = instance.func("_free").unwrap(); let memalign = instance.func("_memalign").ok(); @@ -272,6 +278,7 @@ impl<'a> EmscriptenData<'a> { stack_save, stack_restore, set_threw, + mapped_dirs, } } } @@ -282,8 +289,9 @@ pub fn run_emscripten_instance( path: &str, args: Vec<&str>, entrypoint: Option, + mapped_dirs: Vec<(String, PathBuf)>, ) -> CallResult<()> { - let mut data = EmscriptenData::new(instance); + let mut data = EmscriptenData::new(instance, mapped_dirs.into_iter().collect()); 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 057a8ac1961..05766bf7943 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -10,7 +10,8 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; -use super::utils::copy_stat_into_wasm; +use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory}; + use super::varargs::VarArgs; use byteorder::{ByteOrder, LittleEndian}; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 @@ -95,12 +96,16 @@ pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall12 (chdir) {}", _which); let path_ptr = varargs.get_str(ctx); - unsafe { - let _path = std::ffi::CStr::from_ptr(path_ptr); - let ret = chdir(path_ptr); - debug!("=> path: {:?}, ret: {}", _path, ret); + let real_path = get_cstr_path(ctx, path_ptr) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(path_ptr); + let ret = unsafe { chdir(real_path) }; + debug!( + "=> path: {:?}, ret: {}", + unsafe { std::ffi::CStr::from_ptr(real_path) }, ret - } + ); + ret } pub fn ___syscall10(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { @@ -124,11 +129,17 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall38 (rename)"); let old_path = varargs.get_str(ctx); let new_path = varargs.get_str(ctx); - let result = unsafe { rename(old_path, new_path) }; + let real_old_path = get_cstr_path(ctx, old_path) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(old_path); + let real_new_path = get_cstr_path(ctx, new_path) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(new_path); + let result = unsafe { rename(real_old_path, real_new_path) }; debug!( "=> old_path: {}, new_path: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() }, - unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_old_path).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_new_path).to_str().unwrap() }, result ); result @@ -138,7 +149,10 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall40 (rmdir)"); let pathname_addr = varargs.get_str(ctx); - unsafe { rmdir(pathname_addr) } + let real_path = get_cstr_path(ctx, pathname_addr) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(pathname_addr); + unsafe { rmdir(real_path) } } // pipe @@ -244,10 +258,9 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { // getcwd pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { debug!("emscripten::___syscall183"); - use std::env; let buf_offset: c_int = varargs.get(ctx); let _size: c_int = varargs.get(ctx); - let path = env::current_dir(); + let path = get_current_directory(ctx); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); unsafe { @@ -420,9 +433,13 @@ pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in let pathname_addr = varargs.get_str(ctx); let buf: u32 = varargs.get(ctx); + let real_path = get_cstr_path(ctx, pathname_addr) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(pathname_addr); + unsafe { let mut _stat: stat = std::mem::zeroed(); - let ret = stat(pathname_addr, &mut _stat); + let ret = stat(real_path, &mut _stat); debug!( "=> pathname: {}, buf: {} = {}, last os error: {}", std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap(), @@ -457,11 +474,6 @@ pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in 0 } -pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall220"); - -1 -} - // fcntl64 pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall221 (fcntl64) {}", _which); @@ -474,7 +486,7 @@ pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in // |FNONBLOCK - 0x04 debug!("=> fd: {}, cmd: {}", _fd, cmd); match cmd { - 2 => 0, + 1 | 2 => 0, 13 | 14 => 0, // pretend file locking worked _ => -1, } diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index 83a972dfa9e..797878fa366 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -43,6 +43,7 @@ use libc::{ pid_t, pread, pwrite, + readdir, // readv, recvfrom, recvmsg, @@ -107,8 +108,11 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int let pathname_addr = varargs.get_str(ctx); let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); - let _path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() }; - let fd = unsafe { open(pathname_addr, flags, mode) }; + let real_path = utils::get_cstr_path(ctx, pathname_addr) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(pathname_addr); + let _path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }; + let fd = unsafe { open(real_path, flags, mode) }; debug!( "=> path: {}, flags: {}, mode: {} = fd: {}, last os error: {}", _path_str, @@ -154,11 +158,17 @@ pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int let path1 = varargs.get_str(ctx); let path2 = varargs.get_str(ctx); - let result = unsafe { symlink(path1, path2) }; + let real_path1 = utils::get_cstr_path(ctx, path1) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(path1); + let real_path2 = utils::get_cstr_path(ctx, path2) + .map(|cstr| cstr.as_c_str() as *const _ as *const i8) + .unwrap_or(path2); + let result = unsafe { symlink(real_path1, real_path2) }; debug!( "=> path1: {}, path2: {}, result: {}", - unsafe { std::ffi::CStr::from_ptr(path1).to_str().unwrap() }, - unsafe { std::ffi::CStr::from_ptr(path2).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path1).to_str().unwrap() }, + unsafe { std::ffi::CStr::from_ptr(real_path2).to_str().unwrap() }, result, ); result @@ -784,6 +794,45 @@ pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { 0 } +// getdents +// dirent structure is +// i64, i64, u16 (280), i8, [i8; 256] +pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall220"); + let fd: i32 = varargs.get(ctx); + let dirp_addr: i32 = varargs.get(ctx); + let count: u32 = varargs.get(ctx); + + let dirp = emscripten_memory_pointer!(ctx.memory(0), dirp_addr) as *mut u8; + // need to persist stream across calls? + let dir: *mut libc::DIR = unsafe { libc::fdopendir(fd) }; + + let mut pos = 0; + let offset = 280; + while pos + offset <= count as usize { + let dirent = unsafe { readdir(dir) }; + if dirent.is_null() { + break; + } + #[allow(clippy::cast_ptr_alignment)] + unsafe { + *(dirp.add(pos) as *mut u64) = (*dirent).d_ino; + *(dirp.add(pos + 8) as *mut u64) = pos as u64 + offset as u64; + *(dirp.add(pos + 16) as *mut u16) = offset as u16; + *(dirp.add(pos + 18) as *mut u8) = (*dirent).d_type; + let upper_bound = std::cmp::min((*dirent).d_reclen, 254) as usize; + let mut i = 0; + while i < upper_bound { + *(dirp.add(pos + 19 + i) as *mut i8) = (*dirent).d_name[i]; + i += 1; + } + *(dirp.add(pos + 19 + i) as *mut i8) = 0 as i8; + } + pos += offset; + } + pos as i32 +} + /// fallocate pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { debug!("emscripten::___syscall324 (fallocate) {}", _which); diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index d74f3c55b24..1355ebb1cdf 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -249,6 +249,12 @@ pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// getdents +pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { + debug!("emscripten::___syscall220"); + -1 +} + /// fchown pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int { debug!("emscripten::___syscall207 (fchown) {}", _which); diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index 2ad5b9a407c..090ac25d709 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -5,6 +5,7 @@ use libc::stat; use std::ffi::CStr; use std::mem::size_of; use std::os::raw::c_char; +use std::path::PathBuf; use std::slice; use wasmer_runtime_core::memory::Memory; use wasmer_runtime_core::{ @@ -204,6 +205,37 @@ pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String { String::from_utf8_lossy(&v).to_owned().to_string() } +/// This function trys to find an entry in mapdir +/// translating paths into their correct value +pub fn get_cstr_path(ctx: &mut Ctx, path: *const i8) -> Option { + let path_str = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string(); + if let Some(val) = get_emscripten_data(ctx).mapped_dirs.get(&path_str) { + std::ffi::CString::new(val.to_string_lossy().as_bytes()).ok() + } else { + None + } +} + +/// gets the current directory +/// handles mapdir logic +pub fn get_current_directory(ctx: &mut Ctx) -> Option { + if let Some(val) = get_emscripten_data(ctx).mapped_dirs.get(".") { + return Some(val.clone()); + } + std::env::current_dir() + .map(|cwd| { + if let Some(val) = get_emscripten_data(ctx) + .mapped_dirs + .get(&cwd.to_string_lossy().to_string()) + { + val.clone() + } else { + cwd + } + }) + .ok() +} + #[cfg(test)] mod tests { use super::is_emscripten_module; diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 43c1aecbf72..993842a1818 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -56,6 +56,7 @@ macro_rules! assert_emscripten_output { $name, $args, None, + vec![], ).expect("run_emscripten_instance finishes"); let output = capturer.end().unwrap().0; diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 00249ebf92c..cd43a5f0789 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -458,6 +458,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { }, options.args.iter().map(|arg| arg.as_str()).collect(), options.em_entrypoint.clone(), + mapped_dirs, ) .map_err(|e| format!("{:?}", e))?; } else {