Merge remote-tracking branch 'origin/master' into command/dash

This commit is contained in:
Jesús Leganés-Combarro 'piranna 2019-07-06 12:21:33 +02:00
commit c827a6a993
40 changed files with 3204 additions and 792 deletions

View File

@ -35,13 +35,14 @@ jobs:
name: Install lint deps
command: |
git config --global --unset url."ssh://git@github.com".insteadOf || true
rustup toolchain install nightly-2019-05-20
# rustup toolchain install nightly-2019-06-10
# rustup default nightly-2019-06-10
rustup component add rustfmt
rustup component add clippy --toolchain=nightly-2019-05-20 || cargo +nightly-2019-05-20 install --git https://github.com/rust-lang/rust-clippy/ --force clippy
rustup component add clippy || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
- run:
name: Execute lints
command: |
make lint
cargo fmt --all -- --check
- save_cache:
paths:
- /usr/local/cargo/registry
@ -50,7 +51,7 @@ jobs:
- target/debug/deps
key: v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
test:
test-stable:
docker:
- image: circleci/rust:latest
<<: *run_with_build_env_vars
@ -61,24 +62,57 @@ jobs:
- v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
- <<: *run_install_dependencies
- run:
name: Tests
command: make test
- run:
name: Emscripten Tests
name: Test everything (except singlepass)
command: |
make test-emscripten-clif
make test-emscripten-llvm
make cranelift
make llvm
make test-rest
- run:
name: Release
command: make release-fast
- run:
name: Integration Tests
command: make integration-tests
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
- target/release/.fingerprint
- target/release/build
- target/release/deps
key: v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
test:
docker:
- image: circleci/rust:latest
<<: *run_with_build_env_vars
steps:
- checkout
- restore_cache:
keys:
- v8-test-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- <<: *run_install_dependencies
- run: rustup default nightly-2019-06-10
- run:
name: Tests
command: make test
- run:
name: Debug flag checked
command: |
cargo check --features "debug" --release
- run:
name: Release
command: make release-fast
- run:
name: Integration Tests
command: make integration-tests
- save_cache:
paths:
- /usr/local/cargo/registry
- target/release/.fingerprint
- target/release/build
- target/release/deps
key: v8-test-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
test-macos:
macos:
xcode: "9.0"
@ -86,7 +120,7 @@ jobs:
- checkout
- restore_cache:
keys:
- v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install crate dependencies
command: |
@ -100,7 +134,7 @@ jobs:
- run:
name: Install Rust
command: |
curl -sSf https://sh.rustup.rs | sh -s -- -y
curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2019-06-10
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
- run:
@ -114,16 +148,10 @@ jobs:
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test
- run:
name: Emscripten Tests
name: Release
command: |
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten-clif
make test-emscripten-llvm
make release-fast
- run:
name: Integration Tests
command: |
@ -134,13 +162,10 @@ jobs:
- save_cache:
paths:
- ~/.cargo/registry/
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
- target/release/.fingerprint
- target/release/build
- target/release/deps
key: v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
key: v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
test-and-build:
docker:
@ -161,28 +186,17 @@ jobs:
sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
# Use rust nightly (for singlepass, for now)
- run: rustup default nightly-2019-04-11
- run: rustup default nightly-2019-06-10
- run:
name: Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Emscripten Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten-clif
make test-emscripten-llvm
- run:
name: Debug flag checked
command: |
cargo check --features "debug"
- run:
name: Release Build
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make production-release
make release
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
mkdir -p artifacts
VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
@ -203,9 +217,6 @@ jobs:
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
- target/release/.fingerprint
- target/release/build
- target/release/deps
@ -240,15 +251,9 @@ jobs:
- run:
name: Install Rust
command: |
curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly
curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2019-06-10
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
# Use rust nightly (for singlepass, for now)
# - run:
# name: Install Rust nightly
# command: |
# export PATH="$HOME/.rustup/bin:$PATH"
# rustup default nightly-2019-04-11
- run:
name: Tests
command: |
@ -258,25 +263,15 @@ jobs:
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test
- run:
name: Emscripten Tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten-clif
make test-emscripten-singlepass
- run:
name: Release Build
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
make production-release
make release
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
mkdir -p artifacts
make build-install
@ -297,9 +292,6 @@ jobs:
- save_cache:
paths:
- ~/.cargo/registry/
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
- target/release/.fingerprint
- target/release/build
- target/release/deps
@ -308,41 +300,6 @@ jobs:
- wapm-cli/target/release/deps
key: v8-cargo-cache-darwin-nightly-{ arch }}-{{ checksum "Cargo.lock" }}
test-rust-nightly:
docker:
- image: circleci/rust:latest
steps:
- checkout
- restore_cache:
keys:
- v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install dependencies
command: |
sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
rustup toolchain install nightly
rustup target add wasm32-wasi --toolchain nightly
- run: |
rustup default nightly
make test-wasi-singlepass
make test-wasi-clif
- run: rustup default nightly-2019-04-11
- run: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
make test-singlepass
make test-emscripten-clif
make test-emscripten-singlepass
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
publish-github-release:
docker:
- image: cibuilds/github
@ -412,7 +369,7 @@ workflows:
branches:
only:
- master
- test-rust-nightly:
- test-stable:
filters:
branches:
only:

83
Cargo.lock generated
View File

