Function imports now fully working

This commit is contained in:
Syrus Akbary
2018-10-17 11:22:45 +02:00
parent e2667e995d
commit c834f6b33e
5 changed files with 114 additions and 63 deletions

View File

@ -2,15 +2,11 @@
/// let func: fn(i32) -> i32 = get_instance_function!(instance, func_index); /// let func: fn(i32) -> i32 = get_instance_function!(instance, func_index);
#[macro_export] #[macro_export]
macro_rules! get_instance_function { macro_rules! get_instance_function {
($instance:expr, $func_index:expr) => { ($instance:expr, $func_index:expr) => {{
{
use std::mem; use std::mem;
let func_addr = $instance.get_function_pointer($func_index); let func_addr = $instance.get_function_pointer($func_index);
unsafe { unsafe { mem::transmute(func_addr) }
mem::transmute(func_addr) }};
}
}
};
} }
// #[cfg(feature = "debug")] // #[cfg(feature = "debug")]

View File

@ -127,7 +127,18 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
unimplemented!() unimplemented!()
} }
fn module(&mut self, bytes: Vec<u8>, name: Option<String>) { fn module(&mut self, bytes: Vec<u8>, name: Option<String>) {
let module_wrapped = instantiate(bytes, None); let mut import_object = HashMap::new();
let mut test_import = import_object
.entry("test".to_string())
.or_insert_with(|| HashMap::new());
fn identity(x: i32) -> i32 {
x
};
test_import.insert("identity".to_string(), identity as *const u8);
// let import_object = import_object!{
// test.identity => fn(x: i32) {x},
// }
let module_wrapped = instantiate(bytes, Some(&import_object));
let mut result = module_wrapped.expect("Module is invalid"); let mut result = module_wrapped.expect("Module is invalid");
// let module: &'module Module = result.module; // let module: &'module Module = result.module;
self.last_module = Some(result); self.last_module = Some(result);
@ -163,10 +174,10 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
} }
mod tests { mod tests {
use std::path::Path;
use crate::test::Bencher;
use super::run_single_file; use super::run_single_file;
use crate::test::Bencher;
use std::mem; use std::mem;
use std::path::Path;
#[macro_use] #[macro_use]
use crate::webassembly::{ use crate::webassembly::{
compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject,
@ -199,29 +210,12 @@ mod tests {
} }
macro_rules! instantiate_from_wast { macro_rules! instantiate_from_wast {
($x:expr) => { ($x:expr) => {{
{ pub const WAST_BYTES: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x));
pub const WAST_BYTES: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
$x
));
let wasm_bytes = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert wat to wasm"); let wasm_bytes = wat2wasm(WAST_BYTES.to_vec()).expect("Can't convert wat to wasm");
let result_object = instantiate(wasm_bytes, None).expect("Not compiled properly"); let result_object = instantiate(wasm_bytes, None).expect("Not compiled properly");
result_object result_object
} }};
};
}
wasm_tests!{
_type,
br_if,
call,
}
fn my_func () -> Vec<u8> {
let x = String::from("hello");
let bytes = x.into_bytes();
return bytes;
} }
#[bench] #[bench]
@ -239,4 +233,12 @@ mod tests {
func(1); func(1);
}); });
} }
wasm_tests!{
_type,
br_if,
call,
import,
}
} }

View File

@ -0,0 +1,8 @@
(module
(func $identity (import "test" "identity") (param i32) (result i32))
(func (export "exported_func") (param i32) (result i32)
(call $identity (get_local 0))
)
)
(assert_return (invoke "exported_func" (i32.const 42)) (i32.const 42))

View File

