wasmer_engine_staticlib/
artifact.rs

1//! Define `StaticlibArtifact` to allow compiling and instantiating to be
2//! done as separate steps.
3
4use crate::engine::{StaticlibEngine, StaticlibEngineInner};
5use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry};
6use enumset::EnumSet;
7use loupe::MemoryUsage;
8use std::collections::BTreeMap;
9use std::error::Error;
10use std::mem;
11use std::sync::Arc;
12use wasmer_artifact::ArtifactCreate;
13use wasmer_compiler::{
14    CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple,
15};
16#[cfg(feature = "compiler")]
17use wasmer_compiler::{
18    CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain,
19    ModuleTranslationState,
20};
21use wasmer_engine::{
22    Artifact, DeserializeError, InstantiationError, MetadataHeader, SerializeError,
23};
24#[cfg(feature = "compiler")]
25use wasmer_engine::{Engine, Tunables};
26#[cfg(feature = "compiler")]
27use wasmer_object::{emit_compilation, emit_data, get_object_for_target};
28use wasmer_types::entity::EntityRef;
29use wasmer_types::entity::{BoxedSlice, PrimaryMap};
30#[cfg(feature = "compiler")]
31use wasmer_types::DataInitializer;
32use wasmer_types::{
33    FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer,
34    SignatureIndex, TableIndex,
35};
36use wasmer_vm::{
37    FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex,
38    VMTrampoline,
39};
40
41/// A compiled wasm module, ready to be instantiated.
42#[derive(MemoryUsage)]
43pub struct StaticlibArtifact {
44    metadata: ModuleMetadata,
45    module_bytes: Vec<u8>,
46    finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
47    #[loupe(skip)]
48    finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
49    finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
50    signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
51    func_data_registry: Arc<FuncDataRegistry>,
52    /// Length of the serialized metadata
53    metadata_length: usize,
54    symbol_registry: ModuleMetadataSymbolRegistry,
55    is_compiled: bool,
56}
57
58#[allow(dead_code)]
59fn to_compile_error(err: impl Error) -> CompileError {
60    CompileError::Codegen(format!("{}", err))
61}
62
63#[allow(dead_code)]
64const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
65
66impl StaticlibArtifact {
67    // Mach-O header in Mac
68    #[allow(dead_code)]
69    const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
70
71    // ELF Magic header for Linux (32 bit)
72    #[allow(dead_code)]
73    const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
74
75    // ELF Magic header for Linux (64 bit)
76    #[allow(dead_code)]
77    const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
78
79    // COFF Magic header for Windows (64 bit)
80    #[allow(dead_code)]
81    const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z'];
82
83    /// Check if the provided bytes look like `StaticlibArtifact`.
84    ///
85    /// This means, if the bytes look like a static object file in the
86    /// target system.
87    pub fn is_deserializable(bytes: &[u8]) -> bool {
88        cfg_if::cfg_if! {
89            if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] {
90                bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64)
91            }
92            else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
93                bytes.starts_with(Self::MAGIC_HEADER_ELF_64)
94            }
95            else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
96                bytes.starts_with(Self::MAGIC_HEADER_ELF_32)
97            }
98            else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] {
99                bytes.starts_with(Self::MAGIC_HEADER_COFF_64)
100            }
101            else {
102                false
103            }
104        }
105    }
106
107    #[cfg(feature = "compiler")]
108    /// Generate a compilation
109    fn generate_metadata<'data>(
110        data: &'data [u8],
111        features: &Features,
112        compiler: &dyn Compiler,
113        tunables: &dyn Tunables,
114    ) -> Result<
115        (
116            CompileModuleInfo,
117            PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
118            Vec<DataInitializer<'data>>,
119            Option<ModuleTranslationState>,
120        ),
121        CompileError,
122    > {
123        let environ = ModuleEnvironment::new();
124        let translation = environ.translate(data).map_err(CompileError::Wasm)?;
125
126        // We try to apply the middleware first
127        let mut module = translation.module;
128        let middlewares = compiler.get_middlewares();
129        middlewares.apply_on_module_info(&mut module);
130
131        let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = module
132            .memories
133            .values()
134            .map(|memory_type| tunables.memory_style(memory_type))
135            .collect();
136        let table_styles: PrimaryMap<TableIndex, TableStyle> = module
137            .tables
138            .values()
139            .map(|table_type| tunables.table_style(table_type))
140            .collect();
141
142        let compile_info = CompileModuleInfo {
143            module: Arc::new(module),
144            features: features.clone(),
145            memory_styles,
146            table_styles,
147        };
148        Ok((
149            compile_info,
150            translation.function_body_inputs,
151            translation.data_initializers,
152            translation.module_translation_state,
153        ))
154    }
155
156    /// Compile a data buffer into a `StaticlibArtifact`, which can be statically linked against
157    /// and run later.
158    #[cfg(feature = "compiler")]
159    pub fn new(
160        engine: &StaticlibEngine,
161        data: &[u8],
162        tunables: &dyn Tunables,
163    ) -> Result<Self, CompileError> {
164        let mut engine_inner = engine.inner_mut();
165        let target = engine.target();
166        let compiler = engine_inner.compiler()?;
167        let (compile_info, function_body_inputs, data_initializers, module_translation) =
168            Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?;
169
170        let data_initializers = data_initializers
171            .iter()
172            .map(OwnedDataInitializer::new)
173            .collect::<Vec<_>>()
174            .into_boxed_slice();
175
176        let target_triple = target.triple();
177
178        // TODO: we currently supply all-zero function body lengths.
179        // We don't know the lengths until they're compiled, yet we have to
180        // supply the metadata as an input to the compile.
181        let function_body_lengths = function_body_inputs
182            .keys()
183            .map(|_function_body| 0u64)
184            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
185
186        let mut metadata = ModuleMetadata {
187            compile_info,
188            prefix: engine_inner.get_prefix(&data),
189            data_initializers,
190            function_body_lengths,
191            cpu_features: target.cpu_features().as_u64(),
192        };
193
194        /*
195        In the C file we need:
196        - imports
197        - exports
198
199        to construct an api::Module which is a Store (can be passed in via argument) and an
200        Arc<dyn Artifact> which means this struct which includes:
201        - CompileModuleInfo
202        - Features
203        - ModuleInfo
204        - MemoryIndex -> MemoryStyle
205        - TableIndex -> TableStyle
206        - LocalFunctionIndex -> FunctionBodyPtr // finished functions
207        - FunctionIndex -> FunctionBodyPtr // finished dynamic function trampolines
208        - SignatureIndex -> VMSharedSignatureindextureIndex // signatures
209         */
210
211        let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
212        let mut metadata_binary = vec![];
213        metadata_binary.extend(MetadataHeader::new(serialized_data.len()));
214        metadata_binary.extend(serialized_data);
215        let metadata_length = metadata_binary.len();
216
217        let (compile_info, symbol_registry) = metadata.split();
218
219        let mut module = (*compile_info.module).clone();
220        let middlewares = compiler.get_middlewares();
221        middlewares.apply_on_module_info(&mut module);
222        compile_info.module = Arc::new(module);
223
224        let maybe_obj_bytes = compiler.experimental_native_compile_module(
225            &target,
226            &compile_info,
227            module_translation.as_ref().unwrap(),
228            &function_body_inputs,
229            &symbol_registry,
230            &metadata_binary,
231        );
232
233        let obj_bytes = if let Some(obj_bytes) = maybe_obj_bytes {
234            obj_bytes?
235        } else {
236            let compilation = compiler.compile_module(
237                &target,
238                &metadata.compile_info,
239                module_translation.as_ref().unwrap(),
240                function_body_inputs,
241            )?;
242            // there's an ordering issue, but we can update function_body_lengths here.
243            /*
244            // We construct the function body lengths
245            let function_body_lengths = compilation
246            .get_function_bodies()
247            .values()
248            .map(|function_body| function_body.body.len() as u64)
249            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
250             */
251            let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?;
252            emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary, 1)
253                .map_err(to_compile_error)?;
254            emit_compilation(&mut obj, compilation, &symbol_registry, &target_triple)
255                .map_err(to_compile_error)?;
256            obj.write().map_err(to_compile_error)?
257        };
258
259        Self::from_parts_crosscompiled(&mut *engine_inner, metadata, obj_bytes, metadata_length)
260    }
261
262    /// Get the default extension when serializing this artifact
263    pub fn get_default_extension(triple: &Triple) -> &'static str {
264        match triple.operating_system {
265            OperatingSystem::Windows => "obj",
266            _ => "o",
267        }
268    }
269
270    /// Construct a `StaticlibArtifact` from component parts.
271    pub fn from_parts_crosscompiled(
272        engine_inner: &mut StaticlibEngineInner,
273        metadata: ModuleMetadata,
274        module_bytes: Vec<u8>,
275        metadata_length: usize,
276    ) -> Result<Self, CompileError> {
277        let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
278        let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
279            PrimaryMap::new();
280        let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
281            PrimaryMap::new();
282        let signature_registry = engine_inner.signatures();
283        let signatures = metadata
284            .compile_info
285            .module
286            .signatures
287            .values()
288            .map(|sig| signature_registry.register(sig))
289            .collect::<PrimaryMap<_, _>>();
290
291        let symbol_registry = metadata.get_symbol_registry();
292        Ok(Self {
293            metadata,
294            module_bytes,
295            finished_functions: finished_functions.into_boxed_slice(),
296            finished_function_call_trampolines: finished_function_call_trampolines
297                .into_boxed_slice(),
298            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
299                .into_boxed_slice(),
300            signatures: signatures.into_boxed_slice(),
301            func_data_registry: engine_inner.func_data().clone(),
302            metadata_length,
303            symbol_registry,
304            is_compiled: true,
305        })
306    }
307
308    /// Compile a data buffer into a `StaticlibArtifact`, which may then be instantiated.
309    #[cfg(not(feature = "compiler"))]
310    pub fn new(_engine: &StaticlibEngine, _data: &[u8]) -> Result<Self, CompileError> {
311        Err(CompileError::Codegen(
312            "Compilation is not enabled in the engine".to_string(),
313        ))
314    }
315
316    /// Deserialize a `StaticlibArtifact` from bytes.
317    ///
318    /// # Safety
319    ///
320    /// The bytes must represent a serialized WebAssembly module.
321    pub unsafe fn deserialize(
322        engine: &StaticlibEngine,
323        bytes: &[u8],
324    ) -> Result<Self, DeserializeError> {
325        let metadata_len = MetadataHeader::parse(bytes)?;
326
327        let metadata: ModuleMetadata =
328            bincode::deserialize(&bytes[MetadataHeader::LEN..][..metadata_len]).unwrap();
329
330        const WORD_SIZE: usize = mem::size_of::<usize>();
331        let mut byte_buffer = [0u8; WORD_SIZE];
332
333        let mut cur_offset = MetadataHeader::LEN + metadata_len;
334        byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
335        cur_offset += WORD_SIZE;
336
337        let num_finished_functions = usize::from_ne_bytes(byte_buffer);
338        let mut finished_functions = PrimaryMap::new();
339
340        let engine_inner = engine.inner();
341        let signature_registry = engine_inner.signatures();
342        let func_data_registry = engine_inner.func_data().clone();
343        let mut sig_map: BTreeMap<SignatureIndex, VMSharedSignatureIndex> = BTreeMap::new();
344
345        let num_imported_functions = metadata.compile_info.module.num_imported_functions;
346        // set up the imported functions first...
347        for i in 0..num_imported_functions {
348            let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)];
349            let func_type = &metadata.compile_info.module.signatures[sig_idx];
350            let vm_shared_idx = signature_registry.register(&func_type);
351            sig_map.insert(sig_idx, vm_shared_idx);
352        }
353        // read finished functions in order now...
354        for i in 0..num_finished_functions {
355            let local_func_idx = LocalFunctionIndex::new(i);
356            let func_idx = metadata.compile_info.module.func_index(local_func_idx);
357            let sig_idx = metadata.compile_info.module.functions[func_idx];
358            let func_type = &metadata.compile_info.module.signatures[sig_idx];
359            let vm_shared_idx = signature_registry.register(&func_type);
360            sig_map.insert(sig_idx, vm_shared_idx);
361
362            byte_buffer[0..WORD_SIZE]
363                .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
364            let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _);
365            cur_offset += WORD_SIZE;
366
367            // TODO: we can read back the length here if we serialize it. This will improve debug output.
368
369            finished_functions.push(fp);
370        }
371
372        let mut signatures: PrimaryMap<_, VMSharedSignatureIndex> = PrimaryMap::new();
373        for i in 0..(sig_map.len()) {
374            if let Some(shared_idx) = sig_map.get(&SignatureIndex::new(i)) {
375                signatures.push(*shared_idx);
376            } else {
377                panic!("Invalid data, missing sig idx; TODO: handle this error");
378            }
379        }
380
381        // read trampolines in order
382        let mut finished_function_call_trampolines = PrimaryMap::new();
383        byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
384        cur_offset += WORD_SIZE;
385        let num_function_trampolines = usize::from_ne_bytes(byte_buffer);
386        for _ in 0..num_function_trampolines {
387            byte_buffer[0..WORD_SIZE]
388                .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
389            cur_offset += WORD_SIZE;
390            let trampoline_ptr_bytes = usize::from_ne_bytes(byte_buffer);
391            let trampoline = mem::transmute::<usize, VMTrampoline>(trampoline_ptr_bytes);
392            finished_function_call_trampolines.push(trampoline);
393            // TODO: we can read back the length here if we serialize it. This will improve debug output.
394        }
395
396        // read dynamic function trampolines in order now...
397        let mut finished_dynamic_function_trampolines = PrimaryMap::new();
398        byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
399        cur_offset += WORD_SIZE;
400        let num_dynamic_trampoline_functions = usize::from_ne_bytes(byte_buffer);
401        for _i in 0..num_dynamic_trampoline_functions {
402            byte_buffer[0..WORD_SIZE]
403                .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]);
404            let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _);
405            cur_offset += WORD_SIZE;
406
407            // TODO: we can read back the length here if we serialize it. This will improve debug output.
408
409            finished_dynamic_function_trampolines.push(fp);
410        }
411
412        let symbol_registry = metadata.get_symbol_registry();
413        Ok(Self {
414            metadata,
415            module_bytes: bytes.to_owned(),
416            finished_functions: finished_functions.into_boxed_slice(),
417            finished_function_call_trampolines: finished_function_call_trampolines
418                .into_boxed_slice(),
419            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
420                .into_boxed_slice(),
421            signatures: signatures.into_boxed_slice(),
422            func_data_registry,
423            metadata_length: 0,
424            symbol_registry,
425            is_compiled: false,
426        })
427    }
428
429    /// Get the `SymbolRegistry` used to generate the names used in the Artifact.
430    pub fn symbol_registry(&self) -> &dyn SymbolRegistry {
431        &self.symbol_registry
432    }
433
434    /// The length in bytes of the metadata in the serialized output.
435    pub fn metadata_length(&self) -> usize {
436        self.metadata_length
437    }
438}
439
440impl ArtifactCreate for StaticlibArtifact {
441    fn module(&self) -> Arc<ModuleInfo> {
442        self.metadata.compile_info.module.clone()
443    }
444
445    fn module_ref(&self) -> &ModuleInfo {
446        &self.metadata.compile_info.module
447    }
448
449    fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
450        Arc::get_mut(&mut self.metadata.compile_info.module)
451    }
452
453    fn features(&self) -> &Features {
454        &self.metadata.compile_info.features
455    }
456
457    fn cpu_features(&self) -> EnumSet<CpuFeature> {
458        EnumSet::from_u64(self.metadata.cpu_features)
459    }
460
461    fn data_initializers(&self) -> &[OwnedDataInitializer] {
462        &*self.metadata.data_initializers
463    }
464
465    fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
466        &self.metadata.compile_info.memory_styles
467    }
468
469    fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
470        &self.metadata.compile_info.table_styles
471    }
472
473    /// Serialize a StaticlibArtifact
474    fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
475        Ok(self.module_bytes.clone())
476    }
477}
478impl Artifact for StaticlibArtifact {
479    fn register_frame_info(&self) {
480        // Do nothing for now
481    }
482
483    fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr> {
484        &self.finished_functions
485    }
486
487    fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
488        &self.finished_function_call_trampolines
489    }
490
491    fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
492        &self.finished_dynamic_function_trampolines
493    }
494
495    fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
496        &self.signatures
497    }
498
499    fn func_data_registry(&self) -> &FuncDataRegistry {
500        &self.func_data_registry
501    }
502    fn preinstantiate(&self) -> Result<(), InstantiationError> {
503        if self.is_compiled {
504            panic!(
505                "a module built with the staticlib engine must be linked \
506                into the current executable"
507            );
508        }
509        Ok(())
510    }
511}