Merge branch 'master' into feature/clif-cgapi

This commit is contained in:
Brandon Fish
2019-05-26 12:04:45 -05:00
132 changed files with 11302 additions and 1771 deletions

View File

@ -6,6 +6,7 @@ branches:
only: only:
- staging - staging
- trying - trying
- master
environment: environment:
matrix: matrix:
@ -17,6 +18,7 @@ environment:
cache: cache:
- 'C:\Users\appveyor\.cargo' - 'C:\Users\appveyor\.cargo'
- target - target
- wapm-cli-target
install: install:
# # Install LLVM # # Install LLVM
@ -47,7 +49,21 @@ install:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script: build_script:
- cargo build --release --verbose - cargo build --release --verbose
# Now we build wapm
- git submodule init
- git submodule update
# Cache wapm cli target in dir above to prevent breaking git submodule on windows
- if not exist wapm-cli-target mkdir wapm-cli-target
- move wapm-cli-target wapm-cli
- cd wapm-cli
- rename wapm-cli-target target
- cd ..
- cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
- cd wapm-cli
- rename target wapm-cli-target
- cd ..
- move wapm-cli\wapm-cli-target wapm-cli-target
test_script: test_script:
- cargo test --manifest-path lib/spectests/Cargo.toml --features clif - cargo test --manifest-path lib/spectests/Cargo.toml --features clif
@ -66,7 +82,7 @@ deploy:
description: 'WasmerInstaller' description: 'WasmerInstaller'
artifact: /.*\.exe/ artifact: /.*\.exe/
auth_token: auth_token:
secure: CaKtncy7S1PWxzDUQ0p2264pe3HwxzDn5VIyRizDaa72/SVfskNcoMjwwRh0ut22 secure: BbreGNDJy20922za7OhJG5TERzfX+dJSBQwttNTJkLvszbqMov6hhAtRb3P45hpf
provider: GitHub provider: GitHub
on: on:
branch: master branch: master

View File

@ -30,7 +30,6 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-lint-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-lint-{{ arch }}
- <<: *run_install_dependencies - <<: *run_install_dependencies
- run: - run:
name: Install lint deps name: Install lint deps
@ -60,7 +59,6 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-test-cargo-cache-linux-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-test-cargo-cache-linux-stable-{{ arch }}
- <<: *run_install_dependencies - <<: *run_install_dependencies
- run: - run:
name: Tests name: Tests
@ -89,7 +87,6 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-cargo-cache-darwin-stable-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-cargo-cache-darwin-stable-{{ arch }}
- run: - run:
name: Install crate dependencies name: Install crate dependencies
command: | command: |
@ -154,11 +151,10 @@ jobs:
name: "Pull dependencies" name: "Pull dependencies"
command: | command: |
git submodule init git submodule init
git submodule update --remote git submodule update
- restore_cache: - restore_cache:
keys: keys:
- v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-cargo-cache-linux-nightly-{{ arch }}
- run: - run:
name: Install dependencies name: Install dependencies
command: | command: |
@ -178,6 +174,10 @@ jobs:
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten-clif make test-emscripten-clif
make test-emscripten-llvm make test-emscripten-llvm
- run:
name: Debug flag checked
command: |
cargo check --features "debug"
- run: - run:
name: Release Build name: Release Build
command: | command: |
@ -191,10 +191,6 @@ jobs:
echo "${CIRCLE_TAG}" >> artifacts/git_version echo "${CIRCLE_TAG}" >> artifacts/git_version
make build-install make build-install
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh) cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
- run:
name: Debug flag checked
command: |
cargo check --features "debug"
- persist_to_workspace: - persist_to_workspace:
root: . root: .
paths: paths:
@ -222,11 +218,10 @@ jobs:
name: "Pull dependencies" name: "Pull dependencies"
command: | command: |
git submodule init git submodule init
git submodule update --remote git submodule update
- restore_cache: - restore_cache:
keys: keys:
- v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-cargo-cache-darwin-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-cargo-cache-darwin-nightly-{{ arch }}
- run: - run:
name: Install crate dependencies name: Install crate dependencies
command: | command: |
@ -309,13 +304,18 @@ jobs:
- restore_cache: - restore_cache:
keys: keys:
- v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }} - v8-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
- v8-cargo-cache-linux-nightly-{{ arch }}
- run: - run:
name: Install dependencies name: Install dependencies
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
rustup toolchain install nightly
rustup target add wasm32-wasi --toolchain nightly
- run: |
rustup default nightly
make test-wasi-singlepass
make test-wasi-clif
- run: rustup default nightly-2019-04-11 - run: rustup default nightly-2019-04-11
- run: | - run: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/" export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
@ -372,7 +372,6 @@ jobs:
-d build_parameters[CIRCLE_JOB]=bench \ -d build_parameters[CIRCLE_JOB]=bench \
https://circleci.com/api/v1.1/project/github/wasmerio/wasmer-bench/tree/master https://circleci.com/api/v1.1/project/github/wasmerio/wasmer-bench/tree/master
fi fi
workflows: workflows:
version: 2 version: 2
main: main:

View File

