1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display)]
44pub enum Debugger {
45 #[strum(serialize = "GDB")]
47 Gdb,
48 #[strum(serialize = "LLDB")]
50 Lldb,
51}
52
53#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::Display)]
92pub enum ArtifactFormat {
93 #[strum(serialize = "rkyv")]
95 Rkyv,
96 #[strum(serialize = "native")]
98 Native,
99}
100
101pub trait CompilerConfig {
103 fn experimental_artifact(&mut self, _enable: bool) {}
105
106 fn max_table_elements(&mut self, _max_table_elements: u32) {}
108
109 fn enable_pic(&mut self) {
115 }
118
119 fn enable_verifier(&mut self) {
124 }
127
128 fn enable_perfmap(&mut self) {
130 }
132
133 fn enable_debugger(&mut self, _debugger: Debugger) {
135 }
137
138 fn enable_non_volatile_memops(&mut self) {}
141
142 fn enable_experimental_unaligned_memory_accesses(&mut self) {}
147
148 fn enable_readonly_funcref_table(&mut self) {}
151
152 fn canonicalize_nans(&mut self, _enable: bool) {
157 }
160
161 fn enable_m0_pass_param(&mut self, _enable: bool) {
163 }
166
167 fn compiler(self: Box<Self>) -> Box<dyn Compiler>;
169
170 fn default_features_for_target(&self, target: &Target) -> Features {
172 self.supported_features_for_target(target)
173 }
174
175 fn supported_features_for_target(&self, _target: &Target) -> Features {
177 Features::default()
178 }
179
180 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
193pub trait Compiler: Send + std::fmt::Debug {
195 fn name(&self) -> &str;
199
200 fn deterministic_id(&self) -> String;
203
204 fn artifact_format(&self) -> String {
206 ArtifactFormat::Rkyv.to_string()
207 }
208
209 #[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 #[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 fn compile_module(
254 &self,
255 target: &Target,
256 module: &CompileModuleInfo,
257 compile_info_blob: &[u8],
258 module_translation: &ModuleTranslationState,
259 function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
261 progress_callback: Option<&CompilationProgressCallback>,
262 ) -> Result<Compilation, CompileError>;
263
264 fn get_middlewares(&self) -> &[Arc<dyn ModuleMiddleware>];
266
267 fn enable_readonly_funcref_table(&self) -> bool {
269 false
270 }
271
272 fn get_cpu_features_used(&self, cpu_features: &EnumSet<CpuFeature>) -> EnumSet<CpuFeature> {
274 *cpu_features
275 }
276
277 fn get_perfmap_enabled(&self) -> bool {
279 false
280 }
281
282 fn get_debugger(&self) -> Option<Debugger> {
284 None
285 }
286}
287
288pub struct FunctionBucket<'a> {
290 functions: Vec<(LocalFunctionIndex, &'a FunctionBodyData<'a>)>,
291 pub size: usize,
293}
294
295impl<'a> FunctionBucket<'a> {
296 pub fn new() -> Self {
298 Self {
299 functions: Vec::new(),
300 size: 0,
301 }
302 }
303}
304
305pub 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 || 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
340pub trait CompiledFunction {}
342
343pub trait FuncTranslator {}
345
346#[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
427pub const WASM_LARGE_FUNCTION_THRESHOLD: u64 = 100_000;
429
430pub const WASM_TRAMPOLINE_ESTIMATED_BODY_SIZE: u64 = 1_000;
432
433pub struct CompiledObjects {
437 pub object_files: Vec<Vec<u8>>,
439 pub import_trampoline_object_files: Vec<Vec<u8>>,
441 pub trampoline_object_files: Vec<Vec<u8>>,
443 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 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 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 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 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
696pub 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 "-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 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 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
787pub 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}