@ -12,6 +12,7 @@ use cranelift_wasm::{FuncIndex, GlobalInit};
use memmap::MmapMut; use memmap::MmapMut;
use region; use region;
use spin::RwLock; use spin::RwLock;
use std::collections::HashMap;
use std::iter::Iterator; use std::iter::Iterator;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ptr::{self, write_unaligned}; use std::ptr::{self, write_unaligned};
@ -43,15 +44,29 @@ pub fn protect_codebuf(code_buf: &Vec<u8>) -> Result<(), String> {
} }
} }
pub fn get_function_addr( fn get_function_addr(
base: *const (),
functions: &[usize],
func_index: &FuncIndex, func_index: &FuncIndex,
) -> *const () { import_functions: &Vec<*const u8>,
let offset = functions[func_index.index()]; functions: &Vec<Vec<u8>>,
(base as usize + offset) as _ ) -> *const u8 {
let index = func_index.index();
let len = import_functions.len();
let func_pointer = if index < len {
import_functions[index]
} else {
(&functions[func_index.index() - len]).as_ptr()
};
func_pointer
} }
// pub fn get_function_addr(
// functions: &[usize],
// func_index: &FuncIndex,
// ) -> *const () {
// let offset = functions[func_index.index()];
// (base as usize + offset) as _
// }
/// Zero-sized, non-instantiable type. /// Zero-sized, non-instantiable type.
pub enum VmCtx {} pub enum VmCtx {}
@ -84,6 +99,9 @@ pub struct UserData {
pub instance: Instance, pub instance: Instance,
} }
// The import object for the instance
pub type ImportObject = HashMap<String, HashMap<String, *const u8>>;
/// An Instance of a WebAssembly module /// An Instance of a WebAssembly module
#[derive(Debug)] #[derive(Debug)]
pub struct Instance { pub struct Instance {
@ -101,6 +119,9 @@ pub struct Instance {
// functions: Vec<usize>, // functions: Vec<usize>,
functions: Vec<Vec<u8>>, functions: Vec<Vec<u8>>,
/// Imported functions
import_functions: Vec<*const u8>,
/// The module start function /// The module start function
start_func: Option<FuncIndex>, start_func: Option<FuncIndex>,
// Region start memory location // Region start memory location
@ -128,26 +149,47 @@ pub struct Instance {
// vmctx // vmctx
// } // }
fn fake_fun(x: i32) -> i32 {
return x * 2;
}
impl Instance { impl Instance {
/// Create a new `Instance`. /// Create a new `Instance`.
pub fn new(module: &Module) -> Result<Instance, ErrorKind> { pub fn new(
module: &Module,
import_object: Option<&ImportObject>,
) -> Result<Instance, ErrorKind> {
let mut tables: Vec<Vec<usize>> = Vec::new(); let mut tables: Vec<Vec<usize>> = Vec::new();
let mut memories: Vec<LinearMemory> = Vec::new(); let mut memories: Vec<LinearMemory> = Vec::new();
let mut globals: Vec<u8> = Vec::new(); let mut globals: Vec<u8> = Vec::new();
let mut functions: Vec<Vec<u8>> = Vec::new(); let mut functions: Vec<Vec<u8>> = Vec::new();
let mut import_functions: Vec<*const u8> = Vec::new();
// let mut code_base: *const () = ptr::null(); // let mut code_base: *const () = ptr::null();
// Instantiate functions // Instantiate functions
{ {
functions.reserve_exact(module.info.function_bodies.len()); functions.reserve_exact(module.info.functions.len());
let isa = isa::lookup(module.info.triple.clone()) let isa = isa::lookup(module.info.triple.clone())
.unwrap() .unwrap()
.finish(module.info.flags.clone()); .finish(module.info.flags.clone());
let mut relocations = Vec::new();
// let mut total_size: usize = 0; // let mut total_size: usize = 0;
let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len()); // let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len());
let mut relocations = Vec::new(); for (module, field) in module.info.imported_funcs.iter() {
// let function = &import_object.map(|i| i.get(module).map(|m| m.get(field)));
let mut function = fake_fun as *const u8;
if let Some(import_object) = import_object {
if let Some(module) = import_object.get(module) {
if let Some(field_function) = module.get(field) {
function = *field_function;
}
}
}
// println!("GET FUNC {:?}", function);
import_functions.push(function);
relocations.push(vec![]);
}
// Compile the functions (from cranelift IR to machine code) // Compile the functions (from cranelift IR to machine code)
for function_body in module.info.function_bodies.values() { for function_body in module.info.function_bodies.values() {
let mut func_context = Context::for_function(function_body.to_owned()); let mut func_context = Context::for_function(function_body.to_owned());
@ -174,7 +216,7 @@ impl Instance {
let func_offset = code_buf; let func_offset = code_buf;
functions.push(func_offset); functions.push(func_offset);
context_and_offsets.push(func_context); // context_and_offsets.push(func_context);
relocations.push(reloc_sink.func_relocs); relocations.push(reloc_sink.func_relocs);
// println!("FUNCTION RELOCATIONS {:?}", reloc_sink.func_relocs) // println!("FUNCTION RELOCATIONS {:?}", reloc_sink.func_relocs)
// total_size += code_size_offset; // total_size += code_size_offset;
@ -189,7 +231,7 @@ impl Instance {
for (ref reloc, ref reloc_type) in function_relocs { for (ref reloc, ref reloc_type) in function_relocs {
let target_func_address: isize = match reloc_type { let target_func_address: isize = match reloc_type {
RelocationType::Normal(func_index) => { RelocationType::Normal(func_index) => {
functions[*func_index as usize].as_ptr() as isize get_function_addr(&FuncIndex::new(*func_index as usize), &import_functions, &functions) as isize
}, },
_ => unimplemented!() _ => unimplemented!()
// RelocationType::Intrinsic(name) => { // RelocationType::Intrinsic(name) => {
@ -204,18 +246,17 @@ impl Instance {
// RelocationTarget::CurrentMemory => current_memory as isize, // RelocationTarget::CurrentMemory => current_memory as isize,
}; };
// print!("FUNCTION {:?}", target_func_address); // print!("FUNCTION {:?}", target_func_address);
let body = &mut functions[i]; let func_addr =
get_function_addr(&FuncIndex::new(i), &import_functions, &functions);
match reloc.reloc { match reloc.reloc {
Reloc::Abs8 => unsafe { Reloc::Abs8 => unsafe {
let reloc_address = let reloc_address = func_addr.offset(reloc.offset as isize) as i64;
body.as_mut_ptr().offset(reloc.offset as isize) as i64;
let reloc_addend = reloc.addend; let reloc_addend = reloc.addend;
let reloc_abs = target_func_address as i64 + reloc_addend; let reloc_abs = target_func_address as i64 + reloc_addend;
write_unaligned(reloc_address as *mut i64, reloc_abs); write_unaligned(reloc_address as *mut i64, reloc_abs);
}, },
Reloc::X86PCRel4 => unsafe { Reloc::X86PCRel4 => unsafe {
let reloc_address = let reloc_address = func_addr.offset(reloc.offset as isize) as isize;
body.as_mut_ptr().offset(reloc.offset as isize) as isize;
let reloc_addend = reloc.addend as isize; let reloc_addend = reloc.addend as isize;
// TODO: Handle overflow. // TODO: Handle overflow.
let reloc_delta_i32 = let reloc_delta_i32 =
@ -330,8 +371,8 @@ impl Instance {
// to populate the table. // to populate the table.
// let func_index = *elem_index - module.info.imported_funcs.len() as u32; // let func_index = *elem_index - module.info.imported_funcs.len() as u32;
let func_addr = functions[func_index.index()].as_ptr(); // let func_addr = functions[func_index.index()].as_ptr();
// let func_addr = get_function_addr(code_base, &functions, *&func_index); let func_addr = get_function_addr(&func_index, &import_functions, &functions);
table[base + table_element.offset + i] = func_addr as _; table[base + table_element.offset + i] = func_addr as _;
} }
} }
@ -393,7 +434,8 @@ impl Instance {
memories: Arc::new(memories.into_iter().collect()), memories: Arc::new(memories.into_iter().collect()),
globals: globals, globals: globals,
functions: functions, functions: functions,
start_func: start_func import_functions: import_functions,
start_func: start_func,
// code_base: code_base, // code_base: code_base,
}) })
} }
@ -406,6 +448,10 @@ impl Instance {
func_pointer.as_ptr() func_pointer.as_ptr()
} }
// pub fn is_imported_function(&self, func_index: FuncIndex) -> bool {
// func_index.index() < self.import_functions.len()
// }
/// Invoke a WebAssembly function given a FuncIndex and the /// Invoke a WebAssembly function given a FuncIndex and the
/// arguments that the function should be called with /// arguments that the function should be called with
pub fn get_function<T>(&self, func_index: FuncIndex) -> (fn() -> T) { pub fn get_function<T>(&self, func_index: FuncIndex) -> (fn() -> T) {
@ -423,9 +469,9 @@ impl Instance {
// the generated code. Thanks to this, we can transmute the code region into a first-class // the generated code. Thanks to this, we can transmute the code region into a first-class
// Rust function and call it. // Rust function and call it.
// let func_pointer = get_function_addr(self.code_base, &self.functions, &func_index); // let func_pointer = get_function_addr(self.code_base, &self.functions, &func_index);
let func_pointer = &self.functions[func_index.index()]; let func_pointer = get_function_addr(&func_index, &self.import_functions, &self.functions);
unsafe { unsafe {
let func = mem::transmute::<_, fn() -> T>(func_pointer.as_ptr()); let func = mem::transmute::<_, fn() -> T>(func_pointer);
func func
// let result = func(2); // let result = func(2);
// println!("FUNCTION INVOKED, result {:?}", result); // println!("FUNCTION INVOKED, result {:?}", result);
@ -496,6 +542,7 @@ impl Clone for Instance {
globals: self.globals.clone(), globals: self.globals.clone(),
functions: self.functions.clone(), functions: self.functions.clone(),
start_func: self.start_func.clone(), start_func: self.start_func.clone(),
import_functions: self.import_functions.clone(),
// code_base: self.code_base, // code_base: self.code_base,
} }
} }

View File

@ -14,7 +14,7 @@ use target_lexicon::{self, Triple};
use wasmparser; use wasmparser;
pub use self::errors::{Error, ErrorKind}; pub use self::errors::{Error, ErrorKind};
pub use self::instance::Instance; pub use self::instance::{ImportObject, Instance};
pub use self::memory::LinearMemory; pub use self::memory::LinearMemory;
pub use self::module::{Export, Module, ModuleInfo}; pub use self::module::{Export, Module, ModuleInfo};
@ -27,8 +27,6 @@ pub struct ResultObject {
pub instance: Instance, pub instance: Instance,
} }
pub struct ImportObject {}
/// The webassembly::instantiate() function allows you to compile and /// The webassembly::instantiate() function allows you to compile and
/// instantiate WebAssembly code /// instantiate WebAssembly code
/// Params: /// Params:
@ -45,11 +43,11 @@ pub struct ImportObject {}
/// webassembly::RuntimeError, depending on the cause of the failure. /// webassembly::RuntimeError, depending on the cause of the failure.
pub fn instantiate( pub fn instantiate(
buffer_source: Vec<u8>, buffer_source: Vec<u8>,
import_object: Option<ImportObject>, import_object: Option<&ImportObject>,
) -> Result<ResultObject, ErrorKind> { ) -> Result<ResultObject, ErrorKind> {
let module = compile(buffer_source)?; let module = compile(buffer_source)?;
debug!("webassembly - creating instance"); debug!("webassembly - creating instance");
let instance = Instance::new(&module)?; let instance = Instance::new(&module, import_object)?;
debug!("webassembly - instance created"); debug!("webassembly - instance created");
Ok(ResultObject { module, instance }) Ok(ResultObject { module, instance })
} }