@ -6,6 +6,18 @@ Blocks of changes will separated by version increments.
## **[Unreleased]** ## **[Unreleased]**
- [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API
- [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed
- [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API
- [#459](https://github.com/wasmerio/wasmer/pull/459) Add monotonic and real time clocks for wasi on windows
- [#447](https://github.com/wasmerio/wasmer/pull/447) Add trace macro (`--features trace`) for more verbose debug statements
- [#451](https://github.com/wasmerio/wasmer/pull/451) Add `--mapdir=src:dest` flag to rename host directories in the guest context
- [#457](https://github.com/wasmerio/wasmer/pull/457) Implement file metadata for WASI, fix bugs in WASI clock code for Unix platforms
## 0.4.2 - 2019-05-16
- [#416](https://github.com/wasmerio/wasmer/pull/416) Remote code loading framework
- [#449](https://github.com/wasmerio/wasmer/pull/449) Fix bugs: opening host files in filestat and opening with write permissions unconditionally in path_open
- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir - [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir
- [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API. - [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API.
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs - [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
@ -13,7 +25,7 @@ Blocks of changes will separated by version increments.
- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements - [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements
- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits - [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits
## 0.4.1 - 2018-05-06 ## 0.4.1 - 2019-05-06
- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1 - [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm - [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
@ -23,7 +35,7 @@ Blocks of changes will separated by version increments.
- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule - [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule
- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path - [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path
## 0.4.0 - 2018-04-23 ## 0.4.0 - 2019-04-23
- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli. - [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli.
- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends - [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends
@ -41,7 +53,7 @@ Blocks of changes will separated by version increments.
- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI. - [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI.
- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md). - [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md).
## 0.3.0 - 2018-04-12 ## 0.3.0 - 2019-04-12
- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer) - [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer)
- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func. - [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func.

1521
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer" name = "wasmer"
version = "0.4.1" version = "0.4.2"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
repository = "https://github.com/wasmerio/wasmer" repository = "https://github.com/wasmerio/wasmer"
@ -19,6 +19,7 @@ include = [
] ]
[dependencies] [dependencies]
byteorder = "1.3.1"
errno = "0.2.4" errno = "0.2.4"
structopt = "0.2.11" structopt = "0.2.11"
wabt = "0.7.2" wabt = "0.7.2"
@ -27,14 +28,33 @@ wasmer-clif-backend = { path = "lib/clif-backend" }
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true } wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
wasmer-middleware-common = { path = "lib/middleware-common" } wasmer-middleware-common = { path = "lib/middleware-common" }
wasmer-runtime = { path = "lib/runtime" } wasmer-runtime = { path = "lib/runtime" }
wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true } # wasmer-runtime-abi = { path = "lib/runtime-abi", optional = true }
wasmer-runtime-core = { path = "lib/runtime-core" } wasmer-runtime-core = { path = "lib/runtime-core" }
wasmer-emscripten = { path = "lib/emscripten" } wasmer-emscripten = { path = "lib/emscripten" }
wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true }
wasmer-wasi = { path = "lib/wasi", optional = true } wasmer-wasi = { path = "lib/wasi", optional = true }
wasmer-kernel-loader = { path = "lib/kernel-loader", optional = true }
wasmer-dev-utils = { path = "lib/dev-utils", optional = true }
[workspace] [workspace]
members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi", "lib/middleware-common", "examples/plugin-for-example"] members = [
"lib/clif-backend",
"lib/singlepass-backend",
"lib/runtime",
# "lib/runtime-abi",
"lib/runtime-core",
"lib/emscripten",
"lib/spectests",
"lib/win-exception-handler",
"lib/runtime-c-api",
"lib/llvm-backend",
"lib/wasi",
"lib/middleware-common",
"lib/kernel-loader",
"lib/kernel-net",
"lib/dev-utils",
"examples/plugin-for-example"
]
[build-dependencies] [build-dependencies]
wabt = "0.7.2" wabt = "0.7.2"
@ -43,14 +63,16 @@ rustc_version = "0.2.3"
[features] [features]
default = ["fast-tests", "wasi"] default = ["fast-tests", "wasi"]
"loader:kernel" = ["wasmer-kernel-loader"]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]
trace = ["wasmer-runtime-core/trace"]
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"] extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster # This feature will allow cargo test to run much faster
fast-tests = [] fast-tests = []
"backend:llvm" = ["wasmer-llvm-backend"] "backend:llvm" = ["wasmer-llvm-backend"]
"backend:singlepass" = ["wasmer-singlepass-backend"] "backend:singlepass" = ["wasmer-singlepass-backend"]
wasi = ["wasmer-wasi"] wasi = ["wasmer-wasi"]
vfs = ["wasmer-runtime-abi"] # vfs = ["wasmer-runtime-abi"]
[[example]] [[example]]
name = "plugin" name = "plugin"

View File

@ -12,6 +12,9 @@ spectests:
emtests: emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten
wasitests:
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi
# clean: # clean:
# rm -rf artifacts # rm -rf artifacts
@ -45,7 +48,7 @@ do-install:
test: test:
# We use one thread so the emscripten stdouts doesn't collide # We use one thread so the emscripten stdouts doesn't collide
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend -- $(runargs) cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend --exclude wasmer-wasi -- $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/spectests/Cargo.toml --features clif cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
@ -66,14 +69,20 @@ test-emscripten-clif:
test-emscripten-singlepass: test-emscripten-singlepass:
cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs) cargo test --manifest-path lib/emscripten/Cargo.toml --features singlepass -- --test-threads=1 $(runargs)
test-wasi-clif:
cargo test --manifest-path lib/wasi/Cargo.toml --features "clif" -- --test-threads=1 $(runargs)
test-wasi-singlepass:
cargo test --manifest-path lib/wasi/Cargo.toml --features "singlepass" -- --test-threads=1 $(runargs)
singlepass-debug-release: singlepass-debug-release:
cargo +nightly build --features "backend:singlepass debug" --release cargo +nightly build --features backend:singlepass,debug --release
singlepass-release: singlepass-release:
cargo +nightly build --features "backend:singlepass" --release cargo +nightly build --features backend:singlepass --release
singlepass-build: singlepass-build:
cargo +nightly build --features "backend:singlepass debug" cargo +nightly build --features backend:singlepass,debug
release: release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows # If you are in OS-X, you will need mingw-w64 for cross compiling to windows
@ -81,13 +90,13 @@ release:
cargo build --release cargo build --release
production-release: production-release:
cargo build --release --features backend:singlepass,backend:llvm cargo build --release --features backend:singlepass,backend:llvm,loader:kernel
debug-release: debug-release:
cargo build --release --features "debug" cargo build --release --features debug
extra-debug-release: extra-debug-release:
cargo build --release --features "extra-debug" cargo build --release --features extra-debug
publish-release: publish-release:
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/ ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/

View File

@ -18,7 +18,7 @@
## Introduction ## Introduction
[Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with [WASI](https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/) and [Emscripten](https://emscripten.org/). [Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/).
Install Wasmer with: Install Wasmer with:

13
examples/echo-server/Cargo.lock generated Normal file
View File

@ -0,0 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "echo-server"
version = "0.1.0"
dependencies = [
"kernel-net 0.1.0",
]
[[package]]
name = "kernel-net"
version = "0.1.0"

View File

@ -0,0 +1,10 @@
[package]
name = "echo-server"
version = "0.1.0"
authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
[workspace]
[dependencies]
kernel-net = { path = "../../lib/kernel-net" }

View File

@ -0,0 +1,49 @@
#![feature(wasi_ext)]
use kernel_net::{schedule, Epoll, Tcp4Listener, TcpStream};
use std::sync::Arc;
fn do_echo(stream: Arc<TcpStream>, buf: Vec<u8>) {
let stream2 = stream.clone();
stream.read_async(buf, move |result| match result {
Ok(buf) => {
if buf.len() == 0 {
return;
}
let stream = stream2.clone();
stream2.write_all_async(buf, move |result| match result {
Ok(buf) => {
schedule(|| {
do_echo(stream, buf);
});
}
Err(code) => {
println!("failed to write; code = {}", code);
}
});
}
Err(code) => {
println!("failed to read; code = {}", code);
}
});
}
fn main() {
let epoll = Arc::new(Epoll::new());
let listener = Arc::new(Tcp4Listener::new("0.0.0.0", 2001, 128).unwrap());
listener.accept_async(epoll.clone(), |stream| match stream {
Ok(stream) => {
do_echo(stream, Vec::with_capacity(16384));
Ok(())
}
Err(code) => {
println!("failed to accept; code = {}", code);
Err(())
}
});
println!("start epoll");
unsafe {
epoll.run();
}
}

13
examples/http-server/Cargo.lock generated Normal file
View File

@ -0,0 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "http-server"
version = "0.1.0"
dependencies = [
"kernel-net 0.1.0",
]
[[package]]
name = "kernel-net"
version = "0.1.0"

View File

@ -0,0 +1,12 @@
[package]
name = "http-server"
version = "0.1.0"
authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
[dependencies]
kernel-net = { path = "../../lib/kernel-net" }

View File

@ -0,0 +1,72 @@
#![feature(wasi_ext)]
use kernel_net::{schedule, Epoll, Tcp4Listener, TcpStream};
use std::sync::Arc;
fn serve(stream: Arc<TcpStream>, mut all: Vec<u8>) {
let stream2 = stream.clone();
stream.read_async(
Vec::with_capacity(512),
move |result| {
match result {
Ok(buf) => {
if buf.len() == 0 {
return;
}
all.extend_from_slice(&buf);
if all.len() > 4096 {
println!("header too large");
return;
}
let s = match ::std::str::from_utf8(&all) {
Ok(x) => x,
Err(e) => {
println!("not utf8: {:?}", e);
return;
}
};
if let Some(_pos) = s.find("\r\n\r\n") {
let body = format!(
"Hello, world!\n",
).into_bytes();
let stream = stream2.clone();
stream2.write_all_async(
format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: {}\r\n\r\n",
body.len()
).into_bytes(),
|_| {
stream.write_all_async(body, |_| {});
}
);
} else {
schedule(|| serve(stream2, all));
}
}
Err(code) => {
println!("failed to read; code = {}", code);
}
}
}
);
}
fn main() {
let epoll = Arc::new(Epoll::new());
let listener = Arc::new(Tcp4Listener::new("0.0.0.0", 2011, 128).unwrap());
listener.accept_async(epoll.clone(), |stream| match stream {
Ok(stream) => {
serve(stream, vec![]);
Ok(())
}
Err(code) => {
println!("failed to accept; code = {}", code);
Err(())
}
});
println!("start epoll");
unsafe {
epoll.run();
}
}

9
examples/pipe/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "pipe"
version = "0.1.0"
authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
[workspace]
[dependencies]

13
examples/pipe/src/main.rs Normal file
View File

@ -0,0 +1,13 @@
use std::io::{Read, Write};
fn main() {
let mut stdin = ::std::io::stdin();
let mut stdout = ::std::io::stdout();
let mut buf: Vec<u8> = vec![0; 512];
let mut total: u64 = 0;
while total < 1048576u64 * 2048 {
let n = stdin.read(&mut buf).unwrap();
stdout.write_all(&buf[..n]).unwrap();
total += n as u64;
}
}

View File

@ -17,7 +17,7 @@ fn main() {
)); ));
// WASI imports // WASI imports
let mut base_imports = generate_import_object(vec![], vec![], vec![]); let mut base_imports = generate_import_object(vec![], vec![], vec![], vec![]);
// env is the default namespace for extern functions // env is the default namespace for extern functions
let custom_imports = imports! { let custom_imports = imports! {
"env" => { "env" => {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-clif-backend" name = "wasmer-clif-backend"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime Cranelift compiler backend" description = "Wasmer runtime Cranelift compiler backend"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,19 +8,12 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
# cranelift-native = { path = "../../../cranelift/cranelift-native" }
# cranelift-codegen = { path = "../../../cranelift/cranelift-codegen" }
# cranelift-entity = { path = "../../../cranelift/cranelift-entity" }
# cranelift-frontend = { path = "../../../cranelift/cranelift-frontend" }
# cranelift-wasm = { path = "../../../cranelift/cranelift-wasm" }
cranelift-native = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" } cranelift-native = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" }
cranelift-codegen = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" } cranelift-codegen = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" }
cranelift-entity = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" } cranelift-entity = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" }
cranelift-frontend = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" } cranelift-frontend = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" }
cranelift-wasm = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" } cranelift-wasm = { git = "https://github.com/wasmerio/cranelift.git", branch = "wasmer" }
hashbrown = "0.1" hashbrown = "0.1"
target-lexicon = "0.4.0" target-lexicon = "0.4.0"
wasmparser = "0.29.2" wasmparser = "0.29.2"
@ -41,7 +34,7 @@ version = "0.0.7"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.1" } wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.4.2" }
[features] [features]
debug = ["wasmer-runtime-core/debug"] debug = ["wasmer-runtime-core/debug"]

11
lib/dev-utils/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "wasmer-dev-utils"
version = "0.4.2"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
[dependencies]
libc = "0.2.49"

3
lib/dev-utils/README.md Normal file
View File

@ -0,0 +1,3 @@
# Dev Utils
This is shared code between the modules for testing and development only. Code in this crate will not be shipped.

2
lib/dev-utils/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod file_descriptor;
pub mod stdio;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-emscripten" name = "wasmer-emscripten"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime emscripten implementation library" description = "Wasmer runtime emscripten implementation library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,20 +9,21 @@ edition = "2018"
build = "build/mod.rs" build = "build/mod.rs"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
lazy_static = "1.2.0" lazy_static = "1.2.0"
libc = "0.2.49" libc = "0.2.49"
byteorder = "1" byteorder = "1"
time = "0.1.41" time = "0.1.41"
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
rand = "0.6" rand = "0.6"
[dev-dependencies] [dev-dependencies]
wabt = "0.7.2" wabt = "0.7.2"
wasmer-dev-utils = { path = "../dev-utils", version = "0.4.2"}
[build-dependencies] [build-dependencies]
glob = "0.2.11" glob = "0.2.11"

View File

@ -5,6 +5,7 @@
//! - Generate the test that will compare the output of running the .wasm file //! - Generate the test that will compare the output of running the .wasm file
//! with wasmer with the expected output //! with wasmer with the expected output
use glob::glob; use glob::glob;
use std::collections::HashSet;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -20,7 +21,7 @@ static BANNER: &str = "// Rust test file autogenerated with cargo build (build/e
const EXTENSIONS: [&str; 2] = ["c", "cpp"]; const EXTENSIONS: [&str; 2] = ["c", "cpp"];
const EXCLUDES: [&str; 0] = []; const EXCLUDES: [&str; 0] = [];
pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> { pub fn compile(file: &str, ignores: &HashSet<String>) -> Option<String> {
let mut output_path = PathBuf::from(file); let mut output_path = PathBuf::from(file);
output_path.set_extension("out"); output_path.set_extension("out");
// let output_str = output_path.to_str().unwrap(); // let output_str = output_path.to_str().unwrap();
@ -49,11 +50,7 @@ pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> {
let wasm_file_metadata = { let wasm_file_metadata = {
let mut wasm_file_path = PathBuf::from(file); let mut wasm_file_path = PathBuf::from(file);
wasm_file_path.set_extension("wasm"); wasm_file_path.set_extension("wasm");
if let Ok(wasm_file) = File::open(wasm_file_path) { File::open(wasm_file_path).and_then(|wf| wf.metadata()).ok()
Some(wasm_file.metadata().unwrap())
} else {
None
}
}; };
let real_file = File::open(file).unwrap(); let real_file = File::open(file).unwrap();
@ -111,10 +108,7 @@ pub fn compile(file: &str, ignores: &Vec<String>) -> Option<String> {
"txt" "txt"
}; };
let ignored = if ignores let ignored = if ignores.contains(&module_name.to_lowercase()) {
.iter()
.any(|i| &i.to_lowercase() == &module_name.to_lowercase())
{
"\n#[ignore]" "\n#[ignore]"
} else { } else {
"" ""
@ -193,8 +187,11 @@ pub fn build() {
} }
} }
fn read_ignore_list() -> Vec<String> { fn read_ignore_list() -> HashSet<String> {
let f = File::open("emtests/ignores.txt").unwrap(); let f = File::open("emtests/ignores.txt").unwrap();
let f = BufReader::new(f); let f = BufReader::new(f);
f.lines().filter_map(Result::ok).collect() f.lines()
.filter_map(Result::ok)
.map(|v| v.to_lowercase())
.collect()
} }

View File

@ -52,6 +52,18 @@
- **\_pthread_key_create** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis) - **\_pthread_key_create** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis)
```rust ```rust
```
- **\_pthread_rwlock_destroy** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis)
```rust
```
- **\_pthread_rwlock_init** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis)
```rust
```
- **\_pthread_rwlock_wrlock** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis)
```rust
``` ```
- **\_pthread_setspecific** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis) - **\_pthread_setspecific** &nbsp;&nbsp;&nbsp;&nbsp;[:top:](#host-apis)
```rust ```rust

View File

@ -15,6 +15,11 @@ pub fn getTempRet0(ctx: &mut Ctx) -> i32 {
get_emscripten_data(ctx).temp_ret_0 get_emscripten_data(ctx).temp_ret_0
} }
pub fn _alarm(_ctx: &mut Ctx, _seconds: u32) -> i32 {
debug!("emscripten::_alarm({})", _seconds);
0
}
pub fn _atexit(_ctx: &mut Ctx, _func: i32) -> i32 { pub fn _atexit(_ctx: &mut Ctx, _func: i32) -> i32 {
debug!("emscripten::_atexit"); debug!("emscripten::_atexit");
// TODO: implement atexit properly // TODO: implement atexit properly
@ -105,6 +110,18 @@ pub fn _pthread_key_create(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
debug!("emscripten::_pthread_key_create"); debug!("emscripten::_pthread_key_create");
0 0
} }
pub fn _pthread_rwlock_destroy(_ctx: &mut Ctx, _rwlock: i32) -> i32 {
debug!("emscripten::_pthread_rwlock_destroy({})", _rwlock);
0
}
pub fn _pthread_rwlock_init(_ctx: &mut Ctx, _rwlock: i32, _attr: i32) -> i32 {
debug!("emscripten::_pthread_rwlock_init({}, {})", _rwlock, _attr);
0
}
pub fn _pthread_rwlock_wrlock(_ctx: &mut Ctx, _rwlock: i32) -> i32 {
debug!("emscripten::_pthread_rwlock_wrlock({})", _rwlock);
0
}
pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 { pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 {
debug!("emscripten::_pthread_create"); debug!("emscripten::_pthread_create");
0 0
@ -185,6 +202,12 @@ pub fn ___gxx_personality_v0(
debug!("emscripten::___gxx_personality_v0"); debug!("emscripten::___gxx_personality_v0");
0 0
} }
pub fn _gai_strerror(_ctx: &mut Ctx, _ecode: i32) -> i32 {
debug!("emscripten::_gai_strerror({})", _ecode);
0
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 { pub fn _getdtablesize(_ctx: &mut Ctx) -> i32 {
debug!("emscripten::getdtablesize"); debug!("emscripten::getdtablesize");
@ -216,6 +239,22 @@ pub fn _getloadavg(_ctx: &mut Ctx, _loadavg: i32, _nelem: i32) -> i32 {
debug!("emscripten::getloadavg"); debug!("emscripten::getloadavg");
0 0
} }
pub fn _getnameinfo(
_ctx: &mut Ctx,
_addr: i32,
_addrlen: i32,
_host: i32,
_hostlen: i32,
_serv: i32,
_servlen: i32,
_flags: i32,
) -> i32 {
debug!(
"emscripten::_getnameinfo({}, {}, {}, {}, {}, {}, {})",
_addr, _addrlen, _host, _hostlen, _serv, _servlen, _flags
);
0
}
// Invoke functions // Invoke functions
// They save the stack to allow unwinding // They save the stack to allow unwinding

View File

@ -24,9 +24,6 @@ use wasmer_runtime_core::{
#[macro_use] #[macro_use]
mod macros; mod macros;
//#[cfg(test)]
mod file_descriptor;
pub mod stdio;
// EMSCRIPTEN APIS // EMSCRIPTEN APIS
mod bitwise; mod bitwise;
@ -47,6 +44,7 @@ mod signal;
mod storage; mod storage;
mod syscalls; mod syscalls;
mod time; mod time;
mod ucontext;
mod utils; mod utils;
mod varargs; mod varargs;
@ -725,6 +723,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_dlsym" => func!(crate::linking::_dlsym), "_dlsym" => func!(crate::linking::_dlsym),
// wasm32-unknown-emscripten // wasm32-unknown-emscripten
"_alarm" => func!(crate::emscripten_target::_alarm),
"_atexit" => func!(crate::emscripten_target::_atexit), "_atexit" => func!(crate::emscripten_target::_atexit),
"setTempRet0" => func!(crate::emscripten_target::setTempRet0), "setTempRet0" => func!(crate::emscripten_target::setTempRet0),
"getTempRet0" => func!(crate::emscripten_target::getTempRet0), "getTempRet0" => func!(crate::emscripten_target::getTempRet0),
@ -775,11 +774,16 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_pthread_setspecific" => func!(crate::emscripten_target::_pthread_setspecific), "_pthread_setspecific" => func!(crate::emscripten_target::_pthread_setspecific),
"_pthread_once" => func!(crate::emscripten_target::_pthread_once), "_pthread_once" => func!(crate::emscripten_target::_pthread_once),
"_pthread_key_create" => func!(crate::emscripten_target::_pthread_key_create), "_pthread_key_create" => func!(crate::emscripten_target::_pthread_key_create),
"_pthread_rwlock_destroy" => func!(crate::emscripten_target::_pthread_rwlock_destroy),
"_pthread_rwlock_init" => func!(crate::emscripten_target::_pthread_rwlock_init),
"_pthread_rwlock_wrlock" => func!(crate::emscripten_target::_pthread_rwlock_wrlock),
"___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0), "___gxx_personality_v0" => func!(crate::emscripten_target::___gxx_personality_v0),
"_gai_strerror" => func!(crate::emscripten_target::_gai_strerror),
"_getdtablesize" => func!(crate::emscripten_target::_getdtablesize), "_getdtablesize" => func!(crate::emscripten_target::_getdtablesize),
"_gethostbyaddr" => func!(crate::emscripten_target::_gethostbyaddr), "_gethostbyaddr" => func!(crate::emscripten_target::_gethostbyaddr),
"_gethostbyname_r" => func!(crate::emscripten_target::_gethostbyname_r), "_gethostbyname_r" => func!(crate::emscripten_target::_gethostbyname_r),
"_getloadavg" => func!(crate::emscripten_target::_getloadavg), "_getloadavg" => func!(crate::emscripten_target::_getloadavg),
"_getnameinfo" => func!(crate::emscripten_target::_getnameinfo),
"invoke_dii" => func!(crate::emscripten_target::invoke_dii), "invoke_dii" => func!(crate::emscripten_target::invoke_dii),
"invoke_diiii" => func!(crate::emscripten_target::invoke_diiii), "invoke_diiii" => func!(crate::emscripten_target::invoke_diiii),
"invoke_iiiii" => func!(crate::emscripten_target::invoke_iiiii), "invoke_iiiii" => func!(crate::emscripten_target::invoke_iiiii),
@ -819,6 +823,12 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"invoke_viid" => func!(crate::emscripten_target::invoke_viid), "invoke_viid" => func!(crate::emscripten_target::invoke_viid),
"invoke_viidii" => func!(crate::emscripten_target::invoke_viidii), "invoke_viidii" => func!(crate::emscripten_target::invoke_viidii),
"invoke_viidddddddd" => func!(crate::emscripten_target::invoke_viidddddddd), "invoke_viidddddddd" => func!(crate::emscripten_target::invoke_viidddddddd),
// ucontext
"_getcontext" => func!(crate::ucontext::_getcontext),
"_makecontext" => func!(crate::ucontext::_makecontext),
"_setcontext" => func!(crate::ucontext::_setcontext),
"_swapcontext" => func!(crate::ucontext::_swapcontext),
}; };
for null_func_name in globals.null_func_names.iter() { for null_func_name in globals.null_func_names.iter() {

View File

@ -0,0 +1,20 @@
use wasmer_runtime_core::vm::Ctx;
pub fn _getcontext(_ctx: &mut Ctx, _ucp: i32) -> i32 {
debug!("emscripten::_getcontext({})", _ucp);
0
}
pub fn _makecontext(_ctx: &mut Ctx, _ucp: i32, _func: i32, _argc: i32, _argv: i32) {
debug!(
"emscripten::_makecontext({}, {}, {}, {})",
_ucp, _func, _argc, _argv
);
}
pub fn _setcontext(_ctx: &mut Ctx, _ucp: i32) -> i32 {
debug!("emscripten::_setcontext({})", _ucp);
0
}
pub fn _swapcontext(_ctx: &mut Ctx, _oucp: i32, _ucp: i32) -> i32 {
debug!("emscripten::_swapcontext({}, {})", _oucp, _ucp);
0
}

View File

@ -4,9 +4,11 @@ macro_rules! assert_emscripten_output {
use wasmer_emscripten::{ use wasmer_emscripten::{
EmscriptenGlobals, EmscriptenGlobals,
generate_emscripten_env, generate_emscripten_env,
stdio::StdioCapturer
}; };
use wasmer_runtime_core::backend::Compiler; use wasmer_runtime_core::{
backend::Compiler,
};
use wasmer_dev_utils::stdio::StdioCapturer;
#[cfg(feature = "clif")] #[cfg(feature = "clif")]
fn get_compiler() -> impl Compiler { fn get_compiler() -> impl Compiler {

View File

@ -0,0 +1,9 @@
[package]
name = "wasmer-kernel-loader"
version = "0.1.0"
authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
[dependencies]
libc = "0.2.49"
wasmer-runtime-core = { path = "../runtime-core" }

View File

@ -0,0 +1,168 @@
pub mod service;
use service::{ImportInfo, LoadProfile, RunProfile, ServiceContext, TableEntryRequest};
use wasmer_runtime_core::{
backend::RunnableModule,
loader::{Instance, Loader},
module::ModuleInfo,
structures::TypedIndex,
types::{
FuncIndex, ImportedMemoryIndex, ImportedTableIndex, LocalMemoryIndex, LocalTableIndex,
Value,
},
vm::{Anyfunc, Ctx, LocalGlobal, SigId},
};
pub struct KernelLoader;
impl Loader for KernelLoader {
type Instance = KernelInstance;
type Error = String;
fn load(
&self,
rm: &dyn RunnableModule,
module: &ModuleInfo,
full_ctx: &Ctx,
) -> Result<Self::Instance, Self::Error> {
let ctx = &full_ctx.internal;
let code = rm.get_code().unwrap();
let memory = if let Some(_) = module.memories.get(LocalMemoryIndex::new(0)) {
Some(unsafe {
::std::slice::from_raw_parts((**ctx.memories).base, (**ctx.memories).bound)
})
} else if let Some(_) = module.imported_memories.get(ImportedMemoryIndex::new(0)) {
return Err("imported memory is not supported".into());
} else {
None
};
let table: Option<Vec<TableEntryRequest>> =
if let Some(_) = module.tables.get(LocalTableIndex::new(0)) {
Some(unsafe {
let table = &**ctx.tables;
let elements: &[Anyfunc];
#[allow(clippy::cast_ptr_alignment)]
{
elements =
::std::slice::from_raw_parts(table.base as *const Anyfunc, table.count);
}
let base_addr = code.as_ptr() as usize;
let end_addr = base_addr + code.len();
elements
.iter()
.map(|x| {
let func_addr = x.func as usize;
TableEntryRequest {
offset: if x.func.is_null()
|| func_addr < base_addr
|| func_addr >= end_addr
{
::std::usize::MAX
} else {
x.func as usize - base_addr
},
sig_id: x.sig_id.0,
}
})
.collect()
})
} else if let Some(_) = module.imported_tables.get(ImportedTableIndex::new(0)) {
return Err("imported table is not supported".into());
} else {
None
};
if module.imported_globals.len() > 0 {
return Err("imported globals are not supported".into());
}
let globals: Vec<u64> = unsafe {
let globals: &[*mut LocalGlobal] =
::std::slice::from_raw_parts(ctx.globals, module.globals.len());
globals.iter().map(|x| (**x).data).collect()
};
let mut import_names: Vec<ImportInfo> = vec![];
for (idx, import) in &module.imported_functions {
let name = format!(
"{}##{}",
module.namespace_table.get(import.namespace_index),
module.name_table.get(import.name_index)
);
let sig = module
.signatures
.get(*module.func_assoc.get(FuncIndex::new(idx.index())).unwrap())
.unwrap();
import_names.push(ImportInfo {
name: name,
param_count: sig.params().len(),
});
}
let dynamic_sigindices: &[u32] = unsafe {
::std::mem::transmute::<&[SigId], &[u32]>(::std::slice::from_raw_parts(
ctx.dynamic_sigindices,
full_ctx.dynamic_sigindice_count(),
))
};
let param_counts: Vec<usize> = (0..module.func_assoc.len())
.map(|x| {
module
.signatures
.get(*module.func_assoc.get(FuncIndex::new(x)).unwrap())
.unwrap()
.params()
.len()
})
.collect();
let profile = LoadProfile {
code: code,
memory: memory,
memory_max: 0,
globals: &globals,
imports: &import_names,
dynamic_sigindices: dynamic_sigindices,
table: table.as_ref().map(|x| x.as_slice()),
};
let sc = ServiceContext::new(profile).map_err(|x| format!("{:?}", x))?;
Ok(KernelInstance {
context: sc,
offsets: rm.get_offsets().unwrap(),
param_counts: param_counts,
})
}
}
pub struct KernelInstance {
context: ServiceContext,
offsets: Vec<usize>,
param_counts: Vec<usize>, // FIXME: Full signature check
}
impl Instance for KernelInstance {
type Error = String;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, String> {
if args.len() != self.param_counts[id] {
return Err("param count mismatch".into());
}
let args: Vec<u64> = args.iter().map(|x| x.to_u64()).collect();
let ret = self
.context
.run_code(RunProfile {
entry_offset: self.offsets[id] as u32,
params: &args,
})
.map_err(|x| format!("{:?}", x))?;
Ok(ret)
}
fn read_memory(&mut self, offset: u32, len: u32) -> Result<Vec<u8>, String> {
self.context
.read_memory(offset, len)
.map_err(|x| format!("{:?}", x))
}
fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> Result<(), String> {
self.context
.write_memory(offset, len, buf)
.map_err(|x| format!("{:?}", x))
}
}

View File

@ -0,0 +1,255 @@
use std::error::Error;
use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
macro_rules! impl_debug_display {
($target:ident) => {
impl ::std::fmt::Display for $target {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
<Self as ::std::fmt::Debug>::fmt(self, f)
}
}
};
}
#[repr(i32)]
pub enum Command {
LoadCode = 0x1001,
RunCode = 0x1002,
ReadMemory = 0x1003,
WriteMemory = 0x1004,
}
#[derive(Debug)]
pub enum ServiceError {
Io(io::Error),
Code(i32),
InvalidInput,
Rejected,
}
pub type ServiceResult<T> = Result<T, ServiceError>;
impl_debug_display!(ServiceError);
impl Error for ServiceError {
fn description(&self) -> &str {
"ServiceError"
}
}
impl From<io::Error> for ServiceError {
fn from(other: io::Error) -> ServiceError {
ServiceError::Io(other)
}
}
#[repr(C)]
struct LoadCodeRequest {
code: *const u8,
code_len: u32,
memory: *const u8,
memory_len: u32,
memory_max: u32,
table: *const TableEntryRequest,
table_count: u32,
globals: *const u64,
global_count: u32,
imported_funcs: *const ImportRequest,
imported_func_count: u32,
dynamic_sigindices: *const u32,
dynamic_sigindice_count: u32,
}
#[repr(C)]
struct RunCodeRequest {
entry_offset: u32,
params: *const u64,
param_count: u32,
result: *mut RunCodeResult,
}
#[repr(C)]
struct RunCodeResult {
success: u32,
retval: u64,
}
#[repr(C)]
struct ReadMemoryRequest {
out: *mut u8,
offset: u32,
len: u32,
}
#[repr(C)]
struct WriteMemoryRequest {
_in: *const u8,
offset: u32,
len: u32,
}
#[repr(C)]
struct ImportRequest {
name: [u8; 64],
param_count: u32,
}
#[repr(C)]
pub struct TableEntryRequest {
pub offset: usize,
pub sig_id: u32,
}
pub struct LoadProfile<'a> {
pub code: &'a [u8],
pub memory: Option<&'a [u8]>,
pub memory_max: usize,
pub globals: &'a [u64],
pub imports: &'a [ImportInfo],
pub dynamic_sigindices: &'a [u32],
pub table: Option<&'a [TableEntryRequest]>,
}
pub struct ImportInfo {
pub name: String,
pub param_count: usize,
}
pub struct RunProfile<'a> {
pub entry_offset: u32,
pub params: &'a [u64],
}
pub struct ServiceContext {
dev: File,
}
impl ServiceContext {
pub fn new(load: LoadProfile) -> ServiceResult<ServiceContext> {
let dev = File::open("/dev/wasmctl")?;
let imports: Vec<ImportRequest> = load
.imports
.iter()
.map(|x| {
let mut req = ImportRequest {
name: [0u8; 64],
param_count: x.param_count as u32,
};
let name = x.name.as_bytes();
let mut count = req.name.len() - 1;
if name.len() < count {
count = name.len();
}
req.name[..count].copy_from_slice(&name[..count]);
req
})
.collect();
let req = LoadCodeRequest {
code: load.code.as_ptr(),
code_len: load.code.len() as u32,
memory: load
.memory
.map(|x| x.as_ptr())
.unwrap_or(::std::ptr::null()),
memory_len: load.memory.map(|x| x.len() as u32).unwrap_or(0),
memory_max: load.memory_max as u32,
table: load.table.map(|x| x.as_ptr()).unwrap_or(::std::ptr::null()),
table_count: load.table.map(|x| x.len() as u32).unwrap_or(0),
globals: load.globals.as_ptr(),
global_count: load.globals.len() as u32,
imported_funcs: imports.as_ptr(),
imported_func_count: imports.len() as u32,
dynamic_sigindices: load.dynamic_sigindices.as_ptr(),
dynamic_sigindice_count: load.dynamic_sigindices.len() as u32,
};
let fd = dev.as_raw_fd();
let ret = unsafe {
::libc::ioctl(
fd,
Command::LoadCode as i32 as ::libc::c_ulong,
&req as *const _ as ::libc::c_ulong,
)
};
if ret != 0 {
Err(ServiceError::Code(ret))
} else {
Ok(ServiceContext { dev: dev })
}
}
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u64> {
let mut result: RunCodeResult = unsafe { ::std::mem::zeroed() };
let mut req = RunCodeRequest {
entry_offset: run.entry_offset,
params: run.params.as_ptr(),
param_count: run.params.len() as u32,
result: &mut result,
};
let fd = self.dev.as_raw_fd();
let err = unsafe {
::libc::ioctl(
fd,
Command::RunCode as i32 as ::libc::c_ulong,
&mut req as *mut _ as ::libc::c_ulong,
)
};
if err < 0 {
Err(ServiceError::Code(err))
} else if result.success == 0 {
println!("Rejected {} {}", result.success, result.retval);
Err(ServiceError::Rejected)
} else {
Ok(result.retval)
}
}
pub fn read_memory(&mut self, offset: u32, len: u32) -> ServiceResult<Vec<u8>> {
let fd = self.dev.as_raw_fd();
let mut ret = Vec::with_capacity(len as usize);
unsafe {
ret.set_len(len as usize);
}
let req = ReadMemoryRequest {
out: ret.as_mut_ptr(),
offset: offset,
len: len,
};
let err = unsafe {
::libc::ioctl(
fd,
Command::ReadMemory as i32 as ::libc::c_ulong,
&req as *const _ as ::libc::c_ulong,
)
};
if err < 0 {
Err(ServiceError::Code(err))
} else {
Ok(ret)
}
}
pub fn write_memory(&mut self, offset: u32, len: u32, buf: &[u8]) -> ServiceResult<()> {
let fd = self.dev.as_raw_fd();
let req = WriteMemoryRequest {
_in: buf.as_ptr(),
offset: offset,
len: len,
};
let err = unsafe {
::libc::ioctl(
fd,
Command::WriteMemory as i32 as ::libc::c_ulong,
&req as *const _ as ::libc::c_ulong,
)
};
if err < 0 {
Err(ServiceError::Code(err))
} else {
Ok(())
}
}
}

View File

@ -0,0 +1,9 @@
[package]
name = "kernel-net"
version = "0.1.0"
authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
[dependencies]
#runtime = "0.3.0-alpha.4"
#runtime-raw = "0.3.0-alpha.3"

466
lib/kernel-net/src/lib.rs Normal file
View File

@ -0,0 +1,466 @@
#![cfg(all(target_arch = "wasm32", target_os = "wasi"))]
#![feature(wasi_ext)]
use std::cell::RefCell;
use std::fs::File;
use std::net::{AddrParseError, Ipv4Addr};
use std::os::wasi::io::FromRawFd;
use std::sync::{Arc, Mutex};
const AF_INET: i32 = 2;
const SOCK_STREAM: i32 = 1;
const O_NONBLOCK: u32 = 2048;
const F_GETFL: i32 = 3;
const F_SETFL: i32 = 4;
const EPOLLIN: u32 = 1u32;
const EPOLLOUT: u32 = 4u32;
const EPOLLONESHOT: u32 = 1u32 << 30;
const EPOLLET: u32 = 1u32 << 31;
const EAGAIN: i32 = 11;
const EWOULDBLOCK: i32 = EAGAIN;
const EPOLL_CTL_ADD: i32 = 1;
const EPOLL_CTL_DEL: i32 = 2;
#[link(wasm_import_module = "net")]
extern "C" {
fn _socket(family: i32, _type: i32, proto: i32) -> i32;
fn _bind(fd: i32, sa: *const SockaddrIn, sa_len: usize) -> i32;
fn _listen(fd: i32, backlog: i32) -> i32;
fn _accept4(fd: i32, sa: *mut SockaddrIn, sa_len: *mut usize, flags: u32) -> i32;
fn _sendto(
fd: i32,
buf: *const u8,
buf_len: usize,
flags: u32,
addr: *const SockaddrIn,
addr_len: usize,
) -> i32;
fn _recvfrom(
fd: i32,
buf: *mut u8,
buf_len: usize,
flags: u32,
addr: *mut SockaddrIn,
addr_len: *mut usize,
) -> i32;
fn _eventfd_sem(initial: u32) -> i32;
fn _epoll_create() -> i32;
fn _epoll_ctl(epfd: i32, op: i32, fd: i32, event: *const EpollEvent) -> i32;
fn _epoll_wait(epfd: i32, events: *mut EpollEvent, maxevents: usize, timeout: i32) -> i32;
fn _fcntl(fd: i32, cmd: i32, arg: u32) -> i32;
}
thread_local! {
static GLOBAL_EPOLL: RefCell<Option<Arc<Epoll>>> = RefCell::new(None);
static ASYNC_STATE_POOL: RefCell<Vec<Box<AsyncState>>> = RefCell::new(Vec::new());
}
#[derive(Default)]
struct AsyncState {
callback: Option<Box<FnOnce()>>,
_epoll: Option<Arc<Epoll>>,
}
pub struct Epoll {
fd: i32,
imm_queue: Mutex<Vec<Box<FnOnce()>>>,
}
impl Epoll {
pub fn new() -> Epoll {
let fd = unsafe { _epoll_create() };
assert!(fd >= 0);
Epoll {
fd: fd,
imm_queue: Mutex::new(Vec::new()),
}
}
pub fn schedule<F: FnOnce() + 'static>(&self, f: F) {
self.imm_queue.lock().unwrap().push(Box::new(f));
}
pub unsafe fn run(self: Arc<Self>) -> ! {
GLOBAL_EPOLL.with(|x| {
*x.borrow_mut() = Some(self.clone());
});
let mut events: Vec<EpollEvent> = vec![EpollEvent::default(); 32];
loop {
loop {
let imm_queue =
::std::mem::replace(&mut *self.imm_queue.lock().unwrap(), Vec::new());
if imm_queue.len() == 0 {
break;
}
for f in imm_queue {
f();
}
}
let events_len = events.len();
let n_ready = _epoll_wait(self.fd, events.as_mut_ptr(), events_len, -1);
assert!(n_ready >= 0);
/*if n_ready > 1 {
println!("n_ready = {}", n_ready);
}*/
for ev in &events[..n_ready as usize] {
if ev.events & (EPOLLIN | EPOLLOUT) != 0 {
//println!("Free event {:x} {:?}", ev.events, ev.data as usize as *mut AsyncState);
let mut state = Box::from_raw(ev.data as usize as *mut AsyncState);
(state.callback.take().unwrap())();
put_async_state(state);
//println!("After callback");
} else {
println!("unknown event(s): 0x{:x}", ev.events);
}
}
}
}
}
impl Drop for Epoll {
fn drop(&mut self) {
unsafe {
File::from_raw_fd(self.fd as _);
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum EpollDirection {
In,
Out,
}
fn get_async_state() -> Box<AsyncState> {
ASYNC_STATE_POOL.with(|pool| {
pool.borrow_mut()
.pop()
.unwrap_or_else(|| Box::new(AsyncState::default()))
})
}
fn put_async_state(mut x: Box<AsyncState>) {
x.callback = None;
x._epoll = None;
ASYNC_STATE_POOL.with(|pool| pool.borrow_mut().push(x));
}
pub fn schedule<F: FnOnce() + 'static>(f: F) {
//println!("schedule");
let epoll = GLOBAL_EPOLL.with(|x| x.borrow().as_ref().unwrap().clone());
epoll.schedule(f);
}
fn get_async_io_payload<
T: 'static,
P: FnMut(i32) -> Result<T, i32> + 'static,
F: FnOnce(Result<T, i32>) + 'static,
>(
epoll: Arc<Epoll>,
fd: i32,
direction: EpollDirection,
poll_action: P,
on_ready: F,
) -> Box<FnOnce()> {
__get_async_io_payload(epoll, fd, direction, poll_action, on_ready, false)
}
fn __get_async_io_payload<
T: 'static,
P: FnMut(i32) -> Result<T, i32> + 'static,
F: FnOnce(Result<T, i32>) + 'static,
>(
epoll: Arc<Epoll>,
fd: i32,
direction: EpollDirection,
mut poll_action: P,
on_ready: F,
registered: bool,
) -> Box<FnOnce()> {
let epfd = epoll.fd;
Box::new(move || {
//println!("async io payload");
let ret = poll_action(fd);
//println!("async io payload (after poll_action)");
match ret {
Err(x) if x == -EAGAIN || x == -EWOULDBLOCK => {
let mut state = get_async_state();
state.callback = Some(__get_async_io_payload(
epoll.clone(),
fd,
direction,
poll_action,
on_ready,
true,
));
state._epoll = Some(epoll);
let direction_flag = match direction {
EpollDirection::In => EPOLLIN,
EpollDirection::Out => EPOLLOUT,
};
let ev = EpollEvent {
events: direction_flag | EPOLLET | EPOLLONESHOT,
data: Box::into_raw(state) as usize as _,
};
//println!("Alloc event {:?}", ev.data as usize as *mut AsyncState);
let ret = unsafe { _epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) };
assert!(ret >= 0);
}
x => {
if registered {
assert!(
unsafe { _epoll_ctl(epfd, EPOLL_CTL_DEL, fd, ::std::ptr::null(),) } >= 0
);
}
on_ready(x); // fast path
}
}
})
}
#[repr(C)]
#[derive(Copy, Clone)]
struct SockaddrIn {
sin_family: u16, // e.g. AF_INET
sin_port: u16, // e.g. htons(3490)
sin_addr: InAddr,
sin_zero: [u8; 8],
}
#[repr(C)]
#[derive(Copy, Clone)]
struct InAddr {
s_addr: u32,
}
#[repr(C, packed)]
#[derive(Copy, Clone, Default)]
struct EpollEvent {
events: u32,
data: u64,
}
fn invert_byteorder_u16(x: u16) -> u16 {
unsafe {
use std::mem::transmute;
let buf: [u8; 2] = transmute(x);
let out: [u8; 2] = [buf[1], buf[0]];
transmute(out)
}
}
#[derive(Debug)]
pub enum SocketError {
AddrParse(AddrParseError),
SocketCreate,
Bind,
Listen,
Accept,
Message(String),
}
pub struct Tcp4Listener {
_addr: Ipv4Addr,
_port: u16,
fd: i32,
}
impl Tcp4Listener {
pub fn new<A: AsRef<str>>(
addr: A,
port: u16,
backlog: u32,
) -> Result<Tcp4Listener, SocketError> {
let addr: Ipv4Addr = addr.as_ref().parse().map_err(SocketError::AddrParse)?;
let sa = SockaddrIn {
sin_family: AF_INET as _,
sin_port: invert_byteorder_u16(port),
sin_addr: InAddr {
s_addr: unsafe { ::std::mem::transmute(addr.octets()) },
},
sin_zero: [0; 8],
};
let fd = unsafe { _socket(AF_INET, SOCK_STREAM, 0) };
if fd < 0 {
return Err(SocketError::SocketCreate);
}
if unsafe { _bind(fd, &sa, ::std::mem::size_of::<SockaddrIn>()) } < 0 {
return Err(SocketError::Bind);
}
if unsafe { _listen(fd, backlog as _) } < 0 {
return Err(SocketError::Listen);
}
unsafe {
let mut socket_flags = _fcntl(fd, F_GETFL, 0) as u32;
socket_flags |= O_NONBLOCK;
assert!(_fcntl(fd, F_SETFL, socket_flags) >= 0);
}
Ok(Tcp4Listener {
_addr: addr,
_port: port,
fd: fd,
})
}
pub fn accept_async<F: Fn(Result<Arc<TcpStream>, i32>) -> Result<(), ()> + 'static>(
self: Arc<Self>,
ep: Arc<Epoll>,
cb: F,
) {
let ep2 = ep.clone();
(get_async_io_payload(
ep.clone(),
self.fd,
EpollDirection::In,
move |fd| -> Result<Arc<TcpStream>, i32> {
let mut incoming_sa: SockaddrIn = unsafe { ::std::mem::uninitialized() };
let mut real_len: usize = ::std::mem::size_of::<SockaddrIn>();
let conn = unsafe { _accept4(fd, &mut incoming_sa, &mut real_len, O_NONBLOCK) };
if conn >= 0 {
unsafe {
let mut socket_flags = _fcntl(conn, F_GETFL, 0) as u32;
socket_flags |= O_NONBLOCK;
assert!(_fcntl(conn, F_SETFL, socket_flags) >= 0);
}
Ok(Arc::new(TcpStream {
fd: conn,
epoll: ep.clone(),
}))
} else {
Err(conn)
}
},
move |x| {
schedule(|| {
if let Ok(()) = cb(x) {
self.accept_async(ep2, cb);
}
});
},
))();
}
}
pub struct TcpStream {
fd: i32,
epoll: Arc<Epoll>,
}
impl TcpStream {
pub fn __write_async(
self: Arc<Self>,
data: Vec<u8>,
offset: usize,
cb: impl FnOnce(Result<(usize, Vec<u8>), i32>) + 'static,
) {
let mut data = Some(data);
(get_async_io_payload(
self.epoll.clone(),
self.fd,
EpollDirection::Out,
move |fd| -> Result<(usize, Vec<u8>), i32> {
let _data = data.as_ref().unwrap();
let _data = &_data[offset..];
let ret =
unsafe { _sendto(fd, _data.as_ptr(), _data.len(), 0, ::std::ptr::null(), 0) };
if ret >= 0 {
Ok((ret as usize, data.take().unwrap()))
} else {
Err(ret)
}
},
move |x| {
drop(self);
cb(x);
},
))();
}
pub fn write_async(
self: Arc<Self>,
data: Vec<u8>,
cb: impl FnOnce(Result<(usize, Vec<u8>), i32>) + 'static,
) {
self.__write_async(data, 0, cb)
}
pub fn write_all_async(
self: Arc<Self>,
data: Vec<u8>,
cb: impl FnOnce(Result<Vec<u8>, i32>) + 'static,
) {
fn inner(
me: Arc<TcpStream>,
data: Vec<u8>,
offset: usize,
cb: impl FnOnce(Result<Vec<u8>, i32>) + 'static,
) {
let me2 = me.clone();
me.__write_async(data, offset, move |result| match result {
Ok((len, data)) => {
let new_offset = offset + len;
if new_offset == data.len() {
cb(Ok(data));
} else {
inner(me2, data, new_offset, cb);
}
}
Err(code) => {
cb(Err(code));
}
})
}
inner(self, data, 0, cb);
}
pub fn read_async(
self: Arc<Self>,
out: Vec<u8>,
cb: impl FnOnce(Result<Vec<u8>, i32>) + 'static,
) {
let mut out = Some(out);
(get_async_io_payload(
self.epoll.clone(),
self.fd,
EpollDirection::In,
move |fd| -> Result<Vec<u8>, i32> {
let _out = out.as_mut().unwrap();
let out_cap = _out.capacity();
let ret = unsafe {
_recvfrom(
fd,
_out.as_mut_ptr(),
out_cap,
0,
::std::ptr::null_mut(),
::std::ptr::null_mut(),
)
};
if ret >= 0 {
assert!(ret as usize <= out_cap);
unsafe {
_out.set_len(ret as usize);
}
Ok(out.take().unwrap())
} else {
Err(ret)
}
},
move |x| {
drop(self);
cb(x);
},
))();
}
}
impl Drop for TcpStream {
fn drop(&mut self) {
unsafe {
File::from_raw_fd(self.fd as _);
}
}
}

View File

@ -1,11 +1,11 @@
[package] [package]
name = "wasmer-llvm-backend" name = "wasmer-llvm-backend"
version = "0.4.1" version = "0.4.2"
authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"] authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" } inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" }
wasmparser = "0.29.2" wasmparser = "0.29.2"
hashbrown = "0.1.8" hashbrown = "0.1.8"

View File

@ -285,13 +285,16 @@ fn resolve_memory_ptr(
ctx: &mut CtxType, ctx: &mut CtxType,
memarg: &MemoryImmediate, memarg: &MemoryImmediate,
ptr_ty: PointerType, ptr_ty: PointerType,
value_size: usize,
) -> Result<PointerValue, BinaryReaderError> { ) -> Result<PointerValue, BinaryReaderError> {
// Ignore alignment hint for the time being. // Ignore alignment hint for the time being.
let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false); let imm_offset = intrinsics.i64_ty.const_int(memarg.offset as u64, false);
let value_size_v = intrinsics.i64_ty.const_int(value_size as u64, false);
let var_offset_i32 = state.pop1()?.into_int_value(); let var_offset_i32 = state.pop1()?.into_int_value();
let var_offset = let var_offset =
builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name()); builder.build_int_z_extend(var_offset_i32, intrinsics.i64_ty, &state.var_name());
let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name()); let effective_offset = builder.build_int_add(var_offset, imm_offset, &state.var_name());
let end_offset = builder.build_int_add(effective_offset, value_size_v, &state.var_name());
let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics); let memory_cache = ctx.memory(MemoryIndex::new(0), intrinsics);
let mem_base_int = match memory_cache { let mem_base_int = match memory_cache {
@ -306,12 +309,20 @@ fn resolve_memory_ptr(
let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int"); let base_as_int = builder.build_ptr_to_int(base, intrinsics.i64_ty, "base_as_int");
let base_in_bounds = builder.build_int_compare( let base_in_bounds_1 = builder.build_int_compare(
IntPredicate::ULE,
end_offset,
bounds,
"base_in_bounds_1",
);
let base_in_bounds_2 = builder.build_int_compare(
IntPredicate::ULT, IntPredicate::ULT,
effective_offset, effective_offset,
bounds, end_offset,
"base_in_bounds", "base_in_bounds_2",
); );
let base_in_bounds =
builder.build_and(base_in_bounds_1, base_in_bounds_2, "base_in_bounds");
let base_in_bounds = builder let base_in_bounds = builder
.build_call( .build_call(
@ -2000,6 +2011,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i32_ptr_ty, intrinsics.i32_ptr_ty,
4,
)?; )?;
let result = builder.build_load(effective_address, &state.var_name()); let result = builder.build_load(effective_address, &state.var_name());
state.push1(result); state.push1(result);
@ -2014,6 +2026,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i64_ptr_ty, intrinsics.i64_ptr_ty,
8,
)?; )?;
let result = builder.build_load(effective_address, &state.var_name()); let result = builder.build_load(effective_address, &state.var_name());
state.push1(result); state.push1(result);
@ -2028,6 +2041,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.f32_ptr_ty, intrinsics.f32_ptr_ty,
4,
)?; )?;
let result = builder.build_load(effective_address, &state.var_name()); let result = builder.build_load(effective_address, &state.var_name());
state.push1(result); state.push1(result);
@ -2042,6 +2056,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.f64_ptr_ty, intrinsics.f64_ptr_ty,
8,
)?; )?;
let result = builder.build_load(effective_address, &state.var_name()); let result = builder.build_load(effective_address, &state.var_name());
state.push1(result); state.push1(result);
@ -2058,6 +2073,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i32_ptr_ty, intrinsics.i32_ptr_ty,
4,
)?; )?;
builder.build_store(effective_address, value); builder.build_store(effective_address, value);
} }
@ -2072,6 +2088,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i64_ptr_ty, intrinsics.i64_ptr_ty,
8,
)?; )?;
builder.build_store(effective_address, value); builder.build_store(effective_address, value);
} }
@ -2086,6 +2103,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.f32_ptr_ty, intrinsics.f32_ptr_ty,
4,
)?; )?;
builder.build_store(effective_address, value); builder.build_store(effective_address, value);
} }
@ -2100,6 +2118,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.f64_ptr_ty, intrinsics.f64_ptr_ty,
8,
)?; )?;
builder.build_store(effective_address, value); builder.build_store(effective_address, value);
} }
@ -2114,6 +2133,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i8_ptr_ty, intrinsics.i8_ptr_ty,
1,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2132,6 +2152,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i16_ptr_ty, intrinsics.i16_ptr_ty,
2,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2150,6 +2171,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i8_ptr_ty, intrinsics.i8_ptr_ty,
1,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2168,6 +2190,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i16_ptr_ty, intrinsics.i16_ptr_ty,
2,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2186,6 +2209,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i32_ptr_ty, intrinsics.i32_ptr_ty,
4,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2205,6 +2229,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i8_ptr_ty, intrinsics.i8_ptr_ty,
1,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2223,6 +2248,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i16_ptr_ty, intrinsics.i16_ptr_ty,
2,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2241,6 +2267,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i8_ptr_ty, intrinsics.i8_ptr_ty,
1,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2259,6 +2286,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i16_ptr_ty, intrinsics.i16_ptr_ty,
2,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2277,6 +2305,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i32_ptr_ty, intrinsics.i32_ptr_ty,
4,
)?; )?;
let narrow_result = builder let narrow_result = builder
.build_load(effective_address, &state.var_name()) .build_load(effective_address, &state.var_name())
@ -2297,6 +2326,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i8_ptr_ty, intrinsics.i8_ptr_ty,
1,
)?; )?;
let narrow_value = let narrow_value =
builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name()); builder.build_int_truncate(value, intrinsics.i8_ty, &state.var_name());
@ -2313,6 +2343,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i16_ptr_ty, intrinsics.i16_ptr_ty,
2,
)?; )?;
let narrow_value = let narrow_value =
builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name()); builder.build_int_truncate(value, intrinsics.i16_ty, &state.var_name());
@ -2329,6 +2360,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
&mut ctx, &mut ctx,
memarg, memarg,
intrinsics.i32_ptr_ty, intrinsics.i32_ptr_ty,
4,
)?; )?;
let narrow_value = let narrow_value =
builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name()); builder.build_int_truncate(value, intrinsics.i32_ty, &state.var_name());

