Merge branch 'master' into feature/dynasm-backend

This commit is contained in:
Brandon Fish 2019-03-12 20:58:22 -05:00
commit 557be77338
358 changed files with 13992 additions and 1805 deletions

View File

@ -1,11 +1,12 @@
branches:
except:
- master
version: "{build} ~ {branch}"
os: Visual Studio 2017
branches:
only:
- staging
- trying
environment:
matrix:
- CHANNEL: stable
@ -13,20 +14,60 @@ environment:
ABI: msvc
TARGET: x86_64-pc-windows-msvc
cache:
- 'C:\Users\appveyor\.cargo'
- target
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -yv --default-host %target%
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
# # 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 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
- rustup default stable-%target%
- rustup update
- rustc -vV
- cargo -vV
artifacts:
- path: target\debug\wasmer.exe
name: wasmer.exe
# Install InnoSetup
- appveyor-retry appveyor DownloadFile https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-08-22-is.exe
- 2017-08-22-is.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-
- set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH%
# uncomment to RDP to appveyor
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script:
- cargo build --verbose
- cargo build --release --verbose
test_script:
- set RUST_BACKTRACE=1
- cargo test --verbose
- cargo test --manifest-path lib/spectests/Cargo.toml --features clif
before_deploy:
- cd ./src/installer
- iscc wasmer.iss
- copy /y .\WasmerInstaller.exe ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- appveyor PushArtifact ..\..\WasmerInstaller-%APPVEYOR_REPO_TAG_NAME%.exe
- cd ..\..\
matrix:
fast_finish: true
deploy:
description: 'WasmerInstaller'
artifact: /.*\.exe/
auth_token:
secure: CaKtncy7S1PWxzDUQ0p2264pe3HwxzDn5VIyRizDaa72/SVfskNcoMjwwRh0ut22
provider: GitHub
on:
branch: master
appveyor_repo_tag: true

View File

@ -13,6 +13,8 @@ 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
- run:
name: Install lint deps
command: |
@ -20,7 +22,9 @@ jobs:
rustup component add clippy
- run:
name: Execute lints
command: make lint
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make lint
- save_cache:
paths:
- /usr/local/cargo/registry
@ -41,8 +45,23 @@ jobs:
name: Install dependencies
command: |
sudo apt-get install -y cmake
- run: make test
- run: make integration-tests
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
- run:
name: Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Emscripten Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten
- run:
name: Integration Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make integration-tests
- save_cache:
paths:
- /usr/local/cargo/registry
@ -66,6 +85,9 @@ jobs:
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# 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
- run:
name: Install Rust
command: |
@ -73,19 +95,31 @@ jobs:
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
- run:
name: Execute tests
name: 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/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test
- run:
name: Execute integration tests
name: Emscripten 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/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten
- 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/"
make integration-tests
- save_cache:
paths:
@ -110,12 +144,22 @@ 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
- run:
name: Execute tests
command: make test
- run:
name: Make release build
name: Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- run:
name: Emscripten Tests
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test-emscripten
- run:
name: Release Build
command: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make release
mkdir -p artifacts
VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
@ -153,6 +197,9 @@ jobs:
curl -O https://cmake.org/files/v3.4/cmake-3.4.1-Darwin-x86_64.tar.gz
tar xf cmake-3.4.1-Darwin-x86_64.tar.gz
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
# 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
- run:
name: Install Rust
command: |
@ -160,19 +207,31 @@ jobs:
export PATH="$HOME/.cargo/bin:$PATH"
cargo --version
- run:
name: Execute tests
name: Tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test
- run:
name: Make release build
name: Emscripten Tests
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
# We increase the ulimit for fixing cargo unclosed files in mac
ulimit -n 8000
sudo sysctl -w kern.maxfiles=655360 kern.maxfilesperproc=327680
make test-emscripten
- run:
name: Release Build
command: |
export PATH="`pwd`/cmake-3.4.1-Darwin-x86_64/CMake.app/Contents/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/"
make release
mkdir -p artifacts
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
@ -205,8 +264,12 @@ 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
- run: rustup default nightly
- run: make test
- run: |
export LLVM_SYS_70_PREFIX="`pwd`/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/"
make test
- save_cache:
paths:
- /usr/local/cargo/registry
@ -249,23 +312,31 @@ workflows:
- test:
filters:
branches:
ignore: master
only:
- trying
- staging
- test-macos:
filters:
branches:
ignore: master
only:
- trying
- staging
- test-and-build:
filters:
branches:
only: master
only:
- master
- test-and-build-macos:
filters:
branches:
only: master
only:
- master
- test-rust-nightly:
filters:
branches:
only: master
only:
- trying
- staging
- publish-github-release:
requires:
- lint

43
.github/ISSUE_TEMPLATE/---bug-report.md vendored Normal file
View File

@ -0,0 +1,43 @@
---
name: "\U0001F41E Bug report"
about: Create a report to help us improve
title: ''
labels: "\U0001F41E bug"
assignees: ''
---
Thanks for the bug report!
### Describe the bug
A clear and concise description of what the bug is.
Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment.
```bash
echo "`wasmer -V` | `rustc -V` | `uname -m`"
```
### Steps to reproduce
1. Go to '…'
2. Compile with '…'
3. Run '…'
4. See error
If applicable, add a link to a test case (as a zip file or link to a repository we can clone).
### Expected behavior
A clear and concise description of what you expected to happen.
### Actual behavior
A clear and concise description of what actually happened.
If applicable, add screenshots to help explain your problem.
### Additional context
Add any other context about the problem here.

View File

@ -0,0 +1,26 @@
---
name: "\U0001F389 Feature request"
about: Suggest an idea for this project
title: ''
labels: "\U0001F389 enhancement"
assignees: ''
---
Thanks for proposing a new feature!
### Motivation
A clear and concise description of what the motivation for the new feature is, and what problem it is solving.
### Proposed solution
A clear and concise description of the feature you would like to add, and how it solves the motivating problem.
### Alternatives
A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better.
### Additional context
Add any other context or screenshots about the feature request here.

16
.github/ISSUE_TEMPLATE/--question.md vendored Normal file
View File

@ -0,0 +1,16 @@
---
name: "❓ Question"
about: Ask a question about this project
title: ''
labels: "❓ question"
assignees: ''
---
### Summary
A clear and concise summary of your question.
### Additional details
Provide any additional details here.

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/artifacts
.DS_Store
.idea
**/.vscode

View File