@ -16,6 +16,11 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "approx"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayref"
version = "0.3.5"
@ -65,6 +70,16 @@ dependencies = [
"libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bincode"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bindgen"
version = "0.46.0"
@ -163,6 +178,16 @@ name = "cfg-if"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cgmath"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clang-sys"
version = "0.26.4"
@ -203,6 +228,15 @@ dependencies = [
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colored"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "constant_time_eq"
version = "0.1.3"
@ -706,6 +740,14 @@ dependencies = [
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.8"
@ -814,6 +856,18 @@ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.5"
@ -999,6 +1053,11 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rgb"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-demangle"
version = "0.1.15"
@ -1496,7 +1555,10 @@ dependencies = [
name = "wasmer-runtime-core"
version = "0.5.3"
dependencies = [
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1523,6 +1585,7 @@ name = "wasmer-singlepass-backend"
version = "0.5.3"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1558,6 +1621,7 @@ dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.5.3",
"wasmer-dev-utils 0.5.3",
"wasmer-llvm-backend 0.5.3",
"wasmer-runtime-core 0.5.3",
"wasmer-singlepass-backend 0.5.3",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1635,15 +1699,28 @@ dependencies = [
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winconsole"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
"checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e"
@ -1655,10 +1732,12 @@ dependencies = [
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c"
"checksum clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "2ca4386c8954b76a8415b63959337d940d724b336cabd3afe189c2b51a7e1ff0"
"checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum cranelift-bforest 0.31.0 (git+https://github.com/wasmerio/cranelift.git?rev=2ada531d79b34a9e6c94c81f2615677e22d68780)" = "<none>"
"checksum cranelift-codegen 0.31.0 (git+https://github.com/wasmerio/cranelift.git?rev=2ada531d79b34a9e6c94c81f2615677e22d68780)" = "<none>"
@ -1715,6 +1794,7 @@ dependencies = [
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
@ -1729,6 +1809,7 @@ dependencies = [
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
@ -1749,6 +1830,7 @@ dependencies = [
"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58"
"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f"
@ -1804,3 +1886,4 @@ dependencies = [
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
"checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032"

185
Makefile
View File

@ -1,41 +1,130 @@
ifeq (test, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
.PHONY: spectests emtests clean build install lint precommit
# This will re-generate the Rust test files based on spectests/*.wast
spectests:
WASMER_RUNTIME_GENERATE_SPECTESTS=1 cargo build -p wasmer-runtime-core
# Generate files
generate-spectests:
WASMER_RUNTIME_GENERATE_SPECTESTS=1 cargo build -p wasmer-runtime-core --release
emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten
generate-emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten --release
wasitests:
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi
generate-wasitests:
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi --release
# clean:
# rm -rf artifacts
generate: generate-spectests generate-emtests generate-wasitests
build:
cargo build --features debug
install:
cargo install --path .
# Spectests
spectests-singlepass:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass
integration-tests: release
spectests-cranelift:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif
spectests-llvm:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features llvm
spectests: spectests-singlepass spectests-cranelift spectests-llvm
# Emscripten tests
emtests-singlepass:
cargo test --manifest-path lib/emscripten/Cargo.toml --release --features singlepass -- --test-threads=1
emtests-cranelift:
cargo test --manifest-path lib/emscripten/Cargo.toml --release --features clif -- --test-threads=1
emtests-llvm:
cargo test --manifest-path lib/emscripten/Cargo.toml --release --features llvm -- --test-threads=1
emtests: emtests-singlepass emtests-cranelift emtests-llvm
# Middleware tests
middleware-singlepass:
cargo test --manifest-path lib/middleware-common/Cargo.toml --release --features singlepass
middleware-cranelift:
cargo test --manifest-path lib/middleware-common/Cargo.toml --release --features clif
middleware-llvm:
cargo test --manifest-path lib/middleware-common/Cargo.toml --release --features llvm
middleware: middleware-singlepass middleware-cranelift middleware-llvm
# Wasitests
wasitests-singlepass:
cargo test --manifest-path lib/wasi/Cargo.toml --release --features singlepass -- --test-threads=1
wasitests-cranelift:
cargo test --manifest-path lib/wasi/Cargo.toml --release --features clif -- --test-threads=1
wasitests-llvm:
cargo test --manifest-path lib/wasi/Cargo.toml --release --features llvm -- --test-threads=1
wasitests: wasitests-singlepass wasitests-cranelift wasitests-llvm
# Backends
singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasitests-singlepass
cargo test -p wasmer-singlepass-backend --release
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
cargo test -p wasmer-clif-backend --release
llvm: spectests-llvm emtests-llvm middleware-llvm wasitests-llvm
cargo test -p wasmer-llvm-backend --release
# All tests
capi:
cargo build --release
cargo build -p wasmer-runtime-c-api --release
cargo test -p wasmer-runtime-c-api --release
test-rest: capi
cargo test --release --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-wasi --exclude wasmer-middleware-common --exclude wasmer-singlepass-backend --exclude wasmer-clif-backend --exclude wasmer-llvm-backend
circleci-clean:
@if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi;
test: spectests emtests middleware wasitests circleci-clean test-rest
# Integration tests
integration-tests: release-fast
echo "Running Integration Tests"
./integration_tests/lua/test.sh
./integration_tests/nginx/test.sh
./integration_tests/cowsay/test.sh
# Utils
lint:
cargo fmt --all -- --check
cargo +nightly-2019-05-20 clippy --all
precommit: lint test
build:
cargo build --release --features debug
install:
cargo install --path .
release:
cargo build --release --features backend:singlepass,backend:llvm,loader:kernel
# Only one backend (cranelift)
release-fast:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
# brew install mingw-w64
cargo build --release
bench:
cargo bench --all
# Build utils
build-install:
mkdir -p ./install/bin
cp ./wapm-cli/target/release/wapm ./install/bin/
@ -46,62 +135,6 @@ build-install:
do-install:
tar -C ~/.wasmer -zxvf wasmer.tar.gz
test:
# We use one thread so the emscripten stdouts doesn't collide
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend --exclude wasmer-wasi --exclude wasmer-middleware-common -- $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/middleware-common/Cargo.toml --features clif
@if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi;
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
cargo test --manifest-path lib/runtime/Cargo.toml --features llvm
cargo test --manifest-path lib/middleware-common/Cargo.toml --features llvm
cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture
test-singlepass:
cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass
cargo test --manifest-path lib/runtime/Cargo.toml --features singlepass
cargo test --manifest-path lib/middleware-common/Cargo.toml --features singlepass
test-emscripten-llvm:
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)
test-emscripten-clif:
cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs)
test-emscripten-singlepass:
cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs)
test-wasi-clif:
cargo test --manifest-path lib/wasi/Cargo.toml --features "clif" -- --test-threads=1 $(runargs)
test-wasi-singlepass:
cargo test --manifest-path lib/wasi/Cargo.toml --features "singlepass" -- --test-threads=1 $(runargs)
singlepass-debug-release:
cargo +nightly build --features backend:singlepass,debug --release
singlepass-release:
cargo +nightly build --features backend:singlepass --release
singlepass-build:
cargo +nightly build --features backend:singlepass,debug
release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
# brew install mingw-w64
cargo build --release
production-release:
cargo build --release --features backend:singlepass,backend:llvm,loader:kernel
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/

View File

@ -153,6 +153,12 @@ nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmer
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
Set Rust Nightly:
```
rustup default nightly
```
And install Wasmer
```sh
# checkout code
git clone https://github.com/wasmerio/wasmer.git
@ -160,38 +166,45 @@ cd wasmer
# install tools
# make sure that `python` is accessible.
cargo install --path .
make install
```
## Testing
Thanks to [spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests) we can ensure 100% compatibility with the WebAssembly spec test suite.
Tests can be run with:
You can run all the tests with:
```sh
rustup default nightly
make test
```
If you need to regenerate the Rust tests from the spec tests
you can run:
### Testing backends
```sh
make spectests
```
Each backend can be tested separately:
You can also run integration tests with:
* Singlepass: `make singlepass`
* Cranelift: `make cranelift`
* LLVM: `make llvm`
### Testing integrations
Each integration can be tested separately:
* Spec tests: `make spectests`
* Emscripten: `make emtests`
* WASI: `make wasi`
* Middleware: `make middleware`
* C API: `make capi`
```sh
make integration-tests
```
## Benchmarking
Benchmarks can be run with:
```sh
cargo bench --all
make bench
```
## Roadmap

View File

@ -2,7 +2,7 @@ status = [
"ci/circleci: lint",
"ci/circleci: test",
"ci/circleci: test-macos",
"ci/circleci: test-rust-nightly",
"ci/circleci: test-stable",
"continuous-integration/appveyor/branch"
]
required_approvals = 1

74
examples/iterative_hash/Cargo.lock generated Normal file
View File

@ -0,0 +1,74 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "blake2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iterative_hash"
version = "0.1.0"
dependencies = [
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "opaque-debug"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"

View File

@ -0,0 +1,12 @@
[package]
name = "iterative_hash"
version = "0.1.0"
authors = ["losfair <zhy20000919@hotmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
[dependencies]
blake2 = "0.8"

View File

@ -0,0 +1,16 @@
use blake2::{Blake2b, Digest};
fn main() {
let mut data: Vec<u8> = b"test".to_vec();
for i in 0.. {
let mut hasher = Blake2b::new();
hasher.input(&data);
let out = hasher.result();
data = out.to_vec();
if i % 1000000 == 0 {
println!("Round {}: {:?}", i, data);
}
}
}

View File

@ -0,0 +1,67 @@
#[link(wasm_import_module = "wasmer_suspend")]
extern "C" {
fn suspend();
}
use std::collections::BTreeMap;
#[derive(Default)]
struct Node {
count: usize,
children: BTreeMap<char, Node>,
}
impl Node {
fn insert(&mut self, mut s: impl Iterator<Item = char>) {
match s.next() {
Some(x) => {
self.children.entry(x).or_default().insert(s);
}
None => {
self.count += 1;
}
}
}
fn for_each_dyn(&self, cb: &dyn Fn(&str, usize), prefix: &mut String) {
if self.count > 0 {
cb(&prefix, self.count);
}
for (k, v) in self.children.iter() {
prefix.push(*k);
v.for_each_dyn(cb, prefix);
prefix.pop().unwrap();
}
}
}
fn main() {
let mut root = Node::default();
root.insert("Ava".chars());
root.insert("Alexander".chars());
root.insert("Aiden".chars());
root.insert("Bella".chars());
root.insert("Brianna".chars());
root.insert("Brielle".chars());
root.insert("Charlotte".chars());
root.insert("Chloe".chars());
root.insert("Camila".chars());
println!("Tree ready, suspending.");
unsafe {
suspend();
}
root.for_each_dyn(
&|seq, count| {
println!("{}: {}", seq, count);
unsafe {
suspend();
}
},
&mut "".into(),
);
println!("[END]");
}

View File

@ -164,6 +164,7 @@ impl Intrinsics {
let memory_base_ty = i8_ty;
let memory_bound_ty = i8_ty;
let internals_ty = i64_ty;
let interrupt_signal_mem_ty = i8_ty;
let local_function_ty = i8_ptr_ty;
let anyfunc_ty = context.struct_type(
@ -222,6 +223,9 @@ impl Intrinsics {
internals_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
interrupt_signal_mem_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
local_function_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),

View File

@ -17,6 +17,7 @@ impl FunctionMiddleware for CallTrace {
Event::Internal(InternalEvent::FunctionBegin(id)) => sink.push(Event::Internal(
InternalEvent::Breakpoint(Box::new(move |_| {
eprintln!("func ({})", id);
Ok(())
})),
)),
_ => {}

View File

@ -94,11 +94,9 @@ impl FunctionMiddleware for Metering {
sink.push(Event::WasmOwned(Operator::If {
ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType),
}));
sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(
move |ctx| unsafe {
(ctx.throw)(Box::new(ExecutionLimitExceededError));
},
))));
sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(|_| {
Err(Box::new(ExecutionLimitExceededError))
}))));
sink.push(Event::WasmOwned(Operator::End));
}
_ => {}

View File

@ -17,7 +17,7 @@ add_executable(test-validate test-validate.c)
find_library(
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll
PATHS ${CMAKE_SOURCE_DIR}/../../../target/debug/
PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/
)
if(NOT WASMER_LIB)

View File

@ -18,6 +18,8 @@ errno = "0.2.4"
libc = "0.2.49"
hex = "0.3.2"
smallvec = "0.6.9"
bincode = "1.1"
colored = "1.8"
# Dependencies for caching.
[dependencies.serde]
@ -47,6 +49,7 @@ field-offset = "0.1.1"
[build-dependencies]
blake2b_simd = "0.4.1"
rustc_version = "0.2.3"
cc = "1.0"
[features]
debug = []

View File

@ -28,4 +28,16 @@ fn main() {
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
println!("cargo:rustc-cfg=nightly");
}
if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
cc::Build::new()
.file("image-loading-linux-x86-64.s")
.compile("image-loading");
} else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
cc::Build::new()
.file("image-loading-macos-x86-64.s")
.compile("image-loading");
} else {
}
}

View File

@ -0,0 +1,69 @@
# NOTE: Keep this consistent with `fault.rs`.
.globl run_on_alternative_stack
run_on_alternative_stack:
# (stack_end, stack_begin)
# We need to ensure 16-byte alignment here.
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rbp
movq %rsp, -16(%rdi)
leaq run_on_alternative_stack.returning(%rip), %rax
movq %rax, -24(%rdi)
movq %rsi, %rsp
movq (%rsp), %xmm0
add $8, %rsp
movq (%rsp), %xmm1
add $8, %rsp
movq (%rsp), %xmm2
add $8, %rsp
movq (%rsp), %xmm3
add $8, %rsp
movq (%rsp), %xmm4
add $8, %rsp
movq (%rsp), %xmm5
add $8, %rsp
movq (%rsp), %xmm6
add $8, %rsp
movq (%rsp), %xmm7
add $8, %rsp
popq %rbp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
retq
run_on_alternative_stack.returning:
movq (%rsp), %rsp
popq %rbp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
retq

View File

@ -0,0 +1,69 @@
# NOTE: Keep this consistent with `fault.rs`.
.globl _run_on_alternative_stack
_run_on_alternative_stack:
# (stack_end, stack_begin)
# We need to ensure 16-byte alignment here.
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rbp
movq %rsp, -16(%rdi)
leaq _run_on_alternative_stack.returning(%rip), %rax
movq %rax, -24(%rdi)
movq %rsi, %rsp
movq (%rsp), %xmm0
add $8, %rsp
movq (%rsp), %xmm1
add $8, %rsp
movq (%rsp), %xmm2
add $8, %rsp
movq (%rsp), %xmm3
add $8, %rsp
movq (%rsp), %xmm4
add $8, %rsp
movq (%rsp), %xmm5
add $8, %rsp
movq (%rsp), %xmm6
add $8, %rsp
movq (%rsp), %xmm7
add $8, %rsp
popq %rbp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
retq
_run_on_alternative_stack.returning:
movq (%rsp), %rsp
popq %rbp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
retq

View File

@ -1,6 +1,7 @@
use crate::{
error::CompileResult,
module::ModuleInner,
state::ModuleStateMap,
typed_func::Wasm,
types::{LocalFuncIndex, SigIndex},
vm,
@ -8,6 +9,7 @@ use crate::{
use crate::{
cache::{Artifact, Error as CacheError},
codegen::BreakpointMap,
module::ModuleInfo,
sys::Memory,
};
@ -84,6 +86,14 @@ pub trait RunnableModule: Send + Sync {
local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>>;
fn get_module_state_map(&self) -> Option<ModuleStateMap> {
None
}
fn get_breakpoints(&self) -> Option<BreakpointMap> {
None
}
/// A wasm trampoline contains the necessary data to dynamically call an exported wasm function.
/// Given a particular signature index, we are returned a trampoline that is matched with that
/// signature and an invoke function that can call the trampoline.

View File

@ -9,6 +9,7 @@ use crate::{
};
use smallvec::SmallVec;
use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
@ -16,6 +17,10 @@ use std::sync::{Arc, RwLock};
use wasmparser::{self, WasmDecoder};
use wasmparser::{Operator, Type as WpType};
pub type BreakpointHandler =
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
#[derive(Debug)]
pub enum Event<'a, 'b> {
Internal(InternalEvent),
@ -26,7 +31,7 @@ pub enum Event<'a, 'b> {
pub enum InternalEvent {
FunctionBegin(u32),
FunctionEnd,
Breakpoint(Box<Fn(BkptInfo) + Send + Sync + 'static>),
Breakpoint(BreakpointHandler),
SetInternal(u32),
GetInternal(u32),
}
@ -43,8 +48,8 @@ impl fmt::Debug for InternalEvent {
}
}
pub struct BkptInfo {
pub throw: unsafe fn(Box<dyn Any>) -> !,
pub struct BreakpointInfo<'a> {
pub fault: Option<&'a dyn Any>,
}
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {

View File

@ -0,0 +1,464 @@
mod raw {
use std::ffi::c_void;
extern "C" {
pub fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64;
pub fn setjmp(env: *mut c_void) -> i32;
pub fn longjmp(env: *mut c_void, val: i32) -> !;
}
}
use crate::codegen::{BreakpointInfo, BreakpointMap};
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR, XMM};
use crate::vm;
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT,
SIGSEGV, SIGTRAP,
};
use std::any::Any;
use std::cell::UnsafeCell;
use std::ffi::c_void;
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
pub(crate) unsafe fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64 {
raw::run_on_alternative_stack(stack_end, stack_begin)
}
const TRAP_STACK_SIZE: usize = 1048576; // 1MB
const SETJMP_BUFFER_LEN: usize = 27;
type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>,
payload: Option<Box<Any>>, // out
}
thread_local! {
static UNWIND: UnsafeCell<Option<UnwindInfo>> = UnsafeCell::new(None);
}
struct InterruptSignalMem(*mut u8);
unsafe impl Send for InterruptSignalMem {}
unsafe impl Sync for InterruptSignalMem {}
const INTERRUPT_SIGNAL_MEM_SIZE: usize = 4096;
lazy_static! {
static ref INTERRUPT_SIGNAL_MEM: InterruptSignalMem = {
let ptr = unsafe {
mmap(
::std::ptr::null_mut(),
INTERRUPT_SIGNAL_MEM_SIZE,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if ptr as isize == -1 {
panic!("cannot allocate code memory");
}
InterruptSignalMem(ptr as _)
};
}
static INTERRUPT_SIGNAL_DELIVERED: AtomicBool = AtomicBool::new(false);
pub unsafe fn get_wasm_interrupt_signal_mem() -> *mut u8 {
INTERRUPT_SIGNAL_MEM.0
}
pub unsafe fn set_wasm_interrupt() {
let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_NONE) < 0 {
panic!("cannot set PROT_NONE on signal mem");
}
}
pub unsafe fn clear_wasm_interrupt() {
let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_READ | PROT_WRITE) < 0 {
panic!("cannot set PROT_READ | PROT_WRITE on signal mem");
}
}
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
f: F,
breakpoints: Option<BreakpointMap>,
) -> Result<R, Box<Any>> {
let unwind = UNWIND.with(|x| x.get());
let old = (*unwind).take();
*unwind = Some(UnwindInfo {
jmpbuf: [0; SETJMP_BUFFER_LEN],
breakpoints: breakpoints,
payload: None,
});
if raw::setjmp(&mut (*unwind).as_mut().unwrap().jmpbuf as *mut SetJmpBuffer as *mut _) != 0 {
// error
let ret = (*unwind).as_mut().unwrap().payload.take().unwrap();
*unwind = old;
Err(ret)
} else {
let ret = f();
// implicit control flow to the error case...
*unwind = old;
Ok(ret)
}
}
pub unsafe fn begin_unsafe_unwind(e: Box<Any>) -> ! {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()
.expect("not within a catch_unsafe_unwind scope");
inner.payload = Some(e);
raw::longjmp(&mut inner.jmpbuf as *mut SetJmpBuffer as *mut _, 0xffff);
}
unsafe fn with_breakpoint_map<R, F: FnOnce(Option<&BreakpointMap>) -> R>(f: F) -> R {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()
.expect("not within a catch_unsafe_unwind scope");
f(inner.breakpoints.as_ref())
}
pub fn allocate_and_run<R, F: FnOnce() -> R>(size: usize, f: F) -> R {
struct Context<F: FnOnce() -> R, R> {
f: Option<F>,
ret: Option<R>,
}
extern "C" fn invoke<F: FnOnce() -> R, R>(ctx: &mut Context<F, R>) {
let f = ctx.f.take().unwrap();
ctx.ret = Some(f());
}
unsafe {
let mut ctx = Context {
f: Some(f),
ret: None,
};
assert!(size % 16 == 0);
assert!(size >= 4096);
let mut stack: Vec<u64> = vec![0; size / 8];
let end_offset = stack.len();
stack[end_offset - 4] = invoke::<F, R> as usize as u64;
// NOTE: Keep this consistent with `image-loading-*.s`.
stack[end_offset - 4 - 10] = &mut ctx as *mut Context<F, R> as usize as u64; // rdi
const NUM_SAVED_REGISTERS: usize = 23;
let stack_begin = stack
.as_mut_ptr()
.offset((end_offset - 4 - NUM_SAVED_REGISTERS) as isize);
let stack_end = stack.as_mut_ptr().offset(end_offset as isize);
raw::run_on_alternative_stack(stack_end, stack_begin);
ctx.ret.take().unwrap()
}
}
extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
siginfo: *mut siginfo_t,
ucontext: *mut c_void,
) {
unsafe {
let fault = get_fault_info(siginfo as _, ucontext);
let mut unwind_result: Box<dyn Any> = Box::new(());
let should_unwind = allocate_and_run(TRAP_STACK_SIZE, || {
let mut is_suspend_signal = false;
match Signal::from_c_int(signum) {
Ok(SIGTRAP) => {
// breakpoint
let out: Option<Result<(), Box<dyn Any>>> = with_breakpoint_map(|bkpt_map| {
bkpt_map.and_then(|x| x.get(&(fault.ip as usize))).map(|x| {
x(BreakpointInfo {
fault: Some(&fault),
})
})
});
match out {
Some(Ok(())) => {
return false;
}
Some(Err(e)) => {
unwind_result = e;
return true;
}
None => {}
}
}
Ok(SIGSEGV) | Ok(SIGBUS) => {
if fault.faulting_addr as usize == get_wasm_interrupt_signal_mem() as usize {
is_suspend_signal = true;
clear_wasm_interrupt();
INTERRUPT_SIGNAL_DELIVERED.store(false, Ordering::SeqCst);
}
}
_ => {}
}
// TODO: make this safer
let ctx = &mut *(fault.known_registers[X64Register::GPR(GPR::R15).to_index().0].unwrap()
as *mut vm::Ctx);
let rsp = fault.known_registers[X64Register::GPR(GPR::RSP).to_index().0].unwrap();
let msm = (*ctx.module)
.runnable_module
.get_module_state_map()
.unwrap();
let code_base = (*ctx.module).runnable_module.get_code().unwrap().as_ptr() as usize;
let es_image = read_stack(
&msm,
code_base,
rsp as usize as *const u64,
fault.known_registers,
Some(fault.ip as usize as u64),
);
if is_suspend_signal {
let image = build_instance_image(ctx, es_image);
unwind_result = Box::new(image);
} else {
use colored::*;
if es_image.frames.len() > 0 {
eprintln!(
"\n{}",
"Wasmer encountered an error while running your WebAssembly program."
.bold()
.red()
);
es_image.print_backtrace_if_needed();
}
// Just let the error propagate otherrwise
}
true
});
if should_unwind {
begin_unsafe_unwind(unwind_result);
}
}
}
extern "C" fn sigint_handler(
_signum: ::nix::libc::c_int,
_siginfo: *mut siginfo_t,
_ucontext: *mut c_void,
) {
if INTERRUPT_SIGNAL_DELIVERED.swap(true, Ordering::SeqCst) {
eprintln!("Got another SIGINT before trap is triggered on WebAssembly side, aborting");
process::abort();
}
unsafe {
set_wasm_interrupt();
}
}
pub fn ensure_sighandler() {
INSTALL_SIGHANDLER.call_once(|| unsafe {
install_sighandler();
});
}
static INSTALL_SIGHANDLER: Once = Once::new();
unsafe fn install_sighandler() {
let sa_trap = SigAction::new(
SigHandler::SigAction(signal_trap_handler),
SaFlags::SA_ONSTACK,
SigSet::empty(),
);
sigaction(SIGFPE, &sa_trap).unwrap();
sigaction(SIGILL, &sa_trap).unwrap();
sigaction(SIGSEGV, &sa_trap).unwrap();
sigaction(SIGBUS, &sa_trap).unwrap();
sigaction(SIGTRAP, &sa_trap).unwrap();
let sa_interrupt = SigAction::new(
SigHandler::SigAction(sigint_handler),
SaFlags::SA_ONSTACK,
SigSet::empty(),
);
sigaction(SIGINT, &sa_interrupt).unwrap();
}
pub struct FaultInfo {
pub faulting_addr: *const c_void,
pub ip: *const c_void,
pub known_registers: [Option<u64>; 24],
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo {
use libc::{
_libc_xmmreg, ucontext_t, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8,
REG_R9, REG_RAX, REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP,
};
fn read_xmm(reg: &_libc_xmmreg) -> u64 {
(reg.element[0] as u64) | ((reg.element[1] as u64) << 32)
}
#[allow(dead_code)]
#[repr(C)]
struct siginfo_t {
si_signo: i32,
si_errno: i32,
si_code: i32,
si_addr: u64,
// ...
}
let siginfo = siginfo as *const siginfo_t;
let si_addr = (*siginfo).si_addr;
let ucontext = ucontext as *const ucontext_t;
let gregs = &(*ucontext).uc_mcontext.gregs;
let fpregs = &*(*ucontext).uc_mcontext.fpregs;
let mut known_registers: [Option<u64>; 24] = [None; 24];
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[REG_R15 as usize] as _);
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[REG_R14 as usize] as _);
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[REG_R13 as usize] as _);
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs[REG_R12 as usize] as _);
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs[REG_R11 as usize] as _);
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs[REG_R10 as usize] as _);
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs[REG_R9 as usize] as _);
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs[REG_R8 as usize] as _);
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs[REG_RSI as usize] as _);
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs[REG_RDI as usize] as _);
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs[REG_RDX as usize] as _);
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs[REG_RCX as usize] as _);
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs[REG_RBX as usize] as _);
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs[REG_RAX as usize] as _);
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[REG_RBP as usize] as _);
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[REG_RSP as usize] as _);
known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(read_xmm(&fpregs._xmm[0]));
known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(read_xmm(&fpregs._xmm[1]));
known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(read_xmm(&fpregs._xmm[2]));
known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(read_xmm(&fpregs._xmm[3]));
known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(read_xmm(&fpregs._xmm[4]));
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(read_xmm(&fpregs._xmm[5]));
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(read_xmm(&fpregs._xmm[6]));
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(read_xmm(&fpregs._xmm[7]));
FaultInfo {
faulting_addr: si_addr as usize as _,
ip: gregs[REG_RIP as usize] as _,
known_registers,
}
}
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo {
#[allow(dead_code)]
#[repr(C)]
struct ucontext_t {
uc_onstack: u32,
uc_sigmask: u32,
uc_stack: libc::stack_t,
uc_link: *const ucontext_t,
uc_mcsize: u64,
uc_mcontext: *const mcontext_t,
}
#[repr(C)]
struct exception_state {
trapno: u16,
cpu: u16,
err: u32,
faultvaddr: u64,
}
#[repr(C)]
struct regs {
rax: u64,
rbx: u64,
rcx: u64,
rdx: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rsp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
rip: u64,
rflags: u64,
cs: u64,
fs: u64,
gs: u64,
}
#[repr(C)]
struct fpstate {
_unused: [u8; 168],
xmm: [[u64; 2]; 8],
}
#[allow(dead_code)]
#[repr(C)]
struct mcontext_t {
es: exception_state,
ss: regs,
fs: fpstate,
}
let siginfo = siginfo as *const siginfo_t;
let si_addr = (*siginfo).si_addr;
let ucontext = ucontext as *const ucontext_t;
let ss = &(*(*ucontext).uc_mcontext).ss;
let fs = &(*(*ucontext).uc_mcontext).fs;
let mut known_registers: [Option<u64>; 24] = [None; 24];
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(ss.r15);
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(ss.r14);
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(ss.r13);
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(ss.r12);
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(ss.r11);
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(ss.r10);
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(ss.r9);
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(ss.r8);
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(ss.rsi);
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(ss.rdi);
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(ss.rdx);
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(ss.rcx);
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(ss.rbx);
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(ss.rax);
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(ss.rbp);
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(ss.rsp);
known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(fs.xmm[0][0]);
known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(fs.xmm[1][0]);
known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(fs.xmm[2][0]);
known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(fs.xmm[3][0]);
known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(fs.xmm[4][0]);
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(fs.xmm[5][0]);
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(fs.xmm[6][0]);
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(fs.xmm[7][0]);
FaultInfo {
faulting_addr: si_addr,
ip: ss.rip as _,
known_registers,
}
}

