Merge pull request #80 from wasmerio/feature/vm_refactor

VM Refactor, new runtime, and the ability to have multiple compilers.
This commit is contained in:
Lachlan Sneff 2019-01-21 17:23:11 -08:00 committed by GitHub
commit 62b8e7cc2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1182 changed files with 9357 additions and 289030 deletions

View File

@ -1,83 +1,127 @@
version: 2
jobs:
# Job used for testing
test:
lint:
docker:
- image: circleci/rust:latest
steps:
- checkout
- restore_cache:
keys:
- v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
- run: sudo apt-get install -y cmake
- run: make test
- run: rustup component add rustfmt
- run: make lint
- v5-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install dependencies
command: |
sudo apt-get install -y cmake
sudo apt-get install texinfo
sudo apt-get install libclang-dev llvm-3.9-dev libclang-3.9-dev clang-3.9
- run:
name: Install lint deps
command: |
rustup component add rustfmt
rustup component add clippy
- run:
name: Execute lints
command: make lint
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
test-macos:
macos:
xcode: "9.0"
key: v5-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
test:
docker:
- image: circleci/rust:latest
steps:
- checkout
- restore_cache:
keys:
- v4-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install CMAKE
- v5-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install dependencies
command: |
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
sudo apt-get install -y cmake
sudo apt-get install texinfo
sudo apt-get install libclang-dev llvm-3.9-dev libclang-3.9-dev clang-3.9
- run: make test
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: v5-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
test-macos:
macos:
xcode: "9.0"
steps:
- checkout
- restore_cache:
keys:
- v5-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
- v5-cargo-cache-darwin-{{ arch }}-brew
- run:
name: Update Brew
command: brew update || true
- run:
name: Install crate dependencies
command: |
brew install libtool || true
brew install autoconf cmake libffi || true
- save_cache:
paths:
- /usr/local/Homebrew
key: v5-cargo-cache-darwin-{{ arch }}-brew
- run:
name: Install Rust
command: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
rustup component add rustfmt
- run:
name: Execute tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
# 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
make lint
- run:
name: Execute integration tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
./integration_tests/nginx/test.sh
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: v5-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
test-and-build:
docker:
- image: circleci/rust:latest
steps:
- checkout
- restore_cache:
keys:
- v4-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
- run: sudo apt-get install -y cmake
- v5-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Install dependencies
command: |
sudo apt-get install -y cmake
sudo apt-get install texinfo
sudo apt-get install libclang-dev llvm-3.9-dev libclang-3.9-dev clang-3.9
- run:
name: Execute tests
command: make test
- run: rustup component add rustfmt
- run:
name: Execute lints
command: make lint
- run:
name: Make release build
command: |
@ -101,44 +145,47 @@ jobs:
- target/release/.fingerprint
- target/release/build
- target/release/deps
key: v4-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
- /usr/local/Homebrew
key: v5-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
test-and-build-macos:
macos:
xcode: "9.0"
steps:
- checkout
- restore_cache:
keys:
- v4-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
- v5-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
- v5-cargo-cache-darwin-{{ arch }}-brew
- run:
name: Install CMAKE
name: Update Brew
command: brew update || true
- run:
name: Install crate dependencies
command: |
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
brew install libtool || true
brew install autoconf cmake libffi || true
- save_cache:
paths:
- /usr/local/Homebrew
key: v5-cargo-cache-darwin-{{ arch }}-brew
- run:
name: Install Rust
command: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
rustup component add rustfmt
- run:
name: Execute tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
# 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
make lint
- run:
name: Make release build
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
make release
mkdir -p artifacts
@ -158,18 +205,22 @@ jobs:
- target/release/.fingerprint
- target/release/build
- target/release/deps
key: v4-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
key: v5-cargo-cache-darwin-{{ arch }}-{{ checksum "Cargo.lock" }}
test-rust-nightly:
docker:
- image: circleci/rust:latest
steps:
- checkout
- restore_cache:
keys:
- v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
- run: sudo apt-get install -y cmake
- v5-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
- run:
name: Install dependencies
command: |
sudo apt-get install -y cmake
sudo apt-get install texinfo
sudo apt-get install libclang-dev llvm-3.9-dev libclang-3.9-dev clang-3.9
- run: rustup default nightly
- run: make test
- save_cache:
@ -178,7 +229,7 @@ jobs:
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
key: v5-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}-nightly
publish-github-release:
docker:
@ -210,6 +261,7 @@ workflows:
version: 2
main:
jobs:
- lint
- test:
filters:
branches:
@ -230,15 +282,11 @@ workflows:
filters:
branches:
only: master
# :
# filters:
# tags:
# only: /^\d+\.\d+\.\d+$/
- publish-github-release:
requires:
- lint
- test-and-build
- test-and-build-macos
filters:
tags:
only: /^\d+\.\d+\.\d+$/
branches:
only: master

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
/target
**/target
**/*.rs.bk
/artifacts
.DS_Store

276
Cargo.lock generated
View File

@ -1,3 +1,8 @@
[[package]]
name = "abort_on_panic"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aho-corasick"
version = "0.6.8"
@ -53,6 +58,24 @@ dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bindgen"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"clang-sys 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.4"
@ -73,11 +96,29 @@ name = "cc"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cexpr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clang-sys"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
version = "2.32.0"
@ -253,6 +294,15 @@ name = "either"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno"
version = "0.2.4"
@ -300,6 +350,11 @@ dependencies = [
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "field-offset"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@ -324,6 +379,15 @@ name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hashbrown"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indicatif"
version = "0.10.3"
@ -341,6 +405,20 @@ name = "itoa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.2.0"
@ -356,6 +434,36 @@ name = "libc"
version = "0.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libffi"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"abort_on_panic 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"libffi-sys 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libffi-sys"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)",
"make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libloading"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lock_api"
version = "0.1.5"
@ -365,6 +473,14 @@ dependencies = [
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.5"
@ -381,6 +497,19 @@ dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "make-cmd"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.1.0"
@ -413,6 +542,14 @@ name = "nodrop"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nom"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.6"
@ -442,6 +579,16 @@ dependencies = [
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "page_size"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.7.0"
@ -463,6 +610,16 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pkg-config"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.20"
@ -471,6 +628,11 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.6.8"
@ -598,6 +760,18 @@ dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.0.5"
@ -610,6 +784,14 @@ dependencies = [
"utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.2"
@ -898,9 +1080,12 @@ dependencies = [
"docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (git+https://github.com/rust-lang/libc)",
"libffi 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -912,10 +1097,58 @@ dependencies = [
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.1.0",
"wasmer-emscripten 0.1.1",
"wasmer-runtime 0.1.0",
"wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmer-clif-backend"
version = "0.1.0"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libffi 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime 0.1.0",
"wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmer-emscripten"
version = "0.1.1"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.44 (git+https://github.com/rust-lang/libc)",
"time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.1.0",
"wasmer-runtime 0.1.0",
]
[[package]]
name = "wasmer-runtime"
version = "0.1.0"
dependencies = [
"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)",
"hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.1.0",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmparser"
version = "0.22.1"
@ -926,6 +1159,19 @@ name = "wasmparser"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "which"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
@ -935,6 +1181,11 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -946,17 +1197,21 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum abort_on_panic 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fa948f9ec9f095cc955efbe4fd00ac5774ef933cc2442562a8fe5a57c4ef919"
"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57253399c086f4f29e57ffd3b5cdbc23a806a00292619351aa4cfa39cb49d4ea"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
"checksum clang-sys 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e414af9726e1d11660801e73ccc7fb81803fb5f49e5903a25b348b2b3b480d2e"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum clicolors-control 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51872be694bb3bcbd1ea95c6dd467c2c46c6c64d287e1c9084ace7c3116ae9c0"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
@ -974,34 +1229,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c92df70dfaaabecc14b409fd79f55ba0f247780529db1d73bfa601e1d3ac0"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e"
"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64e9bc339e426139e02601fa69d101e96a92aee71b58bc01697ec2a63a5c9e68"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "64b7d419d0622ae02fe5da6b9a5e1964b610a65bb37923b976aeebb6dbb8f86e"
"checksum indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "40ecd1e2ee08e6c255ce890f5a99d17000850e664e7acf119fb03b25b0575bfe"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.44 (git+https://github.com/rust-lang/libc)" = "<none>"
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
"checksum libffi 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8a9dac273181f514d742b6b858be5153570c5b80dd4d6020093c0fa584578b1"
"checksum libffi-sys 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4675d5d7fdbba34b66218fae3b0d528c2b29580a64ca2ccc5bbfc5af2324b373"
"checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9"
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
"checksum mach 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
"checksum make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b"
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee"
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242"
"checksum parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9723236a9525c757d9725b993511e3fc941e33f27751942232f0058298297edf"
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
@ -1016,7 +1287,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
"checksum region 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9d3f2bb4b7085e6996e2765b56b783bd8f3a8a4ea5b95683063ca13cded993"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
@ -1055,6 +1328,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum wabt-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cc8982bfe0a923f152e96d991e50a6f97fe73ca4af6d9d84d76634f03051fa2"
"checksum wasmparser 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e666ecb4a406483a59a49f9d0c17f327e70da53a128eccddae2eadb95865c"
"checksum wasmparser 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5e01c420bc7d36e778bd242e1167b079562ba8b34087122cc9057187026d060"
"checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,13 +1,12 @@
[package]
name = "wasmer"
version = "0.1.4"
authors = ["Syrus Akbary <me@syrusakbary.com>"]
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
publish = true
description = "High-Performance WebAssembly JIT interpreter"
license = "MIT"
build = "build/mod.rs"
include = [
"examples/**/*",
"src/**/*",
@ -42,14 +41,22 @@ region = "0.3.0"
# spin = "0.4.10"
log = "0.4.5"
target-lexicon = "0.2.0"
# libc = "0.2"
libc = { git = "https://github.com/rust-lang/libc" }
nix = "0.12.0"
rayon = "1.0.3"
byteorder = "1"
indicatif = "0.10"
console = "0.7.1"
field-offset = "0.1.1"
hashbrown = "0.1"
libffi = "0.6.4"
time = "0.1.41"
wasmer-clif-backend = { path = "lib/clif-backend" }
wasmer-runtime = { path = "lib/runtime" }
wasmer-emscripten = { path = "lib/emscripten" }
libc = { git = "https://github.com/rust-lang/libc" }
[workspace]
members = ["lib/clif-backend", "lib/runtime", "lib/emscripten"]
[build-dependencies]
wabt = "0.7.2"

