1 //! This file declares `VMContext` and several related structs which contain 2 //! fields that compiled wasm code accesses directly. 3 4 mod vm_host_func_context; 5 6 pub use self::vm_host_func_context::VMArrayCallHostFuncContext; 7 use crate::prelude::*; 8 use crate::runtime::vm::{GcStore, InterpreterRef, VMGcRef, VmPtr, VmSafe, f32x4, f64x2, i8x16}; 9 use crate::store::StoreOpaque; 10 use crate::vm::stack_switching::VMStackChain; 11 use core::cell::UnsafeCell; 12 use core::ffi::c_void; 13 use core::fmt; 14 use core::marker; 15 use core::mem::{self, MaybeUninit}; 16 use core::ops::Range; 17 use core::ptr::{self, NonNull}; 18 use core::sync::atomic::{AtomicUsize, Ordering}; 19 use wasmtime_environ::{ 20 BuiltinFunctionIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, 21 DefinedTagIndex, Unsigned, VMCONTEXT_MAGIC, VMSharedTypeIndex, WasmHeapTopType, WasmValType, 22 }; 23 24 /// A function pointer that exposes the array calling convention. 25 /// 26 /// Regardless of the underlying Wasm function type, all functions using the 27 /// array calling convention have the same Rust signature. 28 /// 29 /// Arguments: 30 /// 31 /// * Callee `vmctx` for the function itself. 32 /// 33 /// * Caller's `vmctx` (so that host functions can access the linear memory of 34 /// their Wasm callers). 35 /// 36 /// * A pointer to a buffer of `ValRaw`s where both arguments are passed into 37 /// this function, and where results are returned from this function. 38 /// 39 /// * The capacity of the `ValRaw` buffer. Must always be at least 40 /// `max(len(wasm_params), len(wasm_results))`. 41 /// 42 /// Return value: 43 /// 44 /// * `true` if this call succeeded. 45 /// * `false` if this call failed and a trap was recorded in TLS. 46 pub type VMArrayCallNative = unsafe extern "C" fn( 47 NonNull<VMOpaqueContext>, 48 NonNull<VMContext>, 49 NonNull<ValRaw>, 50 usize, 51 ) -> bool; 52 53 /// An opaque function pointer which might be `VMArrayCallNative` or it might be 54 /// pulley bytecode. Requires external knowledge to determine what kind of 55 /// function pointer this is. 56 #[repr(transparent)] 57 pub struct VMArrayCallFunction(VMFunctionBody); 58 59 /// A function pointer that exposes the Wasm calling convention. 60 /// 61 /// In practice, different Wasm function types end up mapping to different Rust 62 /// function types, so this isn't simply a type alias the way that 63 /// `VMArrayCallFunction` is. However, the exact details of the calling 64 /// convention are left to the Wasm compiler (e.g. Cranelift or Winch). Runtime 65 /// code never does anything with these function pointers except shuffle them 66 /// around and pass them back to Wasm. 67 #[repr(transparent)] 68 pub struct VMWasmCallFunction(VMFunctionBody); 69 70 /// An imported function. 71 #[derive(Debug, Copy, Clone)] 72 #[repr(C)] 73 pub struct VMFunctionImport { 74 /// Function pointer to use when calling this imported function from Wasm. 75 pub wasm_call: VmPtr<VMWasmCallFunction>, 76 77 /// Function pointer to use when calling this imported function with the 78 /// "array" calling convention that `Func::new` et al use. 79 pub array_call: VmPtr<VMArrayCallFunction>, 80 81 /// The VM state associated with this function. 82 /// 83 /// For Wasm functions defined by core wasm instances this will be `*mut 84 /// VMContext`, but for lifted/lowered component model functions this will 85 /// be a `VMComponentContext`, and for a host function it will be a 86 /// `VMHostFuncContext`, etc. 87 pub vmctx: VmPtr<VMOpaqueContext>, 88 } 89 90 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 91 unsafe impl VmSafe for VMFunctionImport {} 92 93 #[cfg(test)] 94 mod test_vmfunction_import { 95 use super::VMFunctionImport; 96 use core::mem::offset_of; 97 use std::mem::size_of; 98 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 99 100 #[test] 101 fn check_vmfunction_import_offsets() { 102 let module = Module::new(); 103 let offsets = VMOffsets::new(HostPtr, &module); 104 assert_eq!( 105 size_of::<VMFunctionImport>(), 106 usize::from(offsets.size_of_vmfunction_import()) 107 ); 108 assert_eq!( 109 offset_of!(VMFunctionImport, wasm_call), 110 usize::from(offsets.vmfunction_import_wasm_call()) 111 ); 112 assert_eq!( 113 offset_of!(VMFunctionImport, array_call), 114 usize::from(offsets.vmfunction_import_array_call()) 115 ); 116 assert_eq!( 117 offset_of!(VMFunctionImport, vmctx), 118 usize::from(offsets.vmfunction_import_vmctx()) 119 ); 120 } 121 } 122 123 /// A placeholder byte-sized type which is just used to provide some amount of type 124 /// safety when dealing with pointers to JIT-compiled function bodies. Note that it's 125 /// deliberately not Copy, as we shouldn't be carelessly copying function body bytes 126 /// around. 127 #[repr(C)] 128 pub struct VMFunctionBody(u8); 129 130 // SAFETY: this structure is never read and is safe to pass to jit code. 131 unsafe impl VmSafe for VMFunctionBody {} 132 133 #[cfg(test)] 134 mod test_vmfunction_body { 135 use super::VMFunctionBody; 136 use std::mem::size_of; 137 138 #[test] 139 fn check_vmfunction_body_offsets() { 140 assert_eq!(size_of::<VMFunctionBody>(), 1); 141 } 142 } 143 144 /// The fields compiled code needs to access to utilize a WebAssembly table 145 /// imported from another instance. 146 #[derive(Debug, Copy, Clone)] 147 #[repr(C)] 148 pub struct VMTableImport { 149 /// A pointer to the imported table description. 150 pub from: VmPtr<VMTableDefinition>, 151 152 /// A pointer to the `VMContext` that owns the table description. 153 pub vmctx: VmPtr<VMContext>, 154 155 /// The table index, within `vmctx`, this definition resides at. 156 pub index: DefinedTableIndex, 157 } 158 159 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 160 unsafe impl VmSafe for VMTableImport {} 161 162 #[cfg(test)] 163 mod test_vmtable { 164 use super::VMTableImport; 165 use core::mem::offset_of; 166 use std::mem::size_of; 167 use wasmtime_environ::component::{Component, VMComponentOffsets}; 168 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 169 170 #[test] 171 fn check_vmtable_offsets() { 172 let module = Module::new(); 173 let offsets = VMOffsets::new(HostPtr, &module); 174 assert_eq!( 175 size_of::<VMTableImport>(), 176 usize::from(offsets.size_of_vmtable_import()) 177 ); 178 assert_eq!( 179 offset_of!(VMTableImport, from), 180 usize::from(offsets.vmtable_import_from()) 181 ); 182 } 183 184 #[test] 185 fn ensure_sizes_match() { 186 // Because we use `VMTableImport` for recording tables used by components, we 187 // want to make sure that the size calculations between `VMOffsets` and 188 // `VMComponentOffsets` stay the same. 189 let module = Module::new(); 190 let vm_offsets = VMOffsets::new(HostPtr, &module); 191 let component = Component::default(); 192 let vm_component_offsets = VMComponentOffsets::new(HostPtr, &component); 193 assert_eq!( 194 vm_offsets.size_of_vmtable_import(), 195 vm_component_offsets.size_of_vmtable_import() 196 ); 197 } 198 } 199 200 /// The fields compiled code needs to access to utilize a WebAssembly linear 201 /// memory imported from another instance. 202 #[derive(Debug, Copy, Clone)] 203 #[repr(C)] 204 pub struct VMMemoryImport { 205 /// A pointer to the imported memory description. 206 pub from: VmPtr<VMMemoryDefinition>, 207 208 /// A pointer to the `VMContext` that owns the memory description. 209 pub vmctx: VmPtr<VMContext>, 210 211 /// The index of the memory in the containing `vmctx`. 212 pub index: DefinedMemoryIndex, 213 } 214 215 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 216 unsafe impl VmSafe for VMMemoryImport {} 217 218 #[cfg(test)] 219 mod test_vmmemory_import { 220 use super::VMMemoryImport; 221 use core::mem::offset_of; 222 use std::mem::size_of; 223 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 224 225 #[test] 226 fn check_vmmemory_import_offsets() { 227 let module = Module::new(); 228 let offsets = VMOffsets::new(HostPtr, &module); 229 assert_eq!( 230 size_of::<VMMemoryImport>(), 231 usize::from(offsets.size_of_vmmemory_import()) 232 ); 233 assert_eq!( 234 offset_of!(VMMemoryImport, from), 235 usize::from(offsets.vmmemory_import_from()) 236 ); 237 } 238 } 239 240 /// The fields compiled code needs to access to utilize a WebAssembly global 241 /// variable imported from another instance. 242 /// 243 /// Note that unlike with functions, tables, and memories, `VMGlobalImport` 244 /// doesn't include a `vmctx` pointer. Globals are never resized, and don't 245 /// require a `vmctx` pointer to access. 246 #[derive(Debug, Copy, Clone)] 247 #[repr(C)] 248 pub struct VMGlobalImport { 249 /// A pointer to the imported global variable description. 250 pub from: VmPtr<VMGlobalDefinition>, 251 252 /// A pointer to the context that owns the global. 253 /// 254 /// Exactly what's stored here is dictated by `kind` below. This is `None` 255 /// for `VMGlobalKind::Host`, it's a `VMContext` for 256 /// `VMGlobalKind::Instance`, and it's `VMComponentContext` for 257 /// `VMGlobalKind::ComponentFlags`. 258 pub vmctx: Option<VmPtr<VMOpaqueContext>>, 259 260 /// The kind of global, and extra location information in addition to 261 /// `vmctx` above. 262 pub kind: VMGlobalKind, 263 } 264 265 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 266 unsafe impl VmSafe for VMGlobalImport {} 267 268 /// The kinds of globals that Wasmtime has. 269 #[derive(Debug, Copy, Clone)] 270 #[repr(C, u32)] 271 pub enum VMGlobalKind { 272 /// Host globals, stored in a `StoreOpaque`. 273 Host(DefinedGlobalIndex), 274 /// Instance globals, stored in `VMContext`s 275 Instance(DefinedGlobalIndex), 276 /// Flags for a component instance, stored in `VMComponentContext`. 277 #[cfg(feature = "component-model")] 278 ComponentFlags(wasmtime_environ::component::RuntimeComponentInstanceIndex), 279 } 280 281 // SAFETY: the above enum is repr(C) and stores nothing else 282 unsafe impl VmSafe for VMGlobalKind {} 283 284 #[cfg(test)] 285 mod test_vmglobal_import { 286 use super::VMGlobalImport; 287 use core::mem::offset_of; 288 use std::mem::size_of; 289 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 290 291 #[test] 292 fn check_vmglobal_import_offsets() { 293 let module = Module::new(); 294 let offsets = VMOffsets::new(HostPtr, &module); 295 assert_eq!( 296 size_of::<VMGlobalImport>(), 297 usize::from(offsets.size_of_vmglobal_import()) 298 ); 299 assert_eq!( 300 offset_of!(VMGlobalImport, from), 301 usize::from(offsets.vmglobal_import_from()) 302 ); 303 } 304 } 305 306 /// The fields compiled code needs to access to utilize a WebAssembly 307 /// tag imported from another instance. 308 #[derive(Debug, Copy, Clone)] 309 #[repr(C)] 310 pub struct VMTagImport { 311 /// A pointer to the imported tag description. 312 pub from: VmPtr<VMTagDefinition>, 313 314 /// The instance that owns this tag. 315 pub vmctx: VmPtr<VMContext>, 316 317 /// The index of the tag in the containing `vmctx`. 318 pub index: DefinedTagIndex, 319 } 320 321 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 322 unsafe impl VmSafe for VMTagImport {} 323 324 #[cfg(test)] 325 mod test_vmtag_import { 326 use super::VMTagImport; 327 use core::mem::{offset_of, size_of}; 328 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 329 330 #[test] 331 fn check_vmtag_import_offsets() { 332 let module = Module::new(); 333 let offsets = VMOffsets::new(HostPtr, &module); 334 assert_eq!( 335 size_of::<VMTagImport>(), 336 usize::from(offsets.size_of_vmtag_import()) 337 ); 338 assert_eq!( 339 offset_of!(VMTagImport, from), 340 usize::from(offsets.vmtag_import_from()) 341 ); 342 } 343 } 344 345 /// The fields compiled code needs to access to utilize a WebAssembly linear 346 /// memory defined within the instance, namely the start address and the 347 /// size in bytes. 348 #[derive(Debug)] 349 #[repr(C)] 350 pub struct VMMemoryDefinition { 351 /// The start address. 352 pub base: VmPtr<u8>, 353 354 /// The current logical size of this linear memory in bytes. 355 /// 356 /// This is atomic because shared memories must be able to grow their length 357 /// atomically. For relaxed access, see 358 /// [`VMMemoryDefinition::current_length()`]. 359 pub current_length: AtomicUsize, 360 } 361 362 // SAFETY: the above definition has `repr(C)` and each field individually 363 // implements `VmSafe`, which satisfies the requirements of this trait. 364 unsafe impl VmSafe for VMMemoryDefinition {} 365 366 impl VMMemoryDefinition { 367 /// Return the current length (in bytes) of the [`VMMemoryDefinition`] by 368 /// performing a relaxed load; do not use this function for situations in 369 /// which a precise length is needed. Owned memories (i.e., non-shared) will 370 /// always return a precise result (since no concurrent modification is 371 /// possible) but shared memories may see an imprecise value--a 372 /// `current_length` potentially smaller than what some other thread 373 /// observes. Since Wasm memory only grows, this under-estimation may be 374 /// acceptable in certain cases. 375 pub fn current_length(&self) -> usize { 376 self.current_length.load(Ordering::Relaxed) 377 } 378 379 /// Return a copy of the [`VMMemoryDefinition`] using the relaxed value of 380 /// `current_length`; see [`VMMemoryDefinition::current_length()`]. 381 pub unsafe fn load(ptr: *mut Self) -> Self { 382 let other = &*ptr; 383 VMMemoryDefinition { 384 base: other.base, 385 current_length: other.current_length().into(), 386 } 387 } 388 } 389 390 #[cfg(test)] 391 mod test_vmmemory_definition { 392 use super::VMMemoryDefinition; 393 use core::mem::offset_of; 394 use std::mem::size_of; 395 use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; 396 397 #[test] 398 fn check_vmmemory_definition_offsets() { 399 let module = Module::new(); 400 let offsets = VMOffsets::new(HostPtr, &module); 401 assert_eq!( 402 size_of::<VMMemoryDefinition>(), 403 usize::from(offsets.ptr.size_of_vmmemory_definition()) 404 ); 405 assert_eq!( 406 offset_of!(VMMemoryDefinition, base), 407 usize::from(offsets.ptr.vmmemory_definition_base()) 408 ); 409 assert_eq!( 410 offset_of!(VMMemoryDefinition, current_length), 411 usize::from(offsets.ptr.vmmemory_definition_current_length()) 412 ); 413 /* TODO: Assert that the size of `current_length` matches. 414 assert_eq!( 415 size_of::<VMMemoryDefinition::current_length>(), 416 usize::from(offsets.size_of_vmmemory_definition_current_length()) 417 ); 418 */ 419 } 420 } 421 422 /// The fields compiled code needs to access to utilize a WebAssembly table 423 /// defined within the instance. 424 #[derive(Debug, Copy, Clone)] 425 #[repr(C)] 426 pub struct VMTableDefinition { 427 /// Pointer to the table data. 428 pub base: VmPtr<u8>, 429 430 /// The current number of elements in the table. 431 pub current_elements: usize, 432 } 433 434 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 435 unsafe impl VmSafe for VMTableDefinition {} 436 437 #[cfg(test)] 438 mod test_vmtable_definition { 439 use super::VMTableDefinition; 440 use core::mem::offset_of; 441 use std::mem::size_of; 442 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 443 444 #[test] 445 fn check_vmtable_definition_offsets() { 446 let module = Module::new(); 447 let offsets = VMOffsets::new(HostPtr, &module); 448 assert_eq!( 449 size_of::<VMTableDefinition>(), 450 usize::from(offsets.size_of_vmtable_definition()) 451 ); 452 assert_eq!( 453 offset_of!(VMTableDefinition, base), 454 usize::from(offsets.vmtable_definition_base()) 455 ); 456 assert_eq!( 457 offset_of!(VMTableDefinition, current_elements), 458 usize::from(offsets.vmtable_definition_current_elements()) 459 ); 460 } 461 } 462 463 /// The storage for a WebAssembly global defined within the instance. 464 /// 465 /// TODO: Pack the globals more densely, rather than using the same size 466 /// for every type. 467 #[derive(Debug)] 468 #[repr(C, align(16))] 469 pub struct VMGlobalDefinition { 470 storage: [u8; 16], 471 // If more elements are added here, remember to add offset_of tests below! 472 } 473 474 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 475 unsafe impl VmSafe for VMGlobalDefinition {} 476 477 #[cfg(test)] 478 mod test_vmglobal_definition { 479 use super::VMGlobalDefinition; 480 use std::mem::{align_of, size_of}; 481 use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; 482 483 #[test] 484 fn check_vmglobal_definition_alignment() { 485 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>()); 486 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>()); 487 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>()); 488 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>()); 489 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[u8; 16]>()); 490 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f32; 4]>()); 491 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f64; 2]>()); 492 } 493 494 #[test] 495 fn check_vmglobal_definition_offsets() { 496 let module = Module::new(); 497 let offsets = VMOffsets::new(HostPtr, &module); 498 assert_eq!( 499 size_of::<VMGlobalDefinition>(), 500 usize::from(offsets.ptr.size_of_vmglobal_definition()) 501 ); 502 } 503 504 #[test] 505 fn check_vmglobal_begins_aligned() { 506 let module = Module::new(); 507 let offsets = VMOffsets::new(HostPtr, &module); 508 assert_eq!(offsets.vmctx_globals_begin() % 16, 0); 509 } 510 511 #[test] 512 #[cfg(feature = "gc")] 513 fn check_vmglobal_can_contain_gc_ref() { 514 assert!(size_of::<crate::runtime::vm::VMGcRef>() <= size_of::<VMGlobalDefinition>()); 515 } 516 } 517 518 impl VMGlobalDefinition { 519 /// Construct a `VMGlobalDefinition`. 520 pub fn new() -> Self { 521 Self { storage: [0; 16] } 522 } 523 524 /// Create a `VMGlobalDefinition` from a `ValRaw`. 525 /// 526 /// # Unsafety 527 /// 528 /// This raw value's type must match the given `WasmValType`. 529 pub unsafe fn from_val_raw( 530 store: &mut StoreOpaque, 531 wasm_ty: WasmValType, 532 raw: ValRaw, 533 ) -> Result<Self> { 534 let mut global = Self::new(); 535 match wasm_ty { 536 WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(), 537 WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(), 538 WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(), 539 WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(), 540 WasmValType::V128 => global.set_u128(raw.get_v128()), 541 WasmValType::Ref(r) => match r.heap_type.top() { 542 WasmHeapTopType::Extern => { 543 let r = VMGcRef::from_raw_u32(raw.get_externref()); 544 global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) 545 } 546 WasmHeapTopType::Any => { 547 let r = VMGcRef::from_raw_u32(raw.get_anyref()); 548 global.init_gc_ref(store.gc_store_mut()?, r.as_ref()) 549 } 550 WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(), 551 WasmHeapTopType::Cont => *global.as_func_ref_mut() = raw.get_funcref().cast(), // TODO(#10248): temporary hack. 552 }, 553 } 554 Ok(global) 555 } 556 557 /// Get this global's value as a `ValRaw`. 558 /// 559 /// # Unsafety 560 /// 561 /// This global's value's type must match the given `WasmValType`. 562 pub unsafe fn to_val_raw( 563 &self, 564 store: &mut StoreOpaque, 565 wasm_ty: WasmValType, 566 ) -> Result<ValRaw> { 567 Ok(match wasm_ty { 568 WasmValType::I32 => ValRaw::i32(*self.as_i32()), 569 WasmValType::I64 => ValRaw::i64(*self.as_i64()), 570 WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()), 571 WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()), 572 WasmValType::V128 => ValRaw::v128(self.get_u128()), 573 WasmValType::Ref(r) => match r.heap_type.top() { 574 WasmHeapTopType::Extern => ValRaw::externref(match self.as_gc_ref() { 575 Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), 576 None => 0, 577 }), 578 WasmHeapTopType::Any => ValRaw::anyref({ 579 match self.as_gc_ref() { 580 Some(r) => store.gc_store_mut()?.clone_gc_ref(r).as_raw_u32(), 581 None => 0, 582 } 583 }), 584 WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()), 585 WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. 586 }, 587 }) 588 } 589 590 /// Return a reference to the value as an i32. 591 pub unsafe fn as_i32(&self) -> &i32 { 592 &*(self.storage.as_ref().as_ptr().cast::<i32>()) 593 } 594 595 /// Return a mutable reference to the value as an i32. 596 pub unsafe fn as_i32_mut(&mut self) -> &mut i32 { 597 &mut *(self.storage.as_mut().as_mut_ptr().cast::<i32>()) 598 } 599 600 /// Return a reference to the value as a u32. 601 pub unsafe fn as_u32(&self) -> &u32 { 602 &*(self.storage.as_ref().as_ptr().cast::<u32>()) 603 } 604 605 /// Return a mutable reference to the value as an u32. 606 pub unsafe fn as_u32_mut(&mut self) -> &mut u32 { 607 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) 608 } 609 610 /// Return a reference to the value as an i64. 611 pub unsafe fn as_i64(&self) -> &i64 { 612 &*(self.storage.as_ref().as_ptr().cast::<i64>()) 613 } 614 615 /// Return a mutable reference to the value as an i64. 616 pub unsafe fn as_i64_mut(&mut self) -> &mut i64 { 617 &mut *(self.storage.as_mut().as_mut_ptr().cast::<i64>()) 618 } 619 620 /// Return a reference to the value as an u64. 621 pub unsafe fn as_u64(&self) -> &u64 { 622 &*(self.storage.as_ref().as_ptr().cast::<u64>()) 623 } 624 625 /// Return a mutable reference to the value as an u64. 626 pub unsafe fn as_u64_mut(&mut self) -> &mut u64 { 627 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) 628 } 629 630 /// Return a reference to the value as an f32. 631 pub unsafe fn as_f32(&self) -> &f32 { 632 &*(self.storage.as_ref().as_ptr().cast::<f32>()) 633 } 634 635 /// Return a mutable reference to the value as an f32. 636 pub unsafe fn as_f32_mut(&mut self) -> &mut f32 { 637 &mut *(self.storage.as_mut().as_mut_ptr().cast::<f32>()) 638 } 639 640 /// Return a reference to the value as f32 bits. 641 pub unsafe fn as_f32_bits(&self) -> &u32 { 642 &*(self.storage.as_ref().as_ptr().cast::<u32>()) 643 } 644 645 /// Return a mutable reference to the value as f32 bits. 646 pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 { 647 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) 648 } 649 650 /// Return a reference to the value as an f64. 651 pub unsafe fn as_f64(&self) -> &f64 { 652 &*(self.storage.as_ref().as_ptr().cast::<f64>()) 653 } 654 655 /// Return a mutable reference to the value as an f64. 656 pub unsafe fn as_f64_mut(&mut self) -> &mut f64 { 657 &mut *(self.storage.as_mut().as_mut_ptr().cast::<f64>()) 658 } 659 660 /// Return a reference to the value as f64 bits. 661 pub unsafe fn as_f64_bits(&self) -> &u64 { 662 &*(self.storage.as_ref().as_ptr().cast::<u64>()) 663 } 664 665 /// Return a mutable reference to the value as f64 bits. 666 pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 { 667 &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) 668 } 669 670 /// Gets the underlying 128-bit vector value. 671 // 672 // Note that vectors are stored in little-endian format while other types 673 // are stored in native-endian format. 674 pub unsafe fn get_u128(&self) -> u128 { 675 u128::from_le(*(self.storage.as_ref().as_ptr().cast::<u128>())) 676 } 677 678 /// Sets the 128-bit vector values. 679 // 680 // Note that vectors are stored in little-endian format while other types 681 // are stored in native-endian format. 682 pub unsafe fn set_u128(&mut self, val: u128) { 683 *self.storage.as_mut().as_mut_ptr().cast::<u128>() = val.to_le(); 684 } 685 686 /// Return a reference to the value as u128 bits. 687 pub unsafe fn as_u128_bits(&self) -> &[u8; 16] { 688 &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) 689 } 690 691 /// Return a mutable reference to the value as u128 bits. 692 pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] { 693 &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) 694 } 695 696 /// Return a reference to the global value as a borrowed GC reference. 697 pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> { 698 let raw_ptr = self.storage.as_ref().as_ptr().cast::<Option<VMGcRef>>(); 699 let ret = (*raw_ptr).as_ref(); 700 assert!(cfg!(feature = "gc") || ret.is_none()); 701 ret 702 } 703 704 /// Initialize a global to the given GC reference. 705 pub unsafe fn init_gc_ref(&mut self, gc_store: &mut GcStore, gc_ref: Option<&VMGcRef>) { 706 assert!(cfg!(feature = "gc") || gc_ref.is_none()); 707 708 let dest = &mut *(self 709 .storage 710 .as_mut() 711 .as_mut_ptr() 712 .cast::<MaybeUninit<Option<VMGcRef>>>()); 713 714 gc_store.init_gc_ref(dest, gc_ref) 715 } 716 717 /// Write a GC reference into this global value. 718 pub unsafe fn write_gc_ref(&mut self, gc_store: &mut GcStore, gc_ref: Option<&VMGcRef>) { 719 assert!(cfg!(feature = "gc") || gc_ref.is_none()); 720 721 let dest = &mut *(self.storage.as_mut().as_mut_ptr().cast::<Option<VMGcRef>>()); 722 assert!(cfg!(feature = "gc") || dest.is_none()); 723 724 gc_store.write_gc_ref(dest, gc_ref) 725 } 726 727 /// Return a reference to the value as a `VMFuncRef`. 728 pub unsafe fn as_func_ref(&self) -> *mut VMFuncRef { 729 *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) 730 } 731 732 /// Return a mutable reference to the value as a `VMFuncRef`. 733 pub unsafe fn as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef { 734 &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) 735 } 736 } 737 738 #[cfg(test)] 739 mod test_vmshared_type_index { 740 use super::VMSharedTypeIndex; 741 use std::mem::size_of; 742 use wasmtime_environ::{HostPtr, Module, VMOffsets}; 743 744 #[test] 745 fn check_vmshared_type_index() { 746 let module = Module::new(); 747 let offsets = VMOffsets::new(HostPtr, &module); 748 assert_eq!( 749 size_of::<VMSharedTypeIndex>(), 750 usize::from(offsets.size_of_vmshared_type_index()) 751 ); 752 } 753 } 754 755 /// A WebAssembly tag defined within the instance. 756 /// 757 #[derive(Debug)] 758 #[repr(C)] 759 pub struct VMTagDefinition { 760 /// Function signature's type id. 761 pub type_index: VMSharedTypeIndex, 762 } 763 764 impl VMTagDefinition { 765 pub fn new(type_index: VMSharedTypeIndex) -> Self { 766 Self { type_index } 767 } 768 } 769 770 // SAFETY: the above structure is repr(C) and only contains VmSafe 771 // fields. 772 unsafe impl VmSafe for VMTagDefinition {} 773 774 #[cfg(test)] 775 mod test_vmtag_definition { 776 use super::VMTagDefinition; 777 use std::mem::size_of; 778 use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; 779 780 #[test] 781 fn check_vmtag_definition_offsets() { 782 let module = Module::new(); 783 let offsets = VMOffsets::new(HostPtr, &module); 784 assert_eq!( 785 size_of::<VMTagDefinition>(), 786 usize::from(offsets.ptr.size_of_vmtag_definition()) 787 ); 788 } 789 790 #[test] 791 fn check_vmtag_begins_aligned() { 792 let module = Module::new(); 793 let offsets = VMOffsets::new(HostPtr, &module); 794 assert_eq!(offsets.vmctx_tags_begin() % 16, 0); 795 } 796 } 797 798 /// The VM caller-checked "funcref" record, for caller-side signature checking. 799 /// 800 /// It consists of function pointer(s), a type id to be checked by the 801 /// caller, and the vmctx closure associated with this function. 802 #[derive(Debug, Clone)] 803 #[repr(C)] 804 pub struct VMFuncRef { 805 /// Function pointer for this funcref if being called via the "array" 806 /// calling convention that `Func::new` et al use. 807 pub array_call: VmPtr<VMArrayCallFunction>, 808 809 /// Function pointer for this funcref if being called via the calling 810 /// convention we use when compiling Wasm. 811 /// 812 /// Most functions come with a function pointer that we can use when they 813 /// are called from Wasm. The notable exception is when we `Func::wrap` a 814 /// host function, and we don't have a Wasm compiler on hand to compile a 815 /// Wasm-to-native trampoline for the function. In this case, we leave 816 /// `wasm_call` empty until the function is passed as an import to Wasm (or 817 /// otherwise exposed to Wasm via tables/globals). At this point, we look up 818 /// a Wasm-to-native trampoline for the function in the Wasm's compiled 819 /// module and use that fill in `VMFunctionImport::wasm_call`. **However** 820 /// there is no guarantee that the Wasm module has a trampoline for this 821 /// function's signature. The Wasm module only has trampolines for its 822 /// types, and if this function isn't of one of those types, then the Wasm 823 /// module will not have a trampoline for it. This is actually okay, because 824 /// it means that the Wasm cannot actually call this function. But it does 825 /// mean that this field needs to be an `Option` even though it is non-null 826 /// the vast vast vast majority of the time. 827 pub wasm_call: Option<VmPtr<VMWasmCallFunction>>, 828 829 /// Function signature's type id. 830 pub type_index: VMSharedTypeIndex, 831 832 /// The VM state associated with this function. 833 /// 834 /// The actual definition of what this pointer points to depends on the 835 /// function being referenced: for core Wasm functions, this is a `*mut 836 /// VMContext`, for host functions it is a `*mut VMHostFuncContext`, and for 837 /// component functions it is a `*mut VMComponentContext`. 838 pub vmctx: VmPtr<VMOpaqueContext>, 839 // If more elements are added here, remember to add offset_of tests below! 840 } 841 842 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 843 unsafe impl VmSafe for VMFuncRef {} 844 845 impl VMFuncRef { 846 /// Invokes the `array_call` field of this `VMFuncRef` with the supplied 847 /// arguments. 848 /// 849 /// This will invoke the function pointer in the `array_call` field with: 850 /// 851 /// * the `callee` vmctx as `self.vmctx` 852 /// * the `caller` as `caller` specified here 853 /// * the args pointer as `args_and_results` 854 /// * the args length as `args_and_results` 855 /// 856 /// The `args_and_results` area must be large enough to both load all 857 /// arguments from and store all results to. 858 /// 859 /// Returns whether a trap was recorded in TLS for raising. 860 /// 861 /// # Unsafety 862 /// 863 /// This method is unsafe because it can be called with any pointers. They 864 /// must all be valid for this wasm function call to proceed. For example 865 /// the `caller` must be valid machine code if `pulley` is `None` or it must 866 /// be valid bytecode if `pulley` is `Some`. Additionally `args_and_results` 867 /// must be large enough to handle all the arguments/results for this call. 868 /// 869 /// Note that the unsafety invariants to maintain here are not currently 870 /// exhaustively documented. 871 #[inline] 872 pub unsafe fn array_call( 873 me: NonNull<VMFuncRef>, 874 pulley: Option<InterpreterRef<'_>>, 875 caller: NonNull<VMContext>, 876 args_and_results: NonNull<[ValRaw]>, 877 ) -> bool { 878 match pulley { 879 Some(vm) => Self::array_call_interpreted(me, vm, caller, args_and_results), 880 None => Self::array_call_native(me, caller, args_and_results), 881 } 882 } 883 884 unsafe fn array_call_interpreted( 885 me: NonNull<VMFuncRef>, 886 vm: InterpreterRef<'_>, 887 caller: NonNull<VMContext>, 888 args_and_results: NonNull<[ValRaw]>, 889 ) -> bool { 890 // If `caller` is actually a `VMArrayCallHostFuncContext` then skip the 891 // interpreter, even though it's available, as `array_call` will be 892 // native code. 893 if me.as_ref().vmctx.as_non_null().as_ref().magic 894 == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC 895 { 896 return Self::array_call_native(me, caller, args_and_results); 897 } 898 vm.call( 899 me.as_ref().array_call.as_non_null().cast(), 900 me.as_ref().vmctx.as_non_null(), 901 caller, 902 args_and_results, 903 ) 904 } 905 906 #[inline] 907 unsafe fn array_call_native( 908 me: NonNull<VMFuncRef>, 909 caller: NonNull<VMContext>, 910 args_and_results: NonNull<[ValRaw]>, 911 ) -> bool { 912 union GetNativePointer { 913 native: VMArrayCallNative, 914 ptr: NonNull<VMArrayCallFunction>, 915 } 916 let native = GetNativePointer { 917 ptr: me.as_ref().array_call.as_non_null(), 918 } 919 .native; 920 native( 921 me.as_ref().vmctx.as_non_null(), 922 caller, 923 args_and_results.cast(), 924 args_and_results.len(), 925 ) 926 } 927 } 928 929 #[cfg(test)] 930 mod test_vm_func_ref { 931 use super::VMFuncRef; 932 use core::mem::offset_of; 933 use std::mem::size_of; 934 use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; 935 936 #[test] 937 fn check_vm_func_ref_offsets() { 938 let module = Module::new(); 939 let offsets = VMOffsets::new(HostPtr, &module); 940 assert_eq!( 941 size_of::<VMFuncRef>(), 942 usize::from(offsets.ptr.size_of_vm_func_ref()) 943 ); 944 assert_eq!( 945 offset_of!(VMFuncRef, array_call), 946 usize::from(offsets.ptr.vm_func_ref_array_call()) 947 ); 948 assert_eq!( 949 offset_of!(VMFuncRef, wasm_call), 950 usize::from(offsets.ptr.vm_func_ref_wasm_call()) 951 ); 952 assert_eq!( 953 offset_of!(VMFuncRef, type_index), 954 usize::from(offsets.ptr.vm_func_ref_type_index()) 955 ); 956 assert_eq!( 957 offset_of!(VMFuncRef, vmctx), 958 usize::from(offsets.ptr.vm_func_ref_vmctx()) 959 ); 960 } 961 } 962 963 macro_rules! define_builtin_array { 964 ( 965 $( 966 $( #[$attr:meta] )* 967 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; 968 )* 969 ) => { 970 /// An array that stores addresses of builtin functions. We translate code 971 /// to use indirect calls. This way, we don't have to patch the code. 972 /// 973 /// Ignore improper ctypes to permit `__m128i` on x86_64. 974 #[repr(C)] 975 pub struct VMBuiltinFunctionsArray { 976 $( 977 #[allow(improper_ctypes_definitions)] 978 $name: unsafe extern "C" fn( 979 $(define_builtin_array!(@ty $param)),* 980 ) $( -> define_builtin_array!(@ty $result))?, 981 )* 982 } 983 984 impl VMBuiltinFunctionsArray { 985 #[allow(unused_doc_comments)] 986 pub const INIT: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray { 987 $( 988 $name: crate::runtime::vm::libcalls::raw::$name, 989 )* 990 }; 991 992 /// Helper to call `expose_provenance()` on all contained pointers. 993 /// 994 /// This is required to be called at least once before entering wasm 995 /// to inform the compiler that these function pointers may all be 996 /// loaded/stored and used on the "other end" to reacquire 997 /// provenance in Pulley. Pulley models hostcalls with a host 998 /// pointer as the first parameter that's a function pointer under 999 /// the hood, and this call ensures that the use of the function 1000 /// pointer is considered valid. 1001 pub fn expose_provenance(&self) -> NonNull<Self>{ 1002 $( 1003 (self.$name as *mut u8).expose_provenance(); 1004 )* 1005 NonNull::from(self) 1006 } 1007 } 1008 }; 1009 1010 (@ty u32) => (u32); 1011 (@ty u64) => (u64); 1012 (@ty f32) => (f32); 1013 (@ty f64) => (f64); 1014 (@ty u8) => (u8); 1015 (@ty i8x16) => (i8x16); 1016 (@ty f32x4) => (f32x4); 1017 (@ty f64x2) => (f64x2); 1018 (@ty bool) => (bool); 1019 (@ty pointer) => (*mut u8); 1020 (@ty vmctx) => (NonNull<VMContext>); 1021 } 1022 1023 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 1024 unsafe impl VmSafe for VMBuiltinFunctionsArray {} 1025 1026 wasmtime_environ::foreach_builtin_function!(define_builtin_array); 1027 1028 const _: () = { 1029 assert!( 1030 mem::size_of::<VMBuiltinFunctionsArray>() 1031 == mem::size_of::<usize>() * (BuiltinFunctionIndex::len() as usize) 1032 ) 1033 }; 1034 1035 /// Structure that holds all mutable context that is shared across all instances 1036 /// in a store, for example data related to fuel or epochs. 1037 /// 1038 /// `VMStoreContext`s are one-to-one with `wasmtime::Store`s, the same way that 1039 /// `VMContext`s are one-to-one with `wasmtime::Instance`s. And the same way 1040 /// that multiple `wasmtime::Instance`s may be associated with the same 1041 /// `wasmtime::Store`, multiple `VMContext`s hold a pointer to the same 1042 /// `VMStoreContext` when they are associated with the same `wasmtime::Store`. 1043 #[derive(Debug)] 1044 #[repr(C)] 1045 pub struct VMStoreContext { 1046 // NB: 64-bit integer fields are located first with pointer-sized fields 1047 // trailing afterwards. That makes the offsets in this structure easier to 1048 // calculate on 32-bit platforms as we don't have to worry about the 1049 // alignment of 64-bit integers. 1050 // 1051 /// Indicator of how much fuel has been consumed and is remaining to 1052 /// WebAssembly. 1053 /// 1054 /// This field is typically negative and increments towards positive. Upon 1055 /// turning positive a wasm trap will be generated. This field is only 1056 /// modified if wasm is configured to consume fuel. 1057 pub fuel_consumed: UnsafeCell<i64>, 1058 1059 /// Deadline epoch for interruption: if epoch-based interruption 1060 /// is enabled and the global (per engine) epoch counter is 1061 /// observed to reach or exceed this value, the guest code will 1062 /// yield if running asynchronously. 1063 pub epoch_deadline: UnsafeCell<u64>, 1064 1065 /// Current stack limit of the wasm module. 1066 /// 1067 /// For more information see `crates/cranelift/src/lib.rs`. 1068 pub stack_limit: UnsafeCell<usize>, 1069 1070 /// The `VMMemoryDefinition` for this store's GC heap. 1071 pub gc_heap: VMMemoryDefinition, 1072 1073 /// The value of the frame pointer register when we last called from Wasm to 1074 /// the host. 1075 /// 1076 /// Maintained by our Wasm-to-host trampoline, and cleared just before 1077 /// calling into Wasm in `catch_traps`. 1078 /// 1079 /// This member is `0` when Wasm is actively running and has not called out 1080 /// to the host. 1081 /// 1082 /// Used to find the start of a contiguous sequence of Wasm frames when 1083 /// walking the stack. 1084 pub last_wasm_exit_fp: UnsafeCell<usize>, 1085 1086 /// The last Wasm program counter before we called from Wasm to the host. 1087 /// 1088 /// Maintained by our Wasm-to-host trampoline, and cleared just before 1089 /// calling into Wasm in `catch_traps`. 1090 /// 1091 /// This member is `0` when Wasm is actively running and has not called out 1092 /// to the host. 1093 /// 1094 /// Used when walking a contiguous sequence of Wasm frames. 1095 pub last_wasm_exit_pc: UnsafeCell<usize>, 1096 1097 /// The last host stack pointer before we called into Wasm from the host. 1098 /// 1099 /// Maintained by our host-to-Wasm trampoline, and cleared just before 1100 /// calling into Wasm in `catch_traps`. 1101 /// 1102 /// This member is `0` when Wasm is actively running and has not called out 1103 /// to the host. 1104 /// 1105 /// When a host function is wrapped into a `wasmtime::Func`, and is then 1106 /// called from the host, then this member has the sentinel value of `-1 as 1107 /// usize`, meaning that this contiguous sequence of Wasm frames is the 1108 /// empty sequence, and it is not safe to dereference the 1109 /// `last_wasm_exit_fp`. 1110 /// 1111 /// Used to find the end of a contiguous sequence of Wasm frames when 1112 /// walking the stack. 1113 pub last_wasm_entry_fp: UnsafeCell<usize>, 1114 1115 /// Stack information used by stack switching instructions. See documentation 1116 /// on `VMStackChain` for details. 1117 pub stack_chain: UnsafeCell<VMStackChain>, 1118 1119 /// The range, in addresses, of the guard page that is currently in use. 1120 /// 1121 /// This field is used when signal handlers are run to determine whether a 1122 /// faulting address lies within the guard page of an async stack for 1123 /// example. If this happens then the signal handler aborts with a stack 1124 /// overflow message similar to what would happen had the stack overflow 1125 /// happened on the main thread. This field is, by default a null..null 1126 /// range indicating that no async guard is in use (aka no fiber). In such a 1127 /// situation while this field is read it'll never classify a fault as an 1128 /// guard page fault. 1129 pub async_guard_range: Range<*mut u8>, 1130 } 1131 1132 // The `VMStoreContext` type is a pod-type with no destructor, and we don't 1133 // access any fields from other threads, so add in these trait impls which are 1134 // otherwise not available due to the `fuel_consumed` and `epoch_deadline` 1135 // variables in `VMStoreContext`. 1136 unsafe impl Send for VMStoreContext {} 1137 unsafe impl Sync for VMStoreContext {} 1138 1139 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 1140 unsafe impl VmSafe for VMStoreContext {} 1141 1142 impl Default for VMStoreContext { 1143 fn default() -> VMStoreContext { 1144 VMStoreContext { 1145 fuel_consumed: UnsafeCell::new(0), 1146 epoch_deadline: UnsafeCell::new(0), 1147 stack_limit: UnsafeCell::new(usize::max_value()), 1148 gc_heap: VMMemoryDefinition { 1149 base: NonNull::dangling().into(), 1150 current_length: AtomicUsize::new(0), 1151 }, 1152 last_wasm_exit_fp: UnsafeCell::new(0), 1153 last_wasm_exit_pc: UnsafeCell::new(0), 1154 last_wasm_entry_fp: UnsafeCell::new(0), 1155 stack_chain: UnsafeCell::new(VMStackChain::Absent), 1156 async_guard_range: ptr::null_mut()..ptr::null_mut(), 1157 } 1158 } 1159 } 1160 1161 #[cfg(test)] 1162 mod test_vmstore_context { 1163 use super::{VMMemoryDefinition, VMStoreContext}; 1164 use core::mem::offset_of; 1165 use wasmtime_environ::{HostPtr, Module, PtrSize, VMOffsets}; 1166 1167 #[test] 1168 fn field_offsets() { 1169 let module = Module::new(); 1170 let offsets = VMOffsets::new(HostPtr, &module); 1171 assert_eq!( 1172 offset_of!(VMStoreContext, stack_limit), 1173 usize::from(offsets.ptr.vmstore_context_stack_limit()) 1174 ); 1175 assert_eq!( 1176 offset_of!(VMStoreContext, fuel_consumed), 1177 usize::from(offsets.ptr.vmstore_context_fuel_consumed()) 1178 ); 1179 assert_eq!( 1180 offset_of!(VMStoreContext, epoch_deadline), 1181 usize::from(offsets.ptr.vmstore_context_epoch_deadline()) 1182 ); 1183 assert_eq!( 1184 offset_of!(VMStoreContext, gc_heap), 1185 usize::from(offsets.ptr.vmstore_context_gc_heap()) 1186 ); 1187 assert_eq!( 1188 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, base), 1189 usize::from(offsets.ptr.vmstore_context_gc_heap_base()) 1190 ); 1191 assert_eq!( 1192 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, current_length), 1193 usize::from(offsets.ptr.vmstore_context_gc_heap_current_length()) 1194 ); 1195 assert_eq!( 1196 offset_of!(VMStoreContext, last_wasm_exit_fp), 1197 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_fp()) 1198 ); 1199 assert_eq!( 1200 offset_of!(VMStoreContext, last_wasm_exit_pc), 1201 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_pc()) 1202 ); 1203 assert_eq!( 1204 offset_of!(VMStoreContext, last_wasm_entry_fp), 1205 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_fp()) 1206 ); 1207 assert_eq!( 1208 offset_of!(VMStoreContext, stack_chain), 1209 usize::from(offsets.ptr.vmstore_context_stack_chain()) 1210 ) 1211 } 1212 } 1213 1214 /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. 1215 /// This has information about globals, memories, tables, and other runtime 1216 /// state associated with the current instance. 1217 /// 1218 /// The struct here is empty, as the sizes of these fields are dynamic, and 1219 /// we can't describe them in Rust's type system. Sufficient memory is 1220 /// allocated at runtime. 1221 #[derive(Debug)] 1222 #[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside 1223 pub struct VMContext; 1224 1225 impl VMContext { 1226 /// Helper function to cast between context types using a debug assertion to 1227 /// protect against some mistakes. 1228 #[inline] 1229 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMContext> { 1230 // Note that in general the offset of the "magic" field is stored in 1231 // `VMOffsets::vmctx_magic`. Given though that this is a sanity check 1232 // about converting this pointer to another type we ideally don't want 1233 // to read the offset from potentially corrupt memory. Instead it would 1234 // be better to catch errors here as soon as possible. 1235 // 1236 // To accomplish this the `VMContext` structure is laid out with the 1237 // magic field at a statically known offset (here it's 0 for now). This 1238 // static offset is asserted in `VMOffsets::from` and needs to be kept 1239 // in sync with this line for this debug assertion to work. 1240 // 1241 // Also note that this magic is only ever invalid in the presence of 1242 // bugs, meaning we don't actually read the magic and act differently 1243 // at runtime depending what it is, so this is a debug assertion as 1244 // opposed to a regular assertion. 1245 debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC); 1246 opaque.cast() 1247 } 1248 } 1249 1250 /// A "raw" and unsafe representation of a WebAssembly value. 1251 /// 1252 /// This is provided for use with the `Func::new_unchecked` and 1253 /// `Func::call_unchecked` APIs. In general it's unlikely you should be using 1254 /// this from Rust, rather using APIs like `Func::wrap` and `TypedFunc::call`. 1255 /// 1256 /// This is notably an "unsafe" way to work with `Val` and it's recommended to 1257 /// instead use `Val` where possible. An important note about this union is that 1258 /// fields are all stored in little-endian format, regardless of the endianness 1259 /// of the host system. 1260 #[allow(missing_docs)] 1261 #[repr(C)] 1262 #[derive(Copy, Clone)] 1263 pub union ValRaw { 1264 /// A WebAssembly `i32` value. 1265 /// 1266 /// Note that the payload here is a Rust `i32` but the WebAssembly `i32` 1267 /// type does not assign an interpretation of the upper bit as either signed 1268 /// or unsigned. The Rust type `i32` is simply chosen for convenience. 1269 /// 1270 /// This value is always stored in a little-endian format. 1271 i32: i32, 1272 1273 /// A WebAssembly `i64` value. 1274 /// 1275 /// Note that the payload here is a Rust `i64` but the WebAssembly `i64` 1276 /// type does not assign an interpretation of the upper bit as either signed 1277 /// or unsigned. The Rust type `i64` is simply chosen for convenience. 1278 /// 1279 /// This value is always stored in a little-endian format. 1280 i64: i64, 1281 1282 /// A WebAssembly `f32` value. 1283 /// 1284 /// Note that the payload here is a Rust `u32`. This is to allow passing any 1285 /// representation of NaN into WebAssembly without risk of changing NaN 1286 /// payload bits as its gets passed around the system. Otherwise though this 1287 /// `u32` value is the return value of `f32::to_bits` in Rust. 1288 /// 1289 /// This value is always stored in a little-endian format. 1290 f32: u32, 1291 1292 /// A WebAssembly `f64` value. 1293 /// 1294 /// Note that the payload here is a Rust `u64`. This is to allow passing any 1295 /// representation of NaN into WebAssembly without risk of changing NaN 1296 /// payload bits as its gets passed around the system. Otherwise though this 1297 /// `u64` value is the return value of `f64::to_bits` in Rust. 1298 /// 1299 /// This value is always stored in a little-endian format. 1300 f64: u64, 1301 1302 /// A WebAssembly `v128` value. 1303 /// 1304 /// The payload here is a Rust `[u8; 16]` which has the same number of bits 1305 /// but note that `v128` in WebAssembly is often considered a vector type 1306 /// such as `i32x4` or `f64x2`. This means that the actual interpretation 1307 /// of the underlying bits is left up to the instructions which consume 1308 /// this value. 1309 /// 1310 /// This value is always stored in a little-endian format. 1311 v128: [u8; 16], 1312 1313 /// A WebAssembly `funcref` value (or one of its subtypes). 1314 /// 1315 /// The payload here is a pointer which is runtime-defined. This is one of 1316 /// the main points of unsafety about the `ValRaw` type as the validity of 1317 /// the pointer here is not easily verified and must be preserved by 1318 /// carefully calling the correct functions throughout the runtime. 1319 /// 1320 /// This value is always stored in a little-endian format. 1321 funcref: *mut c_void, 1322 1323 /// A WebAssembly `externref` value (or one of its subtypes). 1324 /// 1325 /// The payload here is a compressed pointer value which is 1326 /// runtime-defined. This is one of the main points of unsafety about the 1327 /// `ValRaw` type as the validity of the pointer here is not easily verified 1328 /// and must be preserved by carefully calling the correct functions 1329 /// throughout the runtime. 1330 /// 1331 /// This value is always stored in a little-endian format. 1332 externref: u32, 1333 1334 /// A WebAssembly `anyref` value (or one of its subtypes). 1335 /// 1336 /// The payload here is a compressed pointer value which is 1337 /// runtime-defined. This is one of the main points of unsafety about the 1338 /// `ValRaw` type as the validity of the pointer here is not easily verified 1339 /// and must be preserved by carefully calling the correct functions 1340 /// throughout the runtime. 1341 /// 1342 /// This value is always stored in a little-endian format. 1343 anyref: u32, 1344 } 1345 1346 // The `ValRaw` type is matched as `wasmtime_val_raw_t` in the C API so these 1347 // are some simple assertions about the shape of the type which are additionally 1348 // matched in C. 1349 const _: () = { 1350 assert!(mem::size_of::<ValRaw>() == 16); 1351 assert!(mem::align_of::<ValRaw>() == mem::align_of::<u64>()); 1352 }; 1353 1354 // This type is just a bag-of-bits so it's up to the caller to figure out how 1355 // to safely deal with threading concerns and safely access interior bits. 1356 unsafe impl Send for ValRaw {} 1357 unsafe impl Sync for ValRaw {} 1358 1359 impl fmt::Debug for ValRaw { 1360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1361 struct Hex<T>(T); 1362 impl<T: fmt::LowerHex> fmt::Debug for Hex<T> { 1363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1364 let bytes = mem::size_of::<T>(); 1365 let hex_digits_per_byte = 2; 1366 let hex_digits = bytes * hex_digits_per_byte; 1367 write!(f, "0x{:0width$x}", self.0, width = hex_digits) 1368 } 1369 } 1370 1371 unsafe { 1372 f.debug_struct("ValRaw") 1373 .field("i32", &Hex(self.i32)) 1374 .field("i64", &Hex(self.i64)) 1375 .field("f32", &Hex(self.f32)) 1376 .field("f64", &Hex(self.f64)) 1377 .field("v128", &Hex(u128::from_le_bytes(self.v128))) 1378 .field("funcref", &self.funcref) 1379 .field("externref", &Hex(self.externref)) 1380 .field("anyref", &Hex(self.anyref)) 1381 .finish() 1382 } 1383 } 1384 } 1385 1386 impl ValRaw { 1387 /// Create a null reference that is compatible with any of 1388 /// `{any,extern,func}ref`. 1389 pub fn null() -> ValRaw { 1390 unsafe { 1391 let raw = mem::MaybeUninit::<Self>::zeroed().assume_init(); 1392 debug_assert_eq!(raw.get_anyref(), 0); 1393 debug_assert_eq!(raw.get_externref(), 0); 1394 debug_assert_eq!(raw.get_funcref(), ptr::null_mut()); 1395 raw 1396 } 1397 } 1398 1399 /// Creates a WebAssembly `i32` value 1400 #[inline] 1401 pub fn i32(i: i32) -> ValRaw { 1402 // Note that this is intentionally not setting the `i32` field, instead 1403 // setting the `i64` field with a zero-extended version of `i`. For more 1404 // information on this see the comments on `Lower for Result` in the 1405 // `wasmtime` crate. Otherwise though all `ValRaw` constructors are 1406 // otherwise constrained to guarantee that the initial 64-bits are 1407 // always initialized. 1408 ValRaw::u64(i.unsigned().into()) 1409 } 1410 1411 /// Creates a WebAssembly `i64` value 1412 #[inline] 1413 pub fn i64(i: i64) -> ValRaw { 1414 ValRaw { i64: i.to_le() } 1415 } 1416 1417 /// Creates a WebAssembly `i32` value 1418 #[inline] 1419 pub fn u32(i: u32) -> ValRaw { 1420 // See comments in `ValRaw::i32` for why this is setting the upper 1421 // 32-bits as well. 1422 ValRaw::u64(i.into()) 1423 } 1424 1425 /// Creates a WebAssembly `i64` value 1426 #[inline] 1427 pub fn u64(i: u64) -> ValRaw { 1428 ValRaw::i64(i as i64) 1429 } 1430 1431 /// Creates a WebAssembly `f32` value 1432 #[inline] 1433 pub fn f32(i: u32) -> ValRaw { 1434 // See comments in `ValRaw::i32` for why this is setting the upper 1435 // 32-bits as well. 1436 ValRaw::u64(i.into()) 1437 } 1438 1439 /// Creates a WebAssembly `f64` value 1440 #[inline] 1441 pub fn f64(i: u64) -> ValRaw { 1442 ValRaw { f64: i.to_le() } 1443 } 1444 1445 /// Creates a WebAssembly `v128` value 1446 #[inline] 1447 pub fn v128(i: u128) -> ValRaw { 1448 ValRaw { 1449 v128: i.to_le_bytes(), 1450 } 1451 } 1452 1453 /// Creates a WebAssembly `funcref` value 1454 #[inline] 1455 pub fn funcref(i: *mut c_void) -> ValRaw { 1456 ValRaw { 1457 funcref: i.map_addr(|i| i.to_le()), 1458 } 1459 } 1460 1461 /// Creates a WebAssembly `externref` value 1462 #[inline] 1463 pub fn externref(e: u32) -> ValRaw { 1464 assert!(cfg!(feature = "gc") || e == 0); 1465 ValRaw { 1466 externref: e.to_le(), 1467 } 1468 } 1469 1470 /// Creates a WebAssembly `anyref` value 1471 #[inline] 1472 pub fn anyref(r: u32) -> ValRaw { 1473 assert!(cfg!(feature = "gc") || r == 0); 1474 ValRaw { anyref: r.to_le() } 1475 } 1476 1477 /// Gets the WebAssembly `i32` value 1478 #[inline] 1479 pub fn get_i32(&self) -> i32 { 1480 unsafe { i32::from_le(self.i32) } 1481 } 1482 1483 /// Gets the WebAssembly `i64` value 1484 #[inline] 1485 pub fn get_i64(&self) -> i64 { 1486 unsafe { i64::from_le(self.i64) } 1487 } 1488 1489 /// Gets the WebAssembly `i32` value 1490 #[inline] 1491 pub fn get_u32(&self) -> u32 { 1492 self.get_i32().unsigned() 1493 } 1494 1495 /// Gets the WebAssembly `i64` value 1496 #[inline] 1497 pub fn get_u64(&self) -> u64 { 1498 self.get_i64().unsigned() 1499 } 1500 1501 /// Gets the WebAssembly `f32` value 1502 #[inline] 1503 pub fn get_f32(&self) -> u32 { 1504 unsafe { u32::from_le(self.f32) } 1505 } 1506 1507 /// Gets the WebAssembly `f64` value 1508 #[inline] 1509 pub fn get_f64(&self) -> u64 { 1510 unsafe { u64::from_le(self.f64) } 1511 } 1512 1513 /// Gets the WebAssembly `v128` value 1514 #[inline] 1515 pub fn get_v128(&self) -> u128 { 1516 unsafe { u128::from_le_bytes(self.v128) } 1517 } 1518 1519 /// Gets the WebAssembly `funcref` value 1520 #[inline] 1521 pub fn get_funcref(&self) -> *mut c_void { 1522 unsafe { self.funcref.map_addr(|i| usize::from_le(i)) } 1523 } 1524 1525 /// Gets the WebAssembly `externref` value 1526 #[inline] 1527 pub fn get_externref(&self) -> u32 { 1528 let externref = u32::from_le(unsafe { self.externref }); 1529 assert!(cfg!(feature = "gc") || externref == 0); 1530 externref 1531 } 1532 1533 /// Gets the WebAssembly `anyref` value 1534 #[inline] 1535 pub fn get_anyref(&self) -> u32 { 1536 let anyref = u32::from_le(unsafe { self.anyref }); 1537 assert!(cfg!(feature = "gc") || anyref == 0); 1538 anyref 1539 } 1540 } 1541 1542 /// An "opaque" version of `VMContext` which must be explicitly casted to a 1543 /// target context. 1544 /// 1545 /// This context is used to represent that contexts specified in 1546 /// `VMFuncRef` can have any type and don't have an implicit 1547 /// structure. Neither wasmtime nor cranelift-generated code can rely on the 1548 /// structure of an opaque context in general and only the code which configured 1549 /// the context is able to rely on a particular structure. This is because the 1550 /// context pointer configured for `VMFuncRef` is guaranteed to be 1551 /// the first parameter passed. 1552 /// 1553 /// Note that Wasmtime currently has a layout where all contexts that are casted 1554 /// to an opaque context start with a 32-bit "magic" which can be used in debug 1555 /// mode to debug-assert that the casts here are correct and have at least a 1556 /// little protection against incorrect casts. 1557 pub struct VMOpaqueContext { 1558 pub(crate) magic: u32, 1559 _marker: marker::PhantomPinned, 1560 } 1561 1562 impl VMOpaqueContext { 1563 /// Helper function to clearly indicate that casts are desired. 1564 #[inline] 1565 pub fn from_vmcontext(ptr: NonNull<VMContext>) -> NonNull<VMOpaqueContext> { 1566 ptr.cast() 1567 } 1568 1569 /// Helper function to clearly indicate that casts are desired. 1570 #[inline] 1571 pub fn from_vm_array_call_host_func_context( 1572 ptr: NonNull<VMArrayCallHostFuncContext>, 1573 ) -> NonNull<VMOpaqueContext> { 1574 ptr.cast() 1575 } 1576 } 1577