use crate::engine::{ObjectFileEngine, ObjectFileEngineInner};
use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry};
use std::collections::BTreeMap;
use std::error::Error;
use std::mem;
use std::sync::Arc;
use wasmer_compiler::{CompileError, Features, OperatingSystem, SymbolRegistry, Triple};
#[cfg(feature = "compiler")]
use wasmer_compiler::{
CompileModuleInfo, FunctionBodyData, ModuleEnvironment, ModuleTranslationState,
};
use wasmer_engine::{Artifact, DeserializeError, InstantiationError, 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, OwnedDataInitializer, SignatureIndex,
TableIndex,
};
use wasmer_vm::{
FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, VMTrampoline,
};
pub struct ObjectFileArtifact {
metadata: ModuleMetadata,
module_bytes: Vec<u8>,
finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
metadata_length: usize,
symbol_registry: ModuleMetadataSymbolRegistry,
}
fn to_compile_error(err: impl Error) -> CompileError {
CompileError::Codegen(format!("{}", err))
}
const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
impl ObjectFileArtifact {
#[allow(dead_code)]
const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
#[allow(dead_code)]
const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
#[allow(dead_code)]
const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
#[allow(dead_code)]
const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z'];
pub fn is_deserializable(bytes: &[u8]) -> bool {
cfg_if::cfg_if! {
if #[cfg(all(target_pointer_width = "64", target_os="macos"))] {
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")]
fn generate_metadata<'data>(
data: &'data [u8],
features: &Features,
tunables: &dyn Tunables,
) -> Result<
(
CompileModuleInfo,
PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
Vec<DataInitializer<'data>>,
Option<ModuleTranslationState>,
),
CompileError,
> {
let environ = ModuleEnvironment::new();
let translation = environ.translate(data).map_err(CompileError::Wasm)?;
let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = translation
.module
.memories
.values()
.map(|memory_type| tunables.memory_style(memory_type))
.collect();
let table_styles: PrimaryMap<TableIndex, TableStyle> = translation
.module
.tables
.values()
.map(|table_type| tunables.table_style(table_type))
.collect();
let compile_info = CompileModuleInfo {
module: Arc::new(translation.module),
features: features.clone(),
memory_styles,
table_styles,
};
Ok((
compile_info,
translation.function_body_inputs,
translation.data_initializers,
translation.module_translation_state,
))
}
#[cfg(feature = "compiler")]
pub fn new(
engine: &ObjectFileEngine,
data: &[u8],
tunables: &dyn Tunables,
) -> Result<Self, CompileError> {
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(), tunables)?;
let data_initializers = data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice();
let target_triple = target.triple();
let function_body_lengths = function_body_inputs
.keys()
.map(|_function_body| 0u64)
.collect::<PrimaryMap<LocalFunctionIndex, u64>>();
let mut metadata = ModuleMetadata {
compile_info,
prefix: engine_inner.get_prefix(&data),
data_initializers,
function_body_lengths,
};
let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
let mut metadata_binary = vec![0; 10];
let mut writable = &mut metadata_binary[..];
leb128::write::unsigned(&mut writable, serialized_data.len() as u64)
.expect("Should write number");
metadata_binary.extend(serialized_data);
let metadata_length = metadata_binary.len();
let (compile_info, symbol_registry) = metadata.split();
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,
&mut metadata.compile_info,
module_translation.as_ref().unwrap(),
function_body_inputs,
)?;
let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?;
emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary)
.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)
}
pub fn get_default_extension(triple: &Triple) -> &'static str {
match triple.operating_system {
OperatingSystem::Windows => "obj",
_ => "o",
}
}
pub fn from_parts_crosscompiled(
engine_inner: &mut ObjectFileEngineInner,
metadata: ModuleMetadata,
module_bytes: Vec<u8>,
metadata_length: usize,
) -> Result<Self, CompileError> {
let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
PrimaryMap::new();
let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
PrimaryMap::new();
let signature_registry = engine_inner.signatures();
let signatures = metadata
.compile_info
.module
.signatures
.values()
.map(|sig| signature_registry.register(sig))
.collect::<PrimaryMap<_, _>>();
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(),
metadata_length,
symbol_registry,
})
}
#[cfg(not(feature = "compiler"))]
pub fn new(_engine: &ObjectFileEngine, _data: &[u8]) -> Result<Self, CompileError> {
Err(CompileError::Codegen(
"Compilation is not enabled in the engine".to_string(),
))
}
pub unsafe fn deserialize(
engine: &ObjectFileEngine,
bytes: &[u8],
) -> Result<Self, DeserializeError> {
let mut reader = bytes;
let data_len = leb128::read::unsigned(&mut reader).unwrap() as usize;
let metadata: ModuleMetadata = bincode::deserialize(&bytes[10..(data_len + 10)]).unwrap();
const WORD_SIZE: usize = mem::size_of::<usize>();
let mut byte_buffer = [0u8; WORD_SIZE];
let mut cur_offset = data_len + 10;
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 mut sig_map: BTreeMap<SignatureIndex, VMSharedSignatureIndex> = BTreeMap::new();
for i in 0..num_finished_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);
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;
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");
}
}
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::<usize, VMTrampoline>(trampoline_ptr_bytes);
finished_function_call_trampolines.push(trampoline);
}
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;
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(),
metadata_length: 0,
symbol_registry,
})
}
pub fn symbol_registry(&self) -> &dyn SymbolRegistry {
&self.symbol_registry
}
pub fn metadata_length(&self) -> usize {
self.metadata_length
}
}
impl Artifact for ObjectFileArtifact {
fn module(&self) -> Arc<ModuleInfo> {
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) {
}
fn features(&self) -> &Features {
&self.metadata.compile_info.features
}
fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers
}
fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
&self.metadata.compile_info.memory_styles
}
fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
&self.metadata.compile_info.table_styles
}
fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr> {
&self.finished_functions
}
fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
&self.finished_function_call_trampolines
}
fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
&self.finished_dynamic_function_trampolines
}
fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
&self.signatures
}
fn preinstantiate(&self) -> Result<(), InstantiationError> {
Ok(())
}
fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
Ok(self.module_bytes.clone())
}
}