Implement minimal call_indirect signature checking.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_codegen::ir::condcodes::*;
|
||||||
use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64};
|
use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64};
|
||||||
use cranelift_codegen::ir::types::*;
|
use cranelift_codegen::ir::types::*;
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
@@ -13,6 +14,7 @@ use cranelift_wasm::{
|
|||||||
};
|
};
|
||||||
use module::{
|
use module::{
|
||||||
DataInitializer, Export, LazyContents, MemoryPlan, MemoryStyle, Module, TableElements,
|
DataInitializer, Export, LazyContents, MemoryPlan, MemoryStyle, Module, TableElements,
|
||||||
|
TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
@@ -221,7 +223,8 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn declare_table(&mut self, table: Table) {
|
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(
|
fn declare_table_elements(
|
||||||
@@ -417,11 +420,15 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
readonly: false,
|
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 {
|
func.create_table(ir::TableData {
|
||||||
base_gv,
|
base_gv,
|
||||||
min_size: Uimm64::new(0),
|
min_size: Uimm64::new(0),
|
||||||
bound_gv,
|
bound_gv,
|
||||||
element_size: Uimm64::new(u64::from(self.pointer_bytes())),
|
element_size: Uimm64::new(element_size),
|
||||||
index_type: I32,
|
index_type: I32,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -449,12 +456,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
mut pos: FuncCursor,
|
mut pos: FuncCursor,
|
||||||
table_index: TableIndex,
|
table_index: TableIndex,
|
||||||
table: ir::Table,
|
table: ir::Table,
|
||||||
_sig_index: SignatureIndex,
|
sig_index: SignatureIndex,
|
||||||
sig_ref: ir::SigRef,
|
sig_ref: ir::SigRef,
|
||||||
callee: ir::Value,
|
callee: ir::Value,
|
||||||
call_args: &[ir::Value],
|
call_args: &[ir::Value],
|
||||||
) -> WasmResult<ir::Inst> {
|
) -> WasmResult<ir::Inst> {
|
||||||
// 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.
|
// so we need to implement it ourselves.
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
table_index.index(),
|
table_index.index(),
|
||||||
@@ -462,7 +469,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
"non-default tables not supported yet"
|
"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.
|
// Dereference table_entry_addr to get the function address.
|
||||||
let mut mem_flags = ir::MemFlags::new();
|
let mut mem_flags = ir::MemFlags::new();
|
||||||
@@ -472,6 +479,26 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
.ins()
|
.ins()
|
||||||
.load(self.pointer_type(), mem_flags, table_entry_addr, 0);
|
.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);
|
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))
|
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ pub use compilation::{
|
|||||||
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
||||||
};
|
};
|
||||||
pub use environ::{ModuleEnvironment, ModuleTranslation};
|
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 tunables::Tunables;
|
||||||
pub use vmoffsets::VMOffsets;
|
pub use vmoffsets::VMOffsets;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
/// A translated WebAssembly module, excluding the function bodies and
|
||||||
/// memory initializers.
|
/// memory initializers.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -111,7 +143,7 @@ pub struct Module {
|
|||||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||||
|
|
||||||
/// WebAssembly tables.
|
/// WebAssembly tables.
|
||||||
pub tables: PrimaryMap<TableIndex, Table>,
|
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||||
|
|
||||||
/// WebAssembly linear memory plans.
|
/// WebAssembly linear memory plans.
|
||||||
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||||
@@ -136,7 +168,7 @@ impl Module {
|
|||||||
signatures: PrimaryMap::new(),
|
signatures: PrimaryMap::new(),
|
||||||
imported_funcs: PrimaryMap::new(),
|
imported_funcs: PrimaryMap::new(),
|
||||||
functions: PrimaryMap::new(),
|
functions: PrimaryMap::new(),
|
||||||
tables: PrimaryMap::new(),
|
table_plans: PrimaryMap::new(),
|
||||||
memory_plans: PrimaryMap::new(),
|
memory_plans: PrimaryMap::new(),
|
||||||
globals: PrimaryMap::new(),
|
globals: PrimaryMap::new(),
|
||||||
exports: HashMap::new(),
|
exports: HashMap::new(),
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ fn instantiate_memories(
|
|||||||
|
|
||||||
/// Allocate memory for just the tables of the current module.
|
/// Allocate memory for just the tables of the current module.
|
||||||
fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap<TableIndex, Table> {
|
fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap<TableIndex, Table> {
|
||||||
let mut tables = PrimaryMap::with_capacity(module.tables.len());
|
let mut tables = PrimaryMap::with_capacity(module.table_plans.len());
|
||||||
for table in module.tables.values() {
|
for table in module.table_plans.values() {
|
||||||
tables.push(Table::new(table));
|
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 slice = &mut tables[init.table_index].as_mut();
|
||||||
let subslice = &mut slice[init.offset..init.offset + init.elements.len()];
|
let subslice = &mut slice[init.offset..init.offset + init.elements.len()];
|
||||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
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(
|
let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect(
|
||||||
"table element initializer with imported function not supported yet",
|
"table element initializer with imported function not supported yet",
|
||||||
)];
|
)];
|
||||||
subslice[i] = AnyFunc {
|
subslice[i] = AnyFunc {
|
||||||
func_ptr: code_buf.as_ptr(),
|
func_ptr: code_buf.as_ptr(),
|
||||||
type_id: 0, // TODO: Implement signature checking.
|
type_id: type_id.index(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
lib/execute/src/signatures.rs
Normal file
2
lib/execute/src/signatures.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
//! Implement a registry of function signatures, for fast indirect call
|
||||||
|
//! signature checking.
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
//!
|
//!
|
||||||
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
|
//! `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 std::ptr;
|
||||||
use vmcontext::VMTable;
|
use vmcontext::VMTable;
|
||||||
|
use wasmtime_environ::{TablePlan, TableStyle};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct AnyFunc {
|
pub struct AnyFunc {
|
||||||
pub func_ptr: *const u8,
|
pub func_ptr: *const u8,
|
||||||
pub type_id: usize,
|
pub type_id: usize,
|
||||||
@@ -29,21 +31,25 @@ pub struct Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
/// Create a new table instance with specified minimum and maximum number of pages.
|
/// Create a new table instance with specified minimum and maximum number of elements.
|
||||||
pub fn new(table: &cranelift_wasm::Table) -> Self {
|
pub fn new(plan: &TablePlan) -> Self {
|
||||||
match table.ty {
|
match plan.table.ty {
|
||||||
TableElementType::Func => (),
|
TableElementType::Func => (),
|
||||||
TableElementType::Val(ty) => {
|
TableElementType::Val(ty) => {
|
||||||
unimplemented!("tables of types other than anyfunc ({})", ty)
|
unimplemented!("tables of types other than anyfunc ({})", ty)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut vec = Vec::new();
|
match plan.style {
|
||||||
vec.resize(table.minimum as usize, AnyFunc::default());
|
TableStyle::CallerChecksSignature => {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
vec.resize(plan.table.minimum as usize, AnyFunc::default());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
vec,
|
vec,
|
||||||
maximum: table.maximum,
|
maximum: plan.table.maximum,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ fn handle_module(path: PathBuf, target: &Option<String>, output: &str) -> Result
|
|||||||
|
|
||||||
emit_module(&mut obj, &translation.module, &compilation, &relocations)?;
|
emit_module(&mut obj, &translation.module, &compilation, &relocations)?;
|
||||||
|
|
||||||
if !translation.module.tables.is_empty() {
|
if !translation.module.table_plans.is_empty() {
|
||||||
if translation.module.tables.len() > 1 {
|
if translation.module.table_plans.len() > 1 {
|
||||||
return Err(String::from("multiple tables not supported yet"));
|
return Err(String::from("multiple tables not supported yet"));
|
||||||
}
|
}
|
||||||
return Err(String::from("FIXME: implement tables"));
|
return Err(String::from("FIXME: implement tables"));
|
||||||
|
|||||||
Reference in New Issue
Block a user