wasmtime/engine.rs
1use crate::Config;
2use crate::prelude::*;
3#[cfg(feature = "runtime")]
4pub use crate::runtime::code_memory::CustomCodeMemory;
5#[cfg(feature = "runtime")]
6use crate::runtime::type_registry::TypeRegistry;
7#[cfg(feature = "runtime")]
8use crate::runtime::vm::GcRuntime;
9use alloc::sync::Arc;
10use core::ptr::NonNull;
11#[cfg(target_has_atomic = "64")]
12use core::sync::atomic::{AtomicU64, Ordering};
13#[cfg(any(feature = "cranelift", feature = "winch"))]
14use object::write::{Object, StandardSegment};
15#[cfg(feature = "std")]
16use std::{fs::File, path::Path};
17use wasmparser::WasmFeatures;
18use wasmtime_environ::{FlagValue, ObjectKind, TripleExt, Tunables};
19
20mod serialization;
21
22/// An `Engine` which is a global context for compilation and management of wasm
23/// modules.
24///
25/// An engine can be safely shared across threads and is a cheap cloneable
26/// handle to the actual engine. The engine itself will be deallocated once all
27/// references to it have gone away.
28///
29/// Engines store global configuration preferences such as compilation settings,
30/// enabled features, etc. You'll likely only need at most one of these for a
31/// program.
32///
33/// ## Engines and `Clone`
34///
35/// Using `clone` on an `Engine` is a cheap operation. It will not create an
36/// entirely new engine, but rather just a new reference to the existing engine.
37/// In other words it's a shallow copy, not a deep copy.
38///
39/// ## Engines and `Default`
40///
41/// You can create an engine with default configuration settings using
42/// `Engine::default()`. Be sure to consult the documentation of [`Config`] for
43/// default settings.
44#[derive(Clone)]
45pub struct Engine {
46 inner: Arc<EngineInner>,
47}
48
49struct EngineInner {
50 config: Config,
51 features: WasmFeatures,
52 tunables: Tunables,
53 #[cfg(any(feature = "cranelift", feature = "winch"))]
54 compiler: Box<dyn wasmtime_environ::Compiler>,
55 #[cfg(feature = "runtime")]
56 allocator: Box<dyn crate::runtime::vm::InstanceAllocator + Send + Sync>,
57 #[cfg(feature = "runtime")]
58 gc_runtime: Option<Arc<dyn GcRuntime>>,
59 #[cfg(feature = "runtime")]
60 profiler: Box<dyn crate::profiling_agent::ProfilingAgent>,
61 #[cfg(feature = "runtime")]
62 signatures: TypeRegistry,
63 #[cfg(all(feature = "runtime", target_has_atomic = "64"))]
64 epoch: AtomicU64,
65
66 /// One-time check of whether the compiler's settings, if present, are
67 /// compatible with the native host.
68 #[cfg(any(feature = "cranelift", feature = "winch"))]
69 compatible_with_native_host: crate::sync::OnceLock<Result<(), String>>,
70}
71
72impl core::fmt::Debug for Engine {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 f.debug_tuple("Engine")
75 .field(&Arc::as_ptr(&self.inner))
76 .finish()
77 }
78}
79
80impl Default for Engine {
81 fn default() -> Engine {
82 Engine::new(&Config::default()).unwrap()
83 }
84}
85
86impl Engine {
87 /// Creates a new [`Engine`] with the specified compilation and
88 /// configuration settings.
89 ///
90 /// # Errors
91 ///
92 /// This method can fail if the `config` is invalid or some
93 /// configurations are incompatible.
94 ///
95 /// For example, feature `reference_types` will need to set
96 /// the compiler setting `enable_safepoints` and `unwind_info`
97 /// to `true`, but explicitly disable these two compiler settings
98 /// will cause errors.
99 pub fn new(config: &Config) -> Result<Engine> {
100 let config = config.clone();
101 let (tunables, features) = config.validate()?;
102
103 #[cfg(feature = "runtime")]
104 if tunables.signals_based_traps {
105 // Ensure that crate::runtime::vm's signal handlers are
106 // configured. This is the per-program initialization required for
107 // handling traps, such as configuring signals, vectored exception
108 // handlers, etc.
109 #[cfg(has_native_signals)]
110 crate::runtime::vm::init_traps(config.macos_use_mach_ports);
111 if !cfg!(miri) {
112 #[cfg(all(has_host_compiler_backend, feature = "debug-builtins"))]
113 crate::runtime::vm::debug_builtins::init();
114 }
115 }
116
117 #[cfg(any(feature = "cranelift", feature = "winch"))]
118 let (config, compiler) = config.build_compiler(&tunables, features)?;
119
120 Ok(Engine {
121 inner: Arc::new(EngineInner {
122 #[cfg(any(feature = "cranelift", feature = "winch"))]
123 compiler,
124 #[cfg(feature = "runtime")]
125 allocator: {
126 let allocator = config.build_allocator(&tunables)?;
127 #[cfg(feature = "gc")]
128 {
129 let mem_ty = tunables.gc_heap_memory_type();
130 allocator.validate_memory(&mem_ty).context(
131 "instance allocator cannot support configured GC heap memory",
132 )?;
133 }
134 allocator
135 },
136 #[cfg(feature = "runtime")]
137 gc_runtime: config.build_gc_runtime()?,
138 #[cfg(feature = "runtime")]
139 profiler: config.build_profiler()?,
140 #[cfg(feature = "runtime")]
141 signatures: TypeRegistry::new(),
142 #[cfg(all(feature = "runtime", target_has_atomic = "64"))]
143 epoch: AtomicU64::new(0),
144 #[cfg(any(feature = "cranelift", feature = "winch"))]
145 compatible_with_native_host: Default::default(),
146 config,
147 tunables,
148 features,
149 }),
150 })
151 }
152
153 /// Returns the configuration settings that this engine is using.
154 #[inline]
155 pub fn config(&self) -> &Config {
156 &self.inner.config
157 }
158
159 #[inline]
160 pub(crate) fn features(&self) -> WasmFeatures {
161 self.inner.features
162 }
163
164 pub(crate) fn run_maybe_parallel<
165 A: Send,
166 B: Send,
167 E: Send,
168 F: Fn(A) -> Result<B, E> + Send + Sync,
169 >(
170 &self,
171 input: Vec<A>,
172 f: F,
173 ) -> Result<Vec<B>, E> {
174 if self.config().parallel_compilation {
175 #[cfg(feature = "parallel-compilation")]
176 {
177 use rayon::prelude::*;
178 // If we collect into Result<Vec<B>, E> directly, the returned error is not
179 // deterministic, because any error could be returned early. So we first materialize
180 // all results in order and then return the first error deterministically, or Ok(_).
181 return input
182 .into_par_iter()
183 .map(|a| f(a))
184 .collect::<Vec<Result<B, E>>>()
185 .into_iter()
186 .collect::<Result<Vec<B>, E>>();
187 }
188 }
189
190 // In case the parallel-compilation feature is disabled or the parallel_compilation config
191 // was turned off dynamically fallback to the non-parallel version.
192 input
193 .into_iter()
194 .map(|a| f(a))
195 .collect::<Result<Vec<B>, E>>()
196 }
197
198 #[cfg(any(feature = "cranelift", feature = "winch"))]
199 pub(crate) fn run_maybe_parallel_mut<
200 T: Send,
201 E: Send,
202 F: Fn(&mut T) -> Result<(), E> + Send + Sync,
203 >(
204 &self,
205 input: &mut [T],
206 f: F,
207 ) -> Result<(), E> {
208 if self.config().parallel_compilation {
209 #[cfg(feature = "parallel-compilation")]
210 {
211 use rayon::prelude::*;
212 // If we collect into `Result<(), E>` directly, the returned
213 // error is not deterministic, because any error could be
214 // returned early. So we first materialize all results in order
215 // and then return the first error deterministically, or
216 // `Ok(_)`.
217 return input
218 .into_par_iter()
219 .map(|a| f(a))
220 .collect::<Vec<Result<(), E>>>()
221 .into_iter()
222 .collect::<Result<(), E>>();
223 }
224 }
225
226 // In case the parallel-compilation feature is disabled or the
227 // parallel_compilation config was turned off dynamically fallback to
228 // the non-parallel version.
229 input.into_iter().map(|a| f(a)).collect::<Result<(), E>>()
230 }
231
232 /// Take a weak reference to this engine.
233 pub fn weak(&self) -> EngineWeak {
234 EngineWeak {
235 inner: Arc::downgrade(&self.inner),
236 }
237 }
238
239 #[inline]
240 pub(crate) fn tunables(&self) -> &Tunables {
241 &self.inner.tunables
242 }
243
244 /// Returns whether the engine `a` and `b` refer to the same configuration.
245 #[inline]
246 pub fn same(a: &Engine, b: &Engine) -> bool {
247 Arc::ptr_eq(&a.inner, &b.inner)
248 }
249
250 /// Returns whether the engine is configured to support async functions.
251 #[cfg(feature = "async")]
252 #[inline]
253 pub fn is_async(&self) -> bool {
254 self.config().async_support
255 }
256
257 /// Detects whether the bytes provided are a precompiled object produced by
258 /// Wasmtime.
259 ///
260 /// This function will inspect the header of `bytes` to determine if it
261 /// looks like a precompiled core wasm module or a precompiled component.
262 /// This does not validate the full structure or guarantee that
263 /// deserialization will succeed, instead it helps higher-levels of the
264 /// stack make a decision about what to do next when presented with the
265 /// `bytes` as an input module.
266 ///
267 /// If the `bytes` looks like a precompiled object previously produced by
268 /// [`Module::serialize`](crate::Module::serialize),
269 /// [`Component::serialize`](crate::component::Component::serialize),
270 /// [`Engine::precompile_module`], or [`Engine::precompile_component`], then
271 /// this will return `Some(...)` indicating so. Otherwise `None` is
272 /// returned.
273 pub fn detect_precompiled(bytes: &[u8]) -> Option<Precompiled> {
274 serialization::detect_precompiled_bytes(bytes)
275 }
276
277 /// Like [`Engine::detect_precompiled`], but performs the detection on a file.
278 #[cfg(feature = "std")]
279 pub fn detect_precompiled_file(path: impl AsRef<Path>) -> Result<Option<Precompiled>> {
280 serialization::detect_precompiled_file(path)
281 }
282
283 /// Returns the target triple which this engine is compiling code for
284 /// and/or running code for.
285 pub(crate) fn target(&self) -> target_lexicon::Triple {
286 return self.config().compiler_target();
287 }
288
289 /// Verify that this engine's configuration is compatible with loading
290 /// modules onto the native host platform.
291 ///
292 /// This method is used as part of `Module::new` to ensure that this
293 /// engine can indeed load modules for the configured compiler (if any).
294 /// Note that if cranelift is disabled this trivially returns `Ok` because
295 /// loaded serialized modules are checked separately.
296 #[cfg(any(feature = "cranelift", feature = "winch"))]
297 pub(crate) fn check_compatible_with_native_host(&self) -> Result<()> {
298 self.inner
299 .compatible_with_native_host
300 .get_or_init(|| self._check_compatible_with_native_host())
301 .clone()
302 .map_err(anyhow::Error::msg)
303 }
304
305 #[cfg(any(feature = "cranelift", feature = "winch"))]
306 fn _check_compatible_with_native_host(&self) -> Result<(), String> {
307 use target_lexicon::Triple;
308
309 let compiler = self.compiler();
310
311 let target = compiler.triple();
312 let host = Triple::host();
313 let target_matches_host = || {
314 // If the host target and target triple match, then it's valid
315 // to run results of compilation on this host.
316 if host == *target {
317 return true;
318 }
319
320 // If there's a mismatch and the target is a compatible pulley
321 // target, then that's also ok to run.
322 if cfg!(feature = "pulley")
323 && target.is_pulley()
324 && target.pointer_width() == host.pointer_width()
325 && target.endianness() == host.endianness()
326 {
327 return true;
328 }
329
330 // ... otherwise everything else is considered not a match.
331 false
332 };
333
334 if !target_matches_host() {
335 return Err(format!(
336 "target '{target}' specified in the configuration does not match the host"
337 ));
338 }
339
340 // Also double-check all compiler settings
341 for (key, value) in compiler.flags().iter() {
342 self.check_compatible_with_shared_flag(key, value)?;
343 }
344 for (key, value) in compiler.isa_flags().iter() {
345 self.check_compatible_with_isa_flag(key, value)?;
346 }
347
348 // Double-check that this configuration isn't requesting capabilities
349 // that this build of Wasmtime doesn't support.
350 if !cfg!(has_native_signals) && self.tunables().signals_based_traps {
351 return Err("signals-based-traps disabled at compile time -- cannot be enabled".into());
352 }
353 if !cfg!(has_virtual_memory) && self.tunables().memory_init_cow {
354 return Err("virtual memory disabled at compile time -- cannot enable CoW".into());
355 }
356 if !cfg!(target_has_atomic = "64") && self.tunables().epoch_interruption {
357 return Err("epochs currently require 64-bit atomics".into());
358 }
359 Ok(())
360 }
361
362 /// Checks to see whether the "shared flag", something enabled for
363 /// individual compilers, is compatible with the native host platform.
364 ///
365 /// This is used both when validating an engine's compilation settings are
366 /// compatible with the host as well as when deserializing modules from
367 /// disk to ensure they're compatible with the current host.
368 ///
369 /// Note that most of the settings here are not configured by users that
370 /// often. While theoretically possible via `Config` methods the more
371 /// interesting flags are the ISA ones below. Typically the values here
372 /// represent global configuration for wasm features. Settings here
373 /// currently rely on the compiler informing us of all settings, including
374 /// those disabled. Settings then fall in a few buckets:
375 ///
376 /// * Some settings must be enabled, such as `preserve_frame_pointers`.
377 /// * Some settings must have a particular value, such as
378 /// `libcall_call_conv`.
379 /// * Some settings do not matter as to their value, such as `opt_level`.
380 pub(crate) fn check_compatible_with_shared_flag(
381 &self,
382 flag: &str,
383 value: &FlagValue,
384 ) -> Result<(), String> {
385 let target = self.target();
386 let ok = match flag {
387 // These settings must all have be enabled, since their value
388 // can affect the way the generated code performs or behaves at
389 // runtime.
390 "libcall_call_conv" => *value == FlagValue::Enum("isa_default"),
391 "preserve_frame_pointers" => *value == FlagValue::Bool(true),
392 "enable_probestack" => *value == FlagValue::Bool(true),
393 "probestack_strategy" => *value == FlagValue::Enum("inline"),
394 "enable_multi_ret_implicit_sret" => *value == FlagValue::Bool(true),
395
396 // Features wasmtime doesn't use should all be disabled, since
397 // otherwise if they are enabled it could change the behavior of
398 // generated code.
399 "enable_llvm_abi_extensions" => *value == FlagValue::Bool(false),
400 "enable_pinned_reg" => *value == FlagValue::Bool(false),
401 "use_colocated_libcalls" => *value == FlagValue::Bool(false),
402 "use_pinned_reg_as_heap_base" => *value == FlagValue::Bool(false),
403
404 // If reference types (or anything that depends on reference types,
405 // like typed function references and GC) are enabled this must be
406 // enabled, otherwise this setting can have any value.
407 "enable_safepoints" => {
408 if self.features().contains(WasmFeatures::REFERENCE_TYPES) {
409 *value == FlagValue::Bool(true)
410 } else {
411 return Ok(())
412 }
413 }
414
415 // Windows requires unwind info as part of its ABI.
416 "unwind_info" => {
417 if target.operating_system == target_lexicon::OperatingSystem::Windows {
418 *value == FlagValue::Bool(true)
419 } else {
420 return Ok(())
421 }
422 }
423
424 // stack switch model must match the current OS
425 "stack_switch_model" => {
426 if self.features().contains(WasmFeatures::STACK_SWITCHING) {
427 use target_lexicon::OperatingSystem;
428 let expected =
429 match target.operating_system {
430 OperatingSystem::Windows => "update_windows_tib",
431 OperatingSystem::Linux
432 | OperatingSystem::MacOSX(_)
433 | OperatingSystem::Darwin(_) => "basic",
434 _ => { return Err(String::from("stack-switching feature not supported on this platform")); }
435 };
436 *value == FlagValue::Enum(expected)
437 } else {
438 return Ok(())
439 }
440 }
441
442 // These settings don't affect the interface or functionality of
443 // the module itself, so their configuration values shouldn't
444 // matter.
445 "enable_heap_access_spectre_mitigation"
446 | "enable_table_access_spectre_mitigation"
447 | "enable_nan_canonicalization"
448 | "enable_jump_tables"
449 | "enable_float"
450 | "enable_verifier"
451 | "enable_pcc"
452 | "regalloc_checker"
453 | "regalloc_verbose_logs"
454 | "regalloc_algorithm"
455 | "is_pic"
456 | "bb_padding_log2_minus_one"
457 | "log2_min_function_alignment"
458 | "machine_code_cfg_info"
459 | "tls_model" // wasmtime doesn't use tls right now
460 | "opt_level" // opt level doesn't change semantics
461 | "enable_alias_analysis" // alias analysis-based opts don't change semantics
462 | "probestack_size_log2" // probestack above asserted disabled
463 | "regalloc" // shouldn't change semantics
464 | "enable_incremental_compilation_cache_checks" // shouldn't change semantics
465 | "enable_atomics" => return Ok(()),
466
467 // Everything else is unknown and needs to be added somewhere to
468 // this list if encountered.
469 _ => {
470 return Err(format!("unknown shared setting {flag:?} configured to {value:?}"))
471 }
472 };
473
474 if !ok {
475 return Err(format!(
476 "setting {flag:?} is configured to {value:?} which is not supported",
477 ));
478 }
479 Ok(())
480 }
481
482 /// Same as `check_compatible_with_native_host` except used for ISA-specific
483 /// flags. This is used to test whether a configured ISA flag is indeed
484 /// available on the host platform itself.
485 pub(crate) fn check_compatible_with_isa_flag(
486 &self,
487 flag: &str,
488 value: &FlagValue,
489 ) -> Result<(), String> {
490 match value {
491 // ISA flags are used for things like CPU features, so if they're
492 // disabled then it's compatible with the native host.
493 FlagValue::Bool(false) => return Ok(()),
494
495 // Fall through below where we test at runtime that features are
496 // available.
497 FlagValue::Bool(true) => {}
498
499 // Pulley's pointer_width must match the host.
500 FlagValue::Enum("pointer32") => {
501 return if cfg!(target_pointer_width = "32") {
502 Ok(())
503 } else {
504 Err("wrong host pointer width".to_string())
505 };
506 }
507 FlagValue::Enum("pointer64") => {
508 return if cfg!(target_pointer_width = "64") {
509 Ok(())
510 } else {
511 Err("wrong host pointer width".to_string())
512 };
513 }
514
515 // Only `bool` values are supported right now, other settings would
516 // need more support here.
517 _ => {
518 return Err(format!(
519 "isa-specific feature {flag:?} configured to unknown value {value:?}"
520 ));
521 }
522 }
523
524 let host_feature = match flag {
525 // aarch64 features to detect
526 "has_lse" => "lse",
527 "has_pauth" => "paca",
528 "has_fp16" => "fp16",
529
530 // aarch64 features which don't need detection
531 // No effect on its own.
532 "sign_return_address_all" => return Ok(()),
533 // The pointer authentication instructions act as a `NOP` when
534 // unsupported, so it is safe to enable them.
535 "sign_return_address" => return Ok(()),
536 // No effect on its own.
537 "sign_return_address_with_bkey" => return Ok(()),
538 // The `BTI` instruction acts as a `NOP` when unsupported, so it
539 // is safe to enable it regardless of whether the host supports it
540 // or not.
541 "use_bti" => return Ok(()),
542
543 // s390x features to detect
544 "has_vxrs_ext2" => "vxrs_ext2",
545 "has_vxrs_ext3" => "vxrs_ext3",
546 "has_mie3" => "mie3",
547 "has_mie4" => "mie4",
548
549 // x64 features to detect
550 "has_cmpxchg16b" => "cmpxchg16b",
551 "has_sse3" => "sse3",
552 "has_ssse3" => "ssse3",
553 "has_sse41" => "sse4.1",
554 "has_sse42" => "sse4.2",
555 "has_popcnt" => "popcnt",
556 "has_avx" => "avx",
557 "has_avx2" => "avx2",
558 "has_fma" => "fma",
559 "has_bmi1" => "bmi1",
560 "has_bmi2" => "bmi2",
561 "has_avx512bitalg" => "avx512bitalg",
562 "has_avx512dq" => "avx512dq",
563 "has_avx512f" => "avx512f",
564 "has_avx512vl" => "avx512vl",
565 "has_avx512vbmi" => "avx512vbmi",
566 "has_lzcnt" => "lzcnt",
567
568 // pulley features
569 "big_endian" if cfg!(target_endian = "big") => return Ok(()),
570 "big_endian" if cfg!(target_endian = "little") => {
571 return Err("wrong host endianness".to_string());
572 }
573
574 _ => {
575 // FIXME: should enumerate risc-v features and plumb them
576 // through to the `detect_host_feature` function.
577 if cfg!(target_arch = "riscv64") && flag != "not_a_flag" {
578 return Ok(());
579 }
580 return Err(format!(
581 "don't know how to test for target-specific flag {flag:?} at runtime"
582 ));
583 }
584 };
585
586 let detect = match self.config().detect_host_feature {
587 Some(detect) => detect,
588 None => {
589 return Err(format!(
590 "cannot determine if host feature {host_feature:?} is \
591 available at runtime, configure a probing function with \
592 `Config::detect_host_feature`"
593 ));
594 }
595 };
596
597 match detect(host_feature) {
598 Some(true) => Ok(()),
599 Some(false) => Err(format!(
600 "compilation setting {flag:?} is enabled, but not \
601 available on the host",
602 )),
603 None => Err(format!(
604 "failed to detect if target-specific flag {flag:?} is \
605 available at runtime"
606 )),
607 }
608 }
609
610 /// Returns whether this [`Engine`] is configured to execute with Pulley,
611 /// Wasmtime's interpreter.
612 ///
613 /// Note that Pulley is the default for host platforms that do not have a
614 /// Cranelift backend to support them. For example at the time of this
615 /// writing 32-bit x86 is not supported in Cranelift so the
616 /// `i686-unknown-linux-gnu` target would by default return `true` here.
617 pub fn is_pulley(&self) -> bool {
618 self.target().is_pulley()
619 }
620}
621
622#[cfg(any(feature = "cranelift", feature = "winch"))]
623impl Engine {
624 pub(crate) fn compiler(&self) -> &dyn wasmtime_environ::Compiler {
625 &*self.inner.compiler
626 }
627
628 /// Ahead-of-time (AOT) compiles a WebAssembly module.
629 ///
630 /// The `bytes` provided must be in one of two formats:
631 ///
632 /// * A [binary-encoded][binary] WebAssembly module. This is always supported.
633 /// * A [text-encoded][text] instance of the WebAssembly text format.
634 /// This is only supported when the `wat` feature of this crate is enabled.
635 /// If this is supplied then the text format will be parsed before validation.
636 /// Note that the `wat` feature is enabled by default.
637 ///
638 /// This method may be used to compile a module for use with a different target
639 /// host. The output of this method may be used with
640 /// [`Module::deserialize`](crate::Module::deserialize) on hosts compatible
641 /// with the [`Config`](crate::Config) associated with this [`Engine`].
642 ///
643 /// The output of this method is safe to send to another host machine for later
644 /// execution. As the output is already a compiled module, translation and code
645 /// generation will be skipped and this will improve the performance of constructing
646 /// a [`Module`](crate::Module) from the output of this method.
647 ///
648 /// [binary]: https://webassembly.github.io/spec/core/binary/index.html
649 /// [text]: https://webassembly.github.io/spec/core/text/index.html
650 pub fn precompile_module(&self, bytes: &[u8]) -> Result<Vec<u8>> {
651 crate::CodeBuilder::new(self)
652 .wasm_binary_or_text(bytes, None)?
653 .compile_module_serialized()
654 }
655
656 /// Same as [`Engine::precompile_module`] except for a
657 /// [`Component`](crate::component::Component)
658 #[cfg(feature = "component-model")]
659 pub fn precompile_component(&self, bytes: &[u8]) -> Result<Vec<u8>> {
660 crate::CodeBuilder::new(self)
661 .wasm_binary_or_text(bytes, None)?
662 .compile_component_serialized()
663 }
664
665 /// Produces a blob of bytes by serializing the `engine`'s configuration data to
666 /// be checked, perhaps in a different process, with the `check_compatible`
667 /// method below.
668 ///
669 /// The blob of bytes is inserted into the object file specified to become part
670 /// of the final compiled artifact.
671 pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) {
672 serialization::append_compiler_info(self, obj, &serialization::Metadata::new(&self))
673 }
674
675 #[cfg(any(feature = "cranelift", feature = "winch"))]
676 pub(crate) fn append_bti(&self, obj: &mut Object<'_>) {
677 let section = obj.add_section(
678 obj.segment_name(StandardSegment::Data).to_vec(),
679 wasmtime_environ::obj::ELF_WASM_BTI.as_bytes().to_vec(),
680 object::SectionKind::ReadOnlyData,
681 );
682 let contents = if self.compiler().is_branch_protection_enabled() {
683 1
684 } else {
685 0
686 };
687 obj.append_section_data(section, &[contents], 1);
688 }
689}
690
691/// Return value from the [`Engine::detect_precompiled`] API.
692#[derive(PartialEq, Eq, Copy, Clone, Debug)]
693pub enum Precompiled {
694 /// The input bytes look like a precompiled core wasm module.
695 Module,
696 /// The input bytes look like a precompiled wasm component.
697 Component,
698}
699
700#[cfg(feature = "runtime")]
701impl Engine {
702 /// Eagerly initialize thread-local functionality shared by all [`Engine`]s.
703 ///
704 /// Wasmtime's implementation on some platforms may involve per-thread
705 /// setup that needs to happen whenever WebAssembly is invoked. This setup
706 /// can take on the order of a few hundred microseconds, whereas the
707 /// overhead of calling WebAssembly is otherwise on the order of a few
708 /// nanoseconds. This setup cost is paid once per-OS-thread. If your
709 /// application is sensitive to the latencies of WebAssembly function
710 /// calls, even those that happen first on a thread, then this function
711 /// can be used to improve the consistency of each call into WebAssembly
712 /// by explicitly frontloading the cost of the one-time setup per-thread.
713 ///
714 /// Note that this function is not required to be called in any embedding.
715 /// Wasmtime will automatically initialize thread-local-state as necessary
716 /// on calls into WebAssembly. This is provided for use cases where the
717 /// latency of WebAssembly calls are extra-important, which is not
718 /// necessarily true of all embeddings.
719 pub fn tls_eager_initialize() {
720 crate::runtime::vm::tls_eager_initialize();
721 }
722
723 pub(crate) fn allocator(&self) -> &dyn crate::runtime::vm::InstanceAllocator {
724 self.inner.allocator.as_ref()
725 }
726
727 pub(crate) fn gc_runtime(&self) -> Option<&Arc<dyn GcRuntime>> {
728 self.inner.gc_runtime.as_ref()
729 }
730
731 pub(crate) fn profiler(&self) -> &dyn crate::profiling_agent::ProfilingAgent {
732 self.inner.profiler.as_ref()
733 }
734
735 #[cfg(all(feature = "cache", any(feature = "cranelift", feature = "winch")))]
736 pub(crate) fn cache(&self) -> Option<&wasmtime_cache::Cache> {
737 self.config().cache.as_ref()
738 }
739
740 pub(crate) fn signatures(&self) -> &TypeRegistry {
741 &self.inner.signatures
742 }
743
744 #[cfg(feature = "runtime")]
745 pub(crate) fn custom_code_memory(&self) -> Option<&Arc<dyn CustomCodeMemory>> {
746 self.config().custom_code_memory.as_ref()
747 }
748
749 #[cfg(target_has_atomic = "64")]
750 pub(crate) fn epoch_counter(&self) -> &AtomicU64 {
751 &self.inner.epoch
752 }
753
754 #[cfg(target_has_atomic = "64")]
755 pub(crate) fn current_epoch(&self) -> u64 {
756 self.epoch_counter().load(Ordering::Relaxed)
757 }
758
759 /// Increments the epoch.
760 ///
761 /// When using epoch-based interruption, currently-executing Wasm
762 /// code within this engine will trap or yield "soon" when the
763 /// epoch deadline is reached or exceeded. (The configuration, and
764 /// the deadline, are set on the `Store`.) The intent of the
765 /// design is for this method to be called by the embedder at some
766 /// regular cadence, for example by a thread that wakes up at some
767 /// interval, or by a signal handler.
768 ///
769 /// See [`Config::epoch_interruption`](crate::Config::epoch_interruption)
770 /// for an introduction to epoch-based interruption and pointers
771 /// to the other relevant methods.
772 ///
773 /// When performing `increment_epoch` in a separate thread, consider using
774 /// [`Engine::weak`] to hold an [`EngineWeak`](crate::EngineWeak) and
775 /// performing [`EngineWeak::upgrade`](crate::EngineWeak::upgrade) on each
776 /// tick, so that the epoch ticking thread does not keep an [`Engine`] alive
777 /// longer than any of its consumers.
778 ///
779 /// ## Signal Safety
780 ///
781 /// This method is signal-safe: it does not make any syscalls, and
782 /// performs only an atomic increment to the epoch value in
783 /// memory.
784 #[cfg(target_has_atomic = "64")]
785 pub fn increment_epoch(&self) {
786 self.inner.epoch.fetch_add(1, Ordering::Relaxed);
787 }
788
789 /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility.
790 ///
791 /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`]
792 /// are compatible with a different [`Engine`] instance only if the two engines use
793 /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries
794 /// from one are guaranteed to deserialize in the other.
795 #[cfg(any(feature = "cranelift", feature = "winch"))]
796 pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ {
797 crate::compile::HashedEngineCompileEnv(self)
798 }
799
800 /// Returns the required alignment for a code image, if we
801 /// allocate in a way that is not a system `mmap()` that naturally
802 /// aligns it.
803 fn required_code_alignment(&self) -> usize {
804 self.custom_code_memory()
805 .map(|c| c.required_alignment())
806 .unwrap_or(1)
807 }
808
809 /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a
810 /// uniquely owned mmap.
811 ///
812 /// The `expected` marker here is whether the bytes are expected to be a
813 /// precompiled module or a component.
814 pub(crate) fn load_code_bytes(
815 &self,
816 bytes: &[u8],
817 expected: ObjectKind,
818 ) -> Result<Arc<crate::CodeMemory>> {
819 self.load_code(
820 crate::runtime::vm::MmapVec::from_slice_with_alignment(
821 bytes,
822 self.required_code_alignment(),
823 )?,
824 expected,
825 )
826 }
827
828 /// Loads a `CodeMemory` from the specified memory region without copying
829 ///
830 /// The `expected` marker here is whether the bytes are expected to be
831 /// a precompiled module or a component. The `memory` provided is expected
832 /// to be a serialized module (.cwasm) generated by `[Module::serialize]`
833 /// or [`Engine::precompile_module] or their `Component` counterparts
834 /// [`Component::serialize`] or `[Engine::precompile_component]`.
835 ///
836 /// The memory provided is guaranteed to only be immutably by the runtime.
837 ///
838 /// # Safety
839 ///
840 /// As there is no copy here, the runtime will be making direct readonly use
841 /// of the provided memory. As such, outside writes to this memory region
842 /// will result in undefined and likely very undesirable behavior.
843 pub(crate) unsafe fn load_code_raw(
844 &self,
845 memory: NonNull<[u8]>,
846 expected: ObjectKind,
847 ) -> Result<Arc<crate::CodeMemory>> {
848 self.load_code(crate::runtime::vm::MmapVec::from_raw(memory)?, expected)
849 }
850
851 /// Like `load_code_bytes`, but creates a mmap from a file on disk.
852 #[cfg(feature = "std")]
853 pub(crate) fn load_code_file(
854 &self,
855 file: File,
856 expected: ObjectKind,
857 ) -> Result<Arc<crate::CodeMemory>> {
858 self.load_code(
859 crate::runtime::vm::MmapVec::from_file(file)
860 .with_context(|| "Failed to create file mapping".to_string())?,
861 expected,
862 )
863 }
864
865 pub(crate) fn load_code(
866 &self,
867 mmap: crate::runtime::vm::MmapVec,
868 expected: ObjectKind,
869 ) -> Result<Arc<crate::CodeMemory>> {
870 serialization::check_compatible(self, &mmap, expected)?;
871 let mut code = crate::CodeMemory::new(self, mmap)?;
872 code.publish()?;
873 Ok(Arc::new(code))
874 }
875
876 /// Unload process-related trap/signal handlers and destroy this engine.
877 ///
878 /// This method is not safe and is not widely applicable. It is not required
879 /// to be called and is intended for use cases such as unloading a dynamic
880 /// library from a process. It is difficult to invoke this method correctly
881 /// and it requires careful coordination to do so.
882 ///
883 /// # Panics
884 ///
885 /// This method will panic if this `Engine` handle is not the last remaining
886 /// engine handle.
887 ///
888 /// # Aborts
889 ///
890 /// This method will abort the process on some platforms in some situations
891 /// where unloading the handler cannot be performed and an unrecoverable
892 /// state is reached. For example on Unix platforms with signal handling
893 /// the process will be aborted if the current signal handlers are not
894 /// Wasmtime's.
895 ///
896 /// # Unsafety
897 ///
898 /// This method is not generally safe to call and has a number of
899 /// preconditions that must be met to even possibly be safe. Even with these
900 /// known preconditions met there may be other unknown invariants to uphold
901 /// as well.
902 ///
903 /// * There must be no other instances of `Engine` elsewhere in the process.
904 /// Note that this isn't just copies of this `Engine` but it's any other
905 /// `Engine` at all. This unloads global state that is used by all
906 /// `Engine`s so this instance must be the last.
907 ///
908 /// * On Unix platforms no other signal handlers could have been installed
909 /// for signals that Wasmtime catches. In this situation Wasmtime won't
910 /// know how to restore signal handlers that Wasmtime possibly overwrote
911 /// when Wasmtime was initially loaded. If possible initialize other
912 /// libraries first and then initialize Wasmtime last (e.g. defer creating
913 /// an `Engine`).
914 ///
915 /// * All existing threads which have used this DLL or copy of Wasmtime may
916 /// no longer use this copy of Wasmtime. Per-thread state is not iterated
917 /// and destroyed. Only future threads may use future instances of this
918 /// Wasmtime itself.
919 ///
920 /// If other crashes are seen from using this method please feel free to
921 /// file an issue to update the documentation here with more preconditions
922 /// that must be met.
923 #[cfg(has_native_signals)]
924 pub unsafe fn unload_process_handlers(self) {
925 assert_eq!(Arc::weak_count(&self.inner), 0);
926 assert_eq!(Arc::strong_count(&self.inner), 1);
927
928 #[cfg(not(miri))]
929 crate::runtime::vm::deinit_traps();
930 }
931}
932
933/// A weak reference to an [`Engine`].
934#[derive(Clone)]
935pub struct EngineWeak {
936 inner: alloc::sync::Weak<EngineInner>,
937}
938
939impl EngineWeak {
940 /// Upgrade this weak reference into an [`Engine`]. Returns `None` if
941 /// strong references (the [`Engine`] type itself) no longer exist.
942 pub fn upgrade(&self) -> Option<Engine> {
943 alloc::sync::Weak::upgrade(&self.inner).map(|inner| Engine { inner })
944 }
945}