Implement wasm trap handlers. (#27)
* Implement wasm trap handlers. This adds signal handlers based on SpiderMonkey's signal-handler code. The functionality for looking up the trap code and wasm bytecode offset isn't yet implemented, but this is a start. I considered rewriting this code in Rust, but decided against it for now as C++ allows us to talk to the relevant OS APIs more directly. Fixes #15. * Compile with -std=c++11. * Refactor InstallState initialization. * Compile with -fPIC. * Factor out the code for calling a wasm function with a given index. * Fix unclear wording in a comment.
This commit is contained in:
102
lib/execute/src/traphandlers.rs
Normal file
102
lib/execute/src/traphandlers.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! WebAssembly trap handling, which is built on top of the lower-level
|
||||
//! signalhandling mechanisms.
|
||||
|
||||
use libc::c_int;
|
||||
use signalhandlers::{jmp_buf, CodeSegment};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
// 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) -> !;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct TrapData {
|
||||
pc: *const u8,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static TRAP_DATA: Cell<TrapData> = Cell::new(TrapData { pc: ptr::null() });
|
||||
static JMP_BUFS: RefCell<Vec<jmp_buf>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
/// Record the Trap code and wasm bytecode offset in TLS somewhere
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn RecordTrap(pc: *const u8, _codeSegment: *const CodeSegment) {
|
||||
// TODO: Look up the wasm bytecode offset and trap code and record them instead.
|
||||
TRAP_DATA.with(|data| data.set(TrapData { pc }));
|
||||
}
|
||||
|
||||
/// Initiate an unwind.
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Unwind() {
|
||||
JMP_BUFS.with(|bufs| unsafe {
|
||||
let buf = bufs.borrow_mut().pop().unwrap();
|
||||
longjmp(&buf, 1);
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the CodeSegment containing the given pc, if any exist in the process.
|
||||
/// This method does not take a lock.
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn LookupCodeSegment(_pc: *const ::std::os::raw::c_void) -> *const CodeSegment {
|
||||
// TODO: Implement this.
|
||||
unsafe { mem::transmute(-1isize) }
|
||||
}
|
||||
|
||||
/// A simple guard to ensure that `JMP_BUFS` is reset when we're done.
|
||||
struct ScopeGuard {
|
||||
orig_num_bufs: usize,
|
||||
}
|
||||
|
||||
impl ScopeGuard {
|
||||
fn new() -> Self {
|
||||
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;
|
||||
// TODO: Use `shrink_to` once it stablizes.
|
||||
JMP_BUFS.with(|bufs| {
|
||||
bufs.borrow_mut()
|
||||
.resize(orig_num_bufs, unsafe { mem::uninitialized() })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the wasm function poined to by `f`.
|
||||
pub fn call_wasm<F>(f: F) -> Result<(), String>
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// In case wasm code calls Rust that panics and unwinds past this point,
|
||||
// ensure that JMP_BUFS is unwound to its incoming state.
|
||||
let _ = ScopeGuard::new();
|
||||
|
||||
JMP_BUFS.with(|bufs| {
|
||||
let mut buf = unsafe { mem::uninitialized() };
|
||||
if unsafe { setjmp(&mut buf) } != 0 {
|
||||
return TRAP_DATA.with(|data| Err(format!("wasm trap at {:?}", data.get().pc)));
|
||||
}
|
||||
bufs.borrow_mut().push(buf);
|
||||
f();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user