View File

@ -46,7 +46,7 @@ impl IsExport for Export {
/// ```
pub struct ImportObject {
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub(crate) state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub allow_missing_functions: bool,
}

View File

@ -43,6 +43,9 @@ pub mod vm;
pub mod vmcalls;
#[cfg(all(unix, target_arch = "x86_64"))]
pub use trampoline_x64 as trampoline;
#[cfg(all(unix, target_arch = "x86_64"))]
pub mod fault;
pub mod state;
use self::error::CompileResult;
#[doc(inline)]

View File

@ -223,13 +223,6 @@ pub fn read_module<
let fcg = mcg
.next_function(Arc::clone(&info))
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
middlewares
.run(
Some(fcg),
Event::Internal(InternalEvent::FunctionBegin(id as u32)),
&info.read().unwrap(),
)
.map_err(|x| LoadError::Codegen(x))?;
let info_read = info.read().unwrap();
let sig = info_read
@ -271,6 +264,13 @@ pub fn read_module<
body_begun = true;
fcg.begin_body(&info.read().unwrap())
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
middlewares
.run(
Some(fcg),
Event::Internal(InternalEvent::FunctionBegin(id as u32)),
&info.read().unwrap(),
)
.map_err(|x| LoadError::Codegen(x))?;
}
middlewares
.run(Some(fcg), Event::Wasm(op), &info.read().unwrap())