View File

@ -7,10 +7,10 @@ endif
# This will re-generate the Rust test files based on spectests/*.wast
spectests:
WASM_GENERATE_SPECTESTS=1 cargo build
WASMER_RUNTIME_GENERATE_SPECTESTS=1 cargo build -p wasmer-runtime
emtests:
WASM_GENERATE_EMTESTS=1 cargo build
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten
# clean:
# rm -rf artifacts
@ -21,14 +21,22 @@ build:
install:
cargo install --path .
integration-tests: release
echo "Running Integration Tests"
# Commented for now until we fix emscripten
# ./integration_tests/nginx/test.sh
lint:
cargo fmt -- --check
cargo fmt --all -- --check
cargo clippy --all
precommit: lint test
test:
# We use one thread so the emscripten stdouts doesn't collide
cargo test -- --test-threads=1 $(runargs)
# cargo test --all -- --test-threads=1 $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
cargo test -p wasmer-runtime -- --test-threads=1 $(runargs)
release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows

View File

@ -57,6 +57,13 @@ you can run:
make spectests
```
You can also run integration tests with:
```sh
make integration-tests
```
## Roadmap
Wasmer is an open project guided by strong principles, aiming to be modular, flexible and fast. It is open to the community to help set its direction.

View File

@ -1,19 +0,0 @@
extern crate glob;
extern crate wabt;
use std::env;
mod emtests;
mod spectests;
static SPECTESTS_ENV_VAR: &str = "WASM_GENERATE_SPECTESTS";
static EMTESTS_ENV_VAR: &str = "WASM_GENERATE_EMTESTS";
fn main() {
if env::var(SPECTESTS_ENV_VAR).unwrap_or("0".to_string()) == "1" {
spectests::build();
}
if env::var(EMTESTS_ENV_VAR).unwrap_or("0".to_string()) == "1" {
emtests::build();
}
}

View File

@ -1,7 +1,5 @@
#! /bin/bash
# Build the release and run nginx
make release
nohup ./target/release/wasmer run examples/nginx/nginx.wasm -- -p integration_tests/nginx/ -c nginx.conf &
sleep 3s

1
lib/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.Cargo.lock

View File

@ -0,0 +1,21 @@
[package]
name = "wasmer-clif-backend"
version = "0.1.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
[dependencies]
wasmer-runtime = { path = "../runtime" }
cranelift-native = "0.26.0"
cranelift-codegen = "0.26.0"
cranelift-entity = "0.26.0"
cranelift-wasm = "0.26.0"
hashbrown = "0.1"
target-lexicon = "0.2.0"
wasmparser = "0.23.0"
byteorder = "1"
nix = "0.12.0"
# We depend on libffi for now.
# This will be removed asap.
libffi = "0.6.4"

View File

@ -0,0 +1,138 @@
mod recovery;
mod sighandler;
pub use self::recovery::HandlerData;
use crate::call::recovery::call_protected;
use hashbrown::HashSet;
use libffi::high::{arg as libffi_arg, call as libffi_call, CodePtr};
use std::iter;
use wasmer_runtime::{
backend::{ProtectedCaller, Token},
error::RuntimeResult,
export::Context,
module::{ExportIndex, ModuleInner},
types::{FuncIndex, FuncSig, LocalOrImport, Type, Value},
vm::{self, ImportBacking},
};
pub struct Caller {
func_export_set: HashSet<FuncIndex>,
handler_data: HandlerData,
}
impl Caller {
pub fn new(module: &ModuleInner, handler_data: HandlerData) -> Self {
let mut func_export_set = HashSet::new();
for export_index in module.exports.values() {
if let ExportIndex::Func(func_index) = export_index {
func_export_set.insert(*func_index);
}
}
if let Some(start_func_index) = module.start_func {
func_export_set.insert(start_func_index);
}
Self {
func_export_set,
handler_data,
}
}
}
impl ProtectedCaller for Caller {
fn call(
&self,
module: &ModuleInner,
func_index: FuncIndex,
params: &[Value],
returns: &mut [Value],
import_backing: &ImportBacking,
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<()> {
let (func_ptr, ctx, signature) = get_func_from_index(&module, import_backing, func_index);
let vmctx_ptr = match ctx {
Context::External(external_vmctx) => external_vmctx,
Context::Internal => vmctx,
};
assert!(self.func_export_set.contains(&func_index));
assert!(
returns.len() == signature.returns.len() && signature.returns.len() <= 1,
"multi-value returns not yet supported"
);
assert!(signature.check_sig(params), "incorrect signature");
let libffi_args: Vec<_> = params
.iter()
.map(|val| match val {
Value::I32(ref x) => libffi_arg(x),
Value::I64(ref x) => libffi_arg(x),
Value::F32(ref x) => libffi_arg(x),
Value::F64(ref x) => libffi_arg(x),
})
.chain(iter::once(libffi_arg(&vmctx_ptr)))
.collect();
let code_ptr = CodePtr::from_ptr(func_ptr as _);
call_protected(&self.handler_data, || {
// Only supports zero or one return values for now.
// To support multiple returns, we will have to
// generate trampolines instead of using libffi.
match signature.returns.first() {
Some(ty) => {
let val = match ty {
Type::I32 => Value::I32(unsafe { libffi_call(code_ptr, &libffi_args) }),
Type::I64 => Value::I64(unsafe { libffi_call(code_ptr, &libffi_args) }),
Type::F32 => Value::F32(unsafe { libffi_call(code_ptr, &libffi_args) }),
Type::F64 => Value::F64(unsafe { libffi_call(code_ptr, &libffi_args) }),
};
returns[0] = val;
}
// call with no returns
None => unsafe {
libffi_call::<()>(code_ptr, &libffi_args);
},
}
})
}
}
fn get_func_from_index<'a>(
module: &'a ModuleInner,
import_backing: &ImportBacking,
func_index: FuncIndex,
) -> (*const vm::Func, Context, &'a FuncSig) {
let sig_index = *module
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
Context::Internal,
),
LocalOrImport::Import(imported_func_index) => {
let imported_func = import_backing.imported_func(imported_func_index);
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
}
};
let signature = module.sig_registry.lookup_func_sig(sig_index);
(func_ptr, ctx, signature)
}

View File

@ -0,0 +1,189 @@
//! When a WebAssembly module triggers any traps, we perform recovery here.
//!
//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling
//! 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 crate::call::sighandler::install_sighandler;
use crate::relocation::{TrapData, TrapSink};
use cranelift_codegen::ir::TrapCode;
use nix::libc::{c_void, siginfo_t};
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
use std::cell::{Cell, UnsafeCell};
use std::ptr;
use std::sync::Once;
use wasmer_runtime::{
error::{RuntimeError, RuntimeResult},
structures::TypedIndex,
types::{MemoryIndex, TableIndex},
};
extern "C" {
pub fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int;
fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !;
}
const SETJMP_BUFFER_LEN: usize = 27;
pub static SIGHANDLER_INIT: Once = Once::new();
thread_local! {
pub static SETJMP_BUFFER: UnsafeCell<[::nix::libc::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 struct HandlerData {
trap_data: TrapSink,
buffer_ptr: *const c_void,
buffer_size: usize,
}
impl HandlerData {
pub fn new(trap_data: TrapSink, buffer_ptr: *const c_void, buffer_size: usize) -> Self {
Self {
trap_data,
buffer_ptr,
buffer_size,
}
}
pub fn lookup(&self, ip: *const c_void) -> Option<TrapData> {
let ip = ip as usize;
let buffer_ptr = self.buffer_ptr as usize;
if buffer_ptr <= ip && ip < buffer_ptr + self.buffer_size {
let offset = ip - buffer_ptr;
self.trap_data.lookup(offset)
} else {
None
}
}
}
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
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 ::nix::libc::c_void);
if signum != 0 {
*jmp_buf = prev_jmp_buf;
let (faulting_addr, _) = CAUGHT_ADDRESSES.with(|cell| cell.get());
if let Some(TrapData {
trapcode,
srcloc: _,
}) = handler_data.lookup(faulting_addr)
{
Err(match Signal::from_c_int(signum) {
Ok(SIGILL) => match trapcode {
TrapCode::BadSignature => RuntimeError::IndirectCallSignature {
table: TableIndex::new(0),
},
TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull {
table: TableIndex::new(0),
},
TrapCode::HeapOutOfBounds => {
let addr =
(faulting_addr as usize) - (handler_data.buffer_ptr as usize);
if addr <= handler_data.buffer_size {
// in the memory
RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0),
addr: addr as u32,
}
} else {
// if there's an invalid access outside of the memory, including guard pages
// just kill the process.
panic!("invalid memory access, way out of bounds")
}
}
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds {
table: TableIndex::new(0),
},
_ => RuntimeError::Unknown {
msg: "unknown trap".to_string(),
},
},
Ok(SIGSEGV) | Ok(SIGBUS) => {
let addr = (faulting_addr as usize) - (handler_data.buffer_ptr as usize);
if addr <= handler_data.buffer_size {
// in the memory
RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0),
addr: addr as u32,
}
} else {
// if there's an invalid access outside of the memory, including guard pages
// just kill the process.
panic!("invalid memory access, way out of bounds")
}
}
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
_ => unimplemented!(),
}
.into())
} else {
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",
_ => "unkown trapped signal",
};
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown {
msg: format!("trap at {:p} - {}", faulting_addr, signal),
}
.into())
}
} else {
let ret = f(); // TODO: Switch stack?
*jmp_buf = prev_jmp_buf;
Ok(ret)
}
}
}
/// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32, siginfo: *mut siginfo_t, 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: *mut siginfo_t,
_ucontext: *const c_void,
) -> (*const c_void, *const c_void) {
(ptr::null(), ptr::null())
}
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
unsafe fn get_faulting_addr_and_ip(
siginfo: *mut siginfo_t,
_ucontext: *const c_void,
) -> (*const c_void, *const c_void) {
((*siginfo).si_addr, ptr::null())
}
#[cfg(not(any(
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "x86_64"),
)))]
compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");

View File

@ -1,10 +1,8 @@
//! We install signal handlers to handle WebAssembly traps within
//! our Rust code. Otherwise we will have errors that stop the Rust process
//! such as `process didn't exit successfully: ... (signal: 8, SIGFPE: erroneous arithmetic operation)`
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
//! accesses that occur when runniing webassembly.
//!
//! Please read more about this here: https://github.com/CraneStation/wasmtime/issues/15
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
use super::recovery;
use crate::call::recovery;
use nix::libc::{c_void, siginfo_t};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
@ -25,9 +23,9 @@ pub unsafe fn install_sighandler() {
extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
siginfo: *mut siginfo_t,
_ucontext: *mut c_void,
ucontext: *mut c_void,
) {
unsafe {
recovery::do_unwind(signum, siginfo);
recovery::do_unwind(signum, siginfo, ucontext);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
use crate::{module::Converter, module_env::ModuleEnv};
use cranelift_codegen::{
cursor::FuncCursor,
ir::{self, InstBuilder},
isa,
};
use cranelift_wasm::{self, FuncEnvironment, ModuleEnvironment};
use wasmer_runtime::{
memory::LinearMemory,
structures::TypedIndex,
types::{FuncIndex, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex},
vm,
};
pub struct FuncEnv<'env, 'module, 'isa> {
env: &'env ModuleEnv<'module, 'isa>,
}
impl<'env, 'module, 'isa> FuncEnv<'env, 'module, 'isa> {
pub fn new(env: &'env ModuleEnv<'module, 'isa>) -> Self {
Self { env }
}
/// Creates a signature with VMContext as the last param
pub fn generate_signature(&self, sig_index: cranelift_wasm::SignatureIndex) -> ir::Signature {
// Get signature
let mut signature = self.env.signatures[Converter(sig_index).into()].clone();
// Add the vmctx parameter type to it
signature.params.push(ir::AbiParam::special(
self.pointer_type(),
ir::ArgumentPurpose::VMContext,
));
// Return signature
signature
}
}
impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
/// Gets configuration information needed for compiling functions
fn target_config(&self) -> isa::TargetFrontendConfig {
self.env.target_config()
}
/// Gets native pointers types.
///
/// `I64` on 64-bit arch; `I32` on 32-bit arch.
fn pointer_type(&self) -> ir::Type {
ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap()
}
/// Gets the size of a native pointer in bytes.
fn pointer_bytes(&self) -> u8 {
self.target_config().pointer_bytes()
}
/// Sets up the necessary preamble definitions in `func` to access the global identified
/// by `index`.
///
/// The index space covers both imported and locally declared globals.
fn make_global(
&mut self,
func: &mut ir::Function,
clif_global_index: cranelift_wasm::GlobalIndex,
) -> cranelift_wasm::GlobalVariable {
let global_index: GlobalIndex = Converter(clif_global_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match global_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_global_index) => {
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_globals() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
let offset = local_global_index.index() * vm::LocalGlobal::size() as usize;
let local_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: globals_base_addr,
offset: (offset as i64).into(),
global_type: ptr_type,
});
// Create global variable based on the data above.
cranelift_wasm::GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.env.get_global(clif_global_index).ty,
}
}
LocalOrImport::Import(imported_global_index) => {
let imported_globals_base_addr =
func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_globals() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let offset = imported_global_index.index() * vm::ImportedGlobal::size() as usize;
let imported_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_globals_base_addr,
offset: (offset as i64).into(),
global_type: ptr_type,
});
let local_global_addr = func.create_global_value(ir::GlobalValueData::Load {
base: imported_global_addr,
offset: (vm::ImportedGlobal::offset_global() as i32).into(),
global_type: ptr_type,
readonly: true,
});
cranelift_wasm::GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.env.get_global(clif_global_index).ty,
}
}
}
}
/// Sets up the necessary preamble definitions in `func` to access the linear memory identified
/// by `index`.
///
/// The index space covers both imported and locally declared memories.
fn make_heap(
&mut self,
func: &mut ir::Function,
clif_mem_index: cranelift_wasm::MemoryIndex,
) -> ir::Heap {
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let memory_offset = local_mem_index.index() * vm::LocalMemory::size() as usize;
let memory_struct_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (memory_offset as i64).into(),
global_type: ptr_type,
});
let memory_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: memory_struct_addr,
offset: (vm::LocalMemory::offset_base() as i32).into(),
global_type: ptr_type,
readonly: true,
});
func.create_heap(ir::HeapData {
base: memory_base_addr,
min_size: (self.env.module.memories[local_mem_index].min as u64).into(),
offset_guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as u64).into(),
style: ir::HeapStyle::Static {
bound: (LinearMemory::DEFAULT_HEAP_SIZE as u64).into(),
},
index_type: ir::types::I32,
})
}
LocalOrImport::Import(imported_mem_index) => {
let imported_memories_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_memory_offset =
imported_mem_index.index() * vm::ImportedMemory::size() as usize;
let imported_memory_struct_addr =
func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_memories_base,
offset: (imported_memory_offset as i64).into(),
global_type: ptr_type,
});
let local_memory_struct_addr =
func.create_global_value(ir::GlobalValueData::Load {
base: imported_memory_struct_addr,
offset: (vm::ImportedMemory::offset_memory() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let local_memory_base = func.create_global_value(ir::GlobalValueData::Load {
base: local_memory_struct_addr,
offset: (vm::LocalMemory::offset_base() as i32).into(),
global_type: ptr_type,
readonly: true,
});
func.create_heap(ir::HeapData {
base: local_memory_base,
min_size: (self.env.module.imported_memories[imported_mem_index].1.min as u64)
.into(),
offset_guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as u64).into(),
style: ir::HeapStyle::Static {
bound: (LinearMemory::DEFAULT_HEAP_SIZE as u64).into(),
},
index_type: ir::types::I32,
})
}
}
}
/// Sets up the necessary preamble definitions in `func` to access the table identified
/// by `index`.
///
/// The index space covers both imported and locally declared tables.
fn make_table(
&mut self,
func: &mut ir::Function,
clif_table_index: cranelift_wasm::TableIndex,
) -> ir::Table {
let table_index: TableIndex = Converter(clif_table_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match table_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_table_index) => {
let tables_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_tables() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let table_struct_offset =
local_table_index.index() * vm::LocalTable::size() as usize;
let table_struct_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: tables_base,
offset: (table_struct_offset as i64).into(),
global_type: ptr_type,
});
let table_base = func.create_global_value(ir::GlobalValueData::Load {
base: table_struct_addr,
offset: (vm::LocalTable::offset_base() as i32).into(),
global_type: ptr_type,
// we will support growing tables, so this cannot be readonly.
readonly: false,
});
let table_bound = func.create_global_value(ir::GlobalValueData::Load {
base: table_struct_addr,
offset: (vm::LocalTable::offset_current_elements() as i32).into(),
// the number of elements in a table will always fit in an `i32`.
global_type: ir::types::I32,
readonly: false,
});
func.create_table(ir::TableData {
base_gv: table_base,
min_size: (self.env.module.tables[local_table_index].min as u64).into(),
bound_gv: table_bound,
element_size: (vm::Anyfunc::size() as u64).into(),
index_type: ir::types::I32,
})
}
LocalOrImport::Import(imported_table_index) => {
let imported_tables_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_tables() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_table_struct_offset =
imported_table_index.index() * vm::ImportedTable::size() as usize;
let imported_table_struct_addr =
func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_tables_base,
offset: (imported_table_struct_offset as i64).into(),
global_type: ptr_type,
});
let local_table_struct_addr = func.create_global_value(ir::GlobalValueData::Load {
base: imported_table_struct_addr,
offset: (vm::ImportedTable::offset_table() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let local_table_base = func.create_global_value(ir::GlobalValueData::Load {
base: local_table_struct_addr,
offset: (vm::LocalTable::offset_base() as i32).into(),
global_type: ptr_type,
readonly: false,
});
let local_table_bound = func.create_global_value(ir::GlobalValueData::Load {
base: local_table_struct_addr,
offset: (vm::LocalTable::offset_current_elements() as i32).into(),
global_type: ir::types::I32,
readonly: false,
});
func.create_table(ir::TableData {
base_gv: local_table_base,
min_size: (self.env.module.imported_tables[imported_table_index].1.min as u64)
.into(),
bound_gv: local_table_bound,
element_size: (vm::Anyfunc::size() as u64).into(),
index_type: ir::types::I32,
})
}
}
}
/// Sets up a signature definition in `func`'s preamble.
///
/// Signature may contain additional argument, but arguments marked as ArgumentPurpose::Normal`
/// must correspond to the arguments in the wasm signature
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: cranelift_wasm::SignatureIndex,
) -> ir::SigRef {
// Create a signature reference out of specified signature (with VMContext param added).
func.import_signature(self.generate_signature(index))
}
/// Sets up an external function definition in the preamble of `func` that can be used to
/// directly call the function `index`.
///
/// The index space covers both imported functions and functions defined in the current module.
fn make_direct_func(
&mut self,
func: &mut ir::Function,
func_index: cranelift_wasm::FuncIndex,
) -> ir::FuncRef {
// Get signature of function.
let signature_index = self.env.get_func_type(func_index);
// Create a signature reference from specified signature (with VMContext param added).
let signature = func.import_signature(self.generate_signature(signature_index));
// Get name of function.
let name = ir::ExternalName::user(0, func_index.as_u32());
// Create function reference from fuction data.
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
}
/// Generates an indirect call IR with `callee` and `call_args`.
///
/// Inserts instructions at `pos` to the function `callee` in the table
/// `table_index` with WebAssembly signature `sig_index`
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
fn translate_call_indirect(
&mut self,
mut pos: FuncCursor,
_table_index: cranelift_wasm::TableIndex,
table: ir::Table,
sig_index: cranelift_wasm::SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> {
// Get the pointer type based on machine's pointer size.
let ptr_type = self.pointer_type();
// The `callee` value is an index into a table of Anyfunc structures.
let entry_addr = pos.ins().table_addr(ptr_type, table, callee, 0);
let mflags = ir::MemFlags::trusted();
let func_ptr = pos.ins().load(
ptr_type,
mflags,
entry_addr,
vm::Anyfunc::offset_func() as i32,
);
let vmctx_ptr = pos.ins().load(
ptr_type,
mflags,
entry_addr,
vm::Anyfunc::offset_vmctx() as i32,
);
let found_sig = pos.ins().load(
ir::types::I32,
mflags,
entry_addr,
vm::Anyfunc::offset_sig_id() as i32,
);
pos.ins().trapz(func_ptr, ir::TrapCode::IndirectCallToNull);
let deduplicated_sig_index = self
.env
.module
.sig_registry
.lookup_deduplicated_sigindex(Converter(sig_index).into());
let expected_sig = pos
.ins()
.iconst(ir::types::I32, deduplicated_sig_index.index() as i64);
let not_equal_flags = pos.ins().ifcmp(found_sig, expected_sig);
pos.ins().trapif(
ir::condcodes::IntCC::NotEqual,
not_equal_flags,
ir::TrapCode::BadSignature,
);
// Build a value list for the indirect call instruction containing the call_args
// and the vmctx parameter.
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx_ptr);
Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args))
}
/// Generates a call IR with `callee` and `call_args` and inserts it at `pos`
/// TODO: add support for imported functions
fn translate_call(
&mut self,
mut pos: FuncCursor,
clif_callee_index: cranelift_wasm::FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> {
let callee_index: FuncIndex = Converter(clif_callee_index).into();
match callee_index.local_or_import(self.env.module) {
LocalOrImport::Local(_) => {
// this is an internal function
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx);
Ok(pos.ins().call(callee, &args))
}
LocalOrImport::Import(imported_func_index) => {
let ptr_type = self.pointer_type();
// this is an imported function
let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext);
let imported_funcs = pos.func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_funcs() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_func_offset =
imported_func_index.index() * vm::ImportedFunc::size() as usize;
let imported_func_struct_addr =
pos.func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_funcs,
offset: (imported_func_offset as i64).into(),
global_type: ptr_type,
});
let imported_func_addr = pos.func.create_global_value(ir::GlobalValueData::Load {
base: imported_func_struct_addr,
offset: (vm::ImportedFunc::offset_func() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_vmctx_addr = pos.func.create_global_value(ir::GlobalValueData::Load {
base: imported_func_struct_addr,
offset: (vm::ImportedFunc::offset_vmctx() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_func_addr = pos.ins().global_value(ptr_type, imported_func_addr);
let imported_vmctx_addr = pos.ins().global_value(ptr_type, imported_vmctx_addr);
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(imported_vmctx_addr);
Ok(pos
.ins()
.call_indirect(sig_ref, imported_func_addr, &args[..]))
}
}
}
/// Generates code corresponding to wasm `memory.grow`.
///
/// `index` refers to the linear memory to query.
///
/// `heap` refers to the IR generated by `make_heap`.
///
/// `val` refers the value to grow the memory by.
fn translate_memory_grow(
&mut self,
mut pos: FuncCursor,
clif_mem_index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
by_value: ir::Value,
) -> cranelift_wasm::WasmResult<ir::Value> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (name, mem_index) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
(
// local_static_memory_grow
ir::ExternalName::user(1, 0),
local_mem_index.index(),
)
}
LocalOrImport::Import(imported_mem_index) => {
(
// imported_static_memory_grow
ir::ExternalName::user(1, 2),
imported_mem_index.index(),
)
}
};
let mem_grow_func = pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
});
let const_mem_index = pos.ins().iconst(ir::types::I32, mem_index as i64);
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let call_inst = pos
.ins()
.call(mem_grow_func, &[const_mem_index, by_value, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
/// Generates code corresponding to wasm `memory.size`.
///
/// `index` refers to the linear memory to query.
///
/// `heap` refers to the IR generated by `make_heap`.
fn translate_memory_size(
&mut self,
mut pos: FuncCursor,
clif_mem_index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
) -> cranelift_wasm::WasmResult<ir::Value> {
// let signature = pos.func.import_signature(ir::Signature {
// call_conv: self.target_config().default_call_conv,
// params: vec![
// ir::AbiParam::new(ir::types::I32),
// ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
// ],
// returns: vec![ir::AbiParam::new(ir::types::I32)],
// });
// let size_mem_func = pos.func.import_function(ir::ExtFuncData {
// // `ir::ExternalName` for static_grow_memory`
// name: ir::ExternalName::user(1, 1),
// signature,
// colocated: false,
// });
// // Create a memory index value.
// let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
// // Create a VMContext value.
// let vmctx = pos
// .func
// .special_param(ir::ArgumentPurpose::VMContext)
// .expect("missing vmctx parameter");
// // Insert call instructions for `grow_memory`.
// let call_inst = pos.ins().call(size_mem_func, &[memory_index, vmctx]);
// // Return value.
// Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (name, mem_index) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
(
// local_static_memory_size
ir::ExternalName::user(1, 1),
local_mem_index.index(),
)
}
LocalOrImport::Import(imported_mem_index) => {
(
// imported_static_memory_size
ir::ExternalName::user(1, 3),
imported_mem_index.index(),
)
}
};
let mem_grow_func = pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
});
let const_mem_index = pos.ins().iconst(ir::types::I32, mem_index as i64);
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let call_inst = pos.ins().call(mem_grow_func, &[const_mem_index, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
}

View File

@ -0,0 +1,73 @@
// pub mod codegen;
mod call;
mod func_env;
mod libcalls;
mod module;
mod module_env;
mod relocation;
mod resolver;
use cranelift_codegen::{
isa,
settings::{self, Configurable},
};
use target_lexicon::Triple;
use wasmer_runtime::{
backend::{Compiler, Token},
error::{CompileError, CompileResult},
module::ModuleInner,
};
use wasmparser::{self, WasmDecoder};
pub struct CraneliftCompiler {}
impl CraneliftCompiler {
pub fn new() -> Self {
Self {}
}
}
impl Compiler for CraneliftCompiler {
// Compiles wasm binary to a wasmer module.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner> {
validate(wasm)?;
let isa = get_isa();
let mut module = module::Module::empty();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
module.compile(&*isa, func_bodies)
}
}
fn get_isa() -> Box<isa::TargetIsa> {
let flags = {
let mut builder = settings::builder();
builder.set("opt_level", "best").unwrap();
if cfg!(not(test)) {
builder.set("enable_verifier", "false").unwrap();
}
let flags = settings::Flags::new(builder);
debug_assert_eq!(flags.opt_level(), settings::OptLevel::Best);
flags
};
isa::lookup(Triple::host()).unwrap().finish(flags)
}
fn validate(bytes: &[u8]) -> CompileResult<()> {
let mut parser = wasmparser::ValidatingParser::new(bytes, None);
loop {
let state = parser.read();
match *state {
wasmparser::ParserState::EndWasm => break Ok(()),
wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError {
msg: err.message.to_string(),
})?,
_ => {}
}
}
}

