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::{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, 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, StaticModuleIndex, VMOffsets}; 99 100 #[test] check_vmfunction_import_offsets()101 fn check_vmfunction_import_offsets() { 102 let module = Module::new(StaticModuleIndex::from_u32(0)); 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] check_vmfunction_body_offsets()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, StaticModuleIndex, VMOffsets}; 169 170 #[test] check_vmtable_offsets()171 fn check_vmtable_offsets() { 172 let module = Module::new(StaticModuleIndex::from_u32(0)); 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 assert_eq!( 183 offset_of!(VMTableImport, vmctx), 184 usize::from(offsets.vmtable_import_vmctx()) 185 ); 186 assert_eq!( 187 offset_of!(VMTableImport, index), 188 usize::from(offsets.vmtable_import_index()) 189 ); 190 } 191 192 #[test] ensure_sizes_match()193 fn ensure_sizes_match() { 194 // Because we use `VMTableImport` for recording tables used by components, we 195 // want to make sure that the size calculations between `VMOffsets` and 196 // `VMComponentOffsets` stay the same. 197 let module = Module::new(StaticModuleIndex::from_u32(0)); 198 let vm_offsets = VMOffsets::new(HostPtr, &module); 199 let component = Component::default(); 200 let vm_component_offsets = VMComponentOffsets::new(HostPtr, &component); 201 assert_eq!( 202 vm_offsets.size_of_vmtable_import(), 203 vm_component_offsets.size_of_vmtable_import() 204 ); 205 } 206 } 207 208 /// The fields compiled code needs to access to utilize a WebAssembly linear 209 /// memory imported from another instance. 210 #[derive(Debug, Copy, Clone)] 211 #[repr(C)] 212 pub struct VMMemoryImport { 213 /// A pointer to the imported memory description. 214 pub from: VmPtr<VMMemoryDefinition>, 215 216 /// A pointer to the `VMContext` that owns the memory description. 217 pub vmctx: VmPtr<VMContext>, 218 219 /// The index of the memory in the containing `vmctx`. 220 pub index: DefinedMemoryIndex, 221 } 222 223 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 224 unsafe impl VmSafe for VMMemoryImport {} 225 226 #[cfg(test)] 227 mod test_vmmemory_import { 228 use super::VMMemoryImport; 229 use core::mem::offset_of; 230 use std::mem::size_of; 231 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets}; 232 233 #[test] check_vmmemory_import_offsets()234 fn check_vmmemory_import_offsets() { 235 let module = Module::new(StaticModuleIndex::from_u32(0)); 236 let offsets = VMOffsets::new(HostPtr, &module); 237 assert_eq!( 238 size_of::<VMMemoryImport>(), 239 usize::from(offsets.size_of_vmmemory_import()) 240 ); 241 assert_eq!( 242 offset_of!(VMMemoryImport, from), 243 usize::from(offsets.vmmemory_import_from()) 244 ); 245 assert_eq!( 246 offset_of!(VMMemoryImport, vmctx), 247 usize::from(offsets.vmmemory_import_vmctx()) 248 ); 249 assert_eq!( 250 offset_of!(VMMemoryImport, index), 251 usize::from(offsets.vmmemory_import_index()) 252 ); 253 } 254 } 255 256 /// The fields compiled code needs to access to utilize a WebAssembly global 257 /// variable imported from another instance. 258 /// 259 /// Note that unlike with functions, tables, and memories, `VMGlobalImport` 260 /// doesn't include a `vmctx` pointer. Globals are never resized, and don't 261 /// require a `vmctx` pointer to access. 262 #[derive(Debug, Copy, Clone)] 263 #[repr(C)] 264 pub struct VMGlobalImport { 265 /// A pointer to the imported global variable description. 266 pub from: VmPtr<VMGlobalDefinition>, 267 268 /// A pointer to the context that owns the global. 269 /// 270 /// Exactly what's stored here is dictated by `kind` below. This is `None` 271 /// for `VMGlobalKind::Host`, it's a `VMContext` for 272 /// `VMGlobalKind::Instance`, and it's `VMComponentContext` for 273 /// `VMGlobalKind::ComponentFlags`. 274 pub vmctx: Option<VmPtr<VMOpaqueContext>>, 275 276 /// The kind of global, and extra location information in addition to 277 /// `vmctx` above. 278 pub kind: VMGlobalKind, 279 } 280 281 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 282 unsafe impl VmSafe for VMGlobalImport {} 283 284 /// The kinds of globals that Wasmtime has. 285 #[derive(Debug, Copy, Clone)] 286 #[repr(C, u32)] 287 pub enum VMGlobalKind { 288 /// Host globals, stored in a `StoreOpaque`. 289 Host(DefinedGlobalIndex), 290 /// Instance globals, stored in `VMContext`s 291 Instance(DefinedGlobalIndex), 292 /// Flags for a component instance, stored in `VMComponentContext`. 293 #[cfg(feature = "component-model")] 294 ComponentFlags(wasmtime_environ::component::RuntimeComponentInstanceIndex), 295 #[cfg(feature = "component-model")] 296 TaskMayBlock, 297 } 298 299 // SAFETY: the above enum is repr(C) and stores nothing else 300 unsafe impl VmSafe for VMGlobalKind {} 301 302 #[cfg(test)] 303 mod test_vmglobal_import { 304 use super::VMGlobalImport; 305 use core::mem::offset_of; 306 use std::mem::size_of; 307 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets}; 308 309 #[test] check_vmglobal_import_offsets()310 fn check_vmglobal_import_offsets() { 311 let module = Module::new(StaticModuleIndex::from_u32(0)); 312 let offsets = VMOffsets::new(HostPtr, &module); 313 assert_eq!( 314 size_of::<VMGlobalImport>(), 315 usize::from(offsets.size_of_vmglobal_import()) 316 ); 317 assert_eq!( 318 offset_of!(VMGlobalImport, from), 319 usize::from(offsets.vmglobal_import_from()) 320 ); 321 } 322 } 323 324 /// The fields compiled code needs to access to utilize a WebAssembly 325 /// tag imported from another instance. 326 #[derive(Debug, Copy, Clone)] 327 #[repr(C)] 328 pub struct VMTagImport { 329 /// A pointer to the imported tag description. 330 pub from: VmPtr<VMTagDefinition>, 331 332 /// The instance that owns this tag. 333 pub vmctx: VmPtr<VMContext>, 334 335 /// The index of the tag in the containing `vmctx`. 336 pub index: DefinedTagIndex, 337 } 338 339 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 340 unsafe impl VmSafe for VMTagImport {} 341 342 #[cfg(test)] 343 mod test_vmtag_import { 344 use super::VMTagImport; 345 use core::mem::{offset_of, size_of}; 346 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets}; 347 348 #[test] check_vmtag_import_offsets()349 fn check_vmtag_import_offsets() { 350 let module = Module::new(StaticModuleIndex::from_u32(0)); 351 let offsets = VMOffsets::new(HostPtr, &module); 352 assert_eq!( 353 size_of::<VMTagImport>(), 354 usize::from(offsets.size_of_vmtag_import()) 355 ); 356 assert_eq!( 357 offset_of!(VMTagImport, from), 358 usize::from(offsets.vmtag_import_from()) 359 ); 360 assert_eq!( 361 offset_of!(VMTagImport, vmctx), 362 usize::from(offsets.vmtag_import_vmctx()) 363 ); 364 assert_eq!( 365 offset_of!(VMTagImport, index), 366 usize::from(offsets.vmtag_import_index()) 367 ); 368 } 369 } 370 371 /// The fields compiled code needs to access to utilize a WebAssembly linear 372 /// memory defined within the instance, namely the start address and the 373 /// size in bytes. 374 #[derive(Debug)] 375 #[repr(C)] 376 pub struct VMMemoryDefinition { 377 /// The start address. 378 pub base: VmPtr<u8>, 379 380 /// The current logical size of this linear memory in bytes. 381 /// 382 /// This is atomic because shared memories must be able to grow their length 383 /// atomically. For relaxed access, see 384 /// [`VMMemoryDefinition::current_length()`]. 385 pub current_length: AtomicUsize, 386 } 387 388 // SAFETY: the above definition has `repr(C)` and each field individually 389 // implements `VmSafe`, which satisfies the requirements of this trait. 390 unsafe impl VmSafe for VMMemoryDefinition {} 391 392 impl VMMemoryDefinition { 393 /// Return the current length (in bytes) of the [`VMMemoryDefinition`] by 394 /// performing a relaxed load; do not use this function for situations in 395 /// which a precise length is needed. Owned memories (i.e., non-shared) will 396 /// always return a precise result (since no concurrent modification is 397 /// possible) but shared memories may see an imprecise value--a 398 /// `current_length` potentially smaller than what some other thread 399 /// observes. Since Wasm memory only grows, this under-estimation may be 400 /// acceptable in certain cases. 401 #[inline] current_length(&self) -> usize402 pub fn current_length(&self) -> usize { 403 self.current_length.load(Ordering::Relaxed) 404 } 405 406 /// Return a copy of the [`VMMemoryDefinition`] using the relaxed value of 407 /// `current_length`; see [`VMMemoryDefinition::current_length()`]. 408 #[inline] load(ptr: *mut Self) -> Self409 pub unsafe fn load(ptr: *mut Self) -> Self { 410 let other = unsafe { &*ptr }; 411 VMMemoryDefinition { 412 base: other.base, 413 current_length: other.current_length().into(), 414 } 415 } 416 } 417 418 #[cfg(test)] 419 mod test_vmmemory_definition { 420 use super::VMMemoryDefinition; 421 use core::mem::offset_of; 422 use std::mem::size_of; 423 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets}; 424 425 #[test] check_vmmemory_definition_offsets()426 fn check_vmmemory_definition_offsets() { 427 let module = Module::new(StaticModuleIndex::from_u32(0)); 428 let offsets = VMOffsets::new(HostPtr, &module); 429 assert_eq!( 430 size_of::<VMMemoryDefinition>(), 431 usize::from(offsets.ptr.size_of_vmmemory_definition()) 432 ); 433 assert_eq!( 434 offset_of!(VMMemoryDefinition, base), 435 usize::from(offsets.ptr.vmmemory_definition_base()) 436 ); 437 assert_eq!( 438 offset_of!(VMMemoryDefinition, current_length), 439 usize::from(offsets.ptr.vmmemory_definition_current_length()) 440 ); 441 /* TODO: Assert that the size of `current_length` matches. 442 assert_eq!( 443 size_of::<VMMemoryDefinition::current_length>(), 444 usize::from(offsets.size_of_vmmemory_definition_current_length()) 445 ); 446 */ 447 } 448 } 449 450 /// The fields compiled code needs to access to utilize a WebAssembly table 451 /// defined within the instance. 452 #[derive(Debug, Copy, Clone)] 453 #[repr(C)] 454 pub struct VMTableDefinition { 455 /// Pointer to the table data. 456 pub base: VmPtr<u8>, 457 458 /// The current number of elements in the table. 459 pub current_elements: usize, 460 } 461 462 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 463 unsafe impl VmSafe for VMTableDefinition {} 464 465 #[cfg(test)] 466 mod test_vmtable_definition { 467 use super::VMTableDefinition; 468 use core::mem::offset_of; 469 use std::mem::size_of; 470 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets}; 471 472 #[test] check_vmtable_definition_offsets()473 fn check_vmtable_definition_offsets() { 474 let module = Module::new(StaticModuleIndex::from_u32(0)); 475 let offsets = VMOffsets::new(HostPtr, &module); 476 assert_eq!( 477 size_of::<VMTableDefinition>(), 478 usize::from(offsets.size_of_vmtable_definition()) 479 ); 480 assert_eq!( 481 offset_of!(VMTableDefinition, base), 482 usize::from(offsets.vmtable_definition_base()) 483 ); 484 assert_eq!( 485 offset_of!(VMTableDefinition, current_elements), 486 usize::from(offsets.vmtable_definition_current_elements()) 487 ); 488 } 489 } 490 491 /// The storage for a WebAssembly global defined within the instance. 492 /// 493 /// TODO: Pack the globals more densely, rather than using the same size 494 /// for every type. 495 #[derive(Debug)] 496 #[repr(C, align(16))] 497 pub struct VMGlobalDefinition { 498 storage: [u8; 16], 499 // If more elements are added here, remember to add offset_of tests below! 500 } 501 502 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 503 unsafe impl VmSafe for VMGlobalDefinition {} 504 505 #[cfg(test)] 506 mod test_vmglobal_definition { 507 use super::VMGlobalDefinition; 508 use std::mem::{align_of, size_of}; 509 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets}; 510 511 #[test] check_vmglobal_definition_alignment()512 fn check_vmglobal_definition_alignment() { 513 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>()); 514 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>()); 515 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>()); 516 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>()); 517 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[u8; 16]>()); 518 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f32; 4]>()); 519 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f64; 2]>()); 520 } 521 522 #[test] check_vmglobal_definition_offsets()523 fn check_vmglobal_definition_offsets() { 524 let module = Module::new(StaticModuleIndex::from_u32(0)); 525 let offsets = VMOffsets::new(HostPtr, &module); 526 assert_eq!( 527 size_of::<VMGlobalDefinition>(), 528 usize::from(offsets.ptr.size_of_vmglobal_definition()) 529 ); 530 } 531 532 #[test] check_vmglobal_begins_aligned()533 fn check_vmglobal_begins_aligned() { 534 let module = Module::new(StaticModuleIndex::from_u32(0)); 535 let offsets = VMOffsets::new(HostPtr, &module); 536 assert_eq!(offsets.vmctx_globals_begin() % 16, 0); 537 } 538 539 #[test] 540 #[cfg(feature = "gc")] check_vmglobal_can_contain_gc_ref()541 fn check_vmglobal_can_contain_gc_ref() { 542 assert!(size_of::<crate::runtime::vm::VMGcRef>() <= size_of::<VMGlobalDefinition>()); 543 } 544 } 545 546 impl VMGlobalDefinition { 547 /// Construct a `VMGlobalDefinition`. new() -> Self548 pub fn new() -> Self { 549 Self { storage: [0; 16] } 550 } 551 552 /// Create a `VMGlobalDefinition` from a `ValRaw`. 553 /// 554 /// # Unsafety 555 /// 556 /// This raw value's type must match the given `WasmValType`. from_val_raw( store: &mut StoreOpaque, wasm_ty: WasmValType, raw: ValRaw, ) -> Result<Self>557 pub unsafe fn from_val_raw( 558 store: &mut StoreOpaque, 559 wasm_ty: WasmValType, 560 raw: ValRaw, 561 ) -> Result<Self> { 562 let mut global = Self::new(); 563 unsafe { 564 match wasm_ty { 565 WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(), 566 WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(), 567 WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(), 568 WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(), 569 WasmValType::V128 => global.set_u128(raw.get_v128()), 570 WasmValType::Ref(r) => match r.heap_type.top() { 571 WasmHeapTopType::Extern => { 572 let r = VMGcRef::from_raw_u32(raw.get_externref()); 573 global.init_gc_ref(store, r.as_ref()) 574 } 575 WasmHeapTopType::Any => { 576 let r = VMGcRef::from_raw_u32(raw.get_anyref()); 577 global.init_gc_ref(store, r.as_ref()) 578 } 579 WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(), 580 WasmHeapTopType::Cont => *global.as_func_ref_mut() = raw.get_funcref().cast(), // TODO(#10248): temporary hack. 581 WasmHeapTopType::Exn => { 582 let r = VMGcRef::from_raw_u32(raw.get_exnref()); 583 global.init_gc_ref(store, r.as_ref()) 584 } 585 }, 586 } 587 } 588 Ok(global) 589 } 590 591 /// Get this global's value as a `ValRaw`. 592 /// 593 /// # Unsafety 594 /// 595 /// This global's value's type must match the given `WasmValType`. to_val_raw( &self, store: &mut StoreOpaque, wasm_ty: WasmValType, ) -> Result<ValRaw>596 pub unsafe fn to_val_raw( 597 &self, 598 store: &mut StoreOpaque, 599 wasm_ty: WasmValType, 600 ) -> Result<ValRaw> { 601 unsafe { 602 Ok(match wasm_ty { 603 WasmValType::I32 => ValRaw::i32(*self.as_i32()), 604 WasmValType::I64 => ValRaw::i64(*self.as_i64()), 605 WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()), 606 WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()), 607 WasmValType::V128 => ValRaw::v128(self.get_u128()), 608 WasmValType::Ref(r) => match r.heap_type.top() { 609 WasmHeapTopType::Extern => ValRaw::externref(match self.as_gc_ref() { 610 Some(r) => store.clone_gc_ref(r).as_raw_u32(), 611 None => 0, 612 }), 613 WasmHeapTopType::Any => ValRaw::anyref({ 614 match self.as_gc_ref() { 615 Some(r) => store.clone_gc_ref(r).as_raw_u32(), 616 None => 0, 617 } 618 }), 619 WasmHeapTopType::Exn => ValRaw::exnref({ 620 match self.as_gc_ref() { 621 Some(r) => store.clone_gc_ref(r).as_raw_u32(), 622 None => 0, 623 } 624 }), 625 WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()), 626 WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. 627 }, 628 }) 629 } 630 } 631 632 /// Return a reference to the value as an i32. as_i32(&self) -> &i32633 pub unsafe fn as_i32(&self) -> &i32 { 634 unsafe { &*(self.storage.as_ref().as_ptr().cast::<i32>()) } 635 } 636 637 /// Return a mutable reference to the value as an i32. as_i32_mut(&mut self) -> &mut i32638 pub unsafe fn as_i32_mut(&mut self) -> &mut i32 { 639 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<i32>()) } 640 } 641 642 /// Return a reference to the value as a u32. as_u32(&self) -> &u32643 pub unsafe fn as_u32(&self) -> &u32 { 644 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u32>()) } 645 } 646 647 /// Return a mutable reference to the value as an u32. as_u32_mut(&mut self) -> &mut u32648 pub unsafe fn as_u32_mut(&mut self) -> &mut u32 { 649 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) } 650 } 651 652 /// Return a reference to the value as an i64. as_i64(&self) -> &i64653 pub unsafe fn as_i64(&self) -> &i64 { 654 unsafe { &*(self.storage.as_ref().as_ptr().cast::<i64>()) } 655 } 656 657 /// Return a mutable reference to the value as an i64. as_i64_mut(&mut self) -> &mut i64658 pub unsafe fn as_i64_mut(&mut self) -> &mut i64 { 659 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<i64>()) } 660 } 661 662 /// Return a reference to the value as an u64. as_u64(&self) -> &u64663 pub unsafe fn as_u64(&self) -> &u64 { 664 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u64>()) } 665 } 666 667 /// Return a mutable reference to the value as an u64. as_u64_mut(&mut self) -> &mut u64668 pub unsafe fn as_u64_mut(&mut self) -> &mut u64 { 669 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) } 670 } 671 672 /// Return a reference to the value as an f32. as_f32(&self) -> &f32673 pub unsafe fn as_f32(&self) -> &f32 { 674 unsafe { &*(self.storage.as_ref().as_ptr().cast::<f32>()) } 675 } 676 677 /// Return a mutable reference to the value as an f32. as_f32_mut(&mut self) -> &mut f32678 pub unsafe fn as_f32_mut(&mut self) -> &mut f32 { 679 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<f32>()) } 680 } 681 682 /// Return a reference to the value as f32 bits. as_f32_bits(&self) -> &u32683 pub unsafe fn as_f32_bits(&self) -> &u32 { 684 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u32>()) } 685 } 686 687 /// Return a mutable reference to the value as f32 bits. as_f32_bits_mut(&mut self) -> &mut u32688 pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 { 689 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) } 690 } 691 692 /// Return a reference to the value as an f64. as_f64(&self) -> &f64693 pub unsafe fn as_f64(&self) -> &f64 { 694 unsafe { &*(self.storage.as_ref().as_ptr().cast::<f64>()) } 695 } 696 697 /// Return a mutable reference to the value as an f64. as_f64_mut(&mut self) -> &mut f64698 pub unsafe fn as_f64_mut(&mut self) -> &mut f64 { 699 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<f64>()) } 700 } 701 702 /// Return a reference to the value as f64 bits. as_f64_bits(&self) -> &u64703 pub unsafe fn as_f64_bits(&self) -> &u64 { 704 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u64>()) } 705 } 706 707 /// Return a mutable reference to the value as f64 bits. as_f64_bits_mut(&mut self) -> &mut u64708 pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 { 709 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) } 710 } 711 712 /// Gets the underlying 128-bit vector value. 713 // 714 // Note that vectors are stored in little-endian format while other types 715 // are stored in native-endian format. get_u128(&self) -> u128716 pub unsafe fn get_u128(&self) -> u128 { 717 unsafe { u128::from_le(*(self.storage.as_ref().as_ptr().cast::<u128>())) } 718 } 719 720 /// Sets the 128-bit vector values. 721 // 722 // Note that vectors are stored in little-endian format while other types 723 // are stored in native-endian format. set_u128(&mut self, val: u128)724 pub unsafe fn set_u128(&mut self, val: u128) { 725 unsafe { 726 *self.storage.as_mut().as_mut_ptr().cast::<u128>() = val.to_le(); 727 } 728 } 729 730 /// Return a reference to the value as u128 bits. as_u128_bits(&self) -> &[u8; 16]731 pub unsafe fn as_u128_bits(&self) -> &[u8; 16] { 732 unsafe { &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) } 733 } 734 735 /// Return a mutable reference to the value as u128 bits. as_u128_bits_mut(&mut self) -> &mut [u8; 16]736 pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] { 737 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) } 738 } 739 740 /// Return a reference to the global value as a borrowed GC reference. as_gc_ref(&self) -> Option<&VMGcRef>741 pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> { 742 let raw_ptr = self.storage.as_ref().as_ptr().cast::<Option<VMGcRef>>(); 743 let ret = unsafe { (*raw_ptr).as_ref() }; 744 assert!(cfg!(feature = "gc") || ret.is_none()); 745 ret 746 } 747 748 /// Initialize a global to the given GC reference. init_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>)749 pub unsafe fn init_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>) { 750 let dest = unsafe { 751 &mut *(self 752 .storage 753 .as_mut() 754 .as_mut_ptr() 755 .cast::<MaybeUninit<Option<VMGcRef>>>()) 756 }; 757 758 store.init_gc_ref(dest, gc_ref) 759 } 760 761 /// Write a GC reference into this global value. write_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>)762 pub unsafe fn write_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>) { 763 let dest = unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<Option<VMGcRef>>()) }; 764 store.write_gc_ref(dest, gc_ref) 765 } 766 767 /// Return a reference to the value as a `VMFuncRef`. as_func_ref(&self) -> *mut VMFuncRef768 pub unsafe fn as_func_ref(&self) -> *mut VMFuncRef { 769 unsafe { *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) } 770 } 771 772 /// Return a mutable reference to the value as a `VMFuncRef`. as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef773 pub unsafe fn as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef { 774 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) } 775 } 776 } 777 778 #[cfg(test)] 779 mod test_vmshared_type_index { 780 use super::VMSharedTypeIndex; 781 use std::mem::size_of; 782 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets}; 783 784 #[test] check_vmshared_type_index()785 fn check_vmshared_type_index() { 786 let module = Module::new(StaticModuleIndex::from_u32(0)); 787 let offsets = VMOffsets::new(HostPtr, &module); 788 assert_eq!( 789 size_of::<VMSharedTypeIndex>(), 790 usize::from(offsets.size_of_vmshared_type_index()) 791 ); 792 } 793 } 794 795 /// A WebAssembly tag defined within the instance. 796 /// 797 #[derive(Debug)] 798 #[repr(C)] 799 pub struct VMTagDefinition { 800 /// Function signature's type id. 801 pub type_index: VMSharedTypeIndex, 802 } 803 804 impl VMTagDefinition { new(type_index: VMSharedTypeIndex) -> Self805 pub fn new(type_index: VMSharedTypeIndex) -> Self { 806 Self { type_index } 807 } 808 } 809 810 // SAFETY: the above structure is repr(C) and only contains VmSafe 811 // fields. 812 unsafe impl VmSafe for VMTagDefinition {} 813 814 #[cfg(test)] 815 mod test_vmtag_definition { 816 use super::VMTagDefinition; 817 use std::mem::size_of; 818 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets}; 819 820 #[test] check_vmtag_definition_offsets()821 fn check_vmtag_definition_offsets() { 822 let module = Module::new(StaticModuleIndex::from_u32(0)); 823 let offsets = VMOffsets::new(HostPtr, &module); 824 assert_eq!( 825 size_of::<VMTagDefinition>(), 826 usize::from(offsets.ptr.size_of_vmtag_definition()) 827 ); 828 } 829 830 #[test] check_vmtag_begins_aligned()831 fn check_vmtag_begins_aligned() { 832 let module = Module::new(StaticModuleIndex::from_u32(0)); 833 let offsets = VMOffsets::new(HostPtr, &module); 834 assert_eq!(offsets.vmctx_tags_begin() % 16, 0); 835 } 836 } 837 838 /// The VM caller-checked "funcref" record, for caller-side signature checking. 839 /// 840 /// It consists of function pointer(s), a type id to be checked by the 841 /// caller, and the vmctx closure associated with this function. 842 #[derive(Debug, Clone)] 843 #[repr(C)] 844 pub struct VMFuncRef { 845 /// Function pointer for this funcref if being called via the "array" 846 /// calling convention that `Func::new` et al use. 847 pub array_call: VmPtr<VMArrayCallFunction>, 848 849 /// Function pointer for this funcref if being called via the calling 850 /// convention we use when compiling Wasm. 851 /// 852 /// Most functions come with a function pointer that we can use when they 853 /// are called from Wasm. The notable exception is when we `Func::wrap` a 854 /// host function, and we don't have a Wasm compiler on hand to compile a 855 /// Wasm-to-native trampoline for the function. In this case, we leave 856 /// `wasm_call` empty until the function is passed as an import to Wasm (or 857 /// otherwise exposed to Wasm via tables/globals). At this point, we look up 858 /// a Wasm-to-native trampoline for the function in the Wasm's compiled 859 /// module and use that fill in `VMFunctionImport::wasm_call`. **However** 860 /// there is no guarantee that the Wasm module has a trampoline for this 861 /// function's signature. The Wasm module only has trampolines for its 862 /// types, and if this function isn't of one of those types, then the Wasm 863 /// module will not have a trampoline for it. This is actually okay, because 864 /// it means that the Wasm cannot actually call this function. But it does 865 /// mean that this field needs to be an `Option` even though it is non-null 866 /// the vast vast vast majority of the time. 867 pub wasm_call: Option<VmPtr<VMWasmCallFunction>>, 868 869 /// Function signature's type id. 870 pub type_index: VMSharedTypeIndex, 871 872 /// The VM state associated with this function. 873 /// 874 /// The actual definition of what this pointer points to depends on the 875 /// function being referenced: for core Wasm functions, this is a `*mut 876 /// VMContext`, for host functions it is a `*mut VMHostFuncContext`, and for 877 /// component functions it is a `*mut VMComponentContext`. 878 pub vmctx: VmPtr<VMOpaqueContext>, 879 // If more elements are added here, remember to add offset_of tests below! 880 } 881 882 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 883 unsafe impl VmSafe for VMFuncRef {} 884 885 impl VMFuncRef { 886 /// Invokes the `array_call` field of this `VMFuncRef` with the supplied 887 /// arguments. 888 /// 889 /// This will invoke the function pointer in the `array_call` field with: 890 /// 891 /// * the `callee` vmctx as `self.vmctx` 892 /// * the `caller` as `caller` specified here 893 /// * the args pointer as `args_and_results` 894 /// * the args length as `args_and_results` 895 /// 896 /// The `args_and_results` area must be large enough to both load all 897 /// arguments from and store all results to. 898 /// 899 /// Returns whether a trap was recorded in TLS for raising. 900 /// 901 /// # Unsafety 902 /// 903 /// This method is unsafe because it can be called with any pointers. They 904 /// must all be valid for this wasm function call to proceed. For example 905 /// the `caller` must be valid machine code if `pulley` is `None` or it must 906 /// be valid bytecode if `pulley` is `Some`. Additionally `args_and_results` 907 /// must be large enough to handle all the arguments/results for this call. 908 /// 909 /// Note that the unsafety invariants to maintain here are not currently 910 /// exhaustively documented. 911 #[inline] array_call( me: NonNull<VMFuncRef>, pulley: Option<InterpreterRef<'_>>, caller: NonNull<VMContext>, args_and_results: NonNull<[ValRaw]>, ) -> bool912 pub unsafe fn array_call( 913 me: NonNull<VMFuncRef>, 914 pulley: Option<InterpreterRef<'_>>, 915 caller: NonNull<VMContext>, 916 args_and_results: NonNull<[ValRaw]>, 917 ) -> bool { 918 match pulley { 919 Some(vm) => unsafe { Self::array_call_interpreted(me, vm, caller, args_and_results) }, 920 None => unsafe { Self::array_call_native(me, caller, args_and_results) }, 921 } 922 } 923 array_call_interpreted( me: NonNull<VMFuncRef>, vm: InterpreterRef<'_>, caller: NonNull<VMContext>, args_and_results: NonNull<[ValRaw]>, ) -> bool924 unsafe fn array_call_interpreted( 925 me: NonNull<VMFuncRef>, 926 vm: InterpreterRef<'_>, 927 caller: NonNull<VMContext>, 928 args_and_results: NonNull<[ValRaw]>, 929 ) -> bool { 930 // If `caller` is actually a `VMArrayCallHostFuncContext` then skip the 931 // interpreter, even though it's available, as `array_call` will be 932 // native code. 933 unsafe { 934 if me.as_ref().vmctx.as_non_null().as_ref().magic 935 == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC 936 { 937 return Self::array_call_native(me, caller, args_and_results); 938 } 939 vm.call( 940 me.as_ref().array_call.as_non_null().cast(), 941 me.as_ref().vmctx.as_non_null(), 942 caller, 943 args_and_results, 944 ) 945 } 946 } 947 948 #[inline] array_call_native( me: NonNull<VMFuncRef>, caller: NonNull<VMContext>, args_and_results: NonNull<[ValRaw]>, ) -> bool949 unsafe fn array_call_native( 950 me: NonNull<VMFuncRef>, 951 caller: NonNull<VMContext>, 952 args_and_results: NonNull<[ValRaw]>, 953 ) -> bool { 954 unsafe { 955 union GetNativePointer { 956 native: VMArrayCallNative, 957 ptr: NonNull<VMArrayCallFunction>, 958 } 959 let native = GetNativePointer { 960 ptr: me.as_ref().array_call.as_non_null(), 961 } 962 .native; 963 native( 964 me.as_ref().vmctx.as_non_null(), 965 caller, 966 args_and_results.cast(), 967 args_and_results.len(), 968 ) 969 } 970 } 971 } 972 973 #[cfg(test)] 974 mod test_vm_func_ref { 975 use super::VMFuncRef; 976 use core::mem::offset_of; 977 use std::mem::size_of; 978 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets}; 979 980 #[test] check_vm_func_ref_offsets()981 fn check_vm_func_ref_offsets() { 982 let module = Module::new(StaticModuleIndex::from_u32(0)); 983 let offsets = VMOffsets::new(HostPtr, &module); 984 assert_eq!( 985 size_of::<VMFuncRef>(), 986 usize::from(offsets.ptr.size_of_vm_func_ref()) 987 ); 988 assert_eq!( 989 offset_of!(VMFuncRef, array_call), 990 usize::from(offsets.ptr.vm_func_ref_array_call()) 991 ); 992 assert_eq!( 993 offset_of!(VMFuncRef, wasm_call), 994 usize::from(offsets.ptr.vm_func_ref_wasm_call()) 995 ); 996 assert_eq!( 997 offset_of!(VMFuncRef, type_index), 998 usize::from(offsets.ptr.vm_func_ref_type_index()) 999 ); 1000 assert_eq!( 1001 offset_of!(VMFuncRef, vmctx), 1002 usize::from(offsets.ptr.vm_func_ref_vmctx()) 1003 ); 1004 } 1005 } 1006 1007 macro_rules! define_builtin_array { 1008 ( 1009 $( 1010 $( #[$attr:meta] )* 1011 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; 1012 )* 1013 ) => { 1014 /// An array that stores addresses of builtin functions. We translate code 1015 /// to use indirect calls. This way, we don't have to patch the code. 1016 #[repr(C)] 1017 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")] 1018 pub struct VMBuiltinFunctionsArray { 1019 $( 1020 $name: unsafe extern "C" fn( 1021 $(define_builtin_array!(@ty $param)),* 1022 ) $( -> define_builtin_array!(@ty $result))?, 1023 )* 1024 } 1025 1026 impl VMBuiltinFunctionsArray { 1027 pub const INIT: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray { 1028 $( 1029 $name: crate::runtime::vm::libcalls::raw::$name, 1030 )* 1031 }; 1032 1033 /// Helper to call `expose_provenance()` on all contained pointers. 1034 /// 1035 /// This is required to be called at least once before entering wasm 1036 /// to inform the compiler that these function pointers may all be 1037 /// loaded/stored and used on the "other end" to reacquire 1038 /// provenance in Pulley. Pulley models hostcalls with a host 1039 /// pointer as the first parameter that's a function pointer under 1040 /// the hood, and this call ensures that the use of the function 1041 /// pointer is considered valid. 1042 pub fn expose_provenance(&self) -> NonNull<Self>{ 1043 $( 1044 (self.$name as *mut u8).expose_provenance(); 1045 )* 1046 NonNull::from(self) 1047 } 1048 } 1049 }; 1050 1051 (@ty u32) => (u32); 1052 (@ty u64) => (u64); 1053 (@ty f32) => (f32); 1054 (@ty f64) => (f64); 1055 (@ty u8) => (u8); 1056 (@ty i8x16) => (i8x16); 1057 (@ty f32x4) => (f32x4); 1058 (@ty f64x2) => (f64x2); 1059 (@ty bool) => (bool); 1060 (@ty pointer) => (*mut u8); 1061 (@ty size) => (usize); 1062 (@ty vmctx) => (NonNull<VMContext>); 1063 } 1064 1065 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 1066 unsafe impl VmSafe for VMBuiltinFunctionsArray {} 1067 1068 wasmtime_environ::foreach_builtin_function!(define_builtin_array); 1069 1070 const _: () = { 1071 assert!( 1072 mem::size_of::<VMBuiltinFunctionsArray>() 1073 == mem::size_of::<usize>() * (BuiltinFunctionIndex::len() as usize) 1074 ) 1075 }; 1076 1077 /// Structure that holds all mutable context that is shared across all instances 1078 /// in a store, for example data related to fuel or epochs. 1079 /// 1080 /// `VMStoreContext`s are one-to-one with `wasmtime::Store`s, the same way that 1081 /// `VMContext`s are one-to-one with `wasmtime::Instance`s. And the same way 1082 /// that multiple `wasmtime::Instance`s may be associated with the same 1083 /// `wasmtime::Store`, multiple `VMContext`s hold a pointer to the same 1084 /// `VMStoreContext` when they are associated with the same `wasmtime::Store`. 1085 #[derive(Debug)] 1086 #[repr(C)] 1087 pub struct VMStoreContext { 1088 // NB: 64-bit integer fields are located first with pointer-sized fields 1089 // trailing afterwards. That makes the offsets in this structure easier to 1090 // calculate on 32-bit platforms as we don't have to worry about the 1091 // alignment of 64-bit integers. 1092 // 1093 /// Indicator of how much fuel has been consumed and is remaining to 1094 /// WebAssembly. 1095 /// 1096 /// This field is typically negative and increments towards positive. Upon 1097 /// turning positive a wasm trap will be generated. This field is only 1098 /// modified if wasm is configured to consume fuel. 1099 pub fuel_consumed: UnsafeCell<i64>, 1100 1101 /// Deadline epoch for interruption: if epoch-based interruption 1102 /// is enabled and the global (per engine) epoch counter is 1103 /// observed to reach or exceed this value, the guest code will 1104 /// yield if running asynchronously. 1105 pub epoch_deadline: UnsafeCell<u64>, 1106 1107 /// The "store version". 1108 /// 1109 /// This is used to test whether stack-frame handles referring to 1110 /// suspended stack frames remain valid. 1111 /// 1112 /// The invariant that this upward-counting number must satisfy 1113 /// is: the number must be incremented whenever execution starts 1114 /// or resumes in the `Store` or when any stack is 1115 /// dropped/freed. That way, if we take a reference to some 1116 /// suspended stack frame and track the "version" at the time we 1117 /// took that reference, if the version still matches, we can be 1118 /// sure that nothing could have unwound the referenced Wasm 1119 /// frame. 1120 /// 1121 /// This version number is incremented in exactly one place: the 1122 /// Wasm-to-host trampolines, after return from host code. Note 1123 /// that this captures both the normal "return into Wasm" case 1124 /// (where Wasm frames can subsequently return normally and thus 1125 /// invalidate frames), and the "trap/exception unwinds Wasm 1126 /// frames" case, which is done internally via the `raise` libcall 1127 /// invoked after the main hostcall returns an error, and after we 1128 /// increment this version number. 1129 /// 1130 /// Note that this also handles the fiber/future-drop case because 1131 /// because we *always* return into the trampoline to clean up; 1132 /// that trampoline immediately raises an error and uses the 1133 /// longjmp-like unwind within Cranelift frames to skip over all 1134 /// the guest Wasm frames, but not before it increments the 1135 /// store's execution version number. 1136 /// 1137 /// This field is in use only if guest debugging is enabled. 1138 pub execution_version: u64, 1139 1140 /// Current stack limit of the wasm module. 1141 /// 1142 /// For more information see `crates/cranelift/src/lib.rs`. 1143 pub stack_limit: UnsafeCell<usize>, 1144 1145 /// The `VMMemoryDefinition` for this store's GC heap. 1146 pub gc_heap: VMMemoryDefinition, 1147 1148 /// The value of the frame pointer register in the trampoline used 1149 /// to call from Wasm to the host. 1150 /// 1151 /// Maintained by our Wasm-to-host trampoline, and cleared just 1152 /// before calling into Wasm in `catch_traps`. 1153 /// 1154 /// This member is `0` when Wasm is actively running and has not called out 1155 /// to the host. 1156 /// 1157 /// Used to find the start of a contiguous sequence of Wasm frames 1158 /// when walking the stack. Note that we record the FP of the 1159 /// *trampoline*'s frame, not the last Wasm frame, because we need 1160 /// to know the SP (bottom of frame) of the last Wasm frame as 1161 /// well in case we need to resume to an exception handler in that 1162 /// frame. The FP of the last Wasm frame can be recovered by 1163 /// loading the saved FP value at this FP address. 1164 pub last_wasm_exit_trampoline_fp: UnsafeCell<usize>, 1165 1166 /// The last Wasm program counter before we called from Wasm to the host. 1167 /// 1168 /// Maintained by our Wasm-to-host trampoline, and cleared just before 1169 /// calling into Wasm in `catch_traps`. 1170 /// 1171 /// This member is `0` when Wasm is actively running and has not called out 1172 /// to the host. 1173 /// 1174 /// Used when walking a contiguous sequence of Wasm frames. 1175 pub last_wasm_exit_pc: UnsafeCell<usize>, 1176 1177 /// The last host stack pointer before we called into Wasm from the host. 1178 /// 1179 /// Maintained by our host-to-Wasm trampoline. This member is `0` when Wasm 1180 /// is not running, and it's set to nonzero once a host-to-wasm trampoline 1181 /// is executed. 1182 /// 1183 /// When a host function is wrapped into a `wasmtime::Func`, and is then 1184 /// called from the host, then this member is not changed meaning that the 1185 /// previous activation in pointed to by `last_wasm_exit_trampoline_fp` is 1186 /// still the last wasm set of frames on the stack. 1187 /// 1188 /// This field is saved/restored during fiber suspension/resumption 1189 /// resumption as part of `CallThreadState::swap`. 1190 /// 1191 /// This field is used to find the end of a contiguous sequence of Wasm 1192 /// frames when walking the stack. Additionally it's used when a trap is 1193 /// raised as part of the set of parameters used to resume in the entry 1194 /// trampoline's "catch" block. 1195 pub last_wasm_entry_sp: UnsafeCell<usize>, 1196 1197 /// Same as `last_wasm_entry_sp`, but for the `fp` of the trampoline. 1198 pub last_wasm_entry_fp: UnsafeCell<usize>, 1199 1200 /// The last trap handler from a host-to-wasm entry trampoline on the stack. 1201 /// 1202 /// This field is configured when the host calls into wasm by the trampoline 1203 /// itself. It stores the `pc` of an exception handler suitable to handle 1204 /// all traps (or uncaught exceptions). 1205 pub last_wasm_entry_trap_handler: UnsafeCell<usize>, 1206 1207 /// Stack information used by stack switching instructions. See documentation 1208 /// on `VMStackChain` for details. 1209 pub stack_chain: UnsafeCell<VMStackChain>, 1210 1211 /// A pointer to the embedder's `T` inside a `Store<T>`, for use with the 1212 /// `store-data-address` unsafe intrinsic. 1213 pub store_data: VmPtr<()>, 1214 1215 /// The range, in addresses, of the guard page that is currently in use. 1216 /// 1217 /// This field is used when signal handlers are run to determine whether a 1218 /// faulting address lies within the guard page of an async stack for 1219 /// example. If this happens then the signal handler aborts with a stack 1220 /// overflow message similar to what would happen had the stack overflow 1221 /// happened on the main thread. This field is, by default a null..null 1222 /// range indicating that no async guard is in use (aka no fiber). In such a 1223 /// situation while this field is read it'll never classify a fault as an 1224 /// guard page fault. 1225 pub async_guard_range: Range<*mut u8>, 1226 } 1227 1228 impl VMStoreContext { 1229 /// From the current saved trampoline FP, get the FP of the last 1230 /// Wasm frame. If the current saved trampoline FP is null, return 1231 /// null. 1232 /// 1233 /// We store only the trampoline FP, because (i) we need the 1234 /// trampoline FP, so we know the size (bottom) of the last Wasm 1235 /// frame; and (ii) the last Wasm frame, just above the trampoline 1236 /// frame, can be recovered via the FP chain. 1237 /// 1238 /// # Safety 1239 /// 1240 /// This function requires that the `last_wasm_exit_trampoline_fp` 1241 /// field either points to an active trampoline frame or is a null 1242 /// pointer. last_wasm_exit_fp(&self) -> usize1243 pub(crate) unsafe fn last_wasm_exit_fp(&self) -> usize { 1244 // SAFETY: the unsafe cell is safe to load (no other threads 1245 // will be writing our store when we have control), and the 1246 // helper function's safety condition is the same as ours. 1247 unsafe { 1248 let trampoline_fp = *self.last_wasm_exit_trampoline_fp.get(); 1249 Self::wasm_exit_fp_from_trampoline_fp(trampoline_fp) 1250 } 1251 } 1252 1253 /// From any saved trampoline FP, get the FP of the last Wasm 1254 /// frame. If the given trampoline FP is null, return null. 1255 /// 1256 /// This differs from `last_wasm_exit_fp()` above in that it 1257 /// allows accessing activations further up the stack as well, 1258 /// e.g. via `CallThreadState::old_state`. 1259 /// 1260 /// # Safety 1261 /// 1262 /// This function requires that the provided FP value is valid, 1263 /// and points to an active trampoline frame, or is null. 1264 /// 1265 /// This function depends on the invariant that on all supported 1266 /// architectures, we store the previous FP value under the 1267 /// current FP. This is a property of our ABI that we control and 1268 /// ensure. wasm_exit_fp_from_trampoline_fp(trampoline_fp: usize) -> usize1269 pub(crate) unsafe fn wasm_exit_fp_from_trampoline_fp(trampoline_fp: usize) -> usize { 1270 if trampoline_fp != 0 { 1271 // SAFETY: We require that trampoline_fp points to a valid 1272 // frame, which will (by definition) contain an old FP value 1273 // that we can load. 1274 unsafe { *(trampoline_fp as *const usize) } 1275 } else { 1276 0 1277 } 1278 } 1279 } 1280 1281 // The `VMStoreContext` type is a pod-type with no destructor, and we don't 1282 // access any fields from other threads, so add in these trait impls which are 1283 // otherwise not available due to the `fuel_consumed` and `epoch_deadline` 1284 // variables in `VMStoreContext`. 1285 unsafe impl Send for VMStoreContext {} 1286 unsafe impl Sync for VMStoreContext {} 1287 1288 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 1289 unsafe impl VmSafe for VMStoreContext {} 1290 1291 impl Default for VMStoreContext { default() -> VMStoreContext1292 fn default() -> VMStoreContext { 1293 VMStoreContext { 1294 fuel_consumed: UnsafeCell::new(0), 1295 epoch_deadline: UnsafeCell::new(0), 1296 execution_version: 0, 1297 stack_limit: UnsafeCell::new(usize::max_value()), 1298 gc_heap: VMMemoryDefinition { 1299 base: NonNull::dangling().into(), 1300 current_length: AtomicUsize::new(0), 1301 }, 1302 last_wasm_exit_trampoline_fp: UnsafeCell::new(0), 1303 last_wasm_exit_pc: UnsafeCell::new(0), 1304 last_wasm_entry_fp: UnsafeCell::new(0), 1305 last_wasm_entry_sp: UnsafeCell::new(0), 1306 last_wasm_entry_trap_handler: UnsafeCell::new(0), 1307 stack_chain: UnsafeCell::new(VMStackChain::Absent), 1308 async_guard_range: ptr::null_mut()..ptr::null_mut(), 1309 store_data: VmPtr::dangling(), 1310 } 1311 } 1312 } 1313 1314 #[cfg(test)] 1315 mod test_vmstore_context { 1316 use super::{VMMemoryDefinition, VMStoreContext}; 1317 use core::mem::offset_of; 1318 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets}; 1319 1320 #[test] field_offsets()1321 fn field_offsets() { 1322 let module = Module::new(StaticModuleIndex::from_u32(0)); 1323 let offsets = VMOffsets::new(HostPtr, &module); 1324 assert_eq!( 1325 offset_of!(VMStoreContext, stack_limit), 1326 usize::from(offsets.ptr.vmstore_context_stack_limit()) 1327 ); 1328 assert_eq!( 1329 offset_of!(VMStoreContext, fuel_consumed), 1330 usize::from(offsets.ptr.vmstore_context_fuel_consumed()) 1331 ); 1332 assert_eq!( 1333 offset_of!(VMStoreContext, epoch_deadline), 1334 usize::from(offsets.ptr.vmstore_context_epoch_deadline()) 1335 ); 1336 assert_eq!( 1337 offset_of!(VMStoreContext, execution_version), 1338 usize::from(offsets.ptr.vmstore_context_execution_version()) 1339 ); 1340 assert_eq!( 1341 offset_of!(VMStoreContext, gc_heap), 1342 usize::from(offsets.ptr.vmstore_context_gc_heap()) 1343 ); 1344 assert_eq!( 1345 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, base), 1346 usize::from(offsets.ptr.vmstore_context_gc_heap_base()) 1347 ); 1348 assert_eq!( 1349 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, current_length), 1350 usize::from(offsets.ptr.vmstore_context_gc_heap_current_length()) 1351 ); 1352 assert_eq!( 1353 offset_of!(VMStoreContext, last_wasm_exit_trampoline_fp), 1354 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_trampoline_fp()) 1355 ); 1356 assert_eq!( 1357 offset_of!(VMStoreContext, last_wasm_exit_pc), 1358 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_pc()) 1359 ); 1360 assert_eq!( 1361 offset_of!(VMStoreContext, last_wasm_entry_fp), 1362 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_fp()) 1363 ); 1364 assert_eq!( 1365 offset_of!(VMStoreContext, last_wasm_entry_sp), 1366 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_sp()) 1367 ); 1368 assert_eq!( 1369 offset_of!(VMStoreContext, last_wasm_entry_trap_handler), 1370 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_trap_handler()) 1371 ); 1372 assert_eq!( 1373 offset_of!(VMStoreContext, stack_chain), 1374 usize::from(offsets.ptr.vmstore_context_stack_chain()) 1375 ); 1376 assert_eq!( 1377 offset_of!(VMStoreContext, store_data), 1378 usize::from(offsets.ptr.vmstore_context_store_data()) 1379 ); 1380 } 1381 } 1382 1383 /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. 1384 /// This has information about globals, memories, tables, and other runtime 1385 /// state associated with the current instance. 1386 /// 1387 /// The struct here is empty, as the sizes of these fields are dynamic, and 1388 /// we can't describe them in Rust's type system. Sufficient memory is 1389 /// allocated at runtime. 1390 #[derive(Debug)] 1391 #[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside 1392 pub struct VMContext { 1393 _magic: u32, 1394 } 1395 1396 impl VMContext { 1397 /// Helper function to cast between context types using a debug assertion to 1398 /// protect against some mistakes. 1399 #[inline] from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMContext>1400 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMContext> { 1401 // Note that in general the offset of the "magic" field is stored in 1402 // `VMOffsets::vmctx_magic`. Given though that this is a sanity check 1403 // about converting this pointer to another type we ideally don't want 1404 // to read the offset from potentially corrupt memory. Instead it would 1405 // be better to catch errors here as soon as possible. 1406 // 1407 // To accomplish this the `VMContext` structure is laid out with the 1408 // magic field at a statically known offset (here it's 0 for now). This 1409 // static offset is asserted in `VMOffsets::from` and needs to be kept 1410 // in sync with this line for this debug assertion to work. 1411 // 1412 // Also note that this magic is only ever invalid in the presence of 1413 // bugs, meaning we don't actually read the magic and act differently 1414 // at runtime depending what it is, so this is a debug assertion as 1415 // opposed to a regular assertion. 1416 unsafe { 1417 debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC); 1418 } 1419 opaque.cast() 1420 } 1421 } 1422 1423 /// A "raw" and unsafe representation of a WebAssembly value. 1424 /// 1425 /// This is provided for use with the `Func::new_unchecked` and 1426 /// `Func::call_unchecked` APIs. In general it's unlikely you should be using 1427 /// this from Rust, rather using APIs like `Func::wrap` and `TypedFunc::call`. 1428 /// 1429 /// This is notably an "unsafe" way to work with `Val` and it's recommended to 1430 /// instead use `Val` where possible. An important note about this union is that 1431 /// fields are all stored in little-endian format, regardless of the endianness 1432 /// of the host system. 1433 #[repr(C)] 1434 #[derive(Copy, Clone)] 1435 pub union ValRaw { 1436 /// A WebAssembly `i32` value. 1437 /// 1438 /// Note that the payload here is a Rust `i32` but the WebAssembly `i32` 1439 /// type does not assign an interpretation of the upper bit as either signed 1440 /// or unsigned. The Rust type `i32` is simply chosen for convenience. 1441 /// 1442 /// This value is always stored in a little-endian format. 1443 i32: i32, 1444 1445 /// A WebAssembly `i64` value. 1446 /// 1447 /// Note that the payload here is a Rust `i64` but the WebAssembly `i64` 1448 /// type does not assign an interpretation of the upper bit as either signed 1449 /// or unsigned. The Rust type `i64` is simply chosen for convenience. 1450 /// 1451 /// This value is always stored in a little-endian format. 1452 i64: i64, 1453 1454 /// A WebAssembly `f32` value. 1455 /// 1456 /// Note that the payload here is a Rust `u32`. This is to allow passing any 1457 /// representation of NaN into WebAssembly without risk of changing NaN 1458 /// payload bits as its gets passed around the system. Otherwise though this 1459 /// `u32` value is the return value of `f32::to_bits` in Rust. 1460 /// 1461 /// This value is always stored in a little-endian format. 1462 f32: u32, 1463 1464 /// A WebAssembly `f64` value. 1465 /// 1466 /// Note that the payload here is a Rust `u64`. This is to allow passing any 1467 /// representation of NaN into WebAssembly without risk of changing NaN 1468 /// payload bits as its gets passed around the system. Otherwise though this 1469 /// `u64` value is the return value of `f64::to_bits` in Rust. 1470 /// 1471 /// This value is always stored in a little-endian format. 1472 f64: u64, 1473 1474 /// A WebAssembly `v128` value. 1475 /// 1476 /// The payload here is a Rust `[u8; 16]` which has the same number of bits 1477 /// but note that `v128` in WebAssembly is often considered a vector type 1478 /// such as `i32x4` or `f64x2`. This means that the actual interpretation 1479 /// of the underlying bits is left up to the instructions which consume 1480 /// this value. 1481 /// 1482 /// This value is always stored in a little-endian format. 1483 v128: [u8; 16], 1484 1485 /// A WebAssembly `funcref` value (or one of its subtypes). 1486 /// 1487 /// The payload here is a pointer which is runtime-defined. This is one of 1488 /// the main points of unsafety about the `ValRaw` type as the validity of 1489 /// the pointer here is not easily verified and must be preserved by 1490 /// carefully calling the correct functions throughout the runtime. 1491 /// 1492 /// This value is always stored in a little-endian format. 1493 funcref: *mut c_void, 1494 1495 /// A WebAssembly `externref` value (or one of its subtypes). 1496 /// 1497 /// The payload here is a compressed pointer value which is 1498 /// runtime-defined. This is one of the main points of unsafety about the 1499 /// `ValRaw` type as the validity of the pointer here is not easily verified 1500 /// and must be preserved by carefully calling the correct functions 1501 /// throughout the runtime. 1502 /// 1503 /// This value is always stored in a little-endian format. 1504 externref: u32, 1505 1506 /// A WebAssembly `anyref` value (or one of its subtypes). 1507 /// 1508 /// The payload here is a compressed pointer value which is 1509 /// runtime-defined. This is one of the main points of unsafety about the 1510 /// `ValRaw` type as the validity of the pointer here is not easily verified 1511 /// and must be preserved by carefully calling the correct functions 1512 /// throughout the runtime. 1513 /// 1514 /// This value is always stored in a little-endian format. 1515 anyref: u32, 1516 1517 /// A WebAssembly `exnref` value (or one of its subtypes). 1518 /// 1519 /// The payload here is a compressed pointer value which is 1520 /// runtime-defined. This is one of the main points of unsafety about the 1521 /// `ValRaw` type as the validity of the pointer here is not easily verified 1522 /// and must be preserved by carefully calling the correct functions 1523 /// throughout the runtime. 1524 /// 1525 /// This value is always stored in a little-endian format. 1526 exnref: u32, 1527 } 1528 1529 // The `ValRaw` type is matched as `wasmtime_val_raw_t` in the C API so these 1530 // are some simple assertions about the shape of the type which are additionally 1531 // matched in C. 1532 const _: () = { 1533 assert!(mem::size_of::<ValRaw>() == 16); 1534 assert!(mem::align_of::<ValRaw>() == mem::align_of::<u64>()); 1535 }; 1536 1537 // This type is just a bag-of-bits so it's up to the caller to figure out how 1538 // to safely deal with threading concerns and safely access interior bits. 1539 unsafe impl Send for ValRaw {} 1540 unsafe impl Sync for ValRaw {} 1541 1542 impl fmt::Debug for ValRaw { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1543 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1544 struct Hex<T>(T); 1545 impl<T: fmt::LowerHex> fmt::Debug for Hex<T> { 1546 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1547 let bytes = mem::size_of::<T>(); 1548 let hex_digits_per_byte = 2; 1549 let hex_digits = bytes * hex_digits_per_byte; 1550 write!(f, "0x{:0width$x}", self.0, width = hex_digits) 1551 } 1552 } 1553 1554 unsafe { 1555 f.debug_struct("ValRaw") 1556 .field("i32", &Hex(self.i32)) 1557 .field("i64", &Hex(self.i64)) 1558 .field("f32", &Hex(self.f32)) 1559 .field("f64", &Hex(self.f64)) 1560 .field("v128", &Hex(u128::from_le_bytes(self.v128))) 1561 .field("funcref", &self.funcref) 1562 .field("externref", &Hex(self.externref)) 1563 .field("anyref", &Hex(self.anyref)) 1564 .field("exnref", &Hex(self.exnref)) 1565 .finish() 1566 } 1567 } 1568 } 1569 1570 impl ValRaw { 1571 /// Create a null reference that is compatible with any of 1572 /// `{any,extern,func,exn}ref`. null() -> ValRaw1573 pub fn null() -> ValRaw { 1574 unsafe { 1575 let raw = mem::MaybeUninit::<Self>::zeroed().assume_init(); 1576 debug_assert_eq!(raw.get_anyref(), 0); 1577 debug_assert_eq!(raw.get_exnref(), 0); 1578 debug_assert_eq!(raw.get_externref(), 0); 1579 debug_assert_eq!(raw.get_funcref(), ptr::null_mut()); 1580 raw 1581 } 1582 } 1583 1584 /// Creates a WebAssembly `i32` value 1585 #[inline] i32(i: i32) -> ValRaw1586 pub fn i32(i: i32) -> ValRaw { 1587 // Note that this is intentionally not setting the `i32` field, instead 1588 // setting the `i64` field with a zero-extended version of `i`. For more 1589 // information on this see the comments on `Lower for Result` in the 1590 // `wasmtime` crate. Otherwise though all `ValRaw` constructors are 1591 // otherwise constrained to guarantee that the initial 64-bits are 1592 // always initialized. 1593 ValRaw::u64(i.cast_unsigned().into()) 1594 } 1595 1596 /// Creates a WebAssembly `i64` value 1597 #[inline] i64(i: i64) -> ValRaw1598 pub fn i64(i: i64) -> ValRaw { 1599 ValRaw { i64: i.to_le() } 1600 } 1601 1602 /// Creates a WebAssembly `i32` value 1603 #[inline] u32(i: u32) -> ValRaw1604 pub fn u32(i: u32) -> ValRaw { 1605 // See comments in `ValRaw::i32` for why this is setting the upper 1606 // 32-bits as well. 1607 ValRaw::u64(i.into()) 1608 } 1609 1610 /// Creates a WebAssembly `i64` value 1611 #[inline] u64(i: u64) -> ValRaw1612 pub fn u64(i: u64) -> ValRaw { 1613 ValRaw::i64(i as i64) 1614 } 1615 1616 /// Creates a WebAssembly `f32` value 1617 #[inline] f32(i: u32) -> ValRaw1618 pub fn f32(i: u32) -> ValRaw { 1619 // See comments in `ValRaw::i32` for why this is setting the upper 1620 // 32-bits as well. 1621 ValRaw::u64(i.into()) 1622 } 1623 1624 /// Creates a WebAssembly `f64` value 1625 #[inline] f64(i: u64) -> ValRaw1626 pub fn f64(i: u64) -> ValRaw { 1627 ValRaw { f64: i.to_le() } 1628 } 1629 1630 /// Creates a WebAssembly `v128` value 1631 #[inline] v128(i: u128) -> ValRaw1632 pub fn v128(i: u128) -> ValRaw { 1633 ValRaw { 1634 v128: i.to_le_bytes(), 1635 } 1636 } 1637 1638 /// Creates a WebAssembly `funcref` value 1639 #[inline] funcref(i: *mut c_void) -> ValRaw1640 pub fn funcref(i: *mut c_void) -> ValRaw { 1641 ValRaw { 1642 funcref: i.map_addr(|i| i.to_le()), 1643 } 1644 } 1645 1646 /// Creates a WebAssembly `externref` value 1647 #[inline] externref(e: u32) -> ValRaw1648 pub fn externref(e: u32) -> ValRaw { 1649 assert!(cfg!(feature = "gc") || e == 0); 1650 ValRaw { 1651 externref: e.to_le(), 1652 } 1653 } 1654 1655 /// Creates a WebAssembly `anyref` value 1656 #[inline] anyref(r: u32) -> ValRaw1657 pub fn anyref(r: u32) -> ValRaw { 1658 assert!(cfg!(feature = "gc") || r == 0); 1659 ValRaw { anyref: r.to_le() } 1660 } 1661 1662 /// Creates a WebAssembly `exnref` value 1663 #[inline] exnref(r: u32) -> ValRaw1664 pub fn exnref(r: u32) -> ValRaw { 1665 assert!(cfg!(feature = "gc") || r == 0); 1666 ValRaw { exnref: r.to_le() } 1667 } 1668 1669 /// Gets the WebAssembly `i32` value 1670 #[inline] get_i32(&self) -> i321671 pub fn get_i32(&self) -> i32 { 1672 unsafe { i32::from_le(self.i32) } 1673 } 1674 1675 /// Gets the WebAssembly `i64` value 1676 #[inline] get_i64(&self) -> i641677 pub fn get_i64(&self) -> i64 { 1678 unsafe { i64::from_le(self.i64) } 1679 } 1680 1681 /// Gets the WebAssembly `i32` value 1682 #[inline] get_u32(&self) -> u321683 pub fn get_u32(&self) -> u32 { 1684 self.get_i32().cast_unsigned() 1685 } 1686 1687 /// Gets the WebAssembly `i64` value 1688 #[inline] get_u64(&self) -> u641689 pub fn get_u64(&self) -> u64 { 1690 self.get_i64().cast_unsigned() 1691 } 1692 1693 /// Gets the WebAssembly `f32` value 1694 #[inline] get_f32(&self) -> u321695 pub fn get_f32(&self) -> u32 { 1696 unsafe { u32::from_le(self.f32) } 1697 } 1698 1699 /// Gets the WebAssembly `f64` value 1700 #[inline] get_f64(&self) -> u641701 pub fn get_f64(&self) -> u64 { 1702 unsafe { u64::from_le(self.f64) } 1703 } 1704 1705 /// Gets the WebAssembly `v128` value 1706 #[inline] get_v128(&self) -> u1281707 pub fn get_v128(&self) -> u128 { 1708 unsafe { u128::from_le_bytes(self.v128) } 1709 } 1710 1711 /// Gets the WebAssembly `funcref` value 1712 #[inline] get_funcref(&self) -> *mut c_void1713 pub fn get_funcref(&self) -> *mut c_void { 1714 let addr = unsafe { usize::from_le(self.funcref.addr()) }; 1715 core::ptr::with_exposed_provenance_mut(addr) 1716 } 1717 1718 /// Gets the WebAssembly `externref` value 1719 #[inline] get_externref(&self) -> u321720 pub fn get_externref(&self) -> u32 { 1721 let externref = u32::from_le(unsafe { self.externref }); 1722 assert!(cfg!(feature = "gc") || externref == 0); 1723 externref 1724 } 1725 1726 /// Gets the WebAssembly `anyref` value 1727 #[inline] get_anyref(&self) -> u321728 pub fn get_anyref(&self) -> u32 { 1729 let anyref = u32::from_le(unsafe { self.anyref }); 1730 assert!(cfg!(feature = "gc") || anyref == 0); 1731 anyref 1732 } 1733 1734 /// Gets the WebAssembly `exnref` value 1735 #[inline] get_exnref(&self) -> u321736 pub fn get_exnref(&self) -> u32 { 1737 let exnref = u32::from_le(unsafe { self.exnref }); 1738 assert!(cfg!(feature = "gc") || exnref == 0); 1739 exnref 1740 } 1741 } 1742 1743 /// An "opaque" version of `VMContext` which must be explicitly casted to a 1744 /// target context. 1745 /// 1746 /// This context is used to represent that contexts specified in 1747 /// `VMFuncRef` can have any type and don't have an implicit 1748 /// structure. Neither wasmtime nor cranelift-generated code can rely on the 1749 /// structure of an opaque context in general and only the code which configured 1750 /// the context is able to rely on a particular structure. This is because the 1751 /// context pointer configured for `VMFuncRef` is guaranteed to be 1752 /// the first parameter passed. 1753 /// 1754 /// Note that Wasmtime currently has a layout where all contexts that are casted 1755 /// to an opaque context start with a 32-bit "magic" which can be used in debug 1756 /// mode to debug-assert that the casts here are correct and have at least a 1757 /// little protection against incorrect casts. 1758 pub struct VMOpaqueContext { 1759 pub(crate) magic: u32, 1760 _marker: marker::PhantomPinned, 1761 } 1762 1763 impl VMOpaqueContext { 1764 /// Helper function to clearly indicate that casts are desired. 1765 #[inline] from_vmcontext(ptr: NonNull<VMContext>) -> NonNull<VMOpaqueContext>1766 pub fn from_vmcontext(ptr: NonNull<VMContext>) -> NonNull<VMOpaqueContext> { 1767 ptr.cast() 1768 } 1769 1770 /// Helper function to clearly indicate that casts are desired. 1771 #[inline] from_vm_array_call_host_func_context( ptr: NonNull<VMArrayCallHostFuncContext>, ) -> NonNull<VMOpaqueContext>1772 pub fn from_vm_array_call_host_func_context( 1773 ptr: NonNull<VMArrayCallHostFuncContext>, 1774 ) -> NonNull<VMOpaqueContext> { 1775 ptr.cast() 1776 } 1777 } 1778