View File

@ -0,0 +1,840 @@
use std::collections::BTreeMap;
use std::ops::Bound::{Included, Unbounded};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct RegisterIndex(pub usize);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum WasmAbstractValue {
Runtime,
Const(u64),
}
#[derive(Clone, Debug)]
pub struct MachineState {
pub stack_values: Vec<MachineValue>,
pub register_values: Vec<MachineValue>,
pub wasm_stack: Vec<WasmAbstractValue>,
pub wasm_stack_private_depth: usize,
pub wasm_inst_offset: usize,
}
#[derive(Clone, Debug, Default)]
pub struct MachineStateDiff {
pub last: Option<usize>,
pub stack_push: Vec<MachineValue>,
pub stack_pop: usize,
pub reg_diff: Vec<(RegisterIndex, MachineValue)>,
pub wasm_stack_push: Vec<WasmAbstractValue>,
pub wasm_stack_pop: usize,
pub wasm_stack_private_depth: usize, // absolute value; not a diff.
pub wasm_inst_offset: usize, // absolute value; not a diff.
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MachineValue {
Undefined,
Vmctx,
PreserveRegister(RegisterIndex),
CopyStackBPRelative(i32), // relative to Base Pointer, in byte offset
ExplicitShadow, // indicates that all values above this are above the shadow region
WasmStack(usize),
WasmLocal(usize),
}
#[derive(Clone, Debug)]
pub struct FunctionStateMap {
pub initial: MachineState,
pub local_function_id: usize,
pub locals: Vec<WasmAbstractValue>,
pub shadow_size: usize, // for single-pass backend, 32 bytes on x86-64
pub diffs: Vec<MachineStateDiff>,
pub wasm_function_header_target_offset: Option<SuspendOffset>,
pub wasm_offset_to_target_offset: BTreeMap<usize, SuspendOffset>,
pub loop_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
pub call_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
pub trappable_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
}
#[derive(Clone, Copy, Debug)]
pub enum SuspendOffset {
Loop(usize),
Call(usize),
Trappable(usize),
}
#[derive(Clone, Debug)]
pub struct OffsetInfo {
pub diff_id: usize,
pub activate_offset: usize,
}
#[derive(Clone, Debug)]
pub struct ModuleStateMap {
pub local_functions: BTreeMap<usize, FunctionStateMap>,
pub total_size: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WasmFunctionStateDump {
pub local_function_id: usize,
pub wasm_inst_offset: usize,
pub stack: Vec<Option<u64>>,
pub locals: Vec<Option<u64>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ExecutionStateImage {
pub frames: Vec<WasmFunctionStateDump>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceImage {
pub memory: Option<Vec<u8>>,
pub globals: Vec<u64>,
pub execution_state: ExecutionStateImage,
}
impl ModuleStateMap {
fn lookup_call_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
if ip < base || ip - base >= self.total_size {
None
} else {
let (_, fsm) = self
.local_functions
.range((Unbounded, Included(&(ip - base))))
.last()
.unwrap();
match fsm.call_offsets.get(&(ip - base)) {
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
None => None,
}
}
}
fn lookup_trappable_ip(
&self,
ip: usize,
base: usize,
) -> Option<(&FunctionStateMap, MachineState)> {
if ip < base || ip - base >= self.total_size {
None
} else {
let (_, fsm) = self
.local_functions
.range((Unbounded, Included(&(ip - base))))
.last()
.unwrap();
match fsm.trappable_offsets.get(&(ip - base)) {
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
None => None,
}
}
}
fn lookup_loop_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
if ip < base || ip - base >= self.total_size {
None
} else {
let (_, fsm) = self
.local_functions
.range((Unbounded, Included(&(ip - base))))
.last()
.unwrap();
match fsm.loop_offsets.get(&(ip - base)) {
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
None => None,
}
}
}
}
impl FunctionStateMap {
pub fn new(
initial: MachineState,
local_function_id: usize,
shadow_size: usize,
locals: Vec<WasmAbstractValue>,
) -> FunctionStateMap {
FunctionStateMap {
initial,
local_function_id,
shadow_size,
locals,
diffs: vec![],
wasm_function_header_target_offset: None,
wasm_offset_to_target_offset: BTreeMap::new(),
loop_offsets: BTreeMap::new(),
call_offsets: BTreeMap::new(),
trappable_offsets: BTreeMap::new(),
}
}
}
impl MachineState {
pub fn diff(&self, old: &MachineState) -> MachineStateDiff {
let first_diff_stack_depth: usize = self
.stack_values
.iter()
.zip(old.stack_values.iter())
.enumerate()
.find(|&(_, (&a, &b))| a != b)
.map(|x| x.0)
.unwrap_or(old.stack_values.len().min(self.stack_values.len()));
assert_eq!(self.register_values.len(), old.register_values.len());
let reg_diff: Vec<_> = self
.register_values
.iter()
.zip(old.register_values.iter())
.enumerate()
.filter(|&(_, (&a, &b))| a != b)
.map(|(i, (&a, _))| (RegisterIndex(i), a))
.collect();
let first_diff_wasm_stack_depth: usize = self
.wasm_stack
.iter()
.zip(old.wasm_stack.iter())
.enumerate()
.find(|&(_, (&a, &b))| a != b)
.map(|x| x.0)
.unwrap_or(old.wasm_stack.len().min(self.wasm_stack.len()));
MachineStateDiff {
last: None,
stack_push: self.stack_values[first_diff_stack_depth..].to_vec(),
stack_pop: old.stack_values.len() - first_diff_stack_depth,
reg_diff: reg_diff,
wasm_stack_push: self.wasm_stack[first_diff_wasm_stack_depth..].to_vec(),
wasm_stack_pop: old.wasm_stack.len() - first_diff_wasm_stack_depth,
wasm_stack_private_depth: self.wasm_stack_private_depth,
wasm_inst_offset: self.wasm_inst_offset,
}
}
}
impl MachineStateDiff {
pub fn build_state(&self, m: &FunctionStateMap) -> MachineState {
let mut chain: Vec<&MachineStateDiff> = vec![];
chain.push(self);
let mut current = self.last;
while let Some(x) = current {
let that = &m.diffs[x];
current = that.last;
chain.push(that);
}
chain.reverse();
let mut state = m.initial.clone();
for x in chain {
for _ in 0..x.stack_pop {
state.stack_values.pop().unwrap();
}
for v in &x.stack_push {
state.stack_values.push(*v);
}
for &(index, v) in &x.reg_diff {
state.register_values[index.0] = v;
}
for _ in 0..x.wasm_stack_pop {
state.wasm_stack.pop().unwrap();
}
for v in &x.wasm_stack_push {
state.wasm_stack.push(*v);
}
}
state.wasm_stack_private_depth = self.wasm_stack_private_depth;
state.wasm_inst_offset = self.wasm_inst_offset;
state
}
}
impl ExecutionStateImage {
pub fn print_backtrace_if_needed(&self) {
use std::env;
if let Ok(x) = env::var("WASMER_BACKTRACE") {
if x == "1" {
eprintln!("{}", self.colored_output());
return;
}
}
eprintln!("Run with `WASMER_BACKTRACE=1` environment variable to display a backtrace.");
}
pub fn colored_output(&self) -> String {
use colored::*;
fn join_strings(x: impl Iterator<Item = String>, sep: &str) -> String {
let mut ret = String::new();
let mut first = true;
for s in x {
if first {
first = false;
} else {
ret += sep;
}
ret += &s;
}
ret
}
fn format_optional_u64_sequence(x: &[Option<u64>]) -> String {
if x.len() == 0 {
"(empty)".into()
} else {
join_strings(
x.iter().enumerate().map(|(i, x)| {
format!(
"[{}] = {}",
i,
x.map(|x| format!("{}", x))
.unwrap_or_else(|| "?".to_string())
.bold()
.cyan()
)
}),
", ",
)
}
}
let mut ret = String::new();
if self.frames.len() == 0 {
ret += &"Unknown fault address, cannot read stack.".yellow();
ret += "\n";
} else {
ret += &"Backtrace:".bold();
ret += "\n";
for (i, f) in self.frames.iter().enumerate() {
ret += &format!("* Frame {} @ Local function {}", i, f.local_function_id).bold();
ret += "\n";
ret += &format!(
" {} {}\n",
"Offset:".bold().yellow(),
format!("{}", f.wasm_inst_offset).bold().cyan(),
);
ret += &format!(
" {} {}\n",
"Locals:".bold().yellow(),
format_optional_u64_sequence(&f.locals)
);
ret += &format!(
" {} {}\n\n",
"Stack:".bold().yellow(),
format_optional_u64_sequence(&f.stack)
);
}
}
ret
}
}
impl InstanceImage {
pub fn from_bytes(input: &[u8]) -> Option<InstanceImage> {
use bincode::deserialize;
match deserialize(input) {
Ok(x) => Some(x),
Err(_) => None,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
use bincode::serialize;
serialize(self).unwrap()
}
}
#[cfg(all(unix, target_arch = "x86_64"))]
pub mod x64 {
use super::*;
use crate::codegen::BreakpointMap;
use crate::fault::{catch_unsafe_unwind, run_on_alternative_stack};
use crate::structures::TypedIndex;
use crate::types::LocalGlobalIndex;
use crate::vm::Ctx;
use std::any::Any;
pub fn new_machine_state() -> MachineState {
MachineState {
stack_values: vec![],
register_values: vec![MachineValue::Undefined; 16 + 8],
wasm_stack: vec![],
wasm_stack_private_depth: 0,
wasm_inst_offset: ::std::usize::MAX,
}
}
#[warn(unused_variables)]
pub unsafe fn invoke_call_return_on_stack(
msm: &ModuleStateMap,
code_base: usize,
image: InstanceImage,
vmctx: &mut Ctx,
breakpoints: Option<BreakpointMap>,
) -> Result<u64, Box<dyn Any>> {
let mut stack: Vec<u64> = vec![0; 1048576 * 8 / 8]; // 8MB stack
let mut stack_offset: usize = stack.len();
stack_offset -= 3; // placeholder for call return
let mut last_stack_offset: u64 = 0; // rbp
let mut known_registers: [Option<u64>; 24] = [None; 24];
let local_functions_vec: Vec<&FunctionStateMap> =
msm.local_functions.iter().map(|(_, v)| v).collect();
// Bottom to top
for f in image.execution_state.frames.iter().rev() {
let fsm = local_functions_vec[f.local_function_id];
let suspend_offset = if f.wasm_inst_offset == ::std::usize::MAX {
fsm.wasm_function_header_target_offset
} else {
fsm.wasm_offset_to_target_offset
.get(&f.wasm_inst_offset)
.map(|x| *x)
}
.expect("instruction is not a critical point");
let (activate_offset, diff_id) = match suspend_offset {
SuspendOffset::Loop(x) => fsm.loop_offsets.get(&x),
SuspendOffset::Call(x) => fsm.call_offsets.get(&x),
SuspendOffset::Trappable(x) => fsm.trappable_offsets.get(&x),
}
.map(|x| (x.activate_offset, x.diff_id))
.expect("offset cannot be found in table");
let diff = &fsm.diffs[diff_id];
let state = diff.build_state(fsm);
stack_offset -= 1;
stack[stack_offset] = stack.as_ptr().offset(last_stack_offset as isize) as usize as u64; // push rbp
last_stack_offset = stack_offset as _;
let mut got_explicit_shadow = false;
for v in state.stack_values.iter() {
match *v {
MachineValue::Undefined => stack_offset -= 1,
MachineValue::Vmctx => {
stack_offset -= 1;
stack[stack_offset] = vmctx as *mut Ctx as usize as u64;
}
MachineValue::PreserveRegister(index) => {
stack_offset -= 1;
stack[stack_offset] = known_registers[index.0].unwrap_or(0);
}
MachineValue::CopyStackBPRelative(byte_offset) => {
assert!(byte_offset % 8 == 0);
let target_offset = (byte_offset / 8) as isize;
let v = stack[(last_stack_offset as isize + target_offset) as usize];
stack_offset -= 1;
stack[stack_offset] = v;
}
MachineValue::ExplicitShadow => {
assert!(fsm.shadow_size % 8 == 0);
stack_offset -= fsm.shadow_size / 8;
got_explicit_shadow = true;
}
MachineValue::WasmStack(x) => {
stack_offset -= 1;
match state.wasm_stack[x] {
WasmAbstractValue::Const(x) => {
stack[stack_offset] = x;
}
WasmAbstractValue::Runtime => {
stack[stack_offset] = f.stack[x].unwrap();
}
}
}
MachineValue::WasmLocal(x) => {
stack_offset -= 1;
match fsm.locals[x] {
WasmAbstractValue::Const(x) => {
stack[stack_offset] = x;
}
WasmAbstractValue::Runtime => {
stack[stack_offset] = f.locals[x].unwrap();
}
}
}
}
}
if !got_explicit_shadow {
assert!(fsm.shadow_size % 8 == 0);
stack_offset -= fsm.shadow_size / 8;
}
for (i, v) in state.register_values.iter().enumerate() {
match *v {
MachineValue::Undefined => {}
MachineValue::Vmctx => {
known_registers[i] = Some(vmctx as *mut Ctx as usize as u64);
}
MachineValue::WasmStack(x) => match state.wasm_stack[x] {
WasmAbstractValue::Const(x) => {
known_registers[i] = Some(x);
}
WasmAbstractValue::Runtime => {
known_registers[i] = Some(f.stack[x].unwrap());
}
},
MachineValue::WasmLocal(x) => match fsm.locals[x] {
WasmAbstractValue::Const(x) => {
known_registers[i] = Some(x);
}
WasmAbstractValue::Runtime => {
known_registers[i] = Some(f.locals[x].unwrap());
}
},
_ => unreachable!(),
}
}
// no need to check 16-byte alignment here because it's possible that we're not at a call entry.
stack_offset -= 1;
stack[stack_offset] = (code_base + activate_offset) as u64; // return address
}
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R15).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R14).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R13).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R12).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R11).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R10).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R9).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R8).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RSI).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RDI).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RDX).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RCX).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RBX).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RAX).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] = stack.as_ptr().offset(last_stack_offset as isize) as usize as u64; // rbp
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM7).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM6).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM5).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM4).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM3).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM2).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM1).to_index().0].unwrap_or(0);
stack_offset -= 1;
stack[stack_offset] =
known_registers[X64Register::XMM(XMM::XMM0).to_index().0].unwrap_or(0);
if let Some(ref memory) = image.memory {
assert!(vmctx.internal.memory_bound <= memory.len());
if vmctx.internal.memory_bound < memory.len() {
let grow: unsafe extern "C" fn(ctx: &mut Ctx, memory_index: usize, delta: usize) =
::std::mem::transmute((*vmctx.internal.intrinsics).memory_grow);
grow(
vmctx,
0,
(memory.len() - vmctx.internal.memory_bound) / 65536,
);
assert_eq!(vmctx.internal.memory_bound, memory.len());
}
::std::slice::from_raw_parts_mut(
vmctx.internal.memory_base,
vmctx.internal.memory_bound,
)
.copy_from_slice(memory);
}
let globals_len = (*vmctx.module).info.globals.len();
for i in 0..globals_len {
(*(*vmctx.local_backing).globals[LocalGlobalIndex::new(i)].vm_local_global()).data =
image.globals[i];
}
drop(image); // free up host memory
catch_unsafe_unwind(
|| {
run_on_alternative_stack(
stack.as_mut_ptr().offset(stack.len() as isize),
stack.as_mut_ptr().offset(stack_offset as isize),
)
},
breakpoints,
)
}
pub fn build_instance_image(
vmctx: &mut Ctx,
execution_state: ExecutionStateImage,
) -> InstanceImage {
unsafe {
let memory = if vmctx.internal.memory_base.is_null() {
None
} else {
Some(
::std::slice::from_raw_parts(
vmctx.internal.memory_base,
vmctx.internal.memory_bound,
)
.to_vec(),
)
};
// FIXME: Imported globals
let globals_len = (*vmctx.module).info.globals.len();
let globals: Vec<u64> = (0..globals_len)
.map(|i| {
(*vmctx.local_backing).globals[LocalGlobalIndex::new(i)]
.get()
.to_u64()
})
.collect();
InstanceImage {
memory: memory,
globals: globals,
execution_state: execution_state,
}
}
}
#[warn(unused_variables)]
pub unsafe fn read_stack(
msm: &ModuleStateMap,
code_base: usize,
mut stack: *const u64,
initially_known_registers: [Option<u64>; 24],
mut initial_address: Option<u64>,
) -> ExecutionStateImage {
let mut known_registers: [Option<u64>; 24] = initially_known_registers;
let mut results: Vec<WasmFunctionStateDump> = vec![];
for _ in 0.. {
let ret_addr = initial_address.take().unwrap_or_else(|| {
let x = *stack;
stack = stack.offset(1);
x
});
let (fsm, state) = match msm
.lookup_call_ip(ret_addr as usize, code_base)
.or_else(|| msm.lookup_trappable_ip(ret_addr as usize, code_base))
.or_else(|| msm.lookup_loop_ip(ret_addr as usize, code_base))
{
Some(x) => x,
_ => return ExecutionStateImage { frames: results },
};
let mut wasm_stack: Vec<Option<u64>> = state
.wasm_stack
.iter()
.map(|x| match *x {
WasmAbstractValue::Const(x) => Some(x),
WasmAbstractValue::Runtime => None,
})
.collect();
let mut wasm_locals: Vec<Option<u64>> = fsm
.locals
.iter()
.map(|x| match *x {
WasmAbstractValue::Const(x) => Some(x),
WasmAbstractValue::Runtime => None,
})
.collect();
// This must be before the next loop because that modifies `known_registers`.
for (i, v) in state.register_values.iter().enumerate() {
match *v {
MachineValue::Undefined => {}
MachineValue::Vmctx => {}
MachineValue::WasmStack(idx) => {
if let Some(v) = known_registers[i] {
wasm_stack[idx] = Some(v);
} else {
eprintln!(
"BUG: Register {} for WebAssembly stack slot {} has unknown value.",
i, idx
);
}
}
MachineValue::WasmLocal(idx) => {
if let Some(v) = known_registers[i] {
wasm_locals[idx] = Some(v);
}
}
_ => unreachable!(),
}
}
let mut found_shadow = false;
for v in state.stack_values.iter() {
match *v {
MachineValue::ExplicitShadow => {
found_shadow = true;
break;
}
_ => {}
}
}
if !found_shadow {
stack = stack.offset((fsm.shadow_size / 8) as isize);
}
for v in state.stack_values.iter().rev() {
match *v {
MachineValue::ExplicitShadow => {
stack = stack.offset((fsm.shadow_size / 8) as isize);
}
MachineValue::Undefined => {
stack = stack.offset(1);
}
MachineValue::Vmctx => {
stack = stack.offset(1);
}
MachineValue::PreserveRegister(idx) => {
known_registers[idx.0] = Some(*stack);
stack = stack.offset(1);
}
MachineValue::CopyStackBPRelative(_) => {
stack = stack.offset(1);
}
MachineValue::WasmStack(idx) => {
wasm_stack[idx] = Some(*stack);
stack = stack.offset(1);
}
MachineValue::WasmLocal(idx) => {
wasm_locals[idx] = Some(*stack);
stack = stack.offset(1);
}
}
}
stack = stack.offset(1); // RBP
wasm_stack.truncate(
wasm_stack
.len()
.checked_sub(state.wasm_stack_private_depth)
.unwrap(),
);
let wfs = WasmFunctionStateDump {
local_function_id: fsm.local_function_id,
wasm_inst_offset: state.wasm_inst_offset,
stack: wasm_stack,
locals: wasm_locals,
};
results.push(wfs);
}
unreachable!();
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GPR {
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum XMM {
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
}
pub enum X64Register {
GPR(GPR),
XMM(XMM),
}
impl X64Register {
pub fn to_index(&self) -> RegisterIndex {
match *self {
X64Register::GPR(x) => RegisterIndex(x as usize),
X64Register::XMM(x) => RegisterIndex(x as usize + 16),
}
}
}
}

View File

@ -7,6 +7,8 @@
//! Variadic functions are not supported because `rax` is used by the trampoline code.
use crate::loader::CodeMemory;
use crate::vm::Ctx;
use std::fmt;
use std::{mem, slice};
lazy_static! {
@ -98,6 +100,46 @@ impl TrampolineBufferBuilder {
idx
}
pub fn add_context_rsp_state_preserving_trampoline(
&mut self,
target: unsafe extern "C" fn(&mut Ctx, *const CallContext, *const u64),
context: *const CallContext,
) -> usize {
let idx = self.offsets.len();
self.offsets.push(self.code.len());
self.code.extend_from_slice(&[
0x53, // push %rbx
0x41, 0x54, // push %r12
0x41, 0x55, // push %r13
0x41, 0x56, // push %r14
0x41, 0x57, // push %r15
]);
self.code.extend_from_slice(&[
0x48, 0xbe, // movabsq ?, %rsi
]);
self.code.extend_from_slice(value_to_bytes(&context));
self.code.extend_from_slice(&[
0x48, 0x89, 0xe2, // mov %rsp, %rdx
]);
self.code.extend_from_slice(&[
0x48, 0xb8, // movabsq ?, %rax
]);
self.code.extend_from_slice(value_to_bytes(&target));
self.code.extend_from_slice(&[
0xff, 0xd0, // callq *%rax
]);
self.code.extend_from_slice(&[
0x48, 0x81, 0xc4, // add ?, %rsp
]);
self.code.extend_from_slice(value_to_bytes(&40i32)); // 5 * 8
self.code.extend_from_slice(&[
0xc3, //retq
]);
idx
}
/// Adds a callinfo trampoline.
///
/// This generates a trampoline function that collects `num_params` parameters into an array
@ -196,6 +238,12 @@ impl TrampolineBuffer {
}
}
impl fmt::Debug for TrampolineBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TrampolineBuffer {{}}")
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -180,6 +180,10 @@ where
_phantom: PhantomData,
}
}
pub fn get_vm_func(&self) -> NonNull<vm::Func> {
self.f
}
}
impl<'a, Args, Rets> Func<'a, Args, Rets, Host>
@ -364,30 +368,35 @@ macro_rules! impl_traits {
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
#[allow(non_snake_case)]
fn to_raw(&self) -> NonNull<vm::Func> {
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
if mem::size_of::<Self>() == 0 {
/// This is required for the llvm backend to be able to unwind through this function.
#[cfg_attr(nightly, unwind(allowed))]
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
let f: FN = unsafe { mem::transmute_copy(&()) };
/// This is required for the llvm backend to be able to unwind through this function.
#[cfg_attr(nightly, unwind(allowed))]
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
let f: FN = unsafe { mem::transmute_copy(&()) };
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
})) {
Ok(Ok(returns)) => return returns.into_c_struct(),
Ok(Err(err)) => {
let b: Box<_> = err.into();
b as Box<dyn Any>
},
Err(err) => err,
};
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
})) {
Ok(Ok(returns)) => return returns.into_c_struct(),
Ok(Err(err)) => {
let b: Box<_> = err.into();
b as Box<dyn Any>
},
Err(err) => err,
};
unsafe {
(&*ctx.module).runnable_module.do_early_trap(err)
unsafe {
(&*ctx.module).runnable_module.do_early_trap(err)
}
}
}
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
} else {
assert_eq!(mem::size_of::<Self>(), mem::size_of::<usize>(), "you cannot use a closure that captures state for `Func`.");
NonNull::new(unsafe {
::std::mem::transmute_copy::<_, *mut vm::Func>(self)
}).unwrap()
}
}
}