View File

@ -16,6 +16,7 @@ use wasmer_runtime_core::{
GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
TableIndex, Type, TableIndex, Type,
}, },
vm::Ctx,
}; };
fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType { fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
@ -158,6 +159,10 @@ impl Intrinsics {
let imported_func_ty = let imported_func_ty =
context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false); context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false);
let sigindex_ty = i32_ty; let sigindex_ty = i32_ty;
let rt_intrinsics_ty = void_ty;
let stack_lower_bound_ty = i8_ty;
let memory_base_ty = i8_ty;
let memory_bound_ty = void_ty;
let local_function_ty = i8_ptr_ty; let local_function_ty = i8_ptr_ty;
let anyfunc_ty = context.struct_type( let anyfunc_ty = context.struct_type(
@ -201,6 +206,18 @@ impl Intrinsics {
sigindex_ty sigindex_ty
.ptr_type(AddressSpace::Generic) .ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), .as_basic_type_enum(),
rt_intrinsics_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
stack_lower_bound_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
memory_base_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
memory_bound_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
local_function_ty local_function_ty
.ptr_type(AddressSpace::Generic) .ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), .as_basic_type_enum(),
@ -416,6 +433,10 @@ pub struct CtxType<'a> {
_phantom: PhantomData<&'a FunctionValue>, _phantom: PhantomData<&'a FunctionValue>,
} }
fn offset_to_index(offset: u8) -> u32 {
(offset as usize / ::std::mem::size_of::<usize>()) as u32
}
impl<'a> CtxType<'a> { impl<'a> CtxType<'a> {
pub fn new( pub fn new(
info: &'a ModuleInfo, info: &'a ModuleInfo,
@ -454,14 +475,22 @@ impl<'a> CtxType<'a> {
let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) { let (memory_array_ptr_ptr, index, memory_type) = match index.local_or_import(info) {
LocalOrImport::Local(local_mem_index) => ( LocalOrImport::Local(local_mem_index) => (
unsafe { unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 0, "memory_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_memories()),
"memory_array_ptr_ptr",
)
}, },
local_mem_index.index() as u64, local_mem_index.index() as u64,
info.memories[local_mem_index].memory_type(), info.memories[local_mem_index].memory_type(),
), ),
LocalOrImport::Import(import_mem_index) => ( LocalOrImport::Import(import_mem_index) => (
unsafe { unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 3, "memory_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_imported_memories()),
"memory_array_ptr_ptr",
)
}, },
import_mem_index.index() as u64, import_mem_index.index() as u64,
info.imported_memories[import_mem_index].1.memory_type(), info.imported_memories[import_mem_index].1.memory_type(),
@ -527,13 +556,21 @@ impl<'a> CtxType<'a> {
let (table_array_ptr_ptr, index) = match index.local_or_import(info) { let (table_array_ptr_ptr, index) = match index.local_or_import(info) {
LocalOrImport::Local(local_table_index) => ( LocalOrImport::Local(local_table_index) => (
unsafe { unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 1, "table_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_tables()),
"table_array_ptr_ptr",
)
}, },
local_table_index.index() as u64, local_table_index.index() as u64,
), ),
LocalOrImport::Import(import_table_index) => ( LocalOrImport::Import(import_table_index) => (
unsafe { unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 4, "table_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_imported_tables()),
"table_array_ptr_ptr",
)
}, },
import_table_index.index() as u64, import_table_index.index() as u64,
), ),
@ -578,8 +615,13 @@ impl<'a> CtxType<'a> {
intrinsics: &Intrinsics, intrinsics: &Intrinsics,
builder: &Builder, builder: &Builder,
) -> PointerValue { ) -> PointerValue {
let local_func_array_ptr_ptr = let local_func_array_ptr_ptr = unsafe {
unsafe { builder.build_struct_gep(self.ctx_ptr_value, 8, "local_func_array_ptr_ptr") }; builder.build_struct_gep(
self.ctx_ptr_value,
offset_to_index(Ctx::offset_local_functions()),
"local_func_array_ptr_ptr",
)
};
let local_func_array_ptr = builder let local_func_array_ptr = builder
.build_load(local_func_array_ptr_ptr, "local_func_array_ptr") .build_load(local_func_array_ptr_ptr, "local_func_array_ptr")
.into_pointer_value(); .into_pointer_value();
@ -609,7 +651,11 @@ impl<'a> CtxType<'a> {
*cached_sigindices.entry(index).or_insert_with(|| { *cached_sigindices.entry(index).or_insert_with(|| {
let sigindex_array_ptr_ptr = unsafe { let sigindex_array_ptr_ptr = unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 7, "sigindex_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_signatures()),
"sigindex_array_ptr_ptr",
)
}; };
let sigindex_array_ptr = cache_builder let sigindex_array_ptr = cache_builder
.build_load(sigindex_array_ptr_ptr, "sigindex_array_ptr") .build_load(sigindex_array_ptr_ptr, "sigindex_array_ptr")
@ -647,7 +693,7 @@ impl<'a> CtxType<'a> {
unsafe { unsafe {
cache_builder.build_struct_gep( cache_builder.build_struct_gep(
ctx_ptr_value, ctx_ptr_value,
2, offset_to_index(Ctx::offset_globals()),
"globals_array_ptr_ptr", "globals_array_ptr_ptr",
) )
}, },
@ -662,7 +708,7 @@ impl<'a> CtxType<'a> {
unsafe { unsafe {
cache_builder.build_struct_gep( cache_builder.build_struct_gep(
ctx_ptr_value, ctx_ptr_value,
5, offset_to_index(Ctx::offset_imported_globals()),
"globals_array_ptr_ptr", "globals_array_ptr_ptr",
) )
}, },
@ -718,7 +764,11 @@ impl<'a> CtxType<'a> {
let imported_func_cache = cached_imported_functions.entry(index).or_insert_with(|| { let imported_func_cache = cached_imported_functions.entry(index).or_insert_with(|| {
let func_array_ptr_ptr = unsafe { let func_array_ptr_ptr = unsafe {
cache_builder.build_struct_gep(ctx_ptr_value, 6, "imported_func_array_ptr_ptr") cache_builder.build_struct_gep(
ctx_ptr_value,
offset_to_index(Ctx::offset_imported_funcs()),
"imported_func_array_ptr_ptr",
)
}; };
let func_array_ptr = cache_builder let func_array_ptr = cache_builder
.build_load(func_array_ptr_ptr, "func_array_ptr") .build_load(func_array_ptr_ptr, "func_array_ptr")

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-middleware-common" name = "wasmer-middleware-common"
version = "0.4.1" version = "0.4.2"
repository = "https://github.com/wasmerio/wasmer" repository = "https://github.com/wasmerio/wasmer"
description = "Wasmer runtime common middlewares" description = "Wasmer runtime common middlewares"
license = "MIT" license = "MIT"
@ -8,4 +8,4 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime-abi" name = "wasmer-runtime-abi"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime core library" description = "Wasmer runtime core library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -16,10 +16,10 @@ tar = "0.4"
wasmparser = "0.29.2" wasmparser = "0.29.2"
zstd = "0.4" zstd = "0.4"
[target.'cfg(unix)'.dependencies.zbox] # [target.'cfg(unix)'.dependencies.zbox]
git = "https://github.com/wasmerio/zbox" # git = "https://github.com/wasmerio/zbox"
branch = "bundle-libsodium" # branch = "bundle-libsodium"
features = ["libsodium-bundled"] # features = ["libsodium-bundled"]
[dev-dependencies] [dev-dependencies]
tempdir = "0.3" tempdir = "0.3"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime-c-api" name = "wasmer-runtime-c-api"
version = "0.4.1" version = "0.4.2"
description = "Wasmer C API library" description = "Wasmer C API library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -16,11 +16,11 @@ libc = "0.2"
[dependencies.wasmer-runtime] [dependencies.wasmer-runtime]
path = "../runtime" path = "../runtime"
version = "0.4.1" version = "0.4.2"
[dependencies.wasmer-runtime-core] [dependencies.wasmer-runtime-core]
path = "../runtime-core" path = "../runtime-core"
version = "0.4.1" version = "0.4.2"
[features] [features]
debug = ["wasmer-runtime/debug"] debug = ["wasmer-runtime/debug"]

View File

@ -8,7 +8,7 @@ use crate::{
value::wasmer_value_tag, value::wasmer_value_tag,
wasmer_byte_array, wasmer_result_t, wasmer_byte_array, wasmer_result_t,
}; };
use libc::{c_int, uint32_t}; use libc::{c_uint, uint32_t};
use std::{ffi::c_void, ptr, slice, sync::Arc}; use std::{ffi::c_void, ptr, slice, sync::Arc};
use wasmer_runtime::Module; use wasmer_runtime::Module;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
@ -154,11 +154,11 @@ pub extern "C" fn wasmer_import_descriptors_destroy(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasmer_import_descriptors_len( pub unsafe extern "C" fn wasmer_import_descriptors_len(
exports: *mut wasmer_import_descriptors_t, exports: *mut wasmer_import_descriptors_t,
) -> c_int { ) -> c_uint {
if exports.is_null() { if exports.is_null() {
return 0; return 0;
} }
(*(exports as *mut NamedImportDescriptors)).0.len() as c_int (*(exports as *mut NamedImportDescriptors)).0.len() as c_uint
} }
/// Gets import descriptor by index /// Gets import descriptor by index
@ -166,7 +166,7 @@ pub unsafe extern "C" fn wasmer_import_descriptors_len(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn wasmer_import_descriptors_get( pub unsafe extern "C" fn wasmer_import_descriptors_get(
import_descriptors: *mut wasmer_import_descriptors_t, import_descriptors: *mut wasmer_import_descriptors_t,
idx: c_int, idx: c_uint,
) -> *mut wasmer_import_descriptor_t { ) -> *mut wasmer_import_descriptor_t {
if import_descriptors.is_null() { if import_descriptors.is_null() {
return ptr::null_mut(); return ptr::null_mut();
@ -244,9 +244,9 @@ pub unsafe extern "C" fn wasmer_import_func_params_arity(
pub unsafe extern "C" fn wasmer_import_func_new( pub unsafe extern "C" fn wasmer_import_func_new(
func: extern "C" fn(data: *mut c_void), func: extern "C" fn(data: *mut c_void),
params: *const wasmer_value_tag, params: *const wasmer_value_tag,
params_len: c_int, params_len: c_uint,
returns: *const wasmer_value_tag, returns: *const wasmer_value_tag,
returns_len: c_int, returns_len: c_uint,
) -> *mut wasmer_import_func_t { ) -> *mut wasmer_import_func_t {
let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize); let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize);
let params: Vec<Type> = params.iter().cloned().map(|x| x.into()).collect(); let params: Vec<Type> = params.iter().cloned().map(|x| x.into()).collect();
@ -272,7 +272,7 @@ pub unsafe extern "C" fn wasmer_import_func_new(
pub unsafe extern "C" fn wasmer_import_func_params( pub unsafe extern "C" fn wasmer_import_func_params(
func: *const wasmer_import_func_t, func: *const wasmer_import_func_t,
params: *mut wasmer_value_tag, params: *mut wasmer_value_tag,
params_len: c_int, params_len: c_uint,
) -> wasmer_result_t { ) -> wasmer_result_t {
let export = &*(func as *const Export); let export = &*(func as *const Export);
if let Export::Function { ref signature, .. } = *export { if let Export::Function { ref signature, .. } = *export {
@ -301,7 +301,7 @@ pub unsafe extern "C" fn wasmer_import_func_params(
pub unsafe extern "C" fn wasmer_import_func_returns( pub unsafe extern "C" fn wasmer_import_func_returns(
func: *const wasmer_import_func_t, func: *const wasmer_import_func_t,
returns: *mut wasmer_value_tag, returns: *mut wasmer_value_tag,
returns_len: c_int, returns_len: c_uint,
) -> wasmer_result_t { ) -> wasmer_result_t {
let export = &*(func as *const Export); let export = &*(func as *const Export);
if let Export::Function { ref signature, .. } = *export { if let Export::Function { ref signature, .. } = *export {

View File

@ -99,12 +99,8 @@ pub unsafe extern "C" fn wasmer_instantiate(
let result = wasmer_runtime::instantiate(bytes, &import_object); let result = wasmer_runtime::instantiate(bytes, &import_object);
let new_instance = match result { let new_instance = match result {
Ok(instance) => instance, Ok(instance) => instance,
Err(_error) => { Err(error) => {
// TODO the trait bound `wasmer_runtime::error::Error: std::error::Error` is not satisfied update_last_error(error);
//update_last_error(error);
update_last_error(CApiError {
msg: "error instantiating".to_string(),
});
return wasmer_result_t::WASMER_ERROR; return wasmer_result_t::WASMER_ERROR;
} }
}; };

View File

@ -321,12 +321,12 @@ void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descr
* Gets import descriptor by index * Gets import descriptor by index
*/ */
wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors,
int idx); unsigned int idx);
/** /**
* Gets the length of the import descriptors * Gets the length of the import descriptors
*/ */
int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); unsigned int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports);
/** /**
* Frees memory for the given Func * Frees memory for the given Func
@ -339,9 +339,9 @@ void wasmer_import_func_destroy(wasmer_import_func_t *func);
*/ */
wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data), wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data),
const wasmer_value_tag *params, const wasmer_value_tag *params,
int params_len, unsigned int params_len,
const wasmer_value_tag *returns, const wasmer_value_tag *returns,
int returns_len); unsigned int returns_len);
/** /**
* Sets the params buffer to the parameter types of the given wasmer_import_func_t * Sets the params buffer to the parameter types of the given wasmer_import_func_t
@ -351,7 +351,7 @@ wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data),
*/ */
wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func, wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func,
wasmer_value_tag *params, wasmer_value_tag *params,
int params_len); unsigned int params_len);
/** /**
* Sets the result parameter to the arity of the params of the wasmer_import_func_t * Sets the result parameter to the arity of the params of the wasmer_import_func_t
@ -369,7 +369,7 @@ wasmer_result_t wasmer_import_func_params_arity(const wasmer_import_func_t *func
*/ */
wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func, wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_value_tag *returns, wasmer_value_tag *returns,
int returns_len); unsigned int returns_len);
/** /**
* Sets the result parameter to the arity of the returns of the wasmer_import_func_t * Sets the result parameter to the arity of the returns of the wasmer_import_func_t

View File

@ -4,6 +4,7 @@
#include <cstdarg> #include <cstdarg>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <new>
enum class wasmer_import_export_kind : uint32_t { enum class wasmer_import_export_kind : uint32_t {
WASM_FUNCTION, WASM_FUNCTION,
@ -260,10 +261,10 @@ void wasmer_import_descriptors_destroy(wasmer_import_descriptors_t *import_descr
/// Gets import descriptor by index /// Gets import descriptor by index
wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors, wasmer_import_descriptor_t *wasmer_import_descriptors_get(wasmer_import_descriptors_t *import_descriptors,
int idx); unsigned int idx);
/// Gets the length of the import descriptors /// Gets the length of the import descriptors
int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports); unsigned int wasmer_import_descriptors_len(wasmer_import_descriptors_t *exports);
/// Frees memory for the given Func /// Frees memory for the given Func
void wasmer_import_func_destroy(wasmer_import_func_t *func); void wasmer_import_func_destroy(wasmer_import_func_t *func);
@ -272,9 +273,9 @@ void wasmer_import_func_destroy(wasmer_import_func_t *func);
/// The caller owns the object and should call `wasmer_import_func_destroy` to free it. /// The caller owns the object and should call `wasmer_import_func_destroy` to free it.
wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data), wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data),
const wasmer_value_tag *params, const wasmer_value_tag *params,
int params_len, unsigned int params_len,
const wasmer_value_tag *returns, const wasmer_value_tag *returns,
int returns_len); unsigned int returns_len);
/// Sets the params buffer to the parameter types of the given wasmer_import_func_t /// Sets the params buffer to the parameter types of the given wasmer_import_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success. /// Returns `wasmer_result_t::WASMER_OK` upon success.
@ -282,7 +283,7 @@ wasmer_import_func_t *wasmer_import_func_new(void (*func)(void *data),
/// and `wasmer_last_error_message` to get an error message. /// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func, wasmer_result_t wasmer_import_func_params(const wasmer_import_func_t *func,
wasmer_value_tag *params, wasmer_value_tag *params,
int params_len); unsigned int params_len);
/// Sets the result parameter to the arity of the params of the wasmer_import_func_t /// Sets the result parameter to the arity of the params of the wasmer_import_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success. /// Returns `wasmer_result_t::WASMER_OK` upon success.
@ -296,7 +297,7 @@ wasmer_result_t wasmer_import_func_params_arity(const wasmer_import_func_t *func
/// and `wasmer_last_error_message` to get an error message. /// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func, wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_value_tag *returns, wasmer_value_tag *returns,
int returns_len); unsigned int returns_len);
/// Sets the result parameter to the arity of the returns of the wasmer_import_func_t /// Sets the result parameter to the arity of the returns of the wasmer_import_func_t
/// Returns `wasmer_result_t::WASMER_OK` upon success. /// Returns `wasmer_result_t::WASMER_OK` upon success.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime-core" name = "wasmer-runtime-core"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime core library" description = "Wasmer runtime core library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -50,3 +50,4 @@ rustc_version = "0.2.3"
[features] [features]
debug = [] debug = []
trace = ["debug"]

View File

@ -39,16 +39,26 @@ impl Token {
} }
} }
#[derive(Copy, Clone, Debug)]
pub enum MemoryBoundCheckMode {
Default,
Enable,
Disable,
}
impl Default for MemoryBoundCheckMode {
fn default() -> MemoryBoundCheckMode {
MemoryBoundCheckMode::Default
}
}
/// Configuration data for the compiler /// Configuration data for the compiler
#[derive(Default)]
pub struct CompilerConfig { pub struct CompilerConfig {
/// Symbol information generated from emscripten; used for more detailed debug messages /// Symbol information generated from emscripten; used for more detailed debug messages
pub symbol_map: Option<HashMap<u32, String>>, pub symbol_map: Option<HashMap<u32, String>>,
} pub memory_bound_check_mode: MemoryBoundCheckMode,
pub enforce_stack_check: bool,
impl Default for CompilerConfig {
fn default() -> CompilerConfig {
CompilerConfig { symbol_map: None }
}
} }
pub trait Compiler { pub trait Compiler {
@ -80,6 +90,16 @@ pub trait RunnableModule: Send + Sync {
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>; fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !; unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
/// Returns the machine code associated with this module.
fn get_code(&self) -> Option<&[u8]> {
None
}
/// Returns the beginning offsets of all functions, including import trampolines.
fn get_offsets(&self) -> Option<Vec<usize>> {
None
}
} }
pub trait CacheGen: Send + Sync { pub trait CacheGen: Send + Sync {

View File

@ -456,10 +456,17 @@ fn import_functions(
}); });
} }
None => { None => {
link_errors.push(LinkError::ImportNotFound { if imports.allow_missing_functions {
namespace: namespace.to_string(), functions.push(vm::ImportedFunc {
name: name.to_string(), func: ::std::ptr::null(),
}); vmctx: ::std::ptr::null_mut(),
});
} else {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.to_string(),
name: name.to_string(),
});
}
} }
} }
} }

View File

@ -44,21 +44,30 @@ impl fmt::Debug for InternalEvent {
pub struct BkptInfo {} pub struct BkptInfo {}
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> { pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
/// Creates a new module code generator.
fn new() -> Self; fn new() -> Self;
/// Returns the backend id associated with this MCG.
fn backend_id() -> Backend; fn backend_id() -> Backend;
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
/// Creates a new function and returns the function-scope code generator for it.
fn next_function(&mut self, module_info: Arc<RwLock<ModuleInfo>>) -> Result<&mut FCG, E>;
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
/// Sets function signatures.
fn feed_function_signatures(&mut self, assoc: Map<FuncIndex, SigIndex>) -> Result<(), E>;
/// Feeds the compiler config.
fn feed_compiler_config(&mut self, _config: &CompilerConfig) -> Result<(), E> {
Ok(())
}
/// Adds an import function. /// Adds an import function.
fn feed_import_function(&mut self) -> Result<(), E>; fn feed_import_function(&mut self) -> Result<(), E>;
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
/// Sets function signatures.
fn feed_function_signatures(&mut self, assoc: Map<FuncIndex, SigIndex>) -> Result<(), E>;
/// Checks the precondition for a module.
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
/// Creates a new function and returns the function-scope code generator for it.
fn next_function(&mut self, module_info: Arc<RwLock<ModuleInfo>>) -> Result<&mut FCG, E>;
/// Finalizes this module.
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
/// Creates a module from cache.
unsafe fn from_cache(cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>; unsafe fn from_cache(cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
} }

View File

@ -47,6 +47,7 @@ impl IsExport for Export {
pub struct ImportObject { pub struct ImportObject {
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>, map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>, state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub allow_missing_functions: bool,
} }
impl ImportObject { impl ImportObject {
@ -55,6 +56,7 @@ impl ImportObject {
Self { Self {
map: Rc::new(RefCell::new(HashMap::new())), map: Rc::new(RefCell::new(HashMap::new())),
state_creator: None, state_creator: None,
allow_missing_functions: false,
} }
} }
@ -65,6 +67,7 @@ impl ImportObject {
Self { Self {
map: Rc::new(RefCell::new(HashMap::new())), map: Rc::new(RefCell::new(HashMap::new())),
state_creator: Some(Rc::new(state_creator)), state_creator: Some(Rc::new(state_creator)),
allow_missing_functions: false,
} }
} }
@ -116,6 +119,7 @@ impl ImportObject {
Self { Self {
map: Rc::clone(&self.map), map: Rc::clone(&self.map),
state_creator: self.state_creator.clone(), state_creator: self.state_creator.clone(),
allow_missing_functions: false,
} }
} }

View File

@ -5,9 +5,11 @@ use crate::{
export::{Context, Export, ExportIter, FuncPointer}, export::{Context, Export, ExportIter, FuncPointer},
global::Global, global::Global,
import::{ImportObject, LikeNamespace}, import::{ImportObject, LikeNamespace},
loader::Loader,
memory::Memory, memory::Memory,
module::{ExportIndex, Module, ModuleInfo, ModuleInner}, module::{ExportIndex, Module, ModuleInfo, ModuleInner},
sig_registry::SigRegistry, sig_registry::SigRegistry,
structures::TypedIndex,
table::Table, table::Table,
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
@ -38,7 +40,7 @@ impl Drop for InstanceInner {
/// ///
/// [`ImportObject`]: struct.ImportObject.html /// [`ImportObject`]: struct.ImportObject.html
pub struct Instance { pub struct Instance {
module: Arc<ModuleInner>, pub module: Arc<ModuleInner>,
inner: Box<InstanceInner>, inner: Box<InstanceInner>,
#[allow(dead_code)] #[allow(dead_code)]
import_object: ImportObject, import_object: ImportObject,
@ -127,6 +129,12 @@ impl Instance {
Ok(instance) Ok(instance)
} }
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
loader.load(&*self.module.runnable_module, &self.module.info, unsafe {
&*self.inner.vmctx
})
}
/// Through generic magic and the awe-inspiring power of traits, we bring you... /// Through generic magic and the awe-inspiring power of traits, we bring you...
/// ///
/// # "Func" /// # "Func"
@ -214,6 +222,26 @@ impl Instance {
} }
} }
pub fn resolve_func(&self, name: &str) -> ResolveResult<usize> {
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
name: name.to_string(),
})?;
if let ExportIndex::Func(func_index) = export_index {
Ok(func_index.index())
} else {
Err(ResolveError::ExportWrongType {
name: name.to_string(),
}
.into())
}
}
/// This returns the representation of a function that can be called /// This returns the representation of a function that can be called
/// safely. /// safely.
/// ///

View File

@ -21,6 +21,7 @@ pub mod export;
pub mod global; pub mod global;
pub mod import; pub mod import;
pub mod instance; pub mod instance;
pub mod loader;
pub mod memory; pub mod memory;
pub mod module; pub mod module;
pub mod parse; pub mod parse;

View File

@ -0,0 +1,169 @@
use crate::{backend::RunnableModule, module::ModuleInfo, types::Value, vm::Ctx};
#[cfg(unix)]
use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
pub trait Loader {
type Instance: Instance;
type Error: Debug;
fn load(
&self,
rm: &dyn RunnableModule,
module: &ModuleInfo,
ctx: &Ctx,
) -> Result<Self::Instance, Self::Error>;
}
pub trait Instance {
type Error: Debug;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
unimplemented!()
}
fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
unimplemented!()
}
}
pub struct LocalLoader;
impl Loader for LocalLoader {
type Instance = LocalInstance;
type Error = String;
fn load(
&self,
rm: &dyn RunnableModule,
_module: &ModuleInfo,
_ctx: &Ctx,
) -> Result<Self::Instance, Self::Error> {
let code = rm.get_code().unwrap();
let mut code_mem = CodeMemory::new(code.len());
code_mem[..code.len()].copy_from_slice(code);
code_mem.make_executable();
Ok(LocalInstance {
code: code_mem,
offsets: rm.get_offsets().unwrap(),
})
}
}
pub struct LocalInstance {
code: CodeMemory,
offsets: Vec<usize>,
}
impl Instance for LocalInstance {
type Error = String;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error> {
let offset = self.offsets[id];
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
use std::mem::transmute;
Ok(unsafe {
match args.len() {
0 => (transmute::<_, extern "C" fn() -> u64>(addr))(),
1 => (transmute::<_, extern "C" fn(u64) -> u64>(addr))(args[0].to_u64()),
2 => (transmute::<_, extern "C" fn(u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
),
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
),
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
args[3].to_u64(),
),
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
args[3].to_u64(),
args[4].to_u64(),
),
_ => return Err("too many arguments".into()),
}
})
}
}
pub struct CodeMemory {
ptr: *mut u8,
size: usize,
}
#[cfg(not(unix))]
impl CodeMemory {
pub fn new(_size: usize) -> CodeMemory {
unimplemented!();
}
pub fn make_executable(&mut self) {
unimplemented!();
}
}
#[cfg(unix)]
impl CodeMemory {
pub fn new(size: usize) -> CodeMemory {
fn round_up_to_page_size(size: usize) -> usize {
(size + (4096 - 1)) & !(4096 - 1)
}
let size = round_up_to_page_size(size);
let ptr = unsafe {
mmap(
::std::ptr::null_mut(),
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if ptr as isize == -1 {
panic!("cannot allocate code memory");
}
CodeMemory {
ptr: ptr as _,
size: size,
}
}
pub fn make_executable(&mut self) {
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
panic!("cannot set code memory to executable");
}
}
}
#[cfg(unix)]
impl Drop for CodeMemory {
fn drop(&mut self) {
unsafe {
munmap(self.ptr as _, self.size);
}
}
}
impl Deref for CodeMemory {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { ::std::slice::from_raw_parts(self.ptr, self.size) }
}
}
impl DerefMut for CodeMemory {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { ::std::slice::from_raw_parts_mut(self.ptr, self.size) }
}
}

View File

@ -18,6 +18,24 @@ macro_rules! debug {
($fmt:expr, $($arg:tt)*) => {}; ($fmt:expr, $($arg:tt)*) => {};
} }
#[macro_export]
#[cfg(feature = "trace")]
macro_rules! trace {
($fmt:expr) => {
debug!($fmt)
};
($fmt:expr, $($arg:tt)*) => {
debug!($fmt, $($arg)*);
}
}
#[macro_export]
#[cfg(not(feature = "trace"))]
macro_rules! trace {
($fmt:expr) => {};
($fmt:expr, $($arg:tt)*) => {};
}
#[macro_export] #[macro_export]
macro_rules! func { macro_rules! func {
($func:path) => {{ ($func:path) => {{

View File

@ -54,6 +54,8 @@ pub fn read_module<
middlewares: &mut MiddlewareChain, middlewares: &mut MiddlewareChain,
compiler_config: &CompilerConfig, compiler_config: &CompilerConfig,
) -> Result<Arc<RwLock<ModuleInfo>>, LoadError> { ) -> Result<Arc<RwLock<ModuleInfo>>, LoadError> {
mcg.feed_compiler_config(compiler_config)
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
let info = Arc::new(RwLock::new(ModuleInfo { let info = Arc::new(RwLock::new(ModuleInfo {
memories: Map::new(), memories: Map::new(),
globals: Map::new(), globals: Map::new(),

View File

@ -45,6 +45,15 @@ impl Value {
Value::F64(_) => Type::F64, Value::F64(_) => Type::F64,
} }
} }
pub fn to_u64(&self) -> u64 {
match *self {
Value::I32(x) => x as u32 as u64,
Value::I64(x) => x as u64,
Value::F32(x) => f32::to_bits(x) as u64,
Value::F64(x) => f64::to_bits(x),
}
}
} }
impl From<i32> for Value { impl From<i32> for Value {

View File

@ -1,9 +1,10 @@
pub use crate::backing::{ImportBacking, LocalBacking}; pub use crate::backing::{ImportBacking, LocalBacking};
use crate::{ use crate::{
memory::Memory, memory::{Memory, MemoryType},
module::ModuleInner, module::{ModuleInfo, ModuleInner},
structures::TypedIndex, structures::TypedIndex,
types::{LocalOrImport, MemoryIndex}, types::{LocalOrImport, MemoryIndex},
vmcalls,
}; };
use std::{ffi::c_void, mem, ptr}; use std::{ffi::c_void, mem, ptr};
@ -25,7 +26,7 @@ use hashbrown::HashMap;
#[repr(C)] #[repr(C)]
pub struct Ctx { pub struct Ctx {
// `internal` must be the first field of `Ctx`. // `internal` must be the first field of `Ctx`.
pub(crate) internal: InternalCtx, pub internal: InternalCtx,
pub(crate) local_functions: *const *const Func, pub(crate) local_functions: *const *const Func,
@ -84,6 +85,83 @@ pub struct InternalCtx {
/// signature id. This is used to allow call-indirect to other /// signature id. This is used to allow call-indirect to other
/// modules safely. /// modules safely.
pub dynamic_sigindices: *const SigId, pub dynamic_sigindices: *const SigId,
pub intrinsics: *const Intrinsics,
pub stack_lower_bound: *mut u8,
pub memory_base: *mut u8,
pub memory_bound: usize,
}
#[repr(C)]
pub struct Intrinsics {
pub memory_grow: *const Func,
pub memory_size: *const Func,
/*pub memory_grow: unsafe extern "C" fn(
ctx: &mut Ctx,
memory_index: usize,
delta: Pages,
) -> i32,
pub memory_size: unsafe extern "C" fn(
ctx: &Ctx,
memory_index: usize,
) -> Pages,*/
}
unsafe impl Send for Intrinsics {}
unsafe impl Sync for Intrinsics {}
impl Intrinsics {
#[allow(clippy::erasing_op)]
pub fn offset_memory_grow() -> u8 {
(0 * ::std::mem::size_of::<usize>()) as u8
}
pub fn offset_memory_size() -> u8 {
(1 * ::std::mem::size_of::<usize>()) as u8
}
}
pub static INTRINSICS_LOCAL_STATIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::local_static_memory_grow as _,
memory_size: vmcalls::local_static_memory_size as _,
};
pub static INTRINSICS_LOCAL_DYNAMIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::local_dynamic_memory_grow as _,
memory_size: vmcalls::local_dynamic_memory_size as _,
};
pub static INTRINSICS_IMPORTED_STATIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::imported_static_memory_grow as _,
memory_size: vmcalls::imported_static_memory_size as _,
};
pub static INTRINSICS_IMPORTED_DYNAMIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::imported_dynamic_memory_grow as _,
memory_size: vmcalls::imported_dynamic_memory_size as _,
};
fn get_intrinsics_for_module(m: &ModuleInfo) -> *const Intrinsics {
if m.memories.len() == 0 && m.imported_memories.len() == 0 {
::std::ptr::null()
} else {
match MemoryIndex::new(0).local_or_import(m) {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &m.memories[local_mem_index];
match mem_desc.memory_type() {
MemoryType::Dynamic => &INTRINSICS_LOCAL_DYNAMIC_MEMORY,
MemoryType::Static => &INTRINSICS_LOCAL_STATIC_MEMORY,
MemoryType::SharedStatic => unimplemented!(),
}
}
LocalOrImport::Import(import_mem_index) => {
let mem_desc = &m.imported_memories[import_mem_index].1;
match mem_desc.memory_type() {
MemoryType::Dynamic => &INTRINSICS_IMPORTED_DYNAMIC_MEMORY,
MemoryType::Static => &INTRINSICS_IMPORTED_STATIC_MEMORY,
MemoryType::SharedStatic => unimplemented!(),
}
}
}
}
} }
impl Ctx { impl Ctx {
@ -93,6 +171,16 @@ impl Ctx {
import_backing: &mut ImportBacking, import_backing: &mut ImportBacking,
module: &ModuleInner, module: &ModuleInner,
) -> Self { ) -> Self {
let (mem_base, mem_bound): (*mut u8, usize) =
if module.info.memories.len() == 0 && module.info.imported_memories.len() == 0 {
(::std::ptr::null_mut(), 0)
} else {
let mem = match MemoryIndex::new(0).local_or_import(&module.info) {
LocalOrImport::Local(index) => local_backing.vm_memories[index],
LocalOrImport::Import(index) => import_backing.vm_memories[index],
};
((*mem).base, (*mem).bound)
};
Self { Self {
internal: InternalCtx { internal: InternalCtx {
memories: local_backing.vm_memories.as_mut_ptr(), memories: local_backing.vm_memories.as_mut_ptr(),
@ -105,6 +193,13 @@ impl Ctx {
imported_funcs: import_backing.vm_functions.as_mut_ptr(), imported_funcs: import_backing.vm_functions.as_mut_ptr(),
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
intrinsics: get_intrinsics_for_module(&module.info),
stack_lower_bound: ::std::ptr::null_mut(),
memory_base: mem_base,
memory_bound: mem_bound,
}, },
local_functions: local_backing.local_functions.as_ptr(), local_functions: local_backing.local_functions.as_ptr(),
@ -125,6 +220,16 @@ impl Ctx {
data: *mut c_void, data: *mut c_void,
data_finalizer: fn(*mut c_void), data_finalizer: fn(*mut c_void),
) -> Self { ) -> Self {
let (mem_base, mem_bound): (*mut u8, usize) =
if module.info.memories.len() == 0 && module.info.imported_memories.len() == 0 {
(::std::ptr::null_mut(), 0)
} else {
let mem = match MemoryIndex::new(0).local_or_import(&module.info) {
LocalOrImport::Local(index) => local_backing.vm_memories[index],
LocalOrImport::Import(index) => import_backing.vm_memories[index],
};
((*mem).base, (*mem).bound)
};
Self { Self {
internal: InternalCtx { internal: InternalCtx {
memories: local_backing.vm_memories.as_mut_ptr(), memories: local_backing.vm_memories.as_mut_ptr(),
@ -137,6 +242,13 @@ impl Ctx {
imported_funcs: import_backing.vm_functions.as_mut_ptr(), imported_funcs: import_backing.vm_functions.as_mut_ptr(),
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(), dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
intrinsics: get_intrinsics_for_module(&module.info),
stack_lower_bound: ::std::ptr::null_mut(),
memory_base: mem_base,
memory_bound: mem_bound,
}, },
local_functions: local_backing.local_functions.as_ptr(), local_functions: local_backing.local_functions.as_ptr(),
@ -186,6 +298,11 @@ impl Ctx {
pub unsafe fn borrow_symbol_map(&self) -> &Option<HashMap<u32, String>> { pub unsafe fn borrow_symbol_map(&self) -> &Option<HashMap<u32, String>> {
&(*self.module).info.em_symbol_map &(*self.module).info.em_symbol_map
} }
/// Returns the number of dynamic sigindices.
pub fn dynamic_sigindice_count(&self) -> usize {
unsafe { (*self.local_backing).dynamic_sigindices.len() }
}
} }
#[doc(hidden)] #[doc(hidden)]
@ -223,9 +340,25 @@ impl Ctx {
7 * (mem::size_of::<usize>() as u8) 7 * (mem::size_of::<usize>() as u8)
} }
pub fn offset_local_functions() -> u8 { pub fn offset_intrinsics() -> u8 {
8 * (mem::size_of::<usize>() as u8) 8 * (mem::size_of::<usize>() as u8)
} }
pub fn offset_stack_lower_bound() -> u8 {
9 * (mem::size_of::<usize>() as u8)
}
pub fn offset_memory_base() -> u8 {
10 * (mem::size_of::<usize>() as u8)
}
pub fn offset_memory_bound() -> u8 {
11 * (mem::size_of::<usize>() as u8)
}
pub fn offset_local_functions() -> u8 {
12 * (mem::size_of::<usize>() as u8)
}
} }
enum InnerFunc {} enum InnerFunc {}
@ -419,6 +552,26 @@ mod vm_offset_tests {
offset_of!(InternalCtx => imported_funcs).get_byte_offset(), offset_of!(InternalCtx => imported_funcs).get_byte_offset(),
); );
assert_eq!(
Ctx::offset_intrinsics() as usize,
offset_of!(InternalCtx => intrinsics).get_byte_offset(),
);
assert_eq!(
Ctx::offset_stack_lower_bound() as usize,
offset_of!(InternalCtx => stack_lower_bound).get_byte_offset(),
);
assert_eq!(
Ctx::offset_memory_base() as usize,
offset_of!(InternalCtx => memory_base).get_byte_offset(),
);
assert_eq!(
Ctx::offset_memory_bound() as usize,
offset_of!(InternalCtx => memory_bound).get_byte_offset(),
);
assert_eq!( assert_eq!(
Ctx::offset_local_functions() as usize, Ctx::offset_local_functions() as usize,
offset_of!(Ctx => local_functions).get_byte_offset(), offset_of!(Ctx => local_functions).get_byte_offset(),

View File

@ -20,10 +20,15 @@ pub unsafe extern "C" fn local_static_memory_grow(
let local_memory = *ctx.internal.memories.add(memory_index.index()); let local_memory = *ctx.internal.memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut StaticMemory; let memory = (*local_memory).memory as *mut StaticMemory;
match (*memory).grow(delta, &mut *local_memory) { let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32, Ok(old) => old.0 as i32,
Err(_) => -1, Err(_) => -1,
} };
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
} }
pub unsafe extern "C" fn local_static_memory_size( pub unsafe extern "C" fn local_static_memory_size(
@ -44,10 +49,15 @@ pub unsafe extern "C" fn local_dynamic_memory_grow(
let local_memory = *ctx.internal.memories.add(memory_index.index()); let local_memory = *ctx.internal.memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut DynamicMemory; let memory = (*local_memory).memory as *mut DynamicMemory;
match (*memory).grow(delta, &mut *local_memory) { let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32, Ok(old) => old.0 as i32,
Err(_) => -1, Err(_) => -1,
} };
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
} }
pub unsafe extern "C" fn local_dynamic_memory_size( pub unsafe extern "C" fn local_dynamic_memory_size(
@ -75,10 +85,15 @@ pub unsafe extern "C" fn imported_static_memory_grow(
.add(import_memory_index.index()); .add(import_memory_index.index());
let memory = (*local_memory).memory as *mut StaticMemory; let memory = (*local_memory).memory as *mut StaticMemory;
match (*memory).grow(delta, &mut *local_memory) { let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32, Ok(old) => old.0 as i32,
Err(_) => -1, Err(_) => -1,
} };
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
} }
pub unsafe extern "C" fn imported_static_memory_size( pub unsafe extern "C" fn imported_static_memory_size(
@ -102,10 +117,15 @@ pub unsafe extern "C" fn imported_dynamic_memory_grow(
let local_memory = *ctx.internal.imported_memories.add(memory_index.index()); let local_memory = *ctx.internal.imported_memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut DynamicMemory; let memory = (*local_memory).memory as *mut DynamicMemory;
match (*memory).grow(delta, &mut *local_memory) { let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32, Ok(old) => old.0 as i32,
Err(_) => -1, Err(_) => -1,
} };
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
} }
pub unsafe extern "C" fn imported_dynamic_memory_size( pub unsafe extern "C" fn imported_dynamic_memory_size(

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-runtime" name = "wasmer-runtime"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime library" description = "Wasmer runtime library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,17 +9,17 @@ edition = "2018"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
lazy_static = "1.2.0" lazy_static = "1.2.0"
memmap = "0.7.0" memmap = "0.7.0"
[dependencies.wasmer-runtime-core] [dependencies.wasmer-runtime-core]
path = "../runtime-core" path = "../runtime-core"
version = "0.4.1" version = "0.4.2"
[dependencies.wasmer-clif-backend] [dependencies.wasmer-clif-backend]
path = "../clif-backend" path = "../clif-backend"
version = "0.4.1" version = "0.4.2"
optional = true optional = true
[dev-dependencies] [dev-dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-singlepass-backend" name = "wasmer-singlepass-backend"
version = "0.4.1" version = "0.4.2"
repository = "https://github.com/wasmerio/wasmer" repository = "https://github.com/wasmerio/wasmer"
description = "Wasmer runtime single pass compiler backend" description = "Wasmer runtime single pass compiler backend"
license = "MIT" license = "MIT"
@ -8,9 +8,9 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
wasmparser = "0.29.2" wasmparser = "0.29.2"
dynasm = "0.3.1" dynasm = "0.3.2"
dynasmrt = "0.3.1" dynasmrt = "0.3.1"
lazy_static = "1.2.0" lazy_static = "1.2.0"
byteorder = "1" byteorder = "1"

View File

@ -14,7 +14,9 @@ use std::{
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::{sys::Memory, Backend, CacheGen, RunnableModule, Token}, backend::{
sys::Memory, Backend, CacheGen, CompilerConfig, MemoryBoundCheckMode, RunnableModule, Token,
},
cache::{Artifact, Error as CacheError}, cache::{Artifact, Error as CacheError},
codegen::*, codegen::*,
memory::MemoryType, memory::MemoryType,
@ -25,8 +27,7 @@ use wasmer_runtime_core::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
TableIndex, Type, TableIndex, Type,
}, },
vm::{self, LocalGlobal, LocalMemory, LocalTable}, vm::{self, LocalGlobal, LocalTable},
vmcalls,
}; };
use wasmparser::{Operator, Type as WpType}; use wasmparser::{Operator, Type as WpType};
@ -125,6 +126,8 @@ pub struct X64ModuleCodeGenerator {
function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>, function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>,
assembler: Option<Assembler>, assembler: Option<Assembler>,
func_import_count: usize, func_import_count: usize,
config: Option<Arc<CodegenConfig>>,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -139,7 +142,6 @@ pub struct X64FunctionCode {
assembler: Option<Assembler>, assembler: Option<Assembler>,
function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>, function_labels: Option<HashMap<usize, (DynamicLabel, Option<AssemblyOffset>)>>,
br_table_data: Option<Vec<Vec<usize>>>,
breakpoints: Option<HashMap<AssemblyOffset, Box<Fn(BkptInfo) + Send + Sync + 'static>>>, breakpoints: Option<HashMap<AssemblyOffset, Box<Fn(BkptInfo) + Send + Sync + 'static>>>,
returns: SmallVec<[WpType; 1]>, returns: SmallVec<[WpType; 1]>,
locals: Vec<Location>, locals: Vec<Location>,
@ -149,6 +151,8 @@ pub struct X64FunctionCode {
control_stack: Vec<ControlFrame>, control_stack: Vec<ControlFrame>,
machine: Machine, machine: Machine,
unreachable_depth: usize, unreachable_depth: usize,
config: Arc<CodegenConfig>,
} }
enum FuncPtrInner {} enum FuncPtrInner {}
@ -164,8 +168,8 @@ pub struct X64ExecutionContext {
#[allow(dead_code)] #[allow(dead_code)]
functions: Vec<X64FunctionCode>, functions: Vec<X64FunctionCode>,
function_pointers: Vec<FuncPtr>, function_pointers: Vec<FuncPtr>,
function_offsets: Vec<AssemblyOffset>,
signatures: Arc<Map<SigIndex, FuncSig>>, signatures: Arc<Map<SigIndex, FuncSig>>,
_br_table_data: Vec<Vec<usize>>,
breakpoints: Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>, breakpoints: Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>,
func_import_count: usize, func_import_count: usize,
} }
@ -275,6 +279,14 @@ impl RunnableModule for X64ExecutionContext {
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data))); protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
protect_unix::trigger_trap(); protect_unix::trigger_trap();
} }
fn get_code(&self) -> Option<&[u8]> {
Some(&self.code)
}
fn get_offsets(&self) -> Option<Vec<usize>> {
Some(self.function_offsets.iter().map(|x| x.0).collect())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -282,6 +294,12 @@ pub struct CodegenError {
pub message: &'static str, pub message: &'static str,
} }
#[derive(Copy, Clone, Debug)]
struct CodegenConfig {
memory_bound_check_mode: MemoryBoundCheckMode,
enforce_stack_check: bool,
}
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError> impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
for X64ModuleCodeGenerator for X64ModuleCodeGenerator
{ {
@ -293,6 +311,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
function_labels: Some(HashMap::new()), function_labels: Some(HashMap::new()),
assembler: Some(Assembler::new().unwrap()), assembler: Some(Assembler::new().unwrap()),
func_import_count: 0, func_import_count: 0,
config: None,
} }
} }
@ -308,21 +327,19 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
&mut self, &mut self,
_module_info: Arc<RwLock<ModuleInfo>>, _module_info: Arc<RwLock<ModuleInfo>>,
) -> Result<&mut X64FunctionCode, CodegenError> { ) -> Result<&mut X64FunctionCode, CodegenError> {
let (mut assembler, mut function_labels, br_table_data, breakpoints) = let (mut assembler, mut function_labels, breakpoints) = match self.functions.last_mut() {
match self.functions.last_mut() { Some(x) => (
Some(x) => ( x.assembler.take().unwrap(),
x.assembler.take().unwrap(), x.function_labels.take().unwrap(),
x.function_labels.take().unwrap(), x.breakpoints.take().unwrap(),
x.br_table_data.take().unwrap(), ),
x.breakpoints.take().unwrap(), None => (
), self.assembler.take().unwrap(),
None => ( self.function_labels.take().unwrap(),
self.assembler.take().unwrap(), HashMap::new(),
self.function_labels.take().unwrap(), ),
vec![], };
HashMap::new(),
),
};
let begin_offset = assembler.offset(); let begin_offset = assembler.offset();
let begin_label_info = function_labels let begin_label_info = function_labels
.entry(self.functions.len() + self.func_import_count) .entry(self.functions.len() + self.func_import_count)
@ -342,7 +359,6 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
assembler: Some(assembler), assembler: Some(assembler),
function_labels: Some(function_labels), function_labels: Some(function_labels),
br_table_data: Some(br_table_data),
breakpoints: Some(breakpoints), breakpoints: Some(breakpoints),
returns: smallvec![], returns: smallvec![],
locals: vec![], locals: vec![],
@ -352,6 +368,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
control_stack: vec![], control_stack: vec![],
machine: Machine::new(), machine: Machine::new(),
unreachable_depth: 0, unreachable_depth: 0,
config: self.config.as_ref().unwrap().clone(),
}; };
self.functions.push(code); self.functions.push(code);
Ok(self.functions.last_mut().unwrap()) Ok(self.functions.last_mut().unwrap())
@ -361,12 +378,8 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
mut self, mut self,
_: &ModuleInfo, _: &ModuleInfo,
) -> Result<(X64ExecutionContext, Box<dyn CacheGen>), CodegenError> { ) -> Result<(X64ExecutionContext, Box<dyn CacheGen>), CodegenError> {
let (assembler, mut br_table_data, breakpoints) = match self.functions.last_mut() { let (assembler, breakpoints) = match self.functions.last_mut() {
Some(x) => ( Some(x) => (x.assembler.take().unwrap(), x.breakpoints.take().unwrap()),
x.assembler.take().unwrap(),
x.br_table_data.take().unwrap(),
x.breakpoints.take().unwrap(),
),
None => { None => {
return Err(CodegenError { return Err(CodegenError {
message: "no function", message: "no function",
@ -375,18 +388,13 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
}; };
let output = assembler.finalize().unwrap(); let output = assembler.finalize().unwrap();
for table in &mut br_table_data {
for entry in table {
*entry = output.ptr(AssemblyOffset(*entry)) as usize;
}
}
let function_labels = if let Some(x) = self.functions.last() { let function_labels = if let Some(x) = self.functions.last() {
x.function_labels.as_ref().unwrap() x.function_labels.as_ref().unwrap()
} else { } else {
self.function_labels.as_ref().unwrap() self.function_labels.as_ref().unwrap()
}; };
let mut out_labels: Vec<FuncPtr> = vec![]; let mut out_labels: Vec<FuncPtr> = vec![];
let mut out_offsets: Vec<AssemblyOffset> = vec![];
for i in 0..function_labels.len() { for i in 0..function_labels.len() {
let (_, offset) = match function_labels.get(&i) { let (_, offset) = match function_labels.get(&i) {
@ -406,6 +414,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
} }
}; };
out_labels.push(FuncPtr(output.ptr(*offset) as _)); out_labels.push(FuncPtr(output.ptr(*offset) as _));
out_offsets.push(*offset);
} }
let breakpoints: Arc<HashMap<_, _>> = Arc::new( let breakpoints: Arc<HashMap<_, _>> = Arc::new(
@ -423,16 +432,15 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
)) ))
} }
} }
Ok(( Ok((
X64ExecutionContext { X64ExecutionContext {
code: output, code: output,
functions: self.functions, functions: self.functions,
signatures: self.signatures.as_ref().unwrap().clone(), signatures: self.signatures.as_ref().unwrap().clone(),
_br_table_data: br_table_data,
breakpoints: breakpoints, breakpoints: breakpoints,
func_import_count: self.func_import_count, func_import_count: self.func_import_count,
function_pointers: out_labels, function_pointers: out_labels,
function_offsets: out_offsets,
}, },
Box::new(Placeholder), Box::new(Placeholder),
)) ))
@ -485,6 +493,13 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
Ok(()) Ok(())
} }
fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> {
self.config = Some(Arc::new(CodegenConfig {
memory_bound_check_mode: config.memory_bound_check_mode,
enforce_stack_check: config.enforce_stack_check,
}));
Ok(())
}
unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> { unsafe fn from_cache(_artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
Err(CacheError::Unknown( Err(CacheError::Unknown(
"the singlepass compiler API doesn't support caching yet".to_string(), "the singlepass compiler API doesn't support caching yet".to_string(),
@ -1203,6 +1218,7 @@ impl X64FunctionCode {
/// Emits a memory operation. /// Emits a memory operation.
fn emit_memory_op<F: FnOnce(&mut Assembler, &mut Machine, GPR)>( fn emit_memory_op<F: FnOnce(&mut Assembler, &mut Machine, GPR)>(
module_info: &ModuleInfo, module_info: &ModuleInfo,
config: &CodegenConfig,
a: &mut Assembler, a: &mut Assembler,
m: &mut Machine, m: &mut Machine,
addr: Location, addr: Location,
@ -1210,41 +1226,6 @@ impl X64FunctionCode {
value_size: usize, value_size: usize,
cb: F, cb: F,
) { ) {
let tmp_addr = m.acquire_temp_gpr().unwrap();
let tmp_base = m.acquire_temp_gpr().unwrap();
let tmp_bound = m.acquire_temp_gpr().unwrap();
// Loads both base and bound into temporary registers.
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
match MemoryIndex::new(0).local_or_import(module_info) {
LocalOrImport::Local(_) => vm::Ctx::offset_memories(),
LocalOrImport::Import(_) => vm::Ctx::offset_imported_memories(),
} as i32,
),
Location::GPR(tmp_base),
);
a.emit_mov(
Size::S64,
Location::Memory(tmp_base, 0),
Location::GPR(tmp_base),
);
a.emit_mov(
Size::S32,
Location::Memory(tmp_base, LocalMemory::offset_bound() as i32),
Location::GPR(tmp_bound),
);
a.emit_mov(
Size::S64,
Location::Memory(tmp_base, LocalMemory::offset_base() as i32),
Location::GPR(tmp_base),
);
// Adds base to bound so `tmp_bound` now holds the end of linear memory.
a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound));
// If the memory is dynamic, we need to do bound checking at runtime. // If the memory is dynamic, we need to do bound checking at runtime.
let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) { let mem_desc = match MemoryIndex::new(0).local_or_import(module_info) {
LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index], LocalOrImport::Local(local_mem_index) => &module_info.memories[local_mem_index],
@ -1252,12 +1233,40 @@ impl X64FunctionCode {
&module_info.imported_memories[import_mem_index].1 &module_info.imported_memories[import_mem_index].1
} }
}; };
let need_check = match mem_desc.memory_type() { let need_check = match config.memory_bound_check_mode {
MemoryType::Dynamic => true, MemoryBoundCheckMode::Default => match mem_desc.memory_type() {
MemoryType::Static | MemoryType::SharedStatic => false, MemoryType::Dynamic => true,
MemoryType::Static | MemoryType::SharedStatic => false,
},
MemoryBoundCheckMode::Enable => true,
MemoryBoundCheckMode::Disable => false,
}; };
let tmp_addr = m.acquire_temp_gpr().unwrap();
let tmp_base = m.acquire_temp_gpr().unwrap();
let tmp_bound = m.acquire_temp_gpr().unwrap();
// Load base into temporary register.
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
vm::Ctx::offset_memory_base() as i32,
),
Location::GPR(tmp_base),
);
if need_check { if need_check {
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
vm::Ctx::offset_memory_bound() as i32,
),
Location::GPR(tmp_bound),
);
// Adds base to bound so `tmp_bound` now holds the end of linear memory.
a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_bound));
a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); a.emit_mov(Size::S32, addr, Location::GPR(tmp_addr));
// This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range. // This branch is used for emitting "faster" code for the special case of (offset + value_size) not exceeding u32 range.
@ -1421,6 +1430,19 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
a.emit_push(Size::S64, Location::GPR(GPR::RBP)); a.emit_push(Size::S64, Location::GPR(GPR::RBP));
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP));
// Stack check.
if self.config.enforce_stack_check {
a.emit_cmp(
Size::S64,
Location::Memory(
GPR::RDI, // first parameter is vmctx
vm::Ctx::offset_stack_lower_bound() as i32,
),
Location::GPR(GPR::RSP),
);
a.emit_conditional_trap(Condition::Below);
}
self.locals = self self.locals = self
.machine .machine
.init_locals(a, self.num_locals, self.num_params); .init_locals(a, self.num_locals, self.num_params);
@ -3314,33 +3336,23 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Operator::Nop => {} Operator::Nop => {}
Operator::MemorySize { reserved } => { Operator::MemorySize { reserved } => {
let memory_index = MemoryIndex::new(reserved as usize); let memory_index = MemoryIndex::new(reserved as usize);
let target: usize = match memory_index.local_or_import(module_info) { a.emit_mov(
LocalOrImport::Local(local_mem_index) => { Size::S64,
let mem_desc = &module_info.memories[local_mem_index]; Location::Memory(
match mem_desc.memory_type() { Machine::get_vmctx_reg(),
MemoryType::Dynamic => vmcalls::local_dynamic_memory_size as usize, vm::Ctx::offset_intrinsics() as i32,
MemoryType::Static => vmcalls::local_static_memory_size as usize, ),
MemoryType::SharedStatic => unimplemented!(), Location::GPR(GPR::RAX),
} );
} a.emit_mov(
LocalOrImport::Import(import_mem_index) => { Size::S64,
let mem_desc = &module_info.imported_memories[import_mem_index].1; Location::Memory(GPR::RAX, vm::Intrinsics::offset_memory_size() as i32),
match mem_desc.memory_type() { Location::GPR(GPR::RAX),
MemoryType::Dynamic => vmcalls::imported_dynamic_memory_size as usize, );
MemoryType::Static => vmcalls::imported_static_memory_size as usize,
MemoryType::SharedStatic => unimplemented!(),
}
}
};
Self::emit_call_sysv( Self::emit_call_sysv(
a, a,
&mut self.machine, &mut self.machine,
|a| { |a| {
a.emit_mov(
Size::S64,
Location::Imm64(target as u64),
Location::GPR(GPR::RAX),
);
a.emit_call_location(Location::GPR(GPR::RAX)); a.emit_call_location(Location::GPR(GPR::RAX));
}, },
::std::iter::once(Location::Imm32(memory_index.index() as u32)), ::std::iter::once(Location::Imm32(memory_index.index() as u32)),
@ -3351,40 +3363,30 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
} }
Operator::MemoryGrow { reserved } => { Operator::MemoryGrow { reserved } => {
let memory_index = MemoryIndex::new(reserved as usize); let memory_index = MemoryIndex::new(reserved as usize);
let target: usize = match memory_index.local_or_import(module_info) {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &module_info.memories[local_mem_index];
match mem_desc.memory_type() {
MemoryType::Dynamic => vmcalls::local_dynamic_memory_grow as usize,
MemoryType::Static => vmcalls::local_static_memory_grow as usize,
MemoryType::SharedStatic => unimplemented!(),
}
}
LocalOrImport::Import(import_mem_index) => {
let mem_desc = &module_info.imported_memories[import_mem_index].1;
match mem_desc.memory_type() {
MemoryType::Dynamic => vmcalls::imported_dynamic_memory_grow as usize,
MemoryType::Static => vmcalls::imported_static_memory_grow as usize,
MemoryType::SharedStatic => unimplemented!(),
}
}
};
let (param_pages, param_pages_lot) = self.value_stack.pop().unwrap(); let (param_pages, param_pages_lot) = self.value_stack.pop().unwrap();
if param_pages_lot == LocalOrTemp::Temp { if param_pages_lot == LocalOrTemp::Temp {
self.machine.release_locations_only_regs(&[param_pages]); self.machine.release_locations_only_regs(&[param_pages]);
} }
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
vm::Ctx::offset_intrinsics() as i32,
),
Location::GPR(GPR::RAX),
);
a.emit_mov(
Size::S64,
Location::Memory(GPR::RAX, vm::Intrinsics::offset_memory_grow() as i32),
Location::GPR(GPR::RAX),
);
Self::emit_call_sysv( Self::emit_call_sysv(
a, a,
&mut self.machine, &mut self.machine,
|a| { |a| {
a.emit_mov(
Size::S64,
Location::Imm64(target as u64),
Location::GPR(GPR::RAX),
);
a.emit_call_location(Location::GPR(GPR::RAX)); a.emit_call_location(Location::GPR(GPR::RAX));
}, },
::std::iter::once(Location::Imm32(memory_index.index() as u32)) ::std::iter::once(Location::Imm32(memory_index.index() as u32))
@ -3407,6 +3409,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3432,6 +3435,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3457,6 +3461,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3483,6 +3488,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3509,6 +3515,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3535,6 +3542,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3561,6 +3569,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3586,6 +3595,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3611,6 +3621,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3636,6 +3647,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3661,6 +3673,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3686,6 +3699,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3711,6 +3725,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3737,6 +3752,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3763,6 +3779,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3789,6 +3806,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3815,6 +3833,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3846,6 +3865,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target, target,
@ -3872,6 +3892,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3897,6 +3918,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3922,6 +3944,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3947,6 +3970,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -3972,6 +3996,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Self::emit_memory_op( Self::emit_memory_op(
module_info, module_info,
&self.config,
a, a,
&mut self.machine, &mut self.machine,
target_addr, target_addr,
@ -4068,7 +4093,8 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let (targets, default_target) = table.read_table().unwrap(); let (targets, default_target) = table.read_table().unwrap();
let cond = let cond =
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap()); get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
let mut table = vec![0usize; targets.len()]; let table_label = a.get_label();
let mut table: Vec<DynamicLabel> = vec![];
let default_br = a.get_label(); let default_br = a.get_label();
Self::emit_relaxed_binop( Self::emit_relaxed_binop(
a, a,
@ -4080,19 +4106,16 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
); );
a.emit_jmp(Condition::AboveEqual, default_br); a.emit_jmp(Condition::AboveEqual, default_br);
a.emit_mov( a.emit_lea_label(table_label, Location::GPR(GPR::RCX));
Size::S64,
Location::Imm64(table.as_ptr() as usize as u64),
Location::GPR(GPR::RCX),
);
a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); a.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX));
a.emit_shl(Size::S32, Location::Imm8(3), Location::GPR(GPR::RDX)); a.emit_imul_imm32_gpr64(5, GPR::RDX);
a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX)); a.emit_add(Size::S64, Location::GPR(GPR::RCX), Location::GPR(GPR::RDX));
a.emit_jmp_location(Location::Memory(GPR::RDX, 0)); a.emit_jmp_location(Location::GPR(GPR::RDX));
for (i, target) in targets.iter().enumerate() { for target in targets.iter() {
let AssemblyOffset(offset) = a.offset(); let label = a.get_label();
table[i] = offset; a.emit_label(label);
table.push(label);
let frame = let frame =
&self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; &self.control_stack[self.control_stack.len() - 1 - (*target as usize)];
if !frame.loop_like && frame.returns.len() > 0 { if !frame.loop_like && frame.returns.len() > 0 {
@ -4127,7 +4150,10 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
a.emit_jmp(Condition::None, frame.label); a.emit_jmp(Condition::None, frame.label);
} }
self.br_table_data.as_mut().unwrap().push(table); a.emit_label(table_label);
for x in table {
a.emit_jmp(Condition::None, x);
}
self.unreachable_depth = 1; self.unreachable_depth = 1;
} }
Operator::Drop => { Operator::Drop => {

View File

@ -89,6 +89,8 @@ pub trait Emitter {
fn get_label(&mut self) -> Self::Label; fn get_label(&mut self) -> Self::Label;
fn get_offset(&mut self) -> Self::Offset; fn get_offset(&mut self) -> Self::Offset;
fn emit_u64(&mut self, x: u64);
fn emit_label(&mut self, label: Self::Label); fn emit_label(&mut self, label: Self::Label);
fn emit_mov(&mut self, sz: Size, src: Location, dst: Location); fn emit_mov(&mut self, sz: Size, src: Location, dst: Location);
@ -490,6 +492,10 @@ impl Emitter for Assembler {
self.offset() self.offset()
} }
fn emit_u64(&mut self, x: u64) {
self.push_u64(x);
}
fn emit_label(&mut self, label: Self::Label) { fn emit_label(&mut self, label: Self::Label) {
dynasm!(self ; => label); dynasm!(self ; => label);
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "wasmer-spectests" name = "wasmer-spectests"
version = "0.4.1" version = "0.4.2"
description = "Wasmer spectests library" description = "Wasmer spectests library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,10 +9,10 @@ edition = "2018"
build = "build/mod.rs" build = "build/mod.rs"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.1" } wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.1", optional = true } wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.1", optional = true } wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
[build-dependencies] [build-dependencies]
wabt = "0.7.2" wabt = "0.7.2"

View File

@ -1,14 +1,15 @@
[package] [package]
name = "wasmer-wasi" name = "wasmer-wasi"
version = "0.4.1" version = "0.4.2"
description = "Wasmer runtime WASI implementation library" description = "Wasmer runtime WASI implementation library"
license = "MIT" license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"] authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer" repository = "https://github.com/wasmerio/wasmer"
edition = "2018" edition = "2018"
build = "build/mod.rs"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.1" } wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
libc = "0.2.50" libc = "0.2.50"
rand = "0.6.5" rand = "0.6.5"
# wasmer-runtime-abi = { path = "../runtime-abi" } # wasmer-runtime-abi = { path = "../runtime-abi" }
@ -16,3 +17,19 @@ hashbrown = "0.1.8"
generational-arena = "0.2.2" generational-arena = "0.2.2"
log = "0.4.6" log = "0.4.6"
byteorder = "1.3.1" byteorder = "1.3.1"
# hack to get tests to work
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
[build-dependencies]
glob = "0.2.11"
[dev-dependencies]
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" }
wasmer-dev-utils = { path = "../dev-utils", version = "0.4.2"}
[features]
clif = []
singlepass = ["wasmer-singlepass-backend"]

11
lib/wasi/build/mod.rs Normal file
View File

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

207
lib/wasi/build/wasitests.rs Normal file
View File

@ -0,0 +1,207 @@
//! This file will run at build time to autogenerate the WASI regression tests
//! It will compile the files indicated in TESTS, to:executable and .wasm
//! - Compile with the native rust target to get the expected output
//! - Compile with the latest WASI target to get the wasm
//! - Generate the test that will compare the output of running the .wasm file
//! with wasmer with the expected output
use glob::glob;
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
static BANNER: &str = "// !!! THIS IS A GENERATED FILE !!!
// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME
// Files autogenerated with cargo build (build/wasitests.rs).\n";
pub fn compile(file: &str, ignores: &HashSet<String>) -> Option<String> {
dbg!(file);
let mut output_path = PathBuf::from(file);
output_path.set_extension("out");
assert!(file.ends_with(".rs"));
let normalized_name = {
let mut nn = file.to_lowercase();
nn.truncate(file.len() - 3);
nn
};
Command::new("rustc")
.arg("+nightly")
.arg(file)
.arg("-o")
.arg(&normalized_name)
.output()
.expect("Failed to compile program to native code");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let normal_path = PathBuf::from(&normalized_name);
let mut perm = normal_path
.metadata()
.expect("native executable")
.permissions();
perm.set_mode(0o766);
fs::set_permissions(normal_path, perm).expect("set permissions");
}
let rs_module_name = {
let temp = PathBuf::from(&normalized_name);
temp.file_name().unwrap().to_string_lossy().to_string()
};
let result = Command::new(&normalized_name)
.output()
.expect("Failed to execute native program");
let wasm_out_name = format!("{}.wasm", &normalized_name);
Command::new("rustc")
.arg("+nightly")
.arg("--target=wasm32-wasi")
.arg(file)
.arg("-o")
.arg(&wasm_out_name)
.output()
.expect("Failed to compile program to native code");
let ignored = if ignores.contains(&rs_module_name) {
"\n#[ignore]"
} else {
""
};
let src_code = fs::read_to_string(file).expect("read src file");
let args = extract_args_from_source_file(&src_code);
let mapdir_args = if let Some(a) = args {
if !a.mapdir.is_empty() {
let mut out_str = String::new();
out_str.push_str("vec![");
for (alias, real_dir) in a.mapdir {
out_str.push_str(&format!(
"(\"{}\".to_string(), \"{}\".to_string()),",
alias, real_dir
));
}
out_str.push_str("]");
out_str
} else {
"vec![]".to_string()
}
} else {
"vec![]".to_string()
};
let contents = format!(
"#[test]{ignore}
fn test_{rs_module_name}() {{
assert_wasi_output!(
\"../../{module_path}\",
\"{rs_module_name}\",
{mapdir_args},
\"../../{test_output_path}\"
);
}}
",
ignore = ignored,
module_path = wasm_out_name,
rs_module_name = rs_module_name,
test_output_path = format!("{}.out", normalized_name),
mapdir_args = mapdir_args,
);
let rust_test_filepath = format!(
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/{}.rs"),
normalized_name,
);
fs::write(&rust_test_filepath, contents.as_bytes()).expect("writing test file");
fs::write(&output_path, result.stdout).expect("writing output to file");
Some(rs_module_name)
}
pub fn build() {
let rust_test_modpath = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/wasitests/mod.rs");
let mut modules: Vec<String> = Vec::new();
let ignores = read_ignore_list();
for entry in glob("wasitests/*.rs").unwrap() {
match entry {
Ok(path) => {
let test = path.to_str().unwrap();
if let Some(module_name) = compile(test, &ignores) {
modules.push(module_name);
}
}
Err(e) => println!("{:?}", e),
}
}
modules.sort();
let mut modules: Vec<String> = modules.iter().map(|m| format!("mod {};", m)).collect();
assert!(modules.len() > 0, "Expected > 0 modules found");
modules.insert(0, BANNER.to_string());
modules.insert(1, "// The _common module is not autogenerated. It provides common macros for the wasitests\n#[macro_use]\nmod _common;".to_string());
// We add an empty line
modules.push("".to_string());
let modfile: String = modules.join("\n");
let source = fs::read(&rust_test_modpath).unwrap();
// We only modify the mod file if has changed
if source != modfile.as_bytes() {
fs::write(&rust_test_modpath, modfile.as_bytes()).unwrap();
}
}
fn read_ignore_list() -> HashSet<String> {
let f = File::open("wasitests/ignores.txt").unwrap();
let f = BufReader::new(f);
f.lines()
.filter_map(Result::ok)
.map(|v| v.to_lowercase())
.collect()
}
struct Args {
pub mapdir: Vec<(String, String)>,
}
/// Pulls args to the program out of a comment at the top of the file starting with "// Args:"
fn extract_args_from_source_file(source_code: &str) -> Option<Args> {
if source_code.starts_with("// Args:") {
let mut args = Args { mapdir: vec![] };
for arg_line in source_code
.lines()
.skip(1)
.take_while(|line| line.starts_with("// "))
{
let tokenized = arg_line
.split_whitespace()
.skip(1)
.map(String::from)
.collect::<Vec<String>>();
match tokenized[1].as_ref() {
"mapdir" => {
if let [alias, real_dir] = &tokenized[2].split(':').collect::<Vec<&str>>()[..] {
args.mapdir.push((alias.to_string(), real_dir.to_string()));
} else {
eprintln!(
"Parse error in mapdir {} not parsed correctly",
&tokenized[2]
);
}
}
e => {
eprintln!("WARN: comment arg: {} is not supported", e);
}
}
}
return Some(args);
}
None
}

View File

@ -2,6 +2,8 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[cfg(target = "windows")]
extern crate winapi;
#[macro_use] #[macro_use]
mod macros; mod macros;
@ -14,6 +16,7 @@ use self::state::{WasiFs, WasiState};
use self::syscalls::*; use self::syscalls::*;
use std::ffi::c_void; use std::ffi::c_void;
use std::path::PathBuf;
pub use self::utils::is_wasi_module; pub use self::utils::is_wasi_module;
@ -29,6 +32,7 @@ pub fn generate_import_object(
args: Vec<Vec<u8>>, args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>, envs: Vec<Vec<u8>>,
preopened_files: Vec<String>, preopened_files: Vec<String>,
mapped_dirs: Vec<(String, PathBuf)>,
) -> ImportObject { ) -> ImportObject {
let state_gen = move || { let state_gen = move || {
fn state_destructor(data: *mut c_void) { fn state_destructor(data: *mut c_void) {
@ -38,7 +42,7 @@ pub fn generate_import_object(
} }
let state = Box::new(WasiState { let state = Box::new(WasiState {
fs: WasiFs::new(&preopened_files).unwrap(), fs: WasiFs::new(&preopened_files, &mapped_dirs).unwrap(),
args: &args[..], args: &args[..],
envs: &envs[..], envs: &envs[..],
}); });

View File

@ -3,11 +3,11 @@ macro_rules! wasi_try {
let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr; let res: Result<_, crate::syscalls::types::__wasi_errno_t> = $expr;
match res { match res {
Ok(val) => { Ok(val) => {
debug!("wasi::wasi_try::val: {:?}", val); wasmer_runtime_core::trace!("wasi::wasi_try::val: {:?}", val);
val val
} }
Err(err) => { Err(err) => {
debug!("wasi::wasi_try::err: {:?}", err); wasmer_runtime_core::trace!("wasi::wasi_try::err: {:?}", err);
return err; return err;
} }
} }

View File

@ -175,21 +175,20 @@ pub struct WasiFs {
} }
impl WasiFs { impl WasiFs {
pub fn new(preopened_dirs: &[String]) -> Result<Self, String> { pub fn new(
/*let repo = RepoOpener::new() preopened_dirs: &[String],
.create(true) mapped_dirs: &[(String, PathBuf)],
.open("mem://wasmer-test-fs", "") ) -> Result<Self, String> {
.map_err(|e| e.to_string())?;*/
debug!("wasi::fs::inodes"); debug!("wasi::fs::inodes");
let inodes = Arena::new(); let inodes = Arena::new();
let mut wasi_fs = Self { let mut wasi_fs = Self {
//repo: repo,
name_map: HashMap::new(), name_map: HashMap::new(),
inodes: inodes, inodes: inodes,
fd_map: HashMap::new(), fd_map: HashMap::new(),
next_fd: Cell::new(3), next_fd: Cell::new(3),
inode_counter: Cell::new(1000), inode_counter: Cell::new(1000),
}; };
debug!("wasi::fs::preopen_dirs");
for dir in preopened_dirs { for dir in preopened_dirs {
debug!("Attempting to preopen {}", &dir); debug!("Attempting to preopen {}", &dir);
// TODO: think about this // TODO: think about this
@ -218,6 +217,36 @@ impl WasiFs {
.create_fd(default_rights, default_rights, 0, inode) .create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd"); .expect("Could not open fd");
} }
debug!("wasi::fs::mapped_dirs");
for (alias, real_dir) in mapped_dirs {
debug!("Attempting to open {:?} at {}", real_dir, alias);
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let cur_dir_metadata = real_dir
.metadata()
.expect("mapped dir not at previously verified location");
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: None,
path: real_dir.clone(),
entries: Default::default(),
}
} else {
return Err(format!(
"WASI only supports pre-opened directories right now; found \"{:?}\"",
&real_dir,
));
};
// TODO: handle nested pats in `file`
let inode_val =
InodeVal::from_file_metadata(&cur_dir_metadata, alias.clone(), true, kind);
let inode = wasi_fs.inodes.insert(inode_val);
wasi_fs.inodes[inode].stat.st_ino = wasi_fs.inode_counter.get();
wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd");
}
debug!("wasi::fs::end"); debug!("wasi::fs::end");
Ok(wasi_fs) Ok(wasi_fs)
} }
@ -419,29 +448,10 @@ impl WasiFs {
} }
pub fn get_base_path_for_directory(&self, directory: Inode) -> Option<String> { pub fn get_base_path_for_directory(&self, directory: Inode) -> Option<String> {
let mut path_segments = vec![]; if let Kind::Dir { path, .. } = &self.inodes[directory].kind {
let mut cur_inode = directory; return Some(path.to_string_lossy().to_string());
loop {
path_segments.push(self.inodes[cur_inode].name.clone());
if let Kind::Dir { parent, .. } = &self.inodes[cur_inode].kind {
if let Some(p_inode) = parent {
cur_inode = *p_inode;
} else {
break;
}
} else {
return None;
}
} }
None
path_segments.reverse();
Some(
path_segments
.iter()
.skip(1)
.fold(path_segments.first()?.clone(), |a, b| a + "/" + b),
)
} }
} }
@ -451,3 +461,77 @@ pub struct WasiState<'a> {
pub args: &'a [Vec<u8>], pub args: &'a [Vec<u8>],
pub envs: &'a [Vec<u8>], pub envs: &'a [Vec<u8>],
} }
pub fn host_file_type_to_wasi_file_type(file_type: fs::FileType) -> __wasi_filetype_t {
// TODO: handle other file types
if file_type.is_dir() {
__WASI_FILETYPE_DIRECTORY
} else if file_type.is_file() {
__WASI_FILETYPE_REGULAR_FILE
} else if file_type.is_symlink() {
__WASI_FILETYPE_SYMBOLIC_LINK
} else {
__WASI_FILETYPE_UNKNOWN
}
}
pub fn get_stat_for_kind(kind: &Kind) -> Option<__wasi_filestat_t> {
match kind {
Kind::File { handle } => match handle {
WasiFile::HostFile(hf) => {
let md = hf.metadata().ok()?;
Some(__wasi_filestat_t {
st_filetype: host_file_type_to_wasi_file_type(md.file_type()),
st_size: md.len(),
st_atim: md
.accessed()
.ok()?
.duration_since(SystemTime::UNIX_EPOCH)
.ok()?
.as_nanos() as u64,
st_mtim: md
.modified()
.ok()?
.duration_since(SystemTime::UNIX_EPOCH)
.ok()?
.as_nanos() as u64,
st_ctim: md
.created()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0),
..__wasi_filestat_t::default()
})
}
},
Kind::Dir { path, .. } => {
let md = path.metadata().ok()?;
Some(__wasi_filestat_t {
st_filetype: host_file_type_to_wasi_file_type(md.file_type()),
st_size: md.len(),
st_atim: md
.accessed()
.ok()?
.duration_since(SystemTime::UNIX_EPOCH)
.ok()?
.as_nanos() as u64,
st_mtim: md
.modified()
.ok()?
.duration_since(SystemTime::UNIX_EPOCH)
.ok()?
.as_nanos() as u64,
st_ctim: md
.created()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0),
..__wasi_filestat_t::default()
})
}
_ => None,
}
}

