1use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2use std::sync::{Arc, Mutex};
3
4use crate::engine::builder::EngineBuilder;
5#[cfg(feature = "compiler")]
6use crate::{Compiler, CompilerConfig, Debugger};
7
8#[cfg(not(target_arch = "wasm32"))]
9use wasmer_types::CompilationProgressCallback;
10#[cfg(feature = "compiler")]
11use wasmer_types::Features;
12use wasmer_types::{CompileError, target::Target};
13
14#[cfg(not(target_arch = "wasm32"))]
15use shared_buffer::OwnedBuffer;
16#[cfg(not(target_arch = "wasm32"))]
17use std::ffi::c_void;
18#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
19use std::io::Write;
20#[cfg(not(target_arch = "wasm32"))]
21use std::io::{Read, Seek};
22#[cfg(all(not(target_arch = "wasm32"), unix))]
23use std::os::fd::RawFd;
24#[cfg(not(target_arch = "wasm32"))]
25use std::path::Path;
26#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
27use wasmer_types::ModuleInfo;
28#[cfg(not(target_arch = "wasm32"))]
29use wasmer_types::{
30 DeserializeError, FunctionIndex, FunctionType, LocalFunctionIndex, SignatureHash,
31 SignatureIndex, entity::PrimaryMap,
32};
33
34#[cfg(not(target_arch = "wasm32"))]
35use crate::{
36 Artifact, BaseTunables, CodeMemory, FunctionExtent, GlobalFrameInfoRegistration, Tunables,
37 engine::mapped_binary::MemoryMappedBinary,
38 types::{
39 function::FunctionBodyLike,
40 section::{CustomSectionLike, CustomSectionProtection, SectionIndex},
41 },
42};
43
44#[cfg(not(target_arch = "wasm32"))]
45use wasmer_vm::{
46 FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMFunctionBody, VMSignatureHash,
47 VMTrampoline,
48};
49
50#[derive(Clone)]
52pub struct Engine {
53 inner: Arc<Mutex<EngineInner>>,
54 target: Arc<Target>,
56 engine_id: EngineId,
57 #[cfg(not(target_arch = "wasm32"))]
58 tunables: Arc<dyn Tunables + Send + Sync>,
59 name: String,
60}
61
62impl Engine {
63 #[cfg(feature = "compiler")]
65 pub fn new(
66 compiler_config: Box<dyn CompilerConfig>,
67 target: Target,
68 features: Features,
69 ) -> Self {
70 #[cfg(not(target_arch = "wasm32"))]
71 let tunables = BaseTunables::for_target(&target);
72 let compiler = compiler_config.compiler();
73 let name = format!("engine-{}", compiler.name());
74 Self {
75 inner: Arc::new(Mutex::new(EngineInner {
76 compiler: Some(compiler),
77 features,
78 #[cfg(not(target_arch = "wasm32"))]
79 code_memory: vec![],
80 #[cfg(not(target_arch = "wasm32"))]
81 elf_mapped_binary: vec![],
82 #[cfg(not(target_arch = "wasm32"))]
83 signatures: SignatureRegistry::new(),
84 })),
85 target: Arc::new(target),
86 engine_id: EngineId::default(),
87 #[cfg(not(target_arch = "wasm32"))]
88 tunables: Arc::new(tunables),
89 name,
90 }
91 }
92
93 pub fn name(&self) -> &str {
95 self.name.as_str()
96 }
97
98 pub fn deterministic_id(&self) -> String {
100 #[cfg(feature = "compiler")]
101 {
102 let i = self.inner();
103 if let Some(ref c) = i.compiler {
104 return c.deterministic_id();
105 } else {
106 return self.name.clone();
107 }
108 }
109
110 #[allow(unreachable_code)]
111 {
112 self.name.to_string()
113 }
114 }
115
116 pub fn artifact_format(&self) -> String {
118 #[cfg(feature = "compiler")]
119 {
120 let i = self.inner();
121 if let Some(ref c) = i.compiler {
122 return c.artifact_format();
123 }
124 }
125
126 #[allow(unreachable_code)]
127 self.name.to_string()
128 }
129
130 pub fn headless() -> Self {
144 let target = Target::default();
145 #[cfg(not(target_arch = "wasm32"))]
146 let tunables = BaseTunables::for_target(&target);
147 Self {
148 inner: Arc::new(Mutex::new(EngineInner {
149 #[cfg(feature = "compiler")]
150 compiler: None,
151 #[cfg(feature = "compiler")]
152 features: Features::default(),
153 #[cfg(not(target_arch = "wasm32"))]
154 code_memory: vec![],
155 #[cfg(not(target_arch = "wasm32"))]
156 elf_mapped_binary: vec![],
157 #[cfg(not(target_arch = "wasm32"))]
158 signatures: SignatureRegistry::new(),
159 })),
160 target: Arc::new(target),
161 engine_id: EngineId::default(),
162 #[cfg(not(target_arch = "wasm32"))]
163 tunables: Arc::new(tunables),
164 name: "engine-headless".to_string(),
165 }
166 }
167
168 pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
170 self.inner.lock().unwrap()
171 }
172
173 pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
175 self.inner.lock().unwrap()
176 }
177
178 pub fn target(&self) -> &Target {
180 &self.target
181 }
182
183 #[cfg(not(target_arch = "wasm32"))]
185 pub fn register_signature(&self, func_type: &FunctionType) -> VMSignatureHash {
186 let compiler = self.inner();
187 compiler
188 .signatures()
189 .register(func_type, SignatureHash(func_type.signature_hash()))
190 }
191
192 #[cfg(not(target_arch = "wasm32"))]
194 pub fn lookup_signature(&self, sig_hash: VMSignatureHash) -> Option<FunctionType> {
195 let compiler = self.inner();
196 compiler.signatures().lookup_signature(sig_hash)
197 }
198
199 #[cfg(feature = "compiler")]
201 pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
202 self.inner().validate(binary)
203 }
204
205 #[cfg(feature = "compiler")]
207 #[cfg(not(target_arch = "wasm32"))]
208 pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
209 Ok(Arc::new(Artifact::new(
210 self,
211 binary,
212 self.tunables.as_ref(),
213 None,
214 )?))
215 }
216
217 #[cfg(feature = "compiler")]
219 pub fn compile_with_progress(
220 &self,
221 binary: &[u8],
222 progress_callback: Option<CompilationProgressCallback>,
223 ) -> Result<Arc<Artifact>, CompileError> {
224 Ok(Arc::new(Artifact::new(
225 self,
226 binary,
227 self.tunables.as_ref(),
228 progress_callback,
229 )?))
230 }
231
232 #[cfg(not(feature = "compiler"))]
234 #[cfg(not(target_arch = "wasm32"))]
235 pub fn compile_with_progress(
236 &self,
237 binary: &[u8],
238 _progress_callback: Option<CompilationProgressCallback>,
239 ) -> Result<Arc<Artifact>, CompileError> {
240 self.compile(binary, self.tunables.as_ref())
241 }
242
243 #[cfg(not(feature = "compiler"))]
245 #[cfg(not(target_arch = "wasm32"))]
246 pub fn compile(
247 &self,
248 _binary: &[u8],
249 _tunables: &dyn Tunables,
250 ) -> Result<Arc<Artifact>, CompileError> {
251 Err(CompileError::Codegen(
252 "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
253 ))
254 }
255
256 #[cfg(not(target_arch = "wasm32"))]
257 pub unsafe fn deserialize_unchecked(
264 &self,
265 bytes: OwnedBuffer,
266 ) -> Result<Arc<Artifact>, DeserializeError> {
267 unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
268 }
269
270 #[cfg(not(target_arch = "wasm32"))]
277 pub unsafe fn deserialize(
278 &self,
279 bytes: OwnedBuffer,
280 ) -> Result<Arc<Artifact>, DeserializeError> {
281 unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
282 }
283
284 #[cfg(not(target_arch = "wasm32"))]
289 pub unsafe fn deserialize_from_file(
290 &self,
291 file_ref: &Path,
292 ) -> Result<Arc<Artifact>, DeserializeError> {
293 unsafe {
294 let mut file = std::fs::File::open(file_ref)?;
295 let mut magic = [0; 4];
296 let is_elf = file.read_exact(&mut magic).is_ok() && magic == object::elf::ELFMAG;
297 if is_elf {
298 return Ok(Arc::new(Artifact::deserialize_file(self, file_ref)?));
299 }
300 file.rewind()?;
301 self.deserialize(
302 OwnedBuffer::from_file(&file)
303 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
304 )
305 }
306 }
307
308 #[cfg(not(target_arch = "wasm32"))]
314 pub unsafe fn deserialize_from_file_unchecked(
315 &self,
316 file_ref: &Path,
317 ) -> Result<Arc<Artifact>, DeserializeError> {
318 unsafe {
319 let file = std::fs::File::open(file_ref)?;
320 self.deserialize_unchecked(
321 OwnedBuffer::from_file(&file)
322 .map_err(|e| DeserializeError::Generic(e.to_string()))?,
323 )
324 }
325 }
326
327 pub fn id(&self) -> &EngineId {
333 &self.engine_id
334 }
335
336 pub fn cloned(&self) -> Self {
338 self.clone()
339 }
340
341 #[cfg(not(target_arch = "wasm32"))]
343 pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
344 self.tunables = Arc::new(tunables);
345 }
346
347 #[cfg(not(target_arch = "wasm32"))]
349 pub fn tunables(&self) -> &dyn Tunables {
350 self.tunables.as_ref()
351 }
352
353 pub fn with_opts(
361 &mut self,
362 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
363 ) -> Result<(), CompileError> {
364 #[cfg(feature = "compiler")]
365 {
366 let mut i = self.inner_mut();
367 if let Some(ref mut c) = i.compiler {
368 c.with_opts(_suggested_opts)?;
369 }
370 }
371
372 Ok(())
373 }
374}
375
376impl std::fmt::Debug for Engine {
377 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
378 write!(f, "{}", self.deterministic_id())
379 }
380}
381
382pub struct EngineInner {
384 #[cfg(feature = "compiler")]
385 compiler: Option<Box<dyn Compiler>>,
387 #[cfg(feature = "compiler")]
388 features: Features,
390 #[cfg(not(target_arch = "wasm32"))]
393 code_memory: Vec<CodeMemory>,
394 #[cfg(not(target_arch = "wasm32"))]
396 elf_mapped_binary: Vec<MemoryMappedBinary>,
397 #[cfg(not(target_arch = "wasm32"))]
400 signatures: SignatureRegistry,
401}
402
403impl std::fmt::Debug for EngineInner {
404 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
405 let mut formatter = f.debug_struct("EngineInner");
406 #[cfg(feature = "compiler")]
407 {
408 formatter.field("compiler", &self.compiler);
409 formatter.field("features", &self.features);
410 }
411
412 #[cfg(not(target_arch = "wasm32"))]
413 {
414 formatter.field("signatures", &self.signatures);
415 }
416
417 formatter.finish()
418 }
419}
420
421impl EngineInner {
422 #[cfg(feature = "compiler")]
424 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
425 match self.compiler.as_ref() {
426 None => Err(CompileError::Codegen(
427 "No compiler compiled into executable".to_string(),
428 )),
429 Some(compiler) => Ok(&**compiler),
430 }
431 }
432
433 #[cfg(feature = "compiler")]
435 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
436 let compiler = self.compiler()?;
437 compiler.validate_module(&self.features, data)
438 }
439
440 #[cfg(feature = "compiler")]
442 pub fn features(&self) -> &Features {
443 &self.features
444 }
445
446 #[cfg(not(target_arch = "wasm32"))]
448 #[allow(clippy::type_complexity)]
449 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
450 &'a mut self,
451 _module: &wasmer_types::ModuleInfo,
452 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
453 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
454 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
455 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
456 ) -> Result<
457 (
458 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
459 PrimaryMap<SignatureIndex, VMTrampoline>,
460 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
461 PrimaryMap<SectionIndex, SectionBodyPtr>,
462 ),
463 CompileError,
464 >
465 where
466 FunctionBody: FunctionBodyLike<'a> + 'a,
467 CustomSection: CustomSectionLike<'a> + 'a,
468 {
469 let functions_len = functions.len();
470 let function_call_trampolines_len = function_call_trampolines.len();
471
472 let function_bodies = functions
473 .chain(function_call_trampolines)
474 .chain(dynamic_function_trampolines)
475 .collect::<Vec<_>>();
476 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
477 .clone()
478 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
479 self.code_memory.push(CodeMemory::new());
480
481 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
482 self.code_memory
483 .last_mut()
484 .unwrap()
485 .allocate(
486 function_bodies.as_slice(),
487 executable_sections.as_slice(),
488 data_sections.as_slice(),
489 )
490 .map_err(|message| {
491 CompileError::Resource(format!(
492 "failed to allocate memory for functions: {message}",
493 ))
494 })?;
495
496 let allocated_functions_result = allocated_functions
497 .drain(0..functions_len)
498 .map(|slice| FunctionExtent {
499 ptr: FunctionBodyPtr(slice.as_ptr()),
500 length: slice.len(),
501 })
502 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
503
504 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
505 PrimaryMap::new();
506 for ptr in allocated_functions
507 .drain(0..function_call_trampolines_len)
508 .map(|slice| slice.as_ptr())
509 {
510 let trampoline =
511 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
512 allocated_function_call_trampolines.push(trampoline);
513 }
514
515 let allocated_dynamic_function_trampolines = allocated_functions
516 .drain(..)
517 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
518 .collect::<PrimaryMap<FunctionIndex, _>>();
519
520 let mut exec_iter = allocated_executable_sections.iter();
521 let mut data_iter = allocated_data_sections.iter();
522 let allocated_custom_sections = custom_sections
523 .map(|section| {
524 SectionBodyPtr(
525 if section.protection() == CustomSectionProtection::ReadExecute {
526 exec_iter.next()
527 } else {
528 data_iter.next()
529 }
530 .unwrap()
531 .as_ptr(),
532 )
533 })
534 .collect::<PrimaryMap<SectionIndex, _>>();
535 Ok((
536 allocated_functions_result,
537 allocated_function_call_trampolines,
538 allocated_dynamic_function_trampolines,
539 allocated_custom_sections,
540 ))
541 }
542
543 #[cfg(not(target_arch = "wasm32"))]
544 pub(crate) fn publish_compiled_code(&mut self) {
546 self.code_memory.last_mut().unwrap().publish();
547 }
548
549 #[cfg(not(target_arch = "wasm32"))]
550 #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
551 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
553 self.code_memory
554 .last_mut()
555 .unwrap()
556 .unwind_registry_mut()
557 .publish_eh_frame(eh_frame)
558 .map_err(|e| {
559 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
560 })?;
561 Ok(())
562 }
563 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
564 pub(crate) fn publish_compact_unwind(
566 &mut self,
567 compact_unwind: &[u8],
568 eh_personality_addr_in_got: Option<usize>,
569 ) -> Result<(), CompileError> {
570 self.code_memory
571 .last_mut()
572 .unwrap()
573 .unwind_registry_mut()
574 .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
575 .map_err(|e| {
576 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
577 })?;
578 Ok(())
579 }
580
581 #[cfg(not(target_arch = "wasm32"))]
585 pub(crate) fn map_elf_binary<'a, R: object::ReadRef<'a>>(
586 &mut self,
587 object_file: &object::File<'a, R>,
588 data: &[u8],
589 ) -> Result<*mut c_void, CompileError> {
590 let map = MemoryMappedBinary::try_from_bytes(object_file, data)
591 .map_err(CompileError::Resource)?;
592 let base = map.base();
593 self.elf_mapped_binary.push(map);
594 Ok(base)
595 }
596
597 #[cfg(all(not(target_arch = "wasm32"), unix))]
599 pub(crate) fn map_elf_binary_file<'a, R: object::ReadRef<'a>>(
600 &mut self,
601 object_file: &object::File<'a, R>,
602 file: RawFd,
603 ) -> Result<*mut c_void, CompileError> {
604 let map =
605 MemoryMappedBinary::try_from_file(object_file, file).map_err(CompileError::Resource)?;
606 let base = map.base();
607 self.elf_mapped_binary.push(map);
608 Ok(base)
609 }
610
611 #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
612 pub(crate) fn debugger(&self) -> Option<Debugger> {
613 self.compiler
614 .as_ref()
615 .and_then(|compiler| compiler.get_debugger())
616 }
617
618 #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
619 pub(crate) fn register_debugger(
620 &self,
621 path: &Path,
622 base: *mut c_void,
623 debugger: Debugger,
624 ) -> Result<(), CompileError> {
625 use std::io::Write as _;
626
627 let path = path
628 .canonicalize()
629 .unwrap_or_else(|_| path.to_path_buf())
630 .to_string_lossy()
631 .to_string();
632 let (command, source_command) = match debugger {
633 Debugger::Gdb => (
634 format!("add-symbol-file \"{path}\" -o 0x{:x}", base as usize),
635 "source",
636 ),
637 Debugger::Lldb => (
638 format!(
639 "target modules add \"{path}\"\ntarget modules load --file \"{path}\" --slide 0x{:x}",
640 base as usize
641 ),
642 "command source",
643 ),
644 };
645 let filename = format!(
646 "/tmp/wasmer-{}.{}",
647 debugger.to_string().to_lowercase(),
648 std::process::id()
649 );
650 let mut file = std::fs::OpenOptions::new()
651 .append(true)
652 .create(true)
653 .open(&filename)
654 .map_err(|error| CompileError::Resource(format!("Cannot open {filename}: {error}")))?;
655 writeln!(file, "{command}")
656 .map_err(|error| CompileError::Resource(format!("Cannot write {filename}: {error}")))?;
657
658 eprintln!("**************************");
659 eprintln!("For debugging under {debugger}, use: {source_command} {filename}");
660 eprintln!("**************************");
661 Ok(())
662 }
663
664 #[cfg(not(target_arch = "wasm32"))]
666 pub(crate) fn publish_elf_eh_frame(
667 &mut self,
668 address: u64,
669 size: u64,
670 ) -> Result<(), CompileError> {
671 self.elf_mapped_binary
672 .last_mut()
673 .unwrap()
674 .publish_eh_frame_section(address, size)
675 .map_err(|e| {
676 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
677 })
678 }
679
680 #[cfg(not(target_arch = "wasm32"))]
682 pub fn signatures(&self) -> &SignatureRegistry {
683 &self.signatures
684 }
685
686 #[cfg(not(target_arch = "wasm32"))]
687 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
689 self.code_memory
690 .last_mut()
691 .unwrap()
692 .register_frame_info(frame_info);
693 }
694
695 #[cfg(not(target_arch = "wasm32"))]
696 pub(crate) fn register_elf_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
698 self.elf_mapped_binary
699 .last_mut()
700 .unwrap()
701 .register_frame_info(frame_info);
702 }
703
704 #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
705 pub(crate) fn register_perfmap(
706 &self,
707 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
708 module_info: &ModuleInfo,
709 ) -> Result<(), CompileError> {
710 if self
711 .compiler
712 .as_ref()
713 .is_some_and(|v| v.get_perfmap_enabled())
714 {
715 use std::fs::OpenOptions;
716
717 let filename = format!("/tmp/perf-{}.map", std::process::id());
718 let file = OpenOptions::new()
720 .append(true)
721 .create(true)
722 .open(&filename)
723 .map_err(|e| {
724 CompileError::Codegen(format!("failed to open perf map file {filename}: {e}"))
725 })?;
726 let mut file = std::io::BufWriter::new(file);
727
728 for (func_index, code) in finished_functions.iter() {
729 let func_index = module_info.func_index(func_index);
730 if let Some(func_name) = module_info.function_names.get(&func_index) {
731 let sanitized_name = func_name.replace(['\n', '\r'], "_");
732 let line = format!(
733 "{:p} {:x} {sanitized_name}\n",
734 code.ptr.0 as *const _, code.length
735 );
736 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
737 }
738 }
739
740 file.flush()
741 .map_err(|e| CompileError::Codegen(e.to_string()))?;
742 }
743
744 Ok(())
745 }
746}
747
748#[cfg(feature = "compiler")]
749impl From<Box<dyn CompilerConfig>> for Engine {
750 fn from(config: Box<dyn CompilerConfig>) -> Self {
751 EngineBuilder::new(config).engine()
752 }
753}
754
755impl From<EngineBuilder> for Engine {
756 fn from(engine_builder: EngineBuilder) -> Self {
757 engine_builder.engine()
758 }
759}
760
761impl From<&Self> for Engine {
762 fn from(engine_ref: &Self) -> Self {
763 engine_ref.cloned()
764 }
765}
766
767#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
768#[repr(transparent)]
769pub struct EngineId {
771 id: usize,
772}
773
774impl EngineId {
775 pub fn id(&self) -> String {
777 format!("{}", self.id)
778 }
779}
780
781impl Clone for EngineId {
782 fn clone(&self) -> Self {
783 Self::default()
784 }
785}
786
787impl Default for EngineId {
788 fn default() -> Self {
789 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
790 Self {
791 id: NEXT_ID.fetch_add(1, SeqCst),
792 }
793 }
794}