mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-12 22:05:33 +00:00
Merge remote-tracking branch 'origin/master' into command/dash
This commit is contained in:
commit
c827a6a993
@ -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
83
Cargo.lock
generated
@ -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
185
Makefile
@ -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/
|
||||
|
||||
|
37
README.md
37
README.md
@ -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
|
||||
|
@ -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
74
examples/iterative_hash/Cargo.lock
generated
Normal 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"
|
12
examples/iterative_hash/Cargo.toml
Normal file
12
examples/iterative_hash/Cargo.toml
Normal 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"
|
16
examples/iterative_hash/src/main.rs
Normal file
16
examples/iterative_hash/src/main.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
67
examples/trie_traversal/trie_traversal.rs
Normal file
67
examples/trie_traversal/trie_traversal.rs
Normal 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]");
|
||||
}
|
@ -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(),
|
||||
|
@ -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(())
|
||||
})),
|
||||
)),
|
||||
_ => {}
|
||||
|
@ -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));
|
||||
}
|
||||
_ => {}
|
||||
|
@ -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)
|
||||
|
@ -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 = []
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
69
lib/runtime-core/image-loading-linux-x86-64.s
Normal file
69
lib/runtime-core/image-loading-linux-x86-64.s
Normal 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
|
69
lib/runtime-core/image-loading-macos-x86-64.s
Normal file
69
lib/runtime-core/image-loading-macos-x86-64.s
Normal 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
|
@ -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.
|
||||
|
@ -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> {
|
||||
|
464
lib/runtime-core/src/fault.rs
Normal file
464
lib/runtime-core/src/fault.rs
Normal 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,
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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())
|
||||
|
840
lib/runtime-core/src/state.rs
Normal file
840
lib/runtime-core/src/state.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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::*;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
16
lib/spectests/spectests/README.md
vendored
16
lib/spectests/spectests/README.md
vendored
@ -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
|
3
lib/spectests/spectests/memory_grow.wast
vendored
3
lib/spectests/spectests/memory_grow.wast
vendored
@ -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))
|
||||
|
@ -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"]
|
||||
|
@ -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,
|
||||
|
@ -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")]
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user