Remove the LLVM/bindgen/cmake dependencies from wasmtime-runtime (#253)
* Remove cmake/bindgen/llvm from wasmtime-runtime This commit removes the cmake/bindgen dependency (which removes the need for `llvm-config`) from the `wasmtime-runtime` crate. The C++ code is instead compiled with the `cc` crate (it's just one file anyway) and the interface is handwritten since it's quite small anyway. Some other changes are: * The `TrapContext` type in C++ was removed since it was unused, and it was moved to Rust with a `Cell` on each field. * Functions between Rust/C++ now return `int` instead of `bool` to make them a bit more FFI compatible portably. * The `jmp_buf` type has a workaround that will be fixed in the next commit. * Move setjmp/longjmp to C++ This commit moves the definition of setjmp and longjmp into C++. This is primarily done because it's [debatable whether it's possible to call `setjmp` from Rust][rfc]. The semantics of `setjmp` are that it returns twice but LLVM doesn't actually know about this because rustc isn't telling LLVM this information, so it's unclear whether it can ever be safe. Additionally this removes the need for Rust code to know the definition of `jmp_buf` which is a pretty hairy type to define in Rust across platforms. The solution in this commit is to move all setjmp/longjmp code to C++, and that way we should be able to guarantee that jumps over wasm JIT code should always go from C++ to C++, removing Rust from the equation for now from needing to get any fiddly bits working across platforms. This should overall help it be a bit more portable and also means Rust doesn't have to know about `jmp_buf` as a type. The previous `Vec` of `jmp_buf` is now replaced with one thread-local pointer where previous values are stored on the stack and restored when the function returns. This is intended to be functionally the same as the previous implementation. [rfc]: https://github.com/rust-lang/rfcs/issues/2625 * rustfmt * Use volatile loads/stores * Remove mention of cmake from README
This commit is contained in:
committed by
Till Schneidereit
parent
4f04d7d873
commit
6def6de5e0
@@ -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<bool>,
|
||||
have_signal_handlers: Cell<bool>,
|
||||
}
|
||||
|
||||
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<TrapContext> =
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Vec<jmp_buf>> = 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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user