wasmer_engine_object_file/
artifact.rs

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