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