diff --git a/CHANGELOG.md b/CHANGELOG.md index a06380a54..00d7e67e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** + +- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs +- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API +- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements +- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits + +## 0.4.1 - 2018-05-06 + +- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 +- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm - [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm - [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI - [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows diff --git a/Cargo.lock b/Cargo.lock index f556254df..35daf0b07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2256,7 +2256,7 @@ dependencies = [ [[package]] name = "wasmer" -version = "0.4.0" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2266,20 +2266,20 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.3.0", - "wasmer-emscripten 0.3.0", - "wasmer-llvm-backend 0.3.0", - "wasmer-middleware-common 0.3.0", - "wasmer-runtime 0.3.0", - "wasmer-runtime-abi 0.3.0", - "wasmer-runtime-core 0.3.0", - "wasmer-singlepass-backend 0.3.0", - "wasmer-wasi 0.3.0", + "wasmer-clif-backend 0.4.1", + "wasmer-emscripten 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-middleware-common 0.4.1", + "wasmer-runtime 0.4.1", + "wasmer-runtime-abi 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", + "wasmer-wasi 0.4.1", ] [[package]] name = "wasmer-clif-backend" -version = "0.3.0" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "cranelift-codegen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2295,15 +2295,15 @@ dependencies = [ "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", - "wasmer-win-exception-handler 0.3.0", + "wasmer-runtime-core 0.4.1", + "wasmer-win-exception-handler 0.4.1", "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-emscripten" -version = "0.3.0" +version = "0.4.1" 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)", @@ -2312,15 +2312,15 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.3.0", - "wasmer-llvm-backend 0.3.0", - "wasmer-runtime-core 0.3.0", - "wasmer-singlepass-backend 0.3.0", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-llvm-backend" -version = "0.3.0" +version = "0.4.1" dependencies = [ "capstone 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2335,42 +2335,42 @@ dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-middleware-common" -version = "0.3.0" +version = "0.4.1" dependencies = [ - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime" -version = "0.3.0" +version = "0.4.1" dependencies = [ "criterion 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.3.0", - "wasmer-llvm-backend 0.3.0", - "wasmer-runtime-core 0.3.0", - "wasmer-singlepass-backend 0.3.0", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-runtime-abi" -version = "0.3.0" +version = "0.4.1" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", "zbox 0.6.1 (git+https://github.com/wasmerio/zbox?branch=bundle-libsodium)", "zstd 0.4.22+zstd.1.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2378,17 +2378,17 @@ dependencies = [ [[package]] name = "wasmer-runtime-c-api" -version = "0.3.0" +version = "0.4.1" dependencies = [ "cbindgen 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime 0.3.0", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime 0.4.1", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-runtime-core" -version = "0.3.0" +version = "0.4.1" dependencies = [ "blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2414,7 +2414,7 @@ dependencies = [ [[package]] name = "wasmer-singlepass-backend" -version = "0.3.0" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2424,24 +2424,24 @@ dependencies = [ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasmer-spectests" -version = "0.3.0" +version = "0.4.1" dependencies = [ "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-clif-backend 0.3.0", - "wasmer-llvm-backend 0.3.0", - "wasmer-runtime-core 0.3.0", - "wasmer-singlepass-backend 0.3.0", + "wasmer-clif-backend 0.4.1", + "wasmer-llvm-backend 0.4.1", + "wasmer-runtime-core 0.4.1", + "wasmer-singlepass-backend 0.4.1", ] [[package]] name = "wasmer-wasi" -version = "0.3.0" +version = "0.4.1" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2449,18 +2449,18 @@ dependencies = [ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", ] [[package]] name = "wasmer-win-exception-handler" -version = "0.3.0" +version = "0.4.1" dependencies = [ "bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmer-runtime-core 0.3.0", + "wasmer-runtime-core 0.4.1", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index cc9e63a9a..5b1457bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer" -version = "0.4.0" +version = "0.4.1" authors = ["The Wasmer Engineering Team "] edition = "2018" repository = "https://github.com/wasmerio/wasmer" @@ -45,7 +45,8 @@ rustc_version = "0.2.3" [features] default = ["fast-tests", "wasi"] -debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] +debug = ["wasmer-runtime-core/debug"] +extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] # This feature will allow cargo test to run much faster fast-tests = [] "backend:llvm" = ["wasmer-llvm-backend"] diff --git a/Makefile b/Makefile index 1bca6b951..2a1bfa138 100644 --- a/Makefile +++ b/Makefile @@ -84,5 +84,8 @@ production-release: debug-release: cargo build --release --features "debug" +extra-debug-release: + cargo build --release --features "extra-debug" + publish-release: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/ diff --git a/README.md b/README.md index d38ab5c88..b1a4ccfa2 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,12 @@ curl https://get.wasmer.io -sSfL | sh ``` Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨: + * [**Rust**](https://github.com/wasmerio/wasmer-rust-example) * [**C/C++**](https://github.com/wasmerio/wasmer-c-api) -* [**PHP**](https://github.com/wasmerio/php-ext-wasm) -* [**Python**](https://github.com/wasmerio/python-ext-wasm) +* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm) +* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm) +* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm) ### Usage @@ -53,6 +55,24 @@ wasmer run examples/sqlite.wasm wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf ``` +#### With WAPM + +Installing Wasmer through `wasmer.io` includes +[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager. + +Wapm allows you to easily download, run, and distribute WebAssembly binaries. + +```sh +# Install cowsay globally +wapm install -g cowsay + +# Run cowsay +wapm run cowsay "Hello, world!" +``` + +For more information about wapm, check out the [website](https://www.wapm.io) +and this [example program](https://github.com/wapm-packages/rust-wasi-example). + ## Code Structure Wasmer is structured into different directories: diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..5abb6eaee --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +# WebAssembly Examples + +In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime: + +* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)] +* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)] +* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)] +* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)] +* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)] diff --git a/install.sh b/install.sh index c0d0982bd..8d4e1e9d2 100755 --- a/install.sh +++ b/install.sh @@ -130,11 +130,11 @@ wasmer_detect_profile() { wasmer_link() { printf "$cyan> Adding to bash profile...$reset\n" WASMER_PROFILE="$(wasmer_detect_profile)" - LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" - SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$HOME/.wasmer/globals/wapm_packages/.bin:\$PATH\"\n" + LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n" + SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n" # We create the wasmer.sh file - printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh" + printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasmer.sh" if [ -z "${WASMER_PROFILE-}" ] ; then printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n" @@ -156,12 +156,12 @@ wasmer_link() { echo "If this isn't the profile of your current shell then please add the following to your correct profile:" printf "$LOAD_STR$reset\n" - version=`$HOME/.wasmer/bin/wasmer --version` || ( + version=`$INSTALL_DIRECTORY/bin/wasmer --version` || ( printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n" exit 1; ) - printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $HOME/.wasmer/wasmer.sh$reset\n" + printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n" printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n" echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`." echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)" @@ -257,7 +257,7 @@ wasmer_install() { " fi -# if [ -d "$HOME/.wasmer" ]; then +# if [ -d "$INSTALL_DIRECTORY" ]; then # if which wasmer; then # local latest_url # local specified_version @@ -286,11 +286,11 @@ wasmer_install() { # exit 0 # else # printf "$yellow> $wasmer_alt_version is already installed, Specified version: $specified_version.$reset\n" -# rm -rf "$HOME/.wasmer" +# rm -rf "$INSTALL_DIRECTORY" # fi # else -# printf "$red> $HOME/.wasmer already exists, possibly from a past Wasmer install.$reset\n" -# printf "$red> Remove it (rm -rf $HOME/.wasmer) and run this script again.$reset\n" +# printf "$red> $INSTALL_DIRECTORY already exists, possibly from a past Wasmer install.$reset\n" +# printf "$red> Remove it (rm -rf $INSTALL_DIRECTORY) and run this script again.$reset\n" # exit 0 # fi # fi diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index f1aa6ef04..cbfbfb44a 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-clif-backend" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime Cranelift compiler backend" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } cranelift-native = "0.30.0" cranelift-codegen = "0.30.0" cranelift-entity = "0.30.0" @@ -33,7 +33,7 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } -wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.3.0" } +wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" } [features] debug = ["wasmer-runtime-core/debug"] diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index d09da6812..50fbe5dbf 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + mod cache; mod func_env; mod libcalls; diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 343203b9c..2f12713fa 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -1,5 +1,5 @@ //! Installing signal handlers allows us to handle traps and out-of-bounds memory -//! accesses that occur when runniing webassembly. +//! accesses that occur when running WebAssembly. //! //! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 //! @@ -107,7 +107,7 @@ pub fn call_protected( Ok(SIGSEGV) => "segmentation violation", Ok(SIGBUS) => "bus error", Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", + _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. let s = format!("unknown trap at {:p} - {}", faulting_addr, signal); diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index 30cb44ce6..d755cd575 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -4,7 +4,6 @@ use crate::trampoline::Trampoline; use std::cell::Cell; use std::ffi::c_void; use std::ptr::{self, NonNull}; -use wasmer_runtime_core::error::{RuntimeError, RuntimeResult}; use wasmer_runtime_core::typed_func::WasmTrapInfo; use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Func; @@ -12,10 +11,14 @@ use wasmer_win_exception_handler::CallProtectedData; pub use wasmer_win_exception_handler::_call_protected; use winapi::shared::minwindef::DWORD; use winapi::um::minwinbase::{ - EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, + EXCEPTION_ACCESS_VIOLATION, EXCEPTION_ARRAY_BOUNDS_EXCEEDED, EXCEPTION_BREAKPOINT, + EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW, - EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION, - EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW, + EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_GUARD_PAGE, + EXCEPTION_ILLEGAL_INSTRUCTION, EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, + EXCEPTION_INVALID_HANDLE, EXCEPTION_IN_PAGE_ERROR, EXCEPTION_NONCONTINUABLE_EXCEPTION, + EXCEPTION_POSSIBLE_DEADLOCK, EXCEPTION_PRIV_INSTRUCTION, EXCEPTION_SINGLE_STEP, + EXCEPTION_STACK_OVERFLOW, }; thread_local! { @@ -43,7 +46,7 @@ pub fn call_protected( } let CallProtectedData { - code: signum, + code, exception_address, instruction_pointer, } = result.unwrap_err(); @@ -53,7 +56,7 @@ pub fn call_protected( srcloc: _, }) = handler_data.lookup(instruction_pointer as _) { - Err(CallProtError::Trap(match signum as DWORD { + Err(CallProtError::Trap(match code as DWORD { EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds, EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode { TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature, @@ -70,7 +73,7 @@ pub fn call_protected( _ => WasmTrapInfo::Unknown, })) } else { - let signal = match signum as DWORD { + let signal = match code as DWORD { EXCEPTION_FLT_DENORMAL_OPERAND | EXCEPTION_FLT_DIVIDE_BY_ZERO | EXCEPTION_FLT_INEXACT_RESULT @@ -80,10 +83,26 @@ pub fn call_protected( | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", EXCEPTION_ACCESS_VIOLATION => "segmentation violation", - _ => "unkown trapped signal", + EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment", + EXCEPTION_BREAKPOINT => "breakpoint", + EXCEPTION_SINGLE_STEP => "single step", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded", + EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero", + EXCEPTION_INT_OVERFLOW => "int overflow", + EXCEPTION_PRIV_INSTRUCTION => "priv instruction", + EXCEPTION_IN_PAGE_ERROR => "in page error", + EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception", + EXCEPTION_STACK_OVERFLOW => "stack overflow", + EXCEPTION_GUARD_PAGE => "guard page", + EXCEPTION_INVALID_HANDLE => "invalid handle", + EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock", + _ => "unknown exception code", }; - let s = format!("unknown trap at {} - {}", exception_address, signal); + let s = format!( + "unhandled trap at {:x} - code #{:x}: {}", + exception_address, code, signal, + ); Err(CallProtError::Error(Box::new(s))) } diff --git a/lib/emscripten/Cargo.toml b/lib/emscripten/Cargo.toml index 6f37da948..f89f96ca6 100644 --- a/lib/emscripten/Cargo.toml +++ b/lib/emscripten/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-emscripten" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime emscripten implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,14 +9,14 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } lazy_static = "1.2.0" libc = "0.2.49" byteorder = "1" time = "0.1.41" -wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true } +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } [target.'cfg(windows)'.dependencies] rand = "0.6" diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs index 8e107bd3c..88073bc32 100644 --- a/lib/emscripten/src/emscripten_target.rs +++ b/lib/emscripten/src/emscripten_target.rs @@ -55,7 +55,7 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { 0 } pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 { - debug!("emscripten::_pthread_attr_init"); + debug!("emscripten::_pthread_attr_init({})", _a); 0 } pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 { @@ -68,7 +68,10 @@ pub fn _pthread_attr_getstack( _stacksize: i32, _other: i32, ) -> i32 { - debug!("emscripten::_pthread_attr_getstack"); + debug!( + "emscripten::_pthread_attr_getstack({}, {}, {})", + _stackaddr, _stacksize, _other + ); // TODO: Translate from Emscripten // HEAP32[stackaddr >> 2] = STACK_BASE; // HEAP32[stacksize >> 2] = TOTAL_STACK; @@ -87,7 +90,7 @@ pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 { 0 } pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 { - debug!("emscripten::_pthread_getattr_np"); + debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr); 0 } pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 { @@ -732,6 +735,10 @@ pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) { panic!("dyn_call_vj is set to None"); } } +pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) { + debug!("emscripten::invoke_vjji"); + invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5) +} pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) { debug!("emscripten::invoke_vij"); if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij { diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs index 2f799a679..897fb38f0 100644 --- a/lib/emscripten/src/env/mod.rs +++ b/lib/emscripten/src/env/mod.rs @@ -60,21 +60,51 @@ pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) { const MAX_ENV_VALUES: u32 = 64; const TOTAL_ENV_SIZE: u32 = 1024; let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int; - unsafe { + let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe { let (pool_offset, _pool_slice): (u32, &mut [u8]) = allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32); let (env_offset, _env_slice): (u32, &mut [u8]) = allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32); let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int; - let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int; + let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8; *env_ptr = pool_offset as i32; *environment = env_offset as i32; - // *env_ptr = 0; + (pool_offset, env_ptr, pool_ptr) }; - // unsafe { - // *env_ptr = 0; - // }; + + // *env_ptr = 0; + let default_vars = vec![ + ["USER", "web_user"], + ["LOGNAME", "web_user"], + ["PATH", "/"], + ["PWD", "/"], + ["HOME", "/home/web_user"], + ["LANG", "C.UTF-8"], + ["_", "thisProgram"], + ]; + let mut strings = vec![]; + let mut total_size = 0; + for [key, val] in &default_vars { + let line = key.to_string() + "=" + val; + total_size += line.len(); + strings.push(line); + } + if total_size as u32 > TOTAL_ENV_SIZE { + panic!("Environment size exceeded TOTAL_ENV_SIZE!"); + } + unsafe { + for (i, s) in strings.iter().enumerate() { + for (j, c) in s.chars().enumerate() { + debug_assert!(c < u8::max_value() as char); + *pool_ptr.add(j) = c as u8; + } + *env_ptr.add(i * 4) = pool_offset as i32; + pool_offset += s.len() as u32 + 1; + pool_ptr = pool_ptr.add(s.len() + 1); + } + *env_ptr.add(strings.len() * 4) = 0; + } } pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) { diff --git a/lib/emscripten/src/jmp.rs b/lib/emscripten/src/jmp.rs index 015496d29..1d9691bc1 100644 --- a/lib/emscripten/src/jmp.rs +++ b/lib/emscripten/src/jmp.rs @@ -7,7 +7,7 @@ use wasmer_runtime_core::vm::Ctx; /// setjmp pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int { debug!("emscripten::__setjmp (setjmp)"); - abort_with_message(ctx, "missing function: _longjmp"); + abort_with_message(ctx, "missing function: _setjmp"); unreachable!() // unsafe { // // Rather than using the env as the holder of the jump buffer pointer, diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 116a0e88d..6b0340513 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; @@ -50,7 +52,7 @@ mod varargs; pub use self::storage::{align_memory, static_alloc}; pub use self::utils::{ - allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, + allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, get_emscripten_metadata, get_emscripten_table_size, is_emscripten_module, }; @@ -127,6 +129,7 @@ pub struct EmscriptenData<'a> { pub dyn_call_viijiii: Option>, pub dyn_call_viijj: Option>, pub dyn_call_vj: Option>, + pub dyn_call_vjji: Option>, pub dyn_call_vij: Option>, pub dyn_call_viji: Option>, pub dyn_call_vijiii: Option>, @@ -146,11 +149,7 @@ impl<'a> EmscriptenData<'a> { pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> { let malloc = instance.func("_malloc").unwrap(); let free = instance.func("_free").unwrap(); - let memalign = if let Ok(func) = instance.func("_memalign") { - Some(func) - } else { - None - }; + let memalign = instance.func("_memalign").ok(); let memset = instance.func("_memset").unwrap(); let stack_alloc = instance.func("stackAlloc").unwrap(); @@ -198,6 +197,7 @@ impl<'a> EmscriptenData<'a> { let dyn_call_viijiii = instance.func("dynCall_viijiii").ok(); let dyn_call_viijj = instance.func("dynCall_viijj").ok(); let dyn_call_vj = instance.func("dynCall_vj").ok(); + let dyn_call_vjji = instance.func("dynCall_vjji").ok(); let dyn_call_vij = instance.func("dynCall_vij").ok(); let dyn_call_viji = instance.func("dynCall_viji").ok(); let dyn_call_vijiii = instance.func("dynCall_vijiii").ok(); @@ -261,6 +261,7 @@ impl<'a> EmscriptenData<'a> { dyn_call_viijiii, dyn_call_viijj, dyn_call_vj, + dyn_call_vjji, dyn_call_vij, dyn_call_viji, dyn_call_vijiii, @@ -282,6 +283,7 @@ pub fn run_emscripten_instance( instance: &mut Instance, path: &str, args: Vec<&str>, + entrypoint: Option, ) -> CallResult<()> { let mut data = EmscriptenData::new(instance); let data_ptr = &mut data as *mut _ as *mut c_void; @@ -299,52 +301,58 @@ pub fn run_emscripten_instance( // println!("running emscripten instance"); - let main_func = instance.dyn_func("_main")?; - let num_params = main_func.signature().params().len(); - let _result = match num_params { - 2 => { - let (argc, argv) = store_module_arguments(instance.context_mut(), path, args); - instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; - } - 0 => { - instance.call("_main", &[])?; - } - _ => panic!( - "The emscripten main function has received an incorrect number of params {}", - num_params - ), - }; + if let Some(ep) = entrypoint { + debug!("Running entry point: {}", &ep); + let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 }; + //let (argc, argv) = store_module_arguments(instance.context_mut(), args); + instance.call(&ep, &[Value::I32(arg as i32)])?; + } else { + let main_func = instance.dyn_func("_main")?; + let num_params = main_func.signature().params().len(); + let _result = match num_params { + 2 => { + let mut new_args = vec![path]; + new_args.extend(args); + let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); + instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?; + } + 0 => { + instance.call("_main", &[])?; + } + _ => panic!( + "The emscripten main function has received an incorrect number of params {}", + num_params + ), + }; + } // TODO atexit for emscripten // println!("{:?}", data); Ok(()) } -fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) { +fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) { let argc = args.len() + 1; let mut args_slice = vec![0; argc]; - args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 }; - for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) { + for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) { *slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 }; } let (argv_offset, argv_slice): (_, &mut [u32]) = - unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) }; + unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) }; assert!(!argv_slice.is_empty()); for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) { *slot = *arg } argv_slice[argc] = 0; - (argc as u32, argv_offset) + (argc as u32 - 1, argv_offset) } pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) { let dynamictop_ptr = globals.dynamictop_ptr; - let stack_max = globals.stack_max; - - let dynamic_base = align_memory(stack_max); + let dynamic_base = globals.dynamic_base; memory.view::()[(dynamictop_ptr / 4) as usize].set(dynamic_base); } @@ -355,6 +363,7 @@ pub struct EmscriptenGlobalsData { stacktop: u32, stack_max: u32, dynamictop_ptr: u32, + dynamic_base: u32, memory_base: u32, table_base: u32, temp_double_ptr: u32, @@ -424,7 +433,14 @@ impl EmscriptenGlobals { let temp_double_ptr = static_top; static_top += 16; - let dynamictop_ptr = static_alloc(&mut static_top, 4); + let (dynamic_base, dynamictop_ptr) = + get_emscripten_metadata(&module).unwrap_or_else(|| { + let dynamictop_ptr = static_alloc(&mut static_top, 4); + ( + align_memory(align_memory(static_top) + TOTAL_STACK), + dynamictop_ptr, + ) + }); let stacktop = align_memory(static_top); let stack_max = stacktop + TOTAL_STACK; @@ -434,6 +450,7 @@ impl EmscriptenGlobals { stacktop, stack_max, dynamictop_ptr, + dynamic_base, memory_base, table_base, temp_double_ptr, @@ -597,6 +614,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "___syscall272" => func!(crate::syscalls::___syscall272), "___syscall295" => func!(crate::syscalls::___syscall295), "___syscall300" => func!(crate::syscalls::___syscall300), + "___syscall320" => func!(crate::syscalls::___syscall320), "___syscall324" => func!(crate::syscalls::___syscall324), "___syscall330" => func!(crate::syscalls::___syscall330), "___syscall334" => func!(crate::syscalls::___syscall334), @@ -718,6 +736,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject "invoke_v" => func!(crate::emscripten_target::invoke_v), "invoke_vi" => func!(crate::emscripten_target::invoke_vi), "invoke_vj" => func!(crate::emscripten_target::invoke_vj), + "invoke_vjji" => func!(crate::emscripten_target::invoke_vjji), "invoke_vii" => func!(crate::emscripten_target::invoke_vii), "invoke_viii" => func!(crate::emscripten_target::invoke_viii), "invoke_viiii" => func!(crate::emscripten_target::invoke_viiii), diff --git a/lib/emscripten/src/memory.rs b/lib/emscripten/src/memory.rs index 1795dcae6..0f1cb1f19 100644 --- a/lib/emscripten/src/memory.rs +++ b/lib/emscripten/src/memory.rs @@ -21,8 +21,11 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u /// emscripten: _emscripten_get_heap_size pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 { - debug!("emscripten::_emscripten_get_heap_size",); - ctx.memory(0).size().bytes().0 as u32 + debug!("emscripten::_emscripten_get_heap_size"); + let result = ctx.memory(0).size().bytes().0 as u32; + debug!("=> {}", result); + + result } // From emscripten implementation diff --git a/lib/emscripten/src/process.rs b/lib/emscripten/src/process.rs index dbbbcebc2..538ed528f 100644 --- a/lib/emscripten/src/process.rs +++ b/lib/emscripten/src/process.rs @@ -82,7 +82,7 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 { } pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 { - debug!("emscripten::_sem_init"); + debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three); 0 } diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 37f6e2f51..5654a1299 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -31,12 +31,13 @@ use libc::{ off_t, // open, read, + rename, + // sockaddr_in, // readv, rmdir, // writev, stat, write, - // sockaddr_in, }; use wasmer_runtime_core::vm::Ctx; @@ -118,9 +119,21 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { unsafe { getpid() } } -pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { - debug!("emscripten::___syscall38"); - -1 +// rename +pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { + debug!("emscripten::___syscall38 (rename)"); + let old_path_addr: u32 = varargs.get(ctx); + let new_path_addr: u32 = varargs.get(ctx); + let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8; + let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8; + let result = unsafe { rename(old_path, 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() }, + result + ); + result } // rmdir @@ -246,12 +259,21 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in if fd == -1 { let ptr = env::call_memalign(ctx, 16384, len); if ptr == 0 { - return -1; + // ENOMEM + return -12; } + let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8; env::call_memset(ctx, ptr, 0, len); - ptr as _ + for i in 0..(len as usize) { + unsafe { + assert_eq!(*real_ptr.add(i), 0); + } + } + debug!("=> ptr: {}", ptr); + return ptr as i32; } else { - -1 + // return ENODEV + return -19; } } @@ -461,6 +483,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { -1 } +// utimensat +pub fn ___syscall320(_ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int { + debug!("emscripten::___syscall320 (utimensat), {}", _which); + 0 +} + pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { debug!("emscripten::___syscall334"); -1 diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index 32b63593f..ec8b98f3f 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -1,4 +1,6 @@ use crate::varargs::VarArgs; +#[cfg(target_os = "macos")] +use libc::size_t; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html use libc::{ @@ -53,7 +55,6 @@ use libc::{ sendto, setpgid, setsockopt, - size_t, sockaddr, socket, socklen_t, @@ -251,8 +252,9 @@ pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8; let result = unsafe { access(path, amode) }; debug!( - "=> path: {}, result: {}", + "=> path: {}, amode: {}, result: {}", unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }, + amode, result ); result @@ -354,8 +356,13 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall54 (ioctl) {}", _which); let fd: i32 = varargs.get(ctx); let request: u32 = varargs.get(ctx); - debug!("fd: {}, op: {}", fd, request); + debug!("=> fd: {}, op: {}", fd, request); // Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html + // let argp: u32 = varargs.get(ctx); + // let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void; + // let ret = unsafe { ioctl(fd, request as _, argp_ptr) }; + // debug!("=> {}", ret); + // ret match request as _ { 21537 => { // FIONBIO diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 56dfdbda8..32e7a61c6 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -28,7 +28,9 @@ use wasmer_runtime_core::vm::Ctx; use libc::{CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_REALTIME}; #[cfg(target_os = "macos")] -use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; +use libc::CLOCK_REALTIME; +#[cfg(target_os = "macos")] +const CLOCK_MONOTONIC: clockid_t = 1; #[cfg(target_os = "macos")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index e1cf13ef5..2ad5b9a40 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -1,5 +1,6 @@ use super::env; use super::env::get_emscripten_data; +use crate::storage::align_memory; use libc::stat; use std::ffi::CStr; use std::mem::size_of; @@ -39,6 +40,43 @@ pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option) { (memory.minimum, memory.maximum) } +/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1` +/// Assumes values start from the end in this order: +/// Last export: Dynamic Base +/// Second-to-Last export: Dynamic top pointer +pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> { + let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?; + let snd_max_idx = &module + .info() + .globals + .iter() + .map(|(k, _)| k) + .filter(|k| k != max_idx) + .max()?; + + use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32}; + if let ( + GlobalInit { + init: Const(I32(dynamic_base)), + .. + }, + GlobalInit { + init: Const(I32(dynamictop_ptr)), + .. + }, + ) = ( + &module.info().globals[*max_idx], + &module.info().globals[*snd_max_idx], + ) { + Some(( + align_memory(*dynamic_base as u32 - 32), + align_memory(*dynamictop_ptr as u32 - 32), + )) + } else { + None + } +} + pub unsafe fn write_to_buf(ctx: &mut Ctx, string: *const c_char, buf: u32, max: u32) -> u32 { let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *mut c_char; diff --git a/lib/emscripten/tests/emtests/_common.rs b/lib/emscripten/tests/emtests/_common.rs index 18664e4ea..c2e304e32 100644 --- a/lib/emscripten/tests/emtests/_common.rs +++ b/lib/emscripten/tests/emtests/_common.rs @@ -53,6 +53,7 @@ macro_rules! assert_emscripten_output { &mut instance, $name, $args, + None, ).expect("run_emscripten_instance finishes"); let output = capturer.end().unwrap().0; diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index c1a1cbbdc..fabf70cef 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "wasmer-llvm-backend" -version = "0.3.0" +version = "0.4.1" authors = ["Lachlan Sneff "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } wasmparser = "0.29.2" hashbrown = "0.1.8" diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 2795e6271..028207a8c 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8,19 +8,21 @@ use inkwell::{ AddressSpace, FloatPredicate, IntPredicate, }; use smallvec::SmallVec; +use std::sync::Arc; use wasmer_runtime_core::{ + backend::{Backend, CacheGen, Token}, + cache::{Artifact, Error as CacheError}, + codegen::*, memory::MemoryType, - module::ModuleInfo, - structures::{Map, SliceMap, TypedIndex}, + module::{ModuleInfo, ModuleInner}, + structures::{Map, TypedIndex}, types::{ - FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, + FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, }; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, LocalsReader, MemoryImmediate, Operator, OperatorsReader, -}; +use wasmparser::{BinaryReaderError, MemoryImmediate, Operator, Type as WpType}; +use crate::backend::LLVMBackend; use crate::intrinsics::{CtxType, GlobalCache, Intrinsics, MemoryCache}; use crate::read_info::type_to_type; use crate::state::{ControlFrame, IfElseState, State}; @@ -58,141 +60,353 @@ fn type_to_llvm(intrinsics: &Intrinsics, ty: Type) -> BasicTypeEnum { } } -pub fn parse_function_bodies( - info: &ModuleInfo, - code_reader: CodeSectionReader, -) -> Result<(Module, Intrinsics), BinaryReaderError> { - let context = Context::create(); - let module = context.create_module("module"); - let builder = context.create_builder(); - - let intrinsics = Intrinsics::declare(&module, &context); - - let personality_func = module.add_function( - "__gxx_personality_v0", - intrinsics.i32_ty.fn_type(&[], false), - Some(Linkage::External), - ); - - let signatures: Map = info - .signatures - .iter() - .map(|(_, sig)| func_sig_to_llvm(&context, &intrinsics, sig)) - .collect(); - let functions: Map = info - .func_assoc - .iter() - .skip(info.imported_functions.len()) - .map(|(func_index, &sig_index)| { - let func = module.add_function( - &format!("fn{}", func_index.index()), - signatures[sig_index], - Some(Linkage::External), - ); - func.set_personality_function(personality_func); - func - }) - .collect(); - - for (local_func_index, body) in code_reader.into_iter().enumerate() { - let body = body?; - - let locals_reader = body.get_locals_reader()?; - let op_reader = body.get_operators_reader()?; - - parse_function( - &context, - &builder, - &intrinsics, - info, - &signatures, - &functions, - LocalFuncIndex::new(local_func_index), - locals_reader, - op_reader, - ) - .map_err(|e| BinaryReaderError { - message: e.message, - offset: local_func_index, - })?; - } - - // module.print_to_stderr(); - - generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics); - - let pass_manager = PassManager::create_for_module(); - // pass_manager.add_verifier_pass(); - pass_manager.add_function_inlining_pass(); - pass_manager.add_promote_memory_to_register_pass(); - pass_manager.add_cfg_simplification_pass(); - // pass_manager.add_instruction_combining_pass(); - pass_manager.add_aggressive_inst_combiner_pass(); - pass_manager.add_merged_load_store_motion_pass(); - // pass_manager.add_sccp_pass(); - // pass_manager.add_gvn_pass(); - pass_manager.add_new_gvn_pass(); - pass_manager.add_aggressive_dce_pass(); - pass_manager.run_on_module(&module); - - // module.print_to_stderr(); - - Ok((module, intrinsics)) -} - -fn parse_function( - context: &Context, +fn trap_if_not_representatable_as_int( builder: &Builder, intrinsics: &Intrinsics, - info: &ModuleInfo, - signatures: &SliceMap, - functions: &SliceMap, - func_index: LocalFuncIndex, - locals_reader: LocalsReader, - op_reader: OperatorsReader, -) -> Result<(), BinaryReaderError> { - let sig_index = info.func_assoc[func_index.convert_up(info)]; - let func_sig = &info.signatures[sig_index]; + context: &Context, + function: &FunctionValue, + lower_bounds: f64, + upper_bound: f64, + value: FloatValue, +) { + enum FloatSize { + Bits32, + Bits64, + } - let function = functions[func_index]; - let mut state = State::new(); - let entry_block = context.append_basic_block(&function, "entry"); + let failure_block = context.append_basic_block(function, "conversion_failure_block"); + let continue_block = context.append_basic_block(function, "conversion_success_block"); - let return_block = context.append_basic_block(&function, "return"); - builder.position_at_end(&return_block); + let float_ty = value.get_type(); + let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { + (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) + } else if float_ty == intrinsics.f64_ty { + (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) + } else { + unreachable!() + }; - let phis: SmallVec<[PhiValue; 1]> = func_sig - .returns() - .iter() - .map(|&wasmer_ty| type_to_llvm(intrinsics, wasmer_ty)) - .map(|ty| builder.build_phi(ty, &state.var_name())) - .collect(); + let (exponent, invalid_exponent) = { + let float_bits = { + let space = builder.build_alloca(int_ty, "space"); + let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); + builder.build_store(float_ptr, value); + builder.build_load(space, "float_bits").into_int_value() + }; - state.push_block(return_block, phis); - builder.position_at_end(&entry_block); + let (shift_amount, exponent_mask, invalid_exponent) = match float_size { + FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), + FloatSize::Bits64 => ( + 52, + 0b0111111111110000000000000000000000000000000000000000000000000000, + 0b11111111111, + ), + }; - let mut locals = Vec::with_capacity(locals_reader.get_count() as usize); // TODO fix capacity + builder.build_and( + float_bits, + int_ty.const_int(exponent_mask, false), + "masked_bits", + ); - locals.extend( - function - .get_param_iter() - .skip(1) - .enumerate() - .map(|(index, param)| { - let ty = param.get_type(); + ( + builder.build_right_shift( + float_bits, + int_ty.const_int(shift_amount, false), + false, + "exponent", + ), + invalid_exponent, + ) + }; - let alloca = builder.build_alloca(ty, &format!("local{}", index)); - builder.build_store(alloca, param); - alloca - }), + let is_invalid_float = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + exponent, + int_ty.const_int(invalid_exponent, false), + "is_not_normal", + ), + builder.build_or( + builder.build_float_compare( + FloatPredicate::ULT, + value, + float_ty.const_float(lower_bounds), + "less_than_lower_bounds", + ), + builder.build_float_compare( + FloatPredicate::UGT, + value, + float_ty.const_float(upper_bound), + "greater_than_upper_bounds", + ), + "float_not_in_bounds", + ), + "is_invalid_float", ); - let param_len = locals.len(); + let is_invalid_float = builder + .build_call( + intrinsics.expect_i1, + &[ + is_invalid_float.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "is_invalid_float_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); - let mut local_idx = 0; - for local in locals_reader.into_iter() { - let (count, ty) = local?; + builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); + builder.position_at_end(&failure_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&continue_block); +} + +fn trap_if_zero_or_overflow( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + left: IntValue, + right: IntValue, +) { + let int_type = left.get_type(); + + let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { + let min_value = int_type.const_int(i32::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); + (min_value, neg_one_value) + } else if int_type == intrinsics.i64_ty { + let min_value = int_type.const_int(i64::min_value() as u64, false); + let neg_one_value = int_type.const_int(-1i64 as u64, false); + (min_value, neg_one_value) + } else { + unreachable!() + }; + + let should_trap = builder.build_or( + builder.build_int_compare( + IntPredicate::EQ, + right, + int_type.const_int(0, false), + "divisor_is_zero", + ), + builder.build_and( + builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), + builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), + "div_will_overflow", + ), + "div_should_trap", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn trap_if_zero( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + value: IntValue, +) { + let int_type = value.get_type(); + let should_trap = builder.build_int_compare( + IntPredicate::EQ, + value, + int_type.const_int(0, false), + "divisor_is_zero", + ); + + let should_trap = builder + .build_call( + intrinsics.expect_i1, + &[ + should_trap.as_basic_value_enum(), + intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), + ], + "should_trap_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); + let should_trap_block = context.append_basic_block(function, "should_trap_block"); + builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); + builder.position_at_end(&should_trap_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_illegal_arithmetic], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&shouldnt_trap_block); +} + +fn resolve_memory_ptr( + builder: &Builder, + intrinsics: &Intrinsics, + context: &Context, + function: &FunctionValue, + state: &mut State, + ctx: &mut CtxType, + memarg: &MemoryImmediate, + ptr_ty: PointerType, +) -> Result { + // Ignore alignment hint for the time being. + let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); + let var_offset_i32 = state.pop1()?.into_int_value(); + let var_offset = + builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); + let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); + let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics); + + let mem_base_int = match memory_cache { + MemoryCache::Dynamic { + ptr_to_base_ptr, + ptr_to_bounds, + } => { + let base = builder + .build_load(ptr_to_base_ptr, "base") + .into_pointer_value(); + let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); + + let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); + + let base_in_bounds = builder.build_int_compare( + IntPredicate::ULT, + effective_offset, + bounds, + "base_in_bounds", + ); + + let base_in_bounds = builder + .build_call( + intrinsics.expect_i1, + &[ + base_in_bounds.as_basic_value_enum(), + intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), + ], + "base_in_bounds_expect", + ) + .try_as_basic_value() + .left() + .unwrap() + .into_int_value(); + + let in_bounds_continue_block = + context.append_basic_block(function, "in_bounds_continue_block"); + let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); + builder.build_conditional_branch( + base_in_bounds, + &in_bounds_continue_block, + ¬_in_bounds_block, + ); + builder.position_at_end(¬_in_bounds_block); + builder.build_call( + intrinsics.throw_trap, + &[intrinsics.trap_memory_oob], + "throw", + ); + builder.build_unreachable(); + builder.position_at_end(&in_bounds_continue_block); + + base_as_int + } + MemoryCache::Static { + base_ptr, + bounds: _, + } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), + }; + + let effective_address_int = + builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); + Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) +} + +#[derive(Debug)] +pub struct CodegenError { + pub message: String, +} + +pub struct LLVMModuleCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + functions: Vec, + signatures: Map, + signatures_raw: Map, + function_signatures: Option>>, + func_import_count: usize, + personality_func: FunctionValue, + module: Module, +} + +pub struct LLVMFunctionCodeGenerator { + context: Option, + builder: Option, + intrinsics: Option, + state: State, + function: FunctionValue, + func_sig: FuncSig, + signatures: Map, + locals: Vec, // Contains params and locals + num_params: usize, + ctx: Option>, + unreachable_depth: usize, +} + +impl FunctionCodeGenerator for LLVMFunctionCodeGenerator { + fn feed_return(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_param(&mut self, _ty: WpType) -> Result<(), CodegenError> { + Ok(()) + } + + fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + let param_len = self.num_params; + + let mut local_idx = 0; + // let (count, ty) = local?; + let count = n; let wasmer_ty = type_to_type(ty)?; + + let intrinsics = self.intrinsics.as_ref().unwrap(); let ty = type_to_llvm(intrinsics, wasmer_ty); let default_value = match wasmer_ty { @@ -202,51 +416,90 @@ fn parse_function( Type::F64 => intrinsics.f64_zero.as_basic_value_enum(), }; + let builder = self.builder.as_ref().unwrap(); + for _ in 0..count { let alloca = builder.build_alloca(ty, &format!("local{}", param_len + local_idx)); builder.build_store(alloca, default_value); - locals.push(alloca); + self.locals.push(alloca); local_idx += 1; } + Ok(()) } - let start_of_code_block = context.append_basic_block(&function, "start_of_code"); - let entry_end_inst = builder.build_unconditional_branch(&start_of_code_block); - builder.position_at_end(&start_of_code_block); + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let start_of_code_block = self + .context + .as_ref() + .unwrap() + .append_basic_block(&self.function, "start_of_code"); + let entry_end_inst = self + .builder + .as_ref() + .unwrap() + .build_unconditional_branch(&start_of_code_block); + self.builder + .as_ref() + .unwrap() + .position_at_end(&start_of_code_block); - let cache_builder = context.create_builder(); - cache_builder.position_before(&entry_end_inst); - let mut ctx = intrinsics.ctx(info, builder, &function, cache_builder); - let mut unreachable_depth = 0; + let cache_builder = self.context.as_ref().unwrap().create_builder(); + cache_builder.position_before(&entry_end_inst); + let module_info = + unsafe { ::std::mem::transmute::<&ModuleInfo, &'static ModuleInfo>(module_info) }; + let function = unsafe { + ::std::mem::transmute::<&FunctionValue, &'static FunctionValue>(&self.function) + }; + let ctx = CtxType::new(module_info, function, cache_builder); + + self.ctx = Some(ctx); + Ok(()) + } + + fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + let op = match event { + Event::Wasm(x) => x, + Event::Internal(_x) => { + return Ok(()); + } + }; + + let mut state = &mut self.state; + let builder = self.builder.as_ref().unwrap(); + let context = self.context.as_ref().unwrap(); + let function = self.function; + let intrinsics = self.intrinsics.as_ref().unwrap(); + let locals = &self.locals; + let info = module_info; + let signatures = &self.signatures; + let mut ctx = self.ctx.as_mut().unwrap(); - for op in op_reader { - let op = op?; if !state.reachable { - match op { + match *op { Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => { - unreachable_depth += 1; - continue; + self.unreachable_depth += 1; + return Ok(()); } Operator::Else => { - if unreachable_depth != 0 { - continue; + if self.unreachable_depth != 0 { + return Ok(()); } } Operator::End => { - if unreachable_depth != 0 { - unreachable_depth -= 1; - continue; + if self.unreachable_depth != 0 { + self.unreachable_depth -= 1; + return Ok(()); } } _ => { - continue; + return Ok(()); } } } - match op { + match *op { /*************************** * Control Flow instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions @@ -380,7 +633,12 @@ fn parse_function( .iter() .enumerate() .map(|(case_index, &depth)| { - let frame = state.frame_at_depth(depth)?; + let frame_result: Result<&ControlFrame, BinaryReaderError> = + state.frame_at_depth(depth); + let frame = match frame_result { + Ok(v) => v, + Err(e) => return Err(e), + }; let case_index_literal = context.i32_type().const_int(case_index as u64, false); @@ -394,7 +652,8 @@ fn parse_function( builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]); - state.popn(args.len())?; + let args_len = args.len(); + state.popn(args_len)?; state.reachable = false; } Operator::If { ty } => { @@ -616,7 +875,7 @@ fn parse_function( Operator::GetGlobal { global_index } => { let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Const { value } => { state.push1(value); @@ -630,7 +889,7 @@ fn parse_function( Operator::SetGlobal { global_index } => { let value = state.pop1()?; let index = GlobalIndex::new(global_index as usize); - let global_cache = ctx.global_cache(index); + let global_cache = ctx.global_cache(index, intrinsics); match global_cache { GlobalCache::Mut { ptr_to_value } => { builder.build_store(ptr_to_value, value); @@ -666,12 +925,14 @@ fn parse_function( .map(|v| *v) .collect(); - let func_ptr = ctx.local_func(local_func_index, llvm_sig); + let func_ptr = + ctx.local_func(local_func_index, llvm_sig, intrinsics, builder); builder.build_call(func_ptr, ¶ms, &state.var_name()) } LocalOrImport::Import(import_func_index) => { - let (func_ptr_untyped, ctx_ptr) = ctx.imported_func(import_func_index); + let (func_ptr_untyped, ctx_ptr) = + ctx.imported_func(import_func_index, intrinsics); let params: Vec<_> = [ctx_ptr.as_basic_value_enum()] .iter() .chain(state.peekn(func_sig.params().len())?.iter()) @@ -710,8 +971,9 @@ fn parse_function( } Operator::CallIndirect { index, table_index } => { let sig_index = SigIndex::new(index as usize); - let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index); - let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize)); + let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index, intrinsics); + let (table_base, table_bound) = + ctx.table(TableIndex::new(table_index as usize), intrinsics, builder); let func_index = state.pop1()?.into_int_value(); // We assume the table has the `anyfunc` element type. @@ -1468,7 +1730,7 @@ fn parse_function( context, &function, -2147483904.0, - 2_147_483_648.0, + 2147483648.0, v1, ); let res = @@ -1482,8 +1744,8 @@ fn parse_function( intrinsics, context, &function, - -2_147_483_649.0, - 2_147_483_648.0, + -2147483649.0, + 2147483648.0, v1, ); let res = @@ -1503,8 +1765,8 @@ fn parse_function( intrinsics, context, &function, - -9_223_373_136_366_403_584.0, - 9_223_372_036_854_775_808.0, + -9223373136366403584.0, + 9223372036854775808.0, v1, ); let res = @@ -1728,7 +1990,7 @@ fn parse_function( * Load and Store instructions. * https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#load-and-store-instructions ***************************/ - Operator::I32Load { memarg } => { + Operator::I32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1742,7 +2004,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::I64Load { memarg } => { + Operator::I64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1756,7 +2018,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F32Load { memarg } => { + Operator::F32Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1770,7 +2032,7 @@ fn parse_function( let result = builder.build_load(effective_address, &state.var_name()); state.push1(result); } - Operator::F64Load { memarg } => { + Operator::F64Load { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1785,7 +2047,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store { memarg } => { + Operator::I32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1799,7 +2061,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::I64Store { memarg } => { + Operator::I64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1813,7 +2075,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F32Store { memarg } => { + Operator::F32Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1827,7 +2089,7 @@ fn parse_function( )?; builder.build_store(effective_address, value); } - Operator::F64Store { memarg } => { + Operator::F64Store { ref memarg } => { let value = state.pop1()?; let effective_address = resolve_memory_ptr( builder, @@ -1842,7 +2104,7 @@ fn parse_function( builder.build_store(effective_address, value); } - Operator::I32Load8S { memarg } => { + Operator::I32Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1860,7 +2122,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16S { memarg } => { + Operator::I32Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1878,7 +2140,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8S { memarg } => { + Operator::I64Load8S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1896,7 +2158,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16S { memarg } => { + Operator::I64Load16S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1914,7 +2176,7 @@ fn parse_function( builder.build_int_s_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32S { memarg } => { + Operator::I64Load32S { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1933,7 +2195,7 @@ fn parse_function( state.push1(result); } - Operator::I32Load8U { memarg } => { + Operator::I32Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1951,7 +2213,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I32Load16U { memarg } => { + Operator::I32Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1969,7 +2231,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i32_ty, &state.var_name()); state.push1(result); } - Operator::I64Load8U { memarg } => { + Operator::I64Load8U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -1987,7 +2249,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load16U { memarg } => { + Operator::I64Load16U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2005,7 +2267,7 @@ fn parse_function( builder.build_int_z_extend(narrow_result, intrinsics.i64_ty, &state.var_name()); state.push1(result); } - Operator::I64Load32U { memarg } => { + Operator::I64Load32U { ref memarg } => { let effective_address = resolve_memory_ptr( builder, intrinsics, @@ -2024,7 +2286,7 @@ fn parse_function( state.push1(result); } - Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + Operator::I32Store8 { ref memarg } | Operator::I64Store8 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2040,7 +2302,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + Operator::I32Store16 { ref memarg } | Operator::I64Store16 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2056,7 +2318,7 @@ fn parse_function( builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); builder.build_store(effective_address, narrow_value); } - Operator::I64Store32 { memarg } => { + Operator::I64Store32 { ref memarg } => { let value = state.pop1()?.into_int_value(); let effective_address = resolve_memory_ptr( builder, @@ -2139,324 +2401,239 @@ fn parse_function( ); state.push1(result.try_as_basic_value().left().unwrap()); } - op @ _ => { + _ => { unimplemented!("{:?}", op); } } + + Ok(()) } - let results = state.popn_save(func_sig.returns().len())?; + fn finalize(&mut self) -> Result<(), CodegenError> { + let results = self.state.popn_save(self.func_sig.returns().len())?; - match results.as_slice() { - [] => { - builder.build_return(None); - } - [one_value] => { - builder.build_return(Some(one_value)); - } - _ => { - // let struct_ty = llvm_sig.get_return_type().as_struct_type(); - // let ret_struct = struct_ty.const_zero(); - unimplemented!("multi-value returns not yet implemented") + match results.as_slice() { + [] => { + self.builder.as_ref().unwrap().build_return(None); + } + [one_value] => { + self.builder.as_ref().unwrap().build_return(Some(one_value)); + } + _ => unimplemented!("multi-value returns not yet implemented"), } + Ok(()) } - - Ok(()) } -fn trap_if_not_representatable_as_int( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - lower_bounds: f64, - upper_bound: f64, - value: FloatValue, -) { - enum FloatSize { - Bits32, - Bits64, +impl From for CodegenError { + fn from(other: BinaryReaderError) -> CodegenError { + CodegenError { + message: format!("{:?}", other), + } } +} - let failure_block = context.append_basic_block(function, "conversion_failure_block"); - let continue_block = context.append_basic_block(function, "conversion_success_block"); +impl ModuleCodeGenerator + for LLVMModuleCodeGenerator +{ + fn new() -> LLVMModuleCodeGenerator { + let context = Context::create(); + let module = context.create_module("module"); + let builder = context.create_builder(); - let float_ty = value.get_type(); - let (int_ty, float_ptr_ty, float_size) = if float_ty == intrinsics.f32_ty { - (intrinsics.i32_ty, intrinsics.f32_ptr_ty, FloatSize::Bits32) - } else if float_ty == intrinsics.f64_ty { - (intrinsics.i64_ty, intrinsics.f64_ptr_ty, FloatSize::Bits64) - } else { - unreachable!() - }; + let intrinsics = Intrinsics::declare(&module, &context); - let (exponent, invalid_exponent) = { - let float_bits = { - let space = builder.build_alloca(int_ty, "space"); - let float_ptr = builder.build_pointer_cast(space, float_ptr_ty, "float_ptr"); - builder.build_store(float_ptr, value); - builder.build_load(space, "float_bits").into_int_value() - }; - - let (shift_amount, exponent_mask, invalid_exponent) = match float_size { - FloatSize::Bits32 => (23, 0b01111111100000000000000000000000, 0b11111111), - FloatSize::Bits64 => ( - 52, - 0b0111111111110000000000000000000000000000000000000000000000000000, - 0b11111111111, - ), - }; - - builder.build_and( - float_bits, - int_ty.const_int(exponent_mask, false), - "masked_bits", + let personality_func = module.add_function( + "__gxx_personality_v0", + intrinsics.i32_ty.fn_type(&[], false), + Some(Linkage::External), ); - ( - builder.build_right_shift( - float_bits, - int_ty.const_int(shift_amount, false), - false, - "exponent", - ), - invalid_exponent, - ) - }; + let signatures = Map::new(); - let is_invalid_float = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - exponent, - int_ty.const_int(invalid_exponent, false), - "is_not_normal", - ), - builder.build_or( - builder.build_float_compare( - FloatPredicate::ULT, - value, - float_ty.const_float(lower_bounds), - "less_than_lower_bounds", - ), - builder.build_float_compare( - FloatPredicate::UGT, - value, - float_ty.const_float(upper_bound), - "greater_than_upper_bounds", - ), - "float_not_in_bounds", - ), - "is_invalid_float", - ); - - let is_invalid_float = builder - .build_call( - intrinsics.expect_i1, - &[ - is_invalid_float.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "is_invalid_float_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - builder.build_conditional_branch(is_invalid_float, &failure_block, &continue_block); - builder.position_at_end(&failure_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&continue_block); -} - -fn trap_if_zero_or_overflow( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - left: IntValue, - right: IntValue, -) { - let int_type = left.get_type(); - - let (min_value, neg_one_value) = if int_type == intrinsics.i32_ty { - let min_value = int_type.const_int(i32::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i32 as u32 as u64, false); - (min_value, neg_one_value) - } else if int_type == intrinsics.i64_ty { - let min_value = int_type.const_int(i64::min_value() as u64, false); - let neg_one_value = int_type.const_int(-1i64 as u64, false); - (min_value, neg_one_value) - } else { - unreachable!() - }; - - let should_trap = builder.build_or( - builder.build_int_compare( - IntPredicate::EQ, - right, - int_type.const_int(0, false), - "divisor_is_zero", - ), - builder.build_and( - builder.build_int_compare(IntPredicate::EQ, left, min_value, "left_is_min"), - builder.build_int_compare(IntPredicate::EQ, right, neg_one_value, "right_is_neg_one"), - "div_will_overflow", - ), - "div_should_trap", - ); - - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} - -fn trap_if_zero( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - value: IntValue, -) { - let int_type = value.get_type(); - let should_trap = builder.build_int_compare( - IntPredicate::EQ, - value, - int_type.const_int(0, false), - "divisor_is_zero", - ); - - let should_trap = builder - .build_call( - intrinsics.expect_i1, - &[ - should_trap.as_basic_value_enum(), - intrinsics.i1_ty.const_int(0, false).as_basic_value_enum(), - ], - "should_trap_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - let shouldnt_trap_block = context.append_basic_block(function, "shouldnt_trap_block"); - let should_trap_block = context.append_basic_block(function, "should_trap_block"); - builder.build_conditional_branch(should_trap, &should_trap_block, &shouldnt_trap_block); - builder.position_at_end(&should_trap_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_illegal_arithmetic], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&shouldnt_trap_block); -} - -fn resolve_memory_ptr( - builder: &Builder, - intrinsics: &Intrinsics, - context: &Context, - function: &FunctionValue, - state: &mut State, - ctx: &mut CtxType, - memarg: MemoryImmediate, - ptr_ty: PointerType, -) -> Result { - // Ignore alignment hint for the time being. - let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); - let var_offset_i32 = state.pop1()?.into_int_value(); - let var_offset = - builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); - let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); - let memory_cache = ctx.memory(MemoryIndex::new(0)); - - let mem_base_int = match memory_cache { - MemoryCache::Dynamic { - ptr_to_base_ptr, - ptr_to_bounds, - } => { - let base = builder - .build_load(ptr_to_base_ptr, "base") - .into_pointer_value(); - let bounds = builder.build_load(ptr_to_bounds, "bounds").into_int_value(); - - let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); - - let base_in_bounds = builder.build_int_compare( - IntPredicate::ULT, - effective_offset, - bounds, - "base_in_bounds", - ); - - let base_in_bounds = builder - .build_call( - intrinsics.expect_i1, - &[ - base_in_bounds.as_basic_value_enum(), - intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(), - ], - "base_in_bounds_expect", - ) - .try_as_basic_value() - .left() - .unwrap() - .into_int_value(); - - let in_bounds_continue_block = - context.append_basic_block(function, "in_bounds_continue_block"); - let not_in_bounds_block = context.append_basic_block(function, "not_in_bounds_block"); - builder.build_conditional_branch( - base_in_bounds, - &in_bounds_continue_block, - ¬_in_bounds_block, - ); - builder.position_at_end(¬_in_bounds_block); - builder.build_call( - intrinsics.throw_trap, - &[intrinsics.trap_memory_oob], - "throw", - ); - builder.build_unreachable(); - builder.position_at_end(&in_bounds_continue_block); - - base_as_int + LLVMModuleCodeGenerator { + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + module, + functions: vec![], + signatures, + signatures_raw: Map::new(), + function_signatures: None, + func_import_count: 0, + personality_func, } - MemoryCache::Static { - base_ptr, - bounds: _, - } => builder.build_ptr_to_int(base_ptr, intrinsics.i64_ty, "base_as_int"), - }; + } - let effective_address_int = - builder.build_int_add(mem_base_int, effective_offset, &state.var_name()); - Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name())) + fn backend_id() -> Backend { + Backend::LLVM + } + + fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + Ok(()) + } + + fn next_function(&mut self) -> Result<&mut LLVMFunctionCodeGenerator, CodegenError> { + // Creates a new function and returns the function-scope code generator for it. + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + + let sig_id = self.function_signatures.as_ref().unwrap() + [FuncIndex::new(self.func_import_count + self.functions.len())]; + let func_sig = self.signatures_raw[sig_id].clone(); + + let function = self.module.add_function( + &format!("fn{}", self.func_import_count + self.functions.len()), + self.signatures[sig_id], + Some(Linkage::External), + ); + function.set_personality_function(self.personality_func); + + let mut state = State::new(); + let entry_block = context.append_basic_block(&function, "entry"); + + let return_block = context.append_basic_block(&function, "return"); + builder.position_at_end(&return_block); + + let phis: SmallVec<[PhiValue; 1]> = func_sig + .returns() + .iter() + .map(|&wasmer_ty| type_to_llvm(&intrinsics, wasmer_ty)) + .map(|ty| builder.build_phi(ty, &state.var_name())) + .collect(); + + state.push_block(return_block, phis); + builder.position_at_end(&entry_block); + + let mut locals = Vec::new(); + locals.extend( + function + .get_param_iter() + .skip(1) + .enumerate() + .map(|(index, param)| { + let ty = param.get_type(); + + let alloca = builder.build_alloca(ty, &format!("local{}", index)); + builder.build_store(alloca, param); + alloca + }), + ); + let num_params = locals.len(); + + let code = LLVMFunctionCodeGenerator { + state, + context: Some(context), + builder: Some(builder), + intrinsics: Some(intrinsics), + function, + func_sig: func_sig, + locals, + signatures: self.signatures.clone(), + num_params, + ctx: None, + unreachable_depth: 0, + }; + self.functions.push(code); + Ok(self.functions.last_mut().unwrap()) + } + + fn finalize( + mut self, + module_info: &ModuleInfo, + ) -> Result<(LLVMBackend, Box), CodegenError> { + let (context, builder, intrinsics) = match self.functions.last_mut() { + Some(x) => ( + x.context.take().unwrap(), + x.builder.take().unwrap(), + x.intrinsics.take().unwrap(), + ), + None => ( + self.context.take().unwrap(), + self.builder.take().unwrap(), + self.intrinsics.take().unwrap(), + ), + }; + self.context = Some(context); + self.builder = Some(builder); + self.intrinsics = Some(intrinsics); + + generate_trampolines( + module_info, + &self.signatures, + &self.module, + self.context.as_ref().unwrap(), + self.builder.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + ); + + let pass_manager = PassManager::create_for_module(); + if cfg!(test) { + pass_manager.add_verifier_pass(); + } + pass_manager.add_function_inlining_pass(); + pass_manager.add_promote_memory_to_register_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_aggressive_inst_combiner_pass(); + pass_manager.add_merged_load_store_motion_pass(); + pass_manager.add_new_gvn_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.run_on_module(&self.module); + + // self.module.print_to_stderr(); + + let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap()); + Ok((backend, Box::new(cache_gen))) + } + + fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { + self.signatures = signatures + .iter() + .map(|(_, sig)| { + func_sig_to_llvm( + self.context.as_ref().unwrap(), + self.intrinsics.as_ref().unwrap(), + sig, + ) + }) + .collect(); + self.signatures_raw = signatures.clone(); + Ok(()) + } + + fn feed_function_signatures( + &mut self, + assoc: Map, + ) -> Result<(), CodegenError> { + self.function_signatures = Some(Arc::new(assoc)); + Ok(()) + } + + fn feed_import_function(&mut self) -> Result<(), CodegenError> { + self.func_import_count += 1; + Ok(()) + } + + unsafe fn from_cache(artifact: Artifact, _: Token) -> Result { + let (info, _, memory) = artifact.consume(); + let (backend, cache_gen) = + LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; + + Ok(ModuleInner { + runnable_module: Box::new(backend), + cache_gen: Box::new(cache_gen), + + info, + }) + } } diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index 1b367f5d4..6250fd4fd 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -369,31 +369,6 @@ impl Intrinsics { ctx_ptr_ty, } } - - pub fn ctx<'a>( - &'a self, - info: &'a ModuleInfo, - builder: &'a Builder, - func_value: &'a FunctionValue, - cache_builder: Builder, - ) -> CtxType<'a> { - CtxType { - ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), - - builder, - intrinsics: self, - info, - cache_builder, - - cached_memories: HashMap::new(), - cached_tables: HashMap::new(), - cached_sigindices: HashMap::new(), - cached_globals: HashMap::new(), - cached_imported_functions: HashMap::new(), - - _phantom: PhantomData, - } - } } #[derive(Clone, Copy)] @@ -429,8 +404,6 @@ struct ImportedFuncCache { pub struct CtxType<'a> { ctx_ptr_value: PointerValue, - builder: &'a Builder, - intrinsics: &'a Intrinsics, info: &'a ModuleInfo, cache_builder: Builder, @@ -444,16 +417,36 @@ pub struct CtxType<'a> { } impl<'a> CtxType<'a> { + pub fn new( + info: &'a ModuleInfo, + func_value: &'a FunctionValue, + cache_builder: Builder, + ) -> CtxType<'a> { + CtxType { + ctx_ptr_value: func_value.get_nth_param(0).unwrap().into_pointer_value(), + + info, + cache_builder, + + cached_memories: HashMap::new(), + cached_tables: HashMap::new(), + cached_sigindices: HashMap::new(), + cached_globals: HashMap::new(), + cached_imported_functions: HashMap::new(), + + _phantom: PhantomData, + } + } + pub fn basic(&self) -> BasicValueEnum { self.ctx_ptr_value.as_basic_value_enum() } - pub fn memory(&mut self, index: MemoryIndex) -> MemoryCache { - let (cached_memories, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn memory(&mut self, index: MemoryIndex, intrinsics: &Intrinsics) -> MemoryCache { + let (cached_memories, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_memories, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -514,13 +507,16 @@ impl<'a> CtxType<'a> { }) } - pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) { - let (cached_tables, builder, info, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn table( + &mut self, + index: TableIndex, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> (PointerValue, IntValue) { + let (cached_tables, info, ctx_ptr_value, cache_builder) = ( &mut self.cached_tables, - self.builder, self.info, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -575,41 +571,39 @@ impl<'a> CtxType<'a> { ) } - pub fn local_func(&mut self, index: LocalFuncIndex, fn_ty: FunctionType) -> PointerValue { - let local_func_array_ptr_ptr = unsafe { - self.builder - .build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") - }; - let local_func_array_ptr = self - .builder + pub fn local_func( + &mut self, + index: LocalFuncIndex, + fn_ty: FunctionType, + intrinsics: &Intrinsics, + builder: &Builder, + ) -> PointerValue { + let local_func_array_ptr_ptr = + unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") }; + let local_func_array_ptr = builder .build_load(local_func_array_ptr_ptr, "local_func_array_ptr") .into_pointer_value(); let local_func_ptr_ptr = unsafe { - self.builder.build_in_bounds_gep( + builder.build_in_bounds_gep( local_func_array_ptr, - &[self - .intrinsics - .i32_ty - .const_int(index.index() as u64, false)], + &[intrinsics.i32_ty.const_int(index.index() as u64, false)], "local_func_ptr_ptr", ) }; - let local_func_ptr = self - .builder + let local_func_ptr = builder .build_load(local_func_ptr_ptr, "local_func_ptr") .into_pointer_value(); - self.builder.build_pointer_cast( + builder.build_pointer_cast( local_func_ptr, fn_ty.ptr_type(AddressSpace::Generic), "local_func_ptr", ) } - pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue { - let (cached_sigindices, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn dynamic_sigindex(&mut self, index: SigIndex, intrinsics: &Intrinsics) -> IntValue { + let (cached_sigindices, ctx_ptr_value, cache_builder) = ( &mut self.cached_sigindices, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); @@ -636,12 +630,11 @@ impl<'a> CtxType<'a> { }) } - pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache { - let (cached_globals, ctx_ptr_value, info, intrinsics, cache_builder) = ( + pub fn global_cache(&mut self, index: GlobalIndex, intrinsics: &Intrinsics) -> GlobalCache { + let (cached_globals, ctx_ptr_value, info, cache_builder) = ( &mut self.cached_globals, self.ctx_ptr_value, self.info, - self.intrinsics, &self.cache_builder, ); @@ -712,11 +705,14 @@ impl<'a> CtxType<'a> { }) } - pub fn imported_func(&mut self, index: ImportedFuncIndex) -> (PointerValue, PointerValue) { - let (cached_imported_functions, ctx_ptr_value, intrinsics, cache_builder) = ( + pub fn imported_func( + &mut self, + index: ImportedFuncIndex, + intrinsics: &Intrinsics, + ) -> (PointerValue, PointerValue) { + let (cached_imported_functions, ctx_ptr_value, cache_builder) = ( &mut self.cached_imported_functions, self.ctx_ptr_value, - self.intrinsics, &self.cache_builder, ); diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 6ce8139c3..6d8816af8 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -1,13 +1,6 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![cfg_attr(nightly, feature(unwind_attributes))] -use wasmer_runtime_core::{ - backend::{Compiler, CompilerConfig, Token}, - cache::{Artifact, Error as CacheError}, - error::CompileError, - module::ModuleInner, -}; -use wasmparser::{self, WasmDecoder}; - mod backend; mod code; mod intrinsics; @@ -16,111 +9,11 @@ mod read_info; mod state; mod trampolines; -pub struct LLVMCompiler { - _private: (), -} +use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen; -impl LLVMCompiler { - pub fn new() -> Self { - Self { _private: () } - } -} - -impl Compiler for LLVMCompiler { - fn compile( - &self, - wasm: &[u8], - compiler_config: CompilerConfig, - _: Token, - ) -> Result { - validate(wasm)?; - - let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, cache_gen) = backend::LLVMBackend::new(module, intrinsics); - - Ok(ModuleInner { - runnable_module: Box::new(backend), - cache_gen: Box::new(cache_gen), - - info, - }) - } - - unsafe fn from_cache(&self, artifact: Artifact, _: Token) -> Result { - let (info, _, memory) = artifact.consume(); - let (backend, cache_gen) = - backend::LLVMBackend::from_buffer(memory).map_err(CacheError::DeserializeError)?; - - Ok(ModuleInner { - runnable_module: Box::new(backend), - cache_gen: Box::new(cache_gen), - - info, - }) - } -} - -fn validate(bytes: &[u8]) -> Result<(), CompileError> { - let mut parser = wasmparser::ValidatingParser::new( - bytes, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: false, - enable_reference_types: false, - enable_simd: false, - enable_bulk_memory: false, - }, - mutable_global_imports: false, - }), - ); - - loop { - let state = parser.read(); - match *state { - wasmparser::ParserState::EndWasm => break Ok(()), - wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError { - msg: err.message.to_string(), - })?, - _ => {} - } - } -} - -#[test] -fn test_read_module() { - use std::mem::transmute; - use wabt::wat2wasm; - use wasmer_runtime_core::{ - backend::RunnableModule, structures::TypedIndex, types::LocalFuncIndex, vm, - }; - // let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8]; - let wat = r#" - (module - (type $t0 (func (param i32) (result i32))) - (type $t1 (func (result i32))) - (memory 1) - (global $g0 (mut i32) (i32.const 0)) - (func $foo (type $t0) (param i32) (result i32) - get_local 0 - )) - "#; - let wasm = wat2wasm(wat).unwrap(); - - let (info, code_reader) = read_info::read_module(&wasm, Default::default()).unwrap(); - - let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - - let (backend, _) = backend::LLVMBackend::new(module, intrinsics); - - let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); - - println!("func_ptr: {:p}", func_ptr.as_ptr()); - - unsafe { - let func: unsafe extern "C" fn(*mut vm::Ctx, i32) -> i32 = transmute(func_ptr); - let result = func(0 as _, 42); - println!("result: {}", result); - } -} +pub type LLVMCompiler = SimpleStreamingCompilerGen< + code::LLVMModuleCodeGenerator, + code::LLVMFunctionCodeGenerator, + backend::LLVMBackend, + code::CodegenError, +>; diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs index 12894f6bc..d16384179 100644 --- a/lib/llvm-backend/src/platform/unix.rs +++ b/lib/llvm-backend/src/platform/unix.rs @@ -34,7 +34,7 @@ pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut } #[cfg(not(target_os = "macos"))] -pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { +pub unsafe fn visit_fde(addr: *mut u8, _size: usize, visitor: extern "C" fn(*mut u8)) { visitor(addr); } diff --git a/lib/llvm-backend/src/read_info.rs b/lib/llvm-backend/src/read_info.rs index 5ebfa5868..572469ce2 100644 --- a/lib/llvm-backend/src/read_info.rs +++ b/lib/llvm-backend/src/read_info.rs @@ -1,289 +1,5 @@ -use wasmer_runtime_core::{ - backend::{Backend, CompilerConfig}, - module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, - }, - structures::{Map, TypedIndex}, - types::{ - ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, - ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, - TableIndex, Type, Value, - }, - units::Pages, -}; -use wasmparser::{ - BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, - ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, - SectionCode, Type as WpType, -}; - -use hashbrown::HashMap; - -pub fn read_module( - wasm: &[u8], - compiler_config: CompilerConfig, -) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { - let mut info = ModuleInfo { - memories: Map::new(), - globals: Map::new(), - tables: Map::new(), - - imported_functions: Map::new(), - imported_memories: Map::new(), - imported_tables: Map::new(), - imported_globals: Map::new(), - - exports: Default::default(), - - data_initializers: Vec::new(), - elem_initializers: Vec::new(), - - start_func: None, - - func_assoc: Map::new(), - signatures: Map::new(), - backend: Backend::LLVM, - - namespace_table: StringTable::new(), - name_table: StringTable::new(), - - em_symbol_map: compiler_config.symbol_map.clone(), - - custom_sections: HashMap::new(), - }; - - let mut reader = ModuleReader::new(wasm)?; - let mut code_reader = None; - - loop { - if reader.eof() { - return Ok((info, code_reader.unwrap())); - } - - let section = reader.read()?; - - match section.code { - SectionCode::Type => { - let type_reader = section.get_type_section_reader()?; - - for ty in type_reader { - let ty = ty?; - info.signatures.push(func_type_to_func_sig(ty)?); - } - } - SectionCode::Import => { - let import_reader = section.get_import_section_reader()?; - let mut namespace_builder = StringTableBuilder::new(); - let mut name_builder = StringTableBuilder::new(); - - for import in import_reader { - let Import { module, field, ty } = import?; - - let namespace_index = namespace_builder.register(module); - let name_index = name_builder.register(field); - let import_name = ImportName { - namespace_index, - name_index, - }; - - match ty { - ImportSectionEntryType::Function(sigindex) => { - let sigindex = SigIndex::new(sigindex as usize); - info.imported_functions.push(import_name); - info.func_assoc.push(sigindex); - } - ImportSectionEntryType::Table(table_ty) => { - assert_eq!(table_ty.element_type, WpType::AnyFunc); - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.imported_tables.push((import_name, table_desc)); - } - ImportSectionEntryType::Memory(memory_ty) => { - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - info.imported_memories.push((import_name, mem_desc)); - } - ImportSectionEntryType::Global(global_ty) => { - let global_desc = GlobalDescriptor { - mutable: global_ty.mutable, - ty: type_to_type(global_ty.content_type)?, - }; - info.imported_globals.push((import_name, global_desc)); - } - } - } - - info.namespace_table = namespace_builder.finish(); - info.name_table = name_builder.finish(); - } - SectionCode::Function => { - let func_decl_reader = section.get_function_section_reader()?; - - for sigindex in func_decl_reader { - let sigindex = sigindex?; - - let sigindex = SigIndex::new(sigindex as usize); - info.func_assoc.push(sigindex); - } - } - SectionCode::Table => { - let table_decl_reader = section.get_table_section_reader()?; - - for table_ty in table_decl_reader { - let table_ty = table_ty?; - - let table_desc = TableDescriptor { - element: ElementType::Anyfunc, - minimum: table_ty.limits.initial, - maximum: table_ty.limits.maximum, - }; - - info.tables.push(table_desc); - } - } - SectionCode::Memory => { - let mem_decl_reader = section.get_memory_section_reader()?; - - for memory_ty in mem_decl_reader { - let memory_ty = memory_ty?; - - let mem_desc = MemoryDescriptor { - minimum: Pages(memory_ty.limits.initial), - maximum: memory_ty.limits.maximum.map(|max| Pages(max)), - shared: memory_ty.shared, - }; - - info.memories.push(mem_desc); - } - } - SectionCode::Global => { - let global_decl_reader = section.get_global_section_reader()?; - - for global in global_decl_reader { - let global = global?; - - let desc = GlobalDescriptor { - mutable: global.ty.mutable, - ty: type_to_type(global.ty.content_type)?, - }; - - let global_init = GlobalInit { - desc, - init: eval_init_expr(&global.init_expr)?, - }; - - info.globals.push(global_init); - } - } - SectionCode::Export => { - let export_reader = section.get_export_section_reader()?; - - for export in export_reader { - let Export { field, kind, index } = export?; - - let export_index = match kind { - ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), - ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), - ExternalKind::Memory => { - ExportIndex::Memory(MemoryIndex::new(index as usize)) - } - ExternalKind::Global => { - ExportIndex::Global(GlobalIndex::new(index as usize)) - } - }; - - info.exports.insert(field.to_string(), export_index); - } - } - SectionCode::Start => { - let start_index = section.get_start_section_content()?; - - info.start_func = Some(FuncIndex::new(start_index as usize)); - } - SectionCode::Element => { - let element_reader = section.get_element_section_reader()?; - - for element in element_reader { - let Element { kind, items } = element?; - - match kind { - ElementKind::Active { - table_index, - init_expr, - } => { - let table_index = TableIndex::new(table_index as usize); - let base = eval_init_expr(&init_expr)?; - let items_reader = items.get_items_reader()?; - - let elements: Vec<_> = items_reader - .into_iter() - .map(|res| res.map(|index| FuncIndex::new(index as usize))) - .collect::>()?; - - let table_init = TableInitializer { - table_index, - base, - elements, - }; - - info.elem_initializers.push(table_init); - } - ElementKind::Passive(_ty) => { - return Err(BinaryReaderError { - message: "passive tables are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::Code => { - code_reader = Some(section.get_code_section_reader()?); - } - SectionCode::Data => { - let data_reader = section.get_data_section_reader()?; - - for data in data_reader { - let Data { kind, data } = data?; - - match kind { - DataKind::Active { - memory_index, - init_expr, - } => { - let memory_index = MemoryIndex::new(memory_index as usize); - let base = eval_init_expr(&init_expr)?; - - let data_init = DataInitializer { - memory_index, - base, - data: data.to_vec(), - }; - - info.data_initializers.push(data_init); - } - DataKind::Passive => { - return Err(BinaryReaderError { - message: "passive memories are not yet supported", - offset: -1isize as usize, - }); - } - } - } - } - SectionCode::DataCount => {} - SectionCode::Custom { .. } => {} - } - } -} +use wasmer_runtime_core::types::Type; +use wasmparser::{BinaryReaderError, Type as WpType}; pub fn type_to_type(ty: WpType) -> Result { Ok(match ty { @@ -305,46 +21,3 @@ pub fn type_to_type(ty: WpType) -> Result { } }) } - -fn func_type_to_func_sig(func_ty: FuncType) -> Result { - assert_eq!(func_ty.form, WpType::Func); - - Ok(FuncSig::new( - func_ty - .params - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - func_ty - .returns - .iter() - .cloned() - .map(type_to_type) - .collect::, _>>()?, - )) -} - -fn eval_init_expr(expr: &InitExpr) -> Result { - let mut reader = expr.get_operators_reader(); - let (op, offset) = reader.read_with_offset()?; - Ok(match op { - Operator::GetGlobal { global_index } => { - Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) - } - Operator::I32Const { value } => Initializer::Const(Value::I32(value)), - Operator::I64Const { value } => Initializer::Const(Value::I64(value)), - Operator::F32Const { value } => { - Initializer::Const(Value::F32(f32::from_bits(value.bits()))) - } - Operator::F64Const { value } => { - Initializer::Const(Value::F64(f64::from_bits(value.bits()))) - } - _ => { - return Err(BinaryReaderError { - message: "init expr evaluation failed: unsupported opcode", - offset, - }); - } - }) -} diff --git a/lib/middleware-common/Cargo.toml b/lib/middleware-common/Cargo.toml index 9abb4041a..57a42577c 100644 --- a/lib/middleware-common/Cargo.toml +++ b/lib/middleware-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-middleware-common" -version = "0.3.0" +version = "0.4.1" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime common middlewares" license = "MIT" @@ -8,4 +8,4 @@ authors = ["The Wasmer Engineering Team "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs index 3f856eacd..9c47d7d7b 100644 --- a/lib/middleware-common/src/call_trace.rs +++ b/lib/middleware-common/src/call_trace.rs @@ -10,7 +10,7 @@ impl FunctionMiddleware for CallTrace { fn feed_event<'a, 'b: 'a>( &mut self, op: Event<'a, 'b>, - module_info: &ModuleInfo, + _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, ) -> Result<(), Self::Error> { match op { diff --git a/lib/middleware-common/src/lib.rs b/lib/middleware-common/src/lib.rs index 10c8dfd37..e09d9ee03 100644 --- a/lib/middleware-common/src/lib.rs +++ b/lib/middleware-common/src/lib.rs @@ -1 +1,3 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + pub mod call_trace; diff --git a/lib/runtime-abi/Cargo.toml b/lib/runtime-abi/Cargo.toml index bf971f7e8..f8b540e4b 100644 --- a/lib/runtime-abi/Cargo.toml +++ b/lib/runtime-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-abi" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime-abi/src/lib.rs b/lib/runtime-abi/src/lib.rs index 2f0ee8c51..d1c9e2326 100644 --- a/lib/runtime-abi/src/lib.rs +++ b/lib/runtime-abi/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[cfg(not(target_os = "windows"))] #[macro_use] extern crate failure; diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 881c37fcb..de93c42a5 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-c-api" -version = "0.3.0" +version = "0.4.1" description = "Wasmer C API library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -16,11 +16,11 @@ libc = "0.2" [dependencies.wasmer-runtime] path = "../runtime" -version = "0.3.0" +version = "0.4.1" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.3.0" +version = "0.4.1" [features] debug = ["wasmer-runtime/debug"] diff --git a/lib/runtime-c-api/src/error.rs b/lib/runtime-c-api/src/error.rs index 5980c79a9..1a9322df8 100644 --- a/lib/runtime-c-api/src/error.rs +++ b/lib/runtime-c-api/src/error.rs @@ -1,4 +1,4 @@ -//! Errors. +//! Read runtime errors. use libc::{c_char, c_int}; use std::cell::RefCell; @@ -61,20 +61,20 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: return -1; } - let last_error = match take_last_error() { - Some(err) => err, + let error_message = match take_last_error() { + Some(err) => err.to_string(), None => return 0, }; - let error_message = last_error.to_string(); + let length = length as usize; - let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize); - - if error_message.len() >= buffer.len() { - // buffer to small for err message + if error_message.len() >= length { + // buffer is too small to hold the error message return -1; } + let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length); + ptr::copy_nonoverlapping( error_message.as_ptr(), buffer.as_mut_ptr(), @@ -85,7 +85,7 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: // accidentally read into garbage. buffer[error_message.len()] = 0; - error_message.len() as c_int + error_message.len() as c_int + 1 } #[derive(Debug)] diff --git a/lib/runtime-c-api/src/export.rs b/lib/runtime-c-api/src/export.rs index 6bcae32f2..7a7bbd47b 100644 --- a/lib/runtime-c-api/src/export.rs +++ b/lib/runtime-c-api/src/export.rs @@ -1,4 +1,5 @@ -//! Wasm exports. +//! Create, read, destroy export definitions (function, global, memory +//! and table) on an instance. use crate::{ error::{update_last_error, CApiError}, diff --git a/lib/runtime-c-api/src/global.rs b/lib/runtime-c-api/src/global.rs index 2e4ea645f..dc3910330 100644 --- a/lib/runtime-c-api/src/global.rs +++ b/lib/runtime-c-api/src/global.rs @@ -1,4 +1,4 @@ -//! Wasm global. +//! Create, set, get and destroy global variables of an instance. use crate::value::{wasmer_value_t, wasmer_value_tag}; use wasmer_runtime::Global; diff --git a/lib/runtime-c-api/src/import.rs b/lib/runtime-c-api/src/import.rs index cc3598078..a22b7f245 100644 --- a/lib/runtime-c-api/src/import.rs +++ b/lib/runtime-c-api/src/import.rs @@ -1,4 +1,5 @@ -//! Wasm imports. +//! Create, read, destroy import definitions (function, global, memory +//! and table) on an instance. use crate::{ error::{update_last_error, CApiError}, diff --git a/lib/runtime-c-api/src/instance.rs b/lib/runtime-c-api/src/instance.rs index a9d7d5555..87972060d 100644 --- a/lib/runtime-c-api/src/instance.rs +++ b/lib/runtime-c-api/src/instance.rs @@ -1,4 +1,4 @@ -//! Wasm instance. +//! Instantiate a module, call functions, and read exports. use crate::{ error::{update_last_error, CApiError}, diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index aa6455f60..054cd14e1 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -1,3 +1,87 @@ +//! # Wasmer Runtime C API +//! +//! Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully +//! compatible with Emscripten, Rust and Go. [Learn +//! more](https://github.com/wasmerio/wasmer). +//! +//! This crate exposes a C and C++ API for the Wasmer runtime. +//! +//! # Usage +//! +//! The C and C++ header files can be found in the source tree of this +//! crate, respectively [`wasmer.h`][wasmer_h] and +//! [`wasmer.hh`][wasmer_hh]. They are automatically generated, and always +//! up-to-date in this repository. +//! +//! Here is a simple example to use the C API: +//! +//! ```c +//! #include +//! #include "wasmer.h" +//! #include +//! #include +//! +//! int main() +//! { +//! // Read the Wasm file bytes. +//! FILE *file = fopen("sum.wasm", "r"); +//! fseek(file, 0, SEEK_END); +//! long len = ftell(file); +//! uint8_t *bytes = malloc(len); +//! fseek(file, 0, SEEK_SET); +//! fread(bytes, 1, len, file); +//! fclose(file); +//! +//! // Prepare the imports. +//! wasmer_import_t imports[] = {}; +//! +//! // Instantiate! +//! wasmer_instance_t *instance = NULL; +//! wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0); +//! +//! assert(instantiation_result == WASMER_OK); +//! +//! // Let's call a function. +//! // Start by preparing the arguments. +//! +//! // Value of argument #1 is `7i32`. +//! wasmer_value_t argument_one; +//! argument_one.tag = WASM_I32; +//! argument_one.value.I32 = 7; +//! +//! // Value of argument #2 is `8i32`. +//! wasmer_value_t argument_two; +//! argument_two.tag = WASM_I32; +//! argument_two.value.I32 = 8; +//! +//! // Prepare the arguments. +//! wasmer_value_t arguments[] = {argument_one, argument_two}; +//! +//! // Prepare the return value. +//! wasmer_value_t result_one; +//! wasmer_value_t results[] = {result_one}; +//! +//! // Call the `sum` function with the prepared arguments and the return value. +//! wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1); +//! +//! // Let's display the result. +//! printf("Call result: %d\n", call_result); +//! printf("Result: %d\n", results[0].value.I32); +//! +//! // `sum(7, 8) == 15`. +//! assert(results[0].value.I32 == 15); +//! assert(call_result == WASMER_OK); +//! +//! wasmer_instance_destroy(instance); +//! +//! return 0; +//! } +//! ``` +//! +//! [wasmer_h]: ./wasmer.h +//! [wasmer_hh]: ./wasmer.hh +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + extern crate wasmer_runtime; extern crate wasmer_runtime_core; diff --git a/lib/runtime-c-api/src/memory.rs b/lib/runtime-c-api/src/memory.rs index f90965586..5927bee5e 100644 --- a/lib/runtime-c-api/src/memory.rs +++ b/lib/runtime-c-api/src/memory.rs @@ -1,4 +1,4 @@ -//! Wasm memory.o +//! Create, read, write, grow, destroy memory of an instance. use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; use libc::{uint32_t, uint8_t}; diff --git a/lib/runtime-c-api/src/module.rs b/lib/runtime-c-api/src/module.rs index 42d9815a6..62df2948f 100644 --- a/lib/runtime-c-api/src/module.rs +++ b/lib/runtime-c-api/src/module.rs @@ -1,4 +1,4 @@ -//! Wasm module. +//! Compile, validate, instantiate, serialize, and destroy modules. use crate::{ error::{update_last_error, CApiError}, diff --git a/lib/runtime-c-api/src/table.rs b/lib/runtime-c-api/src/table.rs index 84d3b794f..cd382912f 100644 --- a/lib/runtime-c-api/src/table.rs +++ b/lib/runtime-c-api/src/table.rs @@ -1,4 +1,4 @@ -//! Wasm tables. +//! Create, grow, destroy tables of an instance. use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t}; use libc::uint32_t; diff --git a/lib/runtime-c-api/src/value.rs b/lib/runtime-c-api/src/value.rs index c0a3e64d8..e5547bfa6 100644 --- a/lib/runtime-c-api/src/value.rs +++ b/lib/runtime-c-api/src/value.rs @@ -1,4 +1,4 @@ -//! Wasm values. +//! Create and map Rust to WebAssembly values. use libc::{int32_t, int64_t}; use wasmer_runtime::Value; diff --git a/lib/runtime-c-api/tests/test-instantiate.c b/lib/runtime-c-api/tests/test-instantiate.c index 20332623e..8a7c2610e 100644 --- a/lib/runtime-c-api/tests/test-instantiate.c +++ b/lib/runtime-c-api/tests/test-instantiate.c @@ -46,7 +46,8 @@ int main() int error_len = wasmer_last_error_length(); printf("Error len: `%d`\n", error_len); char *error_str = malloc(error_len); - wasmer_last_error_message(error_str, error_len); + int error_result = wasmer_last_error_message(error_str, error_len); + assert(error_len == error_result); printf("Error str: `%s`\n", error_str); assert(0 == strcmp(error_str, "Call error: Parameters of type [I32] did not match signature [I32, I32] -> [I32]")); free(error_str); diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index e311be8fb..d6b735cff 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime-core" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime core library" license = "MIT" authors = ["The Wasmer Engineering Team "] diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 23aaf2f8a..821ff379f 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -17,30 +17,31 @@ use crate::{ }; use std::slice; +/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance. +/// That is, local memories, tables, and globals (as well as some additional +/// data for the virtual call machinery). #[derive(Debug)] pub struct LocalBacking { + /// This is a map from the local resource index to actual memory, + /// table, and globals. pub(crate) memories: BoxedMap, pub(crate) tables: BoxedMap, pub(crate) globals: BoxedMap, + /// This own the memory containing the pointers to the local memories. + /// While simplifying implementation, this adds indirection and may hurt + /// performance, especially on cache-starved systems. pub(crate) vm_memories: BoxedMap, pub(crate) vm_tables: BoxedMap, pub(crate) vm_globals: BoxedMap, + /// The dynamic sigindices are used to efficiently support caching and + /// the `call_indirect` wasm instruction. This field (and local_functions + /// as well) are subject to change. pub(crate) dynamic_sigindices: BoxedMap, pub(crate) local_functions: BoxedMap, } -// impl LocalBacking { -// pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut Memory { -// &mut self.memories[local_memory_index] -// } - -// pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking { -// &mut self.tables[local_table_index] -// } -// } - impl LocalBacking { pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self { let mut memories = Self::generate_memories(module); @@ -102,6 +103,9 @@ impl LocalBacking { memories.into_boxed_map() } + /// Initialize each locally-defined memory in the Module. + /// + /// This involves copying in the data initializers. fn finalize_memories( module: &ModuleInner, imports: &ImportBacking, @@ -174,6 +178,9 @@ impl LocalBacking { tables.into_boxed_map() } + /// This initializes all of the locally-defined tables in the Module, e.g. + /// putting all the table elements (function pointers) + /// in the right places. #[allow(clippy::cast_ptr_alignment)] fn finalize_tables( module: &ModuleInner, diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 1b96a942a..2f7c2d900 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -1,18 +1,19 @@ use crate::{ backend::RunnableModule, - backend::{sys::Memory, Backend, CacheGen, Compiler, CompilerConfig, Token}, + backend::{Backend, CacheGen, Compiler, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, error::{CompileError, CompileResult}, module::{ModuleInfo, ModuleInner}, - parse::LoadError, structures::Map, types::{FuncIndex, FuncSig, SigIndex}, }; use smallvec::SmallVec; +use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; use wasmparser::{Operator, Type as WpType}; +#[derive(Debug)] pub enum Event<'a, 'b> { Internal(InternalEvent), Wasm(&'b Operator<'a>), @@ -26,6 +27,18 @@ pub enum InternalEvent { GetInternal(u32), } +impl fmt::Debug for InternalEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InternalEvent::FunctionBegin(_) => write!(f, "FunctionBegin"), + InternalEvent::FunctionEnd => write!(f, "FunctionEnd"), + InternalEvent::Breakpoint(_) => write!(f, "Breakpoint"), + InternalEvent::SetInternal(_) => write!(f, "SetInternal"), + InternalEvent::GetInternal(_) => write!(f, "GetInternal"), + } + } +} + pub struct BkptInfo {} pub trait ModuleCodeGenerator, RM: RunnableModule, E: Debug> { @@ -35,7 +48,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Creates a new function and returns the function-scope code generator for it. fn next_function(&mut self) -> Result<&mut FCG, E>; - fn finalize(self, module_info: &ModuleInfo) -> Result; + fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box), E>; fn feed_signatures(&mut self, signatures: Map) -> Result<(), E>; /// Sets function signatures. @@ -44,7 +57,8 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Adds an import function. fn feed_import_function(&mut self) -> Result<(), E>; - fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), E> { Ok(()) } + fn feed_compiler_config(&mut self, _config: &CompilerConfig) -> Result<(), E> { Ok(()) } + unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } pub struct StreamingCompiler< @@ -118,15 +132,6 @@ impl< compiler_config: CompilerConfig, _: Token, ) -> CompileResult { - struct Placeholder; - impl CacheGen for Placeholder { - fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { - Err(CacheError::Unknown( - "the streaming compiler API doesn't support caching yet".to_string(), - )) - } - } - let mut mcg = MCG::new(); let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module( @@ -136,22 +141,24 @@ impl< &mut chain, &compiler_config, )?; - let exec_context = mcg - .finalize(&info) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; + let (exec_context, cache_gen) = + mcg.finalize(&info) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; Ok(ModuleInner { - cache_gen: Box::new(Placeholder), + cache_gen, runnable_module: Box::new(exec_context), - info: info, + info, }) } - unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - Err(CacheError::Unknown( - "the streaming compiler API doesn't support caching yet".to_string(), - )) + unsafe fn from_cache( + &self, + artifact: Artifact, + token: Token, + ) -> Result { + MCG::from_cache(artifact, token) } } @@ -248,7 +255,7 @@ pub trait FunctionCodeGenerator { fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; /// Called before the first call to `feed_opcode`. - fn begin_body(&mut self) -> Result<(), E>; + fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Called for each operator. fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index fb25acdb8..c93115d14 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -11,7 +11,7 @@ pub type ResolveResult = std::result::Result; pub type ParseResult = std::result::Result; /// This is returned when the chosen compiler is unable to -/// successfully compile the provided webassembly module into +/// successfully compile the provided WebAssembly module into /// a `Module`. /// /// Comparing two `CompileError`s always evaluates to false. @@ -114,7 +114,7 @@ impl std::fmt::Display for LinkError { impl std::error::Error for LinkError {} /// This is the error type returned when calling -/// a webassembly function. +/// a WebAssembly function. /// /// The main way to do this is `Instance.call`. /// @@ -270,7 +270,7 @@ impl std::error::Error for CreationError {} /// The amalgamation of all errors that can occur /// during the compilation, instantiation, or execution -/// of a webassembly module. +/// of a WebAssembly module. /// /// Comparing two `Error`s always evaluates to false. #[derive(Debug)] diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index f87c62810..347ec37f0 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -12,7 +12,7 @@ use crate::{ typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, vm, - loader::{self, Loader, Instance as _}, + loader::Loader, structures::TypedIndex, }; use smallvec::{smallvec, SmallVec}; @@ -288,7 +288,7 @@ impl Instance { } } - /// Call an exported webassembly function given the export name. + /// Call an exported WebAssembly function given the export name. /// Pass arguments by wrapping each one in the [`Value`] enum. /// The returned values are also each wrapped in a [`Value`]. /// @@ -296,7 +296,7 @@ impl Instance { /// /// # Note: /// This returns `CallResult>` in order to support - /// the future multi-value returns webassembly feature. + /// the future multi-value returns WebAssembly feature. /// /// # Usage: /// ``` @@ -627,7 +627,7 @@ pub struct DynFunc<'a> { } impl<'a> DynFunc<'a> { - /// Call an exported webassembly function safely. + /// Call an exported WebAssembly function safely. /// /// Pass arguments by wrapping each one in the [`Value`] enum. /// The returned values are also each wrapped in a [`Value`]. @@ -636,7 +636,7 @@ impl<'a> DynFunc<'a> { /// /// # Note: /// This returns `CallResult>` in order to support - /// the future multi-value returns webassembly feature. + /// the future multi-value returns WebAssembly feature. /// /// # Usage: /// ``` diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 90517dba5..26d153d12 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![cfg_attr(nightly, feature(unwind_attributes))] #[cfg(test)] diff --git a/lib/runtime-core/src/loader.rs b/lib/runtime-core/src/loader.rs index 749508a57..0982c75f9 100644 --- a/lib/runtime-core/src/loader.rs +++ b/lib/runtime-core/src/loader.rs @@ -9,7 +9,7 @@ use crate::{ types::Value, }; use libc::{ - c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ, + mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE, }; @@ -23,11 +23,11 @@ pub trait Loader { pub trait Instance { type Error: Debug; fn call(&mut self, id: usize, args: &[Value]) -> Result; - fn read_memory(&mut self, offset: u32, len: u32) -> Result, Self::Error> { + fn read_memory(&mut self, _offset: u32, _len: u32) -> Result, Self::Error> { unimplemented!() } - fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), Self::Error> { + fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> { unimplemented!() } } @@ -38,7 +38,7 @@ impl Loader for LocalLoader { type Instance = LocalInstance; type Error = String; - fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &Ctx) -> Result { + fn load(&self, rm: &dyn RunnableModule, _module: &ModuleInfo, _ctx: &Ctx) -> Result { let code = rm.get_code().unwrap(); let mut code_mem = CodeMemory::new(code.len()); code_mem[..code.len()].copy_from_slice(code); diff --git a/lib/runtime-core/src/memory/static_/unshared.rs b/lib/runtime-core/src/memory/static_/unshared.rs index b9e66de0a..602a02bac 100644 --- a/lib/runtime-core/src/memory/static_/unshared.rs +++ b/lib/runtime-core/src/memory/static_/unshared.rs @@ -11,7 +11,7 @@ use crate::{ /// This is an internal-only api. /// /// A static memory allocates 6GB of *virtual* memory when created -/// in order to allow the webassembly module to contain no bounds-checks. +/// in order to allow the WebAssembly module to contain no bounds-checks. /// /// Additionally, static memories stay at a single virtual address, so there is no need /// to reload its address on each use. diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 479e25cf7..7fdbd095a 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -246,7 +246,7 @@ pub fn read_module< ParserState::CodeOperator(ref op) => { if !body_begun { body_begun = true; - fcg.begin_body() + fcg.begin_body(&info) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } middlewares diff --git a/lib/runtime-core/src/sig_registry.rs b/lib/runtime-core/src/sig_registry.rs index 1f6d87b4f..653294f32 100644 --- a/lib/runtime-core/src/sig_registry.rs +++ b/lib/runtime-core/src/sig_registry.rs @@ -23,10 +23,18 @@ struct GlobalSigRegistry { sig_assoc: Map>, } +/// The `SigRegistry` represents a process-global map of function signatures +/// to signature indexes and vice versa (the map goes both ways). +/// +/// This exists for two reasons: +/// 1. The `call_indirect` wasm instruction can compare two signature indices +/// to do signature validation very quickly. +/// 2. To intern function signatures, which may be expensive to create. #[derive(Debug)] pub struct SigRegistry; impl SigRegistry { + /// Map a `FuncSig` to a global `SigIndex`. pub fn lookup_sig_index(&self, func_sig: Sig) -> SigIndex where Sig: Into>, @@ -45,11 +53,15 @@ impl SigRegistry { sig_index } + /// Map a global `SigIndex` to an interned `FuncSig`. pub fn lookup_signature(&self, sig_index: SigIndex) -> Arc { let global = (*GLOBAL_SIG_REGISTRY).read(); Arc::clone(&global.sig_assoc[sig_index]) } + /// Register a function signature with the global signature registry. + /// + /// This will return an interned `FuncSig`. pub fn lookup_signature_ref(&self, func_sig: &FuncSig) -> Arc { let mut global = (*GLOBAL_SIG_REGISTRY).write(); let global = &mut *global; diff --git a/lib/runtime-core/src/sys/windows/memory.rs b/lib/runtime-core/src/sys/windows/memory.rs index d47388170..888aeff4d 100644 --- a/lib/runtime-core/src/sys/windows/memory.rs +++ b/lib/runtime-core/src/sys/windows/memory.rs @@ -33,7 +33,13 @@ impl Memory { let protect = protection.to_protect_const(); - let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) }; + let flags = if protection == Protect::None { + MEM_RESERVE + } else { + MEM_RESERVE | MEM_COMMIT + }; + + let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, flags, protect) }; if ptr.is_null() { Err("unable to allocate memory".to_string()) @@ -229,3 +235,25 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize { fn round_down_to_page_size(size: usize, page_size: usize) -> usize { size & !(page_size - 1) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn clone() { + // these should work + let _ = Memory::with_size_protect(200_000, Protect::Read) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadWrite) + .unwrap() + .clone(); + let _ = Memory::with_size_protect(200_000, Protect::ReadExec) + .unwrap() + .clone(); + + // this would cause segmentation fault as uncommited memory with no access + //let _ = Memory::with_size_protect(200_000, Protect::None).unwrap().clone(); + } +} diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 23a848738..0a9bc8c2f 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -247,7 +247,7 @@ impl WasmTypeList for (A,) { type CStruct = S1; type RetArray = [u64; 1]; fn from_ret_array(array: Self::RetArray) -> Self { - (WasmExternType::from_native(NativeWasmType::from_bits( + (WasmExternType::from_native(NativeWasmType::from_binary( array[0], )),) } @@ -274,7 +274,7 @@ impl WasmTypeList for (A,) { ctx: *mut Ctx, ) -> Result { let (a,) = self; - let args = [a.to_native().to_bits()]; + let args = [a.to_native().to_binary()]; let mut rets = Rets::empty_ret_array(); let mut trap = WasmTrapInfo::Unknown; let mut user_error = None; @@ -322,7 +322,7 @@ macro_rules! impl_traits { fn from_ret_array(array: Self::RetArray) -> Self { #[allow(non_snake_case)] let [ $( $x ),* ] = array; - ( $( WasmExternType::from_native(NativeWasmType::from_bits($x)) ),* ) + ( $( WasmExternType::from_native(NativeWasmType::from_binary($x)) ),* ) } fn empty_ret_array() -> Self::RetArray { [0; count_idents!( $( $x ),* )] @@ -344,7 +344,7 @@ macro_rules! impl_traits { unsafe fn call(self, f: NonNull, wasm: Wasm, ctx: *mut Ctx) -> Result { #[allow(unused_parens)] let ( $( $x ),* ) = self; - let args = [ $( $x.to_native().to_bits() ),* ]; + let args = [ $( $x.to_native().to_binary()),* ]; let mut rets = Rets::empty_ret_array(); let mut trap = WasmTrapInfo::Unknown; let mut user_error = None; diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 62d3235a2..0c8f08d1a 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -85,44 +85,44 @@ where Self: Sized, { const TYPE: Type; - fn from_bits(bits: u64) -> Self; - fn to_bits(self) -> u64; + fn from_binary(bits: u64) -> Self; + fn to_binary(self) -> u64; } unsafe impl NativeWasmType for i32 { const TYPE: Type = Type::I32; - fn from_bits(bits: u64) -> Self { + fn from_binary(bits: u64) -> Self { bits as _ } - fn to_bits(self) -> u64 { + fn to_binary(self) -> u64 { self as _ } } unsafe impl NativeWasmType for i64 { const TYPE: Type = Type::I64; - fn from_bits(bits: u64) -> Self { + fn from_binary(bits: u64) -> Self { bits as _ } - fn to_bits(self) -> u64 { + fn to_binary(self) -> u64 { self as _ } } unsafe impl NativeWasmType for f32 { const TYPE: Type = Type::F32; - fn from_bits(bits: u64) -> Self { - bits as _ + fn from_binary(bits: u64) -> Self { + f32::from_bits(bits as u32) } - fn to_bits(self) -> u64 { - self as _ + fn to_binary(self) -> u64 { + self.to_bits() as _ } } unsafe impl NativeWasmType for f64 { const TYPE: Type = Type::F64; - fn from_bits(bits: u64) -> Self { - bits as _ + fn from_binary(bits: u64) -> Self { + f64::from_bits(bits) } - fn to_bits(self) -> u64 { - self as _ + fn to_binary(self) -> u64 { + self.to_bits() } } @@ -410,7 +410,7 @@ pub trait LocalImport { macro_rules! define_map_index { ($ty:ident) => { #[derive(Serialize, Deserialize)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct $ty (u32); impl TypedIndex for $ty { #[doc(hidden)] @@ -525,3 +525,58 @@ where } } } + +#[cfg(test)] +mod tests { + use crate::types::NativeWasmType; + use crate::types::WasmExternType; + + #[test] + fn test_native_types_round_trip() { + assert_eq!( + 42i32, + i32::from_native(i32::from_binary((42i32).to_native().to_binary())) + ); + + assert_eq!( + -42i32, + i32::from_native(i32::from_binary((-42i32).to_native().to_binary())) + ); + + use std::i64; + let xi64 = i64::MAX; + assert_eq!( + xi64, + i64::from_native(i64::from_binary((xi64).to_native().to_binary())) + ); + let yi64 = i64::MIN; + assert_eq!( + yi64, + i64::from_native(i64::from_binary((yi64).to_native().to_binary())) + ); + + assert_eq!( + 16.5f32, + f32::from_native(f32::from_binary((16.5f32).to_native().to_binary())) + ); + + assert_eq!( + -16.5f32, + f32::from_native(f32::from_binary((-16.5f32).to_native().to_binary())) + ); + + use std::f64; + let xf64: f64 = f64::MAX; + assert_eq!( + xf64, + f64::from_native(f64::from_binary((xf64).to_native().to_binary())) + ); + + let yf64: f64 = f64::MIN; + assert_eq!( + yf64, + f64::from_native(f64::from_binary((yf64).to_native().to_binary())) + ); + } + +} diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 1c7c07d83..3562169ad 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -3,8 +3,7 @@ use crate::{ memory::{Memory, MemoryType}, module::{ModuleInfo, ModuleInner}, structures::TypedIndex, - types::{LocalOrImport, MemoryIndex, LocalMemoryIndex}, - units::{Pages}, + types::{LocalOrImport, MemoryIndex}, vmcalls, }; use std::{ffi::c_void, mem, ptr}; @@ -13,7 +12,16 @@ use hashbrown::HashMap; /// The context of the currently running WebAssembly instance. /// +/// This is implicitly passed to every WebAssembly function. +/// Since this is per-instance, each field has a statically +/// (as in after compiling the wasm) known size, so no +/// runtime checks are necessary. /// +/// While the runtime currently just passes this around +/// as the first, implicit parameter of every function, +/// it may someday be pinned to a register (especially +/// on arm, which has a ton of registers) to reduce +/// register shuffling. #[derive(Debug)] #[repr(C)] pub struct Ctx { @@ -22,11 +30,25 @@ pub struct Ctx { pub(crate) local_functions: *const *const Func, + /// These are pointers to things that are known to be owned + /// by the owning `Instance`. local_backing: *mut LocalBacking, import_backing: *mut ImportBacking, pub module: *const ModuleInner, + //// This is intended to be user-supplied, per-instance + /// contextual data. There are currently some issue with it, + /// notably that it cannot be set before running the `start` + /// function in a WebAssembly module. + /// + /// [#219](https://github.com/wasmerio/wasmer/pull/219) fixes that + /// issue, as well as allowing the user to have *per-function* + /// context, instead of just per-instance. pub data: *mut c_void, + + /// If there's a function set in this field, it gets called + /// when the context is destructed, e.g. when an `Instance` + /// is dropped. pub data_finalizer: Option, } diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index 678d6861c..5e620bc66 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-runtime" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,17 +9,17 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } lazy_static = "1.2.0" memmap = "0.7.0" [dependencies.wasmer-runtime-core] path = "../runtime-core" -version = "0.3.0" +version = "0.4.1" [dependencies.wasmer-clif-backend] path = "../clif-backend" -version = "0.3.0" +version = "0.4.1" optional = true [dev-dependencies] diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 1836bb206..bfdf6c9f7 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + //! Wasmer-runtime is a library that makes embedding WebAssembly //! in your application easy, efficient, and safe. //! diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index f9d7558eb..201a4d879 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-singlepass-backend" -version = "0.3.0" +version = "0.4.1" repository = "https://github.com/wasmerio/wasmer" description = "Wasmer runtime single pass compiler backend" license = "MIT" @@ -8,7 +8,7 @@ authors = ["The Wasmer Engineering Team "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmparser = "0.29.2" dynasm = "0.3.2" dynasmrt = "0.3.1" diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index 4e9e28591..c67dde3f5 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,18 +10,18 @@ use smallvec::SmallVec; use std::ptr::NonNull; use std::{any::Any, collections::HashMap, sync::Arc}; use wasmer_runtime_core::{ - backend::{Backend, RunnableModule, CompilerConfig, MemoryBoundCheckMode}, + backend::{sys::Memory, Backend, CacheGen, Token, RunnableModule, CompilerConfig, MemoryBoundCheckMode}, + cache::{Artifact, Error as CacheError}, codegen::*, memory::MemoryType, - module::ModuleInfo, + module::{ModuleInfo, ModuleInner}, structures::{Map, TypedIndex}, typed_func::Wasm, types::{ FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type, }, - vm::{self, LocalGlobal, LocalMemory, LocalTable}, - vmcalls, + vm::{self, LocalGlobal, LocalTable}, }; use wasmparser::{Operator, Type as WpType}; @@ -214,10 +214,10 @@ impl RunnableModule for X64ExecutionContext { user_error: *mut Option>, num_params_plus_one: Option>, ) -> bool { - let rm: &Box = &unsafe { &*(*ctx).module }.runnable_module; - let execution_context = unsafe { - ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm) - }; + let rm: &Box = &(&*(*ctx).module).runnable_module; + let execution_context = + ::std::mem::transmute_copy::<&dyn RunnableModule, &X64ExecutionContext>(&&**rm); + let args = ::std::slice::from_raw_parts( args, num_params_plus_one.unwrap().as_ptr() as usize - 1, @@ -365,7 +365,7 @@ impl ModuleCodeGenerator Ok(self.functions.last_mut().unwrap()) } - fn finalize(mut self, _: &ModuleInfo) -> Result { + fn finalize(mut self, _: &ModuleInfo) -> Result<(X64ExecutionContext, Box), CodegenError> { let (assembler, breakpoints) = match self.functions.last_mut() { Some(x) => ( x.assembler.take().unwrap(), @@ -415,15 +415,26 @@ impl ModuleCodeGenerator .collect(), ); - Ok(X64ExecutionContext { - code: output, - functions: self.functions, - signatures: self.signatures.as_ref().unwrap().clone(), - breakpoints: breakpoints, - func_import_count: self.func_import_count, - function_pointers: out_labels, - function_offsets: out_offsets, - }) + struct Placeholder; + impl CacheGen for Placeholder { + fn generate_cache(&self) -> Result<(Box<[u8]>, Memory), CacheError> { + Err(CacheError::Unknown( + "the singlepass backend doesn't support caching yet".to_string(), + )) + } + } + Ok(( + X64ExecutionContext { + code: output, + functions: self.functions, + signatures: self.signatures.as_ref().unwrap().clone(), + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + function_offsets: out_offsets, + }, + Box::new(Placeholder), + )) } fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError> { @@ -480,6 +491,11 @@ impl ModuleCodeGenerator })); Ok(()) } + unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result { + Err(CacheError::Unknown( + "the singlepass compiler API doesn't support caching yet".to_string(), + )) + } } impl X64FunctionCode { @@ -1400,7 +1416,7 @@ impl FunctionCodeGenerator for X64FunctionCode { Ok(()) } - fn begin_body(&mut self) -> Result<(), CodegenError> { + fn begin_body(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); a.emit_push(Size::S64, Location::GPR(GPR::RBP)); a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); @@ -4062,7 +4078,7 @@ impl FunctionCodeGenerator for X64FunctionCode { let (targets, default_target) = table.read_table().unwrap(); let cond = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); - let mut table_label = a.get_label(); + let table_label = a.get_label(); let mut table: Vec = vec![]; let default_br = a.get_label(); Self::emit_relaxed_binop( @@ -4084,7 +4100,7 @@ impl FunctionCodeGenerator for X64FunctionCode { a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); a.emit_jmp_location(Location::GPR(GPR::RDX)); - for (i, target) in targets.iter().enumerate() { + for target in targets.iter() { let label = a.get_label(); a.emit_label(label); table.push(label); diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs index 08d977487..f1011591d 100644 --- a/lib/singlepass-backend/src/lib.rs +++ b/lib/singlepass-backend/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![feature(proc_macro_hygiene)] #[cfg(not(any( diff --git a/lib/singlepass-backend/src/protect_unix.rs b/lib/singlepass-backend/src/protect_unix.rs index b66c77b94..6204134a7 100644 --- a/lib/singlepass-backend/src/protect_unix.rs +++ b/lib/singlepass-backend/src/protect_unix.rs @@ -1,5 +1,5 @@ //! Installing signal handlers allows us to handle traps and out-of-bounds memory -//! accesses that occur when runniing webassembly. +//! accesses that occur when runniing WebAssembly. //! //! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 //! @@ -111,7 +111,7 @@ pub fn call_protected(f: impl FnOnce() -> T) -> Result { // Ok(SIGSEGV) => "segmentation violation", // Ok(SIGBUS) => "bus error", // Err(_) => "error while getting the Signal", - // _ => "unkown trapped signal", + // _ => "unknown trapped signal", // }; // // When the trap-handler is fully implemented, this will return more information. // Err(RuntimeError::Trap { diff --git a/lib/spectests/Cargo.toml b/lib/spectests/Cargo.toml index 390e7ebe9..88136daa1 100644 --- a/lib/spectests/Cargo.toml +++ b/lib/spectests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-spectests" -version = "0.3.0" +version = "0.4.1" description = "Wasmer spectests library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -9,10 +9,10 @@ edition = "2018" build = "build/mod.rs" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } -wasmer-clif-backend = { path = "../clif-backend", version = "0.3.0" } -wasmer-llvm-backend = { path = "../llvm-backend", version = "0.3.0", optional = true } -wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.3.0", optional = true } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } +wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } +wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } +wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } [build-dependencies] wabt = "0.7.2" diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 30493f8fa..c4c936a02 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-wasi" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime WASI implementation library" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } libc = "0.2.50" rand = "0.6.5" # wasmer-runtime-abi = { path = "../runtime-abi" } diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 82de5ad1f..86f9ceaac 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate log; diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index fcd1caadd..0389273fc 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -279,7 +279,7 @@ pub fn fd_allocate( len: __wasi_filesize_t, ) -> __wasi_errno_t { debug!("wasi::fd_allocate"); - unimplemented!() + unimplemented!("wasi::fd_allocate") } /// ### `fd_close()` @@ -434,7 +434,7 @@ pub fn fd_filestat_set_size( st_size: __wasi_filesize_t, ) -> __wasi_errno_t { debug!("wasi::fd_filestat_set_size"); - unimplemented!() + unimplemented!("wasi::fd_filestat_set_size") } /// ### `fd_filestat_set_times()` @@ -474,14 +474,14 @@ pub fn fd_filestat_set_times( inode.stat.st_atim = st_atim; } else if fst_flags & __WASI_FILESTAT_SET_ATIM_NOW != 0 { // set to current real time - unimplemented!(); + unimplemented!("Set filestat time to the current real time"); } if fst_flags & __WASI_FILESTAT_SET_MTIM != 0 { inode.stat.st_mtim = st_mtim; } else if fst_flags & __WASI_FILESTAT_SET_MTIM_NOW != 0 { // set to current real time - unimplemented!(); + unimplemented!("Set filestat time to the current real time"); } __WASI_ESUCCESS @@ -501,7 +501,7 @@ pub fn fd_pread( let iov_cells = wasi_try!(iovs.deref(memory, 0, iovs_len)); let nread_cell = wasi_try!(nread.deref(memory)); - unimplemented!(); + unimplemented!("wasi::fd_pread"); __WASI_ESUCCESS } @@ -637,7 +637,7 @@ pub fn fd_pwrite( // TODO: verify return __WASI_EISDIR; } - Kind::Symlink { .. } => unimplemented!(), + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"), Kind::Buffer { buffer } => wasi_try!(write_bytes( &mut buffer[(offset as usize)..], memory, @@ -725,7 +725,7 @@ pub fn fd_read( // TODO: verify return __WASI_EISDIR; } - Kind::Symlink { .. } => unimplemented!(), + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"), Kind::Buffer { buffer } => { wasi_try!(read_bytes(&buffer[offset..], memory, iovs_arr_cell)) } @@ -771,7 +771,7 @@ pub fn fd_readdir( if let (Ok(buf_arr_cell), Ok(bufused_cell)) = (buf.deref(memory, 0, buf_len), bufused.deref(memory)) { - unimplemented!() + unimplemented!("wasi::fd_readdir") } else { __WASI_EFAULT } @@ -833,7 +833,7 @@ 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 => unimplemented!("__WASI__WHENCE_END in wasi::fd_seek"), __WASI_WHENCE_SET => fd_entry.offset = offset as u64, _ => return __WASI_EINVAL, } @@ -855,7 +855,7 @@ pub fn fd_seek( pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_sync"); // TODO: check __WASI_RIGHT_FD_SYNC - unimplemented!() + unimplemented!("wasi::fd_sync") } /// ### `fd_tell()` @@ -950,7 +950,7 @@ pub fn fd_write( // TODO: verify return __WASI_EISDIR; } - Kind::Symlink { .. } => unimplemented!(), + Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"), Kind::Buffer { buffer } => { wasi_try!(write_bytes(&mut buffer[offset..], memory, iovs_arr_cell)) } @@ -987,8 +987,62 @@ pub fn path_create_directory( path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_create_directory"); - // check __WASI_RIGHT_PATH_CREATE_DIRECTORY - unimplemented!() + let memory = ctx.memory(0); + let state = get_wasi_state(ctx); + + let working_dir = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF)); + if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_CREATE_DIRECTORY) { + return __WASI_EACCES; + } + let path_cells = wasi_try!(path.deref(memory, 0, path_len)); + let path_string = + wasi_try!( + std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) + .map_err(|_| __WASI_EINVAL) + ); + debug!("=> path: {}", &path_string); + + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); + if path_vec.is_empty() { + return __WASI_EINVAL; + } + + assert!( + path_vec.len() == 1, + "path_create_directory for paths greater than depth 1 has not been implemented because our WASI FS abstractions are a work in progress. We apologize for the inconvenience" + ); + debug!("Path vec: {:#?}", path_vec); + + wasi_try!(std::fs::create_dir(&path).map_err(|_| __WASI_EIO)); + + let kind = Kind::Dir { + //parent: Some(working_dir.inode), + path: path.clone(), + entries: Default::default(), + }; + let new_inode = state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, + name: path_vec[0].clone(), + kind, + }); + + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind { + entries.insert(path_vec[0].clone(), new_inode); + } else { + return __WASI_ENOTDIR; + } + + __WASI_ESUCCESS } /// ### `path_filestat_get()` @@ -1023,32 +1077,71 @@ pub fn path_filestat_get( return __WASI_EACCES; } - let path_vec = wasi_try!(::std::str::from_utf8(unsafe { + let path_string = wasi_try!(::std::str::from_utf8(unsafe { &*(wasi_try!(path.deref(memory, 0, path_len)) as *const [_] as *const [u8]) }) - .map_err(|_| __WASI_EINVAL)) - .split('/') - .map(|str| str.to_string()) - .collect::>(); + .map_err(|_| __WASI_EINVAL)); + debug!("=> path: {}", &path_string); + let path = std::path::PathBuf::from(path_string); + let path_vec = path + .components() + .map(|comp| comp.as_os_str().to_string_lossy().to_string()) + .collect::>(); let buf_cell = wasi_try!(buf.deref(memory)); + if path_vec.is_empty() { + return __WASI_EINVAL; + } + let mut cumulative_path = std::path::PathBuf::new(); + + debug!("=> Path vec: {:?}:", &path_vec); // find the inode by traversing the path let mut inode = root_dir.inode; - 'outer: for segment in path_vec { + 'outer: for segment in &path_vec[..(path_vec.len() - 1)] { // loop to traverse symlinks // TODO: proper cycle detection let mut sym_count = 0; loop { match &state.fs.inodes[inode].kind { Kind::Dir { entries, .. } => { - if let Some(entry) = entries.get(&segment) { + cumulative_path.push(&segment); + if let Some(entry) = entries.get(segment) { + debug!("Entry {:?} found", &segment); inode = entry.clone(); - continue 'outer; } else { - return __WASI_ENOENT; + // lazily load + debug!("Lazily loading entry {:?}", &segment); + let path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_ENOENT)); + if !path_metadata.is_dir() { + // TODO: should this just return invalid arg? + return __WASI_ENOTDIR; + } + let kind = Kind::Dir { + path: std::path::PathBuf::from(&segment), + entries: Default::default(), + }; + let inode_val = InodeVal::from_file_metadata( + &path_metadata, + segment.clone(), + false, + kind, + ); + let new_inode = state.fs.inodes.insert(inode_val); + let inode_idx = state.fs.inode_counter.get(); + state.fs.inode_counter.replace(inode_idx + 1); + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + // check that we're not displacing any entries + assert!(entries.insert(segment.clone(), new_inode).is_none()); + state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get(); + inode = new_inode; + } + debug!("Directory {:#?} lazily loaded", &cumulative_path); } + continue 'outer; } Kind::Symlink { forwarded } => { + // TODO: updated cumulative path sym_count += 1; inode = forwarded.clone(); if sym_count > MAX_SYMLINKS { @@ -1062,7 +1155,62 @@ pub fn path_filestat_get( } } - let stat = state.fs.inodes[inode].stat; + let final_inode = match &state.fs.inodes[inode].kind { + Kind::Dir { path, entries, .. } => { + // TODO: fail earlier if size 0 + let last_segment = path_vec.last().unwrap(); + cumulative_path.push(last_segment); + + if entries.contains_key(last_segment) { + entries[last_segment] + } else { + // lazily load it if we can + if !cumulative_path.exists() { + return __WASI_ENOENT; + } + let final_path_metadata = + wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO)); + let new_inode = if final_path_metadata.is_dir() { + debug!("Opening host directory {:#?}", &cumulative_path); + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::Dir { + path: std::path::PathBuf::from(&last_segment), + entries: Default::default(), + }, + }) + } else { + debug!("Opening host file {:#?}", &cumulative_path); + let real_open_file = wasi_try!(std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&cumulative_path) + .map_err(|_| __WASI_ENOENT)); + + state.fs.inodes.insert(InodeVal { + stat: __wasi_filestat_t::default(), + is_preopened: false, // is this correct? + name: last_segment.clone(), + kind: Kind::File { + handle: WasiFile::HostFile(real_open_file), + }, + }) + }; + // reborrow to insert entry + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind { + entries.insert(last_segment.clone(), new_inode); + } + new_inode + } + } + _ => { + return __WASI_ENOTDIR; + } + }; + + let stat = state.fs.inodes[final_inode].stat; buf_cell.set(stat); @@ -1097,7 +1245,7 @@ pub fn path_filestat_set_times( fst_flags: __wasi_fstflags_t, ) -> __wasi_errno_t { debug!("wasi::path_filestat_set_times"); - unimplemented!() + unimplemented!("wasi::path_filestat_set_times") } /// ### `path_link()` @@ -1128,7 +1276,7 @@ pub fn path_link( new_path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_link"); - unimplemented!() + unimplemented!("wasi::path_link") } /// ### `path_open()` @@ -1221,6 +1369,7 @@ pub fn path_open( match &state.fs.inodes[cur_dir_inode].kind { Kind::Dir { entries, .. } => { if let Some(child) = entries.get(path_segment) { + cumulative_path.push(path_segment); let inode_val = *child; cur_dir_inode = inode_val; } else { @@ -1376,7 +1525,7 @@ pub fn path_readlink( bufused: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::path_readlink"); - unimplemented!() + unimplemented!("wasi::path_readlink") } pub fn path_remove_directory( ctx: &mut Ctx, @@ -1385,7 +1534,7 @@ pub fn path_remove_directory( path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_remove_directory"); - unimplemented!() + unimplemented!("wasi::path_remove_directory") } pub fn path_rename( ctx: &mut Ctx, @@ -1397,7 +1546,7 @@ pub fn path_rename( new_path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_rename"); - unimplemented!() + unimplemented!("wasi::path_rename") } pub fn path_symlink( ctx: &mut Ctx, @@ -1408,7 +1557,7 @@ pub fn path_symlink( new_path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_symlink"); - unimplemented!() + unimplemented!("wasi::path_symlink") } pub fn path_unlink_file( ctx: &mut Ctx, @@ -1417,7 +1566,7 @@ pub fn path_unlink_file( path_len: u32, ) -> __wasi_errno_t { debug!("wasi::path_unlink_file"); - unimplemented!() + unimplemented!("wasi::path_unlink_file") } pub fn poll_oneoff( ctx: &mut Ctx, @@ -1427,7 +1576,7 @@ pub fn poll_oneoff( nevents: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::poll_oneoff"); - unimplemented!() + unimplemented!("wasi::poll_oneoff") } pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result { debug!("wasi::proc_exit, {}", code); @@ -1435,7 +1584,7 @@ pub fn proc_exit(ctx: &mut Ctx, code: __wasi_exitcode_t) -> Result __wasi_errno_t { debug!("wasi::proc_raise"); - unimplemented!() + unimplemented!("wasi::proc_raise") } /// ### `random_get()` @@ -1478,7 +1627,7 @@ pub fn sock_recv( ro_flags: WasmPtr<__wasi_roflags_t>, ) -> __wasi_errno_t { debug!("wasi::sock_recv"); - unimplemented!() + unimplemented!("wasi::sock_recv") } pub fn sock_send( ctx: &mut Ctx, @@ -1489,9 +1638,9 @@ pub fn sock_send( so_datalen: WasmPtr, ) -> __wasi_errno_t { debug!("wasi::sock_send"); - unimplemented!() + unimplemented!("wasi::sock_send") } pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { debug!("wasi::sock_shutdown"); - unimplemented!() + unimplemented!("wasi::sock_shutdown") } diff --git a/lib/win-exception-handler/Cargo.toml b/lib/win-exception-handler/Cargo.toml index c11d497bd..fe9342feb 100644 --- a/lib/win-exception-handler/Cargo.toml +++ b/lib/win-exception-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmer-win-exception-handler" -version = "0.3.0" +version = "0.4.1" description = "Wasmer runtime exception handling for Windows" license = "MIT" authors = ["The Wasmer Engineering Team "] @@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer" edition = "2018" [target.'cfg(windows)'.dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.3.0" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } winapi = { version = "0.3", features = ["winbase", "errhandlingapi", "minwindef", "minwinbase", "winnt"] } libc = "0.2.49" diff --git a/lib/win-exception-handler/exception_handling/exception_handling.c b/lib/win-exception-handler/exception_handling/exception_handling.c index 3c7990ec2..c3ecbab1d 100644 --- a/lib/win-exception-handler/exception_handling/exception_handling.c +++ b/lib/win-exception-handler/exception_handling/exception_handling.c @@ -5,6 +5,7 @@ #define CALL_FIRST 1 __declspec(thread) jmp_buf jmpBuf; +__declspec(thread) DWORD caughtExceptionCode; __declspec(thread) PVOID caughtExceptionAddress; __declspec(thread) DWORD64 caughtInstructionPointer; __declspec(thread) PVOID savedStackPointer; @@ -25,6 +26,7 @@ static LONG WINAPI exceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { EXCEPTION_RECORD* pExceptionRecord = ExceptionInfo->ExceptionRecord; PCONTEXT pCONTEXT = ExceptionInfo->ContextRecord; + caughtExceptionCode = pExceptionRecord->ExceptionCode; caughtExceptionAddress = pExceptionRecord->ExceptionAddress; caughtInstructionPointer = pCONTEXT->Rip; if (alreadyHandlingException == TRUE) { @@ -61,8 +63,9 @@ uint8_t callProtected(trampoline_t trampoline, } // jmp jmp jmp! - int signum = setjmp(jmpBuf); - if (signum == 0) { + int status = setjmp(jmpBuf); + if (status == 0) // 0 means the original call + { // save the stack pointer savedStackPointer = get_callee_frame_address(); trampoline(ctx, func, param_vec, return_vec); @@ -74,7 +77,7 @@ uint8_t callProtected(trampoline_t trampoline, return TRUE; } - out_result->code = (uint64_t)signum; + out_result->code = (uint64_t)caughtExceptionCode; out_result->exception_address = (uint64_t)caughtExceptionAddress; out_result->instruction_pointer = caughtInstructionPointer; @@ -83,4 +86,4 @@ uint8_t callProtected(trampoline_t trampoline, removeExceptionHandler(); return FALSE; -} +} \ No newline at end of file diff --git a/lib/win-exception-handler/src/exception_handling.rs b/lib/win-exception-handler/src/exception_handling.rs index ea36333ab..c448392b0 100644 --- a/lib/win-exception-handler/src/exception_handling.rs +++ b/lib/win-exception-handler/src/exception_handling.rs @@ -1,4 +1,3 @@ -use std::ffi::c_void; use std::ptr::NonNull; use wasmer_runtime_core::vm::{Ctx, Func}; diff --git a/lib/win-exception-handler/src/lib.rs b/lib/win-exception-handler/src/lib.rs index bc4a142b5..5329e1cc0 100644 --- a/lib/win-exception-handler/src/lib.rs +++ b/lib/win-exception-handler/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[cfg(windows)] mod exception_handling; diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 9af165011..4cf4d95e7 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + extern crate structopt; use std::env; @@ -87,6 +89,10 @@ struct Run { #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] em_symbol_map: Option, + /// Begin execution at the specified symbol + #[structopt(long = "em-entrypoint", group = "emscripten")] + em_entrypoint: Option, + /// WASI pre-opened directory #[structopt(long = "dir", multiple = true, group = "wasi")] pre_opened_directories: Vec, @@ -394,6 +400,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { options.path.to_str().unwrap() }, options.args.iter().map(|arg| arg.as_str()).collect(), + options.em_entrypoint.clone(), ) .map_err(|e| format!("{:?}", e))?; } else { diff --git a/src/installer/wasmer.iss b/src/installer/wasmer.iss index 999b4f49d..16a396593 100644 --- a/src/installer/wasmer.iss +++ b/src/installer/wasmer.iss @@ -18,6 +18,9 @@ DisableWelcomePage=no Source: "..\..\target\release\wasmer.exe"; DestDir: "{app}\bin" Source: "..\..\wapm-cli\target\release\wapm.exe"; DestDir: "{app}\bin" +[Dirs] +Name: "{%USERPROFILE}\.wasmer" + [Code] const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; @@ -68,7 +71,7 @@ begin if CurStep = ssPostInstall then begin EnvAddPath(ExpandConstant('{app}') +'\bin'); - EnvAddPath(ExpandConstant('{app}') +'\globals\wapm_packages\.bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); end end; @@ -77,6 +80,6 @@ begin if CurUninstallStep = usPostUninstall then begin EnvRemovePath(ExpandConstant('{app}') +'\bin'); - EnvAddPath(ExpandConstant('{app}') +'\globals\wapm_packages\.bin'); + EnvAddPath(ExpandConstant('{%USERPROFILE}') +'\globals\wapm_packages\.bin'); end end; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 25af8304f..78d6dae28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] + #[macro_use] extern crate wasmer_runtime_core; // extern crate wasmer_emscripten; diff --git a/update_version_numbers.sh b/update_version_numbers.sh index df7f38952..db448940e 100755 --- a/update_version_numbers.sh +++ b/update_version_numbers.sh @@ -1,5 +1,5 @@ -PREVIOUS_VERSION='0.2.1' -NEXT_VERSION='0.3.0' +PREVIOUS_VERSION='0.4.0' +NEXT_VERSION='0.4.1' # quick hack fd Cargo.toml --exec sed -i '' "s/version = \"$PREVIOUS_VERSION\"/version = \"$NEXT_VERSION\"/" diff --git a/wapm-cli b/wapm-cli index c9399f3fb..8286d0a4b 160000 --- a/wapm-cli +++ b/wapm-cli @@ -1 +1 @@ -Subproject commit c9399f3fb117c8ab1cbaf17b02bd5aeefb8e8e54 +Subproject commit 8286d0a4bcd771c2f5e622b40543143e25e096df