View File

@ -0,0 +1,83 @@
use std::{f32, f64};
// F32
pub extern "C" fn ceilf32(x: f32) -> f32 {
x.ceil()
}
pub extern "C" fn floorf32(x: f32) -> f32 {
x.floor()
}
pub extern "C" fn truncf32(x: f32) -> f32 {
x.trunc()
}
/// `f32.round()` doesn't have the correct behavior. Ideally, we'd use
/// "https://doc.rust-lang.org/std/intrinsics/fn.nearbyintf32.html" for this,
/// but support for stable compilers is necessary, so we must implement
/// this ourselves.
/// This is ported from "https://github.com/rust-lang/rust/issues/55107#issuecomment-431247454"
pub extern "C" fn nearbyintf32(x: f32) -> f32 {
#[inline]
fn copysign(x: f32, y: f32) -> f32 {
let bitmask = y.to_bits() & (1 << 31);
f32::from_bits(x.to_bits() | bitmask)
}
if x.is_nan() {
f32::from_bits(x.to_bits() | (1 << 22))
} else {
let k = f32::EPSILON.recip();
let a = x.abs();
if a < k {
copysign((a + k) - k, x)
} else {
x
}
}
}
// F64
pub extern "C" fn ceilf64(x: f64) -> f64 {
x.ceil()
}
pub extern "C" fn floorf64(x: f64) -> f64 {
x.floor()
}
pub extern "C" fn truncf64(x: f64) -> f64 {
x.trunc()
}
/// `f64.round()` doesn't have the correct behavior. Ideally, we'd use
/// "https://doc.rust-lang.org/std/intrinsics/fn.nearbyintf64.html" for this,
/// but support for stable compilers is necessary, so we must implement
/// this ourselves.
/// This is ported from "https://github.com/rust-lang/rust/issues/55007#issuecomment-431247454"
pub extern "C" fn nearbyintf64(x: f64) -> f64 {
#[inline]
fn copysign(x: f64, y: f64) -> f64 {
let bitmask = y.to_bits() & (1 << 63);
f64::from_bits(x.to_bits() | bitmask)
}
if x.is_nan() {
f64::from_bits(x.to_bits() | (1 << 51))
} else {
let k = f64::EPSILON.recip();
let a = x.abs();
if a < k {
copysign((a + k) - k, x)
} else {
x
}
}
}
/// A declaration for the stack probe function in Rust's standard library, for
/// catching callstack overflow.
extern "C" {
pub fn __rust_probestack();
}

