//! Define `StaticlibArtifact` to allow compiling and instantiating to be //! done as separate steps. use crate::engine::{StaticlibEngine, StaticlibEngineInner}; use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry}; use enumset::EnumSet; use loupe::MemoryUsage; use std::collections::BTreeMap; use std::error::Error; use std::mem; use std::sync::Arc; use wasmer_artifact::ArtifactCreate; use wasmer_compiler::{ CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple, }; #[cfg(feature = "compiler")] use wasmer_compiler::{ CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain, ModuleTranslationState, }; use wasmer_engine::{ Artifact, DeserializeError, InstantiationError, MetadataHeader, SerializeError, }; #[cfg(feature = "compiler")] use wasmer_engine::{Engine, Tunables}; #[cfg(feature = "compiler")] use wasmer_object::{emit_compilation, emit_data, get_object_for_target}; use wasmer_types::entity::EntityRef; use wasmer_types::entity::{BoxedSlice, PrimaryMap}; #[cfg(feature = "compiler")] use wasmer_types::DataInitializer; use wasmer_types::{ FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, SignatureIndex, TableIndex, }; use wasmer_vm::{ FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex, VMTrampoline, }; /// A compiled wasm module, ready to be instantiated. #[derive(MemoryUsage)] pub struct StaticlibArtifact { metadata: ModuleMetadata, module_bytes: Vec, finished_functions: BoxedSlice, #[loupe(skip)] finished_function_call_trampolines: BoxedSlice, finished_dynamic_function_trampolines: BoxedSlice, signatures: BoxedSlice, func_data_registry: Arc, /// Length of the serialized metadata metadata_length: usize, symbol_registry: ModuleMetadataSymbolRegistry, is_compiled: bool, } #[allow(dead_code)] fn to_compile_error(err: impl Error) -> CompileError { CompileError::Codegen(format!("{}", err)) } #[allow(dead_code)] const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA"; impl StaticlibArtifact { // Mach-O header in Mac #[allow(dead_code)] const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254]; // ELF Magic header for Linux (32 bit) #[allow(dead_code)] const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1]; // ELF Magic header for Linux (64 bit) #[allow(dead_code)] const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2]; // COFF Magic header for Windows (64 bit) #[allow(dead_code)] const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z']; /// Check if the provided bytes look like `StaticlibArtifact`. /// /// This means, if the bytes look like a static object file in the /// target system. pub fn is_deserializable(bytes: &[u8]) -> bool { cfg_if::cfg_if! { if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] { bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64) } else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] { bytes.starts_with(Self::MAGIC_HEADER_ELF_64) } else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] { bytes.starts_with(Self::MAGIC_HEADER_ELF_32) } else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] { bytes.starts_with(Self::MAGIC_HEADER_COFF_64) } else { false } } } #[cfg(feature = "compiler")] /// Generate a compilation fn generate_metadata<'data>( data: &'data [u8], features: &Features, compiler: &dyn Compiler, tunables: &dyn Tunables, ) -> Result< ( CompileModuleInfo, PrimaryMap>, Vec>, Option, ), CompileError, > { let environ = ModuleEnvironment::new(); let translation = environ.translate(data).map_err(CompileError::Wasm)?; // We try to apply the middleware first let mut module = translation.module; let middlewares = compiler.get_middlewares(); middlewares.apply_on_module_info(&mut module); let memory_styles: PrimaryMap = module .memories .values() .map(|memory_type| tunables.memory_style(memory_type)) .collect(); let table_styles: PrimaryMap = module .tables .values() .map(|table_type| tunables.table_style(table_type)) .collect(); let compile_info = CompileModuleInfo { module: Arc::new(module), features: features.clone(), memory_styles, table_styles, }; Ok(( compile_info, translation.function_body_inputs, translation.data_initializers, translation.module_translation_state, )) } /// Compile a data buffer into a `StaticlibArtifact`, which can be statically linked against /// and run later. #[cfg(feature = "compiler")] pub fn new( engine: &StaticlibEngine, data: &[u8], tunables: &dyn Tunables, ) -> Result { let mut engine_inner = engine.inner_mut(); let target = engine.target(); let compiler = engine_inner.compiler()?; let (compile_info, function_body_inputs, data_initializers, module_translation) = Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?; let data_initializers = data_initializers .iter() .map(OwnedDataInitializer::new) .collect::>() .into_boxed_slice(); let target_triple = target.triple(); // TODO: we currently supply all-zero function body lengths. // We don't know the lengths until they're compiled, yet we have to // supply the metadata as an input to the compile. let function_body_lengths = function_body_inputs .keys() .map(|_function_body| 0u64) .collect::>(); let mut metadata = ModuleMetadata { compile_info, prefix: engine_inner.get_prefix(&data), data_initializers, function_body_lengths, cpu_features: target.cpu_features().as_u64(), }; /* In the C file we need: - imports - exports to construct an api::Module which is a Store (can be passed in via argument) and an Arc which means this struct which includes: - CompileModuleInfo - Features - ModuleInfo - MemoryIndex -> MemoryStyle - TableIndex -> TableStyle - LocalFunctionIndex -> FunctionBodyPtr // finished functions - FunctionIndex -> FunctionBodyPtr // finished dynamic function trampolines - SignatureIndex -> VMSharedSignatureindextureIndex // signatures */ let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?; let mut metadata_binary = vec![]; metadata_binary.extend(MetadataHeader::new(serialized_data.len())); metadata_binary.extend(serialized_data); let metadata_length = metadata_binary.len(); let (compile_info, symbol_registry) = metadata.split(); let mut module = (*compile_info.module).clone(); let middlewares = compiler.get_middlewares(); middlewares.apply_on_module_info(&mut module); compile_info.module = Arc::new(module); let maybe_obj_bytes = compiler.experimental_native_compile_module( &target, &compile_info, module_translation.as_ref().unwrap(), &function_body_inputs, &symbol_registry, &metadata_binary, ); let obj_bytes = if let Some(obj_bytes) = maybe_obj_bytes { obj_bytes? } else { let compilation = compiler.compile_module( &target, &metadata.compile_info, module_translation.as_ref().unwrap(), function_body_inputs, )?; // there's an ordering issue, but we can update function_body_lengths here. /* // We construct the function body lengths let function_body_lengths = compilation .get_function_bodies() .values() .map(|function_body| function_body.body.len() as u64) .collect::>(); */ let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?; emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary, 1) .map_err(to_compile_error)?; emit_compilation(&mut obj, compilation, &symbol_registry, &target_triple) .map_err(to_compile_error)?; obj.write().map_err(to_compile_error)? }; Self::from_parts_crosscompiled(&mut *engine_inner, metadata, obj_bytes, metadata_length) } /// Get the default extension when serializing this artifact pub fn get_default_extension(triple: &Triple) -> &'static str { match triple.operating_system { OperatingSystem::Windows => "obj", _ => "o", } } /// Construct a `StaticlibArtifact` from component parts. pub fn from_parts_crosscompiled( engine_inner: &mut StaticlibEngineInner, metadata: ModuleMetadata, module_bytes: Vec, metadata_length: usize, ) -> Result { let finished_functions: PrimaryMap = PrimaryMap::new(); let finished_function_call_trampolines: PrimaryMap = PrimaryMap::new(); let finished_dynamic_function_trampolines: PrimaryMap = PrimaryMap::new(); let signature_registry = engine_inner.signatures(); let signatures = metadata .compile_info .module .signatures .values() .map(|sig| signature_registry.register(sig)) .collect::>(); let symbol_registry = metadata.get_symbol_registry(); Ok(Self { metadata, module_bytes, finished_functions: finished_functions.into_boxed_slice(), finished_function_call_trampolines: finished_function_call_trampolines .into_boxed_slice(), finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), signatures: signatures.into_boxed_slice(), func_data_registry: engine_inner.func_data().clone(), metadata_length, symbol_registry, is_compiled: true, }) } /// Compile a data buffer into a `StaticlibArtifact`, which may then be instantiated. #[cfg(not(feature = "compiler"))] pub fn new(_engine: &StaticlibEngine, _data: &[u8]) -> Result { Err(CompileError::Codegen( "Compilation is not enabled in the engine".to_string(), )) } /// Deserialize a `StaticlibArtifact` from bytes. /// /// # Safety /// /// The bytes must represent a serialized WebAssembly module. pub unsafe fn deserialize( engine: &StaticlibEngine, bytes: &[u8], ) -> Result { let metadata_len = MetadataHeader::parse(bytes)?; let metadata: ModuleMetadata = bincode::deserialize(&bytes[MetadataHeader::LEN..][..metadata_len]).unwrap(); const WORD_SIZE: usize = mem::size_of::(); let mut byte_buffer = [0u8; WORD_SIZE]; let mut cur_offset = MetadataHeader::LEN + metadata_len; byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); cur_offset += WORD_SIZE; let num_finished_functions = usize::from_ne_bytes(byte_buffer); let mut finished_functions = PrimaryMap::new(); let engine_inner = engine.inner(); let signature_registry = engine_inner.signatures(); let func_data_registry = engine_inner.func_data().clone(); let mut sig_map: BTreeMap = BTreeMap::new(); let num_imported_functions = metadata.compile_info.module.num_imported_functions; // set up the imported functions first... for i in 0..num_imported_functions { let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)]; let func_type = &metadata.compile_info.module.signatures[sig_idx]; let vm_shared_idx = signature_registry.register(&func_type); sig_map.insert(sig_idx, vm_shared_idx); } // read finished functions in order now... for i in 0..num_finished_functions { let local_func_idx = LocalFunctionIndex::new(i); let func_idx = metadata.compile_info.module.func_index(local_func_idx); let sig_idx = metadata.compile_info.module.functions[func_idx]; let func_type = &metadata.compile_info.module.signatures[sig_idx]; let vm_shared_idx = signature_registry.register(&func_type); sig_map.insert(sig_idx, vm_shared_idx); byte_buffer[0..WORD_SIZE] .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _); cur_offset += WORD_SIZE; // TODO: we can read back the length here if we serialize it. This will improve debug output. finished_functions.push(fp); } let mut signatures: PrimaryMap<_, VMSharedSignatureIndex> = PrimaryMap::new(); for i in 0..(sig_map.len()) { if let Some(shared_idx) = sig_map.get(&SignatureIndex::new(i)) { signatures.push(*shared_idx); } else { panic!("Invalid data, missing sig idx; TODO: handle this error"); } } // read trampolines in order let mut finished_function_call_trampolines = PrimaryMap::new(); byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); cur_offset += WORD_SIZE; let num_function_trampolines = usize::from_ne_bytes(byte_buffer); for _ in 0..num_function_trampolines { byte_buffer[0..WORD_SIZE] .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); cur_offset += WORD_SIZE; let trampoline_ptr_bytes = usize::from_ne_bytes(byte_buffer); let trampoline = mem::transmute::(trampoline_ptr_bytes); finished_function_call_trampolines.push(trampoline); // TODO: we can read back the length here if we serialize it. This will improve debug output. } // read dynamic function trampolines in order now... let mut finished_dynamic_function_trampolines = PrimaryMap::new(); byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); cur_offset += WORD_SIZE; let num_dynamic_trampoline_functions = usize::from_ne_bytes(byte_buffer); for _i in 0..num_dynamic_trampoline_functions { byte_buffer[0..WORD_SIZE] .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _); cur_offset += WORD_SIZE; // TODO: we can read back the length here if we serialize it. This will improve debug output. finished_dynamic_function_trampolines.push(fp); } let symbol_registry = metadata.get_symbol_registry(); Ok(Self { metadata, module_bytes: bytes.to_owned(), finished_functions: finished_functions.into_boxed_slice(), finished_function_call_trampolines: finished_function_call_trampolines .into_boxed_slice(), finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), signatures: signatures.into_boxed_slice(), func_data_registry, metadata_length: 0, symbol_registry, is_compiled: false, }) } /// Get the `SymbolRegistry` used to generate the names used in the Artifact. pub fn symbol_registry(&self) -> &dyn SymbolRegistry { &self.symbol_registry } /// The length in bytes of the metadata in the serialized output. pub fn metadata_length(&self) -> usize { self.metadata_length } } impl ArtifactCreate for StaticlibArtifact { fn module(&self) -> Arc { self.metadata.compile_info.module.clone() } fn module_ref(&self) -> &ModuleInfo { &self.metadata.compile_info.module } fn module_mut(&mut self) -> Option<&mut ModuleInfo> { Arc::get_mut(&mut self.metadata.compile_info.module) } fn register_frame_info(&self) { // Do nothing for now } fn features(&self) -> &Features { &self.metadata.compile_info.features } fn cpu_features(&self) -> EnumSet { EnumSet::from_u64(self.metadata.cpu_features) } fn data_initializers(&self) -> &[OwnedDataInitializer] { &*self.metadata.data_initializers } fn memory_styles(&self) -> &PrimaryMap { &self.metadata.compile_info.memory_styles } fn table_styles(&self) -> &PrimaryMap { &self.metadata.compile_info.table_styles } /// Serialize a StaticlibArtifact fn serialize(&self) -> Result, SerializeError> { Ok(self.module_bytes.clone()) } } impl Artifact for StaticlibArtifact { fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } fn finished_function_call_trampolines(&self) -> &BoxedSlice { &self.finished_function_call_trampolines } fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice { &self.finished_dynamic_function_trampolines } fn signatures(&self) -> &BoxedSlice { &self.signatures } fn func_data_registry(&self) -> &FuncDataRegistry { &self.func_data_registry } fn preinstantiate(&self) -> Result<(), InstantiationError> { if self.is_compiled { panic!( "a module built with the staticlib engine must be linked \ into the current executable" ); } Ok(()) } }