View File

@ -38,8 +38,8 @@ pub struct Ctx {
/// These are pointers to things that are known to be owned
/// by the owning `Instance`.
local_backing: *mut LocalBacking,
import_backing: *mut ImportBacking,
pub local_backing: *mut LocalBacking,
pub import_backing: *mut ImportBacking,
pub module: *const ModuleInner,
//// This is intended to be user-supplied, per-instance
@ -100,6 +100,8 @@ pub struct InternalCtx {
pub memory_bound: usize,
pub internals: *mut [u64; INTERNALS_SIZE], // TODO: Make this dynamic?
pub interrupt_signal_mem: *mut u8,
}
static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0);
@ -207,6 +209,17 @@ fn get_intrinsics_for_module(m: &ModuleInfo) -> *const Intrinsics {
}
}
#[cfg(all(unix, target_arch = "x86_64"))]
fn get_interrupt_signal_mem() -> *mut u8 {
unsafe { crate::fault::get_wasm_interrupt_signal_mem() }
}
#[cfg(not(all(unix, target_arch = "x86_64")))]
fn get_interrupt_signal_mem() -> *mut u8 {
static mut REGION: u64 = 0;
unsafe { &mut REGION as *mut u64 as *mut u8 }
}
impl Ctx {
#[doc(hidden)]
pub unsafe fn new(
@ -245,6 +258,8 @@ impl Ctx {
memory_bound: mem_bound,
internals: &mut local_backing.internals.0,
interrupt_signal_mem: get_interrupt_signal_mem(),
},
local_functions: local_backing.local_functions.as_ptr(),
@ -296,6 +311,8 @@ impl Ctx {
memory_bound: mem_bound,
internals: &mut local_backing.internals.0,
interrupt_signal_mem: get_interrupt_signal_mem(),
},
local_functions: local_backing.local_functions.as_ptr(),
@ -419,9 +436,13 @@ impl Ctx {
12 * (mem::size_of::<usize>() as u8)
}
pub fn offset_local_functions() -> u8 {
pub fn offset_interrupt_signal_mem() -> u8 {
13 * (mem::size_of::<usize>() as u8)
}
pub fn offset_local_functions() -> u8 {
14 * (mem::size_of::<usize>() as u8)
}
}
enum InnerFunc {}
@ -640,6 +661,11 @@ mod vm_offset_tests {
offset_of!(InternalCtx => internals).get_byte_offset(),
);
assert_eq!(
Ctx::offset_interrupt_signal_mem() as usize,
offset_of!(InternalCtx => interrupt_signal_mem).get_byte_offset(),
);
assert_eq!(
Ctx::offset_local_functions() as usize,
offset_of!(Ctx => local_functions).get_byte_offset(),

View File

@ -18,3 +18,4 @@ nix = "0.13.0"
libc = "0.2.49"
smallvec = "0.6.9"
hashbrown = "0.1"
colored = "1.8"

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +1,5 @@
use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi};
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GPR {
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum XMM {
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
}
pub use wasmer_runtime_core::state::x64::{GPR, XMM};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Location {
@ -87,7 +54,7 @@ pub trait Emitter {
type Offset;
fn get_label(&mut self) -> Self::Label;
fn get_offset(&mut self) -> Self::Offset;
fn get_offset(&self) -> Self::Offset;
fn emit_u64(&mut self, x: u64);
@ -488,7 +455,7 @@ impl Emitter for Assembler {
self.new_dynamic_label()
}
fn get_offset(&mut self) -> AssemblyOffset {
fn get_offset(&self) -> AssemblyOffset {
self.offset()
}

View File

@ -22,7 +22,7 @@ extern crate smallvec;
mod codegen_x64;
mod emitter_x64;
mod machine;
mod protect_unix;
pub mod protect_unix;
pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator;
pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator;

View File

@ -1,6 +1,8 @@
use crate::emitter_x64::*;
use smallvec::SmallVec;
use std::collections::HashSet;
use wasmer_runtime_core::state::x64::X64Register;
use wasmer_runtime_core::state::*;
use wasmparser::Type as WpType;
struct MachineStackOffset(usize);
@ -10,6 +12,7 @@ pub struct Machine {
used_xmms: HashSet<XMM>,
stack_offset: MachineStackOffset,
save_area_offset: Option<MachineStackOffset>,
pub state: MachineState,
}
impl Machine {
@ -19,6 +22,7 @@ impl Machine {
used_xmms: HashSet::new(),
stack_offset: MachineStackOffset(0),
save_area_offset: None,
state: x64::new_machine_state(),
}
}
@ -129,13 +133,13 @@ impl Machine {
pub fn acquire_locations<E: Emitter>(
&mut self,
assembler: &mut E,
tys: &[WpType],
tys: &[(WpType, MachineValue)],
zeroed: bool,
) -> SmallVec<[Location; 1]> {
let mut ret = smallvec![];
let mut delta_stack_offset: usize = 0;
for ty in tys {
for (ty, mv) in tys {
let loc = match *ty {
WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM),
WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR),
@ -151,9 +155,14 @@ impl Machine {
};
if let Location::GPR(x) = loc {
self.used_gprs.insert(x);
self.state.register_values[X64Register::GPR(x).to_index().0] = *mv;
} else if let Location::XMM(x) = loc {
self.used_xmms.insert(x);
self.state.register_values[X64Register::XMM(x).to_index().0] = *mv;
} else {
self.state.stack_values.push(*mv);
}
self.state.wasm_stack.push(WasmAbstractValue::Runtime);
ret.push(loc);
}
@ -180,9 +189,13 @@ impl Machine {
match *loc {
Location::GPR(ref x) => {
assert_eq!(self.used_gprs.remove(x), true);
self.state.register_values[X64Register::GPR(*x).to_index().0] =
MachineValue::Undefined;
}
Location::XMM(ref x) => {
assert_eq!(self.used_xmms.remove(x), true);
self.state.register_values[X64Register::XMM(*x).to_index().0] =
MachineValue::Undefined;
}
Location::Memory(GPR::RBP, x) => {
if x >= 0 {
@ -194,9 +207,11 @@ impl Machine {
}
self.stack_offset.0 -= 8;
delta_stack_offset += 8;
self.state.stack_values.pop().unwrap();
}
_ => {}
}
self.state.wasm_stack.pop().unwrap();
}
if delta_stack_offset != 0 {
@ -213,12 +228,17 @@ impl Machine {
match *loc {
Location::GPR(ref x) => {
assert_eq!(self.used_gprs.remove(x), true);
self.state.register_values[X64Register::GPR(*x).to_index().0] =
MachineValue::Undefined;
}
Location::XMM(ref x) => {
assert_eq!(self.used_xmms.remove(x), true);
self.state.register_values[X64Register::XMM(*x).to_index().0] =
MachineValue::Undefined;
}
_ => {}
}
// Wasm state popping is deferred to `release_locations_only_osr_state`.
}
}
@ -241,9 +261,11 @@ impl Machine {
}
self.stack_offset.0 -= 8;
delta_stack_offset += 8;
self.state.stack_values.pop().unwrap();
}
_ => {}
}
// Wasm state popping is deferred to `release_locations_only_osr_state`.
}
if delta_stack_offset != 0 {
@ -255,6 +277,12 @@ impl Machine {
}
}
pub fn release_locations_only_osr_state(&mut self, n: usize) {
for _ in 0..n {
self.state.wasm_stack.pop().unwrap();
}
}
pub fn release_locations_keep_state<E: Emitter>(&self, assembler: &mut E, locs: &[Location]) {
let mut delta_stack_offset: usize = 0;
let mut stack_offset = self.stack_offset.0;
@ -314,7 +342,11 @@ impl Machine {
allocated += 1;
get_local_location(old_idx)
}
Location::Memory(_, _) => loc,
Location::Memory(_, _) => {
let old_idx = allocated;
allocated += 1;
get_local_location(old_idx)
}
_ => unreachable!(),
});
}
@ -325,6 +357,19 @@ impl Machine {
allocated += 1;
}
for (i, loc) in locations.iter().enumerate() {
match *loc {
Location::GPR(x) => {
self.state.register_values[X64Register::GPR(x).to_index().0] =
MachineValue::WasmLocal(i);
}
Location::Memory(_, _) => {
self.state.stack_values.push(MachineValue::WasmLocal(i));
}
_ => unreachable!(),
}
}
// How many machine stack slots did all the locals use?
let num_mem_slots = locations
.iter()
@ -346,15 +391,21 @@ impl Machine {
// Save callee-saved registers.
for loc in locations.iter() {
if let Location::GPR(_) = *loc {
if let Location::GPR(x) = *loc {
a.emit_push(Size::S64, *loc);
self.stack_offset.0 += 8;
self.state.stack_values.push(MachineValue::PreserveRegister(
X64Register::GPR(x).to_index(),
));
}
}
// Save R15 for vmctx use.
a.emit_push(Size::S64, Location::GPR(GPR::R15));
self.stack_offset.0 += 8;
self.state.stack_values.push(MachineValue::PreserveRegister(
X64Register::GPR(GPR::R15).to_index(),
));
// Save the offset of static area.
self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0));
@ -366,7 +417,17 @@ impl Machine {
Location::GPR(_) => {
a.emit_mov(Size::S64, loc, locations[i]);
}
_ => break,
Location::Memory(_, _) => match locations[i] {
Location::GPR(_) => {
a.emit_mov(Size::S64, loc, locations[i]);
}
Location::Memory(_, _) => {
a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX));
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), locations[i]);
}
_ => unreachable!(),
},
_ => unreachable!(),
}
}
@ -429,7 +490,11 @@ mod test {
fn test_release_locations_keep_state_nopanic() {
let mut machine = Machine::new();
let mut assembler = Assembler::new().unwrap();
let locs = machine.acquire_locations(&mut assembler, &[WpType::I32; 10], false);
let locs = machine.acquire_locations(
&mut assembler,
&[(WpType::I32, MachineValue::Undefined); 10],
false,
);
machine.release_locations_keep_state(&mut assembler, &locs);
}

View File

@ -9,77 +9,18 @@
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here
//! unless you have memory unsafety elsewhere in your code.
//!
use libc::{c_int, c_void, siginfo_t};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
SIGTRAP,
};
use std::any::Any;
use std::cell::{Cell, RefCell, UnsafeCell};
use std::collections::HashMap;
use std::ptr;
use std::sync::Arc;
use std::sync::Once;
use wasmer_runtime_core::codegen::BkptInfo;
use std::cell::Cell;
use wasmer_runtime_core::codegen::BreakpointMap;
use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
use wasmer_runtime_core::typed_func::WasmTrapInfo;
extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
siginfo: *mut siginfo_t,
ucontext: *mut c_void,
) {
unsafe {
match Signal::from_c_int(signum) {
Ok(SIGTRAP) => {
let (_, ip) = get_faulting_addr_and_ip(siginfo as _, ucontext);
let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone()));
if let Some(bkpt_map) = bkpt_map {
if let Some(ref x) = bkpt_map.get(&(ip as usize)) {
(x)(BkptInfo { throw: throw });
return;
}
}
}
_ => {}
}
do_unwind(signum, siginfo as _, ucontext);
}
}
extern "C" {
pub fn setjmp(env: *mut c_void) -> c_int;
fn longjmp(env: *mut c_void, val: c_int) -> !;
}
pub unsafe fn install_sighandler() {
let sa = SigAction::new(
SigHandler::SigAction(signal_trap_handler),
SaFlags::SA_ONSTACK,
SigSet::empty(),
);
sigaction(SIGFPE, &sa).unwrap();
sigaction(SIGILL, &sa).unwrap();
sigaction(SIGSEGV, &sa).unwrap();
sigaction(SIGBUS, &sa).unwrap();
sigaction(SIGTRAP, &sa).unwrap();
}
const SETJMP_BUFFER_LEN: usize = 27;
pub static SIGHANDLER_INIT: Once = Once::new();
thread_local! {
pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null()));
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
pub static BKPT_MAP: RefCell<Vec<Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>>> = RefCell::new(Vec::new());
}
pub unsafe fn trigger_trap() -> ! {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
longjmp(jmp_buf as *mut c_void, 0)
begin_unsafe_unwind(Box::new(()));
}
pub enum CallProtError {
@ -87,157 +28,26 @@ pub enum CallProtError {
Error(Box<dyn Any>),
}
pub fn call_protected<T>(f: impl FnOnce() -> T) -> Result<T, CallProtError> {
pub fn call_protected<T>(
f: impl FnOnce() -> T,
breakpoints: Option<BreakpointMap>,
) -> Result<T, CallProtError> {
ensure_sighandler();
unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
let prev_jmp_buf = *jmp_buf;
SIGHANDLER_INIT.call_once(|| {
install_sighandler();
});
let signum = setjmp(jmp_buf as *mut _);
if signum != 0 {
*jmp_buf = prev_jmp_buf;
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(CallProtError::Error(data))
} else {
// let (faulting_addr, _inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
// let signal = match Signal::from_c_int(signum) {
// Ok(SIGFPE) => "floating-point exception",
// Ok(SIGILL) => "illegal instruction",
// Ok(SIGSEGV) => "segmentation violation",
// Ok(SIGBUS) => "bus error",
// Err(_) => "error while getting the Signal",
// _ => "unknown trapped signal",
// };
// // When the trap-handler is fully implemented, this will return more information.
// Err(RuntimeError::Trap {
// msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
// }
// .into())
Err(CallProtError::Trap(WasmTrapInfo::Unknown))
let ret = catch_unsafe_unwind(|| f(), breakpoints);
match ret {
Ok(x) => Ok(x),
Err(e) => {
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(CallProtError::Error(data))
} else {
Err(CallProtError::Error(e))
}
}
} else {
let ret = f(); // TODO: Switch stack?
*jmp_buf = prev_jmp_buf;
Ok(ret)
}
}
}
pub unsafe fn throw(payload: Box<dyn Any>) -> ! {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
::std::process::abort();
}
TRAP_EARLY_DATA.with(|cell| cell.replace(Some(payload)));
longjmp(jmp_buf as *mut ::nix::libc::c_void, 0xffff);
}
/// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! {
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
// temporarily disable the signal handlers to debug it.
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
::std::process::abort();
}
CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext)));
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
unsafe fn get_faulting_addr_and_ip(
siginfo: *const c_void,
ucontext: *const c_void,
) -> (*const c_void, *const c_void) {
use libc::{ucontext_t, RIP};
#[allow(dead_code)]
#[repr(C)]
struct siginfo_t {
si_signo: i32,
si_errno: i32,
si_code: i32,
si_addr: u64,
// ...
}
let siginfo = siginfo as *const siginfo_t;
let si_addr = (*siginfo).si_addr;
let ucontext = ucontext as *const ucontext_t;
let rip = (*ucontext).uc_mcontext.gregs[RIP as usize];
(si_addr as _, rip as _)
}
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
unsafe fn get_faulting_addr_and_ip(
siginfo: *const c_void,
ucontext: *const c_void,
) -> (*const c_void, *const c_void) {
#[allow(dead_code)]
#[repr(C)]
struct ucontext_t {
uc_onstack: u32,
uc_sigmask: u32,
uc_stack: libc::stack_t,
uc_link: *const ucontext_t,
uc_mcsize: u64,
uc_mcontext: *const mcontext_t,
}
#[repr(C)]
struct exception_state {
trapno: u16,
cpu: u16,
err: u32,
faultvaddr: u64,
}
#[repr(C)]
struct regs {
rax: u64,
rbx: u64,
rcx: u64,
rdx: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rsp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
rip: u64,
rflags: u64,
cs: u64,
fs: u64,
gs: u64,
}
#[allow(dead_code)]
#[repr(C)]
struct mcontext_t {
es: exception_state,
ss: regs,
// ...
}
let siginfo = siginfo as *const siginfo_t;
let si_addr = (*siginfo).si_addr;
let ucontext = ucontext as *const ucontext_t;
let rip = (*(*ucontext).uc_mcontext).ss.rip;
(si_addr, rip as _)
begin_unsafe_unwind(payload);
}

