use crate::microwasm; use backend::TranslatedCodeSection; use cranelift_codegen::{ ir::{self, AbiParam, Signature as CraneliftSignature}, isa, }; use error::Error; use std::{ convert::TryInto, hash::{Hash, Hasher}, mem, }; use translate_sections; use wasmparser::{FuncType, MemoryType, ModuleReader, SectionCode, TableType, Type}; pub trait AsValueType { const TYPE: Type; } pub trait TypeList { const TYPE_LIST: &'static [Type]; } impl TypeList for T where T: AsValueType, { const TYPE_LIST: &'static [Type] = &[T::TYPE]; } impl AsValueType for i32 { const TYPE: Type = Type::I32; } impl AsValueType for i64 { const TYPE: Type = Type::I64; } impl AsValueType for u32 { const TYPE: Type = Type::I32; } impl AsValueType for u64 { const TYPE: Type = Type::I64; } impl AsValueType for f32 { const TYPE: Type = Type::F32; } impl AsValueType for f64 { const TYPE: Type = Type::F64; } pub trait FunctionArgs { type FuncType; unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> O; fn into_func(start: *const u8) -> Self::FuncType; } type VmCtxPtr = u64; macro_rules! impl_function_args { ($first:ident $(, $rest:ident)*) => { impl FunctionArgs for ($first, $($rest),*) { type FuncType = unsafe extern "sysv64" fn(VmCtxPtr, $first $(, $rest)*) -> Output; #[allow(non_snake_case)] unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output { let ($first, $($rest),*) = self; func(vm_ctx as VmCtxPtr, $first $(, $rest)*) } fn into_func(start: *const u8) -> Self::FuncType { unsafe { mem::transmute(start) } } } impl<$first: AsValueType, $($rest: AsValueType),*> TypeList for ($first, $($rest),*) { const TYPE_LIST: &'static [Type] = &[$first::TYPE, $($rest::TYPE),*]; } impl_function_args!($($rest),*); }; () => { impl FunctionArgs for () { type FuncType = unsafe extern "sysv64" fn(VmCtxPtr) -> Output; unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output { func(vm_ctx as VmCtxPtr) } fn into_func(start: *const u8) -> Self::FuncType { unsafe { mem::transmute(start) } } } impl TypeList for () { const TYPE_LIST: &'static [Type] = &[]; } }; } impl_function_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); #[derive(Default)] pub struct TranslatedModule { translated_code_section: Option, types: SimpleContext, // TODO: Should we wrap this in a `Mutex` so that calling functions from multiple // threads doesn't cause data races? table: Option<(TableType, Vec)>, memory: Option, } pub fn quickhash(h: H) -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); h.hash(&mut hasher); hasher.finish() } impl TranslatedModule { pub fn instantiate(mut self) -> ExecutableModule { let table = { let code_section = self .translated_code_section .as_ref() .expect("We don't currently support a table section without a code section"); let types = &self.types; self.table .as_mut() .map(|&mut (_, ref mut idxs)| { let initial = idxs .iter() .map(|i| { let start = code_section.func_start(*i as _); let ty = types.func_type(*i); RuntimeFunc { func_start: start, sig_hash: quickhash(ty) as u32, } }) .collect::>(); let out = BoxSlice::from(initial.into_boxed_slice()); out }) .unwrap_or(BoxSlice { ptr: std::ptr::NonNull::dangling().as_ptr(), len: 0, }) }; let mem_size = self.memory.map(|m| m.limits.initial).unwrap_or(0) as usize; let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE] .into_boxed_slice() .into(); let ctx = if mem.len > 0 || table.len > 0 { Some(Box::new(VmCtx { table, mem })) } else { None }; ExecutableModule { module: self, context: ctx, } } pub fn disassemble(&self) { self.translated_code_section .as_ref() .expect("no code section") .disassemble(); } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ExecutionError { FuncIndexOutOfBounds, TypeMismatch, } pub struct ExecutableModule { module: TranslatedModule, context: Option>, } impl ExecutableModule { /// Executes the function _without checking types_. This can cause undefined /// memory to be accessed. pub unsafe fn execute_func_unchecked, T>( &self, func_idx: u32, args: Args, ) -> T { let code_section = self .module .translated_code_section .as_ref() .expect("no code section"); let start_buf = code_section.func_start(func_idx as usize); args.call( Args::into_func(start_buf), self.context .as_ref() .map(|ctx| (&**ctx) as *const VmCtx as *const u8) .unwrap_or(std::ptr::null()), ) } pub fn execute_func + TypeList, T: TypeList>( &self, func_idx: u32, args: Args, ) -> Result { let module = &self.module; if func_idx as usize >= module.types.func_ty_indicies.len() { return Err(ExecutionError::FuncIndexOutOfBounds); } let type_ = module.types.func_type(func_idx); // TODO: Handle "compatible" types (i.e. f32 and i32) if (&type_.params[..], &type_.returns[..]) != (Args::TYPE_LIST, T::TYPE_LIST) { return Err(ExecutionError::TypeMismatch); } Ok(unsafe { self.execute_func_unchecked(func_idx, args) }) } pub fn disassemble(&self) { self.module.disassemble(); } } type FuncRef = *const u8; pub struct RuntimeFunc { sig_hash: u32, func_start: FuncRef, } unsafe impl Send for RuntimeFunc {} unsafe impl Sync for RuntimeFunc {} impl RuntimeFunc { pub fn offset_of_sig_hash() -> usize { offset_of!(Self, sig_hash) } pub fn offset_of_func_start() -> usize { offset_of!(Self, func_start) } } struct BoxSlice { len: usize, ptr: *mut T, } impl From> for BoxSlice { fn from(mut other: Box<[T]>) -> Self { let out = BoxSlice { len: other.len(), ptr: other.as_mut_ptr(), }; mem::forget(other); out } } unsafe impl Send for BoxSlice {} unsafe impl Sync for BoxSlice {} impl Drop for BoxSlice { fn drop(&mut self) { unsafe { Vec::from_raw_parts(self.ptr, self.len, self.len) }; } } pub struct VmCtx { table: BoxSlice, mem: BoxSlice, } impl VmCtx { pub fn offset_of_memory_ptr() -> u8 { offset_of!(Self, mem.ptr) .try_into() .expect("Offset exceeded size of u8") } pub fn offset_of_memory_len() -> u8 { offset_of!(Self, mem.len) .try_into() .expect("Offset exceeded size of u8") } pub fn offset_of_funcs_ptr() -> u8 { offset_of!(Self, table.ptr) .try_into() .expect("Offset exceeded size of u8") } pub fn offset_of_funcs_len() -> u8 { offset_of!(Self, table.len) .try_into() .expect("Offset exceeded size of u8") } } #[derive(Default, Debug)] pub struct SimpleContext { types: Vec, func_ty_indicies: Vec, } const WASM_PAGE_SIZE: usize = 65_536; pub trait Signature { type Type: SigType; fn params(&self) -> &[Self::Type]; fn returns(&self) -> &[Self::Type]; } pub trait SigType { fn to_microwasm_type(&self) -> microwasm::SignlessType; fn is_float(&self) -> bool; } impl SigType for AbiParam { fn to_microwasm_type(&self) -> microwasm::SignlessType { use microwasm::{Size::*, Type::*}; if self.value_type.is_int() { match self.value_type.bits() { 32 => Int(_32), 64 => Int(_64), _ => unimplemented!(), } } else if self.value_type.is_float() { match self.value_type.bits() { 32 => Float(_32), 64 => Float(_64), _ => unimplemented!(), } } else { unimplemented!() } } fn is_float(&self) -> bool { self.value_type.is_float() } } impl Signature for CraneliftSignature { type Type = AbiParam; fn params(&self) -> &[Self::Type] { // TODO: We want to instead add the `VMContext` to the signature used by // cranelift, removing the special-casing from the internals. assert_eq!(self.params[0].purpose, ir::ArgumentPurpose::VMContext); assert_eq!(self.call_conv, isa::CallConv::SystemV); &self.params[1..] } fn returns(&self) -> &[Self::Type] { assert_eq!(self.call_conv, isa::CallConv::SystemV); &self.returns } } impl SigType for wasmparser::Type { fn to_microwasm_type(&self) -> microwasm::SignlessType { microwasm::Type::from_wasm(*self).unwrap() } fn is_float(&self) -> bool { match self { wasmparser::Type::F32 | wasmparser::Type::F64 => true, _ => false, } } } impl Signature for FuncType { type Type = wasmparser::Type; fn params(&self) -> &[Self::Type] { &*self.params } fn returns(&self) -> &[Self::Type] { &*self.returns } } pub trait ModuleContext { type Signature: Signature + Hash; fn func_type_index(&self, func_idx: u32) -> u32; fn signature(&self, index: u32) -> &Self::Signature; fn offset_of_memory_ptr(&self) -> u8; fn offset_of_memory_len(&self) -> u8; fn offset_of_funcs_ptr(&self) -> u8; fn offset_of_funcs_len(&self) -> u8; fn func_index(&self, defined_func_index: u32) -> u32; fn defined_func_index(&self, func_index: u32) -> Option; fn defined_func_type(&self, func_idx: u32) -> &Self::Signature { // TODO: This assumes that there are no imported functions. self.func_type(self.func_index(func_idx)) } fn func_type(&self, func_idx: u32) -> &Self::Signature { // TODO: This assumes that there are no imported functions. self.signature(self.func_type_index(func_idx)) } } impl ModuleContext for SimpleContext { type Signature = FuncType; // TODO: We don't support external functions yet fn func_index(&self, func_idx: u32) -> u32 { func_idx } fn defined_func_index(&self, func_idx: u32) -> Option { Some(func_idx) } fn func_type_index(&self, func_idx: u32) -> u32 { self.func_ty_indicies[func_idx as usize] } fn signature(&self, index: u32) -> &Self::Signature { &self.types[index as usize] } fn offset_of_memory_ptr(&self) -> u8 { VmCtx::offset_of_memory_ptr() } fn offset_of_memory_len(&self) -> u8 { VmCtx::offset_of_memory_len() } fn offset_of_funcs_ptr(&self) -> u8 { VmCtx::offset_of_funcs_ptr() } fn offset_of_funcs_len(&self) -> u8 { VmCtx::offset_of_funcs_len() } // TODO: type of a global } pub fn translate(data: &[u8]) -> Result { translate_only(data).map(|m| m.instantiate()) } /// Translate from a slice of bytes holding a wasm module. pub fn translate_only(data: &[u8]) -> Result { let mut reader = ModuleReader::new(data)?; let mut output = TranslatedModule::default(); let mut table = None; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } let mut section = reader.read()?; if let SectionCode::Type = section.code { let types_reader = section.get_type_section_reader()?; output.types.types = translate_sections::type_(types_reader)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Import = section.code { let imports = section.get_import_section_reader()?; translate_sections::import(imports)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Function = section.code { let functions = section.get_function_section_reader()?; output.types.func_ty_indicies = translate_sections::function(functions)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Table = section.code { let tables = section.get_table_section_reader()?; let mut tables = translate_sections::table(tables)?; assert!(tables.len() <= 1); table = tables.drain(..).next(); reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Memory = section.code { let memories = section.get_memory_section_reader()?; let mem = translate_sections::memory(memories)?; assert!( mem.len() <= 1, "Multiple memory sections not yet unimplemented" ); if !mem.is_empty() { let mem = mem[0]; assert_eq!(Some(mem.limits.initial), mem.limits.maximum); output.memory = Some(mem); } reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Global = section.code { let globals = section.get_global_section_reader()?; translate_sections::global(globals)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Export = section.code { let exports = section.get_export_section_reader()?; translate_sections::export(exports)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Start = section.code { let start = section.get_start_section_content()?; translate_sections::start(start)?; reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Element = section.code { let elements = section.get_element_section_reader()?; let elements = translate_sections::element(elements)?; output.table = Some(( table.expect("Element section with no table section"), elements, )); reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Code = section.code { let code = section.get_code_section_reader()?; output.translated_code_section = Some(translate_sections::code(code, &output.types)?); reader.skip_custom_sections()?; if reader.eof() { return Ok(output); } section = reader.read()?; } if let SectionCode::Data = section.code { let data = section.get_data_section_reader()?; translate_sections::data(data)?; } assert!(reader.eof()); Ok(output) }