mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-09 07:01:20 +00:00
Merge branch 'master' into feature/dynasm-backend
This commit is contained in:
commit
557be77338
@ -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
|
||||
|
@ -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
43
.github/ISSUE_TEMPLATE/---bug-report.md
vendored
Normal 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.
|
26
.github/ISSUE_TEMPLATE/---feature-request.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/---feature-request.md
vendored
Normal 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
16
.github/ISSUE_TEMPLATE/--question.md
vendored
Normal 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
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
/artifacts
|
||||
.DS_Store
|
||||
.idea
|
||||
**/.vscode
|
||||
|
@ -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.
|
||||
|
@ -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
1011
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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 = []
|
||||
|
4
LICENSE
4
LICENSE
@ -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
|
||||
|
16
Makefile
16
Makefile
@ -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"
|
||||
|
||||
|
82
README.md
82
README.md
@ -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
9
bors.toml
Normal 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
26
examples/nginx/LICENSE
Normal 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.
|
||||
*/
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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!(),
|
||||
};
|
||||
}
|
||||
|
@ -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)>,
|
||||
}
|
||||
|
@ -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!(" ]");
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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!();
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 = []
|
BIN
lib/emscripten/emtests/FS_exports.wasm
vendored
BIN
lib/emscripten/emtests/FS_exports.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/clock_gettime.wasm
vendored
BIN
lib/emscripten/emtests/clock_gettime.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/closebitcasts.wasm
vendored
BIN
lib/emscripten/emtests/closebitcasts.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/dyncall.wasm
vendored
BIN
lib/emscripten/emtests/dyncall.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/dyncall_specific.wasm
vendored
BIN
lib/emscripten/emtests/dyncall_specific.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/env.wasm
vendored
BIN
lib/emscripten/emtests/env.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/getValue_setValue.wasm
vendored
BIN
lib/emscripten/emtests/getValue_setValue.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/localtime.wasm
vendored
BIN
lib/emscripten/emtests/localtime.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/modularize_closure_pre.wasm
vendored
BIN
lib/emscripten/emtests/modularize_closure_pre.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/printf.wasm
vendored
BIN
lib/emscripten/emtests/printf.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/puts.wasm
vendored
BIN
lib/emscripten/emtests/puts.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/stackAlloc.wasm
vendored
BIN
lib/emscripten/emtests/stackAlloc.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/stack_overflow.wasm
vendored
BIN
lib/emscripten/emtests/stack_overflow.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_addr_of_stacked.wasm
vendored
BIN
lib/emscripten/emtests/test_addr_of_stacked.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_alloca.wasm
vendored
BIN
lib/emscripten/emtests/test_alloca.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_alloca_stack.wasm
vendored
BIN
lib/emscripten/emtests/test_alloca_stack.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_array2.wasm
vendored
BIN
lib/emscripten/emtests/test_array2.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_array2b.wasm
vendored
BIN
lib/emscripten/emtests/test_array2b.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_atoX.wasm
vendored
BIN
lib/emscripten/emtests/test_atoX.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_atomic.wasm
vendored
BIN
lib/emscripten/emtests/test_atomic.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_atomic_cxx.wasm
vendored
BIN
lib/emscripten/emtests/test_atomic_cxx.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_bsearch.wasm
vendored
BIN
lib/emscripten/emtests/test_bsearch.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_ccall.wasm
vendored
BIN
lib/emscripten/emtests/test_ccall.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_complex.wasm
vendored
BIN
lib/emscripten/emtests/test_complex.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_demangle_stacks.wasm
vendored
BIN
lib/emscripten/emtests/test_demangle_stacks.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_dlmalloc_partial_2.wasm
vendored
BIN
lib/emscripten/emtests/test_dlmalloc_partial_2.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_double_varargs.wasm
vendored
BIN
lib/emscripten/emtests/test_double_varargs.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_em_asm.wasm
vendored
BIN
lib/emscripten/emtests/test_em_asm.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_em_asm_2.wasm
vendored
BIN
lib/emscripten/emtests/test_em_asm_2.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_em_asm_signatures.wasm
vendored
BIN
lib/emscripten/emtests/test_em_asm_signatures.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_em_asm_unicode.wasm
vendored
BIN
lib/emscripten/emtests/test_em_asm_unicode.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_em_js.wasm
vendored
BIN
lib/emscripten/emtests/test_em_js.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_emscripten_api.wasm
vendored
BIN
lib/emscripten/emtests/test_emscripten_api.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_erf.wasm
vendored
BIN
lib/emscripten/emtests/test_erf.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_errar.wasm
vendored
BIN
lib/emscripten/emtests/test_errar.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_2.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_2.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_convert.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_convert.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_libcxx.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_libcxx.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_multi.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_multi.wasm
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_primary.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_primary.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_refcount.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_refcount.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_resume.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_resume.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_rethrow.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_rethrow.wasm
vendored
Binary file not shown.
Binary file not shown.
BIN
lib/emscripten/emtests/test_exceptions_std.wasm
vendored
BIN
lib/emscripten/emtests/test_exceptions_std.wasm
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
17
lib/emscripten/emtests/test_execvp.c
vendored
Normal file
17
lib/emscripten/emtests/test_execvp.c
vendored
Normal 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;
|
||||
}
|
1
lib/emscripten/emtests/test_execvp.out
vendored
Normal file
1
lib/emscripten/emtests/test_execvp.out
vendored
Normal file
@ -0,0 +1 @@
|
||||
_execvp
|
BIN
lib/emscripten/emtests/test_execvp.wasm
vendored
Normal file
BIN
lib/emscripten/emtests/test_execvp.wasm
vendored
Normal file
Binary file not shown.
18
lib/emscripten/emtests/test_execvp_windows.c
vendored
Normal file
18
lib/emscripten/emtests/test_execvp_windows.c
vendored
Normal 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;
|
||||
}
|
BIN
lib/emscripten/emtests/test_execvp_windows.wasm
vendored
Normal file
BIN
lib/emscripten/emtests/test_execvp_windows.wasm
vendored
Normal file
Binary file not shown.
BIN
lib/emscripten/emtests/test_fast_math.wasm
vendored
BIN
lib/emscripten/emtests/test_fast_math.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_flexarray_struct.wasm
vendored
BIN
lib/emscripten/emtests/test_flexarray_struct.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_float32_precise.wasm
vendored
BIN
lib/emscripten/emtests/test_float32_precise.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_float_builtins.wasm
vendored
BIN
lib/emscripten/emtests/test_float_builtins.wasm
vendored
Binary file not shown.
BIN
lib/emscripten/emtests/test_frexp.wasm
vendored
BIN
lib/emscripten/emtests/test_frexp.wasm
vendored
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user