From 96f24409600bfaf7b39c5d71da193592361c2c69 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Thu, 18 Oct 2018 00:09:04 +0200 Subject: [PATCH] Added working mock for memory (grow and size) --- src/integrations/mod.rs | 7 ++-- src/macros.rs | 14 +++---- src/main.rs | 12 +++--- src/spec/tests.rs | 9 +++-- src/spec/tests/memory.wast | 75 +++++++++++++++++++++++++++++++++++ src/webassembly/instance.rs | 12 +++++- src/webassembly/memory.rs | 16 +++++--- src/webassembly/module.rs | 73 ++++++++++++++++++++++++++++++---- src/webassembly/relocation.rs | 10 ++++- 9 files changed, 192 insertions(+), 36 deletions(-) create mode 100644 src/spec/tests/memory.wast diff --git a/src/integrations/mod.rs b/src/integrations/mod.rs index 80c8eede3..857b27bde 100644 --- a/src/integrations/mod.rs +++ b/src/integrations/mod.rs @@ -1,5 +1,5 @@ -use libc::putchar; use crate::webassembly::ImportObject; +use libc::putchar; pub fn generate_libc_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { let mut import_object = ImportObject::new(); @@ -9,18 +9,17 @@ pub fn generate_libc_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { #[cfg(test)] mod tests { + use super::generate_libc_env; use crate::webassembly::{ instantiate, ErrorKind, Export, ImportObject, Instance, Module, ResultObject, }; - use super::generate_libc_env; use libc::putchar; #[test] fn test_putchar() { let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast"); let import_object = generate_libc_env(); - let result_object = - instantiate(wasm_bytes, import_object).expect("Not compiled properly"); + let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly"); let module = result_object.module; let instance = result_object.instance; let func_index = match module.info.exports.get("main") { diff --git a/src/macros.rs b/src/macros.rs index abe5e578b..6bffd2e1a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -18,16 +18,16 @@ macro_rules! include_wast2wasm_bytes { }}; } -#[cfg(feature= "debug")] +// #[cfg(feature= "debug")] #[macro_export] macro_rules! debug { ($fmt:expr) => (println!(concat!("Wasmer::", $fmt))); ($fmt:expr, $($arg:tt)*) => (println!(concat!("Wasmer::", $fmt, "\n"), $($arg)*)); } -#[cfg(not(feature= "debug"))] -#[macro_export] -macro_rules! debug { - ($fmt:expr) => {}; - ($fmt:expr, $($arg:tt)*) => {}; -} +// #[cfg(not(feature= "debug"))] +// #[macro_export] +// macro_rules! debug { +// ($fmt:expr) => {}; +// ($fmt:expr, $($arg:tt)*) => {}; +// } diff --git a/src/main.rs b/src/main.rs index f971d764b..1f8d751a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,13 +71,15 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { } let import_object = integrations::generate_libc_env(); - let webassembly::ResultObject {module, instance} = webassembly::instantiate(wasm_binary, import_object).map_err(|err| String::from(err.description()))?; - let func_index = instance.start_func.unwrap_or_else(|| { - match module.info.exports.get("main") { + let webassembly::ResultObject { module, instance } = + webassembly::instantiate(wasm_binary, import_object) + .map_err(|err| String::from(err.description()))?; + let func_index = instance + .start_func + .unwrap_or_else(|| match module.info.exports.get("main") { Some(&webassembly::Export::Function(index)) => index, _ => panic!("Main function not found"), - } - }); + }); let main: fn() = get_instance_function!(instance, func_index); main(); Ok(()) diff --git a/src/spec/tests.rs b/src/spec/tests.rs index e932d79c8..e12380c05 100644 --- a/src/spec/tests.rs +++ b/src/spec/tests.rs @@ -111,7 +111,7 @@ impl<'module> ScriptHandler for StoreCtrl<'module> { }; println!( - "Function {:?}(index: {:?}) ({:?}) => {:?}", + "Function {:?}(index: {:?}) ({:?}) => returned {:?}", field.to_string(), func_index, call_args, @@ -177,7 +177,8 @@ mod tests { use std::path::Path; #[macro_use] use crate::webassembly::{ - compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, ImportObject + compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, + ImportObject, }; use wabt::wat2wasm; @@ -209,7 +210,8 @@ mod tests { macro_rules! instantiate_from_wast { ($x:expr) => {{ let wasm_bytes = include_wast2wasm_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x)); - let result_object = instantiate(wasm_bytes, ImportObject::new()).expect("Not compiled properly"); + let result_object = + instantiate(wasm_bytes, ImportObject::new()).expect("Not compiled properly"); result_object }}; } @@ -235,6 +237,7 @@ mod tests { br_if, call, import, + memory, } } diff --git a/src/spec/tests/memory.wast b/src/spec/tests/memory.wast new file mode 100644 index 000000000..81132e950 --- /dev/null +++ b/src/spec/tests/memory.wast @@ -0,0 +1,75 @@ + +(module (memory 0 0)) +(module (memory 0 1)) +(module (memory 1 256)) +(module (memory 0 65536)) + +(assert_invalid (module (memory 0) (memory 0)) "multiple memories") +(assert_invalid (module (memory (import "spectest" "memory") 0) (memory 0)) "multiple memories") + +(module (memory (data)) (func (export "memsize") (result i32) (memory.size))) +(assert_return (invoke "memsize") (i32.const 0)) +;; (module (memory (data "")) (func (export "memsize") (result i32) (memory.size))) +;; (assert_return (invoke "memsize") (i32.const 0)) +;; (module (memory (data "x")) (func (export "memsize") (result i32) (memory.size))) +;; (assert_return (invoke "memsize") (i32.const 1)) + +;; (assert_invalid (module (data (i32.const 0))) "unknown memory") +;; (assert_invalid (module (data (i32.const 0) "")) "unknown memory") +;; (assert_invalid (module (data (i32.const 0) "x")) "unknown memory") + +;; (assert_invalid +;; (module (func (drop (f32.load (i32.const 0))))) +;; "unknown memory" +;; ) +;; (assert_invalid +;; (module (func (f32.store (f32.const 0) (i32.const 0)))) +;; "unknown memory" +;; ) +;; (assert_invalid +;; (module (func (drop (i32.load8_s (i32.const 0))))) +;; "unknown memory" +;; ) +;; (assert_invalid +;; (module (func (i32.store8 (i32.const 0) (i32.const 0)))) +;; "unknown memory" +;; ) +;; (assert_invalid +;; (module (func (drop (memory.size)))) +;; "unknown memory" +;; ) +;; (assert_invalid +;; (module (func (drop (memory.grow (i32.const 0))))) +;; "unknown memory" +;; ) + + +;; (assert_invalid +;; (module (memory 1 0)) +;; "size minimum must not be greater than maximum" +;; ) +;; (assert_invalid +;; (module (memory 65537)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) +;; (assert_invalid +;; (module (memory 2147483648)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) +;; (assert_invalid +;; (module (memory 4294967295)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) +;; (assert_invalid +;; (module (memory 0 65537)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) +;; (assert_invalid +;; (module (memory 0 2147483648)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) +;; (assert_invalid +;; (module (memory 0 4294967295)) +;; "memory size must be at most 65536 pages (4GiB)" +;; ) + diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index a8ceabb00..0105d207a 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -232,6 +232,12 @@ impl Instance { RelocationType::Normal(func_index) => { get_function_addr(&FuncIndex::new(*func_index as usize), &import_functions, &functions) as isize }, + RelocationType::CurrentMemory => { + current_memory as isize + }, + RelocationType::GrowMemory => { + grow_memory as isize + }, _ => unimplemented!() // RelocationType::Intrinsic(name) => { // get_abi_intrinsic(name)? @@ -547,7 +553,8 @@ impl Clone for Instance { } extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unimplemented!(); + return 0; + // unimplemented!(); // unsafe { // let instance = (*vmctx.offset(4)) as *mut Instance; // (*instance) @@ -558,7 +565,8 @@ extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> } extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unimplemented!(); + return 0; + // unimplemented!(); // unsafe { // let instance = (*vmctx.offset(4)) as *mut Instance; // (*instance) diff --git a/src/webassembly/memory.rs b/src/webassembly/memory.rs index 31106f5ba..c593ad0a4 100644 --- a/src/webassembly/memory.rs +++ b/src/webassembly/memory.rs @@ -34,16 +34,20 @@ impl LinearMemory { "Instantiate LinearMemory(initial={:?}, maximum={:?})", initial, maximum ); - let len = PAGE_SIZE * match maximum { - Some(val) => val, - None => { - if initial > 0 { - initial + let initial = if initial > 0 { initial } else { 1 }; + + let len: u64 = PAGE_SIZE as u64 * match maximum { + Some(val) => { + if val > initial { + val as u64 } else { - 1 + initial as u64 } } + None => initial as u64, }; + let len = if len == 0 { 1 } else { len }; + let mmap = MmapMut::map_anon(len as usize).unwrap(); debug!("LinearMemory instantiated"); Self { diff --git a/src/webassembly/module.rs b/src/webassembly/module.rs index 8182353a5..6e6253588 100644 --- a/src/webassembly/module.rs +++ b/src/webassembly/module.rs @@ -13,6 +13,7 @@ use cranelift_codegen::ir::{ FuncRef, Function, InstBuilder, Signature, }; use cranelift_codegen::print_errors::pretty_verifier_error; +use cranelift_codegen::settings::CallConv; use cranelift_codegen::{isa, settings, verifier}; use cranelift_entity::{EntityRef, PrimaryMap}; @@ -127,6 +128,12 @@ pub struct ModuleInfo { /// We use this in order to have a O(1) allocation of the exports /// rather than iterating through the Exportable elements. pub exports: HashMap, + + /// The external function declaration for implementing wasm's `current_memory`. + pub current_memory_extfunc: Option, + + /// The external function declaration for implementing wasm's `grow_memory`. + pub grow_memory_extfunc: Option, } impl ModuleInfo { @@ -150,8 +157,13 @@ impl ModuleInfo { main_memory_base: None, memory_base: None, exports: HashMap::new(), + current_memory_extfunc: None, + grow_memory_extfunc: None, } } + pub fn set_current_memory_extfunc(&mut self, func: FuncRef) { + self.current_memory_extfunc = Some(func); + } } /// A data initializer for linear memory. @@ -557,20 +569,66 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { fn translate_memory_grow( &mut self, mut pos: FuncCursor, - _index: MemoryIndex, - _heap: ir::Heap, - _val: ir::Value, + index: MemoryIndex, + heap: ir::Heap, + val: ir::Value, ) -> WasmResult { - Ok(pos.ins().iconst(I32, -1)) + debug_assert_eq!(index, 0, "non-default memories not supported yet"); + let grow_mem_func = self.mod_info.grow_memory_extfunc.unwrap_or_else(|| { + let sig_ref = pos.func.import_signature(Signature { + call_conv: CallConv::SystemV, + // argument_bytes: None, + params: vec![ + AbiParam::new(I32), + AbiParam::special(I64, ArgumentPurpose::VMContext), + ], + returns: vec![AbiParam::new(I32)], + }); + + pos.func.import_function(ExtFuncData { + name: ExternalName::testcase("grow_memory"), + signature: sig_ref, + colocated: false, + }) + }); + + // self.mod_info.grow_memory_extfunc = Some(grow_mem_func); + + let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); + + let call_inst = pos.ins().call(grow_mem_func, &[val, vmctx]); + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } fn translate_memory_size( &mut self, mut pos: FuncCursor, - _index: MemoryIndex, - _heap: ir::Heap, + index: MemoryIndex, + heap: ir::Heap, ) -> WasmResult { - Ok(pos.ins().iconst(I32, -1)) + debug_assert_eq!(index, 0, "non-default memories not supported yet"); + let cur_mem_func = self.mod_info.current_memory_extfunc.unwrap_or_else(|| { + let sig_ref = pos.func.import_signature(Signature { + call_conv: CallConv::SystemV, + // argument_bytes: None, + params: vec![AbiParam::special(I64, ArgumentPurpose::VMContext)], + returns: vec![AbiParam::new(I32)], + }); + + pos.func.import_function(ExtFuncData { + name: ExternalName::testcase("current_memory"), + signature: sig_ref, + colocated: false, + }) + }); + + // self.mod_info.set_current_memory_extfunc(f); + + let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); + + let call_inst = pos.ins().call(cur_mem_func, &[vmctx]); + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) + // Ok(pos.ins().iconst(I32, -1)) } // fn return_mode(&self) -> ReturnMode { @@ -677,6 +735,7 @@ impl<'data> ModuleEnvironment<'data> for Module { data: &'data [u8], ) { debug_assert!(base.is_none(), "global-value offsets not supported yet"); + // debug!("DATA INITIALIZATION {:?} {:?}", memory_index, base); self.info.data_initializers.push(DataInitializer { memory_index, base, diff --git a/src/webassembly/relocation.rs b/src/webassembly/relocation.rs index 119924f03..89e873b61 100644 --- a/src/webassembly/relocation.rs +++ b/src/webassembly/relocation.rs @@ -19,6 +19,8 @@ pub struct Relocation { pub enum RelocationType { Normal(u32), Intrinsic(String), + GrowMemory, + CurrentMemory, } /// Implementation of a relocation sink that just saves all the information for later @@ -62,14 +64,18 @@ impl binemit::RelocSink for RelocSink { ExternalName::TestCase { length, ascii } => { let (slice, _) = ascii.split_at(length as usize); let name = String::from_utf8(slice.to_vec()).unwrap(); - + let relocation_type = match name.as_str() { + "current_memory" => RelocationType::CurrentMemory, + "grow_memory" => RelocationType::GrowMemory, + _ => RelocationType::Intrinsic(name), + }; self.func_relocs.push(( Relocation { reloc, offset, addend, }, - RelocationType::Intrinsic(name), + relocation_type, )); } _ => {