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