1 //! Runtime library support for Wasmtime. 2 3 #![deny(missing_docs)] 4 // See documentation in crates/wasmtime/src/runtime.rs for why this is 5 // selectively enabled here. 6 #![warn(clippy::cast_sign_loss)] 7 8 // Polyfill `std::simd::i8x16` etc. until they're stable. 9 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))] 10 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 11 pub(crate) type i8x16 = core::arch::x86_64::__m128i; 12 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))] 13 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 14 pub(crate) type f32x4 = core::arch::x86_64::__m128; 15 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))] 16 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 17 pub(crate) type f64x2 = core::arch::x86_64::__m128d; 18 19 // On platforms other than x86_64, define i8x16 to a non-constructible type; 20 // we need a type because we have a lot of macros for defining builtin 21 // functions that are awkward to make conditional on the target, but it 22 // doesn't need to actually be constructible unless we're on x86_64. 23 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))] 24 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 25 #[derive(Copy, Clone)] 26 pub(crate) struct i8x16(core::convert::Infallible); 27 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))] 28 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 29 #[derive(Copy, Clone)] 30 pub(crate) struct f32x4(core::convert::Infallible); 31 #[cfg(not(all(target_arch = "x86_64", target_feature = "sse")))] 32 #[expect(non_camel_case_types, reason = "matching wasm conventions")] 33 #[derive(Copy, Clone)] 34 pub(crate) struct f64x2(core::convert::Infallible); 35 36 use crate::StoreContextMut; 37 use crate::prelude::*; 38 use crate::store::{StoreInner, StoreOpaque, StoreResourceLimiter}; 39 use crate::type_registry::RegisteredType; 40 use alloc::sync::Arc; 41 use core::fmt; 42 use core::ops::{Deref, DerefMut}; 43 use core::pin::pin; 44 use core::ptr::NonNull; 45 use core::sync::atomic::{AtomicUsize, Ordering}; 46 use core::task::{Context, Poll, Waker}; 47 use wasmtime_environ::error::OutOfMemory; 48 use wasmtime_environ::{DefinedMemoryIndex, HostPtr, VMOffsets, VMSharedTypeIndex}; 49 50 #[cfg(feature = "gc")] 51 use wasmtime_environ::ModuleInternedTypeIndex; 52 53 mod always_mut; 54 #[cfg(feature = "component-model")] 55 pub mod component; 56 mod const_expr; 57 mod export; 58 mod gc; 59 mod imports; 60 mod instance; 61 mod memory; 62 mod mmap_vec; 63 #[cfg(has_virtual_memory)] 64 mod pagemap_disabled; 65 mod provenance; 66 mod send_sync_ptr; 67 mod stack_switching; 68 mod store_box; 69 mod sys; 70 mod table; 71 #[cfg(feature = "gc")] 72 mod throw; 73 mod traphandlers; 74 mod vmcontext; 75 76 #[cfg(feature = "threads")] 77 mod parking_spot; 78 79 // Note that `debug_builtins` here is disabled with a feature or a lack of a 80 // native compilation backend because it's only here to assist in debugging 81 // natively compiled code. 82 #[cfg(all(has_host_compiler_backend, feature = "debug-builtins"))] 83 pub mod debug_builtins; 84 pub mod libcalls; 85 pub mod mpk; 86 87 #[cfg(feature = "pulley")] 88 pub(crate) mod interpreter; 89 #[cfg(not(feature = "pulley"))] 90 pub(crate) mod interpreter_disabled; 91 #[cfg(not(feature = "pulley"))] 92 pub(crate) use interpreter_disabled as interpreter; 93 94 #[cfg(feature = "debug-builtins")] 95 pub use wasmtime_jit_debug::gdb_jit_int::GdbJitImageRegistration; 96 97 pub use crate::runtime::vm::always_mut::*; 98 pub use crate::runtime::vm::export::*; 99 pub use crate::runtime::vm::gc::*; 100 pub use crate::runtime::vm::imports::Imports; 101 pub use crate::runtime::vm::instance::{ 102 GcHeapAllocationIndex, Instance, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, 103 MemoryAllocationIndex, OnDemandInstanceAllocator, TableAllocationIndex, initialize_instance, 104 }; 105 #[cfg(feature = "pooling-allocator")] 106 pub use crate::runtime::vm::instance::{ 107 InstanceLimits, PoolConcurrencyLimitError, PoolingAllocatorMetrics, PoolingInstanceAllocator, 108 PoolingInstanceAllocatorConfig, 109 }; 110 pub use crate::runtime::vm::interpreter::*; 111 pub use crate::runtime::vm::memory::{ 112 Memory, MemoryBase, RuntimeLinearMemory, RuntimeMemoryCreator, SharedMemory, 113 }; 114 pub use crate::runtime::vm::mmap_vec::MmapVec; 115 pub use crate::runtime::vm::provenance::*; 116 pub use crate::runtime::vm::stack_switching::*; 117 pub use crate::runtime::vm::store_box::*; 118 #[cfg(feature = "std")] 119 pub use crate::runtime::vm::sys::mmap::open_file_for_mmap; 120 #[cfg(has_host_compiler_backend)] 121 pub use crate::runtime::vm::sys::unwind::UnwindRegistration; 122 pub use crate::runtime::vm::table::{Table, TableElementType}; 123 #[cfg(feature = "gc")] 124 pub use crate::runtime::vm::throw::*; 125 pub use crate::runtime::vm::traphandlers::*; 126 #[cfg(feature = "component-model")] 127 pub use crate::runtime::vm::vmcontext::VMArrayCallFunction; 128 pub use crate::runtime::vm::vmcontext::{ 129 VMArrayCallHostFuncContext, VMContext, VMFuncRef, VMFunctionImport, VMGlobalDefinition, 130 VMGlobalImport, VMGlobalKind, VMMemoryDefinition, VMMemoryImport, VMOpaqueContext, 131 VMStoreContext, VMTableImport, VMTagImport, VMWasmCallFunction, ValRaw, 132 }; 133 #[cfg(has_custom_sync)] 134 pub(crate) use sys::capi; 135 136 pub use send_sync_ptr::SendSyncPtr; 137 pub use wasmtime_unwinder::Unwind; 138 139 #[cfg(has_host_compiler_backend)] 140 pub use wasmtime_unwinder::{UnwindHost, get_stack_pointer}; 141 142 mod module_id; 143 pub use module_id::CompiledModuleId; 144 145 #[cfg(has_virtual_memory)] 146 mod byte_count; 147 #[cfg(has_virtual_memory)] 148 mod cow; 149 #[cfg(not(has_virtual_memory))] 150 mod cow_disabled; 151 #[cfg(has_virtual_memory)] 152 mod mmap; 153 154 #[cfg(any(feature = "async", feature = "gc"))] 155 mod async_yield; 156 #[cfg(any(feature = "async", feature = "gc"))] 157 pub use crate::runtime::vm::async_yield::*; 158 159 #[cfg(feature = "gc-null")] 160 mod send_sync_unsafe_cell; 161 #[cfg(feature = "gc-null")] 162 pub use send_sync_unsafe_cell::SendSyncUnsafeCell; 163 164 cfg_if::cfg_if! { 165 if #[cfg(has_virtual_memory)] { 166 pub use crate::runtime::vm::byte_count::*; 167 pub use crate::runtime::vm::mmap::{Mmap, MmapOffset}; 168 pub use self::cow::{MemoryImage, MemoryImageSlot, ModuleMemoryImages}; 169 } else { 170 pub use self::cow_disabled::{MemoryImage, MemoryImageSlot, ModuleMemoryImages}; 171 } 172 } 173 174 /// Source of data used for [`MemoryImage`] 175 pub trait ModuleMemoryImageSource: Send + Sync + 'static { 176 /// Returns this image's slice of all wasm data for a module which is then 177 /// further sub-sliced for a particular initialization segment. 178 fn wasm_data(&self) -> &[u8]; 179 180 /// Optionally returns the backing mmap. Used for using the backing mmap's 181 /// file to perform other mmaps, for example. 182 fn mmap(&self) -> Option<&MmapVec>; 183 } 184 185 /// Dynamic runtime functionality needed by this crate throughout the execution 186 /// of a wasm instance. 187 /// 188 /// This trait is used to store a raw pointer trait object within each 189 /// `VMContext`. This raw pointer trait object points back to the 190 /// `wasmtime::Store` internally but is type-erased to avoid needing to 191 /// monomorphize the entire runtime on the `T` in `Store<T>` 192 /// 193 /// # Safety 194 /// 195 /// This trait should be implemented by nothing other than `StoreInner<T>` in 196 /// this crate. It's not sound to implement it for anything else due to 197 /// `unchecked_context_mut` below. 198 /// 199 /// It's also worth nothing that there are various locations where a `*mut dyn 200 /// VMStore` is asserted to be both `Send` and `Sync` which disregards the `T` 201 /// that's actually stored in the store itself. It's assume that the high-level 202 /// APIs using `Store<T>` are correctly inferring send/sync on the returned 203 /// values (e.g. futures) and that internally in the runtime we aren't doing 204 /// anything "weird" with threads for example. 205 pub unsafe trait VMStore: 'static { 206 /// Get a shared borrow of this store's `StoreOpaque`. 207 fn store_opaque(&self) -> &StoreOpaque; 208 209 /// Get an exclusive borrow of this store's `StoreOpaque`. 210 fn store_opaque_mut(&mut self) -> &mut StoreOpaque; 211 212 /// Returns a split borrow to the limiter plus `StoreOpaque` at the same 213 /// time. 214 fn resource_limiter_and_store_opaque( 215 &mut self, 216 ) -> (Option<StoreResourceLimiter<'_>>, &mut StoreOpaque); 217 218 /// Callback invoked whenever an instance observes a new epoch 219 /// number. Cannot fail; cooperative epoch-based yielding is 220 /// completely semantically transparent. Returns the new deadline. 221 #[cfg(target_has_atomic = "64")] 222 fn new_epoch_updated_deadline(&mut self) -> Result<crate::UpdateDeadline>; 223 224 /// Metadata required for resources for the component model. 225 #[cfg(feature = "component-model")] 226 fn component_task_state_mut(&mut self) -> &mut crate::component::store::ComponentTaskState; 227 228 #[cfg(feature = "component-model-async")] 229 fn component_async_store( 230 &mut self, 231 ) -> &mut dyn crate::runtime::component::VMComponentAsyncStore; 232 233 /// Invoke a debug handler, if present, at a debug event. 234 #[cfg(feature = "debug")] 235 fn block_on_debug_handler(&mut self, event: crate::DebugEvent) -> crate::Result<()>; 236 } 237 238 impl Deref for dyn VMStore + '_ { 239 type Target = StoreOpaque; 240 241 fn deref(&self) -> &Self::Target { 242 self.store_opaque() 243 } 244 } 245 246 impl DerefMut for dyn VMStore + '_ { 247 fn deref_mut(&mut self) -> &mut Self::Target { 248 self.store_opaque_mut() 249 } 250 } 251 252 impl dyn VMStore + '_ { 253 /// Asserts that this `VMStore` was originally paired with `StoreInner<T>` 254 /// and then casts to the `StoreContextMut` type. 255 /// 256 /// # Unsafety 257 /// 258 /// This method is not safe as there's no static guarantee that `T` is 259 /// correct for this store. 260 pub(crate) unsafe fn unchecked_context_mut<T>(&mut self) -> StoreContextMut<'_, T> { 261 unsafe { StoreContextMut(&mut *(self as *mut dyn VMStore as *mut StoreInner<T>)) } 262 } 263 } 264 265 /// A newtype wrapper around `NonNull<dyn VMStore>` intended to be a 266 /// self-pointer back to the `Store<T>` within raw data structures like 267 /// `VMContext`. 268 /// 269 /// This type exists to manually, and unsafely, implement `Send` and `Sync`. 270 /// The `VMStore` trait doesn't require `Send` or `Sync` which means this isn't 271 /// naturally either trait (e.g. with `SendSyncPtr` instead). Note that this 272 /// means that `Instance` is, for example, mistakenly considered 273 /// unconditionally `Send` and `Sync`. This is hopefully ok for now though 274 /// because from a user perspective the only type that matters is `Store<T>`. 275 /// That type is `Send + Sync` if `T: Send + Sync` already so the internal 276 /// storage of `Instance` shouldn't matter as the final result is the same. 277 /// Note though that this means we need to be extra vigilant about cross-thread 278 /// usage of `Instance` and `ComponentInstance` for example. 279 #[derive(Copy, Clone)] 280 #[repr(transparent)] 281 struct VMStoreRawPtr(pub NonNull<dyn VMStore>); 282 283 // SAFETY: this is the purpose of `VMStoreRawPtr`, see docs above about safe 284 // usage. 285 unsafe impl Send for VMStoreRawPtr {} 286 unsafe impl Sync for VMStoreRawPtr {} 287 288 /// Functionality required by this crate for a particular module. This is 289 /// chiefly needed for lazy initialization of various bits of instance state. 290 #[derive(Clone)] 291 pub enum ModuleRuntimeInfo { 292 Module(crate::Module), 293 Bare(Arc<BareModuleInfo>), 294 } 295 296 /// A barebones implementation of ModuleRuntimeInfo that is useful for 297 /// cases where a purpose-built environ::Module is used and a full 298 /// CompiledModule does not exist (for example, for tests or for the 299 /// default-callee instance). 300 #[derive(Clone)] 301 pub struct BareModuleInfo { 302 module: Arc<wasmtime_environ::Module>, 303 offsets: VMOffsets<HostPtr>, 304 _registered_type: Option<RegisteredType>, 305 } 306 307 impl ModuleRuntimeInfo { 308 pub(crate) fn bare(module: Arc<wasmtime_environ::Module>) -> Result<Self, OutOfMemory> { 309 ModuleRuntimeInfo::bare_with_registered_type(module, None) 310 } 311 312 pub(crate) fn bare_with_registered_type( 313 module: Arc<wasmtime_environ::Module>, 314 registered_type: Option<RegisteredType>, 315 ) -> Result<Self, OutOfMemory> { 316 let info = try_new(BareModuleInfo { 317 offsets: VMOffsets::new(HostPtr, &module), 318 module, 319 _registered_type: registered_type, 320 })?; 321 Ok(ModuleRuntimeInfo::Bare(info)) 322 } 323 324 /// The underlying Module. 325 pub(crate) fn env_module(&self) -> &Arc<wasmtime_environ::Module> { 326 match self { 327 ModuleRuntimeInfo::Module(m) => m.env_module(), 328 ModuleRuntimeInfo::Bare(b) => &b.module, 329 } 330 } 331 332 /// Translate a module-level interned type index into an engine-level 333 /// interned type index. 334 #[cfg(feature = "gc")] 335 fn engine_type_index(&self, module_index: ModuleInternedTypeIndex) -> VMSharedTypeIndex { 336 match self { 337 ModuleRuntimeInfo::Module(m) => m 338 .engine_code() 339 .signatures() 340 .shared_type(module_index) 341 .expect("bad module-level interned type index"), 342 ModuleRuntimeInfo::Bare(_) => unreachable!(), 343 } 344 } 345 346 /// Returns the `MemoryImage` structure used for copy-on-write 347 /// initialization of the memory, if it's applicable. 348 fn memory_image(&self, memory: DefinedMemoryIndex) -> crate::Result<Option<&Arc<MemoryImage>>> { 349 match self { 350 ModuleRuntimeInfo::Module(m) => { 351 let images = m.memory_images()?; 352 Ok(images.and_then(|images| images.get_memory_image(memory))) 353 } 354 ModuleRuntimeInfo::Bare(_) => Ok(None), 355 } 356 } 357 358 /// A unique ID for this particular module. This can be used to 359 /// allow for fastpaths to optimize a "re-instantiate the same 360 /// module again" case. 361 #[cfg(feature = "pooling-allocator")] 362 fn unique_id(&self) -> Option<CompiledModuleId> { 363 match self { 364 ModuleRuntimeInfo::Module(m) => Some(m.id()), 365 ModuleRuntimeInfo::Bare(_) => None, 366 } 367 } 368 369 /// A slice pointing to all data that is referenced by this instance. 370 fn wasm_data(&self) -> &[u8] { 371 match self { 372 ModuleRuntimeInfo::Module(m) => m.engine_code().wasm_data(), 373 ModuleRuntimeInfo::Bare(_) => &[], 374 } 375 } 376 377 /// Returns an array, indexed by `ModuleInternedTypeIndex` of all 378 /// `VMSharedSignatureIndex` entries corresponding to the `SignatureIndex`. 379 fn type_ids(&self) -> &[VMSharedTypeIndex] { 380 match self { 381 ModuleRuntimeInfo::Module(m) => m 382 .engine_code() 383 .signatures() 384 .as_module_map() 385 .values() 386 .as_slice(), 387 ModuleRuntimeInfo::Bare(_) => &[], 388 } 389 } 390 391 /// Offset information for the current host. 392 pub(crate) fn offsets(&self) -> &VMOffsets<HostPtr> { 393 match self { 394 ModuleRuntimeInfo::Module(m) => m.offsets(), 395 ModuleRuntimeInfo::Bare(b) => &b.offsets, 396 } 397 } 398 } 399 400 /// Returns the host OS page size, in bytes. 401 #[cfg(has_virtual_memory)] 402 pub fn host_page_size() -> usize { 403 // NB: this function is duplicated in `crates/fiber/src/unix.rs` so if this 404 // changes that should probably get updated as well. 405 static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); 406 407 return match PAGE_SIZE.load(Ordering::Relaxed) { 408 0 => { 409 let size = sys::vm::get_page_size(); 410 assert!(size != 0); 411 PAGE_SIZE.store(size, Ordering::Relaxed); 412 size 413 } 414 n => n, 415 }; 416 } 417 418 /// Result of `Memory::atomic_wait32` and `Memory::atomic_wait64` 419 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 420 pub enum WaitResult { 421 /// Indicates that a `wait` completed by being awoken by a different thread. 422 /// This means the thread went to sleep and didn't time out. 423 Ok = 0, 424 /// Indicates that `wait` did not complete and instead returned due to the 425 /// value in memory not matching the expected value. 426 Mismatch = 1, 427 /// Indicates that `wait` completed with a timeout, meaning that the 428 /// original value matched as expected but nothing ever called `notify`. 429 TimedOut = 2, 430 } 431 432 /// Description about a fault that occurred in WebAssembly. 433 #[derive(Debug)] 434 pub struct WasmFault { 435 /// The size of memory, in bytes, at the time of the fault. 436 pub memory_size: usize, 437 /// The WebAssembly address at which the fault occurred. 438 pub wasm_address: u64, 439 } 440 441 impl fmt::Display for WasmFault { 442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 443 write!( 444 f, 445 "memory fault at wasm address 0x{:x} in linear memory of size 0x{:x}", 446 self.wasm_address, self.memory_size, 447 ) 448 } 449 } 450 451 /// Asserts that the future `f` is ready and returns its output. 452 /// 453 /// This function is intended to be used with `Store::validate_sync_call`. 454 /// Internals of Wasmtime are generally `async` when they optionally can be, 455 /// meaning that synchronous entrypoints will invoke this function after 456 /// invoking the asynchronous internals. The `validate_sync_call` method 457 /// ensures that during this `async` function call there won't actually be any 458 /// yield points. If a yield point could possibly happen, then 459 /// `validate_sync_call` will fail. 460 /// 461 /// If `validate_sync_call` passes, then this function is an extra assert that 462 /// yes, indeed, we coded everything correctly in Wasmtime and there shouldn't 463 /// be any yield points in the future provided, so its result should be ready 464 /// immediately. 465 /// 466 /// # Panics 467 /// 468 /// Panics if `f` is not yet ready. 469 pub fn assert_ready<F: Future>(f: F) -> F::Output { 470 one_poll(f).unwrap() 471 } 472 473 /// Attempts one poll of `f` to see if its output is available. 474 /// 475 /// This function is intended for a few minor entrypoints into the Wasmtime API 476 /// where a synchronous function is documented to work even when `async_support` 477 /// is enabled. For example growing a `Memory` can be done with a synchronous 478 /// function, but it's documented to panic with an async resource limiter. 479 /// 480 /// This function provides the opportunity to poll `f` once to see if its output 481 /// is available. If it isn't then `None` is returned and an appropriate panic 482 /// message should be generated recommending to use an async function (e.g. 483 /// `grow_async` instead of `grow`). 484 fn one_poll<F: Future>(f: F) -> Option<F::Output> { 485 let mut context = Context::from_waker(&Waker::noop()); 486 match pin!(f).poll(&mut context) { 487 Poll::Ready(output) => Some(output), 488 Poll::Pending => None, 489 } 490 } 491