diff --git a/lib/environ/src/environ.rs b/lib/environ/src/environ.rs index 50f4f76f75..55e6aac823 100644 --- a/lib/environ/src/environ.rs +++ b/lib/environ/src/environ.rs @@ -1,5 +1,6 @@ use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir; +use cranelift_codegen::ir::condcodes::*; use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{ @@ -13,6 +14,7 @@ use cranelift_wasm::{ }; use module::{ DataInitializer, Export, LazyContents, MemoryPlan, MemoryStyle, Module, TableElements, + TablePlan, TableStyle, }; use std::clone::Clone; use std::string::String; @@ -221,7 +223,8 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data> } fn declare_table(&mut self, table: Table) { - self.module.tables.push(table); + let plan = TablePlan::for_table(table, &self.tunables); + self.module.table_plans.push(plan); } fn declare_table_elements( @@ -417,11 +420,15 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m readonly: false, }); + let element_size = match self.module.table_plans[index].style { + TableStyle::CallerChecksSignature => 2 * u64::from(self.pointer_bytes()), + }; + func.create_table(ir::TableData { base_gv, min_size: Uimm64::new(0), bound_gv, - element_size: Uimm64::new(u64::from(self.pointer_bytes())), + element_size: Uimm64::new(element_size), index_type: I32, }) } @@ -449,12 +456,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m mut pos: FuncCursor, table_index: TableIndex, table: ir::Table, - _sig_index: SignatureIndex, + sig_index: SignatureIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult { - // TODO: Cranelift's call_indirect doesn't implement signature checking, + // FIXME: Cranelift's call_indirect doesn't implement signature checking, // so we need to implement it ourselves. debug_assert_eq!( table_index.index(), @@ -462,7 +469,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m "non-default tables not supported yet" ); - let table_entry_addr = pos.ins().table_addr(I64, table, callee, 0); + let table_entry_addr = pos.ins().table_addr(self.pointer_type(), table, callee, 0); // Dereference table_entry_addr to get the function address. let mut mem_flags = ir::MemFlags::new(); @@ -472,6 +479,26 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m .ins() .load(self.pointer_type(), mem_flags, table_entry_addr, 0); + // If necessary, check the signature. + match self.module.table_plans[table_index].style { + TableStyle::CallerChecksSignature => { + // Dereference table_type_addr to get the function signature id. + let mut mem_flags = ir::MemFlags::new(); + mem_flags.set_notrap(); + mem_flags.set_aligned(); + let callee_sig = pos.ins().load( + self.pointer_type(), + mem_flags, + table_entry_addr, + i32::from(self.pointer_bytes()), + ); + let cmp = + pos.ins() + .icmp_imm(IntCC::Equal, callee_sig, i64::from(sig_index.as_u32())); + pos.ins().trapz(cmp, ir::TrapCode::BadSignature); + } + } + let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args); Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args)) } diff --git a/lib/environ/src/lib.rs b/lib/environ/src/lib.rs index 9b93d89ac8..302a8f191b 100644 --- a/lib/environ/src/lib.rs +++ b/lib/environ/src/lib.rs @@ -47,7 +47,9 @@ pub use compilation::{ compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations, }; pub use environ::{ModuleEnvironment, ModuleTranslation}; -pub use module::{DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements}; +pub use module::{ + DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle, +}; pub use tunables::Tunables; pub use vmoffsets::VMOffsets; diff --git a/lib/environ/src/module.rs b/lib/environ/src/module.rs index 1a6d017769..6dfff7ddcc 100644 --- a/lib/environ/src/module.rs +++ b/lib/environ/src/module.rs @@ -97,6 +97,38 @@ impl MemoryPlan { } } +/// Implemenation styles for WebAssembly tables. +#[derive(Debug, Clone)] +pub enum TableStyle { + /// Signatures are stored in the table and checked in the caller. + CallerChecksSignature, +} + +impl TableStyle { + /// Decide on an implementation style for the given `Table`. + pub fn for_table(_table: Table, _tunables: &Tunables) -> Self { + TableStyle::CallerChecksSignature + } +} + +/// A WebAssembly table description along with our chosen style for +/// implementing it. +#[derive(Debug)] +pub struct TablePlan { + /// The WebAssembly table description. + pub table: cranelift_wasm::Table, + /// Our chosen implementation style. + pub style: TableStyle, +} + +impl TablePlan { + /// Draw up a plan for implementing a `Table`. + pub fn for_table(table: Table, tunables: &Tunables) -> Self { + let style = TableStyle::for_table(table, tunables); + Self { table, style } + } +} + /// A translated WebAssembly module, excluding the function bodies and /// memory initializers. #[derive(Debug)] @@ -111,7 +143,7 @@ pub struct Module { pub functions: PrimaryMap, /// WebAssembly tables. - pub tables: PrimaryMap, + pub table_plans: PrimaryMap, /// WebAssembly linear memory plans. pub memory_plans: PrimaryMap, @@ -136,7 +168,7 @@ impl Module { signatures: PrimaryMap::new(), imported_funcs: PrimaryMap::new(), functions: PrimaryMap::new(), - tables: PrimaryMap::new(), + table_plans: PrimaryMap::new(), memory_plans: PrimaryMap::new(), globals: PrimaryMap::new(), exports: HashMap::new(), diff --git a/lib/execute/src/instance.rs b/lib/execute/src/instance.rs index 32c22d4dc1..ebe6c45ab6 100644 --- a/lib/execute/src/instance.rs +++ b/lib/execute/src/instance.rs @@ -140,8 +140,8 @@ fn instantiate_memories( /// Allocate memory for just the tables of the current module. fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap { - let mut tables = PrimaryMap::with_capacity(module.tables.len()); - for table in module.tables.values() { + let mut tables = PrimaryMap::with_capacity(module.table_plans.len()); + for table in module.table_plans.values() { tables.push(Table::new(table)); } @@ -150,12 +150,14 @@ fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap< let slice = &mut tables[init.table_index].as_mut(); let subslice = &mut slice[init.offset..init.offset + init.elements.len()]; for (i, func_idx) in init.elements.iter().enumerate() { + // FIXME: Implement cross-module signature checking. + let type_id = module.functions[*func_idx]; let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect( "table element initializer with imported function not supported yet", )]; subslice[i] = AnyFunc { func_ptr: code_buf.as_ptr(), - type_id: 0, // TODO: Implement signature checking. + type_id: type_id.index(), }; } } diff --git a/lib/execute/src/signatures.rs b/lib/execute/src/signatures.rs new file mode 100644 index 0000000000..0cc76f8d4b --- /dev/null +++ b/lib/execute/src/signatures.rs @@ -0,0 +1,2 @@ +//! Implement a registry of function signatures, for fast indirect call +//! signature checking. diff --git a/lib/execute/src/table.rs b/lib/execute/src/table.rs index 0aef5a4c4c..9c5dd22559 100644 --- a/lib/execute/src/table.rs +++ b/lib/execute/src/table.rs @@ -2,11 +2,13 @@ //! //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. -use cranelift_wasm::{self, TableElementType}; +use cranelift_wasm::TableElementType; use std::ptr; use vmcontext::VMTable; +use wasmtime_environ::{TablePlan, TableStyle}; #[derive(Debug, Clone)] +#[repr(C)] pub struct AnyFunc { pub func_ptr: *const u8, pub type_id: usize, @@ -29,21 +31,25 @@ pub struct Table { } impl Table { - /// Create a new table instance with specified minimum and maximum number of pages. - pub fn new(table: &cranelift_wasm::Table) -> Self { - match table.ty { + /// Create a new table instance with specified minimum and maximum number of elements. + pub fn new(plan: &TablePlan) -> Self { + match plan.table.ty { TableElementType::Func => (), TableElementType::Val(ty) => { unimplemented!("tables of types other than anyfunc ({})", ty) } }; - let mut vec = Vec::new(); - vec.resize(table.minimum as usize, AnyFunc::default()); + match plan.style { + TableStyle::CallerChecksSignature => { + let mut vec = Vec::new(); + vec.resize(plan.table.minimum as usize, AnyFunc::default()); - Self { - vec, - maximum: table.maximum, + Self { + vec, + maximum: plan.table.maximum, + } + } } } diff --git a/src/wasm2obj.rs b/src/wasm2obj.rs index 766e3f1560..59ad748dd8 100644 --- a/src/wasm2obj.rs +++ b/src/wasm2obj.rs @@ -152,8 +152,8 @@ fn handle_module(path: PathBuf, target: &Option, output: &str) -> Result emit_module(&mut obj, &translation.module, &compilation, &relocations)?; - if !translation.module.tables.is_empty() { - if translation.module.tables.len() > 1 { + if !translation.module.table_plans.is_empty() { + if translation.module.table_plans.len() > 1 { return Err(String::from("multiple tables not supported yet")); } return Err(String::from("FIXME: implement tables"));