View File

@ -8,7 +8,10 @@ pub mod windows;
use self::types::*; use self::types::*;
use crate::{ use crate::{
ptr::{Array, WasmPtr}, ptr::{Array, WasmPtr},
state::{Fd, InodeVal, Kind, WasiFile, WasiState, MAX_SYMLINKS}, state::{
get_stat_for_kind, host_file_type_to_wasi_file_type, Fd, InodeVal, Kind, WasiFile,
WasiState, MAX_SYMLINKS,
},
ExitCode, ExitCode,
}; };
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
@ -185,7 +188,10 @@ pub fn clock_time_get(
precision: __wasi_timestamp_t, precision: __wasi_timestamp_t,
time: WasmPtr<__wasi_timestamp_t>, time: WasmPtr<__wasi_timestamp_t>,
) -> __wasi_errno_t { ) -> __wasi_errno_t {
debug!("wasi::clock_time_get"); debug!(
"wasi::clock_time_get clock_id: {}, precision: {}",
clock_id, precision
);
let memory = ctx.memory(0); let memory = ctx.memory(0);
let out_addr = wasi_try!(time.deref(memory)); let out_addr = wasi_try!(time.deref(memory));
@ -789,25 +795,17 @@ pub fn fd_readdir(
for entry in entries.iter().skip(cookie as usize) { for entry in entries.iter().skip(cookie as usize) {
cur_cookie += 1; cur_cookie += 1;
let entry_path = entry.path(); let entry_path = entry.path();
let entry_path = wasi_try!(entry_path.file_name().ok_or(__WASI_EIO));
let entry_path_str = entry_path.to_string_lossy(); let entry_path_str = entry_path.to_string_lossy();
let namlen = entry_path_str.len(); let namlen = entry_path_str.len();
debug!("Returning dirent for {}", entry_path_str);
let dirent = __wasi_dirent_t { let dirent = __wasi_dirent_t {
d_next: cur_cookie, d_next: cur_cookie,
d_ino: 0, // TODO: inode d_ino: 0, // TODO: inode
d_namlen: namlen as u32, d_namlen: namlen as u32,
d_type: { d_type: host_file_type_to_wasi_file_type(wasi_try!(entry
let file_type = wasi_try!(entry.file_type().map_err(|_| __WASI_EIO)); .file_type()
// TODO: handle other file types .map_err(|_| __WASI_EIO))),
if file_type.is_dir() {
__WASI_FILETYPE_DIRECTORY
} else if file_type.is_file() {
__WASI_FILETYPE_REGULAR_FILE
} else if file_type.is_symlink() {
__WASI_FILETYPE_SYMBOLIC_LINK
} else {
__WASI_FILETYPE_UNKNOWN
}
},
}; };
let dirent_bytes = dirent_to_le_bytes(&dirent); let dirent_bytes = dirent_to_le_bytes(&dirent);
let upper_limit = std::cmp::min( let upper_limit = std::cmp::min(
@ -1089,7 +1087,7 @@ pub fn path_create_directory(
entries: Default::default(), entries: Default::default(),
}; };
let new_inode = state.fs.inodes.insert(InodeVal { let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(), stat: wasi_try!(get_stat_for_kind(&kind).ok_or(__WASI_EIO)),
is_preopened: false, is_preopened: false,
name: path_vec[0].clone(), name: path_vec[0].clone(),
kind, kind,
@ -1151,7 +1149,10 @@ pub fn path_filestat_get(
if path_vec.is_empty() { if path_vec.is_empty() {
return __WASI_EINVAL; return __WASI_EINVAL;
} }
let mut cumulative_path = std::path::PathBuf::new(); let mut cumulative_path = std::path::PathBuf::from(wasi_try!(state
.fs
.get_base_path_for_directory(root_dir.inode)
.ok_or(__WASI_EIO)));
debug!("=> Path vec: {:?}:", &path_vec); debug!("=> Path vec: {:?}:", &path_vec);
// find the inode by traversing the path // find the inode by traversing the path
@ -1215,64 +1216,28 @@ pub fn path_filestat_get(
} }
} }
let final_inode = match &state.fs.inodes[inode].kind { let stat = match &state.fs.inodes[inode].kind {
Kind::Dir { path, entries, .. } => { Kind::Dir { path, entries, .. } => {
// TODO: fail earlier if size 0 // read it from internal data structures if we can
let last_segment = path_vec.last().unwrap(); let last_segment = path_vec.last().unwrap();
cumulative_path.push(last_segment); cumulative_path.push(last_segment);
if entries.contains_key(last_segment) { if entries.contains_key(last_segment) {
entries[last_segment] state.fs.inodes[entries[last_segment]].stat
} else { } else {
// lazily load it if we can // otherwise read it from the host FS
if !cumulative_path.exists() { if !cumulative_path.exists() {
return __WASI_ENOENT; return __WASI_ENOENT;
} }
let final_path_metadata = let final_path_metadata =
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO)); wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EIO));
let new_inode = if final_path_metadata.is_dir() { wasi_try!(get_stat_for_kind(&state.fs.inodes[inode].kind).ok_or(__WASI_EIO))
debug!("Opening host directory {:#?}", &cumulative_path);
state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false, // is this correct?
name: last_segment.clone(),
kind: Kind::Dir {
parent: Some(inode),
path: std::path::PathBuf::from(&last_segment),
entries: Default::default(),
},
})
} else {
debug!("Opening host file {:#?}", &cumulative_path);
let real_open_file = wasi_try!(std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&cumulative_path)
.map_err(|_| __WASI_ENOENT));
state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false, // is this correct?
name: last_segment.clone(),
kind: Kind::File {
handle: WasiFile::HostFile(real_open_file),
},
})
};
// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[inode].kind {
entries.insert(last_segment.clone(), new_inode);
}
new_inode
} }
} }
_ => { _ => {
return __WASI_ENOTDIR; return __WASI_ENOTDIR;
} }
}; };
let stat = state.fs.inodes[final_inode].stat;
buf_cell.set(stat); buf_cell.set(stat);
__WASI_ESUCCESS __WASI_ESUCCESS
@ -1455,7 +1420,6 @@ pub fn path_open(
}; };
// TODO: handle __WASI_O_TRUNC on directories // TODO: handle __WASI_O_TRUNC on directories
dbg!(&cumulative_path);
// TODO: refactor and reuse // TODO: refactor and reuse
let cur_file_metadata = let cur_file_metadata =
wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL)); wasi_try!(cumulative_path.metadata().map_err(|_| __WASI_EINVAL));
@ -1499,93 +1463,117 @@ pub fn path_open(
"Looking for file {} in directory {:#?}", "Looking for file {} in directory {:#?}",
file_name, cumulative_path file_name, cumulative_path
); );
cumulative_path.push(file_name); cumulative_path.push(file_name);
let file_path = cumulative_path; let file_path = cumulative_path;
let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind { let out_fd = if let Kind::Dir {
if let Some(child) = entries.get(file_name).cloned() { entries, parent, ..
let child_inode_val = &state.fs.inodes[child]; } = &mut state.fs.inodes[cur_dir_inode].kind
// early return based on flags {
if o_flags & __WASI_O_EXCL != 0 { // short circuit logic if attempting to get parent
return __WASI_EEXIST; if file_name == ".." {
} if let Some(p) = parent {
if o_flags & __WASI_O_DIRECTORY != 0 { let parent_inode = *p;
match &child_inode_val.kind { wasi_try!(state.fs.create_fd(
Kind::Dir { .. } => (), fs_rights_base,
Kind::Symlink { .. } => { fs_rights_inheriting,
unimplemented!("Symlinks not yet supported in path_open") fs_flags,
} parent_inode
_ => return __WASI_ENOTDIR, ))
}
}
// do logic on child
wasi_try!(state
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
let file_metadata = wasi_try!(file_path.metadata().map_err(|_| __WASI_ENOENT));
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let kind = if file_metadata.is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
}
} else { } else {
// file is not a dir return __WASI_EACCES;
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true).write(true);
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file = wasi_try!(open_options.open(&file_path).map_err(|e| {
dbg!(e);
__WASI_EIO
}));
real_open_file
};
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};
// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false,
name: file_name.clone(),
kind,
});
// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);
} }
let new_fd = wasi_try!(state.fs.create_fd( } else {
fs_rights_base, if let Some(child) = entries.get(file_name).cloned() {
fs_rights_inheriting, let child_inode_val = &state.fs.inodes[child];
fs_flags, // early return based on flags
new_inode, if o_flags & __WASI_O_EXCL != 0 {
)); return __WASI_EEXIST;
}
if o_flags & __WASI_O_DIRECTORY != 0 {
match &child_inode_val.kind {
Kind::Dir { .. } => (),
Kind::Symlink { .. } => {
unimplemented!("Symlinks not yet supported in path_open")
}
_ => return __WASI_ENOTDIR,
}
}
// do logic on child
wasi_try!(state
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
debug!("Attempting to load file from host system");
new_fd let file_metadata = file_path.metadata();
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let kind = if file_metadata.is_ok() && file_metadata.unwrap().is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
}
} else {
// file is not a dir
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true);
let open_options = if fs_rights_base & __WASI_RIGHT_FD_WRITE != 0 {
open_options.write(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file =
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));
real_open_file
};
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};
// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: wasi_try!(get_stat_for_kind(&kind).ok_or(__WASI_EIO)),
is_preopened: false,
name: file_name.clone(),
kind,
});
// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);
}
let new_fd = wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
new_inode,
));
new_fd
}
} }
} else { } else {
// working_dir did not match on Kind::Dir // working_dir did not match on Kind::Dir

View File

@ -24,9 +24,9 @@ pub struct __wasi_ciovec_t {
unsafe impl ValueType for __wasi_ciovec_t {} unsafe impl ValueType for __wasi_ciovec_t {}
pub type __wasi_clockid_t = u32; pub type __wasi_clockid_t = u32;
pub const __WASI_CLOCK_MONOTONIC: u32 = 0; pub const __WASI_CLOCK_REALTIME: u32 = 0;
pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: u32 = 1; pub const __WASI_CLOCK_MONOTONIC: u32 = 1;
pub const __WASI_CLOCK_REALTIME: u32 = 2; pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: u32 = 2;
pub const __WASI_CLOCK_THREAD_CPUTIME_ID: u32 = 3; pub const __WASI_CLOCK_THREAD_CPUTIME_ID: u32 = 3;
pub type __wasi_device_t = u64; pub type __wasi_device_t = u64;

View File

@ -23,7 +23,8 @@ pub fn platform_clock_res_get(
(clock_getres(unix_clock_id, &mut timespec_out), timespec_out) (clock_getres(unix_clock_id, &mut timespec_out), timespec_out)
}; };
resolution.set(timespec_out.tv_nsec as __wasi_timestamp_t); let t_out = (timespec_out.tv_sec * 1_000_000_000).wrapping_add(timespec_out.tv_nsec);
resolution.set(t_out as __wasi_timestamp_t);
// TODO: map output of clock_getres to __wasi_errno_t // TODO: map output of clock_getres to __wasi_errno_t
__WASI_ESUCCESS __WASI_ESUCCESS
@ -50,9 +51,8 @@ pub fn platform_clock_time_get(
) )
}; };
// TODO: adjust output by precision... let t_out = (timespec_out.tv_sec * 1_000_000_000).wrapping_add(timespec_out.tv_nsec);
time.set(t_out as __wasi_timestamp_t);
time.set(timespec_out.tv_nsec as __wasi_timestamp_t);
// TODO: map output of clock_gettime to __wasi_errno_t // TODO: map output of clock_gettime to __wasi_errno_t
__WASI_ESUCCESS __WASI_ESUCCESS

