wasmer_engine_dylib/
artifact.rs

1//! Define `DylibArtifact` to allow compiling and instantiating
2//! to be done as separate steps.
3
4use crate::engine::{DylibEngine, DylibEngineInner};
5use crate::serialize::ModuleMetadata;
6use crate::trampoline::{emit_trampolines, fill_trampoline_table, WASMER_TRAMPOLINES_SYMBOL};
7use enumset::EnumSet;
8use libloading::{Library, Symbol as LibrarySymbol};
9use loupe::MemoryUsage;
10use object::{write::CoffExportStyle, BinaryFormat};
11use std::error::Error;
12use std::fs::{self, File};
13use std::io::{Read, Write};
14use std::path::{Path, PathBuf};
15#[cfg(feature = "compiler")]
16use std::process::Command;
17use std::sync::{Arc, Mutex};
18use tempfile::NamedTempFile;
19use tracing::log::error;
20#[cfg(feature = "compiler")]
21use tracing::trace;
22use wasmer_artifact::ArtifactCreate;
23use wasmer_compiler::{
24    Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features,
25    FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple,
26};
27#[cfg(feature = "compiler")]
28use wasmer_compiler::{
29    CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain,
30    ModuleTranslationState,
31};
32use wasmer_engine::{
33    register_frame_info, Artifact, DeserializeError, FunctionExtent, GlobalFrameInfoRegistration,
34    InstantiationError, MetadataHeader, SerializeError,
35};
36#[cfg(feature = "compiler")]
37use wasmer_engine::{Engine, Tunables};
38#[cfg(feature = "compiler")]
39use wasmer_object::{emit_compilation, emit_data, get_object_for_target};
40use wasmer_types::entity::{BoxedSlice, PrimaryMap};
41#[cfg(feature = "compiler")]
42use wasmer_types::DataInitializer;
43use wasmer_types::{
44    FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer,
45    SignatureIndex, TableIndex,
46};
47use wasmer_vm::{
48    FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMFunctionBody,
49    VMSharedSignatureIndex, VMTrampoline,
50};
51
52/// A compiled Wasm module, ready to be instantiated.
53#[derive(MemoryUsage)]
54pub struct DylibArtifact {
55    dylib_path: PathBuf,
56    is_temporary: bool,
57    metadata: ModuleMetadata,
58    finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
59    #[loupe(skip)]
60    finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
61    finished_dynamic_function_trampolines: BoxedSlice<FunctionIndex, FunctionBodyPtr>,
62    func_data_registry: Arc<FuncDataRegistry>,
63    signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
64    frame_info_registration: Mutex<Option<GlobalFrameInfoRegistration>>,
65}
66
67impl Drop for DylibArtifact {
68    fn drop(&mut self) {
69        if self.is_temporary {
70            if let Err(err) = std::fs::remove_file(&self.dylib_path) {
71                error!("cannot delete the temporary dylib artifact: {}", err);
72            }
73        }
74    }
75}
76
77fn to_compile_error(err: impl Error) -> CompileError {
78    CompileError::Codegen(err.to_string())
79}
80
81const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA";
82
83impl DylibArtifact {
84    // Mach-O header in iOS/Mac
85    #[allow(dead_code)]
86    const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254];
87
88    // ELF Magic header for Linux (32 bit)
89    #[allow(dead_code)]
90    const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1];
91
92    // ELF Magic header for Linux (64 bit)
93    #[allow(dead_code)]
94    const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2];
95
96    // COFF Magic header for Windows (64 bit)
97    #[allow(dead_code)]
98    const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z'];
99
100    /// Check if the provided bytes look like `DylibArtifact`.
101    ///
102    /// This means, if the bytes look like a shared object file in the target
103    /// system.
104    pub fn is_deserializable(bytes: &[u8]) -> bool {
105        cfg_if::cfg_if! {
106            if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] {
107                bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64)
108            }
109            else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] {
110                bytes.starts_with(Self::MAGIC_HEADER_ELF_64)
111            }
112            else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] {
113                bytes.starts_with(Self::MAGIC_HEADER_ELF_32)
114            }
115            else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] {
116                bytes.starts_with(Self::MAGIC_HEADER_COFF_64)
117            }
118            else {
119                false
120            }
121        }
122    }
123
124    #[cfg(feature = "compiler")]
125    /// Generate a compilation
126    fn generate_metadata<'data>(
127        data: &'data [u8],
128        features: &Features,
129        compiler: &dyn Compiler,
130        tunables: &dyn Tunables,
131    ) -> Result<
132        (
133            CompileModuleInfo,
134            PrimaryMap<LocalFunctionIndex, FunctionBodyData<'data>>,
135            Vec<DataInitializer<'data>>,
136            Option<ModuleTranslationState>,
137        ),
138        CompileError,
139    > {
140        let environ = ModuleEnvironment::new();
141        let translation = environ.translate(data).map_err(CompileError::Wasm)?;
142
143        // We try to apply the middleware first
144        let mut module = translation.module;
145        let middlewares = compiler.get_middlewares();
146        middlewares.apply_on_module_info(&mut module);
147
148        let memory_styles: PrimaryMap<MemoryIndex, MemoryStyle> = module
149            .memories
150            .values()
151            .map(|memory_type| tunables.memory_style(memory_type))
152            .collect();
153        let table_styles: PrimaryMap<TableIndex, TableStyle> = module
154            .tables
155            .values()
156            .map(|table_type| tunables.table_style(table_type))
157            .collect();
158
159        let compile_info = CompileModuleInfo {
160            module: Arc::new(module),
161            features: features.clone(),
162            memory_styles,
163            table_styles,
164        };
165        Ok((
166            compile_info,
167            translation.function_body_inputs,
168            translation.data_initializers,
169            translation.module_translation_state,
170        ))
171    }
172
173    /// Compile a data buffer into a `DylibArtifact`, which may
174    /// then be instantiated.
175    #[cfg(feature = "compiler")]
176    pub fn new(
177        engine: &DylibEngine,
178        data: &[u8],
179        tunables: &dyn Tunables,
180    ) -> Result<Self, CompileError> {
181        let mut engine_inner = engine.inner_mut();
182        let target = engine.target();
183        let compiler = engine_inner.compiler()?;
184        let (compile_info, function_body_inputs, data_initializers, module_translation) =
185            Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?;
186
187        let data_initializers = data_initializers
188            .iter()
189            .map(OwnedDataInitializer::new)
190            .collect::<Vec<_>>()
191            .into_boxed_slice();
192
193        let target_triple = target.triple();
194
195        /*
196        // We construct the function body lengths
197        let function_body_lengths = compilation
198            .get_function_bodies()
199            .values()
200            .map(|function_body| function_body.body.len() as u64)
201            .map(|_function_body| 0u64)
202            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
203         */
204
205        // TODO: we currently supply all-zero function body lengths.
206        // We don't know the lengths until they're compiled, yet we have to
207        // supply the metadata as an input to the compile.
208        let function_body_lengths = function_body_inputs
209            .keys()
210            .map(|_function_body| 0u64)
211            .collect::<PrimaryMap<LocalFunctionIndex, u64>>();
212
213        let function_frame_info = None;
214
215        let mut metadata = ModuleMetadata {
216            compile_info,
217            function_frame_info,
218            prefix: engine_inner.get_prefix(data),
219            data_initializers,
220            function_body_lengths,
221            cpu_features: target.cpu_features().as_u64(),
222        };
223
224        let serialized_data = metadata.serialize()?;
225
226        let mut metadata_binary = vec![];
227        metadata_binary.extend(MetadataHeader::new(serialized_data.len()));
228        metadata_binary.extend(serialized_data);
229
230        let (compile_info, symbol_registry) = metadata.split();
231
232        let maybe_obj_bytes = compiler.experimental_native_compile_module(
233            target,
234            compile_info,
235            module_translation.as_ref().unwrap(),
236            &function_body_inputs,
237            &symbol_registry,
238            &metadata_binary,
239        );
240
241        let mut extra_filepath = None;
242        let filepath = match maybe_obj_bytes {
243            Some(obj_bytes) => {
244                extra_filepath = {
245                    // Create a separate object file with the trampolines.
246                    let mut obj = get_object_for_target(target_triple).map_err(to_compile_error)?;
247                    emit_trampolines(&mut obj, engine.target());
248                    if obj.format() == BinaryFormat::Coff {
249                        obj.add_coff_exports(CoffExportStyle::Gnu);
250                    }
251                    let file = tempfile::Builder::new()
252                        .prefix("wasmer_dylib_")
253                        .suffix(".o")
254                        .tempfile()
255                        .map_err(to_compile_error)?;
256
257                    // Re-open it.
258                    let (mut file, filepath) = file.keep().map_err(to_compile_error)?;
259                    let obj_bytes = obj.write().map_err(to_compile_error)?;
260                    file.write_all(&obj_bytes).map_err(to_compile_error)?;
261                    Some(filepath)
262                };
263
264                // Write the object file generated by the compiler.
265                let obj_bytes = obj_bytes?;
266                let file = tempfile::Builder::new()
267                    .prefix("wasmer_dylib_")
268                    .suffix(".o")
269                    .tempfile()
270                    .map_err(to_compile_error)?;
271
272                // Re-open it.
273                let (mut file, filepath) = file.keep().map_err(to_compile_error)?;
274                file.write_all(&obj_bytes).map_err(to_compile_error)?;
275                filepath
276            }
277            None => {
278                let compilation = compiler.compile_module(
279                    target,
280                    compile_info,
281                    module_translation.as_ref().unwrap(),
282                    function_body_inputs,
283                )?;
284                let mut obj = get_object_for_target(target_triple).map_err(to_compile_error)?;
285                emit_trampolines(&mut obj, engine.target());
286                emit_data(
287                    &mut obj,
288                    WASMER_METADATA_SYMBOL,
289                    &metadata_binary,
290                    MetadataHeader::ALIGN as u64,
291                )
292                .map_err(to_compile_error)?;
293
294                let frame_info = compilation.get_frame_info();
295
296                emit_compilation(&mut obj, compilation, &symbol_registry, target_triple)
297                    .map_err(to_compile_error)?;
298                if obj.format() == BinaryFormat::Coff {
299                    obj.add_coff_exports(CoffExportStyle::Gnu);
300                }
301                let file = tempfile::Builder::new()
302                    .prefix("wasmer_dylib_")
303                    .suffix(".o")
304                    .tempfile()
305                    .map_err(to_compile_error)?;
306
307                metadata.function_frame_info = Some(frame_info);
308
309                // Re-open it.
310                let (mut file, filepath) = file.keep().map_err(to_compile_error)?;
311                let obj_bytes = obj.write().map_err(to_compile_error)?;
312
313                file.write_all(&obj_bytes).map_err(to_compile_error)?;
314                filepath
315            }
316        };
317
318        let output_filepath = {
319            let suffix = format!(".{}", Self::get_default_extension(target_triple));
320            let shared_file = tempfile::Builder::new()
321                .prefix("wasmer_dylib_")
322                .suffix(&suffix)
323                .tempfile()
324                .map_err(to_compile_error)?;
325            shared_file
326                .into_temp_path()
327                .keep()
328                .map_err(to_compile_error)?
329        };
330
331        let is_cross_compiling = engine_inner.is_cross_compiling();
332        let target_triple_str = {
333            let into_str = target_triple.to_string();
334            // We have to adapt the target triple string, because otherwise
335            // Apple's clang will not recognize it.
336            if into_str == "aarch64-apple-darwin" {
337                "arm64-apple-darwin".to_string()
338            } else {
339                into_str
340            }
341        };
342
343        // Set 'isysroot' clang flag if compiling to iOS target
344        let ios_compile_target = target_triple.operating_system == OperatingSystem::Ios;
345        let ios_sdk_flag = {
346            if ios_compile_target {
347                if target_triple.architecture == Architecture::X86_64 {
348                    "-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"
349                } else {
350                    "-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk"
351                }
352            } else {
353                ""
354            }
355        };
356        let ios_sdk_lib = {
357            if ios_compile_target {
358                "-lSystem"
359            } else {
360                ""
361            }
362        };
363
364        // Get the location of the 'ld' linker for clang
365        let fuse_linker = {
366            let ld_install = which::which("ld");
367            if ios_compile_target && ld_install.is_ok() {
368                ld_install.unwrap().into_os_string().into_string().unwrap()
369            } else {
370                "lld".to_string()
371            }
372        };
373
374        let cross_compiling_args: Vec<String> = if is_cross_compiling {
375            vec![
376                format!("--target={}", target_triple_str),
377                format!("-fuse-ld={}", fuse_linker),
378                "-nodefaultlibs".to_string(),
379                "-nostdlib".to_string(),
380                ios_sdk_flag.to_string(),
381                ios_sdk_lib.to_string(),
382            ]
383        } else {
384            // We are explicit on the target when the host system is
385            // Apple Silicon, otherwise compilation fails.
386            if target_triple_str == "arm64-apple-darwin" {
387                vec![format!("--target={}", target_triple_str)]
388            } else {
389                vec![]
390            }
391        };
392        let target_args = match (target_triple.operating_system, is_cross_compiling) {
393            (OperatingSystem::Windows, true) => vec!["-Wl,/force:unresolved,/noentry"],
394            (OperatingSystem::Windows, false) => vec!["-Wl,-undefined,dynamic_lookup"],
395            _ => vec!["-nostartfiles", "-Wl,-undefined,dynamic_lookup"],
396        };
397        trace!(
398            "Compiling for target {} from host {}",
399            target_triple_str,
400            Triple::host().to_string(),
401        );
402
403        let linker = engine_inner.linker().executable();
404        let output = Command::new(linker)
405            .arg(&filepath)
406            .args(&extra_filepath)
407            .arg("-o")
408            .arg(&output_filepath)
409            .args(&target_args)
410            // .args(&wasmer_symbols)
411            .arg("-shared")
412            .args(&cross_compiling_args)
413            .arg("-v")
414            .output()
415            .map_err(to_compile_error);
416
417        if fs::metadata(&filepath).is_ok() {
418            fs::remove_file(filepath).map_err(to_compile_error)?;
419        }
420        if let Some(filepath) = extra_filepath {
421            if fs::metadata(&filepath).is_ok() {
422                fs::remove_file(filepath).map_err(to_compile_error)?;
423            }
424        }
425
426        let output = output?;
427
428        if !output.status.success() {
429            return Err(CompileError::Codegen(format!(
430                "Shared object file generator failed with:\nstderr:{}\nstdout:{}",
431                String::from_utf8_lossy(&output.stderr).trim_end(),
432                String::from_utf8_lossy(&output.stdout).trim_end()
433            )));
434        }
435
436        trace!("gcc command result {:?}", output);
437
438        let mut artifact = if is_cross_compiling {
439            Self::from_parts_crosscompiled(metadata, output_filepath)
440        } else {
441            let lib = unsafe { Library::new(&output_filepath).map_err(to_compile_error)? };
442            Self::from_parts(&mut engine_inner, metadata, output_filepath, lib)
443        }?;
444        artifact.is_temporary = true;
445
446        Ok(artifact)
447    }
448
449    /// Get the default extension when serializing this artifact
450    pub fn get_default_extension(triple: &Triple) -> &'static str {
451        match triple.operating_system {
452            OperatingSystem::Windows => "dll",
453            OperatingSystem::Darwin | OperatingSystem::Ios | OperatingSystem::MacOSX { .. } => {
454                "dylib"
455            }
456            _ => "so",
457        }
458    }
459
460    /// Construct a `DylibArtifact` from component parts.
461    pub fn from_parts_crosscompiled(
462        metadata: ModuleMetadata,
463        dylib_path: PathBuf,
464    ) -> Result<Self, CompileError> {
465        let finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> = PrimaryMap::new();
466        let finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
467            PrimaryMap::new();
468        let finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
469            PrimaryMap::new();
470        let signatures: PrimaryMap<SignatureIndex, VMSharedSignatureIndex> = PrimaryMap::new();
471        Ok(Self {
472            dylib_path,
473            is_temporary: false,
474            metadata,
475            finished_functions: finished_functions.into_boxed_slice(),
476            finished_function_call_trampolines: finished_function_call_trampolines
477                .into_boxed_slice(),
478            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
479                .into_boxed_slice(),
480            func_data_registry: Arc::new(FuncDataRegistry::new()),
481            signatures: signatures.into_boxed_slice(),
482            frame_info_registration: Mutex::new(None),
483        })
484    }
485
486    /// Construct a `DylibArtifact` from component parts.
487    pub fn from_parts(
488        engine_inner: &mut DylibEngineInner,
489        metadata: ModuleMetadata,
490        dylib_path: PathBuf,
491        lib: Library,
492    ) -> Result<Self, CompileError> {
493        unsafe {
494            let trampolines_symbol: LibrarySymbol<usize> = lib
495                .get(WASMER_TRAMPOLINES_SYMBOL)
496                .expect("missing WASMER_TRAMPOLINES symbol");
497            fill_trampoline_table(trampolines_symbol.into_raw().into_raw() as *mut usize);
498        }
499
500        let mut finished_functions: PrimaryMap<LocalFunctionIndex, FunctionBodyPtr> =
501            PrimaryMap::new();
502        for (function_local_index, _function_len) in metadata.function_body_lengths.iter() {
503            let function_name = metadata
504                .get_symbol_registry()
505                .symbol_to_name(Symbol::LocalFunction(function_local_index));
506            unsafe {
507                // We use a fake function signature `fn()` because we just
508                // want to get the function address.
509                let func: LibrarySymbol<unsafe extern "C" fn()> = lib
510                    .get(function_name.as_bytes())
511                    .map_err(to_compile_error)?;
512                finished_functions.push(FunctionBodyPtr(
513                    func.into_raw().into_raw() as *const VMFunctionBody
514                ));
515            }
516        }
517
518        // Retrieve function call trampolines
519        let mut finished_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
520            PrimaryMap::with_capacity(metadata.compile_info.module.signatures.len());
521        for sig_index in metadata.compile_info.module.signatures.keys() {
522            let function_name = metadata
523                .get_symbol_registry()
524                .symbol_to_name(Symbol::FunctionCallTrampoline(sig_index));
525            unsafe {
526                let trampoline: LibrarySymbol<VMTrampoline> = lib
527                    .get(function_name.as_bytes())
528                    .map_err(to_compile_error)?;
529                let raw = *trampoline.into_raw();
530                finished_function_call_trampolines.push(raw);
531            }
532        }
533
534        // Retrieve dynamic function trampolines (only for imported functions)
535        let mut finished_dynamic_function_trampolines: PrimaryMap<FunctionIndex, FunctionBodyPtr> =
536            PrimaryMap::with_capacity(metadata.compile_info.module.num_imported_functions);
537        for func_index in metadata
538            .compile_info
539            .module
540            .functions
541            .keys()
542            .take(metadata.compile_info.module.num_imported_functions)
543        {
544            let function_name = metadata
545                .get_symbol_registry()
546                .symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index));
547            unsafe {
548                let trampoline: LibrarySymbol<unsafe extern "C" fn()> = lib
549                    .get(function_name.as_bytes())
550                    .map_err(to_compile_error)?;
551                finished_dynamic_function_trampolines.push(FunctionBodyPtr(
552                    trampoline.into_raw().into_raw() as *const VMFunctionBody,
553                ));
554            }
555        }
556
557        // Compute indices into the shared signature table.
558        let signatures = {
559            metadata
560                .compile_info
561                .module
562                .signatures
563                .values()
564                .map(|sig| engine_inner.signatures().register(sig))
565                .collect::<PrimaryMap<_, _>>()
566        };
567
568        engine_inner.add_library(lib);
569
570        Ok(Self {
571            dylib_path,
572            is_temporary: false,
573            metadata,
574            finished_functions: finished_functions.into_boxed_slice(),
575            finished_function_call_trampolines: finished_function_call_trampolines
576                .into_boxed_slice(),
577            finished_dynamic_function_trampolines: finished_dynamic_function_trampolines
578                .into_boxed_slice(),
579            func_data_registry: engine_inner.func_data().clone(),
580            signatures: signatures.into_boxed_slice(),
581            frame_info_registration: Mutex::new(None),
582        })
583    }
584
585    /// Compile a data buffer into a `DylibArtifact`, which may
586    /// then be instantiated.
587    #[cfg(not(feature = "compiler"))]
588    pub fn new(_engine: &DylibEngine, _data: &[u8]) -> Result<Self, CompileError> {
589        Err(CompileError::Codegen(
590            "Compilation is not enabled in the engine".to_string(),
591        ))
592    }
593
594    /// Deserialize a `DylibArtifact` from bytes.
595    ///
596    /// # Safety
597    ///
598    /// The bytes must represent a serialized WebAssembly module.
599    pub unsafe fn deserialize(
600        engine: &DylibEngine,
601        bytes: &[u8],
602    ) -> Result<Self, DeserializeError> {
603        if !Self::is_deserializable(bytes) {
604            return Err(DeserializeError::Incompatible(
605                "The provided bytes are not in any native format Wasmer can understand".to_string(),
606            ));
607        }
608        // Dump the bytes into a file, so we can read it with our `dlopen`
609        let named_file = NamedTempFile::new()?;
610        let (mut file, path) = named_file.keep().map_err(|e| e.error)?;
611        file.write_all(bytes)?;
612        // We already checked for the header, so we don't need
613        // to check again.
614        let mut artifact = Self::deserialize_from_file_unchecked(engine, &path)?;
615        artifact.is_temporary = true;
616
617        Ok(artifact)
618    }
619
620    /// Deserialize a `DylibArtifact` from a file path.
621    ///
622    /// # Safety
623    ///
624    /// The file's content must represent a serialized WebAssembly module.
625    pub unsafe fn deserialize_from_file(
626        engine: &DylibEngine,
627        path: &Path,
628    ) -> Result<Self, DeserializeError> {
629        let mut file = File::open(&path)?;
630        let mut buffer = [0; 5];
631        // read up to 5 bytes
632        file.read_exact(&mut buffer)?;
633        if !Self::is_deserializable(&buffer) {
634            return Err(DeserializeError::Incompatible(
635                "The provided bytes are not in any native format Wasmer can understand".to_string(),
636            ));
637        }
638        Self::deserialize_from_file_unchecked(engine, path)
639    }
640
641    /// Deserialize a `DylibArtifact` from a file path (unchecked).
642    ///
643    /// # Safety
644    ///
645    /// The file's content must represent a serialized WebAssembly module.
646    pub unsafe fn deserialize_from_file_unchecked(
647        engine: &DylibEngine,
648        path: &Path,
649    ) -> Result<Self, DeserializeError> {
650        let lib = Library::new(&path).map_err(|e| {
651            DeserializeError::CorruptedBinary(format!("Library loading failed: {}", e))
652        })?;
653        let shared_path: PathBuf = PathBuf::from(path);
654        let metadata_symbol: LibrarySymbol<*mut [u8; MetadataHeader::LEN]> =
655            lib.get(WASMER_METADATA_SYMBOL).map_err(|e| {
656                DeserializeError::CorruptedBinary(format!(
657                    "The provided object file doesn't seem to be generated by Wasmer: {}",
658                    e
659                ))
660            })?;
661        use std::slice;
662
663        let metadata = &**metadata_symbol;
664        let metadata_len = MetadataHeader::parse(metadata)?;
665        let metadata_slice: &'static [u8] =
666            slice::from_raw_parts(metadata.as_ptr().add(MetadataHeader::LEN), metadata_len);
667
668        let metadata = ModuleMetadata::deserialize(metadata_slice)?;
669
670        let mut engine_inner = engine.inner_mut();
671
672        Self::from_parts(&mut engine_inner, metadata, shared_path, lib)
673            .map_err(DeserializeError::Compiler)
674    }
675
676    /// Used in test deserialize metadata is correct
677    pub fn metadata(&self) -> &ModuleMetadata {
678        &self.metadata
679    }
680}
681
682impl ArtifactCreate for DylibArtifact {
683    fn module(&self) -> Arc<ModuleInfo> {
684        self.metadata.compile_info.module.clone()
685    }
686
687    fn module_ref(&self) -> &ModuleInfo {
688        &self.metadata.compile_info.module
689    }
690
691    fn module_mut(&mut self) -> Option<&mut ModuleInfo> {
692        Arc::get_mut(&mut self.metadata.compile_info.module)
693    }
694
695    fn features(&self) -> &Features {
696        &self.metadata.compile_info.features
697    }
698
699    fn cpu_features(&self) -> enumset::EnumSet<CpuFeature> {
700        EnumSet::from_u64(self.metadata.cpu_features)
701    }
702
703    fn data_initializers(&self) -> &[OwnedDataInitializer] {
704        &*self.metadata.data_initializers
705    }
706
707    fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle> {
708        &self.metadata.compile_info.memory_styles
709    }
710
711    fn table_styles(&self) -> &PrimaryMap<TableIndex, TableStyle> {
712        &self.metadata.compile_info.table_styles
713    }
714
715    /// Serialize a `DylibArtifact`.
716    fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
717        Ok(std::fs::read(&self.dylib_path)?)
718    }
719
720    /// Serialize a `DylibArtifact` to a portable file
721    #[cfg(feature = "compiler")]
722    fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> {
723        let serialized = self.serialize()?;
724        std::fs::write(&path, serialized)?;
725
726        /*
727        When you write the artifact to a new file it still has the 'Mach-O Identifier'
728        of the original file, and so this can causes linker issues when adding
729        the new file to an XCode project.
730
731        The below code renames the ID of the file so that it references itself through
732        an @executable_path prefix. Basically it tells XCode to find this file
733        inside of the projects' list of 'linked executables'.
734
735        You need to be running MacOS for the following to actually work though.
736        */
737        let has_extension = path.extension().is_some();
738        if has_extension && path.extension().unwrap() == "dylib" {
739            let filename = path.file_name().unwrap().to_str().unwrap();
740            let parent_dir = path.parent().unwrap();
741            let absolute_path = std::fs::canonicalize(&parent_dir)
742                .unwrap()
743                .into_os_string()
744                .into_string()
745                .unwrap();
746
747            Command::new("install_name_tool")
748                .arg("-id")
749                .arg(format!("@executable_path/{}", &filename))
750                .arg(&filename)
751                .current_dir(&absolute_path)
752                .output()?;
753        }
754
755        Ok(())
756    }
757}
758impl Artifact for DylibArtifact {
759    fn register_frame_info(&self) {
760        let mut info = self.frame_info_registration.lock().unwrap();
761
762        if info.is_some() {
763            return;
764        }
765
766        // We (reverse) order all the functions by their pointer location.
767        // [f9, f6, f7, f8...] and calculate their potential function body size by
768        // getting the diff in pointers between functions (since they are all located
769        // in the same __text section).
770
771        let min_call_trampolines_pointer = self
772            .finished_function_call_trampolines
773            .values()
774            .map(|t| *t as usize)
775            .min()
776            .unwrap_or(0);
777        let min_dynamic_trampolines_pointer = self
778            .finished_dynamic_function_trampolines
779            .values()
780            .map(|t| **t as usize)
781            .min()
782            .unwrap_or(0);
783
784        let fp = self.finished_functions.clone();
785        let mut function_pointers = fp.into_iter().collect::<Vec<_>>();
786
787        // Sort the keys by the funciton pointer values in reverse order.
788        // This way we can get the maximum function lengths (since functions can't overlap in memory)
789        function_pointers.sort_by(|(_k1, v1), (_k2, v2)| v2.cmp(v1));
790        let mut iter = function_pointers.into_iter();
791        let mut function_pointers = iter
792            .next()
793            .map(|(index, function_pointer)| {
794                let fp = **function_pointer as usize;
795                // In case we are in the first function pointer (the one with the highest pointer)
796                // we try to determine it's bounds (function size) by using the other function trampoline
797                // locations.
798                let current_size_by_ptr = if fp < min_call_trampolines_pointer {
799                    if min_call_trampolines_pointer < min_dynamic_trampolines_pointer
800                        || min_dynamic_trampolines_pointer == 0
801                    {
802                        min_call_trampolines_pointer - fp
803                    } else {
804                        min_dynamic_trampolines_pointer - fp
805                    }
806                } else if fp < min_dynamic_trampolines_pointer {
807                    min_dynamic_trampolines_pointer - fp
808                } else {
809                    // The trampoline pointers are before the function.
810                    // We can safely assume the function will be at least 16 bits of length.
811                    // This is a very hacky assumption, but it makes collisions work perfectly
812                    // Since DLOpen simlinks will always be > 16 bits of difference between
813                    // two different libraries for the same symbol.
814                    // Note: the minmum Mach-O file is 0x1000 bytes and the minimum ELF is 0x0060 bytes
815                    16
816                };
817                let mut prev_pointer = fp;
818                // We choose the minimum between the function size given the pointer diff
819                // and the emitted size by the address map
820                let ptr = function_pointer;
821                let length = current_size_by_ptr;
822                let first = (
823                    index,
824                    FunctionExtent {
825                        ptr: *ptr,
826                        length: length,
827                    },
828                );
829                std::iter::once(first)
830                    .chain(iter.map(|(index, function_pointer)| {
831                        let fp = **function_pointer as usize;
832                        // This assumes we never lay any functions bodies across the usize::MAX..nullptr
833                        // wrapping point.
834                        // Which is generally true on most OSes, but certainly doesn't have to be true.
835                        //
836                        // Further reading: https://lwn.net/Articles/342330/ \
837                        // "There is one little problem with that reasoning, though: NULL (zero) can
838                        // actually be a valid pointer address."
839                        let current_size_by_ptr = prev_pointer - fp;
840
841                        prev_pointer = fp;
842                        // We choose the minimum between the function size given the pointer diff
843                        // and the emitted size by the address map
844                        let ptr = function_pointer;
845                        let length = current_size_by_ptr;
846                        (
847                            index,
848                            FunctionExtent {
849                                ptr: *ptr,
850                                length: length,
851                            },
852                        )
853                    }))
854                    .collect::<Vec<_>>()
855            })
856            .unwrap_or_default();
857
858        // We sort them again, by key this time
859        function_pointers.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2));
860
861        let frame_infos = if self.metadata().function_frame_info.is_some() {
862            self.metadata().function_frame_info.clone().unwrap()
863        } else {
864            function_pointers
865                .iter()
866                .map(|(_, extent)| CompiledFunctionFrameInfo {
867                    traps: vec![],
868                    address_map: FunctionAddressMap {
869                        body_len: extent.length,
870                        ..Default::default()
871                    },
872                })
873                .collect::<PrimaryMap<LocalFunctionIndex, _>>()
874        };
875
876        let finished_function_extents = function_pointers
877            .into_iter()
878            .map(|(_, function_extent)| function_extent)
879            .collect::<PrimaryMap<LocalFunctionIndex, _>>()
880            .into_boxed_slice();
881
882        *info = register_frame_info(
883            self.metadata.compile_info.module.clone(),
884            &finished_function_extents,
885            frame_infos,
886        );
887    }
888
889    fn finished_functions(&self) -> &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr> {
890        &self.finished_functions
891    }
892
893    fn finished_function_call_trampolines(&self) -> &BoxedSlice<SignatureIndex, VMTrampoline> {
894        &self.finished_function_call_trampolines
895    }
896
897    fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice<FunctionIndex, FunctionBodyPtr> {
898        &self.finished_dynamic_function_trampolines
899    }
900
901    fn signatures(&self) -> &BoxedSlice<SignatureIndex, VMSharedSignatureIndex> {
902        &self.signatures
903    }
904
905    fn func_data_registry(&self) -> &FuncDataRegistry {
906        &self.func_data_registry
907    }
908
909    fn preinstantiate(&self) -> Result<(), InstantiationError> {
910        Ok(())
911    }
912}