View File

@ -0,0 +1,180 @@
use crate::{call::Caller, resolver::FuncResolverBuilder};
use cranelift_codegen::{ir, isa};
use cranelift_entity::EntityRef;
use cranelift_wasm;
use hashbrown::HashMap;
use std::{
ops::{Deref, DerefMut},
ptr::NonNull,
};
use wasmer_runtime::{
backend::SigRegistry,
backend::{FuncResolver, ProtectedCaller, Token},
error::{CompileResult, RuntimeResult},
module::ModuleInner,
structures::{Map, TypedIndex},
types::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
Value,
},
vm::{self, ImportBacking},
};
struct Placeholder;
impl FuncResolver for Placeholder {
fn get(
&self,
_module: &ModuleInner,
_local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
None
}
}
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_returns: &mut [Value],
_import_backing: &ImportBacking,
_vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<()> {
Ok(())
}
}
/// This contains all of the items in a `ModuleInner` except the `func_resolver`.
pub struct Module {
pub module: ModuleInner,
}
impl Module {
pub fn empty() -> Self {
Self {
module: ModuleInner {
// this is a placeholder
func_resolver: Box::new(Placeholder),
protected_caller: Box::new(Placeholder),
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
imported_functions: Map::new(),
imported_memories: Map::new(),
imported_tables: Map::new(),
imported_globals: Map::new(),
exports: HashMap::new(),
data_initializers: Vec::new(),
elem_initializers: Vec::new(),
start_func: None,
func_assoc: Map::new(),
sig_registry: SigRegistry::new(),
},
}
}
pub fn compile(
mut self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<ModuleInner> {
// we have to deduplicate `module.func_assoc`
let func_assoc = &mut self.module.func_assoc;
let sig_registry = &self.module.sig_registry;
func_assoc.iter_mut().for_each(|(_, sig_index)| {
*sig_index = sig_registry.lookup_deduplicated_sigindex(*sig_index);
});
let (func_resolver_builder, handler_data) = FuncResolverBuilder::new(isa, functions)?;
self.module.func_resolver = Box::new(func_resolver_builder.finalize()?);
self.module.protected_caller = Box::new(Caller::new(&self.module, handler_data));
Ok(self.module)
}
}
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&self.module
}
}
impl DerefMut for Module {
fn deref_mut(&mut self) -> &mut ModuleInner {
&mut self.module
}
}
pub struct Converter<T>(pub T);
macro_rules! convert_clif_to_runtime_index {
($clif_index:ident, $runtime_index:ident) => {
impl From<Converter<cranelift_wasm::$clif_index>> for $runtime_index {
fn from(clif_index: Converter<cranelift_wasm::$clif_index>) -> Self {
$runtime_index::new(clif_index.0.index())
}
}
impl From<Converter<$runtime_index>> for cranelift_wasm::$clif_index {
fn from(runtime_index: Converter<$runtime_index>) -> Self {
cranelift_wasm::$clif_index::new(runtime_index.0.index())
}
}
};
($(($clif_index:ident: $runtime_index:ident),)*) => {
$(
convert_clif_to_runtime_index!($clif_index, $runtime_index);
)*
};
}
convert_clif_to_runtime_index![
(FuncIndex: FuncIndex),
(MemoryIndex: MemoryIndex),
(TableIndex: TableIndex),
(GlobalIndex: GlobalIndex),
(SignatureIndex: SigIndex),
];
impl<'a> From<Converter<&'a ir::Signature>> for FuncSig {
fn from(signature: Converter<&'a ir::Signature>) -> Self {
FuncSig {
params: signature
.0
.params
.iter()
.map(|param| Converter(param.value_type).into())
.collect(),
returns: signature
.0
.returns
.iter()
.map(|ret| Converter(ret.value_type).into())
.collect(),
}
}
}
impl From<Converter<ir::Type>> for Type {
fn from(ty: Converter<ir::Type>) -> Self {
match ty.0 {
ir::types::I32 => Type::I32,
ir::types::I64 => Type::I64,
ir::types::F32 => Type::F32,
ir::types::F64 => Type::F64,
_ => panic!("unsupported wasm type"),
}
}
}