View File

@ -5,7 +5,22 @@ pub fn platform_clock_res_get(
clock_id: __wasi_clockid_t, clock_id: __wasi_clockid_t,
resolution: &Cell<__wasi_timestamp_t>, resolution: &Cell<__wasi_timestamp_t>,
) -> __wasi_errno_t { ) -> __wasi_errno_t {
__WASI_EINVAL let resolution_val = match clock_id {
// resolution of monotonic clock at 10ms, from:
// https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-gettickcount64
__WASI_CLOCK_MONOTONIC => 10_000_000,
// TODO: verify or compute this
__WASI_CLOCK_REALTIME => 1,
__WASI_CLOCK_PROCESS_CPUTIME_ID => {
return __WASI_EINVAL;
}
__WASI_CLOCK_THREAD_CPUTIME_ID => {
return __WASI_EINVAL;
}
_ => return __WASI_EINVAL,
};
resolution.set(resolution_val);
__WASI_ESUCCESS
} }
pub fn platform_clock_time_get( pub fn platform_clock_time_get(
@ -13,5 +28,28 @@ pub fn platform_clock_time_get(
precision: __wasi_timestamp_t, precision: __wasi_timestamp_t,
time: &Cell<__wasi_timestamp_t>, time: &Cell<__wasi_timestamp_t>,
) -> __wasi_errno_t { ) -> __wasi_errno_t {
unimplemented!() let nanos = match clock_id {
__WASI_CLOCK_MONOTONIC => {
let tick_ms = unsafe { winapi::um::sysinfoapi::GetTickCount64() };
tick_ms * 1_000_000
}
__WASI_CLOCK_REALTIME => {
let duration = wasi_try!(std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|e| {
debug!("Error in wasi::platform_clock_time_get: {:?}", e);
__WASI_EIO
}));
duration.as_nanos() as u64
}
__WASI_CLOCK_PROCESS_CPUTIME_ID => {
unimplemented!("wasi::platform_clock_time_get(__WASI_CLOCK_PROCESS_CPUTIME_ID, ..)")
}
__WASI_CLOCK_THREAD_CPUTIME_ID => {
unimplemented!("wasi::platform_clock_time_get(__WASI_CLOCK_THREAD_CPUTIME_ID, ..)")
}
_ => return __WASI_EINVAL,
};
time.set(nanos);
__WASI_ESUCCESS
} }

