266 lines
7.3 KiB
Rust
Raw Normal View History

use crate::relocation::{TrapData, TrapSink};
use crate::trampoline::Trampolines;
2019-01-18 14:30:15 -08:00
use hashbrown::HashSet;
use libc::c_void;
use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
use wasmer_runtime_core::{
backend::{ProtectedCaller, Token, UserTrapper},
2019-01-18 14:30:15 -08:00
error::RuntimeResult,
export::Context,
module::{ExportIndex, ModuleInfo, ModuleInner},
typed_func::{Wasm, WasmTrapInfo},
types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value},
vm::{self, ImportBacking},
};
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
#[cfg(unix)]
pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
}
pub struct Trapper;
impl UserTrapper for Trapper {
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
trigger_trap()
}
}
pub struct Caller {
func_export_set: HashSet<FuncIndex>,
2019-01-18 16:45:30 -08:00
handler_data: HandlerData,
2019-02-19 09:58:01 -08:00
trampolines: Arc<Trampolines>,
}
impl Caller {
2019-02-20 16:41:41 -08:00
pub fn new(
module: &ModuleInfo,
handler_data: HandlerData,
trampolines: Arc<Trampolines>,
) -> Self {
2019-01-18 14:30:15 -08:00
let mut func_export_set = HashSet::new();
for export_index in module.exports.values() {
if let ExportIndex::Func(func_index) = export_index {
func_export_set.insert(*func_index);
}
}
2019-01-18 14:53:46 -08:00
if let Some(start_func_index) = module.start_func {
func_export_set.insert(start_func_index);
}
2019-01-18 14:30:15 -08:00
2019-01-18 16:45:30 -08:00
Self {
func_export_set,
handler_data,
trampolines,
2019-01-18 16:45:30 -08:00
}
}
}
impl ProtectedCaller for Caller {
fn call(
&self,
module: &ModuleInner,
func_index: FuncIndex,
params: &[Value],
import_backing: &ImportBacking,
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
let (func_ptr, ctx, signature, sig_index) =
get_func_from_index(&module, import_backing, func_index);
let vmctx_ptr = match ctx {
Context::External(external_vmctx) => external_vmctx,
Context::Internal => vmctx,
};
assert!(self.func_export_set.contains(&func_index));
assert!(
2019-01-29 10:16:39 -08:00
signature.returns().len() <= 1,
"multi-value returns not yet supported"
);
2019-01-29 10:16:39 -08:00
assert!(
signature.check_param_value_types(params),
"incorrect signature"
);
let param_vec: Vec<u64> = params
.iter()
.map(|val| match val {
Value::I32(x) => *x as u64,
Value::I64(x) => *x as u64,
Value::F32(x) => x.to_bits() as u64,
Value::F64(x) => x.to_bits(),
})
.collect();
2019-01-29 10:16:39 -08:00
let mut return_vec = vec![0; signature.returns().len()];
let trampoline = self
.trampolines
.lookup(sig_index)
.expect("that trampoline doesn't exist");
#[cfg(not(target_os = "windows"))]
call_protected(&self.handler_data, || unsafe {
// Leap of faith.
trampoline(
vmctx_ptr,
func_ptr,
param_vec.as_ptr(),
return_vec.as_mut_ptr(),
);
})?;
// the trampoline is called from C on windows
#[cfg(target_os = "windows")]
call_protected(
&self.handler_data,
trampoline,
vmctx_ptr,
func_ptr,
param_vec.as_ptr(),
return_vec.as_mut_ptr(),
)?;
Ok(return_vec
.iter()
2019-01-29 10:16:39 -08:00
.zip(signature.returns().iter())
.map(|(&x, ty)| match ty {
Type::I32 => Value::I32(x as i32),
Type::I64 => Value::I64(x as i64),
Type::F32 => Value::F32(f32::from_bits(x as u32)),
Type::F64 => Value::F64(f64::from_bits(x as u64)),
})
.collect())
}
fn get_wasm_trampoline(&self, module: &ModuleInner, sig_index: SigIndex) -> Option<Wasm> {
unsafe extern "C" fn invoke(
trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull<vm::Func>, *const u64, *mut u64),
ctx: *mut vm::Ctx,
func: NonNull<vm::Func>,
args: *const u64,
rets: *mut u64,
trap_info: *mut WasmTrapInfo,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
#[cfg(not(target_os = "windows"))]
let res = call_protected(handler_data, || unsafe {
// Leap of faith.
trampoline(ctx, func, args, rets);
})
.is_ok();
// the trampoline is called from C on windows
#[cfg(target_os = "windows")]
let res = call_protected(handler_data, trampoline, ctx, func, args, rets).is_ok();
res
}
let trampoline = self
.trampolines
.lookup(sig_index)
.expect("that trampoline doesn't exist");
Some(unsafe {
Wasm::from_raw_parts(
trampoline,
invoke,
Some(NonNull::from(&self.handler_data).cast()),
)
})
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
Box::new(Trapper)
}
}
fn get_func_from_index<'a>(
module: &'a ModuleInner,
import_backing: &ImportBacking,
func_index: FuncIndex,
) -> (NonNull<vm::Func>, Context, &'a FuncSig, SigIndex) {
let sig_index = *module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
2019-02-09 15:53:40 -08:00
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
2019-01-18 14:30:15 -08:00
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast(),
2019-01-18 14:30:15 -08:00
Context::Internal,
),
LocalOrImport::Import(imported_func_index) => {
let imported_func = import_backing.imported_func(imported_func_index);
(
NonNull::new(imported_func.func as *mut _).unwrap(),
Context::External(imported_func.vmctx),
)
}
};
let signature = &module.info.signatures[sig_index];
(func_ptr, ctx, signature, sig_index)
2019-01-18 14:30:15 -08:00
}
unsafe impl Send for HandlerData {}
unsafe impl Sync for HandlerData {}
2019-02-19 09:58:01 -08:00
#[derive(Clone)]
pub struct HandlerData {
2019-02-19 09:58:01 -08:00
pub trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void,
exec_buffer_size: usize,
}
impl HandlerData {
pub fn new(
2019-02-19 09:58:01 -08:00
trap_data: Arc<TrapSink>,
exec_buffer_ptr: *const c_void,
exec_buffer_size: usize,
) -> Self {
Self {
trap_data,
exec_buffer_ptr,
exec_buffer_size,
}
}
pub fn lookup(&self, ip: *const c_void) -> Option<TrapData> {
let ip = ip as usize;
let buffer_ptr = self.exec_buffer_ptr as usize;
if buffer_ptr <= ip && ip < buffer_ptr + self.exec_buffer_size {
let offset = ip - buffer_ptr;
self.trap_data.lookup(offset)
} else {
None
}
}
}