View File

@ -0,0 +1,364 @@
use crate::{
func_env::FuncEnv,
module::{Converter, Module},
};
use cranelift_codegen::{ir, isa};
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
use wasmer_runtime::{
error::{CompileError, CompileResult},
module::{DataInitializer, ExportIndex, ImportName, TableInitializer},
structures::{Map, TypedIndex},
types::{
ElementType, Global, GlobalDesc, GlobalIndex, Initializer, LocalFuncIndex, LocalOrImport,
Memory, SigIndex, Table, Value,
},
};
pub struct ModuleEnv<'module, 'isa> {
pub module: &'module mut Module,
isa: &'isa isa::TargetIsa,
pub signatures: Map<SigIndex, ir::Signature>,
globals: Map<GlobalIndex, cranelift_wasm::Global>,
func_bodies: Map<LocalFuncIndex, ir::Function>,
}
impl<'module, 'isa> ModuleEnv<'module, 'isa> {
pub fn new(module: &'module mut Module, isa: &'isa isa::TargetIsa) -> Self {
Self {
module,
isa,
signatures: Map::new(),
globals: Map::new(),
func_bodies: Map::new(),
}
}
pub fn translate(mut self, wasm: &[u8]) -> CompileResult<Map<LocalFuncIndex, ir::Function>> {
translate_module(wasm, &mut self)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
Ok(self.func_bodies)
}
}
impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> {
/// Get the information needed to produce Cranelift IR for the current target.
fn target_config(&self) -> isa::TargetFrontendConfig {
self.isa.frontend_config()
}
/// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: &ir::Signature) {
self.signatures.push(sig.clone());
self.module.sig_registry.register(Converter(sig).into());
}
/// Return the signature with the given index.
fn get_signature(&self, sig_index: cranelift_wasm::SignatureIndex) -> &ir::Signature {
&self.signatures[Converter(sig_index).into()]
}
/// Declares a function import to the environment.
fn declare_func_import(
&mut self,
sig_index: cranelift_wasm::SignatureIndex,
namespace: &'data str,
name: &'data str,
) {
self.module.func_assoc.push(Converter(sig_index).into());
// Add import names to list of imported functions
self.module.imported_functions.push(ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
});
}
/// Return the number of imported funcs.
fn get_num_func_imports(&self) -> usize {
self.module.imported_functions.len()
}
/// Declares the type (signature) of a local function in the module.
fn declare_func_type(&mut self, sig_index: cranelift_wasm::SignatureIndex) {
self.module.func_assoc.push(Converter(sig_index).into());
}
/// Return the signature index for the given function index.
fn get_func_type(
&self,
func_index: cranelift_wasm::FuncIndex,
) -> cranelift_wasm::SignatureIndex {
Converter(self.module.func_assoc[Converter(func_index).into()]).into()
}
/// Declares a global to the environment.
fn declare_global(&mut self, global: cranelift_wasm::Global) {
use cranelift_wasm::GlobalInit;
let desc = GlobalDesc {
mutable: global.mutability,
ty: Converter(global.ty).into(),
};
let init = match global.initializer {
GlobalInit::I32Const(x) => Initializer::Const(Value::I32(x)),
GlobalInit::I64Const(x) => Initializer::Const(Value::I64(x)),
GlobalInit::F32Const(x) => Initializer::Const(Value::F32(f32::from_bits(x))),
GlobalInit::F64Const(x) => Initializer::Const(Value::F64(f64::from_bits(x))),
GlobalInit::GetGlobal(global_index) => {
assert!(!desc.mutable);
let global_index: GlobalIndex = Converter(global_index).into();
let imported_global_index = global_index
.local_or_import(self.module)
.import()
.expect("invalid global initializer when declaring an imported global");
Initializer::GetGlobal(imported_global_index)
}
_ => panic!("invalid global initializer when declaring a local global"),
};
// Add global ir to the list of globals
self.module.globals.push(Global { desc, init });
self.globals.push(global);
}
/// Declares a global import to the environment.
fn declare_global_import(
&mut self,
global: cranelift_wasm::Global,
namespace: &'data str,
name: &'data str,
) {
assert!(match global.initializer {
cranelift_wasm::GlobalInit::Import => true,
_ => false,
});
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let desc = GlobalDesc {
mutable: global.mutability,
ty: Converter(global.ty).into(),
};
// Add global ir to the list of globals
self.module.imported_globals.push((import_name, desc));
self.globals.push(global);
}
/// Return the global for the given global index.
fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global {
&self.globals[Converter(global_index).into()]
}
/// Declares a table to the environment.
fn declare_table(&mut self, table: cranelift_wasm::Table) {
use cranelift_wasm::TableElementType;
// Add table ir to the list of tables
self.module.tables.push(Table {
ty: match table.ty {
TableElementType::Func => ElementType::Anyfunc,
_ => unimplemented!(),
},
min: table.minimum,
max: table.maximum,
});
}
/// Declares a table import to the environment.
fn declare_table_import(
&mut self,
table: cranelift_wasm::Table,
namespace: &'data str,
name: &'data str,
) {
use cranelift_wasm::TableElementType;
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let imported_table = Table {
ty: match table.ty {
TableElementType::Func => ElementType::Anyfunc,
_ => unimplemented!(),
},
min: table.minimum,
max: table.maximum,
};
// Add import names to list of imported tables
self.module
.imported_tables
.push((import_name, imported_table));
}
/// Fills a declared table with references to functions in the module.
fn declare_table_elements(
&mut self,
table_index: cranelift_wasm::TableIndex,
base: Option<cranelift_wasm::GlobalIndex>,
offset: usize,
elements: Vec<cranelift_wasm::FuncIndex>,
) {
// Convert Cranelift GlobalIndex to wamser GlobalIndex
// let base = base.map(|index| WasmerGlobalIndex::new(index.index()));
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
}
})
}
None => Initializer::Const((offset as i32).into()),
};
// Add table initializer to list of table initializers
self.module.elem_initializers.push(TableInitializer {
table_index: Converter(table_index).into(),
base,
elements: elements
.iter()
.map(|&func_index| Converter(func_index).into())
.collect(),
});
}
/// Declares a memory to the environment
fn declare_memory(&mut self, memory: cranelift_wasm::Memory) {
self.module.memories.push(Memory {
min: memory.minimum,
max: memory.maximum,
shared: memory.shared,
});
}
/// Declares a memory import to the environment.
fn declare_memory_import(
&mut self,
memory: cranelift_wasm::Memory,
namespace: &'data str,
name: &'data str,
) {
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let memory = Memory {
min: memory.minimum,
max: memory.maximum,
shared: memory.shared,
};
// Add import names to list of imported memories
self.module.imported_memories.push((import_name, memory));
}
/// Fills a declared memory with bytes at module instantiation.
fn declare_data_initialization(
&mut self,
memory_index: cranelift_wasm::MemoryIndex,
base: Option<cranelift_wasm::GlobalIndex>,
offset: usize,
data: &'data [u8],
) {
// Convert Cranelift GlobalIndex to wamser GlobalIndex
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
}
})
}
None => Initializer::Const((offset as i32).into()),
};
// Add data initializer to list of data initializers
self.module.data_initializers.push(DataInitializer {
memory_index: Converter(memory_index).into(),
base,
data: data.to_vec(),
});
}
/// Declares a function export to the environment.
fn declare_func_export(&mut self, func_index: cranelift_wasm::FuncIndex, name: &'data str) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Func(Converter(func_index).into()),
);
}
/// Declares a table export to the environment.
fn declare_table_export(&mut self, table_index: cranelift_wasm::TableIndex, name: &'data str) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Table(Converter(table_index).into()),
);
}
/// Declares a memory export to the environment.
fn declare_memory_export(
&mut self,
memory_index: cranelift_wasm::MemoryIndex,
name: &'data str,
) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Memory(Converter(memory_index).into()),
);
}
/// Declares a global export to the environment.
fn declare_global_export(
&mut self,
global_index: cranelift_wasm::GlobalIndex,
name: &'data str,
) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Global(Converter(global_index).into()),
);
}
/// Declares a start function.
fn declare_start_func(&mut self, func_index: cranelift_wasm::FuncIndex) {
self.module.start_func = Some(Converter(func_index).into());
}
/// Provides the contents of a function body.
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> cranelift_wasm::WasmResult<()> {
let mut func_translator = FuncTranslator::new();
let func_body = {
let mut func_env = FuncEnv::new(self);
let func_index = self.func_bodies.next_index();
let name = ir::ExternalName::user(0, func_index.index() as u32);
let sig = func_env.generate_signature(
self.get_func_type(Converter(func_index.convert_up(self.module)).into()),
);
let mut func = ir::Function::with_name_signature(name, sig);
func_translator.translate(body_bytes, &mut func, &mut func_env)?;
func
};
// Add function body to list of function bodies.
self.func_bodies.push(func_body);
Ok(())
}
}