View File

@ -145,3 +145,19 @@ Currently `cranelift_wasm::ModuleEnvironment` does not provide `declare_table_im
```
- `elem.wast`
- `SKIP_UNARY_OPERATION` [memory_grow.wast]
In some versions of MacOS this is failing (perhaps because of the chip).
More info here:
```
Executing function c82_l299_action_invoke
thread 'test_memory_grow::test_module_5' panicked at 'assertion failed: `(left == right)`
left: `Ok([I32(0)])`,
right: `Ok([I32(31)])`', /Users/distiller/project/target/release/build/wasmer-spectests-98805f54de053dd1/out/spectests.rs:32304:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
failures:
test_memory_grow::test_module_5
```
https://circleci.com/gh/wasmerio/wasmer/9556

View File

@ -296,7 +296,8 @@
(assert_return (invoke "as-storeN-address"))
(assert_return (invoke "as-storeN-value"))
(assert_return (invoke "as-unary-operand") (i32.const 31))
;; SKIP_UNARY_OPERATION
;; (assert_return (invoke "as-unary-operand") (i32.const 31))
(assert_return (invoke "as-binary-left") (i32.const 11))
(assert_return (invoke "as-binary-right") (i32.const 9))

View File

@ -19,6 +19,7 @@ log = "0.4.6"
byteorder = "1.3.1"
# hack to get tests to work
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.5.3", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.5.3", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
@ -33,3 +34,4 @@ wasmer-dev-utils = { path = "../dev-utils", version = "0.5.3"}
[features]
clif = []
singlepass = ["wasmer-singlepass-backend"]
llvm = ["wasmer-llvm-backend"]