@ -2,19 +2,19 @@
Wasmer uses the following components:
- [Cranelift](https://github.com/cranestation/cranelift): for compiling WASM function binaries into Machine IR
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and also to run WebAssembly spectests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly Modules
- [Cranelift](https://github.com/cranestation/cranelift): for compiling Wasm binaries to machine code
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and running WebAssembly spec tests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly modules
## How Wasmer works?
## How Wasmer works
The first time you run `wasmer run myfile.wasm`, wasmer will:
The first time you run `wasmer run myfile.wasm`, Wasmer will:
- Check if is a `.wast` file. If so, transform it to `.wasm`
- Check that the provided binary is a valid WebAssembly one. That means, that its binary format starts with `\0asm`.
- If it looks like a WebAssembly file, try to parse it with `wasmparser` and generate a `Module` from it
- Once a `Module` is generated, an `Instance` is created with the proper `import_object` (that means, if is detected as an emscripten file, it will add the emscripten expected imports)
- Try to call the WebAssembly start function, or if unexistent try to search for the one that is exported as `main`.
- Check if is a `.wast` file, and if so, transform it to `.wasm`
- Check that the provided binary is a valid WebAssembly one, i.e. its binary format starts with `\0asm`.
- Parse it with `wasmparser` and generate a `Module` from it
- Generate an `Instance` with the proper `import_object` (that means, if is detected to be an Emscripten file, it will add the Emscripten expected imports)
- Try to call the WebAssembly `start` function, or if it does not exist, try to search for the function that is exported as `main`
Find a more detailed explanation of the process below:
@ -22,7 +22,7 @@ Find a more detailed explanation of the process below:
As the WebAssembly file is being parsed, it will read the sections in the WebAssembly file (memory, table, function, global and element definitions) using the `Module` (or `ModuleEnvironment`) as the structure to hold this information.
However, the real IR initialization happens while a function body is being parsed/created. That means, when the parser reads the section `(func ...)`.
However, the real IR initialization happens while a function body is being parsed/created, i.e. when the parser reads the section `(func ...)`.
While the function body is being parsed the corresponding `FuncEnvironment` methods will be called.
So for example, if the function is using a table, the `make_table` method within that `FuncEnvironment` will be called.
@ -41,15 +41,14 @@ Once we have the compiled values, we will push them to memory and mark them as e
#### Relocations
Sometimes the functions that we generated will need to call other functions.
However the generated code have no idea how to link this functions together.
Sometimes the functions that we generate will need to call other functions, but the generated code has no idea how to link these functions together.
For example, if a function `A` is calling function `B` (that means is having a `(call b)` on it's body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory).
For example, if a function `A` is calling function `B` (that means is having a `(call b)` on its body) while compiling `A` we will have no idea where the function `B` lives on memory (as `B` is not yet compiled nor pushed into memory).
For that reason, we will start collecting all the calls that function `A` will need to do under the hood, and save it's offsets.
We do that, so we can patch the function calls after compilation, to point to the correct memory address.
Note: Sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values.
Note: sometimes this functions rather than living in the same WebAssembly module, they will be provided as import values.
#### Traps
@ -66,5 +65,5 @@ Once that's finished, we will have a `Instance` function that will be ready to e
## Emscripten
The Wasmer Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the emscripten functions, while instantiating the WebAssembly Instance.
Wasmer's Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the Emscripten functions, while instantiating the WebAssembly Instance.

View File

@ -2,7 +2,7 @@
Wasmer is a community effort.
In order to build the best WebAssembly runtime it's our duty to see how other runtimes are approaching the same space
and get inspired from them on the things that they got right, so wasmer and its community can benefit from a solid
and get inspired from them on the things that they got right, so Wasmer and its community can benefit from a solid
foundation.
These are the different project that we used as inspiration:
@ -10,9 +10,9 @@ These are the different project that we used as inspiration:
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework
- [greenwasm](https://github.com/Kimundi/greenwasm): for their [spectests framework](https://github.com/Kimundi/greenwasm/tree/master/greenwasm-spectest)
- [wasmtime](/wasmtime): on their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs).
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys.
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility.
- [wasmtime](https://github.com/CraneStation/wasmtime): for their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs)
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility
We would love to hear from you if you think we can take inspiration from other projects that we haven't covered here.
😊
@ -21,10 +21,10 @@ We would love to hear from you if you think we can take inspiration from other p
### Nebulet
```
```text
MIT License
Copyright (c) 2018
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -47,7 +47,7 @@ SOFTWARE.
### WAVM
```
```text
Copyright (c) 2018, Andrew Scheidecker
All rights reserved.
@ -69,7 +69,7 @@ The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/L
### Greenwasm
```
```text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -275,7 +275,7 @@ limitations under the License.
### Wasmtime
```
```text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -497,7 +497,7 @@ Software.
```
### Emscripten
```
```text
Emscripten is available under 2 licenses, the MIT license and the
University of Illinois/NCSA Open Source License.
@ -557,7 +557,7 @@ the following conditions:
Neither the names of Mozilla,
nor the names of its contributors may be used to endorse
or promote products derived from this Software without specific prior
written permission.
written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

1011
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer"
version = "0.1.4"
version = "0.2.1"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
repository = "https://github.com/wasmerio/wasmer"
@ -26,8 +26,11 @@ wasmer-runtime = { path = "lib/runtime" }
wasmer-runtime-core = { path = "lib/runtime-core" }
wasmer-emscripten = { path = "lib/emscripten" }
[target.'cfg(not(windows))'.dependencies]
wasmer-llvm-backend = { path = "lib/llvm-backend" }
[workspace]
members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests"]
members = ["lib/clif-backend", "lib/dynasm-backend", "lib/runtime", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend"]
[build-dependencies]
wabt = "0.7.2"
@ -36,6 +39,6 @@ glob = "0.2.11"
[features]
default = ["fast-tests"]
debug = []
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster
fast-tests = []

View File

@ -1,6 +1,4 @@
The MIT License (MIT)
Copyright (c) 2018-Present Syrus Akbary
Copyright (c) 2019 Syrus Akbary
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -16,7 +16,7 @@ emtests:
# rm -rf artifacts
build:
cargo build
cargo build --features debug
install:
cargo install --path .
@ -34,15 +34,25 @@ precommit: lint test
test:
# We use one thread so the emscripten stdouts doesn't collide
cargo test --all -- --test-threads=1 $(runargs)
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests -- $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
# cargo test -p wasmer-spectests -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture
test-emscripten:
cargo test --manifest-path lib/emscripten/Cargo.toml --features clif -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)
release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows
# brew install mingw-w64
cargo build --release
debug-release:
cargo build --release --features debug
debug-release:
cargo build --release --features "debug"

View File

@ -1,16 +1,24 @@
<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"></a></p>
<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">
</a>
</p>
<p align="center">
<a href="https://circleci.com/gh/wasmerio/wasmer/"><img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status"></a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE"><img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"></a>
<a href="https://circleci.com/gh/wasmerio/wasmer/">
<img src="https://img.shields.io/circleci/project/github/wasmerio/wasmer/master.svg" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License">
</a>
<a href="https://spectrum.chat/wasmer">
<img alt="Join the Wasmer Community" src="https://withspectrum.github.io/badge/badge.svg" />
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
</p>
## Introduction
[Wasmer](https://wasmer.io/) is a Standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go.
[Wasmer](https://wasmer.io/) is a standalone JIT WebAssembly runtime, aiming to be fully compatible with Emscripten, Rust and Go.
Install Wasmer with:
@ -18,20 +26,20 @@ Install Wasmer with:
curl https://get.wasmer.io -sSfL | sh
```
_**NEW ✨**: Now you can also embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how to do it!_
_**NEW ✨**: You can now embed Wasmer in your Rust application, check our [example repo](https://github.com/wasmerio/wasmer-rust-example) to see how!_
### Usage
`wasmer` can execute both the standard binary format (`.wasm`) and the text
Wasmer can execute both the standard binary format (`.wasm`) and the text
format defined by the WebAssembly reference interpreter (`.wat`).
Once installed, you will be able to run any WebAssembly files (_including Nginx, and Lua!_):
Once installed, you will be able to run any WebAssembly files (_including nginx and Lua!_):
```sh
# Run Lua
wasmer run examples/lua.wasm
# Run Nginx
# Run nginx
wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
```
@ -39,18 +47,18 @@ wasmer run examples/nginx/nginx.wasm -- -p examples/nginx -c nginx.conf
Wasmer is structured into different directories:
- [`src`](./src): code related to the wasmer excutable binary itself
- [`src`](./src): code related to the Wasmer executable itself
- [`lib`](./lib): modularized libraries that Wasmer uses under the hood
- [`examples`](./examples): some useful examples to getting started with wasmer
- [`examples`](./examples): some useful examples to getting started with Wasmer
## Dependencies
Building wasmer requires [rustup](https://rustup.rs/).
Building Wasmer requires [rustup](https://rustup.rs/).
To install on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
To build on Windows, download and run [`rustup-init.exe`](https://win.rustup.rs/)
then follow the onscreen instructions.
To install on other systems, run:
To build on other systems, run:
```sh
curl https://sh.rustup.rs -sSf | sh
@ -66,13 +74,13 @@ Please select your operating system:
#### macOS
If you have [homebrew](https://brew.sh/) installed:
If you have [Homebrew](https://brew.sh/) installed:
```sh
brew install cmake
```
Or, in case you have [ports](https://www.macports.org/install.php):
Or, in case you have [MacPorts](https://www.macports.org/install.php):
```sh
sudo port install cmake
@ -86,15 +94,23 @@ sudo apt install cmake
#### Windows (MSVC)
Right now Windows support is _highly experimental_.
We are working on this so Wasmer can soon be released for Windows.
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.
1. Install Python for Windows (https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine.
You should change the installation to install the "Add python.exe to Path" feature.
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15)
2. Install Git for Windows (https://git-scm.com/download/win). DO allow it to add git.exe to the PATH (default
2. Install [Rust for Windows](https://win.rustup.rs)
3. Install [Python for Windows](https://www.python.org/downloads/release/python-2714/). The Windows x86-64 MSI installer is fine.
Make sure to enable "Add python.exe to Path" during installation.
4. Install [Git for Windows](https://git-scm.com/download/win). Allow it to add `git.exe` to your PATH (default
settings for the installer are fine).
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)
## Building
Wasmer is built with [Cargo](https://crates.io/), the Rust package manager.
@ -111,7 +127,7 @@ cargo install --path .
## Testing
Thanks to [spectests](https://github.com/wasmerio/wasmer/tree/master/lib/runtime-core/spectests) we can assure 100% compatibility with the WebAssembly spec test suite.
Thanks to [spec tests](https://github.com/wasmerio/wasmer/tree/master/lib/spectests/spectests) we can ensure 100% compatibility with the WebAssembly spec test suite.
Tests can be run with:
@ -119,7 +135,7 @@ Tests can be run with:
make test
```
If you need to re-generate the Rust tests from the spectests
If you need to regenerate the Rust tests from the spec tests
you can run:
```sh
@ -132,24 +148,32 @@ You can also run integration tests with:
make integration-tests
```
## Benchmarking
Benchmarks can be run with:
```sh
cargo bench --all
```
## Roadmap
Wasmer is an open project guided by strong principles, aiming to be modular, flexible and fast. It is open to the community to help set its direction.
Below are some of the goals (written with order) of this project:
Below are some of the goals of this project (in order of priority):
- [x] It should be 100% compatible with the [WebAssembly Spectest](https://github.com/wasmerio/wasmer/tree/master/spectests)
- [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)_
- [ ] Support Emscripten calls _(in the works)_
- [ ] Support Rust ABI calls
- [ ] Support GO ABI calls
- [ ] Support Go ABI calls
## Architecture
If you would like to know how Wasmer works under the hood, please visit our [ARCHITECTURE](https://github.com/wasmerio/wasmer/blob/master/ARCHITECTURE.md) document.
If you would like to know how Wasmer works under the hood, please see [ARCHITECTURE.md](./ARCHITECTURE.md).
## License
MIT/Apache-2.0
Wasmer is primarily distributed under the terms of the [MIT license](http://opensource.org/licenses/MIT) ([LICENSE](./LICENSE)).
<small>[Attributions](./ATTRIBUTIONS.md)</small>.
[ATTRIBUTIONS](./ATTRIBUTIONS.md)

9
bors.toml Normal file
View File

@ -0,0 +1,9 @@
status = [
"ci/circleci: lint",
"ci/circleci: test",
"ci/circleci: test-macos",
"continuous-integration/appveyor/branch"
]
required_approvals = 1
timeout_sec = 900
delete_merged_branches = true

26
examples/nginx/LICENSE Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2002-2019 Igor Sysoev
* Copyright (C) 2011-2019 Nginx, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

View File

@ -130,10 +130,10 @@ wasmer_link() {
printf "$cyan> Adding to bash profile...$reset\n"
WASMER_PROFILE="$(wasmer_detect_profile)"
LOAD_STR="\n# Wasmer\nexport WASMER_DIR=\"\$HOME/.wasmer\"\n[ -s \"\$WASMER_DIR/wasmer.sh\" ] && source \"\$WASMER_DIR/wasmer.sh\" # This loads wasmer\n"
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n"
SOURCE_STR="# Wasmer config\nexport WASMER_DIR=\"\$HOME/.wasmer\"\nexport WASMER_CACHE_DIR=\"\$WASMER_DIR/cache\"\nexport PATH=\"\$HOME/.wasmer/bin:\$PATH\"\n"
# We create the wasmer.sh file
echo "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh"
printf "$SOURCE_STR" > "$HOME/.wasmer/wasmer.sh"
if [ -z "${WASMER_PROFILE-}" ] ; then
printf "${red}Profile not found. Tried:\n* ${WASMER_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n"

View File

@ -1,8 +1,8 @@
# `lua` integration test
This starts wasmer with the lua wasm file. The test asserts on
the output of wasmer. Run test with:
This starts Wasmer with the Lua Wasm file. The test makes assertions on
the output of Wasmer. Run test with:
```
> ./integration_tests/lua/test.sh

View File

@ -1,7 +1,6 @@
#! /bin/bash
nohup ./target/release/wasmer run examples/lua.wasm &
sleep 3s
nohup ./target/release/wasmer run examples/lua.wasm --disable-cache -- -v
if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out
then

View File

@ -1,11 +1,11 @@
# `nginx` integration test
This starts wasmer with the nginx wasm file and serves an html
file with some simple text to assert on. The test script does
This starts Wasmer with the nginx Wasm file and serves an HTML
file with some simple text to assert on. The test script does
the assertion.
Run test with:
Run test with:
```
> ./integration_tests/nginx/test.sh

View File

@ -1,22 +1,14 @@
#! /bin/bash
nohup ./target/release/wasmer run examples/nginx/nginx.wasm -- -p integration_tests/nginx/ -c nginx.conf &
sleep 3s
nohup ./target/release/wasmer run examples/nginx/nginx.wasm --disable-cache -- -v
curl localhost:8080 > ./nginx.out
if grep "wasmer" ./nginx.out
if grep "nginx version: nginx/1.15.3" ./nohup.out
then
echo "nginx integration test succeeded"
rm ./nohup.out
rm ./nginx.out
rm -rf ./integration_tests/nginx/*_temp
exit 0
else
echo "nginx integration test failed"
rm ./nohup.out
rm ./nginx.out
rm -rf ./integration_tests/nginx/*_temp
exit -1
fi

View File

@ -2,35 +2,36 @@
Wasmer is modularized into different libraries, separated into three main sections:
- [Runtime](#Runtime)
- [Integrations](#Integrations)
- [Backends](#Backends)
- [Runtime](#runtime)
- [Integrations](#integrations)
- [Backends](#backends)
## Runtime
The core of Wasmer is the runtime, which provides the necessary
abstractions to create a good user-experience when embedding.
abstractions to create a good user experience when embedding.
The runtime is divided into two main libraries:
- [runtime-core](./runtime-core/): The main implementation of the runtime.
- [runtime](./runtime/): Easy-to-use api on top of runtime-core.
- [runtime](./runtime/): Easy-to-use API on top of `runtime-core`.
## Integrations
The intergration run on-top of the Wasmer runtime and allow us to run WebAssembly files compiled for different environments.
The integration builds on the Wasmer runtime and allow us to run WebAssembly files compiled for different environments.
Wasmer intends to support different integrations:
- [emscripten](./emscripten): run emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [Nginx](../examples/nginx/nginx.wasm).
- [emscripten](./emscripten): run Emscripten-generated WebAssembly files, such as [Lua](../examples/lua.wasm) or [nginx](../examples/nginx/nginx.wasm).
- Go ABI: _we will work on this soon! Want to give us a hand? ✋_
- Blazor: _researching period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
- Blazor: _research period, see [tracking issue](https://github.com/wasmerio/wasmer/issues/97)_
## Backends
The Wasmer [runtime](./runtime) is designed to support multiple compiler backends, allowing the user
to tune the codegen properties (compile speed, performance, etc) to fit your usecase best.
to tune the codegen properties (compile speed, performance, etc) to best fit their use case.
Currently, we support a Cranelift compiler backend:
Currently, we support multiple backends for compiling WebAssembly to machine code:
- [clif-backend](./clif-backend/): The integration of Wasmer with Cranelift
- [clif-backend](./clif-backend/): Cranelift backend
- [llvm-backend](./llvm-backend/): LLVM backend

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-clif-backend"
version = "0.1.2"
version = "0.2.0"
description = "Wasmer runtime Cranelift compiler backend"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -8,7 +8,7 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.2.0" }
cranelift-native = "0.26.0"
cranelift-codegen = "0.26.0"
cranelift-entity = "0.26.0"
@ -18,25 +18,22 @@ target-lexicon = "0.2.0"
wasmparser = "0.23.0"
byteorder = "1"
nix = "0.13.0"
libc = "0.2.48"
libc = "0.2.49"
rayon = "1.0"
# Dependencies for caching.
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dependencies.serde_bytes]
version = "0.10"
optional = true
# [dependencies.bincode]
# version = "1.0.1"
# optional = true
[dependencies.serde-bench]
version = "0.0.7"
optional = true
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] }
wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.2.0" }
[features]
cache = ["serde", "serde_derive", "serde_bytes", "serde-bench", "wasmer-runtime-core/cache"]
debug = ["wasmer-runtime-core/debug"]
debug = ["wasmer-runtime-core/debug"]

View File

@ -1,16 +1,50 @@
use crate::relocation::{ExternalRelocation, TrapSink};
use hashbrown::HashMap;
use std::sync::Arc;
use wasmer_runtime_core::{
backend::sys::Memory,
cache::{Cache, Error},
module::ModuleInfo,
backend::{sys::Memory, CacheGen},
cache::{Artifact, Error},
module::{ModuleInfo, ModuleInner},
structures::Map,
types::{LocalFuncIndex, SigIndex},
};
use serde_bench::{deserialize, serialize};
pub struct CacheGenerator {
backend_cache: BackendCache,
memory: Arc<Memory>,
}
impl CacheGenerator {
pub fn new(backend_cache: BackendCache, memory: Arc<Memory>) -> Self {
Self {
backend_cache,
memory,
}
}
}
impl CacheGen for CacheGenerator {
fn generate_cache(
&self,
module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), Error> {
let info = Box::new(module.info.clone());
// Clone the memory to a new location. This could take a long time,
// depending on the throughput of your memcpy implementation.
let compiled_code = (*self.memory).clone();
Ok((
info,
self.backend_cache.into_backend_data()?.into_boxed_slice(),
compiled_code,
))
}
}
#[derive(Serialize, Deserialize)]
pub struct TrampolineCache {
#[serde(with = "serde_bytes")]
@ -22,24 +56,24 @@ pub struct TrampolineCache {
pub struct BackendCache {
pub external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
pub offsets: Map<LocalFuncIndex, usize>,
pub trap_sink: TrapSink,
pub trap_sink: Arc<TrapSink>,
pub trampolines: TrampolineCache,
}
impl BackendCache {
pub fn from_cache(cache: Cache) -> Result<(ModuleInfo, Memory, Self), Error> {
pub fn from_cache(cache: Artifact) -> Result<(ModuleInfo, Memory, Self), Error> {
let (info, backend_data, compiled_code) = cache.consume();
let backend_cache = deserialize(backend_data.as_slice())
.map_err(|e| Error::DeserializeError(e.to_string()))?;
let backend_cache =
deserialize(&backend_data).map_err(|e| Error::DeserializeError(e.to_string()))?;
Ok((info, compiled_code, backend_cache))
}
pub fn into_backend_data(self) -> Result<Vec<u8>, Error> {
pub fn into_backend_data(&self) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new();
serialize(&mut buffer, &self).map_err(|e| Error::SerializeError(e.to_string()))?;
serialize(&mut buffer, self).map_err(|e| Error::SerializeError(e.to_string()))?;
Ok(buffer)
}

View File

@ -32,10 +32,10 @@ impl<'env, 'module, 'isa> FuncEnv<'env, 'module, 'isa> {
let mut signature = self.env.signatures[Converter(clif_sig_index).into()].clone();
// Add the vmctx parameter type to it
signature.params.push(ir::AbiParam::special(
self.pointer_type(),
ir::ArgumentPurpose::VMContext,
));
signature.params.insert(
0,
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
);
// Return signature
signature
@ -75,7 +75,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
let local_global_addr = match global_index.local_or_import(self.env.module) {
let local_global_addr = match global_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_global_index) => {
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
@ -145,48 +145,49 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
let (local_memory_ptr_ptr, description) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let local_memory_ptr_offset =
local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>();
(
func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (local_memory_ptr_offset as i64).into(),
let (local_memory_ptr_ptr, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_memories() as i32).into(),
global_type: ptr_type,
}),
self.env.module.info.memories[local_mem_index],
)
}
LocalOrImport::Import(import_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
readonly: true,
});
let local_memory_ptr_offset =
import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>();
let local_memory_ptr_offset =
local_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>();
(
func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (local_memory_ptr_offset as i64).into(),
(
func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (local_memory_ptr_offset as i64).into(),
global_type: ptr_type,
}),
self.env.module.info.memories[local_mem_index],
)
}
LocalOrImport::Import(import_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
global_type: ptr_type,
}),
self.env.module.info.imported_memories[import_mem_index].1,
)
}
};
readonly: true,
});
let local_memory_ptr_offset =
import_mem_index.index() * mem::size_of::<*mut vm::LocalMemory>();
(
func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (local_memory_ptr_offset as i64).into(),
global_type: ptr_type,
}),
self.env.module.info.imported_memories[import_mem_index].1,
)
}
};
let (local_memory_ptr, local_memory_base) = {
let local_memory_ptr = func.create_global_value(ir::GlobalValueData::Load {
@ -253,7 +254,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
let (table_struct_ptr_ptr, description) = match table_index.local_or_import(self.env.module)
let (table_struct_ptr_ptr, description) = match table_index
.local_or_import(&self.env.module.info)
{
LocalOrImport::Local(local_table_index) => {
let tables_base = func.create_global_value(ir::GlobalValueData::Load {
@ -443,6 +445,12 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
pos.ins().symbol_value(ir::types::I64, sig_index_global)
// let dynamic_sigindices_array_ptr = pos.ins().load(
// ptr_type,
// mflags,
// )
// let expected_sig = pos.ins().iconst(ir::types::I32, sig_index.index() as i64);
// self.env.deduplicated[clif_sig_index]
@ -459,8 +467,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
// Build a value list for the indirect call instruction containing the call_args
// and the vmctx parameter.
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx_ptr);
args.extend(call_args.iter().cloned());
Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args))
}
@ -475,9 +483,10 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> {
let callee_index: FuncIndex = Converter(clif_callee_index).into();
let ptr_type = self.pointer_type();
match callee_index.local_or_import(self.env.module) {
LocalOrImport::Local(_) => {
match callee_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_function_index) => {
// this is an internal function
let vmctx = pos
.func
@ -485,13 +494,31 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
.expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx);
args.extend(call_args.iter().cloned());
Ok(pos.ins().call(callee, &args))
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let function_ptr = {
let mflags = ir::MemFlags::trusted();
let function_array_ptr = pos.ins().load(
ptr_type,
mflags,
vmctx,
vm::Ctx::offset_local_functions() as i32,
);
pos.ins().load(
ptr_type,
mflags,
function_array_ptr,
(local_function_index.index() as i32) * 8,
)
};
Ok(pos.ins().call_indirect(sig_ref, function_ptr, &args))
}
LocalOrImport::Import(imported_func_index) => {
let ptr_type = self.pointer_type();
// this is an imported function
let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext);
@ -532,8 +559,8 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(imported_vmctx_addr);
args.extend(call_args.iter().cloned());
Ok(pos
.ins()
@ -559,27 +586,28 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.info.imported_memories[import_mem_index].1,
),
};
let (namespace, mem_index, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.info.imported_memories[import_mem_index].1,
),
};
let name_index = match description.memory_type() {
MemoryType::Dynamic => call_names::DYNAMIC_MEM_GROW,
@ -604,7 +632,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let call_inst = pos
.ins()
.call(mem_grow_func, &[const_mem_index, by_value, vmctx]);
.call(mem_grow_func, &[vmctx, const_mem_index, by_value]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
@ -623,26 +651,27 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (namespace, mem_index, description) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.info.imported_memories[import_mem_index].1,
),
};
let (namespace, mem_index, description) =
match mem_index.local_or_import(&self.env.module.info) {
LocalOrImport::Local(local_mem_index) => (
call_names::LOCAL_NAMESPACE,
local_mem_index.index(),
self.env.module.info.memories[local_mem_index],
),
LocalOrImport::Import(import_mem_index) => (
call_names::IMPORT_NAMESPACE,
import_mem_index.index(),
self.env.module.info.imported_memories[import_mem_index].1,
),
};
let name_index = match description.memory_type() {
MemoryType::Dynamic => call_names::DYNAMIC_MEM_SIZE,
@ -664,7 +693,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let call_inst = pos.ins().call(mem_grow_func, &[const_mem_index, vmctx]);
let call_inst = pos.ins().call(mem_grow_func, &[vmctx, const_mem_index]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}

View File

@ -1,4 +1,3 @@
#[cfg(feature = "cache")]
mod cache;
mod func_env;
mod libcalls;
@ -14,21 +13,18 @@ use cranelift_codegen::{
settings::{self, Configurable},
};
use target_lexicon::Triple;
#[cfg(feature = "cache")]
use wasmer_runtime_core::{
backend::sys::Memory,
cache::{Cache, Error as CacheError},
module::ModuleInfo,
};
use wasmer_runtime_core::cache::{Artifact, Error as CacheError};
use wasmer_runtime_core::{
backend::{Compiler, Token},
error::{CompileError, CompileResult},
module::ModuleInner,
};
#[cfg(feature = "cache")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "cache")]
extern crate rayon;
extern crate serde;
use wasmparser::{self, WasmDecoder};
@ -48,7 +44,7 @@ impl Compiler for CraneliftCompiler {
let isa = get_isa();
let mut module = module::Module::empty();
let mut module = module::Module::new();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
@ -57,41 +53,41 @@ impl Compiler for CraneliftCompiler {
}
/// Create a wasmer Module from an already-compiled cache.
#[cfg(feature = "cache")]
unsafe fn from_cache(&self, cache: Cache, _: Token) -> Result<ModuleInner, CacheError> {
unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
module::Module::from_cache(cache)
}
#[cfg(feature = "cache")]
fn compile_to_backend_cache_data(
&self,
wasm: &[u8],
_: Token,
) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> {
validate(wasm)?;
//
// fn compile_to_backend_cache_data(
// &self,
// wasm: &[u8],
// _: Token,
// ) -> CompileResult<(Box<ModuleInfo>, Vec<u8>, Memory)> {
// validate(wasm)?;
let isa = get_isa();
// let isa = get_isa();
let mut module = module::Module::empty();
let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
// let mut module = module::Module::new(wasm);
// let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
// let func_bodies = module_env.translate(wasm)?;
let (info, backend_cache, compiled_code) = module
.compile_to_backend_cache(&*isa, func_bodies)
.map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e),
})?;
// let (info, backend_cache, compiled_code) = module
// .compile_to_backend_cache(&*isa, func_bodies)
// .map_err(|e| CompileError::InternalError {
// msg: format!("{:?}", e),
// })?;
let buffer =
backend_cache
.into_backend_data()
.map_err(|e| CompileError::InternalError {
msg: format!("{:?}", e),
})?;
// let buffer =
// backend_cache
// .into_backend_data()
// .map_err(|e| CompileError::InternalError {
// msg: format!("{:?}", e),
// })?;
Ok((Box::new(info), buffer, compiled_code))
}
// Ok((Box::new(info), buffer, compiled_code))
// }
}
fn get_isa() -> Box<isa::TargetIsa> {

View File

@ -1,178 +1,122 @@
#[cfg(feature = "cache")]
use crate::cache::BackendCache;
use crate::cache::{BackendCache, CacheGenerator};
use crate::{resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines};
use cranelift_codegen::{ir, isa};
use cranelift_entity::EntityRef;
use cranelift_wasm;
use hashbrown::HashMap;
use std::{
ops::{Deref, DerefMut},
ptr::NonNull,
};
#[cfg(feature = "cache")]
use std::sync::Arc;
use wasmer_runtime_core::cache::{Artifact, Error as CacheError};
use wasmer_runtime_core::{
backend::sys::Memory,
cache::{Cache, Error as CacheError},
};
use wasmer_runtime_core::{
backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper},
error::{CompileResult, RuntimeResult},
backend::Backend,
error::CompileResult,
module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex},
types::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
Value,
},
vm::{self, ImportBacking},
};
struct Placeholder;
impl FuncResolver for Placeholder {
fn get(
&self,
_module: &ModuleInner,
_local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
None
}
}
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_import_backing: &ImportBacking,
_vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
Ok(vec![])
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
}
/// This contains all of the items in a `ModuleInner` except the `func_resolver`.
pub struct Module {
pub module: ModuleInner,
pub info: ModuleInfo,
}
impl Module {
pub fn empty() -> Self {
pub fn new() -> Self {
Self {
module: ModuleInner {
// this is a placeholder
func_resolver: Box::new(Placeholder),
protected_caller: Box::new(Placeholder),
info: ModuleInfo {
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
info: ModuleInfo {
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
imported_functions: Map::new(),
imported_memories: Map::new(),
imported_tables: Map::new(),
imported_globals: Map::new(),
imported_functions: Map::new(),
imported_memories: Map::new(),
imported_tables: Map::new(),
imported_globals: Map::new(),
exports: HashMap::new(),
exports: HashMap::new(),
data_initializers: Vec::new(),
elem_initializers: Vec::new(),
data_initializers: Vec::new(),
elem_initializers: Vec::new(),
start_func: None,
start_func: None,
func_assoc: Map::new(),
signatures: Map::new(),
backend: Backend::Cranelift,
func_assoc: Map::new(),
signatures: Map::new(),
backend: Backend::Cranelift,
namespace_table: StringTable::new(),
name_table: StringTable::new(),
},
namespace_table: StringTable::new(),
name_table: StringTable::new(),
},
}
}
pub fn compile(
mut self,
self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<ModuleInner> {
let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
FuncResolverBuilder::new(isa, functions, &self.info)?;
self.module.func_resolver =
Box::new(func_resolver_builder.finalize(&self.module.info.signatures)?);
let trampolines = Arc::new(Trampolines::new(isa, &self.info));
let trampolines = Trampolines::new(isa, &self.module.info);
let (func_resolver, backend_cache) = func_resolver_builder.finalize(
&self.info.signatures,
Arc::clone(&trampolines),
handler_data.clone(),
)?;
self.module.protected_caller =
Box::new(Caller::new(&self.module.info, handler_data, trampolines));
let protected_caller = Caller::new(&self.info, handler_data, trampolines);
Ok(self.module)
let cache_gen = Box::new(CacheGenerator::new(
backend_cache,
Arc::clone(&func_resolver.memory),
));
Ok(ModuleInner {
func_resolver: Box::new(func_resolver),
protected_caller: Box::new(protected_caller),
cache_gen,
info: self.info,
})
}
#[cfg(feature = "cache")]
pub fn compile_to_backend_cache(
self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> CompileResult<(ModuleInfo, BackendCache, Memory)> {
let (func_resolver_builder, handler_data) =
FuncResolverBuilder::new(isa, functions, &self.module.info)?;
let trampolines = Trampolines::new(isa, &self.module.info);
let trampoline_cache = trampolines.to_trampoline_cache();
let (backend_cache, compiled_code) =
func_resolver_builder.to_backend_cache(trampoline_cache, handler_data);
Ok((self.module.info, backend_cache, compiled_code))
}
#[cfg(feature = "cache")]
pub fn from_cache(cache: Cache) -> Result<ModuleInner, CacheError> {
pub fn from_cache(cache: Artifact) -> Result<ModuleInner, CacheError> {
let (info, compiled_code, backend_cache) = BackendCache::from_cache(cache)?;
let (func_resolver_builder, trampolines, handler_data) =
FuncResolverBuilder::new_from_backend_cache(backend_cache, compiled_code, &info)?;
let func_resolver = Box::new(
func_resolver_builder
.finalize(&info.signatures)
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?,
);
let (func_resolver, backend_cache) = func_resolver_builder
.finalize(
&info.signatures,
Arc::clone(&trampolines),
handler_data.clone(),
)
.map_err(|e| CacheError::Unknown(format!("{:?}", e)))?;
let protected_caller = Box::new(Caller::new(&info, handler_data, trampolines));
let protected_caller = Caller::new(&info, handler_data, trampolines);
let cache_gen = Box::new(CacheGenerator::new(
backend_cache,
Arc::clone(&func_resolver.memory),
));
Ok(ModuleInner {
func_resolver,
protected_caller,
func_resolver: Box::new(func_resolver),
protected_caller: Box::new(protected_caller),
cache_gen,
info,
})
}
}
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&self.module
}
}
impl DerefMut for Module {
fn deref_mut(&mut self) -> &mut ModuleInner {
&mut self.module
}
}
pub struct Converter<T>(pub T);
macro_rules! convert_clif_to_runtime_index {

View File

@ -4,7 +4,6 @@ use crate::{
};
use cranelift_codegen::{ir, isa};
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
use std::sync::Arc;
use wasmer_runtime_core::{
error::{CompileError, CompileResult},
module::{
@ -62,10 +61,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
/// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: &ir::Signature) {
self.signatures.push(sig.clone());
self.module
.info
.signatures
.push(Arc::new(Converter(sig).into()));
self.module.info.signatures.push(Converter(sig).into());
}
/// Return the signature with the given index.
@ -139,7 +135,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
// assert!(!desc.mutable);
let global_index: GlobalIndex = Converter(global_index).into();
let imported_global_index = global_index
.local_or_import(self.module)
.local_or_import(&self.module.info)
.import()
.expect("invalid global initializer when declaring an imported global");
Initializer::GetGlobal(imported_global_index)
@ -249,7 +245,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
@ -319,7 +315,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
Initializer::GetGlobal(match global_index.local_or_import(&self.module.info) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
@ -389,7 +385,7 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let name = ir::ExternalName::user(0, func_index.index() as u32);
let sig = func_env.generate_signature(
self.get_func_type(Converter(func_index.convert_up(self.module)).into()),
self.get_func_type(Converter(func_index.convert_up(&self.module.info)).into()),
);
let mut func = ir::Function::with_name_signature(name, sig);
@ -419,8 +415,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![],
});
@ -457,8 +453,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I32),
],
returns: vec![],
});
@ -476,8 +472,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I64),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::I64),
],
returns: vec![],
});
@ -495,8 +491,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::F32),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F32),
],
returns: vec![],
});
@ -514,8 +510,8 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::F64),
ir::AbiParam::special(ir::types::I64, ir::ArgumentPurpose::VMContext),
ir::AbiParam::new(ir::types::F64),
],
returns: vec![],
});
@ -536,14 +532,14 @@ impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa>
let func_index = pos.ins().iconst(ir::types::I32, func_index.index() as i64);
pos.ins().call(start_debug, &[func_index, vmctx]);
pos.ins().call(start_debug, &[vmctx, func_index]);
for param in new_ebb_params.iter().cloned() {
match pos.func.dfg.value_type(param) {
ir::types::I32 => pos.ins().call(i32_print, &[param, vmctx]),
ir::types::I64 => pos.ins().call(i64_print, &[param, vmctx]),
ir::types::F32 => pos.ins().call(f32_print, &[param, vmctx]),
ir::types::F64 => pos.ins().call(f64_print, &[param, vmctx]),
ir::types::I32 => pos.ins().call(i32_print, &[vmctx, param]),
ir::types::I64 => pos.ins().call(i64_print, &[vmctx, param]),
ir::types::F32 => pos.ins().call(f32_print, &[vmctx, param]),
ir::types::F64 => pos.ins().call(f64_print, &[vmctx, param]),
_ => unimplemented!(),
};
}

View File

@ -22,16 +22,14 @@ pub mod call_names {
pub const DYNAMIC_MEM_SIZE: u32 = 5;
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum Reloc {
Abs8,
X86PCRel4,
X86CallPCRel4,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum LibCall {
Probestack,
CeilF32,
@ -44,8 +42,7 @@ pub enum LibCall {
NearestF64,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ExternalRelocation {
/// The relocation code.
pub reloc: Reloc,
@ -66,8 +63,7 @@ pub struct LocalRelocation {
pub target: FuncIndex,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum VmCallKind {
StaticMemoryGrow,
StaticMemorySize,
@ -79,16 +75,14 @@ pub enum VmCallKind {
DynamicMemorySize,
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum VmCall {
Local(VmCallKind),
Import(VmCallKind),
}
/// Specify the type of relocation
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RelocationType {
Intrinsic(String),
LibCall(LibCall),
@ -218,8 +212,7 @@ impl binemit::RelocSink for RelocSink {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum TrapCode {
StackOverflow,
HeapOutOfBounds,
@ -244,8 +237,7 @@ impl RelocSink {
}
}
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct TrapData {
pub trapcode: TrapCode,
pub srcloc: u32,
@ -253,7 +245,7 @@ pub struct TrapData {
/// Simple implementation of a TrapSink
/// that saves the info for later.
#[cfg_attr(feature = "cache", derive(Serialize, Deserialize))]
#[derive(Serialize, Deserialize)]
pub struct TrapSink {
trap_datas: Vec<(usize, TrapData)>,
}

View File

@ -1,8 +1,4 @@
#[cfg(feature = "cache")]
use crate::{
cache::{BackendCache, TrampolineCache},
trampoline::Trampolines,
};
use crate::{cache::BackendCache, trampoline::Trampolines};
use crate::{
libcalls,
relocation::{
@ -11,6 +7,7 @@ use crate::{
},
signal::HandlerData,
};
use rayon::prelude::*;
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context};
@ -19,7 +16,7 @@ use std::{
ptr::{write_unaligned, NonNull},
sync::Arc,
};
#[cfg(feature = "cache")]
use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::{
self,
@ -42,21 +39,32 @@ extern "C" {
pub fn __chkstk();
}
fn lookup_func(
map: &SliceMap<LocalFuncIndex, usize>,
memory: &Memory,
local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
let offset = *map.get(local_func_index)?;
let ptr = unsafe { memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
#[allow(dead_code)]
pub struct FuncResolverBuilder {
resolver: FuncResolver,
map: Map<LocalFuncIndex, usize>,
memory: Memory,
local_relocs: Map<LocalFuncIndex, Box<[LocalRelocation]>>,
external_relocs: Map<LocalFuncIndex, Box<[ExternalRelocation]>>,
import_len: usize,
}
impl FuncResolverBuilder {
#[cfg(feature = "cache")]
pub fn new_from_backend_cache(
backend_cache: BackendCache,
mut code: Memory,
info: &ModuleInfo,
) -> Result<(Self, Trampolines, HandlerData), CacheError> {
) -> Result<(Self, Arc<Trampolines>, HandlerData), CacheError> {
unsafe {
code.protect(.., Protect::ReadWrite)
.map_err(|e| CacheError::Unknown(e.to_string()))?;
@ -67,61 +75,62 @@ impl FuncResolverBuilder {
Ok((
Self {
resolver: FuncResolver {
map: backend_cache.offsets,
memory: code,
},
map: backend_cache.offsets,
memory: code,
local_relocs: Map::new(),
external_relocs: backend_cache.external_relocs,
import_len: info.imported_functions.len(),
},
Trampolines::from_trampoline_cache(backend_cache.trampolines),
Arc::new(Trampolines::from_trampoline_cache(
backend_cache.trampolines,
)),
handler_data,
))
}
#[cfg(feature = "cache")]
pub fn to_backend_cache(
mut self,
trampolines: TrampolineCache,
handler_data: HandlerData,
) -> (BackendCache, Memory) {
self.relocate_locals();
(
BackendCache {
external_relocs: self.external_relocs,
offsets: self.resolver.map,
trap_sink: handler_data.trap_data,
trampolines,
},
self.resolver.memory,
)
}
pub fn new(
isa: &isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>,
info: &ModuleInfo,
) -> CompileResult<(Self, HandlerData)> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
let mut local_relocs = Map::with_capacity(function_bodies.len());
let mut external_relocs = Map::new();
let num_func_bodies = function_bodies.len();
let mut local_relocs = Map::with_capacity(num_func_bodies);
let mut external_relocs = Map::with_capacity(num_func_bodies);
let mut trap_sink = TrapSink::new();
let mut local_trap_sink = LocalTrapSink::new();
let mut ctx = Context::new();
let compiled_functions: Result<Vec<(Vec<u8>, (RelocSink, LocalTrapSink))>, CompileError> =
function_bodies
.into_vec()
.par_iter()
.map_init(
|| Context::new(),
|ctx, func| {
let mut code_buf = Vec::new();
ctx.func = func.to_owned();
let mut reloc_sink = RelocSink::new();
let mut local_trap_sink = LocalTrapSink::new();
ctx.compile_and_emit(
isa,
&mut code_buf,
&mut reloc_sink,
&mut local_trap_sink,
)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
ctx.clear();
Ok((code_buf, (reloc_sink, local_trap_sink)))
},
)
.collect();
let compiled_functions = compiled_functions?;
let mut total_size = 0;
for (_, func) in function_bodies {
ctx.func = func;
let mut code_buf = Vec::new();
let mut reloc_sink = RelocSink::new();
ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut local_trap_sink)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
ctx.clear();
// We separate into two iterators, one iterable and one into iterable
let (code_bufs, sinks): (Vec<Vec<u8>>, Vec<(RelocSink, LocalTrapSink)>) =
compiled_functions.into_iter().unzip();
for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter())
{
// Clear the local trap sink and consolidate all trap info
// into a single location.
trap_sink.drain_local(total_size, &mut local_trap_sink);
@ -129,7 +138,6 @@ impl FuncResolverBuilder {
// Round up each function's size to pointer alignment.
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
compiled_functions.push(code_buf);
local_relocs.push(reloc_sink.local_relocs.into_boxed_slice());
external_relocs.push(reloc_sink.external_relocs.into_boxed_slice());
}
@ -156,10 +164,10 @@ impl FuncResolverBuilder {
*i = 0xCC;
}
let mut map = Map::with_capacity(compiled_functions.len());
let mut map = Map::with_capacity(num_func_bodies);
let mut previous_end = 0;
for compiled in compiled_functions.iter() {
for compiled in code_bufs.iter() {
let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>());
unsafe {
memory.as_slice_mut()[previous_end..previous_end + compiled.len()]
@ -169,10 +177,12 @@ impl FuncResolverBuilder {
previous_end = new_end;
}
let handler_data = HandlerData::new(trap_sink, memory.as_ptr() as _, memory.size());
let handler_data =
HandlerData::new(Arc::new(trap_sink), memory.as_ptr() as _, memory.size());
let mut func_resolver_builder = Self {
resolver: FuncResolver { map, memory },
map,
memory,
local_relocs,
external_relocs,
import_len: info.imported_functions.len(),
@ -187,12 +197,15 @@ impl FuncResolverBuilder {
for (index, relocs) in self.local_relocs.iter() {
for ref reloc in relocs.iter() {
let local_func_index = LocalFuncIndex::new(reloc.target.index() - self.import_len);
let target_func_address =
self.resolver.lookup(local_func_index).unwrap().as_ptr() as usize;
let target_func_address = lookup_func(&self.map, &self.memory, local_func_index)
.unwrap()
.as_ptr() as usize;
// We need the address of the current function
// because these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr() as usize;
let func_addr = lookup_func(&self.map, &self.memory, index)
.unwrap()
.as_ptr() as usize;
unsafe {
let reloc_address = func_addr + reloc.offset as usize;
@ -208,8 +221,10 @@ impl FuncResolverBuilder {
pub fn finalize(
mut self,
signatures: &SliceMap<SigIndex, Arc<FuncSig>>,
) -> CompileResult<FuncResolver> {
signatures: &SliceMap<SigIndex, FuncSig>,
trampolines: Arc<Trampolines>,
handler_data: HandlerData,
) -> CompileResult<(FuncResolver, BackendCache)> {
for (index, relocs) in self.external_relocs.iter() {
for ref reloc in relocs.iter() {
let target_func_address: isize = match reloc.target {
@ -223,9 +238,9 @@ impl FuncResolverBuilder {
LibCall::TruncF64 => libcalls::truncf64 as isize,
LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
#[cfg(all(target_pointer_width = "64", target_os = "windows"))]
Probestack => __chkstk as isize,
LibCall::Probestack => __chkstk as isize,
#[cfg(not(target_os = "windows"))]
Probestack => __rust_probestack as isize,
LibCall::Probestack => __rust_probestack as isize,
},
RelocationType::Intrinsic(ref name) => match name.as_str() {
"i32print" => i32_print as isize,
@ -273,15 +288,17 @@ impl FuncResolverBuilder {
},
},
RelocationType::Signature(sig_index) => {
let sig_index =
SigRegistry.lookup_sig_index(Arc::clone(&signatures[sig_index]));
let signature = SigRegistry.lookup_signature_ref(&signatures[sig_index]);
let sig_index = SigRegistry.lookup_sig_index(signature);
sig_index.index() as _
}
};
// We need the address of the current function
// because some of these calls are relative.
let func_addr = self.resolver.lookup(index).unwrap().as_ptr();
let func_addr = lookup_func(&self.map, &self.memory, index)
.unwrap()
.as_ptr() as usize;
// Determine relocation type and apply relocation.
match reloc.reloc {
@ -289,9 +306,9 @@ impl FuncResolverBuilder {
let ptr_to_write = (target_func_address as u64)
.checked_add(reloc.addend as u64)
.unwrap();
let empty_space_offset = self.resolver.map[index] + reloc.offset as usize;
let empty_space_offset = self.map[index] + reloc.offset as usize;
let ptr_slice = unsafe {
&mut self.resolver.memory.as_slice_mut()
&mut self.memory.as_slice_mut()
[empty_space_offset..empty_space_offset + 8]
};
LittleEndian::write_u64(ptr_slice, ptr_to_write);
@ -309,29 +326,35 @@ impl FuncResolverBuilder {
}
unsafe {
self.resolver
.memory
self.memory
.protect(.., Protect::ReadExec)
.map_err(|e| CompileError::InternalError { msg: e.to_string() })?;
}
Ok(self.resolver)
let backend_cache = BackendCache {
external_relocs: self.external_relocs.clone(),
offsets: self.map.clone(),
trap_sink: handler_data.trap_data,
trampolines: trampolines.to_trampoline_cache(),
};
Ok((
FuncResolver {
map: self.map,
memory: Arc::new(self.memory),
},
backend_cache,
))
}
}
unsafe impl Sync for FuncResolver {}
unsafe impl Send for FuncResolver {}
/// Resolves a function index to a function address.
pub struct FuncResolver {
map: Map<LocalFuncIndex, usize>,
memory: Memory,
}
impl FuncResolver {
fn lookup(&self, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self.map.get(local_func_index)?;
let ptr = unsafe { self.memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
pub(crate) memory: Arc<Memory>,
}
// Implements FuncResolver trait.
@ -341,7 +364,7 @@ impl backend::FuncResolver for FuncResolver {
_module: &wasmer_runtime_core::module::ModuleInner,
index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
self.lookup(index)
lookup_func(&self.map, &self.memory, index)
}
}
@ -350,21 +373,21 @@ fn round_up(n: usize, multiple: usize) -> usize {
(n + multiple - 1) & !(multiple - 1)
}
extern "C" fn i32_print(n: i32) {
extern "C" fn i32_print(_ctx: &mut vm::Ctx, n: i32) {
print!(" i32: {},", n);
}
extern "C" fn i64_print(n: i64) {
extern "C" fn i64_print(_ctx: &mut vm::Ctx, n: i64) {
print!(" i64: {},", n);
}
extern "C" fn f32_print(n: f32) {
extern "C" fn f32_print(_ctx: &mut vm::Ctx, n: f32) {
print!(" f32: {},", n);
}
extern "C" fn f64_print(n: f64) {
extern "C" fn f64_print(_ctx: &mut vm::Ctx, n: f64) {
print!(" f64: {},", n);
}
extern "C" fn start_debug(func_index: u32) {
extern "C" fn start_debug(_ctx: &mut vm::Ctx, func_index: u32) {
print!("func ({}), args: [", func_index);
}
extern "C" fn end_debug() {
extern "C" fn end_debug(_ctx: &mut vm::Ctx) {
println!(" ]");
}

View File

@ -2,7 +2,7 @@ use crate::relocation::{TrapData, TrapSink};
use crate::trampoline::Trampolines;
use hashbrown::HashSet;
use libc::c_void;
use std::{cell::Cell, sync::Arc};
use std::{any::Any, cell::Cell, sync::Arc};
use wasmer_runtime_core::{
backend::{ProtectedCaller, Token, UserTrapper},
error::RuntimeResult,
@ -25,14 +25,14 @@ pub use self::unix::*;
pub use self::windows::*;
thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<String>> = Cell::new(None);
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
}
pub struct Trapper;
impl UserTrapper for Trapper {
unsafe fn do_early_trap(&self, msg: String) -> ! {
TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg)));
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
trigger_trap()
}
}
@ -40,11 +40,15 @@ impl UserTrapper for Trapper {
pub struct Caller {
func_export_set: HashSet<FuncIndex>,
handler_data: HandlerData,
trampolines: Trampolines,
trampolines: Arc<Trampolines>,
}
impl Caller {
pub fn new(module: &ModuleInfo, handler_data: HandlerData, trampolines: Trampolines) -> Self {
pub fn new(
module: &ModuleInfo,
handler_data: HandlerData,
trampolines: Arc<Trampolines>,
) -> Self {
let mut func_export_set = HashSet::new();
for export_index in module.exports.values() {
if let ExportIndex::Func(func_index) = export_index {
@ -110,6 +114,7 @@ impl ProtectedCaller for Caller {
.lookup(sig_index)
.expect("that trampoline doesn't exist");
#[cfg(not(target_os = "windows"))]
call_protected(&self.handler_data, || unsafe {
// Leap of faith.
trampoline(
@ -120,6 +125,17 @@ impl ProtectedCaller for Caller {
);
})?;
// the trampoline is called from C on windows
#[cfg(target_os = "windows")]
call_protected(
&self.handler_data,
trampoline,
vmctx_ptr,
func_ptr,
param_vec.as_ptr(),
return_vec.as_mut_ptr(),
)?;
Ok(return_vec
.iter()
.zip(signature.returns().iter())
@ -137,18 +153,18 @@ impl ProtectedCaller for Caller {
}
}
fn get_func_from_index(
module: &ModuleInner,
fn get_func_from_index<'a>(
module: &'a ModuleInner,
import_backing: &ImportBacking,
func_index: FuncIndex,
) -> (*const vm::Func, Context, Arc<FuncSig>, SigIndex) {
) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) {
let sig_index = *module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(module) {
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
@ -167,7 +183,7 @@ fn get_func_from_index(
}
};
let signature = Arc::clone(&module.info.signatures[sig_index]);
let signature = &module.info.signatures[sig_index];
(func_ptr, ctx, signature, sig_index)
}
@ -175,15 +191,16 @@ fn get_func_from_index(
unsafe impl Send for HandlerData {}
unsafe impl Sync for HandlerData {}
#[derive(Clone)]
pub struct HandlerData {
pub trap_data: TrapSink,
pub trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void,
exec_buffer_size: usize,
}
impl HandlerData {
pub fn new(
trap_data: TrapSink,
trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void,
exec_buffer_size: usize,
) -> Self {

View File

@ -18,11 +18,7 @@ use nix::sys::signal::{
use std::cell::{Cell, UnsafeCell};
use std::ptr;
use std::sync::Once;
use wasmer_runtime_core::{
error::{RuntimeError, RuntimeResult},
structures::TypedIndex,
types::{MemoryIndex, TableIndex},
};
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
@ -79,8 +75,8 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
if signum != 0 {
*jmp_buf = prev_jmp_buf;
if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(RuntimeError::User { msg })
if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(RuntimeError::Panic { data })
} else {
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
@ -91,28 +87,28 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
{
Err(match Signal::from_c_int(signum) {
Ok(SIGILL) => match trapcode {
TrapCode::BadSignature => RuntimeError::IndirectCallSignature {
table: TableIndex::new(0),
TrapCode::BadSignature => RuntimeError::Trap {
msg: "incorrect call_indirect signature".into(),
},
TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull {
table: TableIndex::new(0),
TrapCode::IndirectCallToNull => RuntimeError::Trap {
msg: "indirect call to null".into(),
},
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0),
addr: None,
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds {
table: TableIndex::new(0),
TrapCode::TableOutOfBounds => RuntimeError::Trap {
msg: "table out-of-bounds access".into(),
},
_ => RuntimeError::Unknown {
msg: "unknown trap".to_string(),
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
},
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0),
addr: None,
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
Ok(SIGFPE) => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
},
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
_ => unimplemented!(),
}
.into())
@ -126,8 +122,8 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
_ => "unkown trapped signal",
};
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown {
msg: format!("trap at {:p} - {}", faulting_addr, signal),
Err(RuntimeError::Trap {
msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
}
.into())
}

View File

@ -1,10 +1,115 @@
use crate::relocation::{TrapCode, TrapData};
use crate::signal::HandlerData;
use wasmer_runtime_core::error::RuntimeResult;
use crate::trampoline::Trampoline;
use std::cell::Cell;
use std::ffi::c_void;
use std::ptr;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::vm::Func;
use wasmer_runtime_core::{
error::{RuntimeError, RuntimeResult},
structures::TypedIndex,
types::{MemoryIndex, TableIndex},
};
use wasmer_win_exception_handler::CallProtectedData;
pub use wasmer_win_exception_handler::_call_protected;
use winapi::shared::minwindef::DWORD;
use winapi::um::minwinbase::{
EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DENORMAL_OPERAND, EXCEPTION_FLT_DIVIDE_BY_ZERO,
EXCEPTION_FLT_INEXACT_RESULT, EXCEPTION_FLT_INVALID_OPERATION, EXCEPTION_FLT_OVERFLOW,
EXCEPTION_FLT_STACK_CHECK, EXCEPTION_FLT_UNDERFLOW, EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_INT_OVERFLOW, EXCEPTION_STACK_OVERFLOW,
};
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
unimplemented!("TODO");
thread_local! {
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
}
pub fn call_protected(
handler_data: &HandlerData,
trampoline: Trampoline,
ctx: *mut Ctx,
func: *const Func,
param_vec: *const u64,
return_vec: *mut u64,
) -> RuntimeResult<()> {
// TODO: trap early
// user code error
// if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
// return Err(RuntimeError::User { msg });
// }
let result = _call_protected(trampoline, ctx, func, param_vec, return_vec);
if let Ok(_) = result {
return Ok(());
}
let CallProtectedData {
code: signum,
exceptionAddress: exception_address,
instructionPointer: instruction_pointer,
} = result.unwrap_err();
if let Some(TrapData {
trapcode,
srcloc: _,
}) = handler_data.lookup(instruction_pointer as _)
{
Err(match signum as DWORD {
EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
TrapCode::BadSignature => RuntimeError::Trap {
msg: "incorrect call_indirect signature".into(),
},
TrapCode::IndirectCallToNull => RuntimeError::Trap {
msg: "indirect call to null".into(),
},
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
TrapCode::TableOutOfBounds => RuntimeError::Trap {
msg: "table out-of-bounds access".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
},
EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap {
msg: "stack overflow trap".into(),
},
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
}
.into())
} else {
let signal = match signum as DWORD {
EXCEPTION_FLT_DENORMAL_OPERAND
| EXCEPTION_FLT_DIVIDE_BY_ZERO
| EXCEPTION_FLT_INEXACT_RESULT
| EXCEPTION_FLT_INVALID_OPERATION
| EXCEPTION_FLT_OVERFLOW
| EXCEPTION_FLT_STACK_CHECK
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
_ => "unkown trapped signal",
};
Err(RuntimeError::Trap {
msg: format!("unknown trap at {} - {}", exception_address, signal).into(),
}
.into())
}
}
pub unsafe fn trigger_trap() -> ! {
unimplemented!("TODO");
// TODO
unimplemented!();
}

View File

@ -1,4 +1,3 @@
#[cfg(feature = "cache")]
use crate::cache::TrampolineCache;
use cranelift_codegen::{
binemit::{NullTrapSink, Reloc, RelocSink},
@ -7,6 +6,7 @@ use cranelift_codegen::{
isa, Context,
};
use hashbrown::HashMap;
use std::ffi::c_void;
use std::{iter, mem};
use wasmer_runtime_core::{
backend::sys::{Memory, Protect},
@ -23,13 +23,15 @@ impl RelocSink for NullRelocSink {
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
}
pub type Trampoline =
unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void;
pub struct Trampolines {
memory: Memory,
offsets: HashMap<SigIndex, usize>,
}
impl Trampolines {
#[cfg(feature = "cache")]
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
// pub struct TrampolineCache {
// #[serde(with = "serde_bytes")]
@ -53,8 +55,7 @@ impl Trampolines {
}
}
#[cfg(feature = "cache")]
pub fn to_trampoline_cache(self) -> TrampolineCache {
pub fn to_trampoline_cache(&self) -> TrampolineCache {
let mut code = vec![0; self.memory.size()];
unsafe {
@ -63,7 +64,7 @@ impl Trampolines {
TrampolineCache {
code,
offsets: self.offsets,
offsets: self.offsets.clone(),
}
}
@ -138,10 +139,7 @@ impl Trampolines {
}
}
pub fn lookup(
&self,
sig_index: SigIndex,
) -> Option<unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64)> {
pub fn lookup(&self, sig_index: SigIndex) -> Option<Trampoline> {
let offset = *self.offsets.get(&sig_index)?;
let ptr = unsafe { self.memory.as_ptr().add(offset) };
@ -169,6 +167,7 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function {
let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(entry_ebb);
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
args_vec.push(vmctx_ptr);
for (index, wasm_ty) in func_sig.params().iter().enumerate() {
let mem_flags = ir::MemFlags::trusted();
@ -180,7 +179,6 @@ fn generate_func(func_sig: &FuncSig) -> ir::Function {
);
args_vec.push(val);
}
args_vec.push(vmctx_ptr);
let call_inst = pos.ins().call_indirect(export_sig_ref, func_ptr, &args_vec);
@ -212,7 +210,9 @@ fn wasm_ty_to_clif(ty: Type) -> ir::types::Type {
}
fn generate_trampoline_signature() -> ir::Signature {
let mut sig = ir::Signature::new(isa::CallConv::SystemV);
let isa = super::get_isa();
let call_convention = isa.default_call_conv();
let mut sig = ir::Signature::new(call_convention);
let ptr_param = ir::AbiParam {
value_type: ir::types::I64,
@ -227,24 +227,25 @@ fn generate_trampoline_signature() -> ir::Signature {
}
fn generate_export_signature(func_sig: &FuncSig) -> ir::Signature {
let mut export_clif_sig = ir::Signature::new(isa::CallConv::SystemV);
let isa = super::get_isa();
let call_convention = isa.default_call_conv();
let mut export_clif_sig = ir::Signature::new(call_convention);
export_clif_sig.params = func_sig
.params()
.iter()
.map(|wasm_ty| ir::AbiParam {
value_type: wasm_ty_to_clif(*wasm_ty),
purpose: ir::ArgumentPurpose::Normal,
extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned,
})
.chain(iter::once(ir::AbiParam {
value_type: ir::types::I64,
purpose: ir::ArgumentPurpose::VMContext,
extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned,
}))
.collect();
let func_sig_iter = func_sig.params().iter().map(|wasm_ty| ir::AbiParam {
value_type: wasm_ty_to_clif(*wasm_ty),
purpose: ir::ArgumentPurpose::Normal,
extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned,
});
export_clif_sig.params = iter::once(ir::AbiParam {
value_type: ir::types::I64,
purpose: ir::ArgumentPurpose::VMContext,
extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned,
})
.chain(func_sig_iter)
.collect();
export_clif_sig.returns = func_sig
.returns()

View File

@ -1,6 +1,6 @@
[package]
name = "wasmer-emscripten"
version = "0.1.0"
version = "0.2.1"
description = "Wasmer runtime emscripten implementation library"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
@ -9,14 +9,25 @@ edition = "2018"
build = "build/mod.rs"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.0" }
libc = { git = "https://github.com/rust-lang/libc" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" }
lazy_static = "1.2.0"
libc = "0.2.49"
byteorder = "1"
time = "0.1.41"
[target.'cfg(windows)'.dependencies]
rand = "0.6"
[dev-dependencies]
wasmer-clif-backend = { path = "../clif-backend", version = "0.1.0" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.2.0" }
wabt = "0.7.2"
[target.'cfg(not(windows))'.dev-dependencies]
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.1.0" }
[build-dependencies]
glob = "0.2.11"
[features]
clif = []
llvm = []

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

17
lib/emscripten/emtests/test_execvp.c vendored Normal file
View File

@ -0,0 +1,17 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "touch";
char arg1[] = "foo.txt";
char* argv[3];
argv[0] = command;
argv[1] = arg1;
argv[2] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

View File

@ -0,0 +1 @@
_execvp

BIN
lib/emscripten/emtests/test_execvp.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "C:\\Windows\\System32\\cmd.exe";
char arg1[] = "echo";
char arg2[] = "foo";
char* argv[4];
argv[0] = command;
argv[1] = arg1;
argv[2] = arg2;
argv[3] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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