View File

@ -3,9 +3,10 @@
//! any other calls that this function is doing, so we can "patch" the
//! function addrs in runtime with the functions we need.
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode};
pub use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode};
use hashbrown::HashMap;
use wasmer_runtime::{structures::TypedIndex, types::LocalFuncIndex};
#[derive(Debug, Clone)]
pub struct Relocation {
@ -19,14 +20,21 @@ pub struct Relocation {
pub target: RelocationType,
}
#[derive(Debug, Clone, Copy)]
pub enum VmCall {
LocalStaticMemoryGrow,
LocalStaticMemorySize,
ImportedStaticMemoryGrow,
ImportedStaticMemorySize,
}
/// Specify the type of relocation
#[derive(Debug, Clone)]
pub enum RelocationType {
Normal(u32),
Normal(LocalFuncIndex),
Intrinsic(String),
LibCall(LibCall),
GrowMemory,
CurrentMemory,
VmCall(VmCall),
}
/// Implementation of a relocation sink that just saves all the information for later
@ -61,22 +69,35 @@ impl binemit::RelocSink for RelocSink {
reloc,
offset,
addend,
target: RelocationType::Normal(index as _),
target: RelocationType::Normal(LocalFuncIndex::new(index as usize)),
});
}
ExternalName::User {
namespace: 1,
index,
} => {
let target = RelocationType::VmCall(match index {
0 => VmCall::LocalStaticMemoryGrow,
1 => VmCall::LocalStaticMemorySize,
2 => VmCall::ImportedStaticMemoryGrow,
3 => VmCall::ImportedStaticMemorySize,
_ => unimplemented!(),
});
self.func_relocs.push(Relocation {
reloc,
offset,
addend,
target,
});
}
ExternalName::TestCase { length, ascii } => {
let (slice, _) = ascii.split_at(length as usize);
let name = String::from_utf8(slice.to_vec()).unwrap();
let relocation_type = match name.as_str() {
"current_memory" => RelocationType::CurrentMemory,
"grow_memory" => RelocationType::GrowMemory,
_ => RelocationType::Intrinsic(name),
};
self.func_relocs.push(Relocation {
reloc,
offset,
addend,
target: relocation_type,
target: RelocationType::Intrinsic(name),
});
}
ExternalName::LibCall(libcall) => {
@ -112,32 +133,50 @@ impl RelocSink {
}
}
#[derive(Debug, Clone, Copy)]
pub struct TrapData {
pub offset: usize,
pub code: TrapCode,
pub trapcode: TrapCode,
pub srcloc: SourceLoc,
}
/// Simple implementation of a TrapSink
/// that saves the info for later.
pub struct TrapSink {
current_func_offset: usize,
trap_datas: Vec<TrapData>,
trap_datas: HashMap<usize, TrapData>,
}
impl TrapSink {
pub fn new(current_func_offset: usize) -> TrapSink {
pub fn new() -> TrapSink {
TrapSink {
current_func_offset,
trap_datas: Vec::new(),
trap_datas: HashMap::new(),
}
}
}
impl binemit::TrapSink for TrapSink {
fn trap(&mut self, offset: u32, _: SourceLoc, code: TrapCode) {
self.trap_datas.push(TrapData {
offset: self.current_func_offset + offset as usize,
code,
pub fn lookup(&self, offset: usize) -> Option<TrapData> {
self.trap_datas.get(&offset).cloned()
}
pub fn drain_local(&mut self, current_func_offset: usize, local: &mut LocalTrapSink) {
local.trap_datas.drain(..).for_each(|(offset, trap_data)| {
self.trap_datas
.insert(current_func_offset + offset, trap_data);
});
}
}
pub struct LocalTrapSink {
trap_datas: Vec<(usize, TrapData)>,
}
impl LocalTrapSink {
pub fn new() -> Self {
LocalTrapSink { trap_datas: vec![] }
}
}
impl binemit::TrapSink for LocalTrapSink {
fn trap(&mut self, offset: u32, srcloc: SourceLoc, trapcode: TrapCode) {
self.trap_datas
.push((offset as usize, TrapData { trapcode, srcloc }));
}
}

View File

@ -0,0 +1,219 @@
use crate::call::HandlerData;
use crate::libcalls;
use crate::relocation::{
LocalTrapSink, Reloc, RelocSink, Relocation, RelocationType, TrapSink, VmCall,
};
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context};
use std::mem;
use std::ptr::{write_unaligned, NonNull};
use wasmer_runtime::{
self,
backend::{
self,
sys::{Memory, Protect},
},
error::{CompileError, CompileResult},
structures::Map,
types::LocalFuncIndex,
vm, vmcalls,
};
#[allow(dead_code)]
pub struct FuncResolverBuilder {
resolver: FuncResolver,
relocations: Map<LocalFuncIndex, Vec<Relocation>>,
}
impl FuncResolverBuilder {
pub fn new(
isa: &isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<(Self, HandlerData)> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
let mut relocations = Map::with_capacity(function_bodies.len());
let mut trap_sink = TrapSink::new();
let mut local_trap_sink = LocalTrapSink::new();
let mut ctx = Context::new();
let mut total_size = 0;
for (_, func) in function_bodies {
ctx.func = func;
let mut code_buf = Vec::new();
let mut reloc_sink = RelocSink::new();
ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut local_trap_sink)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
ctx.clear();
// Clear the local trap sink and consolidate all trap info
// into a single location.
trap_sink.drain_local(total_size, &mut local_trap_sink);
// Round up each function's size to pointer alignment.
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
compiled_functions.push(code_buf);
relocations.push(reloc_sink.func_relocs);
}
let mut memory = Memory::with_size(total_size)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
unsafe {
memory
.protect(0..memory.size(), Protect::ReadWrite)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
}
// Normally, excess memory due to alignment and page-rounding would
// be filled with null-bytes. On x86 (and x86_64),
// "\x00\x00" disassembles to "add byte ptr [eax],al".
//
// If the instruction pointer falls out of its designated area,
// it would be better if it would immediately crash instead of
// continuing on and causing non-local issues.
//
// "\xCC" disassembles to "int3", which will immediately cause
// an interrupt that we can catch if we want.
for i in unsafe { memory.as_slice_mut() } {
*i = 0xCC;
}
let mut map = Map::with_capacity(compiled_functions.len());
let mut previous_end = 0;
for compiled in compiled_functions.iter() {
let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>());
unsafe {
memory.as_slice_mut()[previous_end..previous_end + compiled.len()]
.copy_from_slice(&compiled[..]);
}
map.push(previous_end);
previous_end = new_end;
}
let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size());
Ok((
Self {
resolver: FuncResolver { map, memory },
relocations,
},
handler_data,
))
}
pub fn finalize(mut self) -> CompileResult<FuncResolver> {
for (index, relocs) in self.relocations.iter() {
for ref reloc in relocs {
let target_func_address: isize = match reloc.target {
RelocationType::Normal(local_func_index) => {
// This will always be an internal function
// because imported functions are not
// called in this way.
self.resolver.lookup(local_func_index).unwrap().as_ptr() as isize
}
RelocationType::LibCall(libcall) => match libcall {
ir::LibCall::CeilF32 => libcalls::ceilf32 as isize,
ir::LibCall::FloorF32 => libcalls::floorf32 as isize,
ir::LibCall::TruncF32 => libcalls::truncf32 as isize,
ir::LibCall::NearestF32 => libcalls::nearbyintf32 as isize,
ir::LibCall::CeilF64 => libcalls::ceilf64 as isize,
ir::LibCall::FloorF64 => libcalls::floorf64 as isize,
ir::LibCall::TruncF64 => libcalls::truncf64 as isize,
ir::LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
ir::LibCall::Probestack => libcalls::__rust_probestack as isize,
_ => Err(CompileError::InternalError {
msg: format!("unexpected libcall: {}", libcall),
})?,
},
RelocationType::Intrinsic(ref name) => Err(CompileError::InternalError {
msg: format!("unexpected intrinsic: {}", name),
})?,
RelocationType::VmCall(vmcall) => match vmcall {
VmCall::LocalStaticMemoryGrow => vmcalls::local_static_memory_grow as _,
VmCall::LocalStaticMemorySize => vmcalls::local_static_memory_size as _,
VmCall::ImportedStaticMemoryGrow => {
vmcalls::imported_static_memory_grow as _
}
VmCall::ImportedStaticMemorySize => {
vmcalls::imported_static_memory_size as _
}
},
};
// We need the address of the current function
// because these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr();
// Determine relocation type and apply relocation.
match reloc.reloc {
Reloc::Abs8 => {
let ptr_to_write = (target_func_address as u64)
.checked_add(reloc.addend as u64)
.unwrap();
let empty_space_offset = self.resolver.map[index] + reloc.offset as usize;
let ptr_slice = unsafe {
&mut self.resolver.memory.as_slice_mut()
[empty_space_offset..empty_space_offset + 8]
};
LittleEndian::write_u64(ptr_slice, ptr_to_write);
}
Reloc::X86PCRel4 => unsafe {
let reloc_address = func_addr.offset(reloc.offset as isize) as isize;
let reloc_addend = reloc.addend as isize;
// TODO: Handle overflow.
let reloc_delta_i32 =
(target_func_address - reloc_address + reloc_addend) as i32;
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
},
_ => Err(CompileError::InternalError {
msg: format!("unsupported reloc kind: {}", reloc.reloc),
})?,
}
}
}
unsafe {
self.resolver
.memory
.protect(0..self.resolver.memory.size(), Protect::ReadExec)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;;
}
Ok(self.resolver)
}
}
/// Resolves a function index to a function address.
pub struct FuncResolver {
map: Map<LocalFuncIndex, usize>,
memory: Memory,
}
impl FuncResolver {
fn lookup(&self, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self.map.get(local_func_index)?;
let ptr = unsafe { self.memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
}
// Implements FuncResolver trait.
impl backend::FuncResolver for FuncResolver {
fn get(
&self,
_module: &wasmer_runtime::module::ModuleInner,
index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
self.lookup(index)
}
}
#[inline]
fn round_up(n: usize, multiple: usize) -> usize {
(n + multiple - 1) & !(multiple - 1)
}

20
lib/emscripten/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "wasmer-emscripten"
version = "0.1.1"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
build = "build/mod.rs"
[dependencies]
hashbrown = "0.1"
wasmer-runtime = { path = "../runtime" }
libc = { git = "https://github.com/rust-lang/libc" }
byteorder = "1"
time = "0.1.41"
[dev-dependencies]
wasmer-clif-backend = { path = "../clif-backend" }
wabt = "0.7.2"
[build-dependencies]
glob = "0.2.11"

View File

@ -85,7 +85,7 @@ pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> {
let rs_module_name = module_name.to_lowercase();
let rust_test_filepath = format!(
concat!(env!("CARGO_MANIFEST_DIR"), "/src/emtests/{}.rs"),
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/emtests/{}.rs"),
rs_module_name.as_str()
);
@ -138,7 +138,7 @@ fn test_{rs_module_name}() {{
}
pub fn build() {
let rust_test_modpath = concat!(env!("CARGO_MANIFEST_DIR"), "/src/emtests/mod.rs");
let rust_test_modpath = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/emtests/mod.rs");
let mut modules: Vec<String> = Vec::new();
// modules.reserve_exact(TESTS.len());

View File

@ -0,0 +1,11 @@
use std::env;
mod emtests;
static EMTESTS_ENV_VAR: &str = "WASM_EMSCRIPTEN_GENERATE_EMTESTS";
fn main() {
if env::var(EMTESTS_ENV_VAR).unwrap_or("0".to_string()) == "1" {
emtests::build();
}
}

Some files were not shown because too many files have changed in this diff Show More