View File

@ -0,0 +1 @@
mod wasitests;

View File

@ -0,0 +1,62 @@
macro_rules! assert_wasi_output {
($file:expr, $name:expr, $mapdir_args:expr, $expected:expr) => {{
use wasmer_dev_utils::stdio::StdioCapturer;
use wasmer_runtime_core::{backend::Compiler, Func};
use wasmer_wasi::generate_import_object;
#[cfg(feature = "clif")]
fn get_compiler() -> impl Compiler {
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "llvm")]
fn get_compiler() -> impl Compiler {
compile_error!("LLVM compiler not supported right now");
}
#[cfg(feature = "singlepass")]
fn get_compiler() -> impl Compiler {
use wasmer_singlepass_backend::SinglePassCompiler;
SinglePassCompiler::new()
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler() -> impl Compiler {
compile_error!("compiler not specified, activate a compiler via features");
unreachable!();
}
let wasm_bytes = include_bytes!($file);
let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler())
.expect("WASM can't be compiled");
let import_object =
generate_import_object(vec![], vec![], vec![".".to_string()], $mapdir_args);
let instance = module
.instantiate(&import_object)
.map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err))
.unwrap(); // NOTE: Need to figure what the unwrap is for ??
let capturer = StdioCapturer::new();
let start: Func<(), ()> = instance
.func("_start")
.map_err(|e| format!("{:?}", e))
.expect("start function in wasi module");
start.call().expect("execute the wasm");
let output = capturer.end().unwrap().0;
let expected_output = include_str!($expected);
assert!(
output.contains(expected_output),
"Output: `{}` does not contain expected output: `{}`",
output,
expected_output
);
}};
}

