Merge branch 'master' into wasi-common
This commit is contained in:
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@@ -70,19 +70,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build: [stable, beta, nightly, windows, linux]
|
build: [stable, beta, nightly, windows, macos]
|
||||||
include:
|
include:
|
||||||
- build: stable
|
- build: stable
|
||||||
os: macos-latest
|
os: ubuntu-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
- build: beta
|
- build: beta
|
||||||
os: macos-latest
|
os: ubuntu-latest
|
||||||
rust: beta
|
rust: beta
|
||||||
- build: nightly
|
- build: nightly
|
||||||
os: macos-latest
|
|
||||||
rust: nightly
|
|
||||||
- build: linux
|
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
rust: nightly
|
||||||
|
- build: macos
|
||||||
|
os: macos-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
- build: windows
|
- build: windows
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
@@ -216,7 +216,7 @@ jobs:
|
|||||||
- run: $CENTOS cargo build --release --bin wasmtime --bin wasm2obj
|
- run: $CENTOS cargo build --release --bin wasmtime --bin wasm2obj
|
||||||
shell: bash
|
shell: bash
|
||||||
# Build `libwasmtime_api.so`
|
# Build `libwasmtime_api.so`
|
||||||
- run: $CENTOS cargo build --release --features wasm-c-api --manifest-path wasmtime-api/Cargo.toml
|
- run: $CENTOS cargo build --release --manifest-path wasmtime-api/Cargo.toml
|
||||||
shell: bash
|
shell: bash
|
||||||
# Test what we just built
|
# Test what we just built
|
||||||
- run: $CENTOS cargo test --features wasi-common/wasm_tests --release --all --exclude lightbeam --exclude wasmtime-wasi-c --exclude wasmtime-py --exclude wasmtime-api
|
- run: $CENTOS cargo test --features wasi-common/wasm_tests --release --all --exclude lightbeam --exclude wasmtime-wasi-c --exclude wasmtime-py --exclude wasmtime-api
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ libc = "0.2.60"
|
|||||||
rayon = "1.1"
|
rayon = "1.1"
|
||||||
wasm-webidl-bindings = "0.6"
|
wasm-webidl-bindings = "0.6"
|
||||||
|
|
||||||
# build.rs tests whether to enable a workaround for the libc strtof function.
|
[build-dependencies]
|
||||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
anyhow = "1.0.19"
|
||||||
libc = "0.2.60"
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -35,34 +35,24 @@ of ongoing research.
|
|||||||
Additional goals for Wasmtime include:
|
Additional goals for Wasmtime include:
|
||||||
- Support a variety of host APIs (not just WASI), with fast calling sequences,
|
- Support a variety of host APIs (not just WASI), with fast calling sequences,
|
||||||
and develop proposals for additional API modules to be part of WASI.
|
and develop proposals for additional API modules to be part of WASI.
|
||||||
- Implement the [proposed WebAssembly C API].
|
- Facilitate development and testing around the [Cranelift] and [Lightbeam] JITs,
|
||||||
- Facilitate testing, experimentation, and development around the [Cranelift] and
|
and other WebAssembly execution strategies.
|
||||||
[Lightbeam] JITs.
|
|
||||||
- Develop a native ABI used for compiling WebAssembly suitable for use in both
|
- Develop a native ABI used for compiling WebAssembly suitable for use in both
|
||||||
JIT and AOT to native object files.
|
JIT and AOT to native object files.
|
||||||
|
|
||||||
[proposed WebAssembly C API]: https://github.com/rossberg/wasm-c-api
|
|
||||||
[Cranelift]: https://github.com/CraneStation/cranelift
|
[Cranelift]: https://github.com/CraneStation/cranelift
|
||||||
[Lightbeam]: https://github.com/CraneStation/lightbeam
|
[Lightbeam]: https://github.com/CraneStation/wasmtime/tree/master/lightbeam
|
||||||
|
|
||||||
#### Including Wasmtime in your project
|
#### Including Wasmtime in your project
|
||||||
Wasmtime exposes an API for JIT compilation through the `wasmtime-jit` subcrate, which depends on `wasmtime-environ` and `wasmtime-runtime` for the ABI and runtime support respectively. However, this API is not documented and subject to change. Please use at your own risk!
|
|
||||||
|
|
||||||
Build the individual crates as such:
|
Wasmtime exposes an API for embedding as a library through the `wasmtime-api` subcrate,
|
||||||
|
which contains both a [high-level and safe Rust API], as well as a C-compatible API
|
||||||
|
compatible with the [proposed WebAssembly C API].
|
||||||
|
|
||||||
```
|
For more information, see the [Rust API embedding chapter] of the Wasmtime documentation.
|
||||||
cargo build --package wasmtime-jit
|
|
||||||
```
|
|
||||||
|
|
||||||
Wasmtime does not currently publish these crates on crates.io. They may be included as a git dependency, like this:
|
[high-level and safe Rust API]: https://docs.rs/wasmtime-api/
|
||||||
|
[proposed WebAssembly C API]: https://github.com/WebAssembly/wasm-c-api
|
||||||
```toml
|
[Rust API embedding chapter]: https://cranestation.github.io/wasmtime/embed-rust.html
|
||||||
[dependencies]
|
|
||||||
wasmtime-environ = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" }
|
|
||||||
wasmtime-runtime = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" }
|
|
||||||
wasmtime-jit = { git = "https://github.com/CraneStation/wasmtime", rev = "somecommithash" }
|
|
||||||
```
|
|
||||||
|
|
||||||
All three crates must be specified as dependencies for `wasmtime-jit` to build correctly, at the moment.
|
|
||||||
|
|
||||||
It's Wasmtime.
|
It's Wasmtime.
|
||||||
|
|||||||
245
build.rs
245
build.rs
@@ -3,108 +3,105 @@
|
|||||||
//! By generating a separate `#[test]` test for each file, we allow cargo test
|
//! By generating a separate `#[test]` test for each file, we allow cargo test
|
||||||
//! to automatically run the files in parallel.
|
//! to automatically run the files in parallel.
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{read_dir, File};
|
use std::fmt::Write;
|
||||||
use std::io::{self, Write};
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> anyhow::Result<()> {
|
||||||
let out_dir =
|
let out_dir = PathBuf::from(
|
||||||
PathBuf::from(env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"));
|
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
|
||||||
let mut out = File::create(out_dir.join("wast_testsuite_tests.rs"))
|
);
|
||||||
.expect("error generating test source file");
|
let mut out = String::new();
|
||||||
|
|
||||||
for strategy in &[
|
for strategy in &[
|
||||||
"Cranelift",
|
"Cranelift",
|
||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
"Lightbeam",
|
"Lightbeam",
|
||||||
] {
|
] {
|
||||||
writeln!(out, "#[cfg(test)]").expect("generating tests");
|
writeln!(out, "#[cfg(test)]")?;
|
||||||
writeln!(out, "#[allow(non_snake_case)]").expect("generating tests");
|
writeln!(out, "#[allow(non_snake_case)]")?;
|
||||||
writeln!(out, "mod {} {{", strategy).expect("generating tests");
|
writeln!(out, "mod {} {{", strategy)?;
|
||||||
|
|
||||||
test_directory(&mut out, "misc_testsuite", strategy).expect("generating tests");
|
test_directory(&mut out, "misc_testsuite", strategy)?;
|
||||||
test_directory(&mut out, "spec_testsuite", strategy).expect("generating tests");
|
let spec_tests = test_directory(&mut out, "spec_testsuite", strategy)?;
|
||||||
// Skip running spec_testsuite tests if the submodule isn't checked out.
|
// Skip running spec_testsuite tests if the submodule isn't checked
|
||||||
if read_dir("spec_testsuite")
|
// out.
|
||||||
.expect("reading testsuite directory")
|
if spec_tests > 0 {
|
||||||
.next()
|
start_test_module(&mut out, "simd")?;
|
||||||
.is_some()
|
write_testsuite_tests(
|
||||||
{
|
|
||||||
test_file(
|
|
||||||
&mut out,
|
&mut out,
|
||||||
&to_os_path(&["spec_testsuite", "proposals", "simd", "simd_address.wast"]),
|
"spec_testsuite/proposals/simd/simd_address.wast",
|
||||||
|
"simd",
|
||||||
strategy,
|
strategy,
|
||||||
)
|
)?;
|
||||||
.expect("generating tests");
|
write_testsuite_tests(
|
||||||
test_file(
|
|
||||||
&mut out,
|
&mut out,
|
||||||
&to_os_path(&["spec_testsuite", "proposals", "simd", "simd_align.wast"]),
|
"spec_testsuite/proposals/simd/simd_align.wast",
|
||||||
|
"simd",
|
||||||
strategy,
|
strategy,
|
||||||
)
|
)?;
|
||||||
.expect("generating tests");
|
write_testsuite_tests(
|
||||||
test_file(
|
|
||||||
&mut out,
|
&mut out,
|
||||||
&to_os_path(&["spec_testsuite", "proposals", "simd", "simd_const.wast"]),
|
"spec_testsuite/proposals/simd/simd_const.wast",
|
||||||
|
"simd",
|
||||||
strategy,
|
strategy,
|
||||||
)
|
)?;
|
||||||
.expect("generating tests");
|
finish_test_module(&mut out)?;
|
||||||
|
|
||||||
let multi_value_suite = &to_os_path(&["spec_testsuite", "proposals", "multi-value"]);
|
test_directory(&mut out, "spec_testsuite/proposals/multi-value", strategy)
|
||||||
test_directory(&mut out, &multi_value_suite, strategy).expect("generating tests");
|
.expect("generating tests");
|
||||||
} else {
|
} else {
|
||||||
println!("cargo:warning=The spec testsuite is disabled. To enable, run `git submodule update --remote`.");
|
println!("cargo:warning=The spec testsuite is disabled. To enable, run `git submodule update --remote`.");
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(out, "}}").expect("generating tests");
|
writeln!(out, "}}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write out our auto-generated tests and opportunistically format them with
|
||||||
|
// `rustfmt` if it's installed.
|
||||||
|
let output = out_dir.join("wast_testsuite_tests.rs");
|
||||||
|
fs::write(&output, out)?;
|
||||||
|
drop(Command::new("rustfmt").arg(&output).status());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for creating OS-independent paths.
|
fn test_directory(
|
||||||
fn to_os_path(components: &[&str]) -> String {
|
out: &mut String,
|
||||||
let path: PathBuf = components.iter().collect();
|
path: impl AsRef<Path>,
|
||||||
path.display().to_string()
|
strategy: &str,
|
||||||
}
|
) -> anyhow::Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
fn test_directory(out: &mut File, path: &str, strategy: &str) -> io::Result<()> {
|
let mut dir_entries: Vec<_> = path
|
||||||
let mut dir_entries: Vec<_> = read_dir(path)
|
.read_dir()
|
||||||
.expect("reading testsuite directory")
|
.context(format!("failed to read {:?}", path))?
|
||||||
.map(|r| r.expect("reading testsuite directory entry"))
|
.map(|r| r.expect("reading testsuite directory entry"))
|
||||||
.filter(|dir_entry| {
|
.filter_map(|dir_entry| {
|
||||||
let p = dir_entry.path();
|
let p = dir_entry.path();
|
||||||
if let Some(ext) = p.extension() {
|
let ext = p.extension()?;
|
||||||
// Only look at wast files.
|
// Only look at wast files.
|
||||||
if ext == "wast" {
|
if ext != "wast" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
// Ignore files starting with `.`, which could be editor temporary files
|
// Ignore files starting with `.`, which could be editor temporary files
|
||||||
if let Some(stem) = p.file_stem() {
|
if p.file_stem()?.to_str()?.starts_with(".") {
|
||||||
if let Some(stemstr) = stem.to_str() {
|
return None;
|
||||||
if !stemstr.starts_with('.') {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
Some(p)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
dir_entries.sort_by_key(|dir| dir.path());
|
dir_entries.sort();
|
||||||
|
|
||||||
let testsuite = &extract_name(path);
|
let testsuite = &extract_name(path);
|
||||||
start_test_module(out, testsuite)?;
|
start_test_module(out, testsuite)?;
|
||||||
for dir_entry in dir_entries {
|
for entry in dir_entries.iter() {
|
||||||
write_testsuite_tests(out, &dir_entry.path(), testsuite, strategy)?;
|
write_testsuite_tests(out, entry, testsuite, strategy)?;
|
||||||
}
|
}
|
||||||
finish_test_module(out)
|
finish_test_module(out)?;
|
||||||
}
|
Ok(dir_entries.len())
|
||||||
|
|
||||||
fn test_file(out: &mut File, testfile: &str, strategy: &str) -> io::Result<()> {
|
|
||||||
let path = Path::new(testfile);
|
|
||||||
let testsuite = format!("single_test_{}", extract_name(path));
|
|
||||||
start_test_module(out, &testsuite)?;
|
|
||||||
write_testsuite_tests(out, path, &testsuite, strategy)?;
|
|
||||||
finish_test_module(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a valid Rust identifier from the stem of a path.
|
/// Extract a valid Rust identifier from the stem of a path.
|
||||||
@@ -118,63 +115,38 @@ fn extract_name(path: impl AsRef<Path>) -> String {
|
|||||||
.replace("/", "_")
|
.replace("/", "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_test_module(out: &mut File, testsuite: &str) -> io::Result<()> {
|
fn start_test_module(out: &mut String, testsuite: &str) -> anyhow::Result<()> {
|
||||||
writeln!(out, " mod {} {{", testsuite)?;
|
writeln!(out, "mod {} {{", testsuite)?;
|
||||||
writeln!(
|
Ok(())
|
||||||
out,
|
|
||||||
" use super::super::{{native_isa, Path, WastContext, Compiler, Features, CompilationStrategy}};"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_test_module(out: &mut File) -> io::Result<()> {
|
fn finish_test_module(out: &mut String) -> anyhow::Result<()> {
|
||||||
writeln!(out, " }}")
|
out.push_str("}\n");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_testsuite_tests(
|
fn write_testsuite_tests(
|
||||||
out: &mut File,
|
out: &mut String,
|
||||||
path: &Path,
|
path: impl AsRef<Path>,
|
||||||
testsuite: &str,
|
testsuite: &str,
|
||||||
strategy: &str,
|
strategy: &str,
|
||||||
) -> io::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
println!("cargo:rerun-if-changed={}", path.display());
|
||||||
let testname = extract_name(path);
|
let testname = extract_name(path);
|
||||||
|
|
||||||
writeln!(out, " #[test]")?;
|
writeln!(out, "#[test]")?;
|
||||||
if ignore(testsuite, &testname, strategy) {
|
if ignore(testsuite, &testname, strategy) {
|
||||||
writeln!(out, " #[ignore]")?;
|
writeln!(out, "#[ignore]")?;
|
||||||
}
|
}
|
||||||
writeln!(out, " fn r#{}() {{", &testname)?;
|
writeln!(out, "fn r#{}() -> anyhow::Result<()> {{", &testname)?;
|
||||||
writeln!(out, " let isa = native_isa();")?;
|
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
" let compiler = Compiler::new(isa, CompilationStrategy::{});",
|
"crate::run_wast(r#\"{}\"#, crate::CompilationStrategy::{})",
|
||||||
|
path.display(),
|
||||||
strategy
|
strategy
|
||||||
)?;
|
)?;
|
||||||
writeln!(
|
writeln!(out, "}}")?;
|
||||||
out,
|
|
||||||
" let features = Features {{ simd: {}, multi_value: {}, ..Default::default() }};",
|
|
||||||
testsuite.contains("simd"),
|
|
||||||
testsuite.contains("multi_value")
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
out,
|
|
||||||
" let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);"
|
|
||||||
)?;
|
|
||||||
writeln!(out, " wast_context")?;
|
|
||||||
writeln!(out, " .register_spectest()")?;
|
|
||||||
writeln!(
|
|
||||||
out,
|
|
||||||
" .expect(\"instantiating \\\"spectest\\\"\");"
|
|
||||||
)?;
|
|
||||||
writeln!(out, " wast_context")?;
|
|
||||||
write!(out, " .run_file(Path::new(\"")?;
|
|
||||||
// Write out the string with escape_debug to prevent special characters such
|
|
||||||
// as backslash from being reinterpreted.
|
|
||||||
for c in path.display().to_string().chars() {
|
|
||||||
write!(out, "{}", c.escape_debug())?;
|
|
||||||
}
|
|
||||||
writeln!(out, "\"))")?;
|
|
||||||
writeln!(out, " .expect(\"error running wast file\");",)?;
|
|
||||||
writeln!(out, " }}")?;
|
|
||||||
writeln!(out)?;
|
writeln!(out)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -206,66 +178,9 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
|
|||||||
// ABI only has a single return register, so we need to wait on full
|
// ABI only has a single return register, so we need to wait on full
|
||||||
// multi-value support in Cranelift.
|
// multi-value support in Cranelift.
|
||||||
(_, _) if is_multi_value => true,
|
(_, _) if is_multi_value => true,
|
||||||
|
|
||||||
// Until Windows unwind information is added we must disable SIMD spec tests that trap.
|
|
||||||
(_, _) if testname.starts_with("simd") => return true,
|
|
||||||
|
|
||||||
("spec_testsuite", "address") => true,
|
|
||||||
("spec_testsuite", "align") => true,
|
|
||||||
("spec_testsuite", "call") => true,
|
|
||||||
("spec_testsuite", "call_indirect") => true,
|
|
||||||
("spec_testsuite", "conversions") => true,
|
|
||||||
("spec_testsuite", "elem") => true,
|
|
||||||
("spec_testsuite", "fac") => true,
|
|
||||||
("spec_testsuite", "func_ptrs") => true,
|
|
||||||
("spec_testsuite", "globals") => true,
|
|
||||||
("spec_testsuite", "i32") => true,
|
|
||||||
("spec_testsuite", "i64") => true,
|
|
||||||
("spec_testsuite", "f32") => true,
|
|
||||||
("spec_testsuite", "f64") => true,
|
|
||||||
("spec_testsuite", "if") => true,
|
|
||||||
("spec_testsuite", "imports") => true,
|
|
||||||
("spec_testsuite", "int_exprs") => true,
|
|
||||||
("spec_testsuite", "linking") => true,
|
|
||||||
("spec_testsuite", "memory_grow") => true,
|
|
||||||
("spec_testsuite", "memory_trap") => true,
|
|
||||||
("spec_testsuite", "resizing") => true,
|
|
||||||
("spec_testsuite", "select") => true,
|
|
||||||
("spec_testsuite", "skip_stack_guard_page") => true,
|
|
||||||
("spec_testsuite", "start") => true,
|
|
||||||
("spec_testsuite", "traps") => true,
|
|
||||||
("spec_testsuite", "unreachable") => true,
|
|
||||||
("spec_testsuite", "unwind") => true,
|
|
||||||
("misc_testsuite", "misc_traps") => true,
|
|
||||||
("misc_testsuite", "stack_overflow") => true,
|
|
||||||
(_, _) => false,
|
(_, _) => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
// Test whether the libc correctly parses the following constant; if so,
|
|
||||||
// we can run the "const" test. If not, the "const" test will fail, since
|
|
||||||
// we use wabt to parse the tests and wabt uses strtof.
|
|
||||||
extern "C" {
|
|
||||||
pub fn strtof(s: *const libc::c_char, endp: *mut *mut libc::c_char) -> libc::c_float;
|
|
||||||
}
|
|
||||||
if unsafe {
|
|
||||||
strtof(
|
|
||||||
b"8.8817847263968443574e-16" as *const u8 as *const libc::c_char,
|
|
||||||
core::ptr::null_mut(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.to_bits()
|
|
||||||
!= 0x26800001
|
|
||||||
{
|
|
||||||
return match (testsuite, testname) {
|
|
||||||
("spec_testsuite", "const") => true,
|
|
||||||
("single_file_spec_test", "simd_const") => true,
|
|
||||||
(_, _) => false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,99 @@
|
|||||||
# Embedding Wasmtime in Rust
|
# Embedding Wasmtime in Rust
|
||||||
|
|
||||||
... more coming soon
|
This document shows how to embed Wasmtime using the Rust API, and run a simple
|
||||||
|
wasm program.
|
||||||
|
|
||||||
|
# Create some wasm
|
||||||
|
|
||||||
|
Let's create a simple WebAssembly file with a single exported function that returns an integer:
|
||||||
|
|
||||||
|
```wat
|
||||||
|
(;; wat2wasm hello.wat -o $WASM_FILES/hello.wasm ;;)
|
||||||
|
(module
|
||||||
|
(func (export "answer") (result i32)
|
||||||
|
i32.const 42
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Create rust project
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cargo new --bin wasmtime_hello
|
||||||
|
$ cd wasmtime_hello
|
||||||
|
$ cp $WASM_FILES/hello.wasm .
|
||||||
|
```
|
||||||
|
|
||||||
|
We will be using the wasmtime engine/API to run the wasm file, so we will add the dependency to `Cargo.toml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[dependencies]
|
||||||
|
wasmtime-api = { git = "https://github.com/CraneStation/wasmtime" }
|
||||||
|
```
|
||||||
|
|
||||||
|
It is time to add code to the `src/main.rs`. First, the engine and storage need to be activated:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use wasmtime_api::*;
|
||||||
|
|
||||||
|
let engine = HostRef::new(Engine::default());
|
||||||
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
```
|
||||||
|
|
||||||
|
The `HostRef` will be used a lot -- it is a "convenience" object to store and refer an object between the host and
|
||||||
|
the embedded environments.
|
||||||
|
|
||||||
|
The `hello.wasm` can be read from the file system and provided to the `Module` object constructor as `&[u8]`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::fs::read;
|
||||||
|
|
||||||
|
let hello_wasm = read("hello.wasm").expect("wasm file");
|
||||||
|
|
||||||
|
let module = HostRef::new(Module::new(&store, &hello_wasm).expect("wasm module"));
|
||||||
|
```
|
||||||
|
|
||||||
|
The module instance can now be created. Normally, you would provide exports, but in this case, there is none required:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let instance = Instance::new(&store, &module, &[]).expect("wasm instance");
|
||||||
|
```
|
||||||
|
|
||||||
|
Everything is set. If a WebAssembly module has a start function -- it was run.
|
||||||
|
The instance's exports can be used at this point. This wasm file has only one export, so we can index it directly:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let answer_fn = instance.exports()[0].func().expect("answer function");
|
||||||
|
```
|
||||||
|
|
||||||
|
The exported function can be called using the `call` method. Remember that in most of the cases,
|
||||||
|
a `HostRef<_>` object will be returned, so `borrow()` or `borrow_mut()` method has to be used to refer the
|
||||||
|
specific object. The exported "answer" function accepts no parameters and returns a single `i32` value.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let result = answer_fn.borrow().call(&[]).expect("success");
|
||||||
|
println!("Answer: {}", result[0].i32());
|
||||||
|
```
|
||||||
|
|
||||||
|
The names of the WebAssembly module's imports and exports can be discovered by means of module's corresponding methods.
|
||||||
|
|
||||||
|
# src/main.rs
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::fs::read;
|
||||||
|
use wasmtime_api::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let engine = HostRef::new(Engine::default());
|
||||||
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
|
let wasm = read("hello.wasm").expect("wasm file");
|
||||||
|
|
||||||
|
let module = HostRef::new(Module::new(&store, &wasm).expect("wasm module"));
|
||||||
|
let instance = Instance::new(&store, &module, &[]).expect("wasm instance");
|
||||||
|
|
||||||
|
let answer_fn = instance.exports()[0].func().expect("answer function");
|
||||||
|
let result = answer_fn.borrow().call(&[]).expect("success");
|
||||||
|
println!("Answer: {}", result[0].i32());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -1179,8 +1179,12 @@ where
|
|||||||
sig!((ty) -> (ty))
|
sig!((ty) -> (ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
WasmOperator::GetGlobal { global_index } => sig!(() -> (self.module.global_type(*global_index).to_microwasm_type())),
|
WasmOperator::GetGlobal { global_index } => {
|
||||||
WasmOperator::SetGlobal { global_index } => sig!((self.module.global_type(*global_index).to_microwasm_type()) -> ()),
|
sig!(() -> (self.module.global_type(*global_index).to_microwasm_type()))
|
||||||
|
}
|
||||||
|
WasmOperator::SetGlobal { global_index } => {
|
||||||
|
sig!((self.module.global_type(*global_index).to_microwasm_type()) -> ())
|
||||||
|
}
|
||||||
|
|
||||||
WasmOperator::F32Load { .. } => sig!((I32) -> (F32)),
|
WasmOperator::F32Load { .. } => sig!((I32) -> (F32)),
|
||||||
WasmOperator::F64Load { .. } => sig!((I32) -> (F64)),
|
WasmOperator::F64Load { .. } => sig!((I32) -> (F64)),
|
||||||
@@ -1259,8 +1263,12 @@ where
|
|||||||
| WasmOperator::F64Le
|
| WasmOperator::F64Le
|
||||||
| WasmOperator::F64Ge => sig!((F64, F64) -> (I32)),
|
| WasmOperator::F64Ge => sig!((F64, F64) -> (I32)),
|
||||||
|
|
||||||
WasmOperator::I32Clz | WasmOperator::I32Ctz | WasmOperator::I32Popcnt => sig!((I32) -> (I32)),
|
WasmOperator::I32Clz | WasmOperator::I32Ctz | WasmOperator::I32Popcnt => {
|
||||||
WasmOperator::I64Clz | WasmOperator::I64Ctz | WasmOperator::I64Popcnt => sig!((I64) -> (I64)),
|
sig!((I32) -> (I32))
|
||||||
|
}
|
||||||
|
WasmOperator::I64Clz | WasmOperator::I64Ctz | WasmOperator::I64Popcnt => {
|
||||||
|
sig!((I64) -> (I64))
|
||||||
|
}
|
||||||
|
|
||||||
WasmOperator::I32Add
|
WasmOperator::I32Add
|
||||||
| WasmOperator::I32Sub
|
| WasmOperator::I32Sub
|
||||||
|
|||||||
@@ -1027,18 +1027,18 @@ test_select!(select64, i64);
|
|||||||
mod benches {
|
mod benches {
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use super::{translate, wabt, FIBONACCI, FIBONACCI_OPT};
|
use super::{translate, FIBONACCI, FIBONACCI_OPT};
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_fibonacci_compile(b: &mut test::Bencher) {
|
fn bench_fibonacci_compile(b: &mut test::Bencher) {
|
||||||
let wasm = wabt::wat2wasm(FIBONACCI).unwrap();
|
let wasm = wat::parse_str(FIBONACCI).unwrap();
|
||||||
|
|
||||||
b.iter(|| test::black_box(translate(&wasm).unwrap()));
|
b.iter(|| test::black_box(translate(&wasm).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_fibonacci_run(b: &mut test::Bencher) {
|
fn bench_fibonacci_run(b: &mut test::Bencher) {
|
||||||
let wasm = wabt::wat2wasm(FIBONACCI_OPT).unwrap();
|
let wasm = wat::parse_str(FIBONACCI_OPT).unwrap();
|
||||||
let module = translate(&wasm).unwrap();
|
let module = translate(&wasm).unwrap();
|
||||||
|
|
||||||
b.iter(|| module.execute_func::<_, u32>(0, (20,)));
|
b.iter(|| module.execute_func::<_, u32>(0, (20,)));
|
||||||
@@ -1046,7 +1046,7 @@ mod benches {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_fibonacci_compile_run(b: &mut test::Bencher) {
|
fn bench_fibonacci_compile_run(b: &mut test::Bencher) {
|
||||||
let wasm = wabt::wat2wasm(FIBONACCI).unwrap();
|
let wasm = wat::parse_str(FIBONACCI).unwrap();
|
||||||
|
|
||||||
b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,)));
|
b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use cranelift_entity::{EntityRef, PrimaryMap};
|
|||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
||||||
use target_lexicon::HOST;
|
use target_lexicon::HOST;
|
||||||
use wasmtime_environ::{Export, Module};
|
use wasmtime_environ::{CompiledFunction, Export, Module};
|
||||||
use wasmtime_jit::CodeMemory;
|
use wasmtime_jit::CodeMemory;
|
||||||
use wasmtime_runtime::{Imports, InstanceHandle, VMContext, VMFunctionBody};
|
use wasmtime_runtime::{Imports, InstanceHandle, VMContext, VMFunctionBody};
|
||||||
|
|
||||||
@@ -184,9 +184,16 @@ fn make_trampoline(
|
|||||||
)
|
)
|
||||||
.expect("compile_and_emit");
|
.expect("compile_and_emit");
|
||||||
|
|
||||||
|
let mut unwind_info = Vec::new();
|
||||||
|
context.emit_unwind_info(isa, &mut unwind_info);
|
||||||
|
|
||||||
code_memory
|
code_memory
|
||||||
.allocate_copy_of_byte_slice(&code_buf)
|
.allocate_for_function(&CompiledFunction {
|
||||||
.expect("allocate_copy_of_byte_slice")
|
body: code_buf,
|
||||||
|
jt_offsets: context.func.jt_offsets,
|
||||||
|
unwind_info,
|
||||||
|
})
|
||||||
|
.expect("allocate_for_function")
|
||||||
.as_ptr()
|
.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,14 +270,14 @@ fn main() -> Result<()> {
|
|||||||
strategy,
|
strategy,
|
||||||
);
|
);
|
||||||
let engine = HostRef::new(Engine::new(config));
|
let engine = HostRef::new(Engine::new(config));
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
let mut module_registry = HashMap::new();
|
let mut module_registry = HashMap::new();
|
||||||
|
|
||||||
// Make spectest available by default.
|
// Make spectest available by default.
|
||||||
module_registry.insert(
|
module_registry.insert(
|
||||||
"spectest".to_owned(),
|
"spectest".to_owned(),
|
||||||
Instance::from_handle(store.clone(), instantiate_spectest()?)?,
|
Instance::from_handle(&store, instantiate_spectest()?)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make wasi available by default.
|
// Make wasi available by default.
|
||||||
@@ -301,36 +301,36 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
module_registry.insert(
|
module_registry.insert(
|
||||||
"wasi_unstable".to_owned(),
|
"wasi_unstable".to_owned(),
|
||||||
Instance::from_handle(store.clone(), wasi.clone())?,
|
Instance::from_handle(&store, wasi.clone())?,
|
||||||
);
|
);
|
||||||
module_registry.insert(
|
module_registry.insert(
|
||||||
"wasi_unstable_preview0".to_owned(),
|
"wasi_unstable_preview0".to_owned(),
|
||||||
Instance::from_handle(store.clone(), wasi)?,
|
Instance::from_handle(&store, wasi)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Load the preload wasm modules.
|
// Load the preload wasm modules.
|
||||||
for filename in &args.flag_preload {
|
for filename in &args.flag_preload {
|
||||||
let path = Path::new(&filename);
|
let path = Path::new(&filename);
|
||||||
instantiate_module(store.clone(), &module_registry, path)
|
instantiate_module(&store, &module_registry, path)
|
||||||
.with_context(|| format!("failed to process preload at `{}`", path.display()))?;
|
.with_context(|| format!("failed to process preload at `{}`", path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the main wasm module.
|
// Load the main wasm module.
|
||||||
let path = Path::new(&args.arg_file);
|
let path = Path::new(&args.arg_file);
|
||||||
handle_module(store, &module_registry, &args, path)
|
handle_module(&store, &module_registry, &args, path)
|
||||||
.with_context(|| format!("failed to process main module `{}`", path.display()))?;
|
.with_context(|| format!("failed to process main module `{}`", path.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate_module(
|
fn instantiate_module(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>)> {
|
) -> Result<(HostRef<Instance>, HostRef<Module>, Vec<u8>)> {
|
||||||
// Read the wasm module binary either as `*.wat` or a raw binary
|
// Read the wasm module binary either as `*.wat` or a raw binary
|
||||||
let data = wat::parse_file(path.to_path_buf())?;
|
let data = wat::parse_file(path.to_path_buf())?;
|
||||||
|
|
||||||
let module = HostRef::new(Module::new(store.clone(), &data)?);
|
let module = HostRef::new(Module::new(store, &data)?);
|
||||||
|
|
||||||
// Resolve import using module_registry.
|
// Resolve import using module_registry.
|
||||||
let imports = module
|
let imports = module
|
||||||
@@ -356,18 +356,18 @@ fn instantiate_module(
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let instance = HostRef::new(Instance::new(store.clone(), module.clone(), &imports)?);
|
let instance = HostRef::new(Instance::new(store, &module, &imports)?);
|
||||||
|
|
||||||
Ok((instance, module, data))
|
Ok((instance, module, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_module(
|
fn handle_module(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (instance, _module, data) = instantiate_module(store.clone(), module_registry, path)?;
|
let (instance, _module, data) = instantiate_module(store, module_registry, path)?;
|
||||||
|
|
||||||
// If a function to invoke was given, invoke it.
|
// If a function to invoke was given, invoke it.
|
||||||
if let Some(f) = &args.flag_invoke {
|
if let Some(f) = &args.flag_invoke {
|
||||||
@@ -379,7 +379,7 @@ fn handle_module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn invoke_export(
|
fn invoke_export(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance: HostRef<Instance>,
|
instance: HostRef<Instance>,
|
||||||
data: &ModuleData,
|
data: &ModuleData,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|||||||
@@ -9,7 +9,24 @@ use wasmtime_wast::WastContext;
|
|||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs"));
|
include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs"));
|
||||||
|
|
||||||
#[cfg(test)]
|
// Each of the tests included from `wast_testsuite_tests` will call this
|
||||||
|
// function which actually executes the `wast` test suite given the `strategy`
|
||||||
|
// to compile it.
|
||||||
|
fn run_wast(wast: &str, strategy: CompilationStrategy) -> anyhow::Result<()> {
|
||||||
|
let wast = Path::new(wast);
|
||||||
|
let isa = native_isa();
|
||||||
|
let compiler = Compiler::new(isa, strategy);
|
||||||
|
let features = Features {
|
||||||
|
simd: wast.iter().any(|s| s == "simd"),
|
||||||
|
multi_value: wast.iter().any(|s| s == "multi-value"),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);
|
||||||
|
wast_context.register_spectest()?;
|
||||||
|
wast_context.run_file(wast)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn native_isa() -> Box<dyn isa::TargetIsa> {
|
fn native_isa() -> Box<dyn isa::TargetIsa> {
|
||||||
let mut flag_builder = settings::builder();
|
let mut flag_builder = settings::builder();
|
||||||
flag_builder.enable("enable_verifier").unwrap();
|
flag_builder.enable("enable_verifier").unwrap();
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ hashbrown = { version = "0.6.0", optional = true }
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmparser/std"]
|
std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmparser/std"]
|
||||||
wasm-c-api = []
|
|
||||||
core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core", "wasmparser/core"]
|
core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core", "wasmparser/core"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ WASM_CC_LIBS = $(error unsupported C++)
|
|||||||
|
|
||||||
# Compiler config
|
# Compiler config
|
||||||
ifeq (${WASMTIME_API_MODE},release)
|
ifeq (${WASMTIME_API_MODE},release)
|
||||||
CARGO_BUILD_FLAGS = --features "wasm-c-api" --release
|
CARGO_BUILD_FLAGS = --release
|
||||||
else
|
else
|
||||||
CARGO_BUILD_FLAGS = --features "wasm-c-api"
|
CARGO_BUILD_FLAGS =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq (${C_COMP},clang)
|
ifeq (${C_COMP},clang)
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
// Instantiate engine and store.
|
// Instantiate engine and store.
|
||||||
let engine = HostRef::new(Engine::default());
|
let engine = HostRef::new(Engine::default());
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
// Load a module.
|
// Load a module.
|
||||||
let module = HostRef::new(Module::new(store.clone(), &wasm)?);
|
let module = HostRef::new(Module::new(&store, &wasm)?);
|
||||||
|
|
||||||
// Find index of the `gcd` export.
|
// Find index of the `gcd` export.
|
||||||
let gcd_index = module
|
let gcd_index = module
|
||||||
@@ -26,7 +26,7 @@ fn main() -> Result<()> {
|
|||||||
.0;
|
.0;
|
||||||
|
|
||||||
// Instantiate the module.
|
// Instantiate the module.
|
||||||
let instance = HostRef::new(Instance::new(store.clone(), module, &[])?);
|
let instance = HostRef::new(Instance::new(&store, &module, &[])?);
|
||||||
|
|
||||||
// Invoke `gcd` export
|
// Invoke `gcd` export
|
||||||
let gcd = instance.borrow().exports()[gcd_index]
|
let gcd = instance.borrow().exports()[gcd_index]
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ impl Callable for HelloCallback {
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Initialize.
|
// Initialize.
|
||||||
println!("Initializing...");
|
println!("Initializing...");
|
||||||
let engine = HostRef::new(Engine::new(Config::default()));
|
let engine = HostRef::new(Engine::default());
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
// Load binary.
|
// Load binary.
|
||||||
println!("Loading binary...");
|
println!("Loading binary...");
|
||||||
@@ -30,19 +30,18 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
// Compile.
|
// Compile.
|
||||||
println!("Compiling module...");
|
println!("Compiling module...");
|
||||||
let module =
|
let module = HostRef::new(Module::new(&store, &binary).context("> Error compiling module!")?);
|
||||||
HostRef::new(Module::new(store.clone(), &binary).context("> Error compiling module!")?);
|
|
||||||
|
|
||||||
// Create external print functions.
|
// Create external print functions.
|
||||||
println!("Creating callback...");
|
println!("Creating callback...");
|
||||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||||
let hello_func = HostRef::new(Func::new(store.clone(), hello_type, Rc::new(HelloCallback)));
|
let hello_func = HostRef::new(Func::new(&store, hello_type, Rc::new(HelloCallback)));
|
||||||
|
|
||||||
// Instantiate.
|
// Instantiate.
|
||||||
println!("Instantiating module...");
|
println!("Instantiating module...");
|
||||||
let imports = vec![hello_func.into()];
|
let imports = vec![hello_func.into()];
|
||||||
let instance = HostRef::new(
|
let instance = HostRef::new(
|
||||||
Instance::new(store.clone(), module, imports.as_slice())
|
Instance::new(&store, &module, imports.as_slice())
|
||||||
.context("> Error instantiating module!")?,
|
.context("> Error instantiating module!")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ macro_rules! call {
|
|||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
// Initialize.
|
// Initialize.
|
||||||
println!("Initializing...");
|
println!("Initializing...");
|
||||||
let engine = HostRef::new(Engine::new(Config::default()));
|
let engine = HostRef::new(Engine::default());
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
// Load binary.
|
// Load binary.
|
||||||
println!("Loading binary...");
|
println!("Loading binary...");
|
||||||
@@ -73,14 +73,12 @@ fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
// Compile.
|
// Compile.
|
||||||
println!("Compiling module...");
|
println!("Compiling module...");
|
||||||
let module =
|
let module = HostRef::new(Module::new(&store, &binary).context("> Error compiling module!")?);
|
||||||
HostRef::new(Module::new(store.clone(), &binary).context("> Error compiling module!")?);
|
|
||||||
|
|
||||||
// Instantiate.
|
// Instantiate.
|
||||||
println!("Instantiating module...");
|
println!("Instantiating module...");
|
||||||
let instance = HostRef::new(
|
let instance =
|
||||||
Instance::new(store.clone(), module, &[]).context("> Error instantiating module!")?,
|
HostRef::new(Instance::new(&store, &module, &[]).context("> Error instantiating module!")?);
|
||||||
);
|
|
||||||
|
|
||||||
// Extract export.
|
// Extract export.
|
||||||
println!("Extracting export...");
|
println!("Extracting export...");
|
||||||
@@ -141,7 +139,7 @@ fn main() -> Result<(), Error> {
|
|||||||
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
|
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
|
||||||
println!("Creating stand-alone memory...");
|
println!("Creating stand-alone memory...");
|
||||||
let memorytype = MemoryType::new(Limits::new(5, 5));
|
let memorytype = MemoryType::new(Limits::new(5, 5));
|
||||||
let mut memory2 = Memory::new(store.clone(), memorytype);
|
let mut memory2 = Memory::new(&store, memorytype);
|
||||||
check!(memory2.size(), 5u32);
|
check!(memory2.size(), 5u32);
|
||||||
check!(memory2.grow(1), false);
|
check!(memory2.grow(1), false);
|
||||||
check!(memory2.grow(0), true);
|
check!(memory2.grow(0), true);
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ impl Callable for Callback {
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Initialize.
|
// Initialize.
|
||||||
println!("Initializing...");
|
println!("Initializing...");
|
||||||
let engine = HostRef::new(Engine::new(Config::default()));
|
let engine = HostRef::new(Engine::default());
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
|
|
||||||
// Load binary.
|
// Load binary.
|
||||||
println!("Loading binary...");
|
println!("Loading binary...");
|
||||||
@@ -33,8 +33,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
// Compile.
|
// Compile.
|
||||||
println!("Compiling module...");
|
println!("Compiling module...");
|
||||||
let module =
|
let module = HostRef::new(Module::new(&store, &binary).context("Error compiling module!")?);
|
||||||
HostRef::new(Module::new(store.clone(), &binary).context("Error compiling module!")?);
|
|
||||||
|
|
||||||
// Create external print functions.
|
// Create external print functions.
|
||||||
println!("Creating callback...");
|
println!("Creating callback...");
|
||||||
@@ -42,13 +41,13 @@ fn main() -> Result<()> {
|
|||||||
Box::new([ValType::I32, ValType::I64]),
|
Box::new([ValType::I32, ValType::I64]),
|
||||||
Box::new([ValType::I64, ValType::I32]),
|
Box::new([ValType::I64, ValType::I32]),
|
||||||
);
|
);
|
||||||
let callback_func = HostRef::new(Func::new(store.clone(), callback_type, Rc::new(Callback)));
|
let callback_func = HostRef::new(Func::new(&store, callback_type, Rc::new(Callback)));
|
||||||
|
|
||||||
// Instantiate.
|
// Instantiate.
|
||||||
println!("Instantiating module...");
|
println!("Instantiating module...");
|
||||||
let imports = vec![callback_func.into()];
|
let imports = vec![callback_func.into()];
|
||||||
let instance = HostRef::new(
|
let instance = HostRef::new(
|
||||||
Instance::new(store.clone(), module, imports.as_slice())
|
Instance::new(&store, &module, imports.as_slice())
|
||||||
.context("Error instantiating module!")?,
|
.context("Error instantiating module!")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ pub(crate) struct WasmtimeFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WasmtimeFn {
|
impl WasmtimeFn {
|
||||||
pub fn new(store: HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
|
pub fn new(store: &HostRef<Store>, instance: InstanceHandle, export: Export) -> WasmtimeFn {
|
||||||
WasmtimeFn {
|
WasmtimeFn {
|
||||||
store,
|
store: store.clone(),
|
||||||
instance,
|
instance,
|
||||||
export,
|
export,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl Extern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_wasmtime_export(
|
pub(crate) fn from_wasmtime_export(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: InstanceHandle,
|
instance_handle: InstanceHandle,
|
||||||
export: wasmtime_runtime::Export,
|
export: wasmtime_runtime::Export,
|
||||||
) -> Extern {
|
) -> Extern {
|
||||||
@@ -118,18 +118,18 @@ pub struct Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Func {
|
impl Func {
|
||||||
pub fn new(store: HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
|
pub fn new(store: &HostRef<Store>, ty: FuncType, callable: Rc<dyn Callable + 'static>) -> Self {
|
||||||
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
|
let callable = Rc::new(NativeCallable::new(callable, &ty, &store));
|
||||||
Func::from_wrapped(store, ty, callable)
|
Func::from_wrapped(store, ty, callable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_wrapped(
|
fn from_wrapped(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
r#type: FuncType,
|
r#type: FuncType,
|
||||||
callable: Rc<dyn WrappedCallable + 'static>,
|
callable: Rc<dyn WrappedCallable + 'static>,
|
||||||
) -> Func {
|
) -> Func {
|
||||||
Func {
|
Func {
|
||||||
_store: store,
|
_store: store.clone(),
|
||||||
callable,
|
callable,
|
||||||
r#type,
|
r#type,
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ impl Func {
|
|||||||
|
|
||||||
pub(crate) fn from_wasmtime_function(
|
pub(crate) fn from_wasmtime_function(
|
||||||
export: wasmtime_runtime::Export,
|
export: wasmtime_runtime::Export,
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: InstanceHandle,
|
instance_handle: InstanceHandle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
let ty = if let wasmtime_runtime::Export::Function { signature, .. } = &export {
|
||||||
@@ -167,7 +167,7 @@ impl Func {
|
|||||||
} else {
|
} else {
|
||||||
panic!("expected function export")
|
panic!("expected function export")
|
||||||
};
|
};
|
||||||
let callable = WasmtimeFn::new(store.clone(), instance_handle, export.clone());
|
let callable = WasmtimeFn::new(store, instance_handle, export.clone());
|
||||||
Func::from_wrapped(store, ty, Rc::new(callable))
|
Func::from_wrapped(store, ty, Rc::new(callable))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,11 +187,11 @@ pub struct Global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Global {
|
impl Global {
|
||||||
pub fn new(store: HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
|
pub fn new(store: &HostRef<Store>, r#type: GlobalType, val: Val) -> Global {
|
||||||
let (wasmtime_export, wasmtime_state) =
|
let (wasmtime_export, wasmtime_state) =
|
||||||
generate_global_export(&r#type, val).expect("generated global");
|
generate_global_export(&r#type, val).expect("generated global");
|
||||||
Global {
|
Global {
|
||||||
_store: store,
|
_store: store.clone(),
|
||||||
r#type,
|
r#type,
|
||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
wasmtime_state: Some(wasmtime_state),
|
wasmtime_state: Some(wasmtime_state),
|
||||||
@@ -248,7 +248,7 @@ impl Global {
|
|||||||
|
|
||||||
pub(crate) fn from_wasmtime_global(
|
pub(crate) fn from_wasmtime_global(
|
||||||
export: wasmtime_runtime::Export,
|
export: wasmtime_runtime::Export,
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
) -> Global {
|
) -> Global {
|
||||||
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
|
let global = if let wasmtime_runtime::Export::Global { ref global, .. } = export {
|
||||||
global
|
global
|
||||||
@@ -257,7 +257,7 @@ impl Global {
|
|||||||
};
|
};
|
||||||
let ty = GlobalType::from_cranelift_global(global.clone());
|
let ty = GlobalType::from_cranelift_global(global.clone());
|
||||||
Global {
|
Global {
|
||||||
_store: store,
|
_store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
wasmtime_export: export,
|
wasmtime_export: export,
|
||||||
wasmtime_state: None,
|
wasmtime_state: None,
|
||||||
@@ -302,7 +302,7 @@ fn set_table_item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
pub fn new(store: HostRef<Store>, r#type: TableType, init: Val) -> Table {
|
pub fn new(store: &HostRef<Store>, r#type: TableType, init: Val) -> Table {
|
||||||
match r#type.element() {
|
match r#type.element() {
|
||||||
ValType::FuncRef => (),
|
ValType::FuncRef => (),
|
||||||
_ => panic!("table is not for funcref"),
|
_ => panic!("table is not for funcref"),
|
||||||
@@ -317,7 +317,7 @@ impl Table {
|
|||||||
let len = unsafe { (*definition).current_elements };
|
let len = unsafe { (*definition).current_elements };
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let _success =
|
let _success =
|
||||||
set_table_item(&mut wasmtime_handle, &store, index, i, init.clone());
|
set_table_item(&mut wasmtime_handle, store, index, i, init.clone());
|
||||||
assert!(_success);
|
assert!(_success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +325,7 @@ impl Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Table {
|
Table {
|
||||||
store,
|
store: store.clone(),
|
||||||
r#type,
|
r#type,
|
||||||
wasmtime_handle,
|
wasmtime_handle,
|
||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
@@ -387,7 +387,7 @@ impl Table {
|
|||||||
|
|
||||||
pub(crate) fn from_wasmtime_table(
|
pub(crate) fn from_wasmtime_table(
|
||||||
export: wasmtime_runtime::Export,
|
export: wasmtime_runtime::Export,
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||||
) -> Table {
|
) -> Table {
|
||||||
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
|
let table = if let wasmtime_runtime::Export::Table { ref table, .. } = export {
|
||||||
@@ -397,7 +397,7 @@ impl Table {
|
|||||||
};
|
};
|
||||||
let ty = TableType::from_cranelift_table(table.table.clone());
|
let ty = TableType::from_cranelift_table(table.table.clone());
|
||||||
Table {
|
Table {
|
||||||
store,
|
store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
wasmtime_handle: instance_handle,
|
wasmtime_handle: instance_handle,
|
||||||
wasmtime_export: export,
|
wasmtime_export: export,
|
||||||
@@ -413,11 +413,11 @@ pub struct Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
pub fn new(store: HostRef<Store>, r#type: MemoryType) -> Memory {
|
pub fn new(store: &HostRef<Store>, r#type: MemoryType) -> Memory {
|
||||||
let (wasmtime_handle, wasmtime_export) =
|
let (wasmtime_handle, wasmtime_export) =
|
||||||
generate_memory_export(&r#type).expect("generated memory");
|
generate_memory_export(&r#type).expect("generated memory");
|
||||||
Memory {
|
Memory {
|
||||||
_store: store,
|
_store: store.clone(),
|
||||||
r#type,
|
r#type,
|
||||||
wasmtime_handle,
|
wasmtime_handle,
|
||||||
wasmtime_export,
|
wasmtime_export,
|
||||||
@@ -471,7 +471,7 @@ impl Memory {
|
|||||||
|
|
||||||
pub(crate) fn from_wasmtime_memory(
|
pub(crate) fn from_wasmtime_memory(
|
||||||
export: wasmtime_runtime::Export,
|
export: wasmtime_runtime::Export,
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: wasmtime_runtime::InstanceHandle,
|
instance_handle: wasmtime_runtime::InstanceHandle,
|
||||||
) -> Memory {
|
) -> Memory {
|
||||||
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {
|
let memory = if let wasmtime_runtime::Export::Memory { ref memory, .. } = export {
|
||||||
@@ -481,7 +481,7 @@ impl Memory {
|
|||||||
};
|
};
|
||||||
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
||||||
Memory {
|
Memory {
|
||||||
_store: store,
|
_store: store.clone(),
|
||||||
r#type: ty,
|
r#type: ty,
|
||||||
wasmtime_handle: instance_handle,
|
wasmtime_handle: instance_handle,
|
||||||
wasmtime_export: export,
|
wasmtime_export: export,
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ pub struct Instance {
|
|||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
module: HostRef<Module>,
|
module: &HostRef<Module>,
|
||||||
externs: &[Extern],
|
externs: &[Extern],
|
||||||
) -> Result<Instance> {
|
) -> Result<Instance> {
|
||||||
let context = store.borrow_mut().context().clone();
|
let context = store.borrow_mut().context().clone();
|
||||||
@@ -84,7 +84,7 @@ impl Instance {
|
|||||||
let name = export.name().to_string();
|
let name = export.name().to_string();
|
||||||
let export = instance_handle.lookup(&name).expect("export");
|
let export = instance_handle.lookup(&name).expect("export");
|
||||||
exports.push(Extern::from_wasmtime_export(
|
exports.push(Extern::from_wasmtime_export(
|
||||||
store.clone(),
|
store,
|
||||||
instance_handle.clone(),
|
instance_handle.clone(),
|
||||||
export,
|
export,
|
||||||
));
|
));
|
||||||
@@ -103,7 +103,7 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_handle(
|
pub fn from_handle(
|
||||||
store: HostRef<Store>,
|
store: &HostRef<Store>,
|
||||||
instance_handle: InstanceHandle,
|
instance_handle: InstanceHandle,
|
||||||
) -> Result<(Instance, HashMap<String, usize>)> {
|
) -> Result<(Instance, HashMap<String, usize>)> {
|
||||||
let contexts = HashSet::new();
|
let contexts = HashSet::new();
|
||||||
@@ -121,7 +121,7 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
export_names_map.insert(name.to_owned(), exports.len());
|
export_names_map.insert(name.to_owned(), exports.len());
|
||||||
exports.push(Extern::from_wasmtime_export(
|
exports.push(Extern::from_wasmtime_export(
|
||||||
store.clone(),
|
store,
|
||||||
instance_handle.clone(),
|
instance_handle.clone(),
|
||||||
export.clone(),
|
export.clone(),
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ mod trap;
|
|||||||
mod types;
|
mod types;
|
||||||
mod values;
|
mod values;
|
||||||
|
|
||||||
#[cfg(feature = "wasm-c-api")]
|
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|||||||
@@ -182,10 +182,10 @@ pub struct Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
pub fn new(store: HostRef<Store>, binary: &[u8]) -> Result<Module> {
|
pub fn new(store: &HostRef<Store>, binary: &[u8]) -> Result<Module> {
|
||||||
let (imports, exports) = read_imports_and_exports(binary)?;
|
let (imports, exports) = read_imports_and_exports(binary)?;
|
||||||
Ok(Module {
|
Ok(Module {
|
||||||
store,
|
store: store.clone(),
|
||||||
binary: binary.into(),
|
binary: binary.into(),
|
||||||
imports,
|
imports,
|
||||||
exports,
|
exports,
|
||||||
|
|||||||
@@ -102,13 +102,13 @@ pub struct Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
pub fn new(engine: HostRef<Engine>) -> Store {
|
pub fn new(engine: &HostRef<Engine>) -> Store {
|
||||||
let flags = engine.borrow().config().flags().clone();
|
let flags = engine.borrow().config().flags().clone();
|
||||||
let features = engine.borrow().config().features().clone();
|
let features = engine.borrow().config().features().clone();
|
||||||
let debug_info = engine.borrow().config().debug_info();
|
let debug_info = engine.borrow().config().debug_info();
|
||||||
let strategy = engine.borrow().config().strategy();
|
let strategy = engine.borrow().config().strategy();
|
||||||
Store {
|
Store {
|
||||||
engine,
|
engine: engine.clone(),
|
||||||
context: Context::create(flags, features, debug_info, strategy),
|
context: Context::create(flags, features, debug_info, strategy),
|
||||||
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
||||||
signature_cache: HashMap::new(),
|
signature_cache: HashMap::new(),
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ use cranelift_codegen::{binemit, ir, isa};
|
|||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
||||||
//use target_lexicon::HOST;
|
use wasmtime_environ::{CompiledFunction, Export, Module};
|
||||||
use wasmtime_environ::{Export, Module};
|
|
||||||
use wasmtime_jit::CodeMemory;
|
use wasmtime_jit::CodeMemory;
|
||||||
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
use wasmtime_runtime::{InstanceHandle, VMContext, VMFunctionBody};
|
||||||
|
|
||||||
@@ -185,9 +184,16 @@ fn make_trampoline(
|
|||||||
)
|
)
|
||||||
.expect("compile_and_emit");
|
.expect("compile_and_emit");
|
||||||
|
|
||||||
|
let mut unwind_info = Vec::new();
|
||||||
|
context.emit_unwind_info(isa, &mut unwind_info);
|
||||||
|
|
||||||
code_memory
|
code_memory
|
||||||
.allocate_copy_of_byte_slice(&code_buf)
|
.allocate_for_function(&CompiledFunction {
|
||||||
.expect("allocate_copy_of_byte_slice")
|
body: code_buf,
|
||||||
|
jt_offsets: context.func.jt_offsets,
|
||||||
|
unwind_info,
|
||||||
|
})
|
||||||
|
.expect("allocate_for_function")
|
||||||
.as_ptr()
|
.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,6 @@ pub(crate) fn from_checked_anyfunc(
|
|||||||
signature,
|
signature,
|
||||||
vmctx: item.vmctx,
|
vmctx: item.vmctx,
|
||||||
};
|
};
|
||||||
let f = Func::from_wasmtime_function(export, store.clone(), instance_handle);
|
let f = Func::from_wasmtime_function(export, store, instance_handle);
|
||||||
Val::FuncRef(HostRef::new(f))
|
Val::FuncRef(HostRef::new(f))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ pub unsafe extern "C" fn wasm_func_new(
|
|||||||
ty: *const wasm_functype_t,
|
ty: *const wasm_functype_t,
|
||||||
callback: wasm_func_callback_t,
|
callback: wasm_func_callback_t,
|
||||||
) -> *mut wasm_func_t {
|
) -> *mut wasm_func_t {
|
||||||
let store = (*store).store.clone();
|
let store = &(*store).store;
|
||||||
let ty = (*ty).functype.clone();
|
let ty = (*ty).functype.clone();
|
||||||
let callback = Rc::new(callback);
|
let callback = Rc::new(callback);
|
||||||
let func = Box::new(wasm_func_t {
|
let func = Box::new(wasm_func_t {
|
||||||
@@ -663,13 +663,13 @@ pub unsafe extern "C" fn wasm_instance_new(
|
|||||||
imports: *const *const wasm_extern_t,
|
imports: *const *const wasm_extern_t,
|
||||||
result: *mut *mut wasm_trap_t,
|
result: *mut *mut wasm_trap_t,
|
||||||
) -> *mut wasm_instance_t {
|
) -> *mut wasm_instance_t {
|
||||||
let store = (*store).store.clone();
|
let store = &(*store).store;
|
||||||
let mut externs: Vec<Extern> = Vec::with_capacity((*module).imports.len());
|
let mut externs: Vec<Extern> = Vec::with_capacity((*module).imports.len());
|
||||||
for i in 0..(*module).imports.len() {
|
for i in 0..(*module).imports.len() {
|
||||||
let import = *imports.offset(i as isize);
|
let import = *imports.offset(i as isize);
|
||||||
externs.push((*import).ext.clone());
|
externs.push((*import).ext.clone());
|
||||||
}
|
}
|
||||||
let module = (*module).module.clone();
|
let module = &(*module).module;
|
||||||
match Instance::new(store, module, &externs) {
|
match Instance::new(store, module, &externs) {
|
||||||
Ok(instance) => {
|
Ok(instance) => {
|
||||||
let instance = Box::new(wasm_instance_t {
|
let instance = Box::new(wasm_instance_t {
|
||||||
@@ -731,7 +731,7 @@ pub unsafe extern "C" fn wasm_module_new(
|
|||||||
binary: *const wasm_byte_vec_t,
|
binary: *const wasm_byte_vec_t,
|
||||||
) -> *mut wasm_module_t {
|
) -> *mut wasm_module_t {
|
||||||
let binary = (*binary).as_slice();
|
let binary = (*binary).as_slice();
|
||||||
let store = (*store).store.clone();
|
let store = &(*store).store;
|
||||||
let module = Module::new(store, binary).expect("module");
|
let module = Module::new(store, binary).expect("module");
|
||||||
let imports = module
|
let imports = module
|
||||||
.imports()
|
.imports()
|
||||||
@@ -766,9 +766,9 @@ pub unsafe extern "C" fn wasm_store_delete(store: *mut wasm_store_t) {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasm_store_new(engine: *mut wasm_engine_t) -> *mut wasm_store_t {
|
pub unsafe extern "C" fn wasm_store_new(engine: *mut wasm_engine_t) -> *mut wasm_store_t {
|
||||||
let engine = (*engine).engine.clone();
|
let engine = &(*engine).engine;
|
||||||
let store = Box::new(wasm_store_t {
|
let store = Box::new(wasm_store_t {
|
||||||
store: HostRef::new(Store::new(engine)),
|
store: HostRef::new(Store::new(&engine)),
|
||||||
});
|
});
|
||||||
Box::into_raw(store)
|
Box::into_raw(store)
|
||||||
}
|
}
|
||||||
@@ -804,7 +804,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env(
|
|||||||
env: *mut ::core::ffi::c_void,
|
env: *mut ::core::ffi::c_void,
|
||||||
finalizer: ::core::option::Option<unsafe extern "C" fn(arg1: *mut ::core::ffi::c_void)>,
|
finalizer: ::core::option::Option<unsafe extern "C" fn(arg1: *mut ::core::ffi::c_void)>,
|
||||||
) -> *mut wasm_func_t {
|
) -> *mut wasm_func_t {
|
||||||
let store = (*store).store.clone();
|
let store = &(*store).store;
|
||||||
let ty = (*ty).functype.clone();
|
let ty = (*ty).functype.clone();
|
||||||
let callback = Rc::new(CallbackWithEnv {
|
let callback = Rc::new(CallbackWithEnv {
|
||||||
callback,
|
callback,
|
||||||
@@ -1327,7 +1327,7 @@ pub unsafe extern "C" fn wasm_global_new(
|
|||||||
val: *const wasm_val_t,
|
val: *const wasm_val_t,
|
||||||
) -> *mut wasm_global_t {
|
) -> *mut wasm_global_t {
|
||||||
let global = HostRef::new(Global::new(
|
let global = HostRef::new(Global::new(
|
||||||
(*store).store.clone(),
|
&(*store).store,
|
||||||
(*gt).globaltype.clone(),
|
(*gt).globaltype.clone(),
|
||||||
(*val).val(),
|
(*val).val(),
|
||||||
));
|
));
|
||||||
@@ -1446,10 +1446,7 @@ pub unsafe extern "C" fn wasm_memory_new(
|
|||||||
store: *mut wasm_store_t,
|
store: *mut wasm_store_t,
|
||||||
mt: *const wasm_memorytype_t,
|
mt: *const wasm_memorytype_t,
|
||||||
) -> *mut wasm_memory_t {
|
) -> *mut wasm_memory_t {
|
||||||
let memory = HostRef::new(Memory::new(
|
let memory = HostRef::new(Memory::new(&(*store).store, (*mt).memorytype.clone()));
|
||||||
(*store).store.clone(),
|
|
||||||
(*mt).memorytype.clone(),
|
|
||||||
));
|
|
||||||
let m = Box::new(wasm_memory_t { memory, ext: None });
|
let m = Box::new(wasm_memory_t { memory, ext: None });
|
||||||
Box::into_raw(m)
|
Box::into_raw(m)
|
||||||
}
|
}
|
||||||
@@ -1537,11 +1534,7 @@ pub unsafe extern "C" fn wasm_table_new(
|
|||||||
Val::AnyRef(AnyRef::Null)
|
Val::AnyRef(AnyRef::Null)
|
||||||
};
|
};
|
||||||
let t = Box::new(wasm_table_t {
|
let t = Box::new(wasm_table_t {
|
||||||
table: HostRef::new(Table::new(
|
table: HostRef::new(Table::new(&(*store).store, (*tt).tabletype.clone(), init)),
|
||||||
(*store).store.clone(),
|
|
||||||
(*tt).tabletype.clone(),
|
|
||||||
init,
|
|
||||||
)),
|
|
||||||
ext: None,
|
ext: None,
|
||||||
});
|
});
|
||||||
Box::into_raw(t)
|
Box::into_raw(t)
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ fn test_import_calling_export() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let engine = HostRef::new(Engine::new(Config::default()));
|
let engine = HostRef::new(Engine::new(Config::default()));
|
||||||
let store = HostRef::new(Store::new(engine));
|
let store = HostRef::new(Store::new(&engine));
|
||||||
let module = HostRef::new(
|
let module = HostRef::new(
|
||||||
Module::new(
|
Module::new(
|
||||||
store.clone(),
|
&store,
|
||||||
&read("tests/import_calling_export.wasm").expect("failed to read wasm file"),
|
&read("tests/import_calling_export.wasm").expect("failed to read wasm file"),
|
||||||
)
|
)
|
||||||
.expect("failed to create module"),
|
.expect("failed to create module"),
|
||||||
@@ -39,15 +39,14 @@ fn test_import_calling_export() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let callback_func = HostRef::new(Func::new(
|
let callback_func = HostRef::new(Func::new(
|
||||||
store.clone(),
|
&store,
|
||||||
FuncType::new(Box::new([]), Box::new([])),
|
FuncType::new(Box::new([]), Box::new([])),
|
||||||
callback.clone(),
|
callback.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let imports = vec![callback_func.into()];
|
let imports = vec![callback_func.into()];
|
||||||
let instance = HostRef::new(
|
let instance = HostRef::new(
|
||||||
Instance::new(store.clone(), module, imports.as_slice())
|
Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module"),
|
||||||
.expect("failed to instantiate module"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
let exports = Ref::map(instance.borrow(), |instance| instance.exports());
|
||||||
|
|||||||
5
wasmtime-environ/src/cache/tests.rs
vendored
5
wasmtime-environ/src/cache/tests.rs
vendored
@@ -1,7 +1,7 @@
|
|||||||
use super::config::tests::test_prolog;
|
use super::config::tests::test_prolog;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
||||||
use crate::compilation::{CodeAndJTOffsets, Relocation, RelocationTarget, TrapInformation};
|
use crate::compilation::{CompiledFunction, Relocation, RelocationTarget, TrapInformation};
|
||||||
use crate::module::{MemoryPlan, MemoryStyle, Module};
|
use crate::module::{MemoryPlan, MemoryStyle, Module};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -258,9 +258,10 @@ fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData {
|
|||||||
*v = (j as u32) * 3 / 4
|
*v = (j as u32) * 3 / 4
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CodeAndJTOffsets {
|
CompiledFunction {
|
||||||
body: (0..(i * 3 / 2)).collect(),
|
body: (0..(i * 3 / 2)).collect(),
|
||||||
jt_offsets: sm,
|
jt_offsets: sm,
|
||||||
|
unwind_info: (0..(i * 3 / 2)).collect(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -12,17 +12,20 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Compiled machine code: body and jump table offsets.
|
/// Compiled function: machine code body, jump table offsets, and unwind information.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CodeAndJTOffsets {
|
pub struct CompiledFunction {
|
||||||
/// The function body.
|
/// The function body.
|
||||||
pub body: Vec<u8>,
|
pub body: Vec<u8>,
|
||||||
|
|
||||||
/// The jump tables offsets (in the body).
|
/// The jump tables offsets (in the body).
|
||||||
pub jt_offsets: ir::JumpTableOffsets,
|
pub jt_offsets: ir::JumpTableOffsets,
|
||||||
|
|
||||||
|
/// The unwind information.
|
||||||
|
pub unwind_info: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Functions = PrimaryMap<DefinedFuncIndex, CodeAndJTOffsets>;
|
type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
|
||||||
|
|
||||||
/// The result of compiling a WebAssembly module's functions.
|
/// The result of compiling a WebAssembly module's functions.
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
|
||||||
@@ -40,21 +43,22 @@ impl Compilation {
|
|||||||
/// Allocates the compilation result with the given function bodies.
|
/// Allocates the compilation result with the given function bodies.
|
||||||
pub fn from_buffer(
|
pub fn from_buffer(
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets)>,
|
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets, Range<usize>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
functions
|
functions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(range, jt_offsets)| CodeAndJTOffsets {
|
.map(|(body_range, jt_offsets, unwind_range)| CompiledFunction {
|
||||||
body: buffer[range].to_vec(),
|
body: buffer[body_range].to_vec(),
|
||||||
jt_offsets,
|
jt_offsets,
|
||||||
|
unwind_info: buffer[unwind_range].to_vec(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the bytes of a single function
|
/// Gets the bytes of a single function
|
||||||
pub fn get(&self, func: DefinedFuncIndex) -> &CodeAndJTOffsets {
|
pub fn get(&self, func: DefinedFuncIndex) -> &CompiledFunction {
|
||||||
&self.functions[func]
|
&self.functions[func]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +71,7 @@ impl Compilation {
|
|||||||
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
|
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
|
||||||
self.functions
|
self.functions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, code_and_jt)| code_and_jt.jt_offsets.clone())
|
.map(|(_, func)| func.jt_offsets.clone())
|
||||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +92,7 @@ pub struct Iter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
impl<'a> Iterator for Iter<'a> {
|
||||||
type Item = &'a CodeAndJTOffsets;
|
type Item = &'a CompiledFunction;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.iterator.next().map(|(_, b)| b)
|
self.iterator.next().map(|(_, b)| b)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::address_map::{
|
|||||||
};
|
};
|
||||||
use crate::cache::{ModuleCacheData, ModuleCacheEntry};
|
use crate::cache::{ModuleCacheData, ModuleCacheEntry};
|
||||||
use crate::compilation::{
|
use crate::compilation::{
|
||||||
CodeAndJTOffsets, Compilation, CompileError, Relocation, RelocationTarget, Relocations,
|
Compilation, CompileError, CompiledFunction, Relocation, RelocationTarget, Relocations,
|
||||||
TrapInformation, Traps,
|
TrapInformation, Traps,
|
||||||
};
|
};
|
||||||
use crate::func_environ::{
|
use crate::func_environ::{
|
||||||
@@ -235,6 +235,7 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut code_buf: Vec<u8> = Vec::new();
|
let mut code_buf: Vec<u8> = Vec::new();
|
||||||
|
let mut unwind_info = Vec::new();
|
||||||
let mut reloc_sink = RelocSink::new(func_index);
|
let mut reloc_sink = RelocSink::new(func_index);
|
||||||
let mut trap_sink = TrapSink::new();
|
let mut trap_sink = TrapSink::new();
|
||||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||||
@@ -246,7 +247,7 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
&mut stackmap_sink,
|
&mut stackmap_sink,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let jt_offsets = context.func.jt_offsets.clone();
|
context.emit_unwind_info(isa, &mut unwind_info);
|
||||||
|
|
||||||
let address_transform = if generate_debug_info {
|
let address_transform = if generate_debug_info {
|
||||||
let body_len = code_buf.len();
|
let body_len = code_buf.len();
|
||||||
@@ -261,16 +262,15 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let stack_slots = context.func.stack_slots.clone();
|
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
code_buf,
|
code_buf,
|
||||||
jt_offsets,
|
context.func.jt_offsets,
|
||||||
reloc_sink.func_relocs,
|
reloc_sink.func_relocs,
|
||||||
address_transform,
|
address_transform,
|
||||||
ranges,
|
ranges,
|
||||||
stack_slots,
|
context.func.stack_slots,
|
||||||
trap_sink.traps,
|
trap_sink.traps,
|
||||||
|
unwind_info,
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -285,10 +285,12 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
ranges,
|
ranges,
|
||||||
sss,
|
sss,
|
||||||
function_traps,
|
function_traps,
|
||||||
|
unwind_info,
|
||||||
)| {
|
)| {
|
||||||
functions.push(CodeAndJTOffsets {
|
functions.push(CompiledFunction {
|
||||||
body: function,
|
body: function,
|
||||||
jt_offsets: func_jt_offsets,
|
jt_offsets: func_jt_offsets,
|
||||||
|
unwind_info,
|
||||||
});
|
});
|
||||||
relocations.push(relocs);
|
relocations.push(relocs);
|
||||||
if let Some(address_transform) = address_transform {
|
if let Some(address_transform) = address_transform {
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ pub use crate::address_map::{
|
|||||||
};
|
};
|
||||||
pub use crate::cache::{create_new_config as cache_create_new_config, init as cache_init};
|
pub use crate::cache::{create_new_config as cache_create_new_config, init as cache_init};
|
||||||
pub use crate::compilation::{
|
pub use crate::compilation::{
|
||||||
Compilation, CompileError, Compiler, Relocation, RelocationTarget, Relocations,
|
Compilation, CompileError, CompiledFunction, Compiler, Relocation, RelocationTarget,
|
||||||
TrapInformation, Traps,
|
Relocations, TrapInformation, Traps,
|
||||||
};
|
};
|
||||||
pub use crate::cranelift::Cranelift;
|
pub use crate::cranelift::Cranelift;
|
||||||
pub use crate::func_environ::BuiltinFunctionIndex;
|
pub use crate::func_environ::BuiltinFunctionIndex;
|
||||||
|
|||||||
@@ -65,10 +65,11 @@ impl crate::compilation::Compiler for Lightbeam {
|
|||||||
|
|
||||||
// TODO pass jump table offsets to Compilation::from_buffer() when they
|
// TODO pass jump table offsets to Compilation::from_buffer() when they
|
||||||
// are implemented in lightbeam -- using empty set of offsets for now.
|
// are implemented in lightbeam -- using empty set of offsets for now.
|
||||||
|
// TODO: pass an empty range for the unwind information until lightbeam emits it
|
||||||
let code_section_ranges_and_jt = code_section
|
let code_section_ranges_and_jt = code_section
|
||||||
.funcs()
|
.funcs()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| (r, SecondaryMap::new()));
|
.map(|r| (r, SecondaryMap::new(), 0..0));
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Compilation::from_buffer(code_section.buffer().to_vec(), code_section_ranges_and_jt),
|
Compilation::from_buffer(code_section.buffer().to_vec(), code_section_ranges_and_jt),
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ target-lexicon = { version = "0.9.0", default-features = false }
|
|||||||
hashbrown = { version = "0.6.0", optional = true }
|
hashbrown = { version = "0.6.0", optional = true }
|
||||||
wasmparser = { version = "0.39.2", default-features = false }
|
wasmparser = { version = "0.39.2", default-features = false }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
winapi = { version = "0.3.7", features = ["winnt", "impl-default"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmtime-debug/std", "wasmtime-runtime/std", "wasmparser/std"]
|
std = ["cranelift-codegen/std", "cranelift-wasm/std", "wasmtime-environ/std", "wasmtime-debug/std", "wasmtime-runtime/std", "wasmparser/std"]
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
//! Memory management for executable code.
|
//! Memory management for executable code.
|
||||||
|
|
||||||
|
use crate::function_table::FunctionTable;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::{cmp, mem};
|
use core::{cmp, mem};
|
||||||
use region;
|
use region;
|
||||||
|
use wasmtime_environ::{Compilation, CompiledFunction};
|
||||||
use wasmtime_runtime::{Mmap, VMFunctionBody};
|
use wasmtime_runtime::{Mmap, VMFunctionBody};
|
||||||
|
|
||||||
/// Memory manager for executable code.
|
/// Memory manager for executable code.
|
||||||
pub struct CodeMemory {
|
pub struct CodeMemory {
|
||||||
current: Mmap,
|
current: (Mmap, FunctionTable),
|
||||||
mmaps: Vec<Mmap>,
|
mmaps: Vec<(Mmap, FunctionTable)>,
|
||||||
position: usize,
|
position: usize,
|
||||||
published: usize,
|
published: usize,
|
||||||
}
|
}
|
||||||
@@ -19,30 +21,144 @@ impl CodeMemory {
|
|||||||
/// Create a new `CodeMemory` instance.
|
/// Create a new `CodeMemory` instance.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
current: Mmap::new(),
|
current: (Mmap::new(), FunctionTable::new()),
|
||||||
mmaps: Vec::new(),
|
mmaps: Vec::new(),
|
||||||
position: 0,
|
position: 0,
|
||||||
published: 0,
|
published: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate a continuous memory block for a single compiled function.
|
||||||
|
/// TODO: Reorganize the code that calls this to emit code directly into the
|
||||||
|
/// mmap region rather than into a Vec that we need to copy in.
|
||||||
|
pub fn allocate_for_function(
|
||||||
|
&mut self,
|
||||||
|
func: &CompiledFunction,
|
||||||
|
) -> Result<&mut [VMFunctionBody], String> {
|
||||||
|
let size = Self::function_allocation_size(func);
|
||||||
|
|
||||||
|
let start = self.position as u32;
|
||||||
|
let (buf, table) = self.allocate(size)?;
|
||||||
|
|
||||||
|
let (_, _, _, vmfunc) = Self::copy_function(func, start, buf, table);
|
||||||
|
|
||||||
|
Ok(vmfunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a continuous memory block for a compilation.
|
||||||
|
///
|
||||||
|
/// Allocates memory for both the function bodies as well as function unwind data.
|
||||||
|
pub fn allocate_for_compilation(
|
||||||
|
&mut self,
|
||||||
|
compilation: &Compilation,
|
||||||
|
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
|
||||||
|
let total_len = compilation
|
||||||
|
.into_iter()
|
||||||
|
.fold(0, |acc, func| acc + Self::function_allocation_size(func));
|
||||||
|
|
||||||
|
let mut start = self.position as u32;
|
||||||
|
let (mut buf, mut table) = self.allocate(total_len)?;
|
||||||
|
let mut result = Vec::with_capacity(compilation.len());
|
||||||
|
|
||||||
|
for func in compilation.into_iter() {
|
||||||
|
let (next_start, next_buf, next_table, vmfunc) =
|
||||||
|
Self::copy_function(func, start, buf, table);
|
||||||
|
|
||||||
|
result.push(vmfunc);
|
||||||
|
|
||||||
|
start = next_start;
|
||||||
|
buf = next_buf;
|
||||||
|
table = next_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make all allocated memory executable.
|
||||||
|
pub fn publish(&mut self) {
|
||||||
|
self.push_current(0)
|
||||||
|
.expect("failed to push current memory map");
|
||||||
|
|
||||||
|
for (m, t) in &mut self.mmaps[self.published..] {
|
||||||
|
if m.len() != 0 {
|
||||||
|
unsafe {
|
||||||
|
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
||||||
|
}
|
||||||
|
.expect("unable to make memory readonly and executable");
|
||||||
|
}
|
||||||
|
|
||||||
|
t.publish(m.as_ptr() as u64)
|
||||||
|
.expect("failed to publish function table");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.published = self.mmaps.len();
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate `size` bytes of memory which can be made executable later by
|
/// Allocate `size` bytes of memory which can be made executable later by
|
||||||
/// calling `publish()`. Note that we allocate the memory as writeable so
|
/// calling `publish()`. Note that we allocate the memory as writeable so
|
||||||
/// that it can be written to and patched, though we make it readonly before
|
/// that it can be written to and patched, though we make it readonly before
|
||||||
/// actually executing from it.
|
/// actually executing from it.
|
||||||
///
|
///
|
||||||
/// TODO: Add an alignment flag.
|
/// TODO: Add an alignment flag.
|
||||||
fn allocate(&mut self, size: usize) -> Result<&mut [u8], String> {
|
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable), String> {
|
||||||
if self.current.len() - self.position < size {
|
if self.current.0.len() - self.position < size {
|
||||||
self.mmaps.push(mem::replace(
|
self.push_current(cmp::max(0x10000, size))?;
|
||||||
&mut self.current,
|
|
||||||
Mmap::with_at_least(cmp::max(0x10000, size))?,
|
|
||||||
));
|
|
||||||
self.position = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_position = self.position;
|
let old_position = self.position;
|
||||||
self.position += size;
|
self.position += size;
|
||||||
Ok(&mut self.current.as_mut_slice()[old_position..self.position])
|
|
||||||
|
Ok((
|
||||||
|
&mut self.current.0.as_mut_slice()[old_position..self.position],
|
||||||
|
&mut self.current.1,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the allocation size of the given compiled function.
|
||||||
|
fn function_allocation_size(func: &CompiledFunction) -> usize {
|
||||||
|
if func.unwind_info.is_empty() {
|
||||||
|
func.body.len()
|
||||||
|
} else {
|
||||||
|
// Account for necessary unwind information alignment padding (32-bit)
|
||||||
|
((func.body.len() + 3) & !3) + func.unwind_info.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies the data of the compiled function to the given buffer.
|
||||||
|
///
|
||||||
|
/// This will also add the function to the current function table.
|
||||||
|
fn copy_function<'a>(
|
||||||
|
func: &CompiledFunction,
|
||||||
|
func_start: u32,
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
table: &'a mut FunctionTable,
|
||||||
|
) -> (
|
||||||
|
u32,
|
||||||
|
&'a mut [u8],
|
||||||
|
&'a mut FunctionTable,
|
||||||
|
&'a mut [VMFunctionBody],
|
||||||
|
) {
|
||||||
|
let func_end = func_start + (func.body.len() as u32);
|
||||||
|
|
||||||
|
let (body, remainder) = buf.split_at_mut(func.body.len());
|
||||||
|
body.copy_from_slice(&func.body);
|
||||||
|
let vmfunc = Self::view_as_mut_vmfunc_slice(body);
|
||||||
|
|
||||||
|
if func.unwind_info.is_empty() {
|
||||||
|
return (func_end, remainder, table, vmfunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
|
||||||
|
let padding = ((func.body.len() + 3) & !3) - func.body.len();
|
||||||
|
let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len());
|
||||||
|
unwind[padding..].copy_from_slice(&func.unwind_info);
|
||||||
|
|
||||||
|
let unwind_start = func_end + (padding as u32);
|
||||||
|
let unwind_end = unwind_start + (func.unwind_info.len() as u32);
|
||||||
|
|
||||||
|
table.add_function(func_start, func_end, unwind_start);
|
||||||
|
|
||||||
|
(unwind_end, remainder, table, vmfunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert mut a slice from u8 to VMFunctionBody.
|
/// Convert mut a slice from u8 to VMFunctionBody.
|
||||||
@@ -52,51 +168,28 @@ impl CodeMemory {
|
|||||||
unsafe { &mut *body_ptr }
|
unsafe { &mut *body_ptr }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate enough memory to hold a copy of `slice` and copy the data into it.
|
/// Pushes the current Mmap (and function table) and allocates a new Mmap of the given size.
|
||||||
/// TODO: Reorganize the code that calls this to emit code directly into the
|
fn push_current(&mut self, new_size: usize) -> Result<(), String> {
|
||||||
/// mmap region rather than into a Vec that we need to copy in.
|
let previous = mem::replace(
|
||||||
pub fn allocate_copy_of_byte_slice(
|
&mut self.current,
|
||||||
&mut self,
|
(
|
||||||
slice: &[u8],
|
if new_size == 0 {
|
||||||
) -> Result<&mut [VMFunctionBody], String> {
|
Mmap::new()
|
||||||
let new = self.allocate(slice.len())?;
|
} else {
|
||||||
new.copy_from_slice(slice);
|
Mmap::with_at_least(cmp::max(0x10000, new_size))?
|
||||||
Ok(Self::view_as_mut_vmfunc_slice(new))
|
},
|
||||||
|
FunctionTable::new(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if previous.0.len() > 0 {
|
||||||
|
self.mmaps.push(previous);
|
||||||
|
} else {
|
||||||
|
assert!(previous.1.len() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate enough continuous memory block for multiple code blocks. See also
|
|
||||||
/// allocate_copy_of_byte_slice.
|
|
||||||
pub fn allocate_copy_of_byte_slices(
|
|
||||||
&mut self,
|
|
||||||
slices: &[&[u8]],
|
|
||||||
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
|
|
||||||
let total_len = slices.into_iter().fold(0, |acc, slice| acc + slice.len());
|
|
||||||
let new = self.allocate(total_len)?;
|
|
||||||
let mut tail = new;
|
|
||||||
let mut result = Vec::with_capacity(slices.len());
|
|
||||||
for slice in slices {
|
|
||||||
let (block, next_tail) = tail.split_at_mut(slice.len());
|
|
||||||
block.copy_from_slice(slice);
|
|
||||||
tail = next_tail;
|
|
||||||
result.push(Self::view_as_mut_vmfunc_slice(block));
|
|
||||||
}
|
|
||||||
Ok(result.into_boxed_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make all allocated memory executable.
|
|
||||||
pub fn publish(&mut self) {
|
|
||||||
self.mmaps
|
|
||||||
.push(mem::replace(&mut self.current, Mmap::new()));
|
|
||||||
self.position = 0;
|
self.position = 0;
|
||||||
|
|
||||||
for m in &mut self.mmaps[self.published..] {
|
Ok(())
|
||||||
if m.len() != 0 {
|
|
||||||
unsafe {
|
|
||||||
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
|
||||||
}
|
|
||||||
.expect("unable to make memory readonly and executable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.published = self.mmaps.len();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
|||||||
use cranelift_wasm::{DefinedFuncIndex, DefinedMemoryIndex, ModuleTranslationState};
|
use cranelift_wasm::{DefinedFuncIndex, DefinedMemoryIndex, ModuleTranslationState};
|
||||||
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
Compilation, CompileError, Compiler as _C, FunctionBodyData, Module, ModuleVmctxInfo,
|
Compilation, CompileError, CompiledFunction, Compiler as _C, FunctionBodyData, Module,
|
||||||
Relocations, Traps, Tunables, VMOffsets,
|
ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets,
|
||||||
};
|
};
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
get_mut_trap_registry, InstantiationError, SignatureRegistry, TrapRegistrationGuard,
|
get_mut_trap_registry, InstantiationError, SignatureRegistry, TrapRegistrationGuard,
|
||||||
@@ -323,7 +323,8 @@ fn make_trampoline(
|
|||||||
builder.finalize()
|
builder.finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut code_buf: Vec<u8> = Vec::new();
|
let mut code_buf = Vec::new();
|
||||||
|
let mut unwind_info = Vec::new();
|
||||||
let mut reloc_sink = RelocSink {};
|
let mut reloc_sink = RelocSink {};
|
||||||
let mut trap_sink = binemit::NullTrapSink {};
|
let mut trap_sink = binemit::NullTrapSink {};
|
||||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||||
@@ -337,8 +338,14 @@ fn make_trampoline(
|
|||||||
)
|
)
|
||||||
.map_err(|error| SetupError::Compile(CompileError::Codegen(error)))?;
|
.map_err(|error| SetupError::Compile(CompileError::Codegen(error)))?;
|
||||||
|
|
||||||
|
context.emit_unwind_info(isa, &mut unwind_info);
|
||||||
|
|
||||||
Ok(code_memory
|
Ok(code_memory
|
||||||
.allocate_copy_of_byte_slice(&code_buf)
|
.allocate_for_function(&CompiledFunction {
|
||||||
|
body: code_buf,
|
||||||
|
jt_offsets: context.func.jt_offsets,
|
||||||
|
unwind_info,
|
||||||
|
})
|
||||||
.map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))?
|
.map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))?
|
||||||
.as_ptr())
|
.as_ptr())
|
||||||
}
|
}
|
||||||
@@ -347,14 +354,8 @@ fn allocate_functions(
|
|||||||
code_memory: &mut CodeMemory,
|
code_memory: &mut CodeMemory,
|
||||||
compilation: &Compilation,
|
compilation: &Compilation,
|
||||||
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
|
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
|
||||||
// Allocate code for all function in one continuous memory block.
|
let fat_ptrs = code_memory.allocate_for_compilation(compilation)?;
|
||||||
// First, collect all function bodies into vector to pass to the
|
|
||||||
// allocate_copy_of_byte_slices.
|
|
||||||
let bodies = compilation
|
|
||||||
.into_iter()
|
|
||||||
.map(|code_and_jt| &code_and_jt.body[..])
|
|
||||||
.collect::<Vec<&[u8]>>();
|
|
||||||
let fat_ptrs = code_memory.allocate_copy_of_byte_slices(&bodies)?;
|
|
||||||
// Second, create a PrimaryMap from result vector of pointers.
|
// Second, create a PrimaryMap from result vector of pointers.
|
||||||
let mut result = PrimaryMap::with_capacity(compilation.len());
|
let mut result = PrimaryMap::with_capacity(compilation.len());
|
||||||
for i in 0..fat_ptrs.len() {
|
for i in 0..fat_ptrs.len() {
|
||||||
|
|||||||
134
wasmtime-jit/src/function_table.rs
Normal file
134
wasmtime-jit/src/function_table.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
//! Runtime function table.
|
||||||
|
//!
|
||||||
|
//! This module is primarily used to track JIT functions on Windows for stack walking and unwind.
|
||||||
|
|
||||||
|
/// Represents a runtime function table.
|
||||||
|
///
|
||||||
|
/// The runtime function table is not implemented for non-Windows target platforms.
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub(crate) struct FunctionTable;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
impl FunctionTable {
|
||||||
|
/// Creates a new function table.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of functions in the table, also referred to as its 'length'.
|
||||||
|
///
|
||||||
|
/// For non-Windows platforms, the table will always be empty.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
|
||||||
|
///
|
||||||
|
/// The offsets are from the "module base", which is provided when the table is published.
|
||||||
|
///
|
||||||
|
/// For non-Windows platforms, this is a no-op.
|
||||||
|
pub fn add_function(&mut self, _start: u32, _end: u32, _unwind: u32) {}
|
||||||
|
|
||||||
|
/// Publishes the function table using the given base address.
|
||||||
|
///
|
||||||
|
/// A published function table will automatically be deleted when it is dropped.
|
||||||
|
///
|
||||||
|
/// For non-Windows platforms, this is a no-op.
|
||||||
|
pub fn publish(&mut self, _base_address: u64) -> Result<(), String> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a runtime function table.
|
||||||
|
///
|
||||||
|
/// This is used to register JIT code with the operating system to enable stack walking and unwinding.
|
||||||
|
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
||||||
|
pub(crate) struct FunctionTable {
|
||||||
|
functions: Vec<winapi::um::winnt::RUNTIME_FUNCTION>,
|
||||||
|
published: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
||||||
|
impl FunctionTable {
|
||||||
|
/// Creates a new function table.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: Vec::new(),
|
||||||
|
published: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of functions in the table, also referred to as its 'length'.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.functions.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
|
||||||
|
///
|
||||||
|
/// The offsets are from the "module base", which is provided when the table is published.
|
||||||
|
pub fn add_function(&mut self, start: u32, end: u32, unwind: u32) {
|
||||||
|
use winapi::um::winnt;
|
||||||
|
|
||||||
|
assert!(!self.published, "table has already been published");
|
||||||
|
|
||||||
|
let mut entry = winnt::RUNTIME_FUNCTION::default();
|
||||||
|
|
||||||
|
entry.BeginAddress = start;
|
||||||
|
entry.EndAddress = end;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*entry.u.UnwindInfoAddress_mut() = unwind;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.functions.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Publishes the function table using the given base address.
|
||||||
|
///
|
||||||
|
/// A published function table will automatically be deleted when it is dropped.
|
||||||
|
pub fn publish(&mut self, base_address: u64) -> Result<(), String> {
|
||||||
|
use winapi::um::winnt;
|
||||||
|
|
||||||
|
if self.published {
|
||||||
|
return Err("function table was already published".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.published = true;
|
||||||
|
|
||||||
|
if self.functions.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Windows heap allocations are 32-bit aligned, but assert just in case
|
||||||
|
assert!(
|
||||||
|
(self.functions.as_mut_ptr() as u64) % 4 == 0,
|
||||||
|
"function table allocation was not aligned"
|
||||||
|
);
|
||||||
|
|
||||||
|
if winnt::RtlAddFunctionTable(
|
||||||
|
self.functions.as_mut_ptr(),
|
||||||
|
self.functions.len() as u32,
|
||||||
|
base_address,
|
||||||
|
) == 0
|
||||||
|
{
|
||||||
|
return Err("failed to add function table".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
impl Drop for FunctionTable {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
use winapi::um::winnt;
|
||||||
|
|
||||||
|
if self.published {
|
||||||
|
unsafe {
|
||||||
|
winnt::RtlDeleteFunctionTable(self.functions.as_mut_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ mod action;
|
|||||||
mod code_memory;
|
mod code_memory;
|
||||||
mod compiler;
|
mod compiler;
|
||||||
mod context;
|
mod context;
|
||||||
|
mod function_table;
|
||||||
mod instantiate;
|
mod instantiate;
|
||||||
mod link;
|
mod link;
|
||||||
mod namespace;
|
mod namespace;
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ static
|
|||||||
__attribute__ ((warn_unused_result))
|
__attribute__ ((warn_unused_result))
|
||||||
#endif
|
#endif
|
||||||
bool
|
bool
|
||||||
HandleTrap(CONTEXT* context)
|
HandleTrap(CONTEXT* context, bool reset_guard_page)
|
||||||
{
|
{
|
||||||
assert(sAlreadyHandlingTrap);
|
assert(sAlreadyHandlingTrap);
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@ HandleTrap(CONTEXT* context)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordTrap(ContextToPC(context));
|
RecordTrap(ContextToPC(context), reset_guard_page);
|
||||||
|
|
||||||
// Unwind calls longjmp, so it doesn't run the automatic
|
// Unwind calls longjmp, so it doesn't run the automatic
|
||||||
// sAlreadhHanldingTrap cleanups, so reset it manually before doing
|
// sAlreadhHanldingTrap cleanups, so reset it manually before doing
|
||||||
@@ -467,7 +467,8 @@ WasmTrapHandler(LPEXCEPTION_POINTERS exception)
|
|||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HandleTrap(exception->ContextRecord)) {
|
if (!HandleTrap(exception->ContextRecord,
|
||||||
|
record->ExceptionCode == EXCEPTION_STACK_OVERFLOW)) {
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +550,7 @@ HandleMachException(const ExceptionRequest& request)
|
|||||||
|
|
||||||
{
|
{
|
||||||
AutoHandlingTrap aht;
|
AutoHandlingTrap aht;
|
||||||
if (!HandleTrap(&context)) {
|
if (!HandleTrap(&context, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,7 +633,7 @@ WasmTrapHandler(int signum, siginfo_t* info, void* context)
|
|||||||
if (!sAlreadyHandlingTrap) {
|
if (!sAlreadyHandlingTrap) {
|
||||||
AutoHandlingTrap aht;
|
AutoHandlingTrap aht;
|
||||||
assert(signum == SIGSEGV || signum == SIGBUS || signum == SIGFPE || signum == SIGILL);
|
assert(signum == SIGSEGV || signum == SIGBUS || signum == SIGFPE || signum == SIGILL);
|
||||||
if (HandleTrap(static_cast<CONTEXT*>(context))) {
|
if (HandleTrap(static_cast<CONTEXT*>(context), false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ extern "C" {
|
|||||||
|
|
||||||
int8_t CheckIfTrapAtAddress(const uint8_t* pc);
|
int8_t CheckIfTrapAtAddress(const uint8_t* pc);
|
||||||
// Record the Trap code and wasm bytecode offset in TLS somewhere
|
// Record the Trap code and wasm bytecode offset in TLS somewhere
|
||||||
void RecordTrap(const uint8_t* pc);
|
void RecordTrap(const uint8_t* pc, bool reset_guard_page);
|
||||||
|
|
||||||
void* EnterScope(void*);
|
void* EnterScope(void*);
|
||||||
void LeaveScope(void*);
|
void LeaveScope(void*);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ extern "C" {
|
|||||||
thread_local! {
|
thread_local! {
|
||||||
static RECORDED_TRAP: Cell<Option<TrapDescription>> = Cell::new(None);
|
static RECORDED_TRAP: Cell<Option<TrapDescription>> = Cell::new(None);
|
||||||
static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null());
|
static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null());
|
||||||
|
static RESET_GUARD_PAGE: Cell<bool> = Cell::new(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there is a trap at given PC
|
/// Check if there is a trap at given PC
|
||||||
@@ -40,7 +41,7 @@ pub extern "C" fn CheckIfTrapAtAddress(_pc: *const u8) -> i8 {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn RecordTrap(pc: *const u8) {
|
pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) {
|
||||||
// TODO: please see explanation in CheckIfTrapAtAddress.
|
// TODO: please see explanation in CheckIfTrapAtAddress.
|
||||||
let registry = get_trap_registry();
|
let registry = get_trap_registry();
|
||||||
let trap_desc = registry
|
let trap_desc = registry
|
||||||
@@ -49,6 +50,11 @@ pub extern "C" fn RecordTrap(pc: *const u8) {
|
|||||||
source_loc: ir::SourceLoc::default(),
|
source_loc: ir::SourceLoc::default(),
|
||||||
trap_code: ir::TrapCode::StackOverflow,
|
trap_code: ir::TrapCode::StackOverflow,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if reset_guard_page {
|
||||||
|
RESET_GUARD_PAGE.with(|v| v.set(true));
|
||||||
|
}
|
||||||
|
|
||||||
RECORDED_TRAP.with(|data| {
|
RECORDED_TRAP.with(|data| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.get(),
|
data.get(),
|
||||||
@@ -77,9 +83,32 @@ pub extern "C" fn GetScope() -> *const u8 {
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn LeaveScope(ptr: *const u8) {
|
pub extern "C" fn LeaveScope(ptr: *const u8) {
|
||||||
|
RESET_GUARD_PAGE.with(|v| {
|
||||||
|
if v.get() {
|
||||||
|
reset_guard_page();
|
||||||
|
v.set(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
JMP_BUF.with(|buf| buf.set(ptr))
|
JMP_BUF.with(|buf| buf.set(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn reset_guard_page() {
|
||||||
|
extern "C" {
|
||||||
|
fn _resetstkoflw() -> winapi::ctypes::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to restore guard page under stack to handle future stack overflows properly.
|
||||||
|
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=vs-2019
|
||||||
|
if unsafe { _resetstkoflw() } == 0 {
|
||||||
|
panic!("failed to restore stack guard page");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn reset_guard_page() {}
|
||||||
|
|
||||||
fn trap_message() -> String {
|
fn trap_message() -> String {
|
||||||
let trap_desc = RECORDED_TRAP
|
let trap_desc = RECORDED_TRAP
|
||||||
.with(|data| data.replace(None))
|
.with(|data| data.replace(None))
|
||||||
|
|||||||
Reference in New Issue
Block a user