wasmer_engine_native/
artifact.rs

1//! Define `NativeArtifact` to allow compiling and instantiating to be
2//! done as separate steps.
3
4use crate::engine::{NativeEngine, NativeEngineInner};
5use crate::serialize::ModuleMetadata;
6use libloading::{Library, Symbol as LibrarySymbol};
7use std::error::Error;
8use std::fs::File;
9use std::io::{Read, Write};
10use std::path::{Path, PathBuf};
11#[cfg(feature = "compiler")]
12use std::process::Command;
13use std::sync::Arc;
14use tempfile::NamedTempFile;
15#[cfg(feature = "compiler")]
16use tracing::trace;
17use wasmer_compiler::{CompileError, Features, OperatingSystem, Symbol, SymbolRegistry, Triple};
18#[cfg(feature = "compiler")]
19use wasmer_compiler::{
20    CompileModuleInfo, FunctionBodyData, ModuleEnvironment, ModuleTranslationState,
21};
22use wasmer_engine::{Artifact, DeserializeError, InstantiationError, SerializeError};
23#[cfg(feature = "compiler")]
24use wasmer_engine::{Engine, Tunables};
25#[cfg(feature = "compiler")]
26use wasmer_object::{emit_compilation, emit_data, get_object_for_target};
27use wasmer_types::entity::{BoxedSlice, PrimaryMap};
28#[cfg(feature = "compiler")]
29use wasmer_types::DataInitializer;
30use wasmer_types::{
31    FunctionIndex, LocalFunctionIndex, MemoryIndex, OwnedDataInitializer, SignatureIndex,
32    TableIndex,
33};
34use wasmer_vm::{
35    FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMFunctionBody, VMSharedSignatureIndex,
36    VMTrampoline,
37};
38
39/// A compiled wasm module, ready to be instantiated.
40pub struct NativeArtifact {
41    sharedobject_path: PathBuf,
42    metadata: ModuleMetadata,
43    finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
44    finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
45    finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
46    signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
47}
48
49fn to_compile_error(err: impl Error) -> CompileError {
50    CompileError::Codegen(format!("{}", err))
51}
52
53const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
54
55impl NativeArtifact {
56    // Mach-O header in Mac
57    #[allow(dead_code)]
58    const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
59
60    // ELF Magic header for Linux (32 bit)
61    #[allow(dead_code)]
62    const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
63
64    // ELF Magic header for Linux (64 bit)
65    #[allow(dead_code)]
66    const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
67
68    // COFF Magic header for Windows (64 bit)
69    #[allow(dead_code)]
70    const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z'];
71
72    /// Check if the provided bytes look like `NativeArtifact`.
73    ///
74    /// This means, if the bytes look like a shared object file in the target
75    /// system.
76    pub fn is_deserializable(bytes: &[u8]) -> bool {
77        cfg_if::cfg_if! {
78            if #[cfg(all(target_pointer_width = "64", target_os="macos"))] {
79                bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64)
80            }
81            else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
82                bytes.starts_with(Self::MAGIC_HEADER_ELF_64)
83            }
84            else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
85                bytes.starts_with(Self::MAGIC_HEADER_ELF_32)
86            }
87            else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] {
88                bytes.starts_with(Self::MAGIC_HEADER_COFF_64)
89            }
90            else {
91                false
92            }
93        }
94    }
95
96    #[cfg(feature = "compiler")]
97    /// Generate a compilation
98    fn generate_metadata<'data>(
99        data: &'data [u8],
100        features: &Features,
101        tunables: &dyn Tunables,
102    ) -> Result<
103        (
104            CompileModuleInfo,
105            PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
106            Vec<DataInitializer<'data>>,
107            Option<ModuleTranslationState>,
108        ),
109        CompileError,
110    > {
111        let environ = ModuleEnvironment::new();
112        let translation = environ.translate(data).map_err(CompileError::Wasm)?;
113        let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = translation
114            .module
115            .memories
116            .values()
117            .map(|memory_type| tunables.memory_style(memory_type))
118            .collect();
119        let table_styles: PrimaryMap<TableIndex, TableStyle> = translation
120            .module
121            .tables
122            .values()
123            .map(|table_type| tunables.table_style(table_type))
124            .collect();
125
126        let compile_info = CompileModuleInfo {
127            module: Arc::new(translation.module),
128            features: features.clone(),
129            memory_styles,
130            table_styles,
131        };
132        Ok((
133            compile_info,
134            translation.function_body_inputs,
135            translation.data_initializers,
136            translation.module_translation_state,
137        ))
138    }
139
140    /// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
141    #[cfg(feature = "compiler")]
142    pub fn new(
143        engine: &NativeEngine,
144        data: &[u8],
145        tunables: &dyn Tunables,
146    ) -> Result<Self, CompileError> {
147        let mut engine_inner = engine.inner_mut();
148        let target = engine.target();
149        let compiler = engine_inner.compiler()?;
150        let (compile_info, function_body_inputs, data_initializers, module_translation) =
151            Self::generate_metadata(data, engine_inner.features(), tunables)?;
152
153        let data_initializers = data_initializers
154            .iter()
155            .map(OwnedDataInitializer::new)
156            .collect::<Vec<_>>()
157            .into_boxed_slice();
158
159        let target_triple = target.triple();
160
161        /*
162        // We construct the function body lengths
163        let function_body_lengths = compilation
164            .get_function_bodies()
165            .values()
166            .map(|function_body| function_body.body.len() as u64)
167            .map(|_function_body| 0u64)
168            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
169         */
170
171        // TODO: we currently supply all-zero function body lengths.
172        // We don't know the lengths until they're compiled, yet we have to
173        // supply the metadata as an input to the compile.
174        let function_body_lengths = function_body_inputs
175            .keys()
176            .map(|_function_body| 0u64)
177            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
178
179        let mut metadata = ModuleMetadata {
180            compile_info,
181            prefix: engine_inner.get_prefix(&data),
182            data_initializers,
183            function_body_lengths,
184        };
185
186        let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?;
187        let mut metadata_binary = vec![0; 10];
188        let mut writable = &mut metadata_binary[..];
189        leb128::write::unsigned(&mut writable, serialized_data.len() as u64)
190            .expect("Should write number");
191        metadata_binary.extend(serialized_data);
192
193        let (mut compile_info, symbol_registry) = metadata.split();
194        let maybe_obj_bytes = compiler.experimental_native_compile_module(
195            &target,
196            &mut compile_info,
197            module_translation.as_ref().unwrap(),
198            &function_body_inputs,
199            &symbol_registry,
200            &metadata_binary,
201        );
202
203        let filepath = match maybe_obj_bytes {
204            Some(obj_bytes) => {
205                let obj_bytes = obj_bytes?;
206                let file = tempfile::Builder::new()
207                    .prefix("wasmer_native")
208                    .suffix(".o")
209                    .tempfile()
210                    .map_err(to_compile_error)?;
211
212                // Re-open it.
213                let (mut file, filepath) = file.keep().map_err(to_compile_error)?;
214                file.write(&obj_bytes).map_err(to_compile_error)?;
215                filepath
216            }
217            None => {
218                let compilation = compiler.compile_module(
219                    &target,
220                    &mut compile_info,
221                    module_translation.as_ref().unwrap(),
222                    function_body_inputs,
223                )?;
224                let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?;
225                emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary)
226                    .map_err(to_compile_error)?;
227                emit_compilation(&mut obj, compilation, &symbol_registry, &target_triple)
228                    .map_err(to_compile_error)?;
229                let file = tempfile::Builder::new()
230                    .prefix("wasmer_native")
231                    .suffix(".o")
232                    .tempfile()
233                    .map_err(to_compile_error)?;
234
235                // Re-open it.
236                let (mut file, filepath) = file.keep().map_err(to_compile_error)?;
237                let obj_bytes = obj.write().map_err(to_compile_error)?;
238
239                file.write(&obj_bytes).map_err(to_compile_error)?;
240                filepath
241            }
242        };
243
244        let shared_filepath = {
245            let suffix = format!(".{}", Self::get_default_extension(&target_triple));
246            let shared_file = tempfile::Builder::new()
247                .prefix("wasmer_native")
248                .suffix(&suffix)
249                .tempfile()
250                .map_err(to_compile_error)?;
251            shared_file
252                .into_temp_path()
253                .keep()
254                .map_err(to_compile_error)?
255        };
256
257        let is_cross_compiling = engine_inner.is_cross_compiling();
258        let target_triple_str = {
259            let into_str = target_triple.to_string();
260            // We have to adapt the target triple string, because otherwise
261            // Apple's clang will not recognize it.
262            if into_str == "aarch64-apple-darwin" {
263                "arm64-apple-darwin".to_string()
264            } else {
265                into_str
266            }
267        };
268
269        let cross_compiling_args: Vec<String> = if is_cross_compiling {
270            vec![
271                format!("--target={}", target_triple_str),
272                "-fuse-ld=lld".to_string(),
273                "-nodefaultlibs".to_string(),
274                "-nostdlib".to_string(),
275            ]
276        } else {
277            // We are explicit on the target when the host system is
278            // Apple Silicon, otherwise compilation fails.
279            if target_triple_str == "arm64-apple-darwin" {
280                vec![format!("--target={}", target_triple_str)]
281            } else {
282                vec![]
283            }
284        };
285        let target_args = match (target_triple.operating_system, is_cross_compiling) {
286            (OperatingSystem::Windows, true) => vec!["-Wl,/force:unresolved,/noentry"],
287            (OperatingSystem::Windows, false) => vec!["-Wl,-undefined,dynamic_lookup"],
288            _ => vec!["-nostartfiles", "-Wl,-undefined,dynamic_lookup"],
289        };
290        trace!(
291            "Compiling for target {} from host {}",
292            target_triple_str,
293            Triple::host().to_string(),
294        );
295
296        let linker: &'static str = engine_inner.linker().into();
297        let output = Command::new(linker)
298            .arg(&filepath)
299            .arg("-o")
300            .arg(&shared_filepath)
301            .args(&target_args)
302            // .args(&wasmer_symbols)
303            .arg("-shared")
304            .args(&cross_compiling_args)
305            .arg("-v")
306            .output()
307            .map_err(to_compile_error)?;
308
309        if !output.status.success() {
310            return Err(CompileError::Codegen(format!(
311                "Shared object file generator failed with:\nstderr:{}\nstdout:{}",
312                String::from_utf8_lossy(&output.stderr).trim_end(),
313                String::from_utf8_lossy(&output.stdout).trim_end()
314            )));
315        }
316        trace!("gcc command result {:?}", output);
317        if is_cross_compiling {
318            Self::from_parts_crosscompiled(metadata, shared_filepath)
319        } else {
320            let lib = Library::new(&shared_filepath).map_err(to_compile_error)?;
321            Self::from_parts(&mut engine_inner, metadata, shared_filepath, lib)
322        }
323    }
324
325    /// Get the default extension when serializing this artifact
326    pub fn get_default_extension(triple: &Triple) -> &'static str {
327        match triple.operating_system {
328            OperatingSystem::Windows => "dll",
329            OperatingSystem::Darwin | OperatingSystem::Ios | OperatingSystem::MacOSX { .. } => {
330                "dylib"
331            }
332            _ => "so",
333        }
334    }
335
336    /// Construct a `NativeArtifact` from component parts.
337    pub fn from_parts_crosscompiled(
338        metadata: ModuleMetadata,
339        sharedobject_path: PathBuf,
340    ) -> Result<Self, CompileError> {
341        let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
342        let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
343            PrimaryMap::new();
344        let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
345            PrimaryMap::new();
346        let signatures: PrimaryMap<SignatureIndex, VMSharedSignatureIndex> = PrimaryMap::new();
347        Ok(Self {
348            sharedobject_path,
349            metadata,
350            finished_functions: finished_functions.into_boxed_slice(),
351            finished_function_call_trampolines: finished_function_call_trampolines
352                .into_boxed_slice(),
353            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
354                .into_boxed_slice(),
355            signatures: signatures.into_boxed_slice(),
356        })
357    }
358
359    /// Construct a `NativeArtifact` from component parts.
360    pub fn from_parts(
361        engine_inner: &mut NativeEngineInner,
362        metadata: ModuleMetadata,
363        sharedobject_path: PathBuf,
364        lib: Library,
365    ) -> Result<Self, CompileError> {
366        let mut finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> =
367            PrimaryMap::new();
368        for (function_local_index, _function_len) in metadata.function_body_lengths.iter() {
369            let function_name = metadata
370                .get_symbol_registry()
371                .symbol_to_name(Symbol::LocalFunction(function_local_index));
372            unsafe {
373                // We use a fake function signature `fn()` because we just
374                // want to get the function address.
375                let func: LibrarySymbol<unsafe extern "C" fn()> = lib
376                    .get(function_name.as_bytes())
377                    .map_err(to_compile_error)?;
378                finished_functions.push(FunctionBodyPtr(
379                    func.into_raw().into_raw() as *const VMFunctionBody
380                ));
381            }
382        }
383
384        // Retrieve function call trampolines
385        let mut finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
386            PrimaryMap::with_capacity(metadata.compile_info.module.signatures.len());
387        for sig_index in metadata.compile_info.module.signatures.keys() {
388            let function_name = metadata
389                .get_symbol_registry()
390                .symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
391            unsafe {
392                let trampoline: LibrarySymbol<VMTrampoline> = lib
393                    .get(function_name.as_bytes())
394                    .map_err(to_compile_error)?;
395                let raw = *trampoline.into_raw();
396                finished_function_call_trampolines.push(raw);
397            }
398        }
399
400        // Retrieve dynamic function trampolines (only for imported functions)
401        let mut finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
402            PrimaryMap::with_capacity(metadata.compile_info.module.num_imported_functions);
403        for func_index in metadata
404            .compile_info
405            .module
406            .functions
407            .keys()
408            .take(metadata.compile_info.module.num_imported_functions)
409        {
410            let function_name = metadata
411                .get_symbol_registry()
412                .symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
413            unsafe {
414                let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib
415                    .get(function_name.as_bytes())
416                    .map_err(to_compile_error)?;
417                finished_dynamic_function_trampolines.push(FunctionBodyPtr(
418                    trampoline.into_raw().into_raw() as *const VMFunctionBody,
419                ));
420            }
421        }
422
423        // Leaving frame infos from now, as they are not yet used
424        // however they might be useful for the future.
425        // let frame_infos = compilation
426        //     .get_frame_info()
427        //     .values()
428        //     .map(|frame_info| SerializableFunctionFrameInfo::Processed(frame_info.clone()))
429        //     .collect::<PrimaryMap<LocalFunctionIndex, _>>();
430        // Self::from_parts(&mut engine_inner, lib, metadata, )
431        // let frame_info_registration = register_frame_info(
432        //     serializable.module.clone(),
433        //     &finished_functions,
434        //     serializable.compilation.function_frame_info.clone(),
435        // );
436
437        // Compute indices into the shared signature table.
438        let signatures = {
439            metadata
440                .compile_info
441                .module
442                .signatures
443                .values()
444                .map(|sig| engine_inner.signatures().register(sig))
445                .collect::<PrimaryMap<_, _>>()
446        };
447
448        engine_inner.add_library(lib);
449
450        Ok(Self {
451            sharedobject_path,
452            metadata,
453            finished_functions: finished_functions.into_boxed_slice(),
454            finished_function_call_trampolines: finished_function_call_trampolines
455                .into_boxed_slice(),
456            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
457                .into_boxed_slice(),
458            signatures: signatures.into_boxed_slice(),
459        })
460    }
461
462    /// Compile a data buffer into a `NativeArtifact`, which may then be instantiated.
463    #[cfg(not(feature = "compiler"))]
464    pub fn new(_engine: &NativeEngine, _data: &[u8]) -> Result<Self, CompileError> {
465        Err(CompileError::Codegen(
466            "Compilation is not enabled in the engine".to_string(),
467        ))
468    }
469
470    /// Deserialize a `NativeArtifact` from bytes.
471    ///
472    /// # Safety
473    ///
474    /// The bytes must represent a serialized WebAssembly module.
475    pub unsafe fn deserialize(
476        engine: &NativeEngine,
477        bytes: &[u8],
478    ) -> Result<Self, DeserializeError> {
479        if !Self::is_deserializable(&bytes) {
480            return Err(DeserializeError::Incompatible(
481                "The provided bytes are not in any native format Wasmer can understand".to_string(),
482            ));
483        }
484        // Dump the bytes into a file, so we can read it with our `dlopen`
485        let named_file = NamedTempFile::new()?;
486        let (mut file, path) = named_file.keep().map_err(|e| e.error)?;
487        file.write_all(&bytes)?;
488        // We already checked for the header, so we don't need
489        // to check again.
490        Self::deserialize_from_file_unchecked(&engine, &path)
491    }
492
493    /// Deserialize a `NativeArtifact` from a file path.
494    ///
495    /// # Safety
496    ///
497    /// The file's content must represent a serialized WebAssembly module.
498    pub unsafe fn deserialize_from_file(
499        engine: &NativeEngine,
500        path: &Path,
501    ) -> Result<Self, DeserializeError> {
502        let mut file = File::open(&path)?;
503        let mut buffer = [0; 5];
504        // read up to 5 bytes
505        file.read_exact(&mut buffer)?;
506        if !Self::is_deserializable(&buffer) {
507            return Err(DeserializeError::Incompatible(
508                "The provided bytes are not in any native format Wasmer can understand".to_string(),
509            ));
510        }
511        Self::deserialize_from_file_unchecked(&engine, &path)
512    }
513
514    /// Deserialize a `NativeArtifact` from a file path (unchecked).
515    ///
516    /// # Safety
517    ///
518    /// The file's content must represent a serialized WebAssembly module.
519    pub unsafe fn deserialize_from_file_unchecked(
520        engine: &NativeEngine,
521        path: &Path,
522    ) -> Result<Self, DeserializeError> {
523        let lib = Library::new(&path).map_err(|e| {
524            DeserializeError::CorruptedBinary(format!("Library loading failed: {}", e))
525        })?;
526        let shared_path: PathBuf = PathBuf::from(path);
527        // We use 10 + 1, as the length of the module will take 10 bytes
528        // (we construct it like that in `metadata_length`) and we also want
529        // to take the first element of the data to construct the slice from
530        // it.
531        let symbol: LibrarySymbol<*mut [u8; 10 + 1]> =
532            lib.get(WASMER_METADATA_SYMBOL).map_err(|e| {
533                DeserializeError::CorruptedBinary(format!(
534                    "The provided object file doesn't seem to be generated by Wasmer: {}",
535                    e
536                ))
537            })?;
538        use std::ops::Deref;
539        use std::slice;
540
541        let size = &mut **symbol.deref();
542        let mut readable = &size[..];
543        let metadata_len = leb128::read::unsigned(&mut readable).map_err(|_e| {
544            DeserializeError::CorruptedBinary("Can't read metadata size".to_string())
545        })?;
546        let metadata_slice: &'static [u8] =
547            slice::from_raw_parts(&size[10] as *const u8, metadata_len as usize);
548        let metadata: ModuleMetadata = bincode::deserialize(metadata_slice)
549            .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e)))?;
550        let mut engine_inner = engine.inner_mut();
551
552        Self::from_parts(&mut engine_inner, metadata, shared_path, lib)
553            .map_err(DeserializeError::Compiler)
554    }
555}
556
557impl Artifact for NativeArtifact {
558    fn module(&self) -> Arc<ModuleInfo> {
559        self.metadata.compile_info.module.clone()
560    }
561
562    fn module_ref(&self) -> &ModuleInfo {
563        &self.metadata.compile_info.module
564    }
565
566    fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
567        Arc::get_mut(&mut self.metadata.compile_info.module)
568    }
569
570    fn register_frame_info(&self) {
571        // Do nothing for now
572    }
573
574    fn features(&self) -> &Features {
575        &self.metadata.compile_info.features
576    }
577
578    fn data_initializers(&self) -> &[OwnedDataInitializer] {
579        &*self.metadata.data_initializers
580    }
581
582    fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
583        &self.metadata.compile_info.memory_styles
584    }
585
586    fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
587        &self.metadata.compile_info.table_styles
588    }
589
590    fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr> {
591        &self.finished_functions
592    }
593
594    fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
595        &self.finished_function_call_trampolines
596    }
597
598    fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
599        &self.finished_dynamic_function_trampolines
600    }
601
602    fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
603        &self.signatures
604    }
605
606    fn preinstantiate(&self) -> Result<(), InstantiationError> {
607        Ok(())
608    }
609
610    /// Serialize a NativeArtifact
611    fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
612        Ok(std::fs::read(&self.sharedobject_path)?)
613    }
614}