View File

@ -0,0 +1,10 @@
#[test]
#[ignore]
fn test_create_dir() {
assert_wasi_output!(
"../../wasitests/create_dir.wasm",
"create_dir",
vec![],
"../../wasitests/create_dir.out"
);
}

View File

@ -0,0 +1,9 @@
#[test]
fn test_file_metadata() {
assert_wasi_output!(
"../../wasitests/file_metadata.wasm",
"file_metadata",
vec![],
"../../wasitests/file_metadata.out"
);
}

View File

@ -0,0 +1,9 @@
#[test]
fn test_fs_sandbox_test() {
assert_wasi_output!(
"../../wasitests/fs_sandbox_test.wasm",
"fs_sandbox_test",
vec![],
"../../wasitests/fs_sandbox_test.out"
);
}

View File

@ -0,0 +1,9 @@
#[test]
fn test_hello() {
assert_wasi_output!(
"../../wasitests/hello.wasm",
"hello",
vec![],
"../../wasitests/hello.out"
);
}

View File

@ -0,0 +1,9 @@
#[test]
fn test_mapdir() {
assert_wasi_output!(
"../../wasitests/mapdir.wasm",
"mapdir",
vec![],
"../../wasitests/mapdir.out"
);
}

View File

@ -0,0 +1,13 @@
// !!! THIS IS A GENERATED FILE !!!
// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME
// Files autogenerated with cargo build (build/wasitests.rs).
// The _common module is not autogenerated. It provides common macros for the wasitests
#[macro_use]
mod _common;
mod create_dir;
mod file_metadata;
mod fs_sandbox_test;
mod hello;
mod mapdir;
mod quine;

View File

@ -0,0 +1,9 @@
#[test]
fn test_quine() {
assert_wasi_output!(
"../../wasitests/quine.wasm",
"quine",
vec![],
"../../wasitests/quine.out"
);
}

BIN
lib/wasi/wasitests/create_dir Executable file

Binary file not shown.

View File

@ -0,0 +1,5 @@
Test file exists: false
Dir exists: false
Dir exists: false
Dir exists: false
Success

View File

@ -0,0 +1,37 @@
use std::fs;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::*;
fn main() {
let mut path = PathBuf::from("wasitests/testing/nested/directories");
let test_file = path.join("test.file");
fs::create_dir_all(&path).unwrap();
{
let mut file = fs::OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.create(true)
.open(&test_file)
.unwrap();
assert_eq!(file.write(b"hello").unwrap(), 5);
file.flush().unwrap();
file.seek(SeekFrom::Start(0)).unwrap();
let mut in_str = String::new();
file.read_to_string(&mut in_str).unwrap();
assert_eq!(&in_str, "hello");
}
fs::remove_file(&test_file).unwrap();
println!("Test file exists: {}", test_file.exists());
assert!(!test_file.exists());
for _ in 0..3 {
fs::remove_dir_all(&path).unwrap();
println!("Dir exists: {}", path.exists());
assert!(!path.exists());
path.pop();
}
println!("Success");
}

Binary file not shown.

BIN
lib/wasi/wasitests/file_metadata Executable file

Binary file not shown.

View File

@ -0,0 +1,3 @@
is dir: false
filetype: false true false
file info: 456

View File

@ -0,0 +1,17 @@
use std::fs;
use std::io::Read;
fn main() {
let mut this_file =
fs::File::open("wasitests/file_metadata.rs").expect("could not find src file");
let md = this_file.metadata().unwrap();
println!("is dir: {}", md.is_dir());
let filetype = md.file_type();
println!(
"filetype: {} {} {}",
filetype.is_dir(),
filetype.is_file(),
filetype.is_symlink()
);
println!("file info: {}", md.len());
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
Reading the parent directory was okay? false

View File

@ -0,0 +1,10 @@
fn main() {
#[cfg(target = "wasi")]
let result = std::fs::read_dir("..");
#[cfg(not(target = "wasi"))]
let result: Result<(), String> = Err("placeholder".to_string());
println!(
"Reading the parent directory was okay? {:?}",
result.is_ok()
);
}

Binary file not shown.

BIN
lib/wasi/wasitests/hello Executable file

Binary file not shown.

View File

@ -0,0 +1 @@
Hello, world!

View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

BIN
lib/wasi/wasitests/hello.wasm Executable file

Binary file not shown.

View File

@ -0,0 +1 @@
create_dir

BIN
lib/wasi/wasitests/mapdir Executable file

Binary file not shown.

View File

@ -0,0 +1,6 @@
"wasitests/test_fs/hamlet/README.md"
"wasitests/test_fs/hamlet/act1"
"wasitests/test_fs/hamlet/act2"
"wasitests/test_fs/hamlet/act3"
"wasitests/test_fs/hamlet/act4"
"wasitests/test_fs/hamlet/act5"

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