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::new();
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::new();
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 #[deprecated(note = "User compilation options are currently unused")]
355 pub fn with_opts(
356 &mut self,
357 _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
358 ) -> Result<(), CompileError> {
359 Ok(())
360 }
361}
362
363impl std::fmt::Debug for Engine {
364 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
365 write!(f, "{}", self.deterministic_id())
366 }
367}
368
369pub struct EngineInner {
371 #[cfg(feature = "compiler")]
372 compiler: Option<Box<dyn Compiler>>,
374 #[cfg(feature = "compiler")]
375 features: Features,
377 #[cfg(not(target_arch = "wasm32"))]
380 code_memory: Vec<CodeMemory>,
381 #[cfg(not(target_arch = "wasm32"))]
383 elf_mapped_binary: Vec<MemoryMappedBinary>,
384 #[cfg(not(target_arch = "wasm32"))]
387 signatures: SignatureRegistry,
388}
389
390impl std::fmt::Debug for EngineInner {
391 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
392 let mut formatter = f.debug_struct("EngineInner");
393 #[cfg(feature = "compiler")]
394 {
395 formatter.field("compiler", &self.compiler);
396 formatter.field("features", &self.features);
397 }
398
399 #[cfg(not(target_arch = "wasm32"))]
400 {
401 formatter.field("signatures", &self.signatures);
402 }
403
404 formatter.finish()
405 }
406}
407
408impl EngineInner {
409 #[cfg(feature = "compiler")]
411 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
412 match self.compiler.as_ref() {
413 None => Err(CompileError::Codegen(
414 "No compiler compiled into executable".to_string(),
415 )),
416 Some(compiler) => Ok(&**compiler),
417 }
418 }
419
420 #[cfg(feature = "compiler")]
422 pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
423 let compiler = self.compiler()?;
424 compiler.validate_module(&self.features, data)
425 }
426
427 #[cfg(feature = "compiler")]
429 pub fn features(&self) -> &Features {
430 &self.features
431 }
432
433 #[cfg(not(target_arch = "wasm32"))]
435 #[allow(clippy::type_complexity)]
436 pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
437 &'a mut self,
438 _module: &wasmer_types::ModuleInfo,
439 functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
440 function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
441 dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
442 custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
443 ) -> Result<
444 (
445 PrimaryMap<LocalFunctionIndex, FunctionExtent>,
446 PrimaryMap<SignatureIndex, VMTrampoline>,
447 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
448 PrimaryMap<SectionIndex, SectionBodyPtr>,
449 ),
450 CompileError,
451 >
452 where
453 FunctionBody: FunctionBodyLike<'a> + 'a,
454 CustomSection: CustomSectionLike<'a> + 'a,
455 {
456 let functions_len = functions.len();
457 let function_call_trampolines_len = function_call_trampolines.len();
458
459 let function_bodies = functions
460 .chain(function_call_trampolines)
461 .chain(dynamic_function_trampolines)
462 .collect::<Vec<_>>();
463 let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
464 .clone()
465 .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
466 self.code_memory.push(CodeMemory::new());
467
468 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
469 self.code_memory
470 .last_mut()
471 .unwrap()
472 .allocate(
473 function_bodies.as_slice(),
474 executable_sections.as_slice(),
475 data_sections.as_slice(),
476 )
477 .map_err(|message| {
478 CompileError::Resource(format!(
479 "failed to allocate memory for functions: {message}",
480 ))
481 })?;
482
483 let allocated_functions_result = allocated_functions
484 .drain(0..functions_len)
485 .map(|slice| FunctionExtent {
486 ptr: FunctionBodyPtr(slice.as_ptr()),
487 length: slice.len(),
488 })
489 .collect::<PrimaryMap<LocalFunctionIndex, _>>();
490
491 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
492 PrimaryMap::new();
493 for ptr in allocated_functions
494 .drain(0..function_call_trampolines_len)
495 .map(|slice| slice.as_ptr())
496 {
497 let trampoline =
498 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
499 allocated_function_call_trampolines.push(trampoline);
500 }
501
502 let allocated_dynamic_function_trampolines = allocated_functions
503 .drain(..)
504 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
505 .collect::<PrimaryMap<FunctionIndex, _>>();
506
507 let mut exec_iter = allocated_executable_sections.iter();
508 let mut data_iter = allocated_data_sections.iter();
509 let allocated_custom_sections = custom_sections
510 .map(|section| {
511 SectionBodyPtr(
512 if section.protection() == CustomSectionProtection::ReadExecute {
513 exec_iter.next()
514 } else {
515 data_iter.next()
516 }
517 .unwrap()
518 .as_ptr(),
519 )
520 })
521 .collect::<PrimaryMap<SectionIndex, _>>();
522 Ok((
523 allocated_functions_result,
524 allocated_function_call_trampolines,
525 allocated_dynamic_function_trampolines,
526 allocated_custom_sections,
527 ))
528 }
529
530 #[cfg(not(target_arch = "wasm32"))]
531 pub(crate) fn publish_compiled_code(&mut self) {
533 self.code_memory.last_mut().unwrap().publish();
534 }
535
536 #[cfg(not(target_arch = "wasm32"))]
537 #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
538 pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
540 self.code_memory
541 .last_mut()
542 .unwrap()
543 .unwind_registry_mut()
544 .publish_eh_frame(eh_frame)
545 .map_err(|e| {
546 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
547 })?;
548 Ok(())
549 }
550 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
551 pub(crate) fn publish_compact_unwind(
553 &mut self,
554 compact_unwind: &[u8],
555 eh_personality_addr_in_got: Option<usize>,
556 ) -> Result<(), CompileError> {
557 self.code_memory
558 .last_mut()
559 .unwrap()
560 .unwind_registry_mut()
561 .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
562 .map_err(|e| {
563 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
564 })?;
565 Ok(())
566 }
567
568 #[cfg(not(target_arch = "wasm32"))]
572 pub(crate) fn map_elf_binary<'a, R: object::ReadRef<'a>>(
573 &mut self,
574 object_file: &object::File<'a, R>,
575 data: &[u8],
576 ) -> Result<*mut c_void, CompileError> {
577 let map = MemoryMappedBinary::try_from_bytes(object_file, data)
578 .map_err(CompileError::Resource)?;
579 let base = map.base();
580 self.elf_mapped_binary.push(map);
581 Ok(base)
582 }
583
584 #[cfg(all(not(target_arch = "wasm32"), unix))]
586 pub(crate) fn map_elf_binary_file<'a, R: object::ReadRef<'a>>(
587 &mut self,
588 object_file: &object::File<'a, R>,
589 file: RawFd,
590 ) -> Result<*mut c_void, CompileError> {
591 let map =
592 MemoryMappedBinary::try_from_file(object_file, file).map_err(CompileError::Resource)?;
593 let base = map.base();
594 self.elf_mapped_binary.push(map);
595 Ok(base)
596 }
597
598 #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
599 pub(crate) fn debugger(&self) -> Option<Debugger> {
600 self.compiler
601 .as_ref()
602 .and_then(|compiler| compiler.get_debugger())
603 }
604
605 #[cfg(all(not(target_arch = "wasm32"), unix, feature = "compiler"))]
606 pub(crate) fn register_debugger(
607 &self,
608 path: &Path,
609 base: *mut c_void,
610 debugger: Debugger,
611 ) -> Result<(), CompileError> {
612 use std::io::Write as _;
613
614 let path = path
615 .canonicalize()
616 .unwrap_or_else(|_| path.to_path_buf())
617 .to_string_lossy()
618 .to_string();
619 let (command, source_command) = match debugger {
620 Debugger::Gdb => (
621 format!("add-symbol-file \"{path}\" -o 0x{:x}", base as usize),
622 "source",
623 ),
624 Debugger::Lldb => (
625 format!(
626 "target modules add \"{path}\"\ntarget modules load --file \"{path}\" --slide 0x{:x}",
627 base as usize
628 ),
629 "command source",
630 ),
631 };
632 let filename = format!(
633 "/tmp/wasmer-{}.{}",
634 debugger.to_string().to_lowercase(),
635 std::process::id()
636 );
637 let mut file = std::fs::OpenOptions::new()
638 .append(true)
639 .create(true)
640 .open(&filename)
641 .map_err(|error| CompileError::Resource(format!("Cannot open {filename}: {error}")))?;
642 writeln!(file, "{command}")
643 .map_err(|error| CompileError::Resource(format!("Cannot write {filename}: {error}")))?;
644
645 eprintln!("**************************");
646 eprintln!("For debugging under {debugger}, use: {source_command} {filename}");
647 eprintln!("**************************");
648 Ok(())
649 }
650
651 #[cfg(not(target_arch = "wasm32"))]
653 pub(crate) fn publish_elf_eh_frame(
654 &mut self,
655 address: u64,
656 size: u64,
657 ) -> Result<(), CompileError> {
658 self.elf_mapped_binary
659 .last_mut()
660 .unwrap()
661 .publish_eh_frame_section(address, size)
662 .map_err(|e| {
663 CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
664 })
665 }
666
667 #[cfg(not(target_arch = "wasm32"))]
669 pub fn signatures(&self) -> &SignatureRegistry {
670 &self.signatures
671 }
672
673 #[cfg(not(target_arch = "wasm32"))]
674 pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
676 self.code_memory
677 .last_mut()
678 .unwrap()
679 .register_frame_info(frame_info);
680 }
681
682 #[cfg(not(target_arch = "wasm32"))]
683 pub(crate) fn register_elf_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
685 self.elf_mapped_binary
686 .last_mut()
687 .unwrap()
688 .register_frame_info(frame_info);
689 }
690
691 #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
692 pub(crate) fn register_perfmap(
693 &self,
694 finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
695 module_info: &ModuleInfo,
696 ) -> Result<(), CompileError> {
697 if self
698 .compiler
699 .as_ref()
700 .is_some_and(|v| v.get_perfmap_enabled())
701 {
702 use std::fs::OpenOptions;
703
704 let filename = format!("/tmp/perf-{}.map", std::process::id());
705 let file = OpenOptions::new()
707 .append(true)
708 .create(true)
709 .open(&filename)
710 .map_err(|e| {
711 CompileError::Codegen(format!("failed to open perf map file {filename}: {e}"))
712 })?;
713 let mut file = std::io::BufWriter::new(file);
714
715 for (func_index, code) in finished_functions.iter() {
716 let func_index = module_info.func_index(func_index);
717 if let Some(func_name) = module_info.function_names.get(&func_index) {
718 let sanitized_name = func_name.replace(['\n', '\r'], "_");
719 let line = format!(
720 "{:p} {:x} {sanitized_name}\n",
721 code.ptr.0 as *const _, code.length
722 );
723 write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
724 }
725 }
726
727 file.flush()
728 .map_err(|e| CompileError::Codegen(e.to_string()))?;
729 }
730
731 Ok(())
732 }
733}
734
735#[cfg(feature = "compiler")]
736impl From<Box<dyn CompilerConfig>> for Engine {
737 fn from(config: Box<dyn CompilerConfig>) -> Self {
738 EngineBuilder::new(config).engine()
739 }
740}
741
742impl From<EngineBuilder> for Engine {
743 fn from(engine_builder: EngineBuilder) -> Self {
744 engine_builder.engine()
745 }
746}
747
748impl From<&Self> for Engine {
749 fn from(engine_ref: &Self) -> Self {
750 engine_ref.cloned()
751 }
752}
753
754#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
755#[repr(transparent)]
756pub struct EngineId {
758 id: usize,
759}
760
761impl EngineId {
762 pub fn id(&self) -> String {
764 format!("{}", self.id)
765 }
766}
767
768impl Clone for EngineId {
769 fn clone(&self) -> Self {
770 Self::default()
771 }
772}
773
774impl Default for EngineId {
775 fn default() -> Self {
776 static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
777 Self {
778 id: NEXT_ID.fetch_add(1, SeqCst),
779 }
780 }
781}