diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 418dcde67e..a910225dd3 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -65,12 +65,6 @@ jobs: submodules: true - template: ci/azure-install-rust.yml - - script: | - powershell -Command "$ProgressPreference = 'SilentlyContinue'; iwr -outf %TEMP%\LLVM-8.0.0-win64.exe https://rust-lang-ci2.s3.amazonaws.com/rust-ci-mirror/LLVM-8.0.0-win64.exe" - %TEMP%\LLVM-8.0.0-win64.exe /S /NCRC - condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: Install clang (Windows) - - script: cargo fetch displayName: Fetch cargo dependencies @@ -128,12 +122,6 @@ jobs: - bash: echo "##vso[task.setvariable variable=RUSTC_VERSION;]`rustc --version`" displayName: Set rustc version string for caching - - script: | - powershell -Command "$ProgressPreference = 'SilentlyContinue'; iwr -outf %TEMP%\LLVM-8.0.0-win64.exe https://rust-lang-ci2.s3.amazonaws.com/rust-ci-mirror/LLVM-8.0.0-win64.exe" - %TEMP%\LLVM-8.0.0-win64.exe /S /NCRC - condition: eq(variables['Agent.OS'], 'Windows_NT') - displayName: Install clang (Windows) - # - bash: | # set -e # curl -Lfo sccache.tar.gz https://github.com/mozilla/sccache/releases/download/0.2.9/sccache-0.2.9-x86_64-apple-darwin.tar.gz diff --git a/README.md b/README.md index 4861421363..2f822ca159 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,11 @@ obtain a full checkout, like this: git clone --recurse-submodules https://github.com/CraneStation/wasmtime.git ``` -To build Wasmtime, install cmake and clang, and then build with cargo, such -as with "cargo build --release". For information on installing clang, see -[rust-bindgen's documentation](https://github.com/rust-lang/rust-bindgen/blob/master/book/src/requirements.md). +To build an optimized version of Wasmtime, use Cargo: + +``` +cargo build --release +``` There are Rust, C, and C++ toolchains that can compile programs with WASI. See the [WASI intro][WASI intro] for more information, and the [WASI tutorial][WASI tutorial] diff --git a/wasmtime-runtime/Cargo.toml b/wasmtime-runtime/Cargo.toml index 995120581c..225b560a6f 100644 --- a/wasmtime-runtime/Cargo.toml +++ b/wasmtime-runtime/Cargo.toml @@ -29,9 +29,7 @@ indexmap = "1.0.2" winapi = { version = "0.3.6", features = ["winbase", "memoryapi"] } [build-dependencies] -cmake = "0.1.35" -bindgen = "0.51.0" -regex = "1.0.6" +cc = "1.0" [features] default = ["std"] diff --git a/wasmtime-runtime/build.rs b/wasmtime-runtime/build.rs index 2e855b6c91..a2db1468e8 100644 --- a/wasmtime-runtime/build.rs +++ b/wasmtime-runtime/build.rs @@ -1,37 +1,20 @@ -extern crate bindgen; -extern crate cmake; -extern crate regex; - -use cmake::Config; -use regex::Regex; -use std::env; -use std::path::PathBuf; - fn main() { - let dst = Config::new("signalhandlers").build(); - - println!("cargo:rustc-link-search=native={}", dst.display()); - println!("cargo:rustc-link-lib=static=SignalHandlers"); - - let mut bindings_builder = bindgen::Builder::default() - .header("signalhandlers/SignalHandlers.hpp") - .whitelist_type("TrapContext") - .whitelist_type("jmp_buf") - .whitelist_function("EnsureEagerSignalHandlers"); - - // If we're compiling for Darwin, compile in extra Darwin support routines. - if Regex::new(r"-darwin[[:digit:].]*$") - .unwrap() - .is_match(&env::var("TARGET").unwrap()) - { - bindings_builder = bindings_builder.whitelist_function("EnsureDarwinMachPorts"); + println!("cargo:rerun-if-changed=signalhandlers/SignalHandlers.cpp"); + println!("cargo:rerun-if-changed=signalhandlers/SignalHandlers.hpp"); + println!("cargo:rerun-if-changed=signalhandlers/Trampolines.cpp"); + let target = std::env::var("TARGET").unwrap(); + let mut build = cc::Build::new(); + build + .cpp(true) + .warnings(false) + .file("signalhandlers/SignalHandlers.cpp") + .file("signalhandlers/Trampolines.cpp"); + if !target.contains("windows") { + build + .flag("-std=c++11") + .flag("-fno-exceptions") + .flag("-fno-rtti"); } - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - - bindings_builder - .generate() - .expect("Unable to generate bindings") - .write_to_file(out_path.join("signalhandlers.rs")) - .expect("Couldn't write bindings!"); + build.compile("signalhandlers"); } diff --git a/wasmtime-runtime/signalhandlers/CMakeLists.txt b/wasmtime-runtime/signalhandlers/CMakeLists.txt deleted file mode 100644 index b1568f2676..0000000000 --- a/wasmtime-runtime/signalhandlers/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -project(SignalHandlers CXX) - -if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - set(CMAKE_CXX_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -fPIC") -else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - set(CMAKE_CXX_FLAGS "-m32 -std=c++11 -fno-exceptions -fno-rtti -fPIC") -endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - -add_library(SignalHandlers STATIC SignalHandlers.cpp) - -install(TARGETS SignalHandlers DESTINATION .) diff --git a/wasmtime-runtime/signalhandlers/SignalHandlers.cpp b/wasmtime-runtime/signalhandlers/SignalHandlers.cpp index 588ac8e341..3f3635f11d 100644 --- a/wasmtime-runtime/signalhandlers/SignalHandlers.cpp +++ b/wasmtime-runtime/signalhandlers/SignalHandlers.cpp @@ -668,7 +668,7 @@ WasmTrapHandler(int signum, siginfo_t* info, void* context) extern "C" MFBT_API bool IsSignalHandlingBroken(); #endif -bool +int EnsureEagerSignalHandlers() { #if defined(ANDROID) && defined(MOZ_LINKER) @@ -765,7 +765,7 @@ EnsureEagerSignalHandlers() return true; } -bool +int EnsureDarwinMachPorts() { #ifdef USE_APPLE_MACH_PORTS diff --git a/wasmtime-runtime/signalhandlers/SignalHandlers.hpp b/wasmtime-runtime/signalhandlers/SignalHandlers.hpp index a7214e92fa..3e98065283 100644 --- a/wasmtime-runtime/signalhandlers/SignalHandlers.hpp +++ b/wasmtime-runtime/signalhandlers/SignalHandlers.hpp @@ -14,15 +14,11 @@ extern "C" { // Record the Trap code and wasm bytecode offset in TLS somewhere void RecordTrap(const uint8_t* pc); -// Initiate an unwind. +void* EnterScope(void*); +void LeaveScope(void*); +void* GetScope(void); void Unwind(void); -// Trap initialization state. -struct TrapContext { - bool triedToInstallSignalHandlers; - bool haveSignalHandlers; -}; - // This function performs the low-overhead signal handler initialization that we // want to do eagerly to ensure a more-deterministic global process state. This // is especially relevant for signal handlers since handler ordering depends on @@ -31,14 +27,14 @@ struct TrapContext { // called at the end of the startup process, after other handlers have been // installed. This function can thus be called multiple times, having no effect // after the first call. -bool +int EnsureEagerSignalHandlers(void); // Assuming EnsureEagerProcessSignalHandlers() has already been called, // this function performs the full installation of signal handlers which must // be performed per-thread. This operation may incur some overhead and // so should be done only when needed to use wasm. -bool +int EnsureDarwinMachPorts(void); #ifdef __cplusplus diff --git a/wasmtime-runtime/signalhandlers/Trampolines.cpp b/wasmtime-runtime/signalhandlers/Trampolines.cpp new file mode 100644 index 0000000000..4c6bbcc310 --- /dev/null +++ b/wasmtime-runtime/signalhandlers/Trampolines.cpp @@ -0,0 +1,37 @@ +#include + +#include "SignalHandlers.hpp" + +extern "C" +int WasmtimeCallTrampoline(void *vmctx, void (*body)(void*, void*), void *args) { + jmp_buf buf; + void *volatile prev; + if (setjmp(buf) != 0) { + LeaveScope(prev); + return 0; + } + prev = EnterScope(&buf); + body(vmctx, args); + LeaveScope(prev); + return 1; +} + +extern "C" +int WasmtimeCall(void *vmctx, void (*body)(void*)) { + jmp_buf buf; + void *volatile prev; + if (setjmp(buf) != 0) { + LeaveScope(prev); + return 0; + } + prev = EnterScope(&buf); + body(vmctx); + LeaveScope(prev); + return 1; +} + +extern "C" +void Unwind() { + jmp_buf *buf = (jmp_buf*) GetScope(); + longjmp(*buf, 1); +} diff --git a/wasmtime-runtime/src/signalhandlers.rs b/wasmtime-runtime/src/signalhandlers.rs index b9cc41c0ae..a383e54995 100644 --- a/wasmtime-runtime/src/signalhandlers.rs +++ b/wasmtime-runtime/src/signalhandlers.rs @@ -6,10 +6,20 @@ use crate::vmcontext::VMContext; use core::borrow::{Borrow, BorrowMut}; -use core::cell::RefCell; +use core::cell::Cell; use std::sync::RwLock; -include!(concat!(env!("OUT_DIR"), "/signalhandlers.rs")); +#[derive(Default)] +struct TrapContext { + tried_to_install_signal_handlers: Cell, + have_signal_handlers: Cell, +} + +extern "C" { + fn EnsureEagerSignalHandlers() -> libc::c_int; + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn EnsureDarwinMachPorts() -> libc::c_int; +} struct InstallState { tried: bool, @@ -50,7 +60,7 @@ pub extern "C" fn wasmtime_init_eager() { state.tried = true; assert!(!state.success); - if !unsafe { EnsureEagerSignalHandlers() } { + if unsafe { EnsureEagerSignalHandlers() == 0 } { return; } @@ -58,8 +68,7 @@ pub extern "C" fn wasmtime_init_eager() { } thread_local! { - static TRAP_CONTEXT: RefCell = - RefCell::new(TrapContext { triedToInstallSignalHandlers: false, haveSignalHandlers: false }); + static TRAP_CONTEXT: TrapContext = TrapContext::default(); } /// Assuming `EnsureEagerProcessSignalHandlers` has already been called, @@ -68,10 +77,10 @@ thread_local! { /// so should be done only when needed to use wasm. #[no_mangle] pub extern "C" fn wasmtime_init_finish(vmctx: &mut VMContext) { - if !TRAP_CONTEXT.with(|cx| cx.borrow().triedToInstallSignalHandlers) { + if !TRAP_CONTEXT.with(|cx| cx.tried_to_install_signal_handlers.get()) { TRAP_CONTEXT.with(|cx| { - cx.borrow_mut().triedToInstallSignalHandlers = true; - assert!(!cx.borrow().haveSignalHandlers); + cx.tried_to_install_signal_handlers.set(true); + assert!(!cx.have_signal_handlers.get()); }); { @@ -90,12 +99,12 @@ pub extern "C" fn wasmtime_init_finish(vmctx: &mut VMContext) { ensure_darwin_mach_ports(); TRAP_CONTEXT.with(|cx| { - cx.borrow_mut().haveSignalHandlers = true; + cx.have_signal_handlers.set(true); }) } let instance = unsafe { vmctx.instance() }; - let have_signal_handlers = TRAP_CONTEXT.with(|cx| cx.borrow().haveSignalHandlers); + let have_signal_handlers = TRAP_CONTEXT.with(|cx| cx.have_signal_handlers.get()); if !have_signal_handlers && instance.needs_signal_handlers() { panic!("failed to install signal handlers"); } @@ -113,7 +122,7 @@ fn ensure_darwin_mach_ports() { state.tried = true; assert!(!state.success); - if !unsafe { EnsureDarwinMachPorts() } { + if unsafe { EnsureDarwinMachPorts() != 0 } { return; } diff --git a/wasmtime-runtime/src/traphandlers.rs b/wasmtime-runtime/src/traphandlers.rs index 76a4cde7ad..360c392d2f 100644 --- a/wasmtime-runtime/src/traphandlers.rs +++ b/wasmtime-runtime/src/traphandlers.rs @@ -1,28 +1,23 @@ //! WebAssembly trap handling, which is built on top of the lower-level //! signalhandling mechanisms. -use crate::signalhandlers::jmp_buf; use crate::vmcontext::{VMContext, VMFunctionBody}; -use core::cell::{Cell, RefCell}; -use core::mem; +use core::cell::Cell; use core::ptr; -use libc::c_int; use std::string::String; -use std::vec::Vec; -// Currently we uset setjmp/longjmp to unwind out of a signal handler -// and back to the point where WebAssembly was called (via `call_wasm`). -// This works because WebAssembly code currently does not use any EH -// or require any cleanups, and we never unwind through non-wasm frames. -// In the future, we'll likely replace this with fancier stack unwinding. extern "C" { - fn setjmp(env: *mut jmp_buf) -> c_int; - fn longjmp(env: *const jmp_buf, val: c_int) -> !; + fn WasmtimeCallTrampoline( + vmctx: *mut u8, + callee: *const VMFunctionBody, + values_vec: *mut u8, + ) -> i32; + fn WasmtimeCall(vmctx: *mut u8, callee: *const VMFunctionBody) -> i32; } thread_local! { static TRAP_PC: Cell<*const u8> = Cell::new(ptr::null()); - static JMP_BUFS: RefCell> = RefCell::new(Vec::new()); + static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null()); } /// Record the Trap code and wasm bytecode offset in TLS somewhere @@ -34,43 +29,25 @@ pub extern "C" fn RecordTrap(pc: *const u8) { TRAP_PC.with(|data| data.set(pc)); } -/// Initiate an unwind. #[doc(hidden)] #[allow(non_snake_case)] #[no_mangle] -pub extern "C" fn Unwind() { - JMP_BUFS.with(|bufs| { - let buf = bufs.borrow_mut().pop().unwrap(); - unsafe { longjmp(&buf, 1) }; - }) +pub extern "C" fn EnterScope(ptr: *const u8) -> *const u8 { + JMP_BUF.with(|buf| buf.replace(ptr)) } -/// A simple guard to ensure that `JMP_BUFS` is reset when we're done. -struct ScopeGuard { - orig_num_bufs: usize, +#[doc(hidden)] +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn GetScope() -> *const u8 { + JMP_BUF.with(|buf| buf.get()) } -impl ScopeGuard { - fn new() -> Self { - assert_eq!( - TRAP_PC.with(Cell::get), - ptr::null(), - "unfinished trap detected" - ); - Self { - orig_num_bufs: JMP_BUFS.with(|bufs| bufs.borrow().len()), - } - } -} - -impl Drop for ScopeGuard { - fn drop(&mut self) { - let orig_num_bufs = self.orig_num_bufs; - JMP_BUFS.with(|bufs| { - bufs.borrow_mut() - .resize(orig_num_bufs, unsafe { mem::zeroed() }) - }); - } +#[doc(hidden)] +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn LeaveScope(ptr: *const u8) { + JMP_BUF.with(|buf| buf.set(ptr)) } fn trap_message(_vmctx: *mut VMContext) -> String { @@ -82,10 +59,6 @@ fn trap_message(_vmctx: *mut VMContext) -> String { format!("wasm trap at {:?}", pc) } -fn push_jmp_buf(buf: jmp_buf) { - JMP_BUFS.with(|bufs| bufs.borrow_mut().push(buf)); -} - /// Call the wasm function pointed to by `callee`. `values_vec` points to /// a buffer which holds the incoming arguments, and to which the outgoing /// return values will be written. @@ -95,21 +68,11 @@ pub unsafe extern "C" fn wasmtime_call_trampoline( callee: *const VMFunctionBody, values_vec: *mut u8, ) -> Result<(), String> { - // Reset JMP_BUFS if the stack is unwound through this point. - let _guard = ScopeGuard::new(); - - // Set a setjmp catch point. - let mut buf = mem::uninitialized(); - if setjmp(&mut buf) != 0 { - return Err(trap_message(vmctx)); + if WasmtimeCallTrampoline(vmctx as *mut u8, callee, values_vec) == 0 { + Err(trap_message(vmctx)) + } else { + Ok(()) } - push_jmp_buf(buf); - - // Call the function! - let func: fn(*mut VMContext, *mut u8) = mem::transmute(callee); - func(vmctx, values_vec); - - Ok(()) } /// Call the wasm function pointed to by `callee`, which has no arguments or @@ -119,19 +82,9 @@ pub unsafe extern "C" fn wasmtime_call( vmctx: *mut VMContext, callee: *const VMFunctionBody, ) -> Result<(), String> { - // Reset JMP_BUFS if the stack is unwound through this point. - let _guard = ScopeGuard::new(); - - // Set a setjmp catch point. - let mut buf = mem::uninitialized(); - if setjmp(&mut buf) != 0 { - return Err(trap_message(vmctx)); + if WasmtimeCall(vmctx as *mut u8, callee) == 0 { + Err(trap_message(vmctx)) + } else { + Ok(()) } - push_jmp_buf(buf); - - // Call the function! - let func: fn(*mut VMContext) = mem::transmute(callee); - func(vmctx); - - Ok(()) }