Skip to main content

wasmer_compiler/
compiler.rs

1//! This module mainly outputs the `Compiler` trait that custom
2//! compilers will need to implement.
3
4use std::cmp::Reverse;
5use std::collections::HashMap;
6use std::fs::File;
7use std::path::{Path, PathBuf};
8use std::sync::Mutex;
9
10use crate::EH_FRAME_SECTION_NAME;
11use crate::misc::{CompiledFunctionExt, CompiledKind};
12use crate::object::get_object_for_target;
13use crate::progress::ProgressContext;
14use crate::types::function::Compilation;
15use crate::types::module::CompileModuleInfo;
16use crate::{
17    FunctionBodyData, ModuleTranslationState, WASMER_FUNCTION_OFFSETS_SECTION_NAME,
18    WASMER_TRAP_FUNCTION_OFFSETS_SECTION_NAME, translator::ModuleMiddleware,
19};
20use crossbeam_channel::unbounded;
21use enumset::EnumSet;
22use itertools::Itertools;
23use libwild::{
24    Args, FileSystem, FileType, InputFileData, Linker, OutputFileData, OutputOptions, error,
25};
26use object::write::{Relocation, StandardSegment, Symbol as ObjSymbol, SymbolSection};
27use object::{
28    RelocationEncoding, RelocationFlags, RelocationKind, SectionFlags, SectionKind, SymbolFlags,
29    SymbolKind, SymbolScope, elf,
30};
31use std::{boxed::Box, sync::Arc};
32use wasmer_types::{
33    CompilationProgressCallback, Features, FunctionIndex, LocalFunctionIndex,
34    entity::{EntityRef, PrimaryMap},
35    error::CompileError,
36    target::{CpuFeature, Target, UserCompilerOptimizations},
37};
38use wasmer_types::{FunctionType, SignatureIndex};
39#[cfg(feature = "translator")]
40use wasmparser::{Validator, WasmFeatures};
41
42/// A debugger command-file format for registering JIT-compiled modules.
43#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display)]
44pub enum Debugger {
45    /// GDB command file.
46    #[strum(serialize = "GDB")]
47    Gdb,
48    /// LLDB command file.
49    #[strum(serialize = "LLDB")]
50    Lldb,
51}
52
53/// A component representing a code-generation-sensitive aspect of a compiler
54/// configuration for artifact format creation.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display)]
56#[allow(missing_docs)]
57pub enum DeterministicIdComponent {
58    #[strum(serialize = "llvm")]
59    Llvm,
60    #[strum(serialize = "cranelift")]
61    Cranelift,
62    #[strum(serialize = "singlepass")]
63    Singlepass,
64    #[strum(serialize = "opt0")]
65    OptNone,
66    #[strum(serialize = "optl")]
67    OptLess,
68    #[strum(serialize = "optd")]
69    OptDefault,
70    #[strum(serialize = "opta")]
71    OptAggressive,
72    #[strum(serialize = "opts")]
73    OptSpeed,
74    #[strum(serialize = "optsz")]
75    OptSpeedAndSize,
76    #[strum(serialize = "nan_canon")]
77    NanCanonicalization,
78    #[strum(serialize = "non_vol_mem")]
79    NonVolatileMemops,
80    #[strum(serialize = "pic")]
81    Pic,
82    #[strum(serialize = "ro_ftable")]
83    ReadonlyFuncrefTable,
84    #[strum(serialize = "unaligned_mem")]
85    ExperimentalUnalignedMemoryAccesses,
86}
87
88/// The artifact format used for purpose of serialization.
89#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display)]
90pub enum ArtifactFormat {
91    /// rkyv serialization based.
92    #[strum(serialize = "rkyv")]
93    Rkyv,
94    /// Native format, such as ELF.
95    #[strum(serialize = "native")]
96    Native,
97}
98
99/// The compiler configuration options.
100pub trait CompilerConfig {
101    /// Enable the experimental artifact format.
102    fn experimental_artifact(&mut self, _enable: bool) {}
103
104    /// Enable Position Independent Code (PIC).
105    ///
106    /// This is required for shared object generation (Native Engine),
107    /// but will make the JIT Engine to fail, since PIC is not yet
108    /// supported in the JIT linking phase.
109    fn enable_pic(&mut self) {
110        // By default we do nothing, each backend will need to customize this
111        // in case they do something special for emitting PIC code.
112    }
113
114    /// Enable compiler IR verification.
115    ///
116    /// For compilers capable of doing so, this enables internal consistency
117    /// checking.
118    fn enable_verifier(&mut self) {
119        // By default we do nothing, each backend will need to customize this
120        // in case they create an IR that they can verify.
121    }
122
123    /// Enable generation of perfmaps to sample the JIT compiled frames.
124    fn enable_perfmap(&mut self) {
125        // By default we do nothing, each backend will need to customize this.
126    }
127
128    /// Enable generation of a debugger command file for JIT compiled frames.
129    fn enable_debugger(&mut self, _debugger: Debugger) {
130        // By default we do nothing, each backend will need to customize this.
131    }
132
133    /// For the LLVM compiler, we can use non-volatile memory operations which lead to a better performance
134    /// (but are not 100% SPEC compliant).
135    fn enable_non_volatile_memops(&mut self) {}
136
137    /// Enable run-time handling of potentially unaligned memory accesses.
138    ///
139    /// This feature is experimental and currently supports only Cranelift scalar types
140    /// and Singlepass on RISC-V for integral types.
141    fn enable_experimental_unaligned_memory_accesses(&mut self) {}
142
143    /// Enables treating eligible funcref tables as read-only so the backend can
144    /// place them in read-only data.
145    fn enable_readonly_funcref_table(&mut self) {}
146
147    /// Enable NaN canonicalization.
148    ///
149    /// NaN canonicalization is useful when trying to run WebAssembly
150    /// deterministically across different architectures.
151    fn canonicalize_nans(&mut self, _enable: bool) {
152        // By default we do nothing, each backend will need to customize this
153        // in case they create an IR that they can verify.
154    }
155
156    /// Gets the custom compiler config
157    fn compiler(self: Box<Self>) -> Box<dyn Compiler>;
158
159    /// Gets the default features for this compiler in the given target
160    fn default_features_for_target(&self, target: &Target) -> Features {
161        self.supported_features_for_target(target)
162    }
163
164    /// Gets the supported features for this compiler in the given target
165    fn supported_features_for_target(&self, _target: &Target) -> Features {
166        Features::default()
167    }
168
169    /// Pushes a middleware onto the back of the middleware chain.
170    fn push_middleware(&mut self, middleware: Arc<dyn ModuleMiddleware>);
171}
172
173impl<T> From<T> for Box<dyn CompilerConfig + 'static>
174where
175    T: CompilerConfig + 'static,
176{
177    fn from(other: T) -> Self {
178        Box::new(other)
179    }
180}
181
182/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
183pub trait Compiler: Send + std::fmt::Debug {
184    /// Returns a descriptive name for this compiler.
185    ///
186    /// Note that this is an API breaking change since 3.0
187    fn name(&self) -> &str;
188
189    /// Returns the deterministic id of this compiler. Same compilers with different
190    /// optimizations map to different deterministic IDs.
191    fn deterministic_id(&self) -> String;
192
193    /// Returns the used artifact format: `rkyv` or `native`.
194    fn artifact_format(&self) -> String {
195        ArtifactFormat::Rkyv.to_string()
196    }
197
198    /// Add suggested optimizations to this compiler.
199    ///
200    /// # Note
201    ///
202    /// Not every compiler supports every optimization. This function may fail (i.e. not set the
203    /// suggested optimizations) silently if the underlying compiler does not support one or
204    /// more optimizations.
205    fn with_opts(
206        &mut self,
207        suggested_compiler_opts: &UserCompilerOptimizations,
208    ) -> Result<(), CompileError> {
209        _ = suggested_compiler_opts;
210        Ok(())
211    }
212
213    /// Validates a module.
214    ///
215    /// It returns the a successful Result in case is valid, `CompileError` in case is not.
216    #[cfg(feature = "translator")]
217    fn validate_module(&self, features: &Features, data: &[u8]) -> Result<(), CompileError> {
218        let mut wasm_features = WasmFeatures::empty();
219        wasm_features.set(WasmFeatures::BULK_MEMORY, features.bulk_memory);
220        wasm_features.set(WasmFeatures::THREADS, features.threads);
221        wasm_features.set(WasmFeatures::REFERENCE_TYPES, features.reference_types);
222        wasm_features.set(WasmFeatures::MULTI_VALUE, features.multi_value);
223        wasm_features.set(WasmFeatures::SIMD, features.simd);
224        wasm_features.set(WasmFeatures::TAIL_CALL, features.tail_call);
225        wasm_features.set(WasmFeatures::MULTI_MEMORY, features.multi_memory);
226        wasm_features.set(WasmFeatures::MEMORY64, features.memory64);
227        wasm_features.set(WasmFeatures::EXCEPTIONS, features.exceptions);
228        wasm_features.set(WasmFeatures::EXTENDED_CONST, features.extended_const);
229        wasm_features.set(WasmFeatures::RELAXED_SIMD, features.relaxed_simd);
230        wasm_features.set(WasmFeatures::WIDE_ARITHMETIC, features.wide_arithmetic);
231        wasm_features.set(WasmFeatures::TAIL_CALL, features.tail_call);
232        wasm_features.set(WasmFeatures::MUTABLE_GLOBAL, true);
233        wasm_features.set(WasmFeatures::SATURATING_FLOAT_TO_INT, true);
234        wasm_features.set(WasmFeatures::FLOATS, true);
235        wasm_features.set(WasmFeatures::SIGN_EXTENSION, true);
236        wasm_features.set(WasmFeatures::GC_TYPES, true);
237
238        let mut validator = Validator::new_with_features(wasm_features);
239        validator
240            .validate_all(data)
241            .map_err(|e| CompileError::Validate(format!("{e}")))?;
242        Ok(())
243    }
244
245    /// Compiles a parsed module.
246    ///
247    /// It returns the [`Compilation`] or a [`CompileError`].
248    fn compile_module(
249        &self,
250        target: &Target,
251        module: &CompileModuleInfo,
252        compile_info_blob: &[u8],
253        module_translation: &ModuleTranslationState,
254        // The list of function bodies
255        function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
256        progress_callback: Option<&CompilationProgressCallback>,
257    ) -> Result<Compilation, CompileError>;
258
259    /// Get the middlewares for this compiler
260    fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>];
261
262    /// Get whether translation-time readonly funcref table analysis should run.
263    fn enable_readonly_funcref_table(&self) -> bool {
264        false
265    }
266
267    /// Get the CpuFeatures used by the compiler
268    fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
269        *cpu_features
270    }
271
272    /// Get whether `perfmap` is enabled or not.
273    fn get_perfmap_enabled(&self) -> bool {
274        false
275    }
276
277    /// Get the enabled debugger command-file format, if any.
278    fn get_debugger(&self) -> Option<Debugger> {
279        None
280    }
281}
282
283/// A bucket containing a group of functions and their total size, used to balance compilation units for parallel compilation.
284pub struct FunctionBucket<'a> {
285    functions: Vec<(LocalFunctionIndex, &'a FunctionBodyData<'a>)>,
286    /// IR size of the bucket (in bytes).
287    pub size: usize,
288}
289
290impl<'a> FunctionBucket<'a> {
291    /// Creates a new, empty `FunctionBucket`.
292    pub fn new() -> Self {
293        Self {
294            functions: Vec::new(),
295            size: 0,
296        }
297    }
298}
299
300/// Build buckets sized by function length to keep compilation units balanced for parallel compilation.
301pub fn build_function_buckets<'a>(
302    function_body_inputs: &'a PrimaryMap<LocalFunctionIndex, FunctionBodyData<'a>>,
303    bucket_threshold_size: u64,
304) -> Vec<FunctionBucket<'a>> {
305    let mut function_bodies = function_body_inputs
306        .iter()
307        .sorted_by_key(|(id, body)| Reverse((body.data.len(), id.as_u32())))
308        .collect_vec();
309
310    let mut buckets = Vec::new();
311
312    while !function_bodies.is_empty() {
313        let mut next_function_body = Vec::with_capacity(function_bodies.len());
314        let mut bucket = FunctionBucket::new();
315
316        for (fn_index, fn_body) in function_bodies.into_iter() {
317            if bucket.size + fn_body.data.len() <= bucket_threshold_size as usize
318                // Huge functions must fit into a bucket!
319                || bucket.size == 0
320            {
321                bucket.size += fn_body.data.len();
322                bucket.functions.push((fn_index, fn_body));
323            } else {
324                next_function_body.push((fn_index, fn_body));
325            }
326        }
327
328        function_bodies = next_function_body;
329        buckets.push(bucket);
330    }
331
332    buckets
333}
334
335/// Represents a function that has been compiled by the backend compiler.
336pub trait CompiledFunction {}
337
338/// Translates a function from its input representation to a compiled form.
339pub trait FuncTranslator {}
340
341/// Compile function buckets largest-first via the channel (instead of Rayon's par_iter).
342#[allow(clippy::too_many_arguments)]
343pub fn translate_function_buckets<'a, C, T, F, G>(
344    pool: &rayon::ThreadPool,
345    func_translator_builder: F,
346    translate_fn: G,
347    progress: Option<ProgressContext>,
348    buckets: &[FunctionBucket<'a>],
349) -> Result<Vec<C>, CompileError>
350where
351    T: FuncTranslator,
352    C: CompiledFunction + Send + Sync,
353    F: Fn() -> T + Send + Sync + Copy,
354    G: Fn(&mut T, &LocalFunctionIndex, &FunctionBodyData) -> Result<C, CompileError>
355        + Send
356        + Sync
357        + Copy,
358{
359    let progress = progress.as_ref();
360
361    let functions = pool.install(|| {
362        let (bucket_tx, bucket_rx) = unbounded::<&FunctionBucket<'a>>();
363        for bucket in buckets {
364            bucket_tx.send(bucket).map_err(|e| {
365                CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
366            })?;
367        }
368        drop(bucket_tx);
369
370        let (result_tx, result_rx) =
371            unbounded::<Result<Vec<(LocalFunctionIndex, C)>, CompileError>>();
372
373        pool.scope(|s| {
374            let worker_count = pool.current_num_threads().max(1);
375            for _ in 0..worker_count {
376                let bucket_rx = bucket_rx.clone();
377                let result_tx = result_tx.clone();
378                s.spawn(move |_| {
379                    let mut func_translator = func_translator_builder();
380
381                    while let Ok(bucket) = bucket_rx.recv() {
382                        let bucket_result = (|| {
383                            let mut translated_functions = Vec::new();
384                            for (i, input) in bucket.functions.iter() {
385                                let translated = translate_fn(&mut func_translator, i, input)?;
386                                if let Some(progress) = progress {
387                                    progress.notify_steps(input.data.len() as u64)?;
388                                }
389                                translated_functions.push((*i, translated));
390                            }
391                            Ok(translated_functions)
392                        })();
393
394                        if result_tx.send(bucket_result).is_err() {
395                            break;
396                        }
397                    }
398                });
399            }
400        });
401
402        drop(result_tx);
403        let mut functions = Vec::with_capacity(buckets.iter().map(|b| b.functions.len()).sum());
404        for _ in 0..buckets.len() {
405            match result_rx.recv().map_err(|e| {
406                CompileError::Resource(format!("cannot allocate crossbeam channel item: {e}"))
407            })? {
408                Ok(bucket_functions) => functions.extend(bucket_functions),
409                Err(err) => return Err(err),
410            }
411        }
412        Ok(functions)
413    })?;
414
415    Ok(functions
416        .into_iter()
417        .sorted_by_key(|x| x.0)
418        .map(|(_, body)| body)
419        .collect_vec())
420}
421
422/// Byte size threshold for a function that is considered large.
423pub const WASM_LARGE_FUNCTION_THRESHOLD: u64 = 100_000;
424
425/// Estimated byte size of a trampoline (used for progress bar reporting).
426pub const WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE: u64 = 1_000;
427
428/// Holds the sets of compiled object buffers produced during compilation.
429///
430/// Counts of each category are derived from the slice lengths.
431pub struct CompiledObjects {
432    /// Objects for local (user-defined) functions.
433    pub object_files: Vec<Vec<u8>>,
434    /// Objects for imported function call trampolines.
435    pub import_trampoline_object_files: Vec<Vec<u8>>,
436    /// Objects for static trampolines.
437    pub trampoline_object_files: Vec<Vec<u8>>,
438    /// Objects for dynamic trampolines.
439    pub dynamic_trampoline_object_files: Vec<Vec<u8>>,
440}
441
442fn emit_wasmer_meta_object(
443    target: &Target,
444    compile_info_blob: &[u8],
445    compiled_objects: &CompiledObjects,
446) -> Result<Vec<u8>, String> {
447    let mut obj = get_object_for_target(target.triple())
448        .map_err(|e| format!("failed to create Wasmer meta object: {e}"))?;
449
450    let section_id = obj.add_section(
451        obj.segment_name(StandardSegment::Data).to_vec(),
452        crate::WASMER_MODULE_INFO_SECTION_NAME.to_vec(),
453        SectionKind::Other,
454    );
455    obj.append_section_data(section_id, compile_info_blob, 8);
456    obj.section_mut(section_id).flags = SectionFlags::Elf {
457        sh_type: elf::SHT_PROGBITS,
458        sh_flags: elf::SHF_GNU_RETAIN,
459    };
460
461    // Emit zero sentinel for the .eh_frame section.
462    let section_id = obj.add_section(
463        obj.segment_name(StandardSegment::Debug).to_vec(),
464        EH_FRAME_SECTION_NAME.to_vec(),
465        SectionKind::Debug,
466    );
467    obj.append_section_data(section_id, &0u64.to_ne_bytes(), 4);
468
469    // Emit offsets of the functions
470    let section_id = obj.add_section(
471        obj.segment_name(StandardSegment::Data).to_vec(),
472        WASMER_FUNCTION_OFFSETS_SECTION_NAME.to_vec(),
473        SectionKind::Other,
474    );
475    obj.section_mut(section_id).flags = SectionFlags::Elf {
476        sh_type: elf::SHT_PROGBITS,
477        sh_flags: elf::SHF_GNU_RETAIN,
478    };
479    let pointer_size = target
480        .triple()
481        .pointer_width()
482        .map_err(|_| "unknown pointer width".to_string())?
483        .bytes() as u64;
484    let pointer_bits = (pointer_size * 8) as u8;
485    let zero_pointer = vec![0; pointer_size as usize];
486
487    let function_offset_names = (0..compiled_objects.object_files.len())
488        .map(|i| CompiledKind::Local(LocalFunctionIndex::new(i), String::new()).linkage_name())
489        .chain(
490            (0..compiled_objects.trampoline_object_files.len()).map(|i| {
491                CompiledKind::FunctionCallTrampoline(
492                    SignatureIndex::new(i),
493                    // Unused by the linkage_name.
494                    FunctionType::new([], []),
495                )
496                .linkage_name()
497            }),
498        )
499        .chain(
500            (0..compiled_objects.dynamic_trampoline_object_files.len()).map(|i| {
501                CompiledKind::DynamicFunctionTrampoline(
502                    FunctionIndex::new(i),
503                    // Unused by the linkage_name.
504                    FunctionType::new([], []),
505                )
506                .linkage_name()
507            }),
508        );
509    for function_name in function_offset_names {
510        let offset = obj.append_section_data(section_id, &zero_pointer, pointer_size);
511        let symbol_id = obj.add_symbol(ObjSymbol {
512            name: function_name.to_owned().into(),
513            value: 0,
514            size: 0,
515            kind: SymbolKind::Text,
516            scope: SymbolScope::Unknown,
517            weak: false,
518            section: SymbolSection::Undefined,
519            flags: SymbolFlags::None,
520        });
521        obj.add_relocation(
522            section_id,
523            Relocation {
524                offset,
525                flags: RelocationFlags::Generic {
526                    kind: RelocationKind::Absolute,
527                    encoding: RelocationEncoding::Generic,
528                    size: pointer_bits,
529                },
530                symbol: symbol_id,
531                addend: 0,
532            },
533        )
534        .map_err(|e| {
535            format!("failed to add function offset relocation for {function_name}: {e}")
536        })?;
537    }
538
539    let trap_fn_offsets_section_id = obj.add_section(
540        obj.segment_name(StandardSegment::Data).to_vec(),
541        WASMER_TRAP_FUNCTION_OFFSETS_SECTION_NAME.to_vec(),
542        SectionKind::Other,
543    );
544    obj.section_mut(trap_fn_offsets_section_id).flags = SectionFlags::Elf {
545        sh_type: elf::SHT_PROGBITS,
546        sh_flags: elf::SHF_GNU_RETAIN,
547    };
548    for traps_name in (0..compiled_objects.object_files.len())
549        .map(|i| CompiledKind::Local(LocalFunctionIndex::new(i), String::new()).traps_name())
550    {
551        let offset =
552            obj.append_section_data(trap_fn_offsets_section_id, &zero_pointer, pointer_size);
553        let symbol_id = obj.add_symbol(ObjSymbol {
554            name: traps_name.as_bytes().into(),
555            value: 0,
556            size: 0,
557            kind: SymbolKind::Data,
558            scope: SymbolScope::Linkage,
559            weak: true,
560            section: SymbolSection::Undefined,
561            flags: SymbolFlags::None,
562        });
563        obj.add_relocation(
564            trap_fn_offsets_section_id,
565            Relocation {
566                offset,
567                flags: RelocationFlags::Generic {
568                    kind: RelocationKind::Absolute,
569                    encoding: RelocationEncoding::Generic,
570                    size: pointer_bits,
571                },
572                symbol: symbol_id,
573                addend: 0,
574            },
575        )
576        .map_err(|e| {
577            format!("failed to add function trap offset relocation for {traps_name}: {e}")
578        })?;
579    }
580
581    obj.write()
582        .map_err(|e| format!("failed to serialize Wasmer meta object: {e}"))
583}
584
585#[derive(Clone, Default)]
586struct InMemoryFileSystem {
587    files: Arc<Mutex<HashMap<PathBuf, Arc<Vec<u8>>>>>,
588}
589
590#[derive(Debug)]
591struct InMemoryInput(Arc<Vec<u8>>);
592
593impl InputFileData for InMemoryInput {
594    fn bytes(&self) -> &[u8] {
595        &self.0
596    }
597}
598
599struct InMemoryOutput {
600    path: PathBuf,
601    bytes: Vec<u8>,
602    files: Arc<Mutex<HashMap<PathBuf, Arc<Vec<u8>>>>>,
603}
604
605impl OutputFileData for InMemoryOutput {
606    fn bytes(&self) -> &[u8] {
607        &self.bytes
608    }
609    fn bytes_mut(&mut self) -> &mut [u8] {
610        &mut self.bytes
611    }
612    fn finish(mut self) -> error::Result {
613        self.files
614            .lock()
615            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?
616            .insert(self.path, Arc::new(std::mem::take(&mut self.bytes)));
617        Ok(())
618    }
619}
620
621impl FileSystem for InMemoryFileSystem {
622    type Input = InMemoryInput;
623    type Output = InMemoryOutput;
624
625    fn open_input(&self, path: &Path, _: bool) -> error::Result<(Self::Input, Option<Arc<File>>)> {
626        let bytes = self
627            .files
628            .lock()
629            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?
630            .get(path)
631            .map(Arc::clone)
632            .ok_or_else(|| error!("No such in-memory file: {}", path.display()))?;
633        Ok((InMemoryInput(bytes), None))
634    }
635
636    fn file_type(&self, path: &Path) -> error::Result<FileType> {
637        self.files
638            .lock()
639            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?
640            .contains_key(path)
641            .then_some(FileType::File)
642            .ok_or_else(|| error!("no such in-memory file"))
643    }
644
645    fn canonicalize(&self, path: &Path) -> error::Result<PathBuf> {
646        Ok(path.to_path_buf())
647    }
648    fn remove_file(&self, path: &Path) -> error::Result<()> {
649        self.files
650            .lock()
651            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?
652            .remove(path)
653            .map(|_| ())
654            .ok_or_else(|| error!("no such in-memory file"))
655    }
656    fn rename_file(&self, path: &Path, new_path: &Path) -> error::Result<()> {
657        let mut files = self
658            .files
659            .lock()
660            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?;
661        let bytes = files
662            .remove(path)
663            .ok_or_else(|| error!("no such in-memory file"))?;
664        files.insert(new_path.to_path_buf(), bytes);
665        Ok(())
666    }
667    fn create_output(
668        &self,
669        path: Arc<Path>,
670        options: OutputOptions,
671    ) -> error::Result<Self::Output> {
672        let size = usize::try_from(options.size).map_err(|_| error!("output is too large"))?;
673        Ok(InMemoryOutput {
674            path: path.to_path_buf(),
675            bytes: vec![0; size],
676            files: Arc::clone(&self.files),
677        })
678    }
679    fn write_auxiliary(&self, path: &Path, bytes: &[u8]) -> error::Result {
680        self.files
681            .lock()
682            .map_err(|e| format!("cannot lock in-memory FS: {e}"))?
683            .insert(path.to_path_buf(), Arc::new(bytes.to_vec()));
684        Ok(())
685    }
686}
687
688const WASMER_IMAGE_FILENAME: &str = "wasmer-image.so";
689const WASMER_META_FILENAME: &str = "__wasmer_meta.o";
690
691/// Emits Wasmer metadata sections and links backend-generated object buffers into a shared object.
692pub fn emit_metadata_and_link(
693    pool: &rayon::ThreadPool,
694    target: &Target,
695    compile_info_blob: &[u8],
696    compiled_objects: CompiledObjects,
697    mut debug_dir: Option<PathBuf>,
698    module_hash: Option<String>,
699) -> Result<Vec<u8>, CompileError> {
700    pool.install(|| {
701        let meta_object = emit_wasmer_meta_object(target, compile_info_blob, &compiled_objects)
702            .map_err(CompileError::Codegen)?;
703        let CompiledObjects {
704            object_files,
705            import_trampoline_object_files,
706            trampoline_object_files,
707            dynamic_trampoline_object_files,
708        } = compiled_objects;
709        let fs = InMemoryFileSystem::default();
710        let mut link_args = vec![
711            "ld".to_string(),
712            // Allow resolution of the public symbols directly without PLT entries!
713            "-Bsymbolic".to_string(),
714            "-shared".to_string(),
715            "-z".to_string(),
716            "now".to_string(),
717            "-z".to_string(),
718            "relro".to_string(),
719            "-o".to_string(),
720            WASMER_IMAGE_FILENAME.to_string(),
721        ];
722
723        {
724            let mut files = fs
725                .files
726                .lock()
727                .map_err(|e| CompileError::Codegen(format!("cannot lock in-memory FS: {e}")))?;
728            for (index, object) in object_files
729                .into_iter()
730                .chain(import_trampoline_object_files)
731                .chain(trampoline_object_files)
732                .chain(dynamic_trampoline_object_files)
733                .enumerate()
734            {
735                let path = PathBuf::from(format!("object-{index}.o"));
736                files.insert(path.clone(), Arc::new(object));
737                link_args.push(path.display().to_string());
738            }
739            files.insert(PathBuf::from(WASMER_META_FILENAME), Arc::new(meta_object));
740        }
741        // Keep the synthetic `.eh_frame` terminator after the real CIE/FDE
742        // records. Linkers concatenate input sections in object order, and a
743        // leading terminator makes frame registration see an empty table.
744        link_args.push(WASMER_META_FILENAME.to_string());
745
746        let mut wild_args = Args::new(|| link_args.iter().map(String::as_str)).map_err(|e| {
747            CompileError::Codegen(format!("failed to initialize Wild linker: {e:?}"))
748        })?;
749        wild_args
750            .parse(|| link_args.iter().map(String::as_str))
751            .map_err(|e| {
752                CompileError::Codegen(format!("failed to parse Wild linker args: {e:?}"))
753            })?;
754        Linker::with_file_system(fs.clone())
755            .run(&wild_args)
756            .map_err(|e| CompileError::Codegen(format!("Wild linker failed: {e:?}")))?;
757
758        let image = fs
759            .files
760            .lock()
761            .map_err(|e| CompileError::Codegen(format!("cannot lock in-memory FS: {e}")))?
762            .remove(Path::new(WASMER_IMAGE_FILENAME))
763            .ok_or_else(|| CompileError::Codegen("Wild linker did not produce an output".into()))?;
764        let image = Arc::try_unwrap(image).map_err(|_| {
765            CompileError::Codegen("Wild linker retained a reference to the output buffer".into())
766        })?;
767
768        // If compiler-debug-dir is set, copy the final linked .so image
769        // into the module_hash subfolder.
770        if let Some(debug_dir) = debug_dir.as_mut() {
771            if let Some(ref hash) = module_hash {
772                debug_dir.push(hash);
773            }
774            std::fs::create_dir_all(&debug_dir).ok();
775            debug_dir.push(WASMER_IMAGE_FILENAME);
776            let _ = std::fs::write(debug_dir, &image);
777        }
778        Ok(image)
779    })
780}