View File

@ -1600,16 +1600,17 @@ pub fn path_open(
pub fn path_readlink(
ctx: &mut Ctx,
fd: __wasi_fd_t,
dir_fd: __wasi_fd_t,
path: WasmPtr<u8, Array>,
path_len: u32,
buf: WasmPtr<u8>,
buf: WasmPtr<u8, Array>,
buf_len: u32,
bufused: WasmPtr<u32>,
buf_used: WasmPtr<u32>,
) -> __wasi_errno_t {
debug!("wasi::path_readlink");
unimplemented!("wasi::path_readlink")
}
pub fn path_remove_directory(
ctx: &mut Ctx,
fd: __wasi_fd_t,

View File

@ -12,7 +12,8 @@ macro_rules! assert_wasi_output {
#[cfg(feature = "llvm")]
fn get_compiler() -> impl Compiler {
compile_error!("LLVM compiler not supported right now");
use wasmer_llvm_backend::LLVMCompiler;
LLVMCompiler::new()
}
#[cfg(feature = "singlepass")]

View File

@ -1,24 +1,16 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
extern crate byteorder;
extern crate structopt;
use std::thread;
use structopt::StructOpt;
use wasmer::*;
use wasmer_runtime::Value;
use wasmer_runtime_core::{
self,
backend::{CompilerConfig, MemoryBoundCheckMode},
loader::Instance as LoadedInstance,
};
#[cfg(feature = "loader:kernel")]
use wasmer_singlepass_backend::SinglePassCompiler;
use std::io::prelude::*;
#[cfg(feature = "loader:kernel")]
use std::os::unix::net::{UnixListener, UnixStream};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
#[derive(Debug, StructOpt)]
#[structopt(name = "kwasmd", about = "Kernel-mode WebAssembly service.")]
enum CLIOptions {
@ -32,12 +24,17 @@ struct Listen {
socket: String,
}
#[cfg(feature = "loader:kernel")]
const CMD_RUN_CODE: u32 = 0x901;
#[cfg(feature = "loader:kernel")]
const CMD_READ_MEMORY: u32 = 0x902;
#[cfg(feature = "loader:kernel")]
const CMD_WRITE_MEMORY: u32 = 0x903;
#[cfg(feature = "loader:kernel")]
fn handle_client(mut stream: UnixStream) {
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write};
let binary_size = stream.read_u32::<LittleEndian>().unwrap();
if binary_size > 1048576 * 16 {
println!("binary too large");
@ -46,6 +43,11 @@ fn handle_client(mut stream: UnixStream) {
let mut wasm_binary: Vec<u8> = Vec::with_capacity(binary_size as usize);
unsafe { wasm_binary.set_len(binary_size as usize) };
stream.read_exact(&mut wasm_binary).unwrap();
use wasmer::webassembly;
use wasmer_runtime_core::{
backend::{CompilerConfig, MemoryBoundCheckMode},
loader::Instance,
};
let module = webassembly::compile_with_config_with(
&wasm_binary[..],
CompilerConfig {
@ -80,6 +82,7 @@ fn handle_client(mut stream: UnixStream) {
println!("Too many arguments");
return;
}
use wasmer_runtime::Value;
let mut args: Vec<Value> = Vec::with_capacity(arg_count as usize);
for _ in 0..arg_count {
args.push(Value::I64(stream.read_u64::<LittleEndian>().unwrap() as _));
@ -131,6 +134,7 @@ fn handle_client(mut stream: UnixStream) {
#[cfg(feature = "loader:kernel")]
fn run_listen(opts: Listen) {
let listener = UnixListener::bind(&opts.socket).unwrap();
use std::thread;
for stream in listener.incoming() {
match stream {
Ok(stream) => {

View File

@ -19,7 +19,6 @@ use wasmer_clif_backend::CraneliftCompiler;
use wasmer_llvm_backend::LLVMCompiler;
use wasmer_runtime::{
cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH},
error::RuntimeError,
Func, Value,
};
use wasmer_runtime_core::{
@ -112,6 +111,10 @@ struct Run {
)]
loader: Option<LoaderName>,
#[cfg(feature = "backend:singlepass")]
#[structopt(long = "resume")]
resume: Option<String>,
#[structopt(long = "command-name", hidden = true)]
command_name: Option<String>,
@ -151,7 +154,7 @@ impl FromStr for LoaderName {
}
#[allow(dead_code)]
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
enum Backend {
Cranelift,
Singlepass,
@ -502,24 +505,96 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
mapped_dirs,
);
let instance = module
#[allow(unused_mut)] // mut used in feature
let mut instance = module
.instantiate(&import_object)
.map_err(|e| format!("Can't instantiate module: {:?}", e))?;
let start: Func<(), ()> = instance.func("_start").map_err(|e| format!("{:?}", e))?;
let result = start.call();
#[cfg(feature = "backend:singlepass")]
unsafe {
if options.backend == Backend::Singlepass {
use wasmer_runtime_core::fault::{catch_unsafe_unwind, ensure_sighandler};
use wasmer_runtime_core::state::{
x64::invoke_call_return_on_stack, InstanceImage,
};
use wasmer_runtime_core::vm::Ctx;
if let Err(ref err) = result {
match err {
RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg),
RuntimeError::Error { data } => {
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
std::process::exit(error_code.code as i32)
ensure_sighandler();
let start_raw: extern "C" fn(&mut Ctx) =
::std::mem::transmute(start.get_vm_func());
let mut image: Option<InstanceImage> = if let Some(ref path) = options.resume {
let mut f = File::open(path).unwrap();
let mut out: Vec<u8> = vec![];
f.read_to_end(&mut out).unwrap();
Some(InstanceImage::from_bytes(&out).expect("failed to decode image"))
} else {
None
};
let breakpoints = instance.module.runnable_module.get_breakpoints();
loop {
let ret = if let Some(image) = image.take() {
let msm = instance
.module
.runnable_module
.get_module_state_map()
.unwrap();
let code_base =
instance.module.runnable_module.get_code().unwrap().as_ptr()
as usize;
invoke_call_return_on_stack(
&msm,
code_base,
image,
instance.context_mut(),
breakpoints.clone(),
)
.map(|_| ())
} else {
catch_unsafe_unwind(
|| start_raw(instance.context_mut()),
breakpoints.clone(),
)
};
if let Err(e) = ret {
if let Some(new_image) = e.downcast_ref::<InstanceImage>() {
let op = interactive_shell(InteractiveShellContext {
image: Some(new_image.clone()),
});
match op {
ShellExitOperation::ContinueWith(new_image) => {
image = Some(new_image);
}
}
} else {
return Err("Error while executing WebAssembly".into());
}
} else {
return Ok(());
}
}
}
panic!("error: {:?}", err)
}
{
use wasmer_runtime::error::RuntimeError;
let result = start.call();
if let Err(ref err) = result {
match err {
RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg),
RuntimeError::Error { data } => {
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
std::process::exit(error_code.code as i32)
}
}
}
panic!("error: {:?}", err)
}
}
} else {
let import_object = wasmer_runtime_core::import::ImportObject::new();
@ -544,11 +619,95 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
Ok(())
}
#[cfg(feature = "backend:singlepass")]
struct InteractiveShellContext {
image: Option<wasmer_runtime_core::state::InstanceImage>,
}
#[cfg(feature = "backend:singlepass")]
#[derive(Debug)]
enum ShellExitOperation {
ContinueWith(wasmer_runtime_core::state::InstanceImage),
}
#[cfg(feature = "backend:singlepass")]
fn interactive_shell(mut ctx: InteractiveShellContext) -> ShellExitOperation {
use std::io::Write;
let mut stdout = ::std::io::stdout();
let stdin = ::std::io::stdin();
loop {
print!("Wasmer> ");
stdout.flush().unwrap();
let mut line = String::new();
stdin.read_line(&mut line).unwrap();
let mut parts = line.split(" ").filter(|x| x.len() > 0).map(|x| x.trim());
let cmd = parts.next();
if cmd.is_none() {
println!("Command required");
continue;
}
let cmd = cmd.unwrap();
match cmd {
"snapshot" => {
let path = parts.next();
if path.is_none() {
println!("Usage: snapshot [out_path]");
continue;
}
let path = path.unwrap();
if let Some(ref image) = ctx.image {
let buf = image.to_bytes();
let mut f = match File::create(path) {
Ok(x) => x,
Err(e) => {
println!("Cannot open output file at {}: {:?}", path, e);
continue;
}
};
if let Err(e) = f.write_all(&buf) {
println!("Cannot write to output file at {}: {:?}", path, e);
continue;
}
println!("Done");
} else {
println!("Program state not available");
}
}
"continue" | "c" => {
if let Some(image) = ctx.image.take() {
return ShellExitOperation::ContinueWith(image);
} else {
println!("Program state not available, cannot continue execution");
}
}
"backtrace" | "bt" => {
if let Some(ref image) = ctx.image {
println!("{}", image.execution_state.colored_output());
} else {
println!("State not available");
}
}
"exit" | "quit" => {
exit(0);
}
"" => {}
_ => {
println!("Unknown command: {}", cmd);
}
}
}
}
fn run(options: Run) {
match execute_wasm(&options) {
Ok(()) => {}
Err(message) => {
eprintln!("{:?}", message);
eprintln!("execute_wasm: {:?}", message);
exit(1);
}
}