diff --git a/.circleci/config.yml b/.circleci/config.yml index e02dd9a55..40c2772ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -111,6 +111,10 @@ jobs: name: Debug flag checked command: | cargo check --features "debug" --release + - run: + name: Check + command: | + make check - run: name: Release command: make release-fast @@ -163,6 +167,11 @@ jobs: ulimit -n 8000 sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680 make test + - run: + name: Check + command: | + export PATH="$HOME/.cargo/bin:$PATH" + make check - run: name: Release command: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 774a3ef4e..cdbf982f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#563](https://github.com/wasmerio/wasmer/pull/563) Improve wasi testing infrastructure + - fixes arg parsing from comments & fixes the mapdir test to have the native code doing the same thing as the WASI code + - makes wasitests-generate output stdout/stderr by default & adds function to print stdout and stderr for a command if it fails + - compiles wasm with size optimizations & strips generated wasm with wasm-strip +- [#554](https://github.com/wasmerio/wasmer/pull/554) Finish implementation of `wasi::fd_seek`, fix bug in filestat ## 0.5.5 - [#541](https://github.com/wasmerio/wasmer/pull/541) Fix dependency graph by making separate test crates; ABI implementations should not depend on compilers. Add Cranelift fork as git submodule of clif-backend diff --git a/Cargo.toml b/Cargo.toml index 017d18e15..dbb82e820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ rustc_version = "0.2.3" [features] default = ["fast-tests", "wasi"] -"loader:kernel" = ["wasmer-kernel-loader"] +"loader-kernel" = ["wasmer-kernel-loader"] debug = ["wasmer-runtime-core/debug"] trace = ["wasmer-runtime-core/trace"] extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] diff --git a/Makefile b/Makefile index 38fbb8dc4..0b518fe2d 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ generate-emtests: WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten-tests --release generate-wasitests: - WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release + WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv generate: generate-spectests generate-emtests generate-wasitests @@ -117,8 +117,11 @@ debug: install: cargo install --path . +check: + cargo check --release --features backend-singlepass,backend-llvm,loader-kernel + release: - cargo build --release --features backend-singlepass,backend-llvm,loader:kernel + cargo build --release --features backend-singlepass,backend-llvm,loader-kernel # Only one backend (cranelift) release-fast: diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 000000000..572e03bdf --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 000000000..5c37cb47e --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,21 @@ + +[package] +name = "wasmer-fuzz" +version = "0.0.1" +authors = ["Automatically generated"] +publish = false + +[package.metadata] +cargo-fuzz = true + +[dependencies] +wasmer-runtime = { path = "../lib/runtime" } +libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "simple_instantiate" +path = "fuzz_targets/simple_instantiate.rs" diff --git a/fuzz/README.md b/fuzz/README.md new file mode 100644 index 000000000..eeb43ebd5 --- /dev/null +++ b/fuzz/README.md @@ -0,0 +1,38 @@ +This directory contains the fuzz tests for wasmer. To fuzz, we use the `cargo-fuzz` package. + +## Installation + +You may need to install the `cargo-fuzz` package to get the `cargo fuzz` subcommand. Use + +```sh +$ cargo install cargo-fuzz +``` + +`cargo-fuzz` is documented in the [Rust Fuzz Book](https://rust-fuzz.github.io/book/cargo-fuzz.html). + +## Running a fuzzer + +Once `cargo-fuzz` is installed, you can run the `simple_instantiate` fuzzer with +```sh +cargo fuzz run simple_instantiate +``` + +You should see output that looks something like this: + +``` +INFO: Seed: 3276026494 +INFO: 8 files found in wasmer/fuzz/corpus/simple_instantiate +INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes +INFO: seed corpus: files: 8 min: 1b max: 1b total: 8b rss: 133Mb +#9 INITED ft: 3 corp: 3/3b lim: 4 exec/s: 0 rss: 142Mb +#23 NEW ft: 4 corp: 4/5b lim: 4 exec/s: 0 rss: 142Mb L: 2/2 MS: 4 ChangeByte-InsertByte-ShuffleBytes-ChangeBit- +#25 NEW ft: 5 corp: 5/6b lim: 4 exec/s: 0 rss: 142Mb L: 1/2 MS: 2 ChangeBinInt-ChangeBit- +#27 NEW ft: 6 corp: 6/9b lim: 4 exec/s: 0 rss: 142Mb L: 3/3 MS: 2 InsertByte-ChangeByte- +#190 REDUCE ft: 6 corp: 6/7b lim: 4 exec/s: 0 rss: 142Mb L: 1/2 MS: 3 ChangeBit-EraseBytes-CrossOver- +#205 REDUCE ft: 7 corp: 7/11b lim: 4 exec/s: 0 rss: 142Mb L: 4/4 MS: 5 ShuffleBytes-CrossOver-InsertByte-ChangeBinInt-CrossOver- +``` +It will continue to generate random inputs forever, until it finds a bug or is terminated. The testcases for bugs it finds go into `fuzz/artifacts/simple_instantiate` and you can rerun the fuzzer on a single input by passing it on the command line `cargo fuzz run simple_instantiate my_testcase.wasm`. + +## Trophy case + +- [x] https://github.com/wasmerio/wasmer/issues/558 diff --git a/fuzz/fuzz_targets/simple_instantiate.rs b/fuzz/fuzz_targets/simple_instantiate.rs new file mode 100644 index 000000000..831bbb1a5 --- /dev/null +++ b/fuzz/fuzz_targets/simple_instantiate.rs @@ -0,0 +1,13 @@ +#![no_main] +#[macro_use] extern crate libfuzzer_sys; +extern crate wasmer_runtime; + +use wasmer_runtime::{ + instantiate, + imports, +}; + +fuzz_target!(|data: &[u8]| { + let import_object = imports! {}; + instantiate(data, &import_object); +}); diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index aa1f6c730..1eb8ba108 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -117,6 +117,7 @@ pub struct CompilerConfig { pub symbol_map: Option>, pub memory_bound_check_mode: MemoryBoundCheckMode, pub enforce_stack_check: bool, + pub track_state: bool, } pub trait Compiler { diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 72046bf6f..8650cb4ac 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -111,7 +111,13 @@ impl ModuleStateMap { .unwrap(); match fsm.call_offsets.get(&(ip - base)) { - Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))), + Some(x) => { + if x.diff_id < fsm.diffs.len() { + Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))) + } else { + None + } + } None => None, } } @@ -132,7 +138,13 @@ impl ModuleStateMap { .unwrap(); match fsm.trappable_offsets.get(&(ip - base)) { - Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))), + Some(x) => { + if x.diff_id < fsm.diffs.len() { + Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))) + } else { + None + } + } None => None, } } @@ -149,7 +161,13 @@ impl ModuleStateMap { .unwrap(); match fsm.loop_offsets.get(&(ip - base)) { - Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))), + Some(x) => { + if x.diff_id < fsm.diffs.len() { + Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))) + } else { + None + } + } None => None, } } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index ffc517910..1e0b3773d 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -74,7 +74,7 @@ pub struct InternalCtx { /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. pub globals: *mut *mut LocalGlobal, - /// A pointer to an array of imported memories, indexed by `MemoryIndex, + /// A pointer to an array of imported memories, indexed by `MemoryIndex`, pub imported_memories: *mut *mut LocalMemory, /// A pointer to an array of imported tables, indexed by `TableIndex`. diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 6ccd8cf29..7bc6581ba 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -317,6 +317,7 @@ pub struct CodegenError { struct CodegenConfig { memory_bound_check_mode: MemoryBoundCheckMode, enforce_stack_check: bool, + track_state: bool, } impl ModuleCodeGenerator @@ -366,7 +367,8 @@ impl ModuleCodeGenerator begin_label_info.1 = Some(begin_offset); let begin_label = begin_label_info.0; - let machine = Machine::new(); + let mut machine = Machine::new(); + machine.track_state = self.config.as_ref().unwrap().track_state; dynasm!( assembler @@ -532,6 +534,7 @@ impl ModuleCodeGenerator self.config = Some(Arc::new(CodegenConfig { memory_bound_check_mode: config.memory_bound_check_mode, enforce_stack_check: config.enforce_stack_check, + track_state: config.track_state, })); Ok(()) } @@ -1594,6 +1597,9 @@ impl X64FunctionCode { fsm: &mut FunctionStateMap, control_stack: &mut [ControlFrame], ) -> usize { + if !m.track_state { + return ::std::usize::MAX; + } let last_frame = control_stack.last_mut().unwrap(); let mut diff = m.state.diff(&last_frame.state); diff.last = Some(last_frame.state_diff_id); @@ -1769,7 +1775,7 @@ impl FunctionCodeGenerator for X64FunctionCode { .unwrap() .insert(a.get_offset(), callback); } - InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {}, + InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} InternalEvent::GetInternal(idx) => { let idx = idx as usize; assert!(idx < INTERNALS_SIZE); @@ -1820,7 +1826,11 @@ impl FunctionCodeGenerator for X64FunctionCode { ), Location::GPR(tmp), ); - let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); + let loc = get_location_released( + a, + &mut self.machine, + self.value_stack.pop().unwrap(), + ); // Move internal into storage. Self::emit_relaxed_binop( @@ -1832,8 +1842,7 @@ impl FunctionCodeGenerator for X64FunctionCode { Location::Memory(tmp, (idx * 8) as i32), ); self.machine.release_temp_gpr(tmp); - } - //_ => unimplemented!(), + } //_ => unimplemented!(), } return Ok(()); } @@ -2411,7 +2420,14 @@ impl FunctionCodeGenerator for X64FunctionCode { loc_b, ); a.emit_jmp(Condition::NotEqual, normal_path); - a.emit_mov(Size::S64, Location::Imm64(0), ret); + Self::emit_relaxed_binop( + a, + &mut self.machine, + Assembler::emit_mov, + Size::S64, + Location::Imm64(0), + ret, + ); a.emit_jmp(Condition::None, end); a.emit_label(normal_path); @@ -4525,9 +4541,14 @@ impl FunctionCodeGenerator for X64FunctionCode { |a, m, addr| { match ret { Location::GPR(_) => {} - _ => { - a.emit_mov(Size::S64, Location::Imm64(0), ret); + Location::Memory(base, offset) => { + a.emit_mov( + Size::S32, + Location::Imm32(0), + Location::Memory(base, offset + 4), + ); // clear upper bits } + _ => unreachable!(), } Self::emit_relaxed_binop( a, diff --git a/lib/singlepass-backend/src/machine.rs b/lib/singlepass-backend/src/machine.rs index 61c3c79ce..b09d0bf85 100644 --- a/lib/singlepass-backend/src/machine.rs +++ b/lib/singlepass-backend/src/machine.rs @@ -13,6 +13,7 @@ pub struct Machine { stack_offset: MachineStackOffset, save_area_offset: Option, pub state: MachineState, + pub(crate) track_state: bool, } impl Machine { @@ -23,6 +24,7 @@ impl Machine { stack_offset: MachineStackOffset(0), save_area_offset: None, state: x64::new_machine_state(), + track_state: true, } } diff --git a/lib/wasi-tests/build/wasitests.rs b/lib/wasi-tests/build/wasitests.rs index c305ee44e..189ab9c17 100644 --- a/lib/wasi-tests/build/wasitests.rs +++ b/lib/wasi-tests/build/wasitests.rs @@ -20,7 +20,6 @@ static BANNER: &str = "// !!! THIS IS A GENERATED FILE !!! // Files autogenerated with cargo build (build/wasitests.rs).\n"; pub fn compile(file: &str, ignores: &HashSet) -> Option { - dbg!(file); let mut output_path = PathBuf::from(file); output_path.set_extension("out"); @@ -31,13 +30,14 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { nn }; - Command::new("rustc") - .arg("+nightly") + println!("Compiling program {} to native", file); + let native_out = Command::new("rustc") .arg(file) .arg("-o") .arg(&normalized_name) .output() .expect("Failed to compile program to native code"); + print_info_on_error(&native_out, "COMPILATION FAILED"); #[cfg(unix)] { @@ -58,18 +58,29 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { let result = Command::new(&normalized_name) .output() .expect("Failed to execute native program"); + print_info_on_error(&result, "NATIVE PROGRAM FAILED"); std::fs::remove_file(&normalized_name).expect("could not delete executable"); let wasm_out_name = format!("{}.wasm", &normalized_name); - Command::new("rustc") + let wasm_compilation_out = Command::new("rustc") .arg("+nightly") .arg("--target=wasm32-wasi") + .arg("-C") + .arg("opt-level=s") .arg(file) .arg("-o") .arg(&wasm_out_name) .output() .expect("Failed to compile program to native code"); + print_info_on_error(&wasm_compilation_out, "WASM COMPILATION"); + + // to prevent commiting huge binary blobs forever + let wasm_strip_out = Command::new("wasm-strip") + .arg(&wasm_out_name) + .output() + .expect("Failed to strip compiled wasm module"); + print_info_on_error(&wasm_strip_out, "STRIPPING WASM"); let ignored = if ignores.contains(&rs_module_name) { "\n#[ignore]" @@ -85,7 +96,7 @@ pub fn compile(file: &str, ignores: &HashSet) -> Option { out_str.push_str("vec!["); for (alias, real_dir) in args.mapdir { out_str.push_str(&format!( - "(\"{}\".to_string(), \"{}\".to_string()),", + "(\"{}\".to_string(), ::std::path::PathBuf::from(\"{}\")),", alias, real_dir )); } @@ -194,25 +205,36 @@ fn extract_args_from_source_file(source_code: &str) -> Option { { let tokenized = arg_line .split_whitespace() + // skip trailing space .skip(1) .map(String::from) .collect::>(); - match tokenized[1].as_ref() { + let command_name = { + let mut cn = tokenized[0].clone(); + assert_eq!( + cn.pop(), + Some(':'), + "Final character of argname must be a colon" + ); + cn + }; + + match command_name.as_ref() { "mapdir" => { - if let [alias, real_dir] = &tokenized[2].split(':').collect::>()[..] { + if let [alias, real_dir] = &tokenized[1].split(':').collect::>()[..] { args.mapdir.push((alias.to_string(), real_dir.to_string())); } else { eprintln!( "Parse error in mapdir {} not parsed correctly", - &tokenized[2] + &tokenized[1] ); } } "env" => { - if let [name, val] = &tokenized[2].split('=').collect::>()[..] { + if let [name, val] = &tokenized[1].split('=').collect::>()[..] { args.envvars.push((name.to_string(), val.to_string())); } else { - eprintln!("Parse error in env {} not parsed correctly", &tokenized[2]); + eprintln!("Parse error in env {} not parsed correctly", &tokenized[1]); } } e => { @@ -224,3 +246,17 @@ fn extract_args_from_source_file(source_code: &str) -> Option { } None } + +fn print_info_on_error(output: &std::process::Output, context: &str) { + if !output.status.success() { + println!("{}", context); + println!( + "stdout:\n{}", + std::str::from_utf8(&output.stdout[..]).unwrap() + ); + eprintln!( + "stderr:\n{}", + std::str::from_utf8(&output.stderr[..]).unwrap() + ); + } +} diff --git a/lib/wasi-tests/tests/README.md b/lib/wasi-tests/tests/README.md new file mode 100644 index 000000000..c7d135cca --- /dev/null +++ b/lib/wasi-tests/tests/README.md @@ -0,0 +1,5 @@ +Most of the files here are generated. + +`_common.rs` is a file containing a macro that the generated tests use to avoid code duplication. + +If you want to add new features, edit `_common.rs` and `wasi-tests/build/wasitests.rs` to use the changed macro. diff --git a/lib/wasi-tests/tests/wasitests/fseek.rs b/lib/wasi-tests/tests/wasitests/fseek.rs new file mode 100644 index 000000000..e48dd5814 --- /dev/null +++ b/lib/wasi-tests/tests/wasitests/fseek.rs @@ -0,0 +1,13 @@ +#[test] +fn test_fseek() { + assert_wasi_output!( + "../../wasitests/fseek.wasm", + "fseek", + vec![( + ".".to_string(), + ::std::path::PathBuf::from("wasitests/test_fs/hamlet") + ),], + vec![], + "../../wasitests/fseek.out" + ); +} diff --git a/lib/wasi-tests/tests/wasitests/mod.rs b/lib/wasi-tests/tests/wasitests/mod.rs index c5e5ed802..fc53db36a 100644 --- a/lib/wasi-tests/tests/wasitests/mod.rs +++ b/lib/wasi-tests/tests/wasitests/mod.rs @@ -9,6 +9,7 @@ mod create_dir; mod envvar; mod file_metadata; mod fs_sandbox_test; +mod fseek; mod hello; mod mapdir; mod quine; diff --git a/lib/wasi-tests/wasitests/create_dir.wasm b/lib/wasi-tests/wasitests/create_dir.wasm index adfb1c2d3..6908bfbe3 100755 Binary files a/lib/wasi-tests/wasitests/create_dir.wasm and b/lib/wasi-tests/wasitests/create_dir.wasm differ diff --git a/lib/wasi-tests/wasitests/envvar.wasm b/lib/wasi-tests/wasitests/envvar.wasm index 2fb521f3c..a952a6c04 100755 Binary files a/lib/wasi-tests/wasitests/envvar.wasm and b/lib/wasi-tests/wasitests/envvar.wasm differ diff --git a/lib/wasi-tests/wasitests/file_metadata.wasm b/lib/wasi-tests/wasitests/file_metadata.wasm index a51bcdf7d..fd9b2ba97 100755 Binary files a/lib/wasi-tests/wasitests/file_metadata.wasm and b/lib/wasi-tests/wasitests/file_metadata.wasm differ diff --git a/lib/wasi-tests/wasitests/fs_sandbox_test.wasm b/lib/wasi-tests/wasitests/fs_sandbox_test.wasm index f41bc5a17..901bdaf16 100755 Binary files a/lib/wasi-tests/wasitests/fs_sandbox_test.wasm and b/lib/wasi-tests/wasitests/fs_sandbox_test.wasm differ diff --git a/lib/wasi-tests/wasitests/fseek.out b/lib/wasi-tests/wasitests/fseek.out new file mode 100644 index 000000000..c9f09be6e --- /dev/null +++ b/lib/wasi-tests/wasitests/fseek.out @@ -0,0 +1,11 @@ +SCENE III. A room in Polonius' h +ouse. + + Enter LAERTES and OPH + And, sister, as the winds gi +r talk with the Lord Hamlet. + +uits, + Breathing like sanctif +is is for all: + I would not, diff --git a/lib/wasi-tests/wasitests/fseek.rs b/lib/wasi-tests/wasitests/fseek.rs new file mode 100644 index 000000000..9b9e9f65e --- /dev/null +++ b/lib/wasi-tests/wasitests/fseek.rs @@ -0,0 +1,47 @@ +// Args: +// mapdir: .:wasitests/test_fs/hamlet + +use std::fs; +use std::io::{Read, Seek, SeekFrom}; +use std::path::PathBuf; + +fn main() { + #[cfg(not(target_os = "wasi"))] + let mut base = PathBuf::from("wasitests/test_fs/hamlet"); + #[cfg(target_os = "wasi")] + let mut base = PathBuf::from("."); + + base.push("act1/scene3.txt"); + + let mut file = fs::File::open(&base).expect("Could not open file"); + + let mut buffer = [0u8; 32]; + + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); + + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); + + assert_eq!(file.seek(SeekFrom::Start(123)).unwrap(), 123); + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); + + assert_eq!(file.seek(SeekFrom::End(-123)).unwrap(), 6617); + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); + + assert_eq!(file.seek(SeekFrom::Current(-250)).unwrap(), 6399); + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); + + assert_eq!(file.seek(SeekFrom::Current(50)).unwrap(), 6481); + assert_eq!(file.read(&mut buffer).unwrap(), 32); + let str_val = std::str::from_utf8(&buffer[..]).unwrap(); + println!("{}", str_val); +} diff --git a/lib/wasi-tests/wasitests/fseek.wasm b/lib/wasi-tests/wasitests/fseek.wasm new file mode 100755 index 000000000..20b51e1f6 Binary files /dev/null and b/lib/wasi-tests/wasitests/fseek.wasm differ diff --git a/lib/wasi-tests/wasitests/hello.wasm b/lib/wasi-tests/wasitests/hello.wasm index 958d5e36d..3e2cfc436 100755 Binary files a/lib/wasi-tests/wasitests/hello.wasm and b/lib/wasi-tests/wasitests/hello.wasm differ diff --git a/lib/wasi-tests/wasitests/mapdir.rs b/lib/wasi-tests/wasitests/mapdir.rs index 915e0249c..8eb90050b 100644 --- a/lib/wasi-tests/wasitests/mapdir.rs +++ b/lib/wasi-tests/wasitests/mapdir.rs @@ -4,9 +4,11 @@ use std::fs; fn main() { - #[cfg(not(target = "wasi"))] - let read_dir = fs::read_dir("wasitests/test_fs/hamlet").unwrap(); - #[cfg(target = "wasi")] + #[cfg(not(target_os = "wasi"))] + let cur_dir = std::env::current_dir().unwrap(); + #[cfg(not(target_os = "wasi"))] + std::env::set_current_dir("wasitests/test_fs/hamlet").unwrap(); + let read_dir = fs::read_dir(".").unwrap(); let mut out = vec![]; for entry in read_dir { @@ -17,4 +19,7 @@ fn main() { for p in out { println!("{}", p); } + // return to the current directory + #[cfg(not(target_os = "wasi"))] + std::env::set_current_dir(cur_dir).unwrap(); } diff --git a/lib/wasi-tests/wasitests/mapdir.wasm b/lib/wasi-tests/wasitests/mapdir.wasm index 32e34888c..ae0eba16b 100755 Binary files a/lib/wasi-tests/wasitests/mapdir.wasm and b/lib/wasi-tests/wasitests/mapdir.wasm differ diff --git a/lib/wasi-tests/wasitests/quine.wasm b/lib/wasi-tests/wasitests/quine.wasm index 3b195bb08..086f19844 100755 Binary files a/lib/wasi-tests/wasitests/quine.wasm and b/lib/wasi-tests/wasitests/quine.wasm differ diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index c03f8d96b..368bf3276 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -911,7 +911,28 @@ pub fn fd_seek( // TODO: handle case if fd is a dir? match whence { __WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64, - __WASI_WHENCE_END => unimplemented!("__WASI__WHENCE_END in wasi::fd_seek"), + __WASI_WHENCE_END => { + use std::io::SeekFrom; + match state.fs.inodes[fd_entry.inode].kind { + Kind::File { ref mut handle } => { + let end = wasi_try!(handle.seek(SeekFrom::End(0)).ok().ok_or(__WASI_EIO)); + // TODO: handle case if fd_entry.offset uses 64 bits of a u64 + fd_entry.offset = (end as i64 + offset) as u64; + } + Kind::Symlink { .. } => { + unimplemented!("wasi::fd_seek not implemented for symlinks") + } + Kind::Dir { .. } => { + // TODO: check this + return __WASI_EINVAL; + } + Kind::Buffer { .. } => { + // seeking buffers probably makes sense + // TODO: implement this + return __WASI_EINVAL; + } + } + } __WASI_WHENCE_SET => fd_entry.offset = offset as u64, _ => return __WASI_EINVAL, } diff --git a/rustfmt.toml b/rustfmt.toml index 4a3e0232a..d590c8b69 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,5 @@ ignore = [ "src/spectests", - "src/emtests", + "src/emscripten-tests", + "src/wasi-tests", ] diff --git a/src/bin/kwasmd.rs b/src/bin/kwasmd.rs index d7c05d48d..def71d589 100644 --- a/src/bin/kwasmd.rs +++ b/src/bin/kwasmd.rs @@ -5,10 +5,10 @@ extern crate structopt; use structopt::StructOpt; -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] use wasmer_singlepass_backend::SinglePassCompiler; -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] use std::os::unix::net::{UnixListener, UnixStream}; #[derive(Debug, StructOpt)] @@ -24,14 +24,14 @@ struct Listen { socket: String, } -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] const CMD_RUN_CODE: u32 = 0x901; -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] const CMD_READ_MEMORY: u32 = 0x902; -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] const CMD_WRITE_MEMORY: u32 = 0x903; -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] fn handle_client(mut stream: UnixStream) { use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::io::{Read, Write}; @@ -54,6 +54,7 @@ fn handle_client(mut stream: UnixStream) { symbol_map: None, memory_bound_check_mode: MemoryBoundCheckMode::Disable, enforce_stack_check: true, + track_state: false, }, &SinglePassCompiler::new(), ) @@ -131,7 +132,7 @@ fn handle_client(mut stream: UnixStream) { } } -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] fn run_listen(opts: Listen) { let listener = UnixListener::bind(&opts.socket).unwrap(); use std::thread; @@ -154,7 +155,7 @@ fn run_listen(opts: Listen) { } } -#[cfg(feature = "loader:kernel")] +#[cfg(feature = "loader-kernel")] fn main() { let options = CLIOptions::from_args(); match options { @@ -164,7 +165,7 @@ fn main() { } } -#[cfg(not(feature = "loader:kernel"))] +#[cfg(not(feature = "loader-kernel"))] fn main() { panic!("Kwasm loader is not enabled during compilation."); } diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 7542e3289..070acd9e8 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -112,10 +112,16 @@ struct Run { )] loader: Option, + /// Path to previously saved instance image to resume. #[cfg(feature = "backend-singlepass")] #[structopt(long = "resume")] resume: Option, + /// Whether or not state tracking should be disabled during compilation. + /// State tracking is necessary for tier switching and backtracing. + #[structopt(long = "no-track-state")] + no_track_state: bool, + /// The command name is a string that will override the first argument passed /// to the wasm program. This is used in wapm to provide nicer output in /// help commands and error messages of the running wasm program @@ -137,7 +143,7 @@ struct Run { #[derive(Debug, Copy, Clone)] enum LoaderName { Local, - #[cfg(feature = "loader:kernel")] + #[cfg(feature = "loader-kernel")] Kernel, } @@ -145,7 +151,7 @@ impl LoaderName { pub fn variants() -> &'static [&'static str] { &[ "local", - #[cfg(feature = "loader:kernel")] + #[cfg(feature = "loader-kernel")] "kernel", ] } @@ -156,7 +162,7 @@ impl FromStr for LoaderName { fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "local" => Ok(LoaderName::Local), - #[cfg(feature = "loader:kernel")] + #[cfg(feature = "loader-kernel")] "kernel" => Ok(LoaderName::Kernel), _ => Err(format!("The loader {} doesn't exist", s)), } @@ -326,14 +332,16 @@ fn execute_wasm(options: &Run) -> Result<(), String> { Backend::LLVM => return Err("the llvm backend is not enabled".to_string()), }; - #[cfg(feature = "loader:kernel")] + let track_state = !options.no_track_state; + + #[cfg(feature = "loader-kernel")] let is_kernel_loader = if let Some(LoaderName::Kernel) = options.loader { true } else { false }; - #[cfg(not(feature = "loader:kernel"))] + #[cfg(not(feature = "loader-kernel"))] let is_kernel_loader = false; let module = if is_kernel_loader { @@ -343,6 +351,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { symbol_map: em_symbol_map, memory_bound_check_mode: MemoryBoundCheckMode::Disable, enforce_stack_check: true, + track_state, }, &*compiler, ) @@ -352,6 +361,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { &wasm_binary[..], CompilerConfig { symbol_map: em_symbol_map, + track_state, ..Default::default() }, &*compiler, @@ -397,6 +407,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { &wasm_binary[..], CompilerConfig { symbol_map: em_symbol_map, + track_state, ..Default::default() }, &*compiler, @@ -440,7 +451,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { .load(LocalLoader) .expect("Can't use the local loader"), ), - #[cfg(feature = "loader:kernel")] + #[cfg(feature = "loader-kernel")] LoaderName::Kernel => Box::new( instance .load(::wasmer_kernel_loader::KernelLoader)