Starting to set up protected call in clif-backend

This commit is contained in:
Lachlan Sneff
2019-01-18 13:29:43 -08:00
parent 705708cafe
commit 539db9f577
11 changed files with 163 additions and 92 deletions

View File

@ -2,6 +2,7 @@ use crate::{
error::CompileResult,
error::RuntimeResult,
module::ModuleInner,
backing::ImportBacking,
types::{FuncIndex, LocalFuncIndex, Value},
vm,
};
@ -54,6 +55,7 @@ pub trait ProtectedCaller {
func_index: FuncIndex,
params: &[Value],
returns: &mut [Value],
import_backing: &ImportBacking,
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<()>;
@ -62,13 +64,9 @@ pub trait ProtectedCaller {
pub trait FuncResolver {
/// This returns a pointer to the function designated by the `local_func_index`
/// parameter.
///
/// The existance of the Token parameter ensures that this can only be called from
/// within the runtime crate.
fn get(
&self,
module: &ModuleInner,
local_func_index: LocalFuncIndex,
_: Token,
) -> Option<NonNull<vm::Func>>;
}

View File

@ -1,5 +1,4 @@
use crate::{
backend::Token,
error::{LinkError, LinkResult},
export::{Context, Export},
import::Imports,
@ -179,17 +178,14 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index, token)
.unwrap()
.as_ptr(),
vmctx,
}
}
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}
@ -233,17 +229,14 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index, token)
.unwrap()
.as_ptr(),
vmctx,
}
}
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}
@ -322,6 +315,10 @@ impl ImportBacking {
})
}
pub fn imported_func(&self, func_index: ImportedFuncIndex) -> vm::ImportedFunc {
self.functions[func_index].clone()
}
pub fn imported_memory(&self, memory_index: ImportedMemoryIndex) -> vm::ImportedMemory {
self.memories[memory_index].clone()
}

View File

@ -1,4 +1,3 @@
use crate::recovery::call_protected;
use crate::{
backend::Token,
backing::{ImportBacking, LocalBacking},
@ -10,13 +9,12 @@ use crate::{
module::{ExportIndex, Module, ModuleInner},
types::{
FuncIndex, FuncSig, GlobalDesc, GlobalIndex, LocalOrImport, Memory, MemoryIndex, Table,
TableIndex, Type, Value,
TableIndex, Value,
},
vm,
};
use libffi::high::{arg as libffi_arg, call as libffi_call, CodePtr};
use std::rc::Rc;
use std::{iter, mem};
use std::mem;
pub(crate) struct InstanceInner {
#[allow(dead_code)]
@ -122,7 +120,7 @@ impl Instance {
let mut returns = vec![Value::I32(0); signature.returns.len()];
let vmctx = match func_index.local_or_import(&self.module) {
LocalOrImport::Local(local_func_index) => &mut *self.inner.vmctx,
LocalOrImport::Local(_) => &mut *self.inner.vmctx,
LocalOrImport::Import(imported_func_index) => {
self.inner.import_backing.functions[imported_func_index].vmctx
}
@ -135,61 +133,12 @@ impl Instance {
func_index,
args,
&mut returns,
&self.inner.import_backing,
vmctx,
token,
)?;
Ok(returns)
// let (func_ref, ctx, signature) = self.inner.get_func_from_index(&self.module, func_index);
// let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
// let vmctx_ptr = match ctx {
// Context::External(vmctx) => vmctx,
// Context::Internal => &mut *self.inner.vmctx,
// };
// assert!(
// signature.returns.len() <= 1,
// "multi-value returns not yet supported"
// );
// if !signature.check_sig(args) {
// Err(CallError::Signature {
// expected: signature.clone(),
// found: args.iter().map(|val| val.ty()).collect(),
// })?
// }
// let libffi_args: Vec<_> = args
// .iter()
// .map(|val| match val {
// Value::I32(ref x) => libffi_arg(x),
// Value::I64(ref x) => libffi_arg(x),
// Value::F32(ref x) => libffi_arg(x),
// Value::F64(ref x) => libffi_arg(x),
// })
// .chain(iter::once(libffi_arg(&vmctx_ptr)))
// .collect();
// Ok(call_protected(|| {
// signature
// .returns
// .first()
// .map(|ty| match ty {
// Type::I32 => Value::I32(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::I64 => Value::I64(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::F32 => Value::F32(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::F64 => Value::F64(unsafe { libffi_call(func_ptr, &libffi_args) }),
// })
// .or_else(|| {
// // call with no returns
// unsafe {
// libffi_call::<()>(func_ptr, &libffi_args);
// }
// None
// })
// })?)
}
}
@ -253,11 +202,10 @@ impl InstanceInner {
let (func_ptr, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => {
let token = Token::generate();
(
module
.func_resolver
.get(&module, local_func_index, token)
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
@ -387,7 +335,7 @@ impl Namespace for Instance {
// TODO Remove this later, only needed for compilation till emscripten is updated
impl Instance {
pub fn memory_offset_addr(&self, index: usize, offset: usize) -> *const u8 {
pub fn memory_offset_addr(&self, _index: usize, _offset: usize) -> *const u8 {
unimplemented!()
}
}

View File

@ -14,9 +14,7 @@ pub mod instance;
pub mod memory;
mod mmap;
pub mod module;
mod recovery;
mod sig_registry;
mod sighandler;
pub mod structures;
pub mod table;
pub mod types;

View File

@ -1,79 +0,0 @@
//! When a WebAssembly module triggers any traps, we perform recovery here.
//!
//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here
//! unless you have memory unsafety elsewhere in your code.
use crate::{
error::{RuntimeError, RuntimeResult},
sighandler::install_sighandler,
};
use nix::libc::siginfo_t;
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
use std::cell::{Cell, UnsafeCell};
use std::sync::Once;
extern "C" {
pub fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int;
fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !;
}
const SETJMP_BUFFER_LEN: usize = 27;
pub static SIGHANDLER_INIT: Once = Once::new();
thread_local! {
pub static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
pub static CAUGHT_ADDRESS: Cell<usize> = Cell::new(0);
}
pub fn call_protected<T>(f: impl FnOnce() -> T) -> RuntimeResult<T> {
unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
let prev_jmp_buf = *jmp_buf;
SIGHANDLER_INIT.call_once(|| {
install_sighandler();
});
let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void);
if signum != 0 {
*jmp_buf = prev_jmp_buf;
let addr = CAUGHT_ADDRESS.with(|cell| cell.get());
let signal = match Signal::from_c_int(signum) {
Ok(SIGFPE) => "floating-point exception",
Ok(SIGILL) => "illegal instruction",
Ok(SIGSEGV) => "segmentation violation",
Ok(SIGBUS) => "bus error",
Err(_) => "error while getting the Signal",
_ => "unkown trapped signal",
};
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown {
msg: format!("trap at {:#x} - {}", addr, signal),
}
.into())
} else {
let ret = f(); // TODO: Switch stack?
*jmp_buf = prev_jmp_buf;
Ok(ret)
}
}
}
/// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32, siginfo: *mut siginfo_t) -> ! {
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
// temporarily disable the signal handlers to debug it.
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
::std::process::abort();
}
// We only target macos at the moment as other ones might not have si_addr field
#[cfg(target_os = "macos")]
CAUGHT_ADDRESS.with(|cell| cell.set((*siginfo).si_addr as _));
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
}

View File

@ -1,33 +0,0 @@
//! We install signal handlers to handle WebAssembly traps within
//! our Rust code. Otherwise we will have errors that stop the Rust process
//! such as `process didn't exit successfully: ... (signal: 8, SIGFPE: erroneous arithmetic operation)`
//!
//! Please read more about this here: https://github.com/CraneStation/wasmtime/issues/15
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
use crate::recovery;
use nix::libc::{c_void, siginfo_t};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
};
pub unsafe fn install_sighandler() {
let sa = SigAction::new(
SigHandler::SigAction(signal_trap_handler),
SaFlags::SA_ONSTACK,
SigSet::empty(),
);
sigaction(SIGFPE, &sa).unwrap();
sigaction(SIGILL, &sa).unwrap();
sigaction(SIGSEGV, &sa).unwrap();
sigaction(SIGBUS, &sa).unwrap();
}
extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
siginfo: *mut siginfo_t,
_ucontext: *mut c_void,
) {
unsafe {
recovery::do_unwind(signum, siginfo);
}
}

View File

@ -159,7 +159,7 @@ pub trait LocalImport {
#[rustfmt::skip]
macro_rules! define_map_index {
($ty:ident) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct $ty (u32);
impl TypedIndex for $ty {
#[doc(hidden)]