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