Merge branch 'master' into doc-runtime-c-api-exports-1

This commit is contained in:
Ivan Enderlin 2019-08-22 13:29:54 +02:00 committed by GitHub
commit 6403159b57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
306 changed files with 39046 additions and 7928 deletions

View File

@ -18,24 +18,22 @@ environment:
cache:
- 'C:\Users\appveyor\.cargo'
- target
- wapm-cli-target
install:
# # Install LLVM
# - mkdir C:\projects\deps
# - cd C:\projects\deps
# - appveyor DownloadFile http://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe -FileName llvm.exe
# - 7z x llvm.exe -oC:\projects\deps\llvm
# # - set "PATH=%PATH%;C:\projects\deps\llvm\bin"
# - set "LLD_LINK=C:\projects\deps\llvm\bin\lld-link.exe"
# - set "LLVM_SYS_70_PREFIX=C:\projects\deps\llvm"
# - cd "%APPVEYOR_BUILD_FOLDER%"
# Install LLVM
- mkdir C:\projects\deps
- cd C:\projects\deps
- appveyor DownloadFile https://github.com/wasmerio/windows-llvm-build/releases/download/v8.0.0/llvm-8.0.0-install.zip -FileName llvm-8.0.0-install.zip
- 7z x llvm-8.0.0-install.zip
- C:\projects\deps\llvm-8.0.0-install\bin\llvm-config.exe --version
- set "LLVM_SYS_80_PREFIX=C:\projects\deps\llvm-8.0.0-install"
- cd "%APPVEYOR_BUILD_FOLDER%"
# Install Rust
# uncomment these lines if the cache is cleared, or if we must re-install rust for some reason
# - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
# - rustup-init.exe -yv --default-host %target%
- set PATH=%PATH%;C:\\Libraries\\llvm-5.0.0\\bin;%USERPROFILE%\.cargo\bin
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -yv --default-host %target%
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustup default stable-%target%
- rustup update
- rustc -vV
@ -49,25 +47,17 @@ install:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script:
- cargo build --release --verbose
- 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
- cd ..
- xcopy wapm-cli\target wapm-cli-target\ /i /y
- cargo build --release --verbose --features backend-llvm
- cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml
test_script:
- cargo test --manifest-path lib/spectests/Cargo.toml --features clif
- cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture
before_deploy:
- appveyor PushArtifact target\release\wasmer_runtime_c_api.dll
- git submodule init
- git submodule update
- cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
- cd ./src/installer
- iscc wasmer.iss
- copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe

View File

@ -1,14 +1,14 @@
run_with_build_env_vars: &run_with_build_env_vars
environment:
LLVM_SYS_70_PREFIX: /home/circleci/project/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/
LLVM_SYS_80_PREFIX: /home/circleci/project/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/
run_install_dependencies: &run_install_dependencies
run:
name: install dependencies
command: |
sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
version: 2
jobs:
@ -75,9 +75,6 @@ jobs:
make cranelift
make llvm
make test-rest
- run:
name: Release
command: make release-fast
- run:
name: Integration Tests
command: make integration-tests
@ -115,9 +112,9 @@ jobs:
name: Check
command: |
make check
- run:
name: Release
command: make release-fast
make compile-bench-singlepass
make compile-bench-llvm
# TODO: add compile-bench-clif when it works
- run:
name: Integration Tests
command: make integration-tests
@ -129,6 +126,26 @@ jobs:
- target/release/deps
key: v8-test-cargo-cache-linux-nightly-{{ arch }}-{{ checksum "Cargo.lock" }}
test-rust-example:
docker:
- image: circleci/rust:latest
<<: *run_with_build_env_vars
steps:
- checkout
- run:
name: "Check Wasmer Rust example"
command: |
git clone https://github.com/wasmerio/wasmer-rust-example
rustup default stable
rustup target add wasm32-unknown-unknown
cd wasmer-rust-example
cd wasm-sample-app
cargo build --release
cd ..
sed -i 's/wasmer-runtime.*/wasmer-runtime = \{ path = "..\/lib\/runtime" \}/g' Cargo.toml
cargo run
cargo test
test-macos:
macos:
xcode: "9.0"
@ -149,8 +166,8 @@ jobs:
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# Installing LLVM outside of brew
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
- run:
name: Install Rust
command: |
@ -162,7 +179,7 @@ jobs:
command: |
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
@ -171,18 +188,14 @@ jobs:
name: Check
command: |
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
make check
- run:
name: Release
command: |
export PATH="$HOME/.cargo/bin:$PATH"
make release-fast
- run:
name: Integration Tests
command: |
export PATH="$HOME/.cargo/bin:$PATH"
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
make integration-tests
- save_cache:
paths:
@ -213,27 +226,27 @@ jobs:
name: Install dependencies
command: |
sudo apt-get install -y cmake
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar xf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
- run: rustup default nightly-2019-06-10
- run:
name: Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Release Build
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make release
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
mkdir -p artifacts
VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
# GIT_VERSION=$(git describe --exact-match --tags)
echo "${VERSION}" >> artifacts/version
echo "${CIRCLE_TAG}" >> artifacts/git_version
make build-install
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh)
- run:
name: Dynamic library
command: |
@ -275,8 +288,8 @@ jobs:
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# Installing LLVM outside of brew
curl -O https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz
curl -O https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
tar xf clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz
- run:
name: Install Rust
command: |
@ -288,7 +301,7 @@ jobs:
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
@ -299,12 +312,12 @@ jobs:
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
export LLVM_SYS_80_PREFIX="`pwd`/clang+llvm-8.0.0-x86_64-apple-darwin/"
make release
cargo build --release --manifest-path wapm-cli/Cargo.toml --features telemetry
cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications"
mkdir -p artifacts
make build-install
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
cp ./wasmer.tar.gz ./artifacts/$(./scripts/binary-name.sh)
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
# echo "${VERSION}" >> artifacts/version
- run:
@ -344,6 +357,8 @@ jobs:
VERSION=$(cat ./artifacts/version)
# VERSION_TAG=${CIRCLE_TAG}
VERSION_TAG=$(cat ./artifacts/git_version)
LATEST_VERSION_PUBLISHED_ON_CRATES=$(curl -s https://raw.githubusercontent.com/rust-lang/crates.io-index/master/wa/sm/wasmer-runtime | tail -n 1 | sed 's/.*"vers":"\([^"]*\)".*/\1/')
if ( [ $VERSION_TAG -ne $LATEST_VERSION_PUBLISHED_ON_CRATES ] ) then { echo "Could not detect version published to crates.io; make sure we've published the crates before publishing the Wasmer binary"; exit 1; } else { true; } fi
rm ./artifacts/version
rm ./artifacts/git_version
# VERSION_TAG=$(git describe --exact-match --tags)
@ -382,6 +397,12 @@ workflows:
only:
- trying
- staging
- test-rust-example:
filters:
branches:
only:
- trying
- staging
- test-macos:
filters:
branches:

39
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,39 @@
#
src/ @syrusakbary @MarkMcCaskey
# Backends
lib/singlepass-backend @losfair @nlewycky
lib/clif-backend @nlewycky @bjfish
lib/llvm-backend @nlewycky @losfair
# Runtime
lib/runtime-core @Hywan @bjfish
lib/runtime-abi @MarkMcCaskey
lib/runtime @MarkMcCaskey @Hywan @bjfish
lib/runtime-c-api @bjfish @Hywan
lib/win-exception-handler @bjfish @losfair
lib/middleware-common @bjfish @losfair
# Frontend integrations
## Emscripten
lib/emscripten @MarkMcCaskey @syrusakbary
lib/emscripten-tests @MarkMcCaskey @syrusakbary
## WASI
lib/wasi @MarkMcCaskey
lib/wasi-tests @MarkMcCaskey
## Spectests
lib/spectests @syrusakbary @MarkMcCaskey @nlewycky
# Kernel
lib/kernel-loader @losfair
lib/kernel-net @losfair
# Examples
examples @syrusakbary
# Documentation
docs @syrusakbary

View File

@ -6,26 +6,64 @@ Blocks of changes will separated by version increments.
## **[Unreleased]**
## 0.5.6
Special thanks to @YaronWittenstein @penberg for their contributions.
- [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted
- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting
- [#608](https://github.com/wasmerio/wasmer/issues/608) Implement wasi syscalls `fd_allocate`, `fd_sync`, `fd_pread`, `path_link`, `path_filestat_set_times`; update WASI fs API in a WIP way; reduce coupling of WASI code to host filesystem; make debug messages from WASI more readable; improve rights-checking when calling syscalls; implement reference counting on inodes; misc bug fixes and improvements
- [#616](https://github.com/wasmerio/wasmer/issues/616) Create the import object separately from instance instantiation in `runtime-c-api`
- [#620](https://github.com/wasmerio/wasmer/issues/620) Replace one `throw()` with `noexcept` in llvm backend
- [#618](https://github.com/wasmerio/wasmer/issues/618) Implement `InternalEvent::Breakpoint` in the llvm backend to allow metering in llvm
- [#615](https://github.com/wasmerio/wasmer/issues/615) Eliminate `FunctionEnvironment` construction in `feed_event()` speeding up to 70% of compilation in clif
- [#609](https://github.com/wasmerio/wasmer/issues/609) Update dependencies
- [#602](https://github.com/wasmerio/wasmer/issues/602) C api extract instance context from instance
- [#590](https://github.com/wasmerio/wasmer/issues/590) Error visibility changes in wasmer-c-api
- [#589](https://github.com/wasmerio/wasmer/issues/589) Make `wasmer_byte_array` fields `public` in wasmer-c-api
## 0.6.0 - 2019-07-31
- [#603](https://github.com/wasmerio/wasmer/pull/603) Update Wapm-cli, bump version numbers
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.
Add `default-backend-singlepass`, `default-backend-llvm`, and `default-backend-cranelift` features to `wasmer-runtime`
to control the `default_compiler()` function (this is a breaking change). Add `compiler_for_backend` function in `wasmer-runtime`
- [#561](https://github.com/wasmerio/wasmer/pull/561) Call the `data_finalizer` field on the `Ctx`
- [#576](https://github.com/wasmerio/wasmer/pull/576) fix `Drop` of uninit `Ctx`
- [#542](https://github.com/wasmerio/wasmer/pull/542) Add SIMD support to Wasmer (LLVM backend only)
- Updates LLVM to version 8.0
## 0.5.7 - 2019-07-23
- [#575](https://github.com/wasmerio/wasmer/pull/575) Prepare for release; update wapm to 0.3.6
- [#555](https://github.com/wasmerio/wasmer/pull/555) WASI filesystem rewrite. Major improvements
- adds virtual root showing all preopened directories
- improved sandboxing and code-reuse
- symlinks work in a lot more situations
- many misc. improvements to most syscalls touching the filesystem
## 0.5.6 - 2019-07-16
- [#565](https://github.com/wasmerio/wasmer/pull/565) Update wapm and bump version to 0.5.6
- [#563](https://github.com/wasmerio/wasmer/pull/563) Improve wasi testing infrastructure
- fixes arg parsing from comments & fixes the mapdir test to have the native code doing the same thing as the WASI code
- makes wasitests-generate output stdout/stderr by default & adds function to print stdout and stderr for a command if it fails
- compiles wasm with size optimizations & strips generated wasm with wasm-strip
- [#554](https://github.com/wasmerio/wasmer/pull/554) Finish implementation of `wasi::fd_seek`, fix bug in filestat
- [#550](https://github.com/wasmerio/wasmer/pull/550) Fix singlepass compilation error with `imul` instruction
## 0.5.5
## 0.5.5 - 2019-07-10
- [#541](https://github.com/wasmerio/wasmer/pull/541) Fix dependency graph by making separate test crates; ABI implementations should not depend on compilers. Add Cranelift fork as git submodule of clif-backend
- [#537](https://github.com/wasmerio/wasmer/pull/537) Add hidden flag (`--cache-key`) to use prehashed key into the compiled wasm cache and change compiler backend-specific caching to use directories
- [#536](https://github.com/wasmerio/wasmer/pull/536) ~Update cache to use compiler backend name in cache key~
## 0.5.4
## 0.5.4 - 2019-07-06
- [#529](https://github.com/wasmerio/wasmer/pull/529) Updates the Wasm Interface library, which is used by wapm, with bug fixes and error message improvements
## 0.5.3
## 0.5.3 - 2019-07-03
- [#523](https://github.com/wasmerio/wasmer/pull/523) Update wapm version to fix bug related to signed packages in the global namespace and locally-stored public keys
## 0.5.2 - 2019-07-02
- [#516](https://github.com/wasmerio/wasmer/pull/516) Add workaround for singlepass miscompilation on GetLocal
- [#521](https://github.com/wasmerio/wasmer/pull/521) Update Wapm-cli, bump version numbers
- [#518](https://github.com/wasmerio/wasmer/pull/518) Update Cranelift and WasmParser
- [#514](https://github.com/wasmerio/wasmer/pull/514) [#519](https://github.com/wasmerio/wasmer/pull/519) Improved Emscripten network related calls, added a null check to `WasmPtr`

33
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,33 @@
# How to Contribute to Wasmer
Thank you for your interest in contributing to Wasmer. This document outlines some recommendations on how to contribute.
## Issues & Feature Requests
Please use the issue template and provide a failing example if possible to help us recreate the issue.
## Pull Requests
For large changes, please try reaching the Wasmer using Github Issues or Spectrum Chat to ensure we can accept the change once it is ready.
We recommend trying the following commands before sending a pull request to ensure code quality:
- `cargo fmt --all` Ensures all code is correctly formatted.
- Run `cargo test` in the crates that you are modifying.
- Run `cargo build --all` (nightly) or `cargo build --all --exclude wasmer-singlepass-backend`
A comprehensive CI test suite will be run by a Wasmer team member after the PR has been created.
### Common Build Issues
**LLVM Dependency**
The LLVM backend requires LLVM to be installed to compile.
So, you may run into the following error:
```
Didn't find usable system-wide LLVM.
No suitable version of LLVM was found system-wide or pointed
```
**Singlepass Nightly Only**
The singlepass crate depends on nightly so you may need to add the `+nightly` cargo flag to compile this crate.
`error[E0554]: #![feature] may not be used on the stable release channel`

1153
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer"
version = "0.5.6"
version = "0.6.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
@ -19,11 +19,10 @@ include = [
]
[dependencies]
byteorder = "1.3.1"
byteorder = "1.3.2"
errno = "0.2.4"
structopt = "0.2.11"
wabt = "0.7.2"
hashbrown = "0.1.8"
structopt = "0.2.18"
wabt = "0.9.1"
wasmer-clif-backend = { path = "lib/clif-backend" }
wasmer-singlepass-backend = { path = "lib/singlepass-backend", optional = true }
wasmer-middleware-common = { path = "lib/middleware-common" }
@ -61,20 +60,35 @@ members = [
]
[build-dependencies]
wabt = "0.7.2"
glob = "0.2.11"
wabt = "0.9.1"
glob = "0.3.0"
rustc_version = "0.2.3"
[features]
default = ["fast-tests", "wasi"]
default = ["fast-tests", "wasi", "backend-cranelift"]
"loader-kernel" = ["wasmer-kernel-loader"]
debug = ["wasmer-runtime-core/debug"]
trace = ["wasmer-runtime-core/trace"]
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster
fast-tests = []
"backend-llvm" = ["wasmer-llvm-backend", "wasmer-runtime-core/backend-llvm"]
"backend-singlepass" = ["wasmer-singlepass-backend", "wasmer-runtime-core/backend-singlepass"]
backend-cranelift = [
"wasmer-runtime-core/backend-cranelift",
"wasmer-runtime/cranelift",
"wasmer-middleware-common/clif"
]
backend-llvm = [
"wasmer-llvm-backend",
"wasmer-runtime-core/backend-llvm",
"wasmer-runtime/llvm",
"wasmer-middleware-common/llvm"
]
backend-singlepass = [
"wasmer-singlepass-backend",
"wasmer-runtime-core/backend-singlepass",
"wasmer-runtime/singlepass",
"wasmer-middleware-common/singlepass"
]
wasi = ["wasmer-wasi"]
# vfs = ["wasmer-runtime-abi"]

View File

@ -3,9 +3,9 @@ RUN sudo apt-get update && \
sudo apt-get install -y --no-install-recommends \
cmake \
&& sudo rm -rf /var/lib/apt/lists/*
RUN curl -SL https://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz \
RUN curl -SL https://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz \
| tar -xJC /home/circleci
ENV LLVM_SYS_70_PREFIX /home/circleci/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/
ENV LLVM_SYS_80_PREFIX /home/circleci/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04/
FROM wasmer-build-env AS wasmer-debug-env
RUN sudo apt-get update && \

13
Dockerfile.build Normal file
View File

@ -0,0 +1,13 @@
FROM ubuntu:19.04
ARG RUST_TOOLCHAIN="nightly"
ENV CARGO_HOME=/usr/local/rust
ENV RUSTUP_HOME=/usr/local/rust
ENV PATH="$PATH:$CARGO_HOME/bin"
RUN apt-get update \
&& apt-get -y install sudo strace curl cmake pkg-config python libssl-dev llvm-dev libz-dev gnuplot-nox \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN"

View File

@ -7,21 +7,27 @@ generate-spectests:
generate-emtests:
WASM_EMSCRIPTEN_GENERATE_EMTESTS=1 cargo build -p wasmer-emscripten-tests --release
generate-wasitests:
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv
generate-wasitests: wasitests-setup
WASM_WASI_GENERATE_WASITESTS=1 cargo build -p wasmer-wasi-tests --release -vv \
&& echo "formatting" \
&& cargo fmt
spectests-generate: generate-spectests
emtests-generate: generate-emtests
wasitests-generate: generate-wasitests
generate: generate-spectests generate-emtests generate-wasitests
# Spectests
spectests-singlepass:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass
cargo test --manifest-path lib/spectests/Cargo.toml --release --features singlepass -- --nocapture
spectests-cranelift:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif
cargo test --manifest-path lib/spectests/Cargo.toml --release --features clif -- --nocapture
spectests-llvm:
cargo test --manifest-path lib/spectests/Cargo.toml --release --features llvm
cargo test --manifest-path lib/spectests/Cargo.toml --release --features llvm -- --nocapture
spectests: spectests-singlepass spectests-cranelift spectests-llvm
@ -56,13 +62,17 @@ middleware: middleware-singlepass middleware-cranelift middleware-llvm
# Wasitests
wasitests-singlepass:
wasitests-setup:
rm -rf lib/wasi-tests/wasitests/test_fs/temp
mkdir -p lib/wasi-tests/wasitests/test_fs/temp
wasitests-singlepass: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features singlepass -- --test-threads=1
wasitests-cranelift:
wasitests-cranelift: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1
wasitests-llvm:
wasitests-llvm: wasitests-setup
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features llvm -- --test-threads=1
wasitests-unit:
@ -78,7 +88,7 @@ singlepass: spectests-singlepass emtests-singlepass middleware-singlepass wasite
cranelift: spectests-cranelift emtests-cranelift middleware-cranelift wasitests-cranelift
cargo test -p wasmer-clif-backend --release
llvm: spectests-llvm emtests-llvm middleware-llvm wasitests-llvm
llvm: spectests-llvm emtests-llvm wasitests-llvm
cargo test -p wasmer-llvm-backend --release
@ -98,7 +108,7 @@ test: spectests emtests middleware wasitests circleci-clean test-rest
# Integration tests
integration-tests: release-fast
integration-tests: release-clif
echo "Running Integration Tests"
./integration_tests/lua/test.sh
./integration_tests/nginx/test.sh
@ -124,7 +134,7 @@ release:
cargo build --release --features backend-singlepass,backend-llvm,loader-kernel
# Only one backend (cranelift)
release-fast:
release-clif:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
# brew install mingw-w64
cargo build --release
@ -135,8 +145,20 @@ release-singlepass:
release-llvm:
cargo build --release --features backend-llvm
bench:
cargo bench --all
bench-singlepass:
cargo bench --all --no-default-features --features "backend-singlepass"
bench-clif:
cargo bench --all --no-default-features --features "backend-cranelift"
bench-llvm:
cargo bench --all --no-default-features --features "backend-llvm"
# compile but don't run the benchmarks
compile-bench-singlepass:
cargo bench --all --no-run --no-default-features --features "backend-singlepass"
compile-bench-clif:
cargo bench --all --no-run --no-default-features --features "backend-cranelift"
compile-bench-llvm:
cargo bench --all --no-run --no-default-features --features "backend-llvm"
# Build utils

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a>
</p>
@ -14,6 +14,9 @@
<a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
<a href="https://twitter.com/wasmerio">
<img alt="Follow @wasmerio on Twitter" src="https://img.shields.io/twitter/follow/wasmerio?label=%40wasmerio&style=social">
</a>
</p>
## Introduction
@ -26,10 +29,13 @@ Install Wasmer with:
curl https://get.wasmer.io -sSfL | sh
```
> Note: *Wasmer is also available on Windows. Download the [`WasmerInstaller.exe` from the Github Releases](https://github.com/wasmerio/wasmer/releases) page.*
Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨:
* [🦀 **Rust**](https://github.com/wasmerio/wasmer-rust-example)
* [**C/C++**](https://github.com/wasmerio/wasmer-c-api)
* [**🦀 Rust**](https://github.com/wasmerio/wasmer-rust-example)
* [**🔗 C/C++**](https://github.com/wasmerio/wasmer-c-api)
* [**#️⃣ C#**](https://github.com/migueldeicaza/WasmerSharp)
* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm)
* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm)
* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm)
@ -45,21 +51,14 @@ Once installed, you will be able to run any WebAssembly files (_including Lua, P
```sh
# Run Lua
wasmer run examples/lua.wasm
# Run PHP
wasmer run examples/php.wasm
# Run SQLite
wasmer run examples/sqlite.wasm
# Run nginx
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
```
*You can find more `wasm/wat` examples in the [examples](./examples) directory.*
#### With WAPM
Installing Wasmer through `wasmer.io` includes
[wapm](https://github.com/wasmerio/wapm-cli), the WebAssembly package manager.
[`wapm`](https://github.com/wasmerio/wapm-cli), the [WebAssembly Package Manager](https://wapm.io/).
Wapm allows you to easily download, run, and distribute WebAssembly binaries.
@ -86,7 +85,7 @@ Wasmer is structured into different directories:
Building Wasmer requires [rustup](https://rustup.rs/).
To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
To build Wasmer on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
then follow the onscreen instructions.
To build on other systems, run:
@ -99,10 +98,9 @@ curl https://sh.rustup.rs -sSf | sh
Please select your operating system:
- [macOS](#macos)
- [Debian-based Linuxes](#debian-based-linuxes)
- [FreeBSD](#freebsd)
- [Microsoft Windows](#windows-msvc)
<details>
<summary><b>macOS</b></summary>
<p>
#### macOS
@ -118,22 +116,41 @@ Or, in case you have [MacPorts](https://www.macports.org/install.php):
sudo port install cmake
```
</p>
</details>
<details>
<summary><b>Debian-based Linuxes</b></summary>
<p>
#### Debian-based Linuxes
```sh
sudo apt install cmake pkg-config libssl-dev
```
</p>
</details>
<details>
<summary><b>FreeBSD</b></summary>
<p>
#### FreeBSD
```sh
pkg install cmake
```
</p>
</details>
<details>
<summary><b>Windows</b></summary>
<p>
#### Windows (MSVC)
Windows support is _highly experimental_. Only simple Wasm programs may be run, and no syscalls are allowed. This means
nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmerio/wasmer/issues/176) regarding Emscripten syscall polyfills for Windows.
Windows support is _experimental_. WASI is fully supported, but Emscripten support is on the works (this means
nginx and Lua do not work on Windows - you can track the progress on [this issue](https://github.com/wasmerio/wasmer/issues/176)).
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15)
@ -147,7 +164,9 @@ nginx and Lua do not work on Windows. See [this issue](https://github.com/wasmer
5. Install [CMake](https://cmake.org/download/). Ensure CMake is in your PATH.
6. Install [LLVM 7.0](https://prereleases.llvm.org/win-snapshots/LLVM-7.0.0-r336178-win64.exe)
6. Install [LLVM 8.0](https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe)
</p>
</details>
## Building
@ -165,8 +184,14 @@ git clone https://github.com/wasmerio/wasmer.git
cd wasmer
# install tools
# make sure that `python` is accessible.
make install
make release-clif # To build with cranelift (default)
make release-llvm # To build with llvm support
make release-singlepass # To build with singlepass support
# or
make release # To build with singlepass, cranelift and llvm support
```
## Testing
@ -204,7 +229,10 @@ Each integration can be tested separately:
Benchmarks can be run with:
```sh
make bench
make bench-[backend]
# for example
make bench-singlepass
```
## Roadmap
@ -216,13 +244,12 @@ Below are some of the goals of this project (in order of priority):
- [x] It should be 100% compatible with the [WebAssembly spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests)
- [x] It should be fast _(partially achieved)_
- [x] Support WASI - released in [0.3.0](https://github.com/wasmerio/wasmer/releases/tag/0.3.0)
- [ ] Support Emscripten calls _(in the works)_
- [ ] Support Rust ABI calls
- [ ] Support Go ABI calls
- [x] Support Emscripten calls _(in the works)_
- [ ] Support Go js ABI calls
## Architecture
If you would like to know how Wasmer works under the hood, please see [ARCHITECTURE.md](./ARCHITECTURE.md).
If you would like to know how Wasmer works under the hood, please see [docs/architecture.md](./docs/architecture.md).
## License

View File

@ -3,8 +3,9 @@ status = [
"ci/circleci: test",
"ci/circleci: test-macos",
"ci/circleci: test-stable",
"ci/circleci: test-rust-example",
"continuous-integration/appveyor/branch"
]
required_approvals = 1
timeout_sec = 900
timeout_sec = 7200
delete_merged_branches = true

79
build Executable file
View File

@ -0,0 +1,79 @@
#!/bin/bash
# Wasmer build tool
#
# This is a script to build Wasmer in a Docker sandbox.
#
# To use the script, first make sure Docker is installed. Then build the
# sandbox image with:
#
# docker build --file Dockerfile.build --tag wasmer-build .
#
# After the sandbox image is built successfully, you can run commands in it
# with this script.
#
# For example, to build Wasmer, run:
#
# ./build make
#
# To test Wasmer, run:
#
# ./build make test
#
# and so on.
docker_hostname="wasmer-build"
docker_img="wasmer-build"
docker_workdir="/wasmer"
docker_args=(
#
# General config.
#
--hostname=${docker_hostname}
--interactive
--network=host
--rm
--tty
#
# User and group config.
#
# Use the same user and group permissions as host to make integration
# between host and container simple.
#
--user "$(id --user):$(id --group)"
--volume "/etc/group:/etc/group:ro"
--volume "/etc/passwd:/etc/passwd:ro"
--volume "/etc/shadow:/etc/shadow:ro"
#
# Time zone config.
#
# Use the same time zone as the host.
#
--volume "/etc/localtime:/etc/localtime:ro"
#
# Linux capabilities.
#
# Add SYS_PTRACE capability to the container so that people can run
# `strace'.
#
--cap-add SYS_PTRACE
#
# Source directory.
#
--workdir=${docker_workdir}
--volume "$(pwd):${docker_workdir}:z"
#
# Environment variables.
#
--env "CARGO_HOME=${docker_workdir}/.cargo"
)
docker run ${docker_args[@]} ${docker_img} $*

View File

@ -0,0 +1,11 @@
extern "C" {
fn it_works() -> i32;
}
#[no_mangle]
pub fn plugin_entrypoint(n: i32) -> i32 {
let result = unsafe { it_works() };
result + n
}
fn main() {}

Binary file not shown.

BIN
examples/particle-repel-simd.wasm Executable file

Binary file not shown.

BIN
examples/particle-repel.wasm Executable file

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,10 @@
use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::generate_import_object;
use wasmer_wasi::{
generate_import_object,
state::{self, WasiFile},
types,
};
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
5
}
#[derive(Debug)]
pub struct LoggingWrapper {
pub wasm_module_name: String,
}
// std io trait boiler plate so we can implement WasiFile
// LoggingWrapper is a write-only type so we just want to immediately
// fail when reading or Seeking
impl std::io::Read for LoggingWrapper {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
}
impl std::io::Seek for LoggingWrapper {
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek logging wrapper",
))
}
}
impl std::io::Write for LoggingWrapper {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
std::io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_all(buf)
}
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_fmt(fmt)
}
}
// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
impl WasiFile for LoggingWrapper {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}
/// Called by the program when it wants to set itself up
fn initialize(ctx: &mut Ctx) {
let state = unsafe { state::get_wasi_state(ctx) };
let wasi_file_inner = LoggingWrapper {
wasm_module_name: "example module name".to_string(),
};
// swap stdout with our new wasifile
let _old_stdout = state
.fs
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
.unwrap();
}
fn main() {
// Load the plugin data
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
@ -27,8 +132,10 @@ fn main() {
// The WASI imports object contains all required import functions for a WASI module to run.
// Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run.
base_imports.extend(custom_imports);
let instance =
let mut instance =
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
// set up logging by replacing stdout
initialize(instance.context_mut());
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();

View File

@ -33,6 +33,19 @@ INFO: seed corpus: files: 8 min: 1b max: 1b total: 8b rss: 133Mb
```
It will continue to generate random inputs forever, until it finds a bug or is terminated. The testcases for bugs it finds go into `fuzz/artifacts/simple_instantiate` and you can rerun the fuzzer on a single input by passing it on the command line `cargo fuzz run simple_instantiate my_testcase.wasm`.
## Seeding the corpus, optional
The fuzzer works best when it has examples of small Wasm files to start with. Using `wast2json` from [wabt](https://github.com/WebAssembly/wabt), we can easily produce `.wasm` files out of the WebAssembly spec tests.
```sh
mkdir spec-test-corpus
for i in lib/spectests/spectests/*.wast; do wast2json $i -o spec-test-corpus/$(basename $i).json; done
mv spec-test-corpus/*.wasm fuzz/corpus/simple_instantiate/
rm -r spec-test-corpus
```
The corpus directory is created on the first run of the fuzzer. If it doesn't exist, run it first and then seed the corpus. The fuzzer will pick up new files added to the corpus while it is running.
## Trophy case
- [x] https://github.com/wasmerio/wasmer/issues/558

View File

@ -35,6 +35,11 @@ dim="\e[2m"
# Warning: Remove this on the public repo
RELEASES_URL="https://github.com/wasmerio/wasmer/releases"
WASMER_VERBOSE="verbose"
if [ -z "$WASMER_INSTALL_LOG" ]; then
WASMER_INSTALL_LOG="$WASMER_VERBOSE"
fi
wasmer_download_json() {
url="$2"
@ -66,9 +71,19 @@ wasmer_download_file() {
# echo "Fetching $url.."
if test -x "$(command -v curl)"; then
code=$(curl --progress-bar -w '%{http_code}' -L "$url" -o "$destination")
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
code=$(curl --progress-bar -w '%{http_code}' -L "$url" -o "$destination")
printf "\033[K\n\033[1A"
else
code=$(curl -s -w '%{http_code}' -L "$url" -o "$destination")
fi
elif test -x "$(command -v wget)"; then
code=$(wget --show-progress --progress=bar:force:noscroll -q -O "$destination" --server-response "$url" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
code=$(wget --show-progress --progress=bar:force:noscroll -q -O "$destination" --server-response "$url" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
printf "\033[K\n\033[1A";
else
code=$(wget --quiet -O "$destination" --server-response "$url" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
fi
else
printf "$red> Neither curl nor wget was available to perform http requests.$reset\n"
exit 1
@ -130,7 +145,7 @@ wasmer_detect_profile() {
wasmer_link() {
printf "$cyan> Adding to bash profile...$reset\n"
WASMER_PROFILE="$(wasmer_detect_profile)"
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\"\n"
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"$INSTALL_DIRECTORY\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$WASMER_DIR/bin:\$WASMER_DIR/globals/wapm_packages/.bin:\$PATH\"\n"
# We create the wasmer.sh file
@ -152,19 +167,22 @@ wasmer_link() {
# fi
fi
printf "\033[1A$cyan> Adding to bash profile... ✓$reset\n"
printf "${dim}Note: We've added the following to your $WASMER_PROFILE\n"
echo "If this isn't the profile of your current shell then please add the following to your correct profile:"
printf "$LOAD_STR$reset\n"
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
printf "${dim}Note: We've added the following to your $WASMER_PROFILE\n"
echo "If you have a different profile please add the following:"
printf "$LOAD_STR$reset\n"
fi
version=`$INSTALL_DIRECTORY/bin/wasmer --version` || (
printf "$red> wasmer was installed, but doesn't seem to be working :($reset\n"
exit 1;
)
printf "$green> Successfully installed $version!\n\n${reset}If you want to have the command available now please execute:\nsource $INSTALL_DIRECTORY/wasmer.sh$reset\n"
printf "\nOtherwise, wasmer and wapm will be available the next time you open the terminal.\n"
echo "Note: during the alpha release of wapm, telemetry is enabled by default; if you would like to opt out, run \`wapm config set telemetry.enabled false\`."
echo "If you notice anything wrong or have any issues, please file a bug at https://github.com/wasmerio/wapm-cli :)"
printf "$green> Successfully installed $version!\n"
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
printf "${reset}${dim}wasmer & wapm will be available the next time you open the terminal.\n"
printf "${reset}${dim}If you want to have the commands available now please execute:\n${reset}source $INSTALL_DIRECTORY/wasmer.sh$reset\n"
fi
fi
}
@ -230,33 +248,36 @@ initOS() {
wasmer_install() {
magenta1="${reset}\033[34;1m"
magenta2="${reset}\033[34m"
magenta3="${reset}\033[34;2m"
magenta2=""
magenta3=""
if which wasmer >/dev/null; then
printf "${reset}Updating wasmer$reset\n"
printf "${reset}Updating Wasmer and WAPM$reset\n"
else
printf "${reset}Installing Wasmer!$reset\n"
printf "
${magenta1} ${magenta2} ${magenta3}###${reset}
${magenta1} ${magenta2} ${magenta3}#####${reset}
${magenta1} ${magenta2}### ${magenta3}######${reset}
${magenta1} ${magenta2}###### ${magenta3}#############${reset}
${magenta1}# ${magenta2}####### ${magenta3}##############${reset}
${magenta1}##### ${magenta2}#############${magenta3}#########${reset}
${magenta1}######${magenta2}###############${magenta3}#######${reset}
${magenta1}############${magenta2}#########${magenta3}#######${reset}
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
${magenta1}##############${magenta2}#######${magenta3}#######${reset}
${magenta1}##############${magenta2}#######${magenta3} ###${reset}
${magenta1}##############${magenta2}#######
${magenta1}###########${magenta2} ###
${magenta1}########${magenta2}
${magenta1}####${reset}
printf "${reset}Installing Wasmer and WAPM!$reset\n"
if [ "$WASMER_INSTALL_LOG" == "$WASMER_VERBOSE" ]; then
printf "
${magenta1} ww
${magenta1} wwwww
${magenta1} ww wwwwww w
${magenta1} wwwww wwwwwwwww
${magenta1}ww wwwwww w wwwwwww
${magenta1}wwwww wwwwwwwwww wwwww
${magenta1}wwwwww w wwwwwww wwwww
${magenta1}wwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwwww
${magenta1}wwwwwwwwwwwwwww wwwww wwww
${magenta1}wwwwwwwwwwwwwww wwwww
${magenta1} wwwwwwwwwwww wwww
${magenta1} wwwwwwww
${magenta1} wwww
${reset}
"
fi
fi
# if [ -d "$INSTALL_DIRECTORY" ]; then
# if which wasmer; then
# local latest_url
@ -419,7 +440,7 @@ wasmer_download() {
printf "$cyan> Downloading $WASMER_RELEASE_TAG release...$reset\n"
wasmer_download_file "$BINARY_URL" "$DOWNLOAD_FILE"
# echo -en "\b\b"
printf "\033[2A$cyan> Downloading $WASMER_RELEASE_TAG release... ✓$reset\033[K\n"
printf "\033[1A$cyan> Downloading $WASMER_RELEASE_TAG release... ✓$reset\n"
printf "\033[K\n\033[1A"
# printf "\033[1A$cyan> Downloaded$reset\033[K\n"
# echo "Setting executable permissions."
@ -431,9 +452,12 @@ wasmer_download() {
# echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME"
printf "$cyan> Unpacking contents...$reset\n"
mkdir -p $INSTALL_DIRECTORY
# Untar the wasmer contents in the install directory
tar -C $INSTALL_DIRECTORY -zxvf $DOWNLOAD_FILE
tar -C $INSTALL_DIRECTORY -zxf $DOWNLOAD_FILE
printf "\033[1A$cyan> Unpacking contents... ✓$reset\n"
}
wasmer_verify_or_quit() {

View File

@ -1,40 +1,41 @@
[package]
name = "wasmer-clif-backend"
version = "0.5.6"
version = "0.6.0"
description = "Wasmer runtime Cranelift compiler backend"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
cranelift-native = { version = "0.31" }
cranelift-codegen = { version = "0.31" }
cranelift-entity = { version = "0.31" }
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.31" }
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.31" }
hashbrown = "0.1"
cranelift-frontend = { package = "wasmer-clif-fork-frontend", version = "0.33" }
cranelift-wasm = { package = "wasmer-clif-fork-wasm", version = "0.33" }
target-lexicon = "0.4.0"
wasmparser = "0.32.1"
byteorder = "1"
nix = "0.14.0"
libc = "0.2.49"
rayon = "1.0"
wasmparser = "0.35.1"
byteorder = "1.3.2"
nix = "0.15.0"
libc = "0.2.60"
rayon = "1.1.0"
# Dependencies for caching.
[dependencies.serde]
version = "1.0"
version = "1.0.99"
features = ["rc"]
[dependencies.serde_derive]
version = "1.0"
version = "1.0.98"
[dependencies.serde_bytes]
version = "0.10"
version = "0.11.2"
[dependencies.serde-bench]
version = "0.0.7"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.5.6" }
winapi = { version = "0.3.7", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.6.0" }
[features]
debug = ["wasmer-runtime-core/debug"]

View File

@ -28,4 +28,26 @@ Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer).
This crate represents the Cranelift backend.
This crate represents the Cranelift backend integration for Wasmer.
## Usage
### Usage in Wasmer Standalone
If you are using the `wasmer` CLI, you can specify the backend with:
```bash
wasmer run program.wasm --backend=cranelift
```
### Usage in Wasmer Embedded
If you are using Wasmer Embedded, you can specify
the Cranelift backend to the [`compile_with` function](https://docs.rs/wasmer-runtime-core/*/wasmer_runtime_core/fn.compile_with.html):
```rust
use wasmer_clif_backend::CraneliftCompiler;
// ...
let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &CraneliftCompiler::new());
```

View File

@ -1,6 +1,6 @@
use crate::relocation::{ExternalRelocation, TrapSink};
use hashbrown::HashMap;
use std::collections::HashMap;
use std::sync::Arc;
use wasmer_runtime_core::{
backend::{sys::Memory, CacheGen},

View File

@ -33,7 +33,7 @@ use wasmer_runtime_core::{
use wasmparser::Type as WpType;
pub struct CraneliftModuleCodeGenerator {
isa: Box<isa::TargetIsa>,
isa: Box<dyn isa::TargetIsa>,
signatures: Option<Arc<Map<SigIndex, FuncSig>>>,
pub clif_signatures: Map<SigIndex, ir::Signature>,
function_signatures: Option<Arc<Map<FuncIndex, SigIndex>>>,
@ -89,10 +89,12 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
func,
func_translator,
next_local: 0,
clif_signatures: self.clif_signatures.clone(),
module_info: Arc::clone(&module_info),
target_config: self.isa.frontend_config().clone(),
position: Position::default(),
func_env: FunctionEnvironment {
module_info: Arc::clone(&module_info),
target_config: self.isa.frontend_config().clone(),
clif_signatures: self.clif_signatures.clone(),
},
};
debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty");
@ -304,8 +306,15 @@ impl ModuleCodeGenerator<CraneliftFunctionCodeGenerator, Caller, CodegenError>
let trampolines = Arc::new(Trampolines::new(&*self.isa, module_info));
let signatures_empty = Map::new();
let signatures = if self.signatures.is_some() {
&self.signatures.as_ref().unwrap()
} else {
&signatures_empty
};
let (func_resolver, backend_cache) = func_resolver_builder.finalize(
&self.signatures.as_ref().unwrap(),
signatures,
Arc::clone(&trampolines),
handler_data.clone(),
)?;
@ -384,10 +393,8 @@ pub struct CraneliftFunctionCodeGenerator {
func: Function,
func_translator: FuncTranslator,
next_local: usize,
pub clif_signatures: Map<SigIndex, ir::Signature>,
module_info: Arc<RwLock<ModuleInfo>>,
target_config: isa::TargetFrontendConfig,
position: Position,
func_env: FunctionEnvironment,
}
pub struct FunctionEnvironment {
@ -1131,11 +1138,6 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
//let builder = self.builder.as_mut().unwrap();
//let func_environment = FuncEnv::new();
//let state = TranslationState::new();
let mut function_environment = FunctionEnvironment {
module_info: Arc::clone(&self.module_info),
target_config: self.target_config.clone(),
clif_signatures: self.clif_signatures.clone(),
};
if self.func_translator.state.control_stack.is_empty() {
return Ok(());
@ -1147,7 +1149,7 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
&mut self.position,
);
let state = &mut self.func_translator.state;
translate_operator(op, &mut builder, state, &mut function_environment)?;
translate_operator(op, &mut builder, state, &mut self.func_env)?;
Ok(())
}

View File

@ -1,5 +1,12 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod cache;
mod code;
mod libcalls;
@ -21,7 +28,7 @@ extern crate serde_derive;
extern crate rayon;
extern crate serde;
fn get_isa() -> Box<isa::TargetIsa> {
fn get_isa() -> Box<dyn isa::TargetIsa> {
let flags = {
let mut builder = settings::builder();
builder.set("opt_level", "best").unwrap();

View File

@ -76,8 +76,7 @@ pub extern "C" fn nearbyintf64(x: f64) -> f64 {
}
}
/// A declaration for the stack probe function in Rust's standard library, for
/// catching callstack overflow.
extern "C" {
pub fn __rust_probestack();
}
// FIXME: Is there a replacement on AArch64?
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[no_mangle]
pub extern "C" fn __rust_probestack() {}

View File

@ -107,7 +107,8 @@ impl From<Converter<ir::Type>> for Type {
ir::types::I64 => Type::I64,
ir::types::F32 => Type::F32,
ir::types::F64 => Type::F64,
_ => panic!("unsupported wasm type"),
ir::types::I32X4 => Type::V128,
_ => unimplemented!("unsupported wasm type"),
}
}
}
@ -119,6 +120,7 @@ impl From<Converter<Type>> for ir::Type {
Type::I64 => ir::types::I64,
Type::F32 => ir::types::F32,
Type::F64 => ir::types::F64,
Type::V128 => ir::types::I32X4,
}
}
}
@ -130,6 +132,7 @@ impl From<Converter<Type>> for ir::AbiParam {
Type::I64 => ir::AbiParam::new(ir::types::I64),
Type::F32 => ir::AbiParam::new(ir::types::F32),
Type::F64 => ir::AbiParam::new(ir::types::F64),
Type::V128 => ir::AbiParam::new(ir::types::I32X4),
}
}
}

View File

@ -88,7 +88,7 @@ impl FuncResolverBuilder {
}
pub fn new(
isa: &isa::TargetIsa,
isa: &dyn isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>,
info: &ModuleInfo,
) -> CompileResult<(Self, HandlerData)> {

View File

@ -138,6 +138,14 @@ pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
}
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
unsafe fn get_faulting_addr_and_ip(
_siginfo: *const c_void,
_ucontext: *const c_void,
) -> (*const c_void, *const c_void) {
(::std::ptr::null(), ::std::ptr::null())
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
unsafe fn get_faulting_addr_and_ip(
siginfo: *const c_void,
@ -230,5 +238,6 @@ unsafe fn get_faulting_addr_and_ip(
#[cfg(not(any(
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
)))]
compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");

View File

@ -5,7 +5,7 @@ use cranelift_codegen::{
ir::{self, InstBuilder},
isa, Context,
};
use hashbrown::HashMap;
use std::collections::HashMap;
use std::{iter, mem, ptr::NonNull};
use wasmer_runtime_core::{
backend::sys::{Memory, Protect},
@ -66,7 +66,7 @@ impl Trampolines {
}
}
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
pub fn new(isa: &dyn isa::TargetIsa, module: &ModuleInfo) -> Self {
let func_index_iter = module
.exports
.values()
@ -204,6 +204,7 @@ fn wasm_ty_to_clif(ty: Type) -> ir::types::Type {
Type::I64 => ir::types::I64,
Type::F32 => ir::types::F32,
Type::F64 => ir::types::F64,
Type::V128 => ir::types::I32X4,
}
}

View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten-tests"
version = "0.5.6"
version = "0.6.0"
description = "Tests for our Emscripten implementation"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,18 +9,18 @@ publish = false
build = "build/mod.rs"
[dependencies]
wasmer-emscripten = { path = "../emscripten", version = "0.5.6" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.5.6" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.5.6", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.5.6", optional = true }
wasmer-emscripten = { path = "../emscripten", version = "0.6.0" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
[dev-dependencies]
wabt = "0.7.2"
wasmer-dev-utils = { path = "../dev-utils", version = "0.5.6"}
wabt = "0.9.1"
wasmer-dev-utils = { path = "../dev-utils", version = "0.6.0"}
[build-dependencies]
glob = "0.2.11"
glob = "0.3.0"
[features]
clif = []

View File

@ -42,7 +42,7 @@ macro_rules! assert_emscripten_output {
// let module = compile(&wasm_bytes[..])
// .map_err(|err| format!("Can't create the WebAssembly module: {}", err)).unwrap(); // NOTE: Need to figure what the unwrap is for ??
let mut emscripten_globals = EmscriptenGlobals::new(&module);
let mut emscripten_globals = EmscriptenGlobals::new(&module).expect("globals are valid");
let import_object = generate_emscripten_env(&mut emscripten_globals);
let mut instance = module.instantiate(&import_object)

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten"
version = "0.5.6"
version = "0.6.0"
description = "Wasmer runtime emscripten implementation library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,15 +8,14 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
byteorder = "1"
hashbrown = "0.1"
lazy_static = "1.2.0"
libc = "0.2.49"
time = "0.1.41"
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
byteorder = "1.3.2"
lazy_static = "1.3.0"
libc = "0.2.60"
time = "0.1.42"
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
[target.'cfg(windows)'.dependencies]
rand = "0.6"
rand = "0.7.0"
[features]
debug = ["wasmer-runtime-core/debug"]

View File

@ -30,6 +30,7 @@ pub fn call_malloc(ctx: &mut Ctx, size: u32) -> u32 {
.unwrap()
}
#[warn(dead_code)]
pub fn call_malloc_with_cast<T: Copy, Ty>(ctx: &mut Ctx, size: u32) -> WasmPtr<T, Ty> {
WasmPtr::new(call_malloc(ctx, size))
}

View File

@ -159,7 +159,7 @@ pub fn _gai_strerror(ctx: &mut Ctx, ecode: i32) -> i32 {
.unwrap()
};
for (i, byte) in bytes.iter().enumerate() {
writer[i].set(*byte as i8);
writer[i].set(*byte as _);
}
string_on_guest.offset() as _
@ -196,18 +196,15 @@ pub fn _getaddrinfo(
let hints = hints_ptr.deref(memory).map(|hints_memory| {
let hints_guest = hints_memory.get();
unsafe {
let mut hints_native: addrinfo = std::mem::uninitialized();
hints_native.ai_flags = hints_guest.ai_flags;
hints_native.ai_family = hints_guest.ai_family;
hints_native.ai_socktype = hints_guest.ai_socktype;
hints_native.ai_protocol = hints_guest.ai_protocol;
hints_native.ai_addrlen = 0;
hints_native.ai_addr = std::ptr::null_mut();
hints_native.ai_canonname = std::ptr::null_mut();
hints_native.ai_next = std::ptr::null_mut();
hints_native
addrinfo {
ai_flags: hints_guest.ai_flags,
ai_family: hints_guest.ai_family,
ai_socktype: hints_guest.ai_socktype,
ai_protocol: hints_guest.ai_protocol,
ai_addrlen: 0,
ai_addr: std::ptr::null_mut(),
ai_canonname: std::ptr::null_mut(),
ai_next: std::ptr::null_mut(),
}
});
@ -286,7 +283,7 @@ pub fn _getaddrinfo(
.deref(ctx.memory(0), 0, str_size as _)
.unwrap();
for (i, b) in canonname_bytes.into_iter().enumerate() {
guest_canonname_writer[i].set(*b as i8)
guest_canonname_writer[i].set(*b as _)
}
guest_canonname

View File

@ -37,7 +37,7 @@ pub fn execvp(ctx: &mut Ctx, command_name_offset: u32, argv_offset: u32) -> i32
// construct raw pointers and hand them to `execvp`
let command_pointer = command_name_string.as_ptr() as *const i8;
let args_pointer = argv.as_ptr();
unsafe { libc_execvp(command_pointer, args_pointer) }
unsafe { libc_execvp(command_pointer as *const _, args_pointer as *const *const _) }
}
/// execl

View File

@ -23,7 +23,7 @@ pub fn printf(ctx: &mut Ctx, memory_offset: i32, extra: i32) -> i32 {
pub fn chroot(ctx: &mut Ctx, name_ptr: i32) -> i32 {
debug!("emscripten::chroot");
let name = emscripten_memory_pointer!(ctx.memory(0), name_ptr) as *const i8;
unsafe { _chroot(name) }
unsafe { _chroot(name as *const _) }
}
/// getpwuid

View File

@ -1,15 +1,22 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
#[macro_use]
extern crate wasmer_runtime_core;
use hashbrown::HashMap;
use lazy_static::lazy_static;
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::path::PathBuf;
use std::{f64, ffi::c_void};
use wasmer_runtime_core::{
error::CallResult,
error::{CallError, CallResult, ResolveError},
export::Export,
func,
global::Global,
@ -25,11 +32,11 @@ use wasmer_runtime_core::{
};
#[cfg(unix)]
use ::libc::DIR as libcDIR;
use ::libc::DIR as LibcDir;
// We use a placeholder for windows
#[cfg(not(unix))]
type libcDIR = u8;
type LibcDir = u8;
#[macro_use]
mod macros;
@ -93,7 +100,7 @@ pub struct EmscriptenData<'a> {
pub memset: Option<Func<'a, (u32, u32, u32), u32>>,
pub stack_alloc: Option<Func<'a, u32, u32>>,
pub jumps: Vec<UnsafeCell<[u32; 27]>>,
pub opened_dirs: HashMap<i32, Box<*mut libcDIR>>,
pub opened_dirs: HashMap<i32, Box<*mut LibcDir>>,
pub dyn_call_i: Option<Func<'a, i32, i32>>,
pub dyn_call_ii: Option<Func<'a, (i32, i32), i32>>,
@ -366,10 +373,11 @@ pub fn run_emscripten_instance(
0 => {
instance.call(func_name, &[])?;
}
_ => panic!(
"The emscripten main function has received an incorrect number of params {}",
num_params
),
_ => {
return Err(CallError::Resolve(ResolveError::ExportWrongType {
name: "main".to_string(),
}))
}
};
}
@ -397,11 +405,18 @@ fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) {
(argc as u32 - 1, argv_offset)
}
pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
pub fn emscripten_set_up_memory(
memory: &Memory,
globals: &EmscriptenGlobalsData,
) -> Result<(), String> {
let dynamictop_ptr = globals.dynamictop_ptr;
let dynamic_base = globals.dynamic_base;
if (dynamictop_ptr / 4) as usize >= memory.view::<u32>().len() {
return Err("dynamictop_ptr beyond memory len".to_string());
}
memory.view::<u32>()[(dynamictop_ptr / 4) as usize].set(dynamic_base);
Ok(())
}
pub struct EmscriptenGlobalsData {
@ -429,7 +444,7 @@ pub struct EmscriptenGlobals {
}
impl EmscriptenGlobals {
pub fn new(module: &Module /*, static_bump: u32 */) -> Self {
pub fn new(module: &Module /*, static_bump: u32 */) -> Result<Self, String> {
let mut use_old_abort_on_cannot_grow_memory = false;
for (
index,
@ -451,8 +466,8 @@ impl EmscriptenGlobals {
}
}
let (table_min, table_max) = get_emscripten_table_size(&module);
let (memory_min, memory_max, shared) = get_emscripten_memory_size(&module);
let (table_min, table_max) = get_emscripten_table_size(&module)?;
let (memory_min, memory_max, shared) = get_emscripten_memory_size(&module)?;
// Memory initialization
let memory_type = MemoryDescriptor {
@ -481,7 +496,7 @@ impl EmscriptenGlobals {
static_top += 16;
let (dynamic_base, dynamictop_ptr) =
get_emscripten_metadata(&module).unwrap_or_else(|| {
get_emscripten_metadata(&module)?.unwrap_or_else(|| {
let dynamictop_ptr = static_alloc(&mut static_top, 4);
(
align_memory(align_memory(static_top) + TOTAL_STACK),
@ -505,7 +520,7 @@ impl EmscriptenGlobals {
}
};
emscripten_set_up_memory(&memory, &data);
emscripten_set_up_memory(&memory, &data)?;
let mut null_func_names = vec![];
for (
@ -523,14 +538,14 @@ impl EmscriptenGlobals {
}
}
Self {
Ok(Self {
data,
memory,
table,
memory_min,
memory_max,
null_func_names,
}
})
}
}

View File

@ -54,7 +54,7 @@ pub fn killpg(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
pub fn pathconf(ctx: &mut Ctx, path_ptr: i32, name: i32) -> i32 {
debug!("emscripten::pathconf");
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
unsafe { libc::pathconf(path, name).try_into().unwrap() }
unsafe { libc::pathconf(path as *const _, name).try_into().unwrap() }
}
#[cfg(not(unix))]

View File

@ -71,7 +71,7 @@ pub fn sbrk(ctx: &mut Ctx, increment: i32) -> i32 {
debug!("emscripten::sbrk");
// let old_dynamic_top = 0;
// let new_dynamic_top = 0;
let mut globals = get_emscripten_data(ctx).globals;
let globals = get_emscripten_data(ctx).globals;
let dynamictop_ptr = (globals.dynamictop_ptr) as usize;
let old_dynamic_top = ctx.memory(0).view::<u32>()[dynamictop_ptr].get() as i32;
let new_dynamic_top: i32 = old_dynamic_top + increment;

View File

@ -97,7 +97,7 @@ pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall12 (chdir) {}", _which);
let path_ptr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, path_ptr);
let real_path_owned = get_cstr_path(ctx, path_ptr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -168,13 +168,13 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall38 (rename)");
let old_path = varargs.get_str(ctx);
let new_path = varargs.get_str(ctx);
let real_old_path_owned = get_cstr_path(ctx, old_path);
let real_old_path_owned = get_cstr_path(ctx, old_path as *const _);
let real_old_path = if let Some(ref rp) = real_old_path_owned {
rp.as_c_str().as_ptr()
} else {
old_path
};
let real_new_path_owned = get_cstr_path(ctx, new_path);
let real_new_path_owned = get_cstr_path(ctx, new_path as *const _);
let real_new_path = if let Some(ref rp) = real_new_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -194,7 +194,7 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall40 (rmdir)");
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path_owned = get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -359,7 +359,7 @@ pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32
let buf_writer = buf_offset.deref(ctx.memory(0), 0, len as u32 + 1).unwrap();
for (i, byte) in path_string.bytes().enumerate() {
buf_writer[i].set(byte as i8);
buf_writer[i].set(byte as _);
}
buf_writer[len].set(0);
buf_offset.offset() as i32
@ -535,7 +535,7 @@ pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
let pathname_addr = varargs.get_str(ctx);
let buf: u32 = varargs.get(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path_owned = get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {

View File

@ -146,7 +146,7 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
let pathname_addr = varargs.get_str(ctx);
let flags: i32 = varargs.get(ctx);
let mode: u32 = varargs.get(ctx);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -198,13 +198,13 @@ pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
let path1 = varargs.get_str(ctx);
let path2 = varargs.get_str(ctx);
let real_path1_owned = utils::get_cstr_path(ctx, path1);
let real_path1_owned = utils::get_cstr_path(ctx, path1 as *const _);
let real_path1 = if let Some(ref rp) = real_path1_owned {
rp.as_c_str().as_ptr()
} else {
path1
};
let real_path2_owned = utils::get_cstr_path(ctx, path2);
let real_path2_owned = utils::get_cstr_path(ctx, path2 as *const _);
let real_path2 = if let Some(ref rp) = real_path2_owned {
rp.as_c_str().as_ptr()
} else {
@ -227,7 +227,7 @@ pub fn ___syscall85(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
let buf = varargs.get_str(ctx);
// let buf_addr: i32 = varargs.get(ctx);
let buf_size: i32 = varargs.get(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path_owned = get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -266,7 +266,7 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub fn ___syscall198(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall198 (lchown) {}", _which);
let path_ptr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path_ptr);
let real_path_owned = utils::get_cstr_path(ctx, path_ptr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -307,7 +307,7 @@ pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
debug!("emscripten::___syscall212 (chown) {}", _which);
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -336,7 +336,7 @@ pub fn ___syscall219(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall33 (access) {}", _which);
let path = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path);
let real_path_owned = utils::get_cstr_path(ctx, path as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -364,7 +364,7 @@ pub fn ___syscall34(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall39 (mkdir) {}", _which);
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -617,13 +617,13 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
// let mut address_len_addr: socklen_t = 0;
let (fd, host_address) = unsafe {
let mut host_address: sockaddr = std::mem::uninitialized();
let fd = accept(socket, &mut host_address, address_len_addr);
(fd, host_address)
let mut host_address: sockaddr = sockaddr {
sa_family: Default::default(),
sa_data: Default::default(),
#[cfg(target_os = "macos")]
sa_len: Default::default(),
};
let fd = unsafe { accept(socket, &mut host_address, address_len_addr) };
let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_addr.sa_family = host_address.sa_family as _;
@ -651,15 +651,18 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
let address_len_addr =
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
let (ret, sock_addr_host) = unsafe {
// read host data into new var
let mut address: sockaddr = std::mem::uninitialized();
let ret = getsockname(
let mut sock_addr_host: sockaddr = sockaddr {
sa_family: Default::default(),
sa_data: Default::default(),
#[cfg(target_os = "macos")]
sa_len: Default::default(),
};
let ret = unsafe {
getsockname(
socket,
&mut address as *mut sockaddr,
&mut sock_addr_host as *mut sockaddr,
address_len_addr as *mut u32,
);
(ret, address)
)
};
// translate from host data into emscripten data
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
@ -983,7 +986,7 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall196 (lstat64) {}", _which);
let path = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path);
let real_path_owned = utils::get_cstr_path(ctx, path as *const _);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
@ -1060,7 +1063,7 @@ pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
let upper_bound = std::cmp::min((*dirent).d_reclen, 255) as usize;
let mut i = 0;
while i < upper_bound {
*(dirp.add(pos + 11 + i) as *mut i8) = (*dirent).d_name[i];
*(dirp.add(pos + 11 + i) as *mut i8) = (*dirent).d_name[i] as _;
i += 1;
}
// We set the termination string char

View File

@ -33,29 +33,43 @@ pub fn is_emscripten_module(module: &Module) -> bool {
false
}
pub fn get_emscripten_table_size(module: &Module) -> (u32, Option<u32>) {
pub fn get_emscripten_table_size(module: &Module) -> Result<(u32, Option<u32>), String> {
if module.info().imported_tables.len() == 0 {
return Err("Emscripten requires at least one imported table".to_string());
}
let (_, table) = &module.info().imported_tables[ImportedTableIndex::new(0)];
(table.minimum, table.maximum)
Ok((table.minimum, table.maximum))
}
pub fn get_emscripten_memory_size(module: &Module) -> (Pages, Option<Pages>, bool) {
pub fn get_emscripten_memory_size(module: &Module) -> Result<(Pages, Option<Pages>, bool), String> {
if module.info().imported_memories.len() == 0 {
return Err("Emscripten requires at least one imported memory".to_string());
}
let (_, memory) = &module.info().imported_memories[ImportedMemoryIndex::new(0)];
(memory.minimum, memory.maximum, memory.shared)
Ok((memory.minimum, memory.maximum, memory.shared))
}
/// Reads values written by `-s EMIT_EMSCRIPTEN_METADATA=1`
/// Assumes values start from the end in this order:
/// Last export: Dynamic Base
/// Second-to-Last export: Dynamic top pointer
pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> {
let max_idx = &module.info().globals.iter().map(|(k, _)| k).max()?;
let snd_max_idx = &module
pub fn get_emscripten_metadata(module: &Module) -> Result<Option<(u32, u32)>, String> {
let max_idx = match module.info().globals.iter().map(|(k, _)| k).max() {
Some(x) => x,
None => return Ok(None),
};
let snd_max_idx = match module
.info()
.globals
.iter()
.map(|(k, _)| k)
.filter(|k| k != max_idx)
.max()?;
.filter(|k| *k != max_idx)
.max()
{
Some(x) => x,
None => return Ok(None),
};
use wasmer_runtime_core::types::{GlobalInit, Initializer::Const, Value::I32};
if let (
@ -68,15 +82,23 @@ pub fn get_emscripten_metadata(module: &Module) -> Option<(u32, u32)> {
..
},
) = (
&module.info().globals[*max_idx],
&module.info().globals[*snd_max_idx],
&module.info().globals[max_idx],
&module.info().globals[snd_max_idx],
) {
Some((
align_memory(*dynamic_base as u32 - 32),
align_memory(*dynamictop_ptr as u32 - 32),
))
let dynamic_base = (*dynamic_base as u32).checked_sub(32).ok_or(format!(
"emscripten unexpected dynamic_base {}",
*dynamic_base as u32
))?;
let dynamictop_ptr = (*dynamictop_ptr as u32).checked_sub(32).ok_or(format!(
"emscripten unexpected dynamictop_ptr {}",
*dynamictop_ptr as u32
))?;
Ok(Some((
align_memory(dynamic_base),
align_memory(dynamictop_ptr),
)))
} else {
None
Ok(None)
}
}
@ -214,7 +236,8 @@ pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String {
pub fn get_cstr_path(ctx: &mut Ctx, path: *const i8) -> Option<std::ffi::CString> {
use std::collections::VecDeque;
let path_str = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string();
let path_str =
unsafe { std::ffi::CStr::from_ptr(path as *const _).to_str().unwrap() }.to_string();
let data = get_emscripten_data(ctx);
let path = PathBuf::from(path_str);
let mut prefix_added = false;

View File

@ -5,5 +5,5 @@ authors = ["Heyang Zhou <zhy20000919@hotmail.com>"]
edition = "2018"
[dependencies]
libc = "0.2.49"
libc = "0.2.60"
wasmer-runtime-core = { path = "../runtime-core" }

View File

@ -75,7 +75,7 @@ impl Loader for KernelLoader {
if module.imported_globals.len() > 0 {
return Err("imported globals are not supported".into());
}
let globals: Vec<u64> = unsafe {
let globals: Vec<u128> = unsafe {
let globals: &[*mut LocalGlobal] =
::std::slice::from_raw_parts(ctx.globals, module.globals.len());
globals.iter().map(|x| (**x).data).collect()
@ -138,11 +138,11 @@ pub struct KernelInstance {
impl Instance for KernelInstance {
type Error = String;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, String> {
fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, 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 args: Vec<u128> = args.iter().map(|x| x.to_u128()).collect();
let ret = self
.context

View File

@ -54,7 +54,7 @@ struct LoadCodeRequest {
memory_max: u32,
table: *const TableEntryRequest,
table_count: u32,
globals: *const u64,
globals: *const u128,
global_count: u32,
imported_funcs: *const ImportRequest,
@ -67,7 +67,7 @@ struct LoadCodeRequest {
#[repr(C)]
struct RunCodeRequest {
entry_offset: u32,
params: *const u64,
params: *const u128,
param_count: u32,
result: *mut RunCodeResult,
}
@ -75,7 +75,7 @@ struct RunCodeRequest {
#[repr(C)]
struct RunCodeResult {
success: u32,
retval: u64,
retval: u128,
}
#[repr(C)]
@ -108,7 +108,7 @@ pub struct LoadProfile<'a> {
pub code: &'a [u8],
pub memory: Option<&'a [u8]>,
pub memory_max: usize,
pub globals: &'a [u64],
pub globals: &'a [u128],
pub imports: &'a [ImportInfo],
pub dynamic_sigindices: &'a [u32],
pub table: Option<&'a [TableEntryRequest]>,
@ -121,7 +121,7 @@ pub struct ImportInfo {
pub struct RunProfile<'a> {
pub entry_offset: u32,
pub params: &'a [u64],
pub params: &'a [u128],
}
pub struct ServiceContext {
@ -181,7 +181,7 @@ impl ServiceContext {
}
}
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u64> {
pub fn run_code(&mut self, run: RunProfile) -> ServiceResult<u128> {
let mut result: RunCodeResult = unsafe { ::std::mem::zeroed() };
let mut req = RunCodeRequest {
entry_offset: run.entry_offset,

View File

@ -219,7 +219,7 @@ fn __get_async_io_payload<
}
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Default, Copy, Clone)]
struct SockaddrIn {
sin_family: u16, // e.g. AF_INET
sin_port: u16, // e.g. htons(3490)
@ -315,7 +315,7 @@ impl Tcp4Listener {
self.fd,
EpollDirection::In,
move |fd| -> Result<Arc<TcpStream>, i32> {
let mut incoming_sa: SockaddrIn = unsafe { ::std::mem::uninitialized() };
let mut incoming_sa: SockaddrIn = SockaddrIn::default();
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 {

View File

@ -1,29 +1,39 @@
[package]
name = "wasmer-llvm-backend"
version = "0.5.6"
authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"]
version = "0.6.0"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
readme = "README.md"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.5.6" }
inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm7-0" }
wasmparser = "0.32.1"
hashbrown = "0.1.8"
smallvec = "0.6.8"
goblin = "0.0.20"
libc = "0.2.49"
nix = "0.14.0"
capstone = { version = "0.5.0", optional = true }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
wasmparser = "0.35.1"
smallvec = "0.6.10"
goblin = "0.0.24"
libc = "0.2.60"
capstone = { version = "0.6.0", optional = true }
[dependencies.inkwell]
git = "https://github.com/wasmerio/inkwell"
branch = "llvm8-0"
default-features = false
features = ["llvm8-0", "target-x86"]
[target.'cfg(unix)'.dependencies]
nix = "0.15.0"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.7", features = ["memoryapi"] }
[build-dependencies]
cc = "1.0"
lazy_static = "1.2.0"
regex = "1.1.0"
lazy_static = "1.3.0"
regex = "1.2.1"
semver = "0.9"
rustc_version = "0.2.3"
[dev-dependencies]
wabt = "0.7.4"
wabt = "0.9.1"
[features]
debug = ["wasmer-runtime-core/debug"]

View File

@ -28,4 +28,26 @@ Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer).
This crate represents the LLVM backend.
This crate represents the LLVM backend integration for Wasmer.
## Usage
### Usage in Wasmer Standalone
If you are using the `wasmer` CLI, you can specify the backend with:
```bash
wasmer run program.wasm --backend=llvm
```
### Usage in Wasmer Embedded
If you are using Wasmer Embedded, you can specify
the LLVM backend to the [`compile_with` function](https://docs.rs/wasmer-runtime-core/*/wasmer_runtime_core/fn.compile_with.html):
```rust
use wasmer_llvm_backend::LLVMCompiler;
// ...
let module = wasmer_runtime_core::compile_with(&wasm_binary[..], &LLVMCompiler::new());
```

View File

@ -1,5 +1,5 @@
//! This file was mostly taken from the llvm-sys crate.
//! (https://bitbucket.org/tari/llvm-sys.rs/src/21ab524ec4df1450035df895209c3f8fbeb8775f/build.rs?at=default&fileviewer=file-view-default)
//! (https://bitbucket.org/tari/llvm-sys.rs/raw/94361c1083a88f439b9d24c59b2d2831517413d7/build.rs)
use lazy_static::lazy_static;
use regex::Regex;
@ -10,23 +10,54 @@ use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;
// Version of the llvm-sys crate that we (through inkwell) depend on.
const LLVM_SYS_MAJOR_VERSION: &str = "80";
const LLVM_SYS_MINOR_VERSION: &str = "0";
// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
/// A single path to search for LLVM in (containing bin/llvm-config)
static ref ENV_LLVM_PREFIX: String =
format!("LLVM_SYS_{}_PREFIX", LLVM_SYS_MAJOR_VERSION);
/// If exactly "YES", ignore the version blacklist
static ref ENV_IGNORE_BLACKLIST: String =
format!("LLVM_SYS_{}_IGNORE_BLACKLIST", LLVM_SYS_MAJOR_VERSION);
/// If set, enforce precise correspondence between crate and binary versions.
static ref ENV_STRICT_VERSIONING: String =
format!("LLVM_SYS_{}_STRICT_VERSIONING", LLVM_SYS_MAJOR_VERSION);
/// If set, do not attempt to strip irrelevant options for llvm-config --cflags
static ref ENV_NO_CLEAN_CXXFLAGS: String =
format!("LLVM_SYS_{}_NO_CLEAN_CXXFLAGS", LLVM_SYS_MAJOR_VERSION);
/// If set and targeting MSVC, force the debug runtime library
static ref ENV_USE_DEBUG_MSVCRT: String =
format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", LLVM_SYS_MAJOR_VERSION);
/// If set, always link against libffi
static ref ENV_FORCE_FFI: String =
format!("LLVM_SYS_{}_FFI_WORKAROUND", LLVM_SYS_MAJOR_VERSION);
}
lazy_static! {
/// LLVM version used by this version of the crate.
static ref CRATE_VERSION: Version = {
let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
.expect("Crate version is somehow not valid semver");
Version {
major: crate_version.major / 10,
minor: crate_version.major % 10,
.. crate_version
}
Version::new(LLVM_SYS_MAJOR_VERSION.parse::<u64>().unwrap() / 10,
LLVM_SYS_MINOR_VERSION.parse::<u64>().unwrap() % 10,
0)
};
static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
vec![
"llvm-config".into(),
// format!("llvm-config-{}", CRATE_VERSION.major),
// format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
format!("llvm-config-{}", CRATE_VERSION.major),
format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
]
};
@ -41,21 +72,7 @@ lazy_static! {
// Did the user give us a binary path to use? If yes, try
// to use that and fail if it doesn't work.
let binary_prefix_var = "LLVM_SYS_70_PREFIX";
let path = if let Some(path) = env::var_os(&binary_prefix_var) {
Some(path.to_str().unwrap().to_owned())
} else if let Ok(mut file) = std::fs::File::open(".llvmenv") {
use std::io::Read;
let mut s = String::new();
file.read_to_string(&mut s).unwrap();
s.truncate(s.len() - 4);
Some(s)
} else {
None
};
if let Some(path) = path {
if let Some(path) = env::var_os(&*ENV_LLVM_PREFIX) {
for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() {
let mut pb: PathBuf = path.clone().into();
pb.push("bin");
@ -67,7 +84,7 @@ lazy_static! {
return pb;
} else {
println!("LLVM binaries specified by {} are the wrong version.
(Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION);
(Found {}, need {}.)", *ENV_LLVM_PREFIX, ver, *CRATE_VERSION);
}
}
}
@ -79,7 +96,7 @@ lazy_static! {
refer to the llvm-sys documentation for more information.
llvm-sys: https://crates.io/crates/llvm-sys
llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var);
llvmenv: https://crates.io/crates/llvmenv", *ENV_LLVM_PREFIX);
panic!("Could not find a compatible version of LLVM");
};
}
@ -115,15 +132,55 @@ fn locate_system_llvm_config() -> Option<&'static str> {
None
}
/// Check whether the given version of LLVM is blacklisted,
/// returning `Some(reason)` if it is.
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];
if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
if &x == "YES" {
println!(
"cargo:warning=Ignoring blacklist entry for LLVM {}",
llvm_version
);
return None;
} else {
println!(
"cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
*ENV_IGNORE_BLACKLIST
);
}
}
for &(major, minor, patch, reason) in BLACKLIST.iter() {
let bad_version = Version {
major: major,
minor: minor,
patch: patch,
pre: vec![],
build: vec![],
};
if &bad_version == llvm_version {
return Some(reason);
}
}
None
}
/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
let strict = env::var_os(format!(
"LLVM_SYS_{}_STRICT_VERSIONING",
env!("CARGO_PKG_VERSION_MAJOR")
))
.is_some()
|| cfg!(feature = "strict-versioning");
if let Some(reason) = is_blacklisted_llvm(llvm_version) {
println!(
"Found LLVM {}, which is blacklisted: {}",
llvm_version, reason
);
return false;
}
let strict =
env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
if strict {
llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
} else {
@ -184,11 +241,7 @@ fn get_llvm_cxxflags() -> String {
// include flags that aren't understood by the default compiler we're
// using. Unless requested otherwise, clean CFLAGS of options that are
// known to be possibly-harmful.
let no_clean = env::var_os(format!(
"LLVM_SYS_{}_NO_CLEAN_CFLAGS",
env!("CARGO_PKG_VERSION_MAJOR")
))
.is_some();
let no_clean = env::var_os(&*ENV_NO_CLEAN_CXXFLAGS).is_some();
if no_clean || cfg!(target_env = "msvc") {
// MSVC doesn't accept -W... options, so don't try to strip them and
// possibly strip something that should be retained. Also do nothing if
@ -204,20 +257,43 @@ fn get_llvm_cxxflags() -> String {
.join(" ")
}
fn is_llvm_debug() -> bool {
// Has to be either Debug or Release
llvm_config("--build-mode").contains("Debug")
}
fn main() {
println!("cargo:rustc-link-lib=static=llvm-backend");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CXXFLAGS);
println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);
std::env::set_var("CXXFLAGS", get_llvm_cxxflags());
cc::Build::new()
.cpp(true)
.file("cpp/object_loader.cpp")
.compile("llvm-backend");
println!("cargo:rustc-link-lib=static=llvm-backend");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
// Enable "nightly" cfg if the current compiler is nightly.
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
println!("cargo:rustc-cfg=nightly");
}
let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
println!("cargo:rustc-link-lib={}", "msvcrtd");
}
// Link libffi if the user requested this workaround.
// See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
if force_ffi {
println!("cargo:rustc-link-lib=dylib={}", "ffi");
}
}

View File

@ -7,198 +7,214 @@ extern "C" void __deregister_frame(uint8_t *);
struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
public:
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
virtual ~MemoryManager() override {
deregisterEHFrames();
// Deallocate all of the allocated memory.
callbacks.dealloc_memory(code_section.base, code_section.size);
callbacks.dealloc_memory(read_section.base, read_section.size);
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
virtual ~MemoryManager() override {
deregisterEHFrames();
// Deallocate all of the allocated memory.
callbacks.dealloc_memory(code_section.base, code_section.size);
callbacks.dealloc_memory(read_section.base, read_section.size);
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
}
virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment,
unsigned section_id,
llvm::StringRef section_name) override {
return allocate_bump(code_section, code_bump_ptr, size, alignment);
}
virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment,
unsigned section_id,
llvm::StringRef section_name,
bool read_only) override {
// Allocate from the read-only section or the read-write section, depending
// on if this allocation should be read-only or not.
if (read_only) {
return allocate_bump(read_section, read_bump_ptr, size, alignment);
} else {
return allocate_bump(readwrite_section, readwrite_bump_ptr, size,
alignment);
}
}
virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override {
return allocate_bump(code_section, code_bump_ptr, size, alignment);
}
virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool read_only) override {
// Allocate from the read-only section or the read-write section, depending on if this allocation
// should be read-only or not.
if (read_only) {
return allocate_bump(read_section, read_bump_ptr, size, alignment);
} else {
return allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment);
}
}
virtual void reserveAllocationSpace(
uintptr_t code_size,
uint32_t code_align,
uintptr_t read_data_size,
uint32_t read_data_align,
uintptr_t read_write_data_size,
uint32_t read_write_data_align
) override {
auto aligner = [](uintptr_t ptr, size_t align) {
if (ptr == 0) {
return align;
}
return (ptr + align - 1) & ~(align - 1);
};
uint8_t *code_ptr_out = nullptr;
size_t code_size_out = 0;
auto code_result = callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE, &code_ptr_out, &code_size_out);
assert(code_result == RESULT_OK);
code_section = Section { code_ptr_out, code_size_out };
code_bump_ptr = (uintptr_t)code_ptr_out;
uint8_t *read_ptr_out = nullptr;
size_t read_size_out = 0;
auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE, &read_ptr_out, &read_size_out);
assert(read_result == RESULT_OK);
read_section = Section { read_ptr_out, read_size_out };
read_bump_ptr = (uintptr_t)read_ptr_out;
uint8_t *readwrite_ptr_out = nullptr;
size_t readwrite_size_out = 0;
auto readwrite_result = callbacks.alloc_memory(aligner(read_write_data_size, 4096), PROTECT_READ_WRITE, &readwrite_ptr_out, &readwrite_size_out);
assert(readwrite_result == RESULT_OK);
readwrite_section = Section { readwrite_ptr_out, readwrite_size_out };
readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
}
/* Turn on the `reserveAllocationSpace` callback. */
virtual bool needsToReserveAllocationSpace() override {
return true;
}
virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override {
eh_frame_ptr = addr;
eh_frame_size = size;
eh_frames_registered = true;
callbacks.visit_fde(addr, size, __register_frame);
}
virtual void deregisterEHFrames() override {
if (eh_frames_registered) {
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
}
}
virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override {
auto code_result = callbacks.protect_memory(code_section.base, code_section.size, mem_protect_t::PROTECT_READ_EXECUTE);
if (code_result != RESULT_OK) {
return false;
}
auto read_result = callbacks.protect_memory(read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
if (read_result != RESULT_OK) {
return false;
}
// The readwrite section is already mapped as read-write.
return false;
}
virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override {}
private:
struct Section {
uint8_t* base;
size_t size;
virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align,
uintptr_t read_data_size,
uint32_t read_data_align,
uintptr_t read_write_data_size,
uint32_t read_write_data_align) override {
auto aligner = [](uintptr_t ptr, size_t align) {
if (ptr == 0) {
return align;
}
return (ptr + align - 1) & ~(align - 1);
};
uint8_t* allocate_bump(Section& section, uintptr_t& bump_ptr, size_t size, size_t align) {
auto aligner = [](uintptr_t& ptr, size_t align) {
ptr = (ptr + align - 1) & ~(align - 1);
};
uint8_t *code_ptr_out = nullptr;
size_t code_size_out = 0;
auto code_result =
callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE,
&code_ptr_out, &code_size_out);
assert(code_result == RESULT_OK);
code_section = Section{code_ptr_out, code_size_out};
code_bump_ptr = (uintptr_t)code_ptr_out;
// Align the bump pointer to the requires alignment.
aligner(bump_ptr, align);
uint8_t *read_ptr_out = nullptr;
size_t read_size_out = 0;
auto read_result = callbacks.alloc_memory(aligner(read_data_size, 4096),
PROTECT_READ_WRITE, &read_ptr_out,
&read_size_out);
assert(read_result == RESULT_OK);
read_section = Section{read_ptr_out, read_size_out};
read_bump_ptr = (uintptr_t)read_ptr_out;
auto ret_ptr = bump_ptr;
bump_ptr += size;
uint8_t *readwrite_ptr_out = nullptr;
size_t readwrite_size_out = 0;
auto readwrite_result = callbacks.alloc_memory(
aligner(read_write_data_size, 4096), PROTECT_READ_WRITE,
&readwrite_ptr_out, &readwrite_size_out);
assert(readwrite_result == RESULT_OK);
readwrite_section = Section{readwrite_ptr_out, readwrite_size_out};
readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
}
assert(bump_ptr <= (uintptr_t)section.base + section.size);
/* Turn on the `reserveAllocationSpace` callback. */
virtual bool needsToReserveAllocationSpace() override { return true; }
return (uint8_t*)ret_ptr;
virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
size_t size) override {
// We don't know yet how to do this on Windows, so we hide this on compilation
// so we can compile and pass spectests on unix systems
#ifndef _WIN32
eh_frame_ptr = addr;
eh_frame_size = size;
eh_frames_registered = true;
callbacks.visit_fde(addr, size, __register_frame);
#endif
}
virtual void deregisterEHFrames() override {
// We don't know yet how to do this on Windows, so we hide this on compilation
// so we can compile and pass spectests on unix systems
#ifndef _WIN32
if (eh_frames_registered) {
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
}
#endif
}
virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override {
auto code_result =
callbacks.protect_memory(code_section.base, code_section.size,
mem_protect_t::PROTECT_READ_EXECUTE);
if (code_result != RESULT_OK) {
return false;
}
Section code_section, read_section, readwrite_section;
uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
uint8_t* eh_frame_ptr;
size_t eh_frame_size;
bool eh_frames_registered = false;
auto read_result = callbacks.protect_memory(
read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
if (read_result != RESULT_OK) {
return false;
}
callbacks_t callbacks;
// The readwrite section is already mapped as read-write.
return false;
}
virtual void
notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
const llvm::object::ObjectFile &Obj) override {}
private:
struct Section {
uint8_t *base;
size_t size;
};
uint8_t *allocate_bump(Section &section, uintptr_t &bump_ptr, size_t size,
size_t align) {
auto aligner = [](uintptr_t &ptr, size_t align) {
ptr = (ptr + align - 1) & ~(align - 1);
};
// Align the bump pointer to the requires alignment.
aligner(bump_ptr, align);
auto ret_ptr = bump_ptr;
bump_ptr += size;
assert(bump_ptr <= (uintptr_t)section.base + section.size);
return (uint8_t *)ret_ptr;
}
Section code_section, read_section, readwrite_section;
uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
uint8_t *eh_frame_ptr;
size_t eh_frame_size;
bool eh_frames_registered = false;
callbacks_t callbacks;
};
struct SymbolLookup : llvm::JITSymbolResolver {
public:
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
virtual llvm::Expected<LookupResult> lookup(const LookupSet& symbols) override {
LookupResult result;
void lookup(const LookupSet &symbols, OnResolvedFunction OnResolved) {
LookupResult result;
for (auto symbol : symbols) {
result.emplace(symbol, symbol_lookup(symbol));
}
return result;
for (auto symbol : symbols) {
result.emplace(symbol, symbol_lookup(symbol));
}
virtual llvm::Expected<LookupFlagsResult> lookupFlags(const LookupSet& symbols) override {
LookupFlagsResult result;
OnResolved(result);
}
for (auto symbol : symbols) {
result.emplace(symbol, symbol_lookup(symbol).getFlags());
}
return result;
}
llvm::Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
const std::set<llvm::StringRef> empty;
return empty;
}
private:
llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) {
uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size());
llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) {
uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size());
return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None);
}
return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None);
}
callbacks_t callbacks;
callbacks_t callbacks;
};
WasmModule::WasmModule(
const uint8_t *object_start,
size_t object_size,
callbacks_t callbacks
) : memory_manager(std::unique_ptr<MemoryManager>(new MemoryManager(callbacks)))
{
WasmModule::WasmModule(const uint8_t *object_start, size_t object_size,
callbacks_t callbacks)
: memory_manager(
std::unique_ptr<MemoryManager>(new MemoryManager(callbacks))) {
if (auto created_object_file = llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
llvm::StringRef((const char *)object_start, object_size), "object"
))) {
object_file = cantFail(std::move(created_object_file));
SymbolLookup symbol_resolver(callbacks);
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
if (auto created_object_file =
llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
llvm::StringRef((const char *)object_start, object_size),
"object"))) {
object_file = cantFail(std::move(created_object_file));
SymbolLookup symbol_resolver(callbacks);
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(
new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
runtime_dyld->setProcessAllSections(true);
runtime_dyld->setProcessAllSections(true);
runtime_dyld->loadObject(*object_file);
runtime_dyld->finalizeWithMemoryManagerLocking();
runtime_dyld->loadObject(*object_file);
runtime_dyld->finalizeWithMemoryManagerLocking();
if (runtime_dyld->hasError()) {
_init_failed = true;
return;
}
} else {
_init_failed = true;
if (runtime_dyld->hasError()) {
_init_failed = true;
return;
}
} else {
_init_failed = true;
}
}
void* WasmModule::get_func(llvm::StringRef name) const {
auto symbol = runtime_dyld->getSymbol(name);
return (void*)symbol.getAddress();
}
void *WasmModule::get_func(llvm::StringRef name) const {
auto symbol = runtime_dyld->getSymbol(name);
return (void *)symbol.getAddress();
}

View File

@ -1,236 +1,220 @@
#include <cstddef>
#include <cstdint>
#include <llvm/ExecutionEngine/RuntimeDyld.h>
#include <exception>
#include <iostream>
#include <sstream>
#include <exception>
typedef enum
{
PROTECT_NONE,
PROTECT_READ,
PROTECT_READ_WRITE,
PROTECT_READ_EXECUTE,
#include <llvm/ExecutionEngine/RuntimeDyld.h>
typedef enum {
PROTECT_NONE,
PROTECT_READ,
PROTECT_READ_WRITE,
PROTECT_READ_EXECUTE,
} mem_protect_t;
typedef enum
{
RESULT_OK,
RESULT_ALLOCATE_FAILURE,
RESULT_PROTECT_FAILURE,
RESULT_DEALLOC_FAILURE,
RESULT_OBJECT_LOAD_FAILURE,
typedef enum {
RESULT_OK,
RESULT_ALLOCATE_FAILURE,
RESULT_PROTECT_FAILURE,
RESULT_DEALLOC_FAILURE,
RESULT_OBJECT_LOAD_FAILURE,
} result_t;
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t **ptr_out, size_t *size_out);
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size, mem_protect_t protect);
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect,
uint8_t **ptr_out, size_t *size_out);
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size,
mem_protect_t protect);
typedef result_t (*dealloc_memory_t)(uint8_t *ptr, size_t size);
typedef uintptr_t (*lookup_vm_symbol_t)(const char *name_ptr, size_t length);
typedef void (*fde_visitor_t)(uint8_t *fde);
typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size, fde_visitor_t visitor);
typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size,
fde_visitor_t visitor);
typedef void (*trampoline_t)(void *, void *, void *, void *);
typedef struct
{
/* Memory management. */
alloc_memory_t alloc_memory;
protect_memory_t protect_memory;
dealloc_memory_t dealloc_memory;
typedef struct {
/* Memory management. */
alloc_memory_t alloc_memory;
protect_memory_t protect_memory;
dealloc_memory_t dealloc_memory;
lookup_vm_symbol_t lookup_vm_symbol;
lookup_vm_symbol_t lookup_vm_symbol;
visit_fde_t visit_fde;
visit_fde_t visit_fde;
} callbacks_t;
typedef struct
{
size_t data, vtable;
typedef struct {
size_t data, vtable;
} box_any_t;
struct WasmException
{
public:
virtual std::string description() const noexcept = 0;
struct WasmException {
public:
virtual std::string description() const noexcept = 0;
};
struct UncatchableException : WasmException
{
public:
virtual std::string description() const noexcept override
{
return "Uncatchable exception";
}
struct UncatchableException : WasmException {
public:
virtual std::string description() const noexcept override {
return "Uncatchable exception";
}
};
struct UserException : UncatchableException
{
public:
UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {}
struct UserException : UncatchableException {
public:
UserException(size_t data, size_t vtable) : error_data({data, vtable}) {}
virtual std::string description() const noexcept override
{
return "user exception";
}
virtual std::string description() const noexcept override {
return "user exception";
}
// The parts of a `Box<dyn Any>`.
box_any_t error_data;
// The parts of a `Box<dyn Any>`.
box_any_t error_data;
};
struct WasmTrap : UncatchableException
{
public:
enum Type
{
Unreachable = 0,
IncorrectCallIndirectSignature = 1,
MemoryOutOfBounds = 2,
CallIndirectOOB = 3,
IllegalArithmetic = 4,
Unknown,
};
struct BreakpointException : UncatchableException {
public:
BreakpointException(uintptr_t callback) : callback(callback) {}
WasmTrap(Type type) : type(type) {}
virtual std::string description() const noexcept override {
return "breakpoint exception";
}
virtual std::string description() const noexcept override
{
std::ostringstream ss;
ss
<< "WebAssembly trap:" << '\n'
<< " - type: " << type << '\n';
return ss.str();
}
Type type;
private:
friend std::ostream &operator<<(std::ostream &out, const Type &ty)
{
switch (ty)
{
case Type::Unreachable:
out << "unreachable";
break;
case Type::IncorrectCallIndirectSignature:
out << "incorrect call_indirect signature";
break;
case Type::MemoryOutOfBounds:
out << "memory access out-of-bounds";
break;
case Type::CallIndirectOOB:
out << "call_indirect out-of-bounds";
break;
case Type::IllegalArithmetic:
out << "illegal arithmetic operation";
break;
case Type::Unknown:
default:
out << "unknown";
break;
}
return out;
}
uintptr_t callback;
};
struct CatchableException : WasmException
{
public:
CatchableException(uint32_t type_id, uint32_t value_num) : type_id(type_id), value_num(value_num) {}
struct WasmTrap : UncatchableException {
public:
enum Type {
Unreachable = 0,
IncorrectCallIndirectSignature = 1,
MemoryOutOfBounds = 2,
CallIndirectOOB = 3,
IllegalArithmetic = 4,
Unknown,
};
virtual std::string description() const noexcept override
{
return "catchable exception";
WasmTrap(Type type) : type(type) {}
virtual std::string description() const noexcept override {
std::ostringstream ss;
ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n';
return ss.str();
}
Type type;
private:
friend std::ostream &operator<<(std::ostream &out, const Type &ty) {
switch (ty) {
case Type::Unreachable:
out << "unreachable";
break;
case Type::IncorrectCallIndirectSignature:
out << "incorrect call_indirect signature";
break;
case Type::MemoryOutOfBounds:
out << "memory access out-of-bounds";
break;
case Type::CallIndirectOOB:
out << "call_indirect out-of-bounds";
break;
case Type::IllegalArithmetic:
out << "illegal arithmetic operation";
break;
case Type::Unknown:
default:
out << "unknown";
break;
}
uint32_t type_id, value_num;
uint64_t values[1];
return out;
}
};
struct WasmModule
{
public:
WasmModule(
const uint8_t *object_start,
size_t object_size,
callbacks_t callbacks);
struct CatchableException : WasmException {
public:
CatchableException(uint32_t type_id, uint32_t value_num)
: type_id(type_id), value_num(value_num) {}
void *get_func(llvm::StringRef name) const;
virtual std::string description() const noexcept override {
return "catchable exception";
}
bool _init_failed = false;
private:
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
std::unique_ptr<llvm::object::ObjectFile> object_file;
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
uint32_t type_id, value_num;
uint64_t values[1];
};
extern "C"
{
result_t module_load(const uint8_t *mem_ptr, size_t mem_size, callbacks_t callbacks, WasmModule **module_out)
{
*module_out = new WasmModule(mem_ptr, mem_size, callbacks);
struct WasmModule {
public:
WasmModule(const uint8_t *object_start, size_t object_size,
callbacks_t callbacks);
if ((*module_out)->_init_failed) {
return RESULT_OBJECT_LOAD_FAILURE;
}
void *get_func(llvm::StringRef name) const;
return RESULT_OK;
}
bool _init_failed = false;
[[noreturn]] void throw_trap(WasmTrap::Type ty) {
throw WasmTrap(ty);
}
private:
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> memory_manager;
std::unique_ptr<llvm::object::ObjectFile> object_file;
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
};
void module_delete(WasmModule *module)
{
delete module;
}
extern "C" {
void callback_trampoline(void *, void *);
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
// side.
[[noreturn]] void throw_any(size_t data, size_t vtable) {
throw UserException(data, vtable);
}
result_t module_load(const uint8_t *mem_ptr, size_t mem_size,
callbacks_t callbacks, WasmModule **module_out) {
*module_out = new WasmModule(mem_ptr, mem_size, callbacks);
bool invoke_trampoline(
trampoline_t trampoline,
void *ctx,
void *func,
void *params,
void *results,
WasmTrap::Type *trap_out,
box_any_t *user_error,
void *invoke_env) throw()
{
try
{
trampoline(ctx, func, params, results);
return true;
}
catch (const WasmTrap &e)
{
*trap_out = e.type;
return false;
}
catch (const UserException &e)
{
*user_error = e.error_data;
return false;
}
catch (const WasmException &e)
{
*trap_out = WasmTrap::Type::Unknown;
return false;
}
catch (...)
{
*trap_out = WasmTrap::Type::Unknown;
return false;
}
}
if ((*module_out)->_init_failed) {
return RESULT_OBJECT_LOAD_FAILURE;
}
void *get_func_symbol(WasmModule *module, const char *name)
{
return module->get_func(llvm::StringRef(name));
}
}
return RESULT_OK;
}
[[noreturn]] void throw_trap(WasmTrap::Type ty) { throw WasmTrap(ty); }
void module_delete(WasmModule *module) { delete module; }
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
// side.
[[noreturn]] void throw_any(size_t data, size_t vtable) {
throw UserException(data, vtable);
}
// Throw a pointer that's assumed to be codegen::BreakpointHandler on the
// rust side.
[[noreturn]] void throw_breakpoint(uintptr_t callback) {
throw BreakpointException(callback);
}
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
void *params, void *results, WasmTrap::Type *trap_out,
box_any_t *user_error, void *invoke_env) noexcept {
try {
trampoline(ctx, func, params, results);
return true;
} catch (const WasmTrap &e) {
*trap_out = e.type;
return false;
} catch (const UserException &e) {
*user_error = e.error_data;
return false;
} catch (const BreakpointException &e) {
callback_trampoline(user_error, (void *)e.callback);
return false;
} catch (const WasmException &e) {
*trap_out = WasmTrap::Type::Unknown;
return false;
} catch (...) {
*trap_out = WasmTrap::Type::Unknown;
return false;
}
}
void *get_func_symbol(WasmModule *module, const char *name) {
return module->get_func(llvm::StringRef(name));
}
}

View File

@ -1,17 +1,17 @@
use crate::intrinsics::Intrinsics;
use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect};
use inkwell::{
memory_buffer::MemoryBuffer,
module::Module,
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
OptimizationLevel,
};
use libc::{
c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ,
PROT_WRITE,
};
use libc::c_char;
use std::{
any::Any,
ffi::{c_void, CString},
fs::File,
io::Write,
mem,
ops::Deref,
ptr::{self, NonNull},
@ -31,42 +31,6 @@ use wasmer_runtime_core::{
vm, vmcalls,
};
#[repr(C)]
struct LLVMModule {
_private: [u8; 0],
}
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(C)]
enum MemProtect {
NONE,
READ,
READ_WRITE,
READ_EXECUTE,
}
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(C)]
enum LLVMResult {
OK,
ALLOCATE_FAILURE,
PROTECT_FAILURE,
DEALLOC_FAILURE,
OBJECT_LOAD_FAILURE,
}
#[repr(C)]
struct Callbacks {
alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult,
protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult,
dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult,
lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func,
visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)),
}
extern "C" {
fn module_load(
mem_ptr: *const u8,
@ -77,7 +41,8 @@ extern "C" {
fn module_delete(module: *mut LLVMModule);
fn get_func_symbol(module: *mut LLVMModule, name: *const c_char) -> *const vm::Func;
fn throw_trap(ty: i32);
fn throw_trap(ty: i32) -> !;
fn throw_breakpoint(ty: i64) -> !;
/// This should be the same as spliting up the fat pointer into two arguments,
/// but this is cleaner, I think?
@ -99,69 +64,21 @@ extern "C" {
}
fn get_callbacks() -> Callbacks {
fn round_up_to_page_size(size: usize) -> usize {
(size + (4096 - 1)) & !(4096 - 1)
}
extern "C" fn alloc_memory(
size: usize,
protect: MemProtect,
ptr_out: &mut *mut u8,
size_out: &mut usize,
) -> LLVMResult {
let size = round_up_to_page_size(size);
let ptr = unsafe {
mmap(
ptr::null_mut(),
size,
match protect {
MemProtect::NONE => PROT_NONE,
MemProtect::READ => PROT_READ,
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
},
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if ptr as isize == -1 {
return LLVMResult::ALLOCATE_FAILURE;
}
*ptr_out = ptr as _;
*size_out = size;
LLVMResult::OK
unsafe { crate::platform::alloc_memory(size, protect, ptr_out, size_out) }
}
extern "C" fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
let res = unsafe {
mprotect(
ptr as _,
round_up_to_page_size(size),
match protect {
MemProtect::NONE => PROT_NONE,
MemProtect::READ => PROT_READ,
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
},
)
};
if res == 0 {
LLVMResult::OK
} else {
LLVMResult::PROTECT_FAILURE
}
unsafe { crate::platform::protect_memory(ptr, size, protect) }
}
extern "C" fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
let res = unsafe { munmap(ptr as _, round_up_to_page_size(size)) };
if res == 0 {
LLVMResult::OK
} else {
LLVMResult::DEALLOC_FAILURE
}
unsafe { crate::platform::dealloc_memory(ptr, size) }
}
extern "C" fn lookup_vm_symbol(name_ptr: *const c_char, length: usize) -> *const vm::Func {
@ -188,7 +105,13 @@ fn get_callbacks() -> Callbacks {
fn_name!("vm.memory.grow.static.local") => vmcalls::local_static_memory_grow as _,
fn_name!("vm.memory.size.static.local") => vmcalls::local_static_memory_size as _,
fn_name!("vm.memory.grow.dynamic.import") => vmcalls::imported_dynamic_memory_grow as _,
fn_name!("vm.memory.size.dynamic.import") => vmcalls::imported_dynamic_memory_size as _,
fn_name!("vm.memory.grow.static.import") => vmcalls::imported_static_memory_grow as _,
fn_name!("vm.memory.size.static.import") => vmcalls::imported_static_memory_size as _,
fn_name!("vm.exception.trap") => throw_trap as _,
fn_name!("vm.breakpoint") => throw_breakpoint as _,
_ => ptr::null(),
}
@ -261,6 +184,14 @@ impl LLVMBackend {
.unwrap();
let mem_buf_slice = memory_buffer.as_slice();
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.obj_file } {
let mut file = File::create(path).unwrap();
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
}
let callbacks = get_callbacks();
let mut module: *mut LLVMModule = ptr::null_mut();

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,15 @@
use hashbrown::HashMap;
use inkwell::{
attributes::{Attribute, AttributeLoc},
builder::Builder,
context::Context,
module::Module,
types::{BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VoidType},
types::{
BasicType, FloatType, FunctionType, IntType, PointerType, StructType, VectorType, VoidType,
},
values::{BasicValue, BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue},
AddressSpace,
};
use std::collections::HashMap;
use std::marker::PhantomData;
use wasmer_runtime_core::{
memory::MemoryType,
@ -16,7 +19,7 @@ use wasmer_runtime_core::{
GlobalIndex, ImportedFuncIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
TableIndex, Type,
},
vm::Ctx,
vm::{Ctx, INTERNALS_SIZE},
};
fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
@ -25,6 +28,7 @@ fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
Type::I64 => intrinsics.i64_ptr_ty,
Type::F32 => intrinsics.f32_ptr_ty,
Type::F64 => intrinsics.f64_ptr_ty,
Type::V128 => intrinsics.i128_ptr_ty,
}
}
@ -40,12 +44,18 @@ pub struct Intrinsics {
pub sqrt_f32: FunctionValue,
pub sqrt_f64: FunctionValue,
pub sqrt_f32x4: FunctionValue,
pub sqrt_f64x2: FunctionValue,
pub minimum_f32: FunctionValue,
pub minimum_f64: FunctionValue,
pub minimum_f32x4: FunctionValue,
pub minimum_f64x2: FunctionValue,
pub maximum_f32: FunctionValue,
pub maximum_f64: FunctionValue,
pub maximum_f32x4: FunctionValue,
pub maximum_f64x2: FunctionValue,
pub ceil_f32: FunctionValue,
pub ceil_f64: FunctionValue,
@ -61,10 +71,22 @@ pub struct Intrinsics {
pub fabs_f32: FunctionValue,
pub fabs_f64: FunctionValue,
pub fabs_f32x4: FunctionValue,
pub fabs_f64x2: FunctionValue,
pub copysign_f32: FunctionValue,
pub copysign_f64: FunctionValue,
pub sadd_sat_i8x16: FunctionValue,
pub sadd_sat_i16x8: FunctionValue,
pub uadd_sat_i8x16: FunctionValue,
pub uadd_sat_i16x8: FunctionValue,
pub ssub_sat_i8x16: FunctionValue,
pub ssub_sat_i16x8: FunctionValue,
pub usub_sat_i8x16: FunctionValue,
pub usub_sat_i16x8: FunctionValue,
pub expect_i1: FunctionValue,
pub trap: FunctionValue,
@ -74,21 +96,33 @@ pub struct Intrinsics {
pub i16_ty: IntType,
pub i32_ty: IntType,
pub i64_ty: IntType,
pub i128_ty: IntType,
pub f32_ty: FloatType,
pub f64_ty: FloatType,
pub i1x128_ty: VectorType,
pub i8x16_ty: VectorType,
pub i16x8_ty: VectorType,
pub i32x4_ty: VectorType,
pub i64x2_ty: VectorType,
pub f32x4_ty: VectorType,
pub f64x2_ty: VectorType,
pub i8_ptr_ty: PointerType,
pub i16_ptr_ty: PointerType,
pub i32_ptr_ty: PointerType,
pub i64_ptr_ty: PointerType,
pub i128_ptr_ty: PointerType,
pub f32_ptr_ty: PointerType,
pub f64_ptr_ty: PointerType,
pub anyfunc_ty: StructType,
pub i1_zero: IntValue,
pub i8_zero: IntValue,
pub i32_zero: IntValue,
pub i64_zero: IntValue,
pub i128_zero: IntValue,
pub f32_zero: FloatValue,
pub f64_zero: FloatValue,
@ -97,6 +131,7 @@ pub struct Intrinsics {
pub trap_call_indirect_oob: BasicValueEnum,
pub trap_memory_oob: BasicValueEnum,
pub trap_illegal_arithmetic: BasicValueEnum,
pub trap_misaligned_atomic: BasicValueEnum,
// VM intrinsics.
pub memory_grow_dynamic_local: FunctionValue,
@ -114,6 +149,7 @@ pub struct Intrinsics {
pub memory_size_shared_import: FunctionValue,
pub throw_trap: FunctionValue,
pub throw_breakpoint: FunctionValue,
pub ctx_ptr_ty: PointerType,
}
@ -126,19 +162,31 @@ impl Intrinsics {
let i16_ty = context.i16_type();
let i32_ty = context.i32_type();
let i64_ty = context.i64_type();
let i128_ty = context.i128_type();
let f32_ty = context.f32_type();
let f64_ty = context.f64_type();
let i1x128_ty = i1_ty.vec_type(128);
let i8x16_ty = i8_ty.vec_type(16);
let i16x8_ty = i16_ty.vec_type(8);
let i32x4_ty = i32_ty.vec_type(4);
let i64x2_ty = i64_ty.vec_type(2);
let f32x4_ty = f32_ty.vec_type(4);
let f64x2_ty = f64_ty.vec_type(2);
let i8_ptr_ty = i8_ty.ptr_type(AddressSpace::Generic);
let i16_ptr_ty = i16_ty.ptr_type(AddressSpace::Generic);
let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::Generic);
let i64_ptr_ty = i64_ty.ptr_type(AddressSpace::Generic);
let i128_ptr_ty = i128_ty.ptr_type(AddressSpace::Generic);
let f32_ptr_ty = f32_ty.ptr_type(AddressSpace::Generic);
let f64_ptr_ty = f64_ty.ptr_type(AddressSpace::Generic);
let i1_zero = i1_ty.const_int(0, false);
let i8_zero = i8_ty.const_int(0, false);
let i32_zero = i32_ty.const_int(0, false);
let i64_zero = i64_ty.const_int(0, false);
let i128_zero = i128_ty.const_int(0, false);
let f32_zero = f32_ty.const_float(0.0);
let f64_zero = f64_ty.const_float(0.0);
@ -147,6 +195,10 @@ impl Intrinsics {
let i64_ty_basic = i64_ty.as_basic_type_enum();
let f32_ty_basic = f32_ty.as_basic_type_enum();
let f64_ty_basic = f64_ty.as_basic_type_enum();
let i8x16_ty_basic = i8x16_ty.as_basic_type_enum();
let i16x8_ty_basic = i16x8_ty.as_basic_type_enum();
let f32x4_ty_basic = f32x4_ty.as_basic_type_enum();
let f64x2_ty_basic = f64x2_ty.as_basic_type_enum();
let i8_ptr_ty_basic = i8_ptr_ty.as_basic_type_enum();
let ctx_ty = context.opaque_struct_type("ctx");
@ -233,6 +285,9 @@ impl Intrinsics {
false,
);
let ret_i8x16_take_i8x16_i8x16 = i8x16_ty.fn_type(&[i8x16_ty_basic, i8x16_ty_basic], false);
let ret_i16x8_take_i16x8_i16x8 = i16x8_ty.fn_type(&[i16x8_ty_basic, i16x8_ty_basic], false);
let ret_i32_take_i32_i1 = i32_ty.fn_type(&[i32_ty_basic, i1_ty_basic], false);
let ret_i64_take_i64_i1 = i64_ty.fn_type(&[i64_ty_basic, i1_ty_basic], false);
@ -241,9 +296,13 @@ impl Intrinsics {
let ret_f32_take_f32 = f32_ty.fn_type(&[f32_ty_basic], false);
let ret_f64_take_f64 = f64_ty.fn_type(&[f64_ty_basic], false);
let ret_f32x4_take_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic], false);
let ret_f64x2_take_f64x2 = f64x2_ty.fn_type(&[f64x2_ty_basic], false);
let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false);
let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false);
let ret_f32x4_take_f32x4_f32x4 = f32x4_ty.fn_type(&[f32x4_ty_basic, f32x4_ty_basic], false);
let ret_f64x2_take_f64x2_f64x2 = f64x2_ty.fn_type(&[f64x2_ty_basic, f64x2_ty_basic], false);
let ret_i32_take_ctx_i32_i32 = i32_ty.fn_type(
&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic],
@ -253,8 +312,7 @@ impl Intrinsics {
i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false);
let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false);
Self {
let intrinsics = Self {
ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None),
ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None),
@ -266,12 +324,34 @@ impl Intrinsics {
sqrt_f32: module.add_function("llvm.sqrt.f32", ret_f32_take_f32, None),
sqrt_f64: module.add_function("llvm.sqrt.f64", ret_f64_take_f64, None),
sqrt_f32x4: module.add_function("llvm.sqrt.v4f32", ret_f32x4_take_f32x4, None),
sqrt_f64x2: module.add_function("llvm.sqrt.v2f64", ret_f64x2_take_f64x2, None),
minimum_f32: module.add_function("llvm.minnum.f32", ret_f32_take_f32_f32, None),
minimum_f64: module.add_function("llvm.minnum.f64", ret_f64_take_f64_f64, None),
minimum_f32x4: module.add_function(
"llvm.minnum.v4f32",
ret_f32x4_take_f32x4_f32x4,
None,
),
minimum_f64x2: module.add_function(
"llvm.minnum.v2f64",
ret_f64x2_take_f64x2_f64x2,
None,
),
maximum_f32: module.add_function("llvm.maxnum.f32", ret_f32_take_f32_f32, None),
maximum_f64: module.add_function("llvm.maxnum.f64", ret_f64_take_f64_f64, None),
maximum_f32x4: module.add_function(
"llvm.maxnum.v4f32",
ret_f32x4_take_f32x4_f32x4,
None,
),
maximum_f64x2: module.add_function(
"llvm.maxnum.v2f64",
ret_f64x2_take_f64x2_f64x2,
None,
),
ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None),
ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None),
@ -287,10 +367,54 @@ impl Intrinsics {
fabs_f32: module.add_function("llvm.fabs.f32", ret_f32_take_f32, None),
fabs_f64: module.add_function("llvm.fabs.f64", ret_f64_take_f64, None),
fabs_f32x4: module.add_function("llvm.fabs.v4f32", ret_f32x4_take_f32x4, None),
fabs_f64x2: module.add_function("llvm.fabs.v2f64", ret_f64x2_take_f64x2, None),
copysign_f32: module.add_function("llvm.copysign.f32", ret_f32_take_f32_f32, None),
copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None),
sadd_sat_i8x16: module.add_function(
"llvm.sadd.sat.v16i8",
ret_i8x16_take_i8x16_i8x16,
None,
),
sadd_sat_i16x8: module.add_function(
"llvm.sadd.sat.v8i16",
ret_i16x8_take_i16x8_i16x8,
None,
),
uadd_sat_i8x16: module.add_function(
"llvm.uadd.sat.v16i8",
ret_i8x16_take_i8x16_i8x16,
None,
),
uadd_sat_i16x8: module.add_function(
"llvm.uadd.sat.v8i16",
ret_i16x8_take_i16x8_i16x8,
None,
),
ssub_sat_i8x16: module.add_function(
"llvm.ssub.sat.v16i8",
ret_i8x16_take_i8x16_i8x16,
None,
),
ssub_sat_i16x8: module.add_function(
"llvm.ssub.sat.v8i16",
ret_i16x8_take_i16x8_i16x8,
None,
),
usub_sat_i8x16: module.add_function(
"llvm.usub.sat.v16i8",
ret_i8x16_take_i8x16_i8x16,
None,
),
usub_sat_i16x8: module.add_function(
"llvm.usub.sat.v8i16",
ret_i16x8_take_i16x8_i16x8,
None,
),
expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None),
trap: module.add_function("llvm.trap", void_ty.fn_type(&[], false), None),
@ -300,21 +424,33 @@ impl Intrinsics {
i16_ty,
i32_ty,
i64_ty,
i128_ty,
f32_ty,
f64_ty,
i1x128_ty,
i8x16_ty,
i16x8_ty,
i32x4_ty,
i64x2_ty,
f32x4_ty,
f64x2_ty,
i8_ptr_ty,
i16_ptr_ty,
i32_ptr_ty,
i64_ptr_ty,
i128_ptr_ty,
f32_ptr_ty,
f64_ptr_ty,
anyfunc_ty,
i1_zero,
i8_zero,
i32_zero,
i64_zero,
i128_zero,
f32_zero,
f64_zero,
@ -323,6 +459,7 @@ impl Intrinsics {
trap_call_indirect_oob: i32_ty.const_int(3, false).as_basic_value_enum(),
trap_memory_oob: i32_ty.const_int(2, false).as_basic_value_enum(),
trap_illegal_arithmetic: i32_ty.const_int(4, false).as_basic_value_enum(),
trap_misaligned_atomic: i32_ty.const_int(5, false).as_basic_value_enum(),
// VM intrinsics.
memory_grow_dynamic_local: module.add_function(
@ -391,8 +528,45 @@ impl Intrinsics {
void_ty.fn_type(&[i32_ty_basic], false),
None,
),
throw_breakpoint: module.add_function(
"vm.breakpoint",
void_ty.fn_type(&[i64_ty_basic], false),
None,
),
ctx_ptr_ty,
}
};
let readonly =
context.create_enum_attribute(Attribute::get_named_enum_kind_id("readonly"), 0);
intrinsics
.memory_size_dynamic_local
.add_attribute(AttributeLoc::Function, readonly);
intrinsics
.memory_size_static_local
.add_attribute(AttributeLoc::Function, readonly);
intrinsics
.memory_size_shared_local
.add_attribute(AttributeLoc::Function, readonly);
intrinsics
.memory_size_dynamic_import
.add_attribute(AttributeLoc::Function, readonly);
intrinsics
.memory_size_static_import
.add_attribute(AttributeLoc::Function, readonly);
intrinsics
.memory_size_shared_import
.add_attribute(AttributeLoc::Function, readonly);
let noreturn =
context.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0);
intrinsics
.throw_trap
.add_attribute(AttributeLoc::Function, noreturn);
intrinsics
.throw_breakpoint
.add_attribute(AttributeLoc::Function, noreturn);
intrinsics
}
}
@ -808,4 +982,31 @@ impl<'a> CtxType<'a> {
(imported_func_cache.func_ptr, imported_func_cache.ctx_ptr)
}
pub fn internal_field(
&mut self,
index: usize,
intrinsics: &Intrinsics,
builder: &Builder,
) -> PointerValue {
assert!(index < INTERNALS_SIZE);
let local_internals_ptr_ptr = unsafe {
builder.build_struct_gep(
self.ctx_ptr_value,
offset_to_index(Ctx::offset_internals()),
"local_internals_ptr_ptr",
)
};
let local_internals_ptr = builder
.build_load(local_internals_ptr_ptr, "local_internals_ptr")
.into_pointer_value();
unsafe {
builder.build_in_bounds_gep(
local_internals_ptr,
&[intrinsics.i32_ty.const_int(index as u64, false)],
"local_internal_field_ptr",
)
}
}
}

View File

@ -1,4 +1,12 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
#![cfg_attr(nightly, feature(unwind_attributes))]
mod backend;
@ -7,8 +15,14 @@ mod intrinsics;
mod platform;
mod read_info;
mod state;
mod structs;
mod trampolines;
use std::path::PathBuf;
pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator;
pub use code::LLVMModuleCodeGenerator as ModuleCodeGenerator;
use wasmer_runtime_core::codegen::SimpleStreamingCompilerGen;
pub type LLVMCompiler = SimpleStreamingCompilerGen<
@ -17,3 +31,22 @@ pub type LLVMCompiler = SimpleStreamingCompilerGen<
backend::LLVMBackend,
code::CodegenError,
>;
#[derive(Debug, Clone)]
/// LLVM backend flags.
pub struct LLVMOptions {
/// Emit LLVM IR before optimization pipeline.
pub pre_opt_ir: Option<PathBuf>,
/// Emit LLVM IR after optimization pipeline.
pub post_opt_ir: Option<PathBuf>,
/// Emit LLVM generated native code object file.
pub obj_file: Option<PathBuf>,
}
pub static mut GLOBAL_OPTIONS: LLVMOptions = LLVMOptions {
pre_opt_ir: None,
post_opt_ir: None,
obj_file: None,
};

View File

@ -0,0 +1,3 @@
pub fn round_up_to_page_size(size: usize) -> usize {
(size + (4096 - 1)) & !(4096 - 1)
}

View File

@ -1,7 +1,14 @@
mod common;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use self::unix::*;
#[cfg(target_family = "windows")]
compile_error!("windows not yet supported for the llvm-based compiler backend");
mod win;
#[cfg(target_family = "windows")]
pub use self::win::*;
#[cfg(not(any(unix, target_family = "windows")))]
compile_error!("Your system is not yet supported for the llvm-based compiler backend");

View File

@ -1,5 +1,11 @@
use libc::{c_void, siginfo_t};
use super::common::round_up_to_page_size;
use crate::structs::{LLVMResult, MemProtect};
use libc::{
c_void, mmap, mprotect, munmap, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE,
PROT_READ, PROT_WRITE,
};
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV};
use std::ptr;
/// `__register_frame` and `__deregister_frame` on macos take a single fde as an
/// argument, so we need to parse the fde table here.
@ -68,3 +74,60 @@ extern "C" fn signal_trap_handler(
throw_trap(2);
}
}
pub unsafe fn alloc_memory(
size: usize,
protect: MemProtect,
ptr_out: &mut *mut u8,
size_out: &mut usize,
) -> LLVMResult {
let size = round_up_to_page_size(size);
let ptr = mmap(
ptr::null_mut(),
size,
match protect {
MemProtect::NONE => PROT_NONE,
MemProtect::READ => PROT_READ,
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
},
MAP_PRIVATE | MAP_ANON,
-1,
0,
);
if ptr as isize == -1 {
return LLVMResult::ALLOCATE_FAILURE;
}
*ptr_out = ptr as _;
*size_out = size;
LLVMResult::OK
}
pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
let res = mprotect(
ptr as _,
round_up_to_page_size(size),
match protect {
MemProtect::NONE => PROT_NONE,
MemProtect::READ => PROT_READ,
MemProtect::READ_WRITE => PROT_READ | PROT_WRITE,
MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC,
},
);
if res == 0 {
LLVMResult::OK
} else {
LLVMResult::PROTECT_FAILURE
}
}
pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
let res = munmap(ptr as _, round_up_to_page_size(size));
if res == 0 {
LLVMResult::OK
} else {
LLVMResult::DEALLOC_FAILURE
}
}

View File

@ -0,0 +1,80 @@
use super::common::round_up_to_page_size;
use crate::structs::{LLVMResult, MemProtect};
use std::ptr;
use winapi::um::memoryapi::{VirtualAlloc, VirtualFree};
use winapi::um::winnt::{
MEM_COMMIT, MEM_DECOMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_NOACCESS, PAGE_READONLY,
PAGE_READWRITE,
};
pub unsafe fn visit_fde(_addr: *mut u8, _size: usize, _visitor: extern "C" fn(*mut u8)) {
// Do nothing on Windows
}
pub unsafe fn install_signal_handler() {
// Do nothing on Windows
}
pub unsafe fn alloc_memory(
size: usize,
protect: MemProtect,
ptr_out: &mut *mut u8,
size_out: &mut usize,
) -> LLVMResult {
let size = round_up_to_page_size(size);
let flags = if protect == MemProtect::NONE {
MEM_RESERVE
} else {
MEM_RESERVE | MEM_COMMIT
};
let ptr = VirtualAlloc(
ptr::null_mut(),
size,
flags,
memprotect_to_protect_const(protect),
);
if ptr.is_null() {
return LLVMResult::ALLOCATE_FAILURE;
}
*ptr_out = ptr as _;
*size_out = size;
LLVMResult::OK
}
pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult {
let size = round_up_to_page_size(size);
let ptr = VirtualAlloc(
ptr as _,
size,
MEM_COMMIT,
memprotect_to_protect_const(protect),
);
if ptr.is_null() {
LLVMResult::PROTECT_FAILURE
} else {
LLVMResult::OK
}
}
pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult {
let success = VirtualFree(ptr as _, size, MEM_DECOMMIT);
// If the function succeeds, the return value is nonzero.
if success == 1 {
LLVMResult::OK
} else {
LLVMResult::DEALLOC_FAILURE
}
}
fn memprotect_to_protect_const(protect: MemProtect) -> u32 {
match protect {
MemProtect::NONE => PAGE_NOACCESS,
MemProtect::READ => PAGE_READONLY,
MemProtect::READ_WRITE => PAGE_READWRITE,
MemProtect::READ_EXECUTE => PAGE_EXECUTE_READ,
}
}

View File

@ -7,12 +7,7 @@ pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
WpType::I64 => Type::I64,
WpType::F32 => Type::F32,
WpType::F64 => Type::F64,
WpType::V128 => {
return Err(BinaryReaderError {
message: "the wasmer llvm backend does not yet support the simd extension",
offset: -1isize as usize,
});
}
WpType::V128 => Type::V128,
_ => {
return Err(BinaryReaderError {
message: "that type is not supported as a wasmer type",

View File

@ -0,0 +1,39 @@
use libc::c_char;
use wasmer_runtime_core::vm;
#[repr(C)]
pub struct LLVMModule {
_private: [u8; 0],
}
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(C)]
pub enum MemProtect {
NONE,
READ,
READ_WRITE,
READ_EXECUTE,
}
#[allow(non_camel_case_types, dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(C)]
pub enum LLVMResult {
OK,
ALLOCATE_FAILURE,
PROTECT_FAILURE,
DEALLOC_FAILURE,
OBJECT_LOAD_FAILURE,
}
#[repr(C)]
pub struct Callbacks {
pub alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult,
pub protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult,
pub dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult,
pub lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func,
pub visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)),
}

View File

@ -72,12 +72,14 @@ fn generate_trampoline(
Type::I64 => intrinsics.i64_ptr_ty,
Type::F32 => intrinsics.f32_ptr_ty,
Type::F64 => intrinsics.f64_ptr_ty,
Type::V128 => intrinsics.i128_ptr_ty,
};
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
args_vec.push(vmctx_ptr);
for (i, param_ty) in func_sig.params().iter().enumerate() {
let mut i = 0;
for param_ty in func_sig.params().iter() {
let index = intrinsics.i32_ty.const_int(i as _, false);
let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") };
@ -88,6 +90,10 @@ fn generate_trampoline(
let arg = builder.build_load(typed_item_pointer, "arg");
args_vec.push(arg);
i = i + 1;
if *param_ty == Type::V128 {
i = i + 1;
}
}
let call_site = builder.build_call(func_ptr, &args_vec, "call");

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-middleware-common"
version = "0.5.6"
version = "0.6.0"
repository = "https://github.com/wasmerio/wasmer"
description = "Wasmer runtime common middlewares"
license = "MIT"
@ -9,12 +9,12 @@ edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.5.6" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.5.6", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.5.6", optional = true }
wasmer-clif-backend = { path = "../clif-backend", version = "0.6.0" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.6.0", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
[dev-dependencies]
wabt = "0.7.4"
wabt = "0.9.1"
criterion = "0.2"
[features]

View File

@ -135,16 +135,15 @@ static WAT_GAS: &'static str = r#"
#[cfg(feature = "llvm")]
fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
use wasmer_llvm_backend::ModuleCodeGenerator;
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
if metering {
chain.push(Metering::new(limit));
}
chain
});
let c: StreamingCompiler<ModuleCodeGenerator, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
if metering {
chain.push(Metering::new(limit));
}
chain
});
c
}
@ -164,15 +163,11 @@ fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
panic!("compiler not specified, activate a compiler via features");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
compile_error!("compiler not specified, activate a compiler via features");
#[cfg(feature = "clif")]
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
panic!("cranelift does not implement metering");
compile_error!("cranelift does not implement metering");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}

View File

@ -1,4 +1,11 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
pub mod call_trace;
pub mod metering;

View File

@ -129,29 +129,27 @@ pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) {
ctx.set_internal(&INTERNAL_FIELD, value);
}
#[cfg(all(test, feature = "singlepass"))]
#[cfg(all(test, any(feature = "singlepass", feature = "llvm")))]
mod tests {
use super::*;
use wabt::wat2wasm;
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func};
#[cfg(feature = "llvm")]
fn get_compiler(limit: u64) -> impl Compiler {
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
chain.push(Metering::new(limit));
chain
});
use wasmer_llvm_backend::ModuleCodeGenerator as LLVMMCG;
let c: StreamingCompiler<LLVMMCG, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
chain.push(Metering::new(limit));
chain
});
c
}
#[cfg(feature = "singlepass")]
fn get_compiler(limit: u64) -> impl Compiler {
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
let c: StreamingCompiler<SinglePassMCG, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
@ -162,15 +160,11 @@ mod tests {
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler(_limit: u64) -> impl Compiler {
panic!("compiler not specified, activate a compiler via features");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
compile_error!("compiler not specified, activate a compiler via features");
#[cfg(feature = "clif")]
fn get_compiler(_limit: u64) -> impl Compiler {
panic!("cranelift does not implement metering");
compile_error!("cranelift does not implement metering");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
@ -253,7 +247,7 @@ mod tests {
// verify it returns the correct value
assert_eq!(value, 7);
// verify is uses the correct number of points
// verify it used the correct number of points
assert_eq!(get_points_used(&instance), 74);
}
@ -282,7 +276,7 @@ mod tests {
_ => unreachable!(),
}
// verify is uses the correct number of points
// verify it used the correct number of points
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
}

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-abi"
version = "0.5.6"
version = "0.6.0"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,12 +8,11 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
libc = "0.2.50"
libc = "0.2.60"
wasmer-runtime-core = { path = "../runtime-core" }
hashbrown = "0.1"
failure = "0.1"
tar = "0.4"
wasmparser = "0.32.1"
wasmparser = "0.35.1"
zstd = "0.4"
# [target.'cfg(unix)'.dependencies.zbox]

View File

@ -1,5 +1,4 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(dead_code, unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#[cfg(not(target_os = "windows"))]
#[macro_use]
extern crate failure;

View File

@ -1,7 +1,7 @@
use crate::vfs::file_like::FileLike;
use crate::vfs::vfs_header::{header_from_bytes, ArchiveType, CompressionType};
use crate::vfs::virtual_file::VirtualFile;
use hashbrown::HashMap;
use std::collections::HashMap;
use std::cell::RefCell;
use std::io;
use std::io::Read;

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-c-api"
version = "0.5.6"
version = "0.6.0"
description = "Wasmer C API library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -12,19 +12,19 @@ readme = "README.md"
crate-type = ["cdylib", "rlib", "staticlib"]
[dependencies]
libc = "0.2"
libc = "0.2.60"
[dependencies.wasmer-runtime]
path = "../runtime"
version = "0.5.6"
version = "0.6.0"
[dependencies.wasmer-runtime-core]
path = "../runtime-core"
version = "0.5.6"
version = "0.6.0"
[features]
debug = ["wasmer-runtime/debug"]
llvm = ["wasmer-runtime/llvm"]
[build-dependencies]
cbindgen = "0.8"
cbindgen = "0.9.0"

View File

@ -8,17 +8,17 @@ use std::ptr;
use std::slice;
thread_local! {
static LAST_ERROR: RefCell<Option<Box<Error>>> = RefCell::new(None);
static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
}
pub(crate) fn update_last_error<E: Error + 'static>(err: E) {
pub fn update_last_error<E: Error + 'static>(err: E) {
LAST_ERROR.with(|prev| {
*prev.borrow_mut() = Some(Box::new(err));
});
}
/// Retrieve the most recent error, clearing it in the process.
pub(crate) fn take_last_error() -> Option<Box<Error>> {
pub(crate) fn take_last_error() -> Option<Box<dyn Error>> {
LAST_ERROR.with(|prev| prev.borrow_mut().take())
}
@ -89,8 +89,8 @@ pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length:
}
#[derive(Debug)]
pub(crate) struct CApiError {
pub(crate) msg: String,
pub struct CApiError {
pub msg: String,
}
impl Display for CApiError {

View File

@ -436,6 +436,7 @@ pub unsafe extern "C" fn wasmer_export_func_call(
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Value::V128(_) => unimplemented!("returning V128 type"),
};
results[0] = ret;
}

View File

@ -10,9 +10,10 @@ use crate::{
};
use libc::c_uint;
use std::{ffi::c_void, ptr, slice, sync::Arc};
use wasmer_runtime::Module;
use wasmer_runtime::{Global, Memory, Module, Table};
use wasmer_runtime_core::{
export::{Context, Export, FuncPointer},
import::ImportObject,
module::ImportName,
types::{FuncSig, Type},
};
@ -25,6 +26,9 @@ pub struct wasmer_import_t {
pub value: wasmer_import_export_value,
}
#[repr(C)]
pub struct wasmer_import_object_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_func_t;
@ -37,6 +41,83 @@ pub struct wasmer_import_descriptor_t;
#[derive(Clone)]
pub struct wasmer_import_descriptors_t;
/// Creates a new empty import object.
/// See also `wasmer_import_object_append`
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object_t {
let import_object = Box::new(ImportObject::new());
Box::into_raw(import_object) as *mut wasmer_import_object_t
}
/// Extends an existing import object with new imports
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_extend(
import_object: *mut wasmer_import_object_t,
imports: *mut wasmer_import_t,
imports_len: c_uint,
) -> wasmer_result_t {
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
let mut extensions: Vec<(String, String, Export)> = Vec::new();
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
for import in imports {
let module_name = slice::from_raw_parts(
import.module_name.bytes,
import.module_name.bytes_len as usize,
);
let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting module name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_name = slice::from_raw_parts(
import.import_name.bytes,
import.import_name.bytes_len as usize,
);
let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting import_name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory;
Export::Memory((&*mem).clone())
}
wasmer_import_export_kind::WASM_FUNCTION => {
let func_export = import.value.func as *mut Export;
(&*func_export).clone()
}
wasmer_import_export_kind::WASM_GLOBAL => {
let global = import.value.global as *mut Global;
Export::Global((&*global).clone())
}
wasmer_import_export_kind::WASM_TABLE => {
let table = import.value.table as *mut Table;
Export::Table((&*table).clone())
}
};
let extension = (module_name.to_string(), import_name.to_string(), export);
extensions.push(extension)
}
import_object.extend(extensions);
return wasmer_result_t::WASMER_OK;
}
/// Gets import descriptors for the given module
///
/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it.
@ -352,6 +433,14 @@ pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) {
}
}
/// Frees memory of the given ImportObject
#[no_mangle]
pub extern "C" fn wasmer_import_object_destroy(import_object: *mut wasmer_import_object_t) {
if !import_object.is_null() {
unsafe { Box::from_raw(import_object as *mut ImportObject) };
}
}
struct NamedImportDescriptor {
module: String,
name: String,

View File

@ -3,15 +3,19 @@
use crate::{
error::{update_last_error, CApiError},
export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports},
import::wasmer_import_t,
import::{wasmer_import_object_t, wasmer_import_t},
memory::wasmer_memory_t,
module::wasmer_module_t,
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
wasmer_result_t,
};
use libc::{c_char, c_int, c_void};
use std::{collections::HashMap, ffi::CStr, slice};
use wasmer_runtime::{Ctx, Global, ImportObject, Instance, Memory, Table, Value};
use wasmer_runtime_core::{export::Export, import::Namespace};
use wasmer_runtime::{Ctx, Global, Instance, Memory, Module, Table, Value};
use wasmer_runtime_core::{
export::Export,
import::{ImportObject, Namespace},
};
#[repr(C)]
pub struct wasmer_instance_t;
@ -108,6 +112,45 @@ pub unsafe extern "C" fn wasmer_instantiate(
wasmer_result_t::WASMER_OK
}
/// Given:
/// * A prepared `wasmer` import-object
/// * A compiled wasmer module
///
/// Instantiates a wasmer instance
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_import_instantiate(
instance: *mut *mut wasmer_instance_t,
module: *const wasmer_module_t,
import_object: *const wasmer_import_object_t,
) -> wasmer_result_t {
let import_object: &ImportObject = &*(import_object as *const ImportObject);
let module: &Module = &*(module as *const Module);
let new_instance: Instance = match module.instantiate(import_object) {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
return wasmer_result_t::WASMER_OK;
}
/// Extracts the instance's context and returns it.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instance_context_get(
instance: *mut wasmer_instance_t,
) -> *const wasmer_instance_context_t {
let instance_ref = &*(instance as *const Instance);
let ctx: *const Ctx = instance_ref.context() as *const _;
ctx as *const wasmer_instance_context_t
}
/// Calls an instances exported function by `name` with the provided parameters.
/// Results are set using the provided `results` pointer.
///
@ -173,6 +216,7 @@ pub unsafe extern "C" fn wasmer_instance_call(
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Value::V128(_) => unimplemented!("calling function with V128 parameter"),
};
results[0] = ret;
}

View File

@ -80,8 +80,13 @@
//!
//! [wasmer_h]: ./wasmer.h
//! [wasmer_hh]: ./wasmer.hh
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
#![deny(
dead_code,
unused_imports,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
extern crate wasmer_runtime;
extern crate wasmer_runtime_core;
@ -118,6 +123,6 @@ pub struct wasmer_limit_option_t {
#[repr(C)]
pub struct wasmer_byte_array {
bytes: *const u8,
bytes_len: u32,
pub bytes: *const u8,
pub bytes_len: u32,
}

View File

@ -51,7 +51,7 @@ impl From<wasmer_value_t> for Value {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64 },
} => Value::F64(F64),
_ => panic!("not implemented"),
_ => unreachable!("unknown WASM type"),
}
}
}
@ -76,6 +76,7 @@ impl From<Value> for wasmer_value_t {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Value::V128(_) => unimplemented!("V128 not supported in C API"),
}
}
}
@ -88,7 +89,7 @@ impl From<Type> for wasmer_value_tag {
Type::I64 => wasmer_value_tag::WASM_I64,
Type::F32 => wasmer_value_tag::WASM_F32,
Type::F64 => wasmer_value_tag::WASM_F64,
_ => panic!("not implemented"),
Type::V128 => unreachable!("V128 not supported in C API"),
}
}
}
@ -101,7 +102,7 @@ impl From<wasmer_value_tag> for Type {
wasmer_value_tag::WASM_I64 => Type::I64,
wasmer_value_tag::WASM_F32 => Type::F32,
wasmer_value_tag::WASM_F64 => Type::F64,
_ => panic!("not implemented"),
_ => unreachable!("unknown WASM type"),
}
}
}
@ -113,6 +114,7 @@ impl From<&wasmer_runtime::wasm::Type> for wasmer_value_tag {
Type::I64 => wasmer_value_tag::WASM_I64,
Type::F32 => wasmer_value_tag::WASM_F32,
Type::F64 => wasmer_value_tag::WASM_F64,
Type::V128 => unimplemented!("V128 not supported in C API"),
}
}
}

View File

@ -22,4 +22,6 @@ test-module-exports
test-module-imports
test-module-serialize
test-tables
test-validate
test-validate
test-context
test-module-import-instantiate

View File

@ -14,6 +14,8 @@ add_executable(test-module-imports test-module-imports.c)
add_executable(test-module-serialize test-module-serialize.c)
add_executable(test-tables test-tables.c)
add_executable(test-validate test-validate.c)
add_executable(test-context test-context.c)
add_executable(test-module-import-instantiate test-module-import-instantiate.c)
find_library(
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so libwasmer_runtime_c_api.dll
@ -87,3 +89,11 @@ add_test(test-tables test-tables)
target_link_libraries(test-validate general ${WASMER_LIB})
target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS})
add_test(test-validate test-validate)
target_link_libraries(test-context general ${WASMER_LIB})
target_compile_options(test-context PRIVATE ${COMPILER_OPTIONS})
add_test(test-context test-context)
target_link_libraries(test-module-import-instantiate general ${WASMER_LIB})
target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS})
add_test(test-module-import-instantiate test-module-import-instantiate)

Binary file not shown.

View File

@ -0,0 +1,12 @@
(module
(func $inc (import "env" "inc"))
(func $mul (import "env" "mul"))
(func $get (import "env" "get") (result i32))
(func (export "inc_and_get") (result i32)
call $inc
call $get)
(func (export "mul_and_get") (result i32)
call $mul
call $get))

View File

@ -4,19 +4,17 @@ use std::process::Command;
fn test_c_api() {
let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests");
run_command("cmake", project_tests_dir, Some("."));
run_command("make", project_tests_dir, Some("-Wdev -Werror=dev"));
run_command("make", project_tests_dir, Some("test"));
run_command("cmake", project_tests_dir, vec!["."]);
run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]);
run_command("make", project_tests_dir, vec!["test", "ARGS=\"-V\""]);
}
fn run_command(command_str: &str, dir: &str, arg: Option<&str>) {
println!("Running command: `{}` arg: {:?}", command_str, arg);
fn run_command(command_str: &str, dir: &str, args: Vec<&str>) {
println!("Running command: `{}` args: {:?}", command_str, args);
let mut command = Command::new(command_str);
if let Some(a) = arg {
command.arg(a);
}
command.args(&args);
command.current_dir(dir);

View File

@ -0,0 +1,137 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int32_t amount;
int32_t value;
} counter_data;
typedef struct {
uint8_t* bytes;
long bytes_len;
} wasm_file_t;
wasm_file_t read_wasm_file(const char* file_name) {
wasm_file_t wasm_file;
FILE *file = fopen(file_name, "r");
fseek(file, 0, SEEK_END);
wasm_file.bytes_len = ftell(file);
wasm_file.bytes = malloc(wasm_file.bytes_len);
fseek(file, 0, SEEK_SET);
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
fclose(file);
return wasm_file;
}
void inc_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value + data->amount;
}
void mul_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value * data->amount;
}
int32_t get_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
return data->value;
}
counter_data *init_counter(int32_t value, int32_t amount) {
counter_data* counter = malloc(sizeof(counter_data));
counter->value = value;
counter->amount = amount;
return counter;
}
void assert_counter(wasmer_instance_t *instance, int32_t expected) {
wasmer_value_t result_one;
wasmer_value_t params[] = {};
wasmer_value_t results[] = {result_one};
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
printf("Call result: %d\n", call1_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == expected);
assert(call1_result == WASMER_OK);
const wasmer_instance_context_t *ctx = wasmer_instance_context_get(instance);
counter_data *cd = (counter_data*)wasmer_instance_context_data_get(ctx);
assert(cd->value == expected);
}
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
wasmer_import_t import;
wasmer_byte_array module_name_bytes;
wasmer_byte_array import_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
return import;
}
int main()
{
// Prepare Imports
wasmer_value_tag inc_params_sig[] = {};
wasmer_value_tag inc_returns_sig[] = {};
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
wasmer_value_tag mul_params_sig[] = {};
wasmer_value_tag mul_returns_sig[] = {};
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
wasmer_value_tag get_params_sig[] = {};
wasmer_value_tag get_returns_sig[] = {WASM_I32};
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
wasmer_import_t get_import = create_import("env", "get", get_func);
wasmer_import_t imports[] = {inc_import, mul_import, get_import};
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_res = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 3);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);
wasmer_instance_context_data_set(instance, counter);
// Run `instance.inc_and_get` and assert
assert_counter(instance, 7);
assert_counter(instance, 12);
assert_counter(instance, 17);
// Clear resources
wasmer_import_func_destroy(inc_func);
wasmer_import_func_destroy(get_func);
wasmer_instance_destroy(instance);
free(counter);
free(wasm_file.bytes);
return 0;
}

View File

@ -42,8 +42,9 @@ int main()
assert(export_length == 5);
// Get the `memory` export.
wasmer_export_t *export = wasmer_exports_get(exports, 1);
wasmer_export_t *export = wasmer_exports_get(exports, 0);
wasmer_import_export_kind kind = wasmer_export_kind(export);
printf("Wasmer import export kind: %d (Memory is %d)\n", kind, WASM_MEMORY);
assert(kind == WASM_MEMORY);
wasmer_byte_array export_name = wasmer_export_name(export);

View File

@ -0,0 +1,149 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int32_t amount;
int32_t value;
} counter_data;
typedef struct {
uint8_t* bytes;
long bytes_len;
} wasm_file_t;
wasm_file_t read_wasm_file(const char* file_name) {
wasm_file_t wasm_file;
FILE *file = fopen(file_name, "r");
fseek(file, 0, SEEK_END);
wasm_file.bytes_len = ftell(file);
wasm_file.bytes = malloc(wasm_file.bytes_len);
fseek(file, 0, SEEK_SET);
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
fclose(file);
return wasm_file;
}
void inc_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value + data->amount;
}
void mul_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value * data->amount;
}
int32_t get_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
return data->value;
}
counter_data *init_counter(int32_t value, int32_t amount) {
counter_data* counter = malloc(sizeof(counter_data));
counter->value = value;
counter->amount = amount;
return counter;
}
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
wasmer_import_t import;
wasmer_byte_array module_name_bytes;
wasmer_byte_array import_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
return import;
}
int main()
{
// Prepare Imports
wasmer_value_tag inc_params_sig[] = {};
wasmer_value_tag inc_returns_sig[] = {};
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
wasmer_value_tag mul_params_sig[] = {};
wasmer_value_tag mul_returns_sig[] = {};
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
wasmer_value_tag get_params_sig[] = {};
wasmer_value_tag get_returns_sig[] = {WASM_I32};
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
wasmer_import_t get_import = create_import("env", "get", get_func);
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
// Compile module
wasmer_module_t *module = NULL;
wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len);
assert(compile_res == WASMER_OK);
// Prepare Import Object
wasmer_import_object_t *import_object = wasmer_import_object_new();
// First, we import `inc_counter` and `mul_counter`
wasmer_import_t imports[] = {inc_import, mul_import};
wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res == WASMER_OK);
// Now, we'll import `inc_counter` and `mul_counter`
wasmer_import_t more_imports[] = {get_import};
wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1);
assert(extend_res2 == WASMER_OK);
// Same `wasmer_import_object_extend` as the first, doesn't affect anything
wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res3 == WASMER_OK);
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);
wasmer_instance_context_data_set(instance, counter);
wasmer_value_t result_one;
wasmer_value_t params[] = {};
wasmer_value_t results[] = {result_one};
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
printf("Call result: %d\n", call1_result);
printf("Result: %d\n", results[0].value.I32);
wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1);
printf("Call result: %d\n", call2_result);
printf("Result: %d\n", results[0].value.I32);
// Clear resources
wasmer_import_func_destroy(inc_func);
wasmer_import_func_destroy(mul_func);
wasmer_import_func_destroy(get_func);
wasmer_instance_destroy(instance);
free(counter);
free(wasm_file.bytes);
return 0;
}

View File

@ -95,11 +95,7 @@ typedef struct {
typedef struct {
} wasmer_instance_t;
typedef struct {
} wasmer_instance_context_t;
} wasmer_import_object_t;
typedef struct {
@ -119,6 +115,14 @@ typedef struct {
wasmer_import_export_value value;
} wasmer_import_t;
typedef struct {
} wasmer_instance_t;
typedef struct {
} wasmer_instance_context_t;
typedef struct {
bool has_some;
uint32_t some;
@ -392,6 +396,24 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
uint32_t *result);
/**
* Frees memory of the given ImportObject
*/
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
/**
* Extends an existing import object with new imports
*/
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
wasmer_import_t *imports,
unsigned int imports_len);
/**
* Creates a new empty import object.
* See also `wasmer_import_object_append`
*/
wasmer_import_object_t *wasmer_import_object_new(void);
/**
* Calls an instances exported function by `name` with the provided parameters.
* Results are set using the provided `results` pointer.
@ -417,6 +439,11 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
*/
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
/**
* Extracts the instance's context and returns it.
*/
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
/**
* Gets the memory within the context at the index `memory_idx`.
* The index is always 0 until multiple memories are supported.
@ -526,6 +553,16 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
*/
void wasmer_module_destroy(wasmer_module_t *module);
/**
* Given:
* A prepared `wasmer` import-object
* A compiled wasmer module
* Instantiates a wasmer instance
*/
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
const wasmer_module_t *module,
const wasmer_import_object_t *import_object);
/**
* Creates a new Instance from the given module and imports.
* Returns `wasmer_result_t::WASMER_OK` upon success.

View File

@ -91,11 +91,7 @@ struct wasmer_import_func_t {
};
struct wasmer_instance_t {
};
struct wasmer_instance_context_t {
struct wasmer_import_object_t {
};
@ -117,6 +113,14 @@ struct wasmer_import_t {
wasmer_import_export_value value;
};
struct wasmer_instance_t {
};
struct wasmer_instance_context_t {
};
struct wasmer_limit_option_t {
bool has_some;
uint32_t some;
@ -318,6 +322,18 @@ wasmer_result_t wasmer_import_func_returns(const wasmer_import_func_t *func,
wasmer_result_t wasmer_import_func_returns_arity(const wasmer_import_func_t *func,
uint32_t *result);
/// Frees memory of the given ImportObject
void wasmer_import_object_destroy(wasmer_import_object_t *import_object);
/// Extends an existing import object with new imports
wasmer_result_t wasmer_import_object_extend(wasmer_import_object_t *import_object,
wasmer_import_t *imports,
unsigned int imports_len);
/// Creates a new empty import object.
/// See also `wasmer_import_object_append`
wasmer_import_object_t *wasmer_import_object_new();
/// Calls an instances exported function by `name` with the provided parameters.
/// Results are set using the provided `results` pointer.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
@ -337,6 +353,9 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx);
/// passed to all imported function for instance.
void wasmer_instance_context_data_set(wasmer_instance_t *instance, void *data_ptr);
/// Extracts the instance's context and returns it.
const wasmer_instance_context_t *wasmer_instance_context_get(wasmer_instance_t *instance);
/// Gets the memory within the context at the index `memory_idx`.
/// The index is always 0 until multiple memories are supported.
const wasmer_memory_t *wasmer_instance_context_memory(const wasmer_instance_context_t *ctx,
@ -418,6 +437,14 @@ wasmer_result_t wasmer_module_deserialize(wasmer_module_t **module,
/// Frees memory for the given Module
void wasmer_module_destroy(wasmer_module_t *module);
/// Given:
/// A prepared `wasmer` import-object
/// A compiled wasmer module
/// Instantiates a wasmer instance
wasmer_result_t wasmer_module_import_instantiate(wasmer_instance_t **instance,
const wasmer_module_t *module,
const wasmer_import_object_t *import_object);
/// Creates a new Instance from the given module and imports.
/// Returns `wasmer_result_t::WASMER_OK` upon success.
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-runtime-core"
version = "0.5.6"
version = "0.6.0"
description = "Wasmer runtime core library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,46 +8,46 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
nix = "0.14.0"
nix = "0.15.0"
page_size = "0.4.1"
wasmparser = "0.32.1"
parking_lot = "0.7.1"
lazy_static = "1.2.0"
indexmap = "1.0.2"
wasmparser = "0.35.1"
parking_lot = "0.9.0"
lazy_static = "1.3.0"
errno = "0.2.4"
libc = "0.2.49"
libc = "0.2.60"
hex = "0.3.2"
smallvec = "0.6.9"
smallvec = "0.6.10"
bincode = "1.1"
colored = "1.8"
[dependencies.indexmap]
version = "1.0.2"
features = ["serde-1"]
# Dependencies for caching.
[dependencies.serde]
version = "1.0"
version = "1.0.99"
# This feature is required for serde to support serializing/deserializing reference counted pointers (e.g. Rc and Arc).
features = ["rc"]
[dependencies.serde_derive]
version = "1.0"
version = "1.0.98"
[dependencies.serde_bytes]
version = "0.10"
version = "0.11.2"
[dependencies.serde-bench]
version = "0.0.7"
[dependencies.blake2b_simd]
version = "0.4.1"
version = "0.5.6"
[dependencies.digest]
version = "0.8.0"
[dependencies.hashbrown]
version = "0.1"
features = ["serde"]
version = "0.8.1"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["memoryapi"] }
winapi = { version = "0.3.7", features = ["memoryapi"] }
[dev-dependencies]
field-offset = "0.1.1"
[build-dependencies]
blake2b_simd = "0.4.1"
blake2b_simd = "0.5.6"
rustc_version = "0.2.3"
cc = "1.0"
@ -55,5 +55,6 @@ cc = "1.0"
debug = []
trace = ["debug"]
# backend flags used in conditional compilation of Backend::variants
"backend-cranelift" = []
"backend-singlepass" = []
"backend-llvm" = []

View File

@ -15,7 +15,7 @@ use crate::{
};
use std::{any::Any, ptr::NonNull};
use hashbrown::HashMap;
use std::collections::HashMap;
pub mod sys {
pub use crate::sys::*;
@ -32,6 +32,7 @@ pub enum Backend {
impl Backend {
pub fn variants() -> &'static [&'static str] {
&[
#[cfg(feature = "backend-cranelift")]
"cranelift",
#[cfg(feature = "backend-singlepass")]
"singlepass",
@ -110,14 +111,21 @@ impl Default for MemoryBoundCheckMode {
}
}
#[derive(Debug, Default)]
pub struct Features {
pub simd: bool,
pub threads: bool,
}
/// Configuration data for the compiler
#[derive(Default)]
#[derive(Debug, Default)]
pub struct CompilerConfig {
/// Symbol information generated from emscripten; used for more detailed debug messages
pub symbol_map: Option<HashMap<u32, String>>,
pub memory_bound_check_mode: MemoryBoundCheckMode,
pub enforce_stack_check: bool,
pub track_state: bool,
pub features: Features,
}
pub trait Compiler {

View File

@ -55,11 +55,19 @@ pub struct LocalBacking {
}
impl LocalBacking {
pub(crate) fn new(module: &ModuleInner, imports: &ImportBacking, vmctx: *mut vm::Ctx) -> Self {
pub(crate) fn new(
module: &ModuleInner,
imports: &ImportBacking,
vmctx: *mut vm::Ctx,
) -> LinkResult<Self> {
let mut memories = Self::generate_memories(module);
let mut tables = Self::generate_tables(module);
let mut globals = Self::generate_globals(module, imports);
// Ensure all initializers are valid before running finalizers
Self::validate_memories(module, imports)?;
Self::validate_tables(module, imports, &mut tables)?;
let vm_memories = Self::finalize_memories(module, imports, &mut memories);
let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx);
let vm_globals = Self::finalize_globals(&mut globals);
@ -67,7 +75,7 @@ impl LocalBacking {
let dynamic_sigindices = Self::generate_sigindices(&module.info);
let local_functions = Self::generate_local_functions(module);
Self {
Ok(Self {
memories,
tables,
globals,
@ -80,7 +88,7 @@ impl LocalBacking {
local_functions,
internals: Internals([0; INTERNALS_SIZE]),
}
})
}
fn generate_local_functions(module: &ModuleInner) -> BoxedMap<LocalFuncIndex, *const vm::Func> {
@ -117,6 +125,51 @@ impl LocalBacking {
memories.into_boxed_map()
}
/// Validate each locally-defined memory in the Module.
///
/// This involves copying in the data initializers.
fn validate_memories(module: &ModuleInner, imports: &ImportBacking) -> LinkResult<()> {
// Validate data size fits
for init in module.info.data_initializers.iter() {
let init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
Initializer::GetGlobal(import_global_index) => {
if let Value::I32(x) = imports.globals[import_global_index].get() {
x as u32
} else {
panic!("unsupported global type for initializer")
}
}
} as usize;
// Validate data size fits
match init.memory_index.local_or_import(&module.info) {
LocalOrImport::Local(local_memory_index) => {
let memory_desc = module.info.memories[local_memory_index];
let data_top = init_base + init.data.len();
if memory_desc.minimum.bytes().0 < data_top || data_top < init_base {
return Err(vec![LinkError::Generic {
message: "data segment does not fit".to_string(),
}]);
}
}
LocalOrImport::Import(imported_memory_index) => {
// Write the initialization data to the memory that
// we think the imported memory is.
let local_memory = unsafe { &*imports.vm_memories[imported_memory_index] };
let data_top = init_base + init.data.len();
if local_memory.bound < data_top || data_top < init_base {
return Err(vec![LinkError::Generic {
message: "data segment does not fit".to_string(),
}]);
}
}
}
}
Ok(())
}
/// Initialize each locally-defined memory in the Module.
///
/// This involves copying in the data initializers.
@ -126,12 +179,8 @@ impl LocalBacking {
memories: &mut SliceMap<LocalMemoryIndex, Memory>,
) -> BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory> {
// For each init that has some data...
for init in module
.info
.data_initializers
.iter()
.filter(|init| init.data.len() > 0)
{
// Initialize data
for init in module.info.data_initializers.iter() {
let init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
@ -146,10 +195,6 @@ impl LocalBacking {
match init.memory_index.local_or_import(&module.info) {
LocalOrImport::Local(local_memory_index) => {
let memory_desc = module.info.memories[local_memory_index];
let data_top = init_base + init.data.len();
assert!(memory_desc.minimum.bytes().0 >= data_top);
let mem = &memories[local_memory_index];
for (mem_byte, data_byte) in mem.view()[init_base..init_base + init.data.len()]
.iter()
@ -161,15 +206,13 @@ impl LocalBacking {
LocalOrImport::Import(imported_memory_index) => {
// Write the initialization data to the memory that
// we think the imported memory is.
unsafe {
let memory_slice = unsafe {
let local_memory = &*imports.vm_memories[imported_memory_index];
let memory_slice =
slice::from_raw_parts_mut(local_memory.base, local_memory.bound);
slice::from_raw_parts_mut(local_memory.base, local_memory.bound)
};
let mem_init_view =
&mut memory_slice[init_base..init_base + init.data.len()];
mem_init_view.copy_from_slice(&init.data);
}
let mem_init_view = &mut memory_slice[init_base..init_base + init.data.len()];
mem_init_view.copy_from_slice(&init.data);
}
}
}
@ -192,6 +235,50 @@ impl LocalBacking {
tables.into_boxed_map()
}
/// This validates all of the locally-defined tables in the Module.
#[allow(clippy::cast_ptr_alignment)]
fn validate_tables(
module: &ModuleInner,
imports: &ImportBacking,
tables: &mut SliceMap<LocalTableIndex, Table>,
) -> LinkResult<()> {
for init in &module.info.elem_initializers {
let init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
Initializer::GetGlobal(import_global_index) => {
if let Value::I32(x) = imports.globals[import_global_index].get() {
x as u32
} else {
panic!("unsupported global type for initializer")
}
}
} as usize;
match init.table_index.local_or_import(&module.info) {
LocalOrImport::Local(local_table_index) => {
let table = &tables[local_table_index];
if (table.size() as usize) < init_base + init.elements.len() {
return Err(vec![LinkError::Generic {
message: "elements segment does not fit".to_string(),
}]);
}
}
LocalOrImport::Import(import_table_index) => {
let table = &imports.tables[import_table_index];
if (table.size() as usize) < init_base + init.elements.len() {
return Err(vec![LinkError::Generic {
message: "elements segment does not fit".to_string(),
}]);
}
}
}
}
Ok(())
}
/// This initializes all of the locally-defined tables in the Module, e.g.
/// putting all the table elements (function pointers)
/// in the right places.
@ -218,13 +305,6 @@ impl LocalBacking {
match init.table_index.local_or_import(&module.info) {
LocalOrImport::Local(local_table_index) => {
let table = &tables[local_table_index];
if (table.size() as usize) < init_base + init.elements.len() {
let delta = (init_base + init.elements.len()) - table.size() as usize;
// Grow the table if it's too small.
table.grow(delta as u32).expect("couldn't grow table");
}
table.anyfunc_direct_access_mut(|elements| {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.info.func_assoc[func_index];
@ -258,12 +338,6 @@ impl LocalBacking {
LocalOrImport::Import(import_table_index) => {
let table = &imports.tables[import_table_index];
if (table.size() as usize) < init_base + init.elements.len() {
let delta = (init_base + init.elements.len()) - table.size() as usize;
// Grow the table if it's too small.
table.grow(delta as u32).expect("couldn't grow table");
}
table.anyfunc_direct_access_mut(|elements| {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.info.func_assoc[func_index];

View File

@ -20,6 +20,7 @@ pub enum Error {
Unknown(String),
InvalidFile(InvalidFileType),
InvalidatedCache,
UnsupportedBackend(Backend),
}
impl From<io::Error> for Error {

View File

@ -1,6 +1,6 @@
use crate::{
backend::RunnableModule,
backend::{Backend, CacheGen, Compiler, CompilerConfig, Token},
backend::{Backend, CacheGen, Compiler, CompilerConfig, Features, Token},
cache::{Artifact, Error as CacheError},
error::{CompileError, CompileResult},
module::{ModuleInfo, ModuleInner},
@ -18,7 +18,7 @@ use wasmparser::{self, WasmDecoder};
use wasmparser::{Operator, Type as WpType};
pub type BreakpointHandler =
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
#[derive(Debug)]
@ -137,8 +137,22 @@ impl<
}
}
fn validate(bytes: &[u8]) -> CompileResult<()> {
let mut parser = wasmparser::ValidatingParser::new(bytes, None);
pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingParserConfig {
wasmparser::ValidatingParserConfig {
operator_config: wasmparser::OperatorValidatorConfig {
enable_threads: features.threads,
enable_reference_types: false,
enable_simd: features.simd,
enable_bulk_memory: false,
enable_multi_value: false,
},
mutable_global_imports: true,
}
}
fn validate_with_features(bytes: &[u8], features: &Features) -> CompileResult<()> {
let mut parser =
wasmparser::ValidatingParser::new(bytes, Some(validating_parser_config(features)));
loop {
let state = parser.read();
match *state {
@ -166,7 +180,7 @@ impl<
_: Token,
) -> CompileResult<ModuleInner> {
if requires_pre_validation(MCG::backend_id()) {
validate(wasm)?;
validate_with_features(wasm, &compiler_config.features)?;
}
let mut mcg = MCG::new();
@ -218,7 +232,7 @@ impl<'a, 'b> EventSink<'a, 'b> {
}
pub struct MiddlewareChain {
chain: Vec<Box<GenericFunctionMiddleware>>,
chain: Vec<Box<dyn GenericFunctionMiddleware>>,
}
impl MiddlewareChain {

View File

@ -80,6 +80,9 @@ pub enum LinkError {
expected: GlobalDescriptor,
found: GlobalDescriptor,
},
Generic {
message: String,
},
}
impl PartialEq for LinkError {
@ -107,6 +110,9 @@ impl std::fmt::Display for LinkError {
LinkError::IncorrectTableDescriptor{namespace, name,expected,found} => {
write!(f, "Incorrect table descriptor, namespace: {}, name: {}, expected table descriptor: {:?}, found table descriptor: {:?}", namespace, name, expected, found)
},
LinkError::Generic { message } => {
write!(f, "{}", message)
},
}
}
}
@ -134,7 +140,7 @@ impl std::fmt::Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RuntimeError::Trap { ref msg } => {
write!(f, "WebAssembly trap occured during runtime: {}", msg)
write!(f, "WebAssembly trap occurred during runtime: {}", msg)
}
RuntimeError::Error { data } => {
if let Some(s) = data.downcast_ref::<String>() {

View File

@ -2,7 +2,7 @@ use crate::{
global::Global, instance::InstanceInner, memory::Memory, module::ExportIndex,
module::ModuleInner, table::Table, types::FuncSig, vm,
};
use hashbrown::hash_map;
use indexmap::map::Iter as IndexMapIter;
use std::sync::Arc;
#[derive(Debug, Copy, Clone)]
@ -41,7 +41,7 @@ impl FuncPointer {
pub struct ExportIter<'a> {
inner: &'a InstanceInner,
iter: hash_map::Iter<'a, String, ExportIndex>,
iter: IndexMapIter<'a, String, ExportIndex>,
module: &'a ModuleInner,
}

View File

@ -35,7 +35,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>,
payload: Option<Box<Any>>, // out
payload: Option<Box<dyn Any>>, // out
}
thread_local! {
@ -89,7 +89,7 @@ pub unsafe fn clear_wasm_interrupt() {
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
f: F,
breakpoints: Option<BreakpointMap>,
) -> Result<R, Box<Any>> {
) -> Result<R, Box<dyn Any>> {
let unwind = UNWIND.with(|x| x.get());
let old = (*unwind).take();
*unwind = Some(UnwindInfo {
@ -111,7 +111,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
}
}
pub unsafe fn begin_unsafe_unwind(e: Box<Any>) -> ! {
pub unsafe fn begin_unsafe_unwind(e: Box<dyn Any>) -> ! {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()

View File

@ -46,10 +46,11 @@ impl Global {
let local_global = vm::LocalGlobal {
data: match value {
Value::I32(x) => x as u64,
Value::I64(x) => x as u64,
Value::F32(x) => x.to_bits() as u64,
Value::F64(x) => x.to_bits(),
Value::I32(x) => x as u128,
Value::I64(x) => x as u128,
Value::F32(x) => x.to_bits() as u128,
Value::F64(x) => x.to_bits() as u128,
Value::V128(x) => x,
},
};
@ -75,10 +76,11 @@ impl Global {
if self.desc.ty == value.ty() {
let local_global = vm::LocalGlobal {
data: match value {
Value::I32(x) => x as u64,
Value::I64(x) => x as u64,
Value::F32(x) => x.to_bits() as u64,
Value::F64(x) => x.to_bits(),
Value::I32(x) => x as u128,
Value::I64(x) => x as u128,
Value::F32(x) => x.to_bits() as u128,
Value::F64(x) => x.to_bits() as u128,
Value::V128(x) => x,
},
};
*self.storage.borrow_mut() = local_global;
@ -98,7 +100,8 @@ impl Global {
Type::I32 => Value::I32(data as i32),
Type::I64 => Value::I64(data as i64),
Type::F32 => Value::F32(f32::from_bits(data as u32)),
Type::F64 => Value::F64(f64::from_bits(data)),
Type::F64 => Value::F64(f64::from_bits(data as u64)),
Type::V128 => Value::V128(data),
}
}

View File

@ -1,6 +1,6 @@
use crate::export::Export;
use hashbrown::{hash_map::Entry, HashMap};
use std::collections::VecDeque;
use std::collections::{hash_map::Entry, HashMap};
use std::{
cell::{Ref, RefCell},
ffi::c_void,
@ -46,12 +46,12 @@ impl IsExport for Export {
/// ```
pub struct ImportObject {
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
pub(crate) state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub(crate) state_creator: Option<Rc<dyn Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub allow_missing_functions: bool,
}
impl ImportObject {
/// Create a new `ImportObject`.
/// Create a new `ImportObject`.
pub fn new() -> Self {
Self {
map: Rc::new(RefCell::new(HashMap::new())),

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