wasmer/src/webassembly/instance.rs

317 lines
12 KiB
Rust
Raw Normal View History

//! A webassembly::Instance object is a stateful, executable instance of a
//! webassembly::Module. Instance objects contain all the Exported
//! WebAssembly functions that allow calling into WebAssembly code.
//! The webassembly::Instance() constructor function can be called to
//! synchronously instantiate a given webassembly::Module object. However, the
//! primary way to get an Instance is through the asynchronous
//! webassembly::instantiateStreaming() function.
2018-10-15 03:03:00 +02:00
use cranelift_codegen::{isa, Context};
use cranelift_entity::EntityRef;
use cranelift_wasm::{FuncIndex, GlobalInit};
use memmap::MmapMut;
use region;
use spin::RwLock;
2018-10-14 13:59:11 +02:00
use std::marker::PhantomData;
2018-10-15 15:58:06 +02:00
use std::ptr;
2018-10-14 13:59:11 +02:00
use std::sync::Arc;
2018-10-14 23:48:59 +02:00
use std::{mem, slice};
2018-10-15 03:03:00 +02:00
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
2018-10-15 02:48:59 +02:00
use super::errors::ErrorKind;
use super::memory::LinearMemory;
use super::module::Module;
use super::module::{DataInitializer, Exportable};
2018-10-15 03:03:00 +02:00
use super::relocation::{RelocSink, TrapSink};
2018-10-12 02:45:09 +02:00
2018-10-14 23:48:59 +02:00
pub fn get_function_addr(
base: *const (),
functions: &[usize],
func_index: &FuncIndex,
) -> *const () {
2018-10-14 13:59:11 +02:00
let offset = functions[func_index.index()];
(base as usize + offset) as _
}
2018-10-12 02:45:09 +02:00
2018-10-14 13:59:11 +02:00
/// Zero-sized, non-instantiable type.
pub enum VmCtx {}
2018-10-14 13:59:11 +02:00
impl VmCtx {
pub fn data(&self) -> &VmCtxData {
let heap_ptr = self as *const _ as *const VmCtxData;
2018-10-14 23:48:59 +02:00
unsafe { &*heap_ptr.sub(1) }
2018-10-14 13:59:11 +02:00
}
2018-10-14 13:59:11 +02:00
/// This is safe because the offset is 32 bits and thus
/// cannot extend out of the guarded wasm memory.
pub fn fastpath_offset_ptr<T>(&self, offset: u32) -> *const T {
let heap_ptr = self as *const _ as *const u8;
2018-10-14 23:48:59 +02:00
unsafe { heap_ptr.add(offset as usize) as *const T }
2018-10-14 13:59:11 +02:00
}
}
2018-10-14 13:59:11 +02:00
#[repr(C)]
2018-10-15 11:46:04 +02:00
pub struct VmCtxData<'phantom> {
2018-10-14 13:59:11 +02:00
pub user_data: UserData,
globals: UncheckedSlice<u8>,
memories: UncheckedSlice<UncheckedSlice<u8>>,
tables: UncheckedSlice<BoundedSlice<usize>>,
2018-10-15 11:46:04 +02:00
phantom: PhantomData<&'phantom ()>,
2018-10-13 15:31:56 +02:00
}
2018-10-14 13:59:11 +02:00
#[repr(C)]
pub struct UserData {
// pub process: Dispatch<Process>,
pub instance: Instance,
}
2018-10-14 13:59:11 +02:00
/// An Instance of a WebAssembly module
#[derive(Debug)]
pub struct Instance {
/// WebAssembly table data
pub tables: Arc<Vec<RwLock<Vec<usize>>>>,
2018-10-14 13:59:11 +02:00
/// WebAssembly linear memory data
pub memories: Arc<Vec<LinearMemory>>,
2018-10-14 13:59:11 +02:00
/// WebAssembly global variable data
pub globals: Vec<u8>,
2018-10-15 15:58:06 +02:00
/// Webassembly functions
functions: Vec<usize>,
/// Region start memory location
code_base: *const (),
2018-10-14 13:59:11 +02:00
}
2018-10-14 13:59:11 +02:00
impl Instance {
/// Create a new `Instance`.
2018-10-15 15:58:06 +02:00
pub fn new(module: &Module) -> Result<Instance, ErrorKind> {
2018-10-14 13:59:11 +02:00
let mut tables: Vec<Vec<usize>> = Vec::new();
let mut memories: Vec<LinearMemory> = Vec::new();
let mut globals: Vec<u8> = Vec::new();
2018-10-15 15:58:06 +02:00
let mut functions: Vec<usize> = Vec::new();
let mut code_base: *const () = ptr::null();
2018-10-14 13:59:11 +02:00
2018-10-15 11:46:04 +02:00
// Instantiate functions
2018-10-15 02:48:59 +02:00
{
2018-10-15 15:58:06 +02:00
functions.reserve_exact(module.info.function_bodies.len());
2018-10-15 02:48:59 +02:00
let isa = isa::lookup(module.info.triple.clone())
.unwrap()
.finish(module.info.flags.clone());
let mut total_size: usize = 0;
let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len());
2018-10-15 11:46:04 +02:00
// Compile the functions (from cranelift IR to machine code)
2018-10-15 02:48:59 +02:00
for function_body in module.info.function_bodies.values() {
2018-10-15 03:03:00 +02:00
let mut func_context = Context::for_function(function_body.to_owned());
func_context
.verify(&*isa)
.map_err(|e| ErrorKind::CompileError(e.to_string()))?;
func_context
.verify_locations(&*isa)
.map_err(|e| ErrorKind::CompileError(e.to_string()))?;
2018-10-15 03:03:00 +02:00
let code_size_offset = func_context
.compile(&*isa)
.map_err(|e| ErrorKind::CompileError(e.to_string()))?
as usize;
context_and_offsets.push((func_context, total_size));
2018-10-15 02:48:59 +02:00
total_size += code_size_offset;
}
// We only want to allocate in memory if there is more than
2018-10-15 15:58:06 +02:00
// 0 functions. Otherwise reserving a 0-sized memory region
// cause a panic error
if total_size > 0 {
// Allocate the total memory for this functions
let map = MmapMut::map_anon(total_size).unwrap();
2018-10-15 15:58:06 +02:00
let region_start = map.as_ptr() as usize;
code_base = map.as_ptr() as *const ();
// // Emit this functions to memory
for (ref func_context, func_offset) in context_and_offsets.iter() {
let mut trap_sink = TrapSink::new(*func_offset);
let mut reloc_sink = RelocSink::new();
2018-10-15 15:58:06 +02:00
// let mut func_pointer = as *mut u8;
unsafe {
func_context.emit_to_memory(
&*isa,
2018-10-15 15:58:06 +02:00
(region_start + func_offset) as *mut u8,
&mut reloc_sink,
&mut trap_sink,
);
};
2018-10-15 15:58:06 +02:00
functions.push(*func_offset);
}
// Set protection of this memory region to Read + Execute
// so we are able to execute the functions emitted to memory
2018-10-15 02:48:59 +02:00
unsafe {
2018-10-15 15:58:06 +02:00
region::protect(region_start as *mut u8, total_size, region::Protection::ReadExecute)
.expect("unable to make memory readable+executable");
}
2018-10-15 02:48:59 +02:00
}
}
2018-10-15 03:03:00 +02:00
2018-10-15 11:46:04 +02:00
// Instantiate tables
2018-10-14 13:59:11 +02:00
{
// Reserve table space
2018-10-14 13:59:11 +02:00
tables.reserve_exact(module.info.tables.len());
for table in &module.info.tables {
let len = table.entity.size;
let mut v = Vec::with_capacity(len);
v.resize(len, 0);
tables.push(v);
}
// instantiate tables
for table_element in &module.info.table_elements {
2018-10-14 23:48:59 +02:00
assert!(
table_element.base.is_none(),
"globalvalue base not supported yet."
);
2018-10-14 13:59:11 +02:00
let base = 0;
2018-10-14 13:59:11 +02:00
let table = &mut tables[table_element.table_index];
for (i, func_index) in table_element.elements.iter().enumerate() {
// since the table just contains functions in the MVP
// we get the address of the specified function indexes
// to populate the table.
2018-10-13 15:31:56 +02:00
2018-10-14 13:59:11 +02:00
// let func_index = *elem_index - module.info.imported_funcs.len() as u32;
2018-10-15 02:48:59 +02:00
let func_addr = get_function_addr(code_base, &functions, *&func_index);
2018-10-14 13:59:11 +02:00
table[base + table_element.offset + i] = func_addr as _;
}
}
2018-10-15 03:03:00 +02:00
}
2018-10-13 15:31:56 +02:00
2018-10-15 11:46:04 +02:00
// Instantiate memories
2018-10-14 13:59:11 +02:00
{
// Allocate the underlying memory and initialize it to all zeros.
memories.reserve_exact(module.info.memories.len());
for memory in &module.info.memories {
let memory = memory.entity;
2018-10-14 23:48:59 +02:00
let v =
LinearMemory::new(memory.pages_count as u32, memory.maximum.map(|m| m as u32));
2018-10-14 13:59:11 +02:00
memories.push(v);
}
for init in &module.info.data_initializers {
2018-10-14 13:59:11 +02:00
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
let mem_mut = memories[init.memory_index].as_mut();
let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()];
to_init.copy_from_slice(&init.data);
}
2018-10-15 03:03:00 +02:00
}
2018-10-13 15:31:56 +02:00
2018-10-15 11:46:04 +02:00
// Instantiate Globals
2018-10-14 13:59:11 +02:00
{
let globals_count = module.info.globals.len();
// Allocate the underlying memory and initialize it to zeros
let globals_data_size = globals_count * 8;
globals.resize(globals_data_size, 0);
// cast the globals slice to a slice of i64.
2018-10-14 23:48:59 +02:00
let globals_data = unsafe {
slice::from_raw_parts_mut(globals.as_mut_ptr() as *mut i64, globals_count)
};
2018-10-14 13:59:11 +02:00
for (i, global) in module.info.globals.iter().enumerate() {
let value: i64 = match global.entity.initializer {
GlobalInit::I32Const(n) => n as _,
GlobalInit::I64Const(n) => n,
GlobalInit::F32Const(f) => unsafe { mem::transmute(f as f64) },
GlobalInit::F64Const(f) => unsafe { mem::transmute(f) },
_ => unimplemented!(),
};
2018-10-14 23:48:59 +02:00
2018-10-14 13:59:11 +02:00
globals_data[i] = value;
}
2018-10-15 03:03:00 +02:00
}
2018-10-13 15:31:56 +02:00
2018-10-15 02:48:59 +02:00
Ok(Instance {
2018-10-14 13:59:11 +02:00
tables: Arc::new(tables.into_iter().map(|table| RwLock::new(table)).collect()),
memories: Arc::new(memories.into_iter().collect()),
globals: globals,
2018-10-15 15:58:06 +02:00
functions: functions,
code_base: code_base,
2018-10-15 02:48:59 +02:00
})
2018-10-14 13:59:11 +02:00
}
2018-10-13 15:31:56 +02:00
2018-10-14 13:59:11 +02:00
pub fn memories(&self) -> Arc<Vec<LinearMemory>> {
self.memories.clone()
2018-10-13 15:31:56 +02:00
}
2018-10-15 11:46:04 +02:00
/// Invoke a WebAssembly function given a FuncIndex and the
/// arguments that the function should be called with
pub fn invoke(&self, func_index: FuncIndex, args: Vec<i32>) {
2018-10-15 15:58:06 +02:00
// let vmctx = make_vmctx(instance, &mut mem_base_addrs);
// let vmctx = ptr::null();
// Rather than writing inline assembly to jump to the code region, we use the fact that
// the Rust ABI for calling a function with no arguments and no return matches the one of
// the generated code. Thanks to this, we can transmute the code region into a first-class
// Rust function and call it.
let func_pointer = get_function_addr(self.code_base, &self.functions, &func_index);
// let index = func_index.index();
// let func_pointer = self.functions[index];
println!("INVOKING FUNCTION {:?} {:?}", func_index, func_pointer);
unsafe {
let func = mem::transmute::<_, fn()>(func_pointer);
let result = func();
println!("FUNCTION INVOKED, result {:?}", result);
// start_func(vmctx.as_ptr());
}
unimplemented!();
}
2018-10-15 15:58:06 +02:00
// pub fn generate_context(&mut self) -> &VmCtx {
// let memories: Vec<UncheckedSlice<u8>> = self.memories.iter()
// .map(|mem| mem.into())
// .collect();
// let tables: Vec<BoundedSlice<usize>> = self.tables.iter()
// .map(|table| table.write()[..].into())
// .collect();
// let globals: UncheckedSlice<u8> = self.globals[..].into();
// assert!(memories.len() >= 1, "modules must have at least one memory");
// // the first memory has a space of `mem::size_of::<VmCtxData>()` rounded
// // up to the 4KiB before it. We write the VmCtxData into that.
// let data = VmCtxData {
// globals: globals,
// memories: memories[1..].into(),
// tables: tables[..].into(),
// user_data: UserData {
// // process,
// instance,
// },
// phantom: PhantomData,
// };
// let main_heap_ptr = memories[0].as_mut_ptr() as *mut VmCtxData;
// unsafe {
// main_heap_ptr
// .sub(1)
// .write(data);
// &*(main_heap_ptr as *const VmCtx)
// }
// }
2018-10-14 20:37:42 +02:00
// pub fn start_func(&self) -> extern fn(&VmCtx) {
// self.start_func
// }
2018-10-14 13:59:11 +02:00
}
2018-10-13 15:31:56 +02:00
2018-10-14 13:59:11 +02:00
impl Clone for Instance {
fn clone(&self) -> Instance {
Instance {
tables: Arc::clone(&self.tables),
memories: Arc::clone(&self.memories),
globals: self.globals.clone(),
2018-10-15 15:58:06 +02:00
functions: self.functions.clone(),
code_base: self.code_base,
2018-10-14 13:59:11 +02:00
}
}
}