1 //! Runtime support for the component model in Wasmtime 2 //! 3 //! Currently this runtime support includes a `VMComponentContext` which is 4 //! similar in purpose to `VMContext`. The context is read from 5 //! cranelift-generated trampolines when entering the host from a wasm module. 6 //! Eventually it's intended that module-to-module calls, which would be 7 //! cranelift-compiled adapters, will use this `VMComponentContext` as well. 8 9 use crate::component::{Component, Instance, InstancePre, ResourceType, RuntimeImport}; 10 use crate::runtime::component::ComponentInstanceId; 11 use crate::runtime::vm::{ 12 Export, ExportFunction, ExportGlobal, ExportGlobalKind, SendSyncPtr, VMArrayCallFunction, 13 VMContext, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition, VMOpaqueContext, VMStore, 14 VMStoreRawPtr, VMTableDefinition, VMTableImport, VMWasmCallFunction, ValRaw, VmPtr, VmSafe, 15 }; 16 use crate::store::{InstanceId, StoreOpaque}; 17 use alloc::alloc::Layout; 18 use alloc::sync::Arc; 19 use core::marker; 20 use core::mem; 21 use core::mem::offset_of; 22 use core::ops::Deref; 23 use core::ptr::{self, NonNull}; 24 use wasmtime_environ::component::*; 25 use wasmtime_environ::{ 26 DefinedTableIndex, EntityIndex, Global, HostPtr, PrimaryMap, VMSharedTypeIndex, WasmValType, 27 }; 28 29 #[allow(clippy::cast_possible_truncation)] // it's intended this is truncated on 30 // 32-bit platforms 31 const INVALID_PTR: usize = 0xdead_dead_beef_beef_u64 as usize; 32 33 mod libcalls; 34 mod resources; 35 36 pub use self::resources::{ 37 CallContexts, ResourceTable, ResourceTables, TypedResource, TypedResourceIndex, 38 }; 39 40 /// Runtime representation of a component instance and all state necessary for 41 /// the instance itself. 42 /// 43 /// This type never exists by-value, but rather it's always behind a pointer. 44 /// The size of the allocation for `ComponentInstance` includes the trailing 45 /// `VMComponentContext` which is variably sized based on the `offsets` 46 /// contained within. 47 #[repr(C)] 48 pub struct ComponentInstance { 49 /// The index within the store of where to find this component instance. 50 id: ComponentInstanceId, 51 52 /// Size and offset information for the trailing `VMComponentContext`. 53 offsets: VMComponentOffsets<HostPtr>, 54 55 /// For more information about this see the documentation on 56 /// `Instance::vmctx_self_reference`. 57 vmctx_self_reference: SendSyncPtr<VMComponentContext>, 58 59 /// The component that this instance was created from. 60 // 61 // NB: in the future if necessary it would be possible to avoid storing an 62 // entire `Component` here and instead storing only information such as: 63 // 64 // * Some reference to `Arc<ComponentTypes>` 65 // * Necessary references to closed-over modules which are exported from the 66 // component itself. 67 // 68 // Otherwise the full guts of this component should only ever be used during 69 // the instantiation of this instance, meaning that after instantiation much 70 // of the component can be thrown away (theoretically). 71 component: Component, 72 73 /// State of resources for this component. 74 /// 75 /// This is paired with other information to create a `ResourceTables` which 76 /// is how this field is manipulated. 77 instance_resource_tables: PrimaryMap<RuntimeComponentInstanceIndex, ResourceTable>, 78 79 /// What all compile-time-identified core instances are mapped to within the 80 /// `Store` that this component belongs to. 81 instances: PrimaryMap<RuntimeInstanceIndex, InstanceId>, 82 83 /// Storage for the type information about resources within this component 84 /// instance. 85 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 86 87 /// Arguments that this instance used to be instantiated. 88 /// 89 /// Strong references are stored to these arguments since pointers are saved 90 /// into the structures such as functions within the 91 /// `OwnedComponentInstance` but it's our job to keep them alive. 92 /// 93 /// One purpose of this storage is to enable embedders to drop a `Linker`, 94 /// for example, after a component is instantiated. In that situation if the 95 /// arguments weren't held here then they might be dropped, and structures 96 /// such as `.lowering()` which point back into the original function would 97 /// become stale and use-after-free conditions when used. By preserving the 98 /// entire list here though we're guaranteed that nothing is lost for the 99 /// duration of the lifetime of this instance. 100 imports: Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 101 102 /// Self-pointer back to `Store<T>` and its functions. 103 store: VMStoreRawPtr, 104 105 /// Cached ABI return value from the last-invoked function call along with 106 /// the function index that was invoked. 107 /// 108 /// Used in `post_return_arg_set` and `post_return_arg_take` below. 109 post_return_arg: Option<(ExportIndex, ValRaw)>, 110 111 /// A zero-sized field which represents the end of the struct for the actual 112 /// `VMComponentContext` to be allocated behind. 113 vmctx: VMComponentContext, 114 } 115 116 /// Type signature for host-defined trampolines that are called from 117 /// WebAssembly. 118 /// 119 /// This function signature is invoked from a cranelift-compiled trampoline that 120 /// adapts from the core wasm System-V ABI into the ABI provided here: 121 /// 122 /// * `vmctx` - this is the first argument to the wasm import, and should always 123 /// end up being a `VMComponentContext`. 124 /// * `data` - this is the data pointer associated with the `VMLowering` for 125 /// which this function pointer was registered. 126 /// * `ty` - the type index, relative to the tables in `vmctx`, that is the 127 /// type of the function being called. 128 /// * `caller_instance` - the `RuntimeComponentInstanceIndex` representing the 129 /// caller component instance, used to track the owner of an async host task. 130 /// * `flags` - the component flags for may_enter/leave corresponding to the 131 /// component instance that the lowering happened within. 132 /// * `opt_memory` - this nullable pointer represents the memory configuration 133 /// option for the canonical ABI options. 134 /// * `opt_realloc` - this nullable pointer represents the realloc configuration 135 /// option for the canonical ABI options. 136 /// * `string_encoding` - this is the configured string encoding for the 137 /// canonical ABI this lowering corresponds to. 138 /// * `async_` - whether the caller is using the async ABI. 139 /// * `args_and_results` - pointer to stack-allocated space in the caller where 140 /// all the arguments are stored as well as where the results will be written 141 /// to. The size and initialized bytes of this depends on the core wasm type 142 /// signature that this callee corresponds to. 143 /// * `nargs_and_results` - the size, in units of `ValRaw`, of 144 /// `args_and_results`. 145 /// 146 /// This function returns a `bool` which indicates whether the call succeeded 147 /// or not. On failure this function records trap information in TLS which 148 /// should be suitable for reading later. 149 // 150 // FIXME: 11 arguments is probably too many. The `data` through `string-encoding` 151 // parameters should probably get packaged up into the `VMComponentContext`. 152 // Needs benchmarking one way or another though to figure out what the best 153 // balance is here. 154 pub type VMLoweringCallee = extern "C" fn( 155 vmctx: NonNull<VMOpaqueContext>, 156 data: NonNull<u8>, 157 ty: u32, 158 caller_instance: u32, 159 flags: NonNull<VMGlobalDefinition>, 160 opt_memory: *mut VMMemoryDefinition, 161 opt_realloc: *mut VMFuncRef, 162 string_encoding: u8, 163 async_: u8, 164 args_and_results: NonNull<mem::MaybeUninit<ValRaw>>, 165 nargs_and_results: usize, 166 ) -> bool; 167 168 /// Structure describing a lowered host function stored within a 169 /// `VMComponentContext` per-lowering. 170 #[derive(Copy, Clone)] 171 #[repr(C)] 172 pub struct VMLowering { 173 /// The host function pointer that is invoked when this lowering is 174 /// invoked. 175 pub callee: VMLoweringCallee, 176 /// The host data pointer (think void* pointer) to get passed to `callee`. 177 pub data: VmPtr<u8>, 178 } 179 180 // SAFETY: the above structure is repr(C) and only contains `VmSafe` fields. 181 unsafe impl VmSafe for VMLowering {} 182 183 /// This is a marker type to represent the underlying allocation of a 184 /// `VMComponentContext`. 185 /// 186 /// This type is similar to `VMContext` for core wasm and is allocated once per 187 /// component instance in Wasmtime. While the static size of this type is 0 the 188 /// actual runtime size is variable depending on the shape of the component that 189 /// this corresponds to. This structure always trails a `ComponentInstance` 190 /// allocation and the allocation/lifetime of this allocation is managed by 191 /// `ComponentInstance`. 192 #[repr(C)] 193 // Set an appropriate alignment for this structure where the most-aligned value 194 // internally right now `VMGlobalDefinition` which has an alignment of 16 bytes. 195 #[repr(align(16))] 196 pub struct VMComponentContext { 197 /// For more information about this see the equivalent field in `VMContext` 198 _marker: marker::PhantomPinned, 199 } 200 201 impl ComponentInstance { 202 /// Converts the `vmctx` provided into a `ComponentInstance` and runs the 203 /// provided closure with that instance. 204 /// 205 /// # Unsafety 206 /// 207 /// This is `unsafe` because `vmctx` cannot be guaranteed to be a valid 208 /// pointer and it cannot be proven statically that it's safe to get a 209 /// mutable reference at this time to the instance from `vmctx`. 210 pub unsafe fn from_vmctx<R>( 211 vmctx: NonNull<VMComponentContext>, 212 f: impl FnOnce(&mut dyn VMStore, Instance) -> R, 213 ) -> R { 214 let mut ptr = vmctx 215 .byte_sub(mem::size_of::<ComponentInstance>()) 216 .cast::<ComponentInstance>(); 217 let reference = ptr.as_mut(); 218 let store = &mut *reference.store.0.as_ptr(); 219 let instance = Instance::from_wasmtime(store, reference.id); 220 f(store, instance) 221 } 222 223 /// Returns the layout corresponding to what would be an allocation of a 224 /// `ComponentInstance` for the `offsets` provided. 225 /// 226 /// The returned layout has space for both the `ComponentInstance` and the 227 /// trailing `VMComponentContext`. 228 fn alloc_layout(offsets: &VMComponentOffsets<HostPtr>) -> Layout { 229 let size = mem::size_of::<Self>() 230 .checked_add(usize::try_from(offsets.size_of_vmctx()).unwrap()) 231 .unwrap(); 232 let align = mem::align_of::<Self>(); 233 Layout::from_size_align(size, align).unwrap() 234 } 235 236 /// Initializes an uninitialized pointer to a `ComponentInstance` in 237 /// addition to its trailing `VMComponentContext`. 238 /// 239 /// The `ptr` provided must be valid for `alloc_size` bytes and will be 240 /// entirely overwritten by this function call. The `offsets` correspond to 241 /// the shape of the component being instantiated and `store` is a pointer 242 /// back to the Wasmtime store for host functions to have access to. 243 unsafe fn new_at( 244 ptr: NonNull<ComponentInstance>, 245 alloc_size: usize, 246 offsets: VMComponentOffsets<HostPtr>, 247 id: ComponentInstanceId, 248 component: &Component, 249 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 250 imports: &Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 251 store: NonNull<dyn VMStore>, 252 ) { 253 assert!(alloc_size >= Self::alloc_layout(&offsets).size()); 254 255 let num_instances = component.env_component().num_runtime_component_instances; 256 let mut instance_resource_tables = 257 PrimaryMap::with_capacity(num_instances.try_into().unwrap()); 258 for _ in 0..num_instances { 259 instance_resource_tables.push(ResourceTable::default()); 260 } 261 262 ptr::write( 263 ptr.as_ptr(), 264 ComponentInstance { 265 id, 266 offsets, 267 vmctx_self_reference: SendSyncPtr::new( 268 NonNull::new( 269 ptr.as_ptr() 270 .byte_add(mem::size_of::<ComponentInstance>()) 271 .cast(), 272 ) 273 .unwrap(), 274 ), 275 instance_resource_tables, 276 instances: PrimaryMap::with_capacity( 277 component 278 .env_component() 279 .num_runtime_instances 280 .try_into() 281 .unwrap(), 282 ), 283 component: component.clone(), 284 resource_types, 285 imports: imports.clone(), 286 store: VMStoreRawPtr(store), 287 post_return_arg: None, 288 vmctx: VMComponentContext { 289 _marker: marker::PhantomPinned, 290 }, 291 }, 292 ); 293 294 (*ptr.as_ptr()).initialize_vmctx(); 295 } 296 297 pub fn vmctx(&self) -> NonNull<VMComponentContext> { 298 let addr = &raw const self.vmctx; 299 let ret = self.vmctx_self_reference.as_ptr().with_addr(addr.addr()); 300 NonNull::new(ret).unwrap() 301 } 302 303 unsafe fn vmctx_plus_offset<T: VmSafe>(&self, offset: u32) -> *const T { 304 self.vmctx() 305 .as_ptr() 306 .byte_add(usize::try_from(offset).unwrap()) 307 .cast() 308 } 309 310 unsafe fn vmctx_plus_offset_mut<T: VmSafe>(&mut self, offset: u32) -> *mut T { 311 self.vmctx() 312 .as_ptr() 313 .byte_add(usize::try_from(offset).unwrap()) 314 .cast() 315 } 316 317 /// Returns a pointer to the "may leave" flag for this instance specified 318 /// for canonical lowering and lifting operations. 319 #[inline] 320 pub fn instance_flags(&self, instance: RuntimeComponentInstanceIndex) -> InstanceFlags { 321 unsafe { 322 let ptr = self 323 .vmctx_plus_offset::<VMGlobalDefinition>(self.offsets.instance_flags(instance)) 324 .cast_mut(); 325 InstanceFlags(SendSyncPtr::new(NonNull::new(ptr).unwrap())) 326 } 327 } 328 329 /// Returns the runtime memory definition corresponding to the index of the 330 /// memory provided. 331 /// 332 /// This can only be called after `idx` has been initialized at runtime 333 /// during the instantiation process of a component. 334 pub fn runtime_memory(&self, idx: RuntimeMemoryIndex) -> *mut VMMemoryDefinition { 335 unsafe { 336 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_memory(idx)); 337 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 338 ret.as_ptr() 339 } 340 } 341 342 /// Returns the runtime table definition and associated instance `VMContext` 343 /// corresponding to the index of the table provided. 344 /// 345 /// This can only be called after `idx` has been initialized at runtime 346 /// during the instantiation process of a component. 347 pub fn runtime_table(&self, idx: RuntimeTableIndex) -> VMTableImport { 348 unsafe { 349 let ret = *self.vmctx_plus_offset::<VMTableImport>(self.offsets.runtime_table(idx)); 350 debug_assert!(ret.from.as_ptr() as usize != INVALID_PTR); 351 debug_assert!(ret.vmctx.as_ptr() as usize != INVALID_PTR); 352 ret 353 } 354 } 355 356 /// Returns the realloc pointer corresponding to the index provided. 357 /// 358 /// This can only be called after `idx` has been initialized at runtime 359 /// during the instantiation process of a component. 360 pub fn runtime_realloc(&self, idx: RuntimeReallocIndex) -> NonNull<VMFuncRef> { 361 unsafe { 362 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_realloc(idx)); 363 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 364 ret.as_non_null() 365 } 366 } 367 368 /// Returns the post-return pointer corresponding to the index provided. 369 /// 370 /// This can only be called after `idx` has been initialized at runtime 371 /// during the instantiation process of a component. 372 pub fn runtime_post_return(&self, idx: RuntimePostReturnIndex) -> NonNull<VMFuncRef> { 373 unsafe { 374 let ret = *self.vmctx_plus_offset::<VmPtr<_>>(self.offsets.runtime_post_return(idx)); 375 debug_assert!(ret.as_ptr() as usize != INVALID_PTR); 376 ret.as_non_null() 377 } 378 } 379 380 /// Returns the host information for the lowered function at the index 381 /// specified. 382 /// 383 /// This can only be called after `idx` has been initialized at runtime 384 /// during the instantiation process of a component. 385 pub fn lowering(&self, idx: LoweredIndex) -> VMLowering { 386 unsafe { 387 let ret = *self.vmctx_plus_offset::<VMLowering>(self.offsets.lowering(idx)); 388 debug_assert!(ret.callee as usize != INVALID_PTR); 389 debug_assert!(ret.data.as_ptr() as usize != INVALID_PTR); 390 ret 391 } 392 } 393 394 /// Returns the core wasm `funcref` corresponding to the trampoline 395 /// specified. 396 /// 397 /// The returned function is suitable to pass directly to a wasm module 398 /// instantiation and the function contains cranelift-compiled trampolines. 399 /// 400 /// This can only be called after `idx` has been initialized at runtime 401 /// during the instantiation process of a component. 402 pub fn trampoline_func_ref(&self, idx: TrampolineIndex) -> NonNull<VMFuncRef> { 403 unsafe { 404 let offset = self.offsets.trampoline_func_ref(idx); 405 let ret = self.vmctx_plus_offset::<VMFuncRef>(offset); 406 debug_assert!( 407 mem::transmute::<Option<VmPtr<VMWasmCallFunction>>, usize>((*ret).wasm_call) 408 != INVALID_PTR 409 ); 410 debug_assert!((*ret).vmctx.as_ptr() as usize != INVALID_PTR); 411 NonNull::new(ret.cast_mut()).unwrap() 412 } 413 } 414 415 /// Stores the runtime memory pointer at the index specified. 416 /// 417 /// This is intended to be called during the instantiation process of a 418 /// component once a memory is available, which may not be until part-way 419 /// through component instantiation. 420 /// 421 /// Note that it should be a property of the component model that the `ptr` 422 /// here is never needed prior to it being configured here in the instance. 423 pub fn set_runtime_memory( 424 &mut self, 425 idx: RuntimeMemoryIndex, 426 ptr: NonNull<VMMemoryDefinition>, 427 ) { 428 unsafe { 429 let storage = self.vmctx_plus_offset_mut::<VmPtr<VMMemoryDefinition>>( 430 self.offsets.runtime_memory(idx), 431 ); 432 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 433 *storage = ptr.into(); 434 } 435 } 436 437 /// Same as `set_runtime_memory` but for realloc function pointers. 438 pub fn set_runtime_realloc(&mut self, idx: RuntimeReallocIndex, ptr: NonNull<VMFuncRef>) { 439 unsafe { 440 let storage = 441 self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_realloc(idx)); 442 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 443 *storage = ptr.into(); 444 } 445 } 446 447 /// Same as `set_runtime_memory` but for async callback function pointers. 448 pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull<VMFuncRef>) { 449 unsafe { 450 let storage = 451 self.vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_callback(idx)); 452 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 453 *storage = ptr.into(); 454 } 455 } 456 457 /// Same as `set_runtime_memory` but for post-return function pointers. 458 pub fn set_runtime_post_return( 459 &mut self, 460 idx: RuntimePostReturnIndex, 461 ptr: NonNull<VMFuncRef>, 462 ) { 463 unsafe { 464 let storage = self 465 .vmctx_plus_offset_mut::<VmPtr<VMFuncRef>>(self.offsets.runtime_post_return(idx)); 466 debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); 467 *storage = ptr.into(); 468 } 469 } 470 471 /// Stores the runtime table pointer at the index specified. 472 /// 473 /// This is intended to be called during the instantiation process of a 474 /// component once a table is available, which may not be until part-way 475 /// through component instantiation. 476 /// 477 /// Note that it should be a property of the component model that the `ptr` 478 /// here is never needed prior to it being configured here in the instance. 479 pub fn set_runtime_table( 480 &mut self, 481 idx: RuntimeTableIndex, 482 ptr: NonNull<VMTableDefinition>, 483 vmctx: NonNull<VMContext>, 484 index: DefinedTableIndex, 485 ) { 486 unsafe { 487 let storage = 488 self.vmctx_plus_offset_mut::<VMTableImport>(self.offsets.runtime_table(idx)); 489 debug_assert!((*storage).vmctx.as_ptr() as usize == INVALID_PTR); 490 debug_assert!((*storage).from.as_ptr() as usize == INVALID_PTR); 491 *storage = VMTableImport { 492 vmctx: vmctx.into(), 493 from: ptr.into(), 494 index, 495 }; 496 } 497 } 498 499 /// Configures host runtime lowering information associated with imported f 500 /// functions for the `idx` specified. 501 pub fn set_lowering(&mut self, idx: LoweredIndex, lowering: VMLowering) { 502 unsafe { 503 debug_assert!( 504 *self.vmctx_plus_offset::<usize>(self.offsets.lowering_callee(idx)) == INVALID_PTR 505 ); 506 debug_assert!( 507 *self.vmctx_plus_offset::<usize>(self.offsets.lowering_data(idx)) == INVALID_PTR 508 ); 509 *self.vmctx_plus_offset_mut(self.offsets.lowering(idx)) = lowering; 510 } 511 } 512 513 /// Same as `set_lowering` but for the resource.drop functions. 514 pub fn set_trampoline( 515 &mut self, 516 idx: TrampolineIndex, 517 wasm_call: NonNull<VMWasmCallFunction>, 518 array_call: NonNull<VMArrayCallFunction>, 519 type_index: VMSharedTypeIndex, 520 ) { 521 unsafe { 522 let offset = self.offsets.trampoline_func_ref(idx); 523 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 524 let vmctx = VMOpaqueContext::from_vmcomponent(self.vmctx()); 525 *self.vmctx_plus_offset_mut(offset) = VMFuncRef { 526 wasm_call: Some(wasm_call.into()), 527 array_call: array_call.into(), 528 type_index, 529 vmctx: vmctx.into(), 530 }; 531 } 532 } 533 534 /// Configures the destructor for a resource at the `idx` specified. 535 /// 536 /// This is required to be called for each resource as it's defined within a 537 /// component during the instantiation process. 538 pub fn set_resource_destructor( 539 &mut self, 540 idx: ResourceIndex, 541 dtor: Option<NonNull<VMFuncRef>>, 542 ) { 543 unsafe { 544 let offset = self.offsets.resource_destructor(idx); 545 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) == INVALID_PTR); 546 *self.vmctx_plus_offset_mut(offset) = dtor.map(VmPtr::from); 547 } 548 } 549 550 /// Returns the destructor, if any, for `idx`. 551 /// 552 /// This is only valid to call after `set_resource_destructor`, or typically 553 /// after instantiation. 554 pub fn resource_destructor(&self, idx: ResourceIndex) -> Option<NonNull<VMFuncRef>> { 555 unsafe { 556 let offset = self.offsets.resource_destructor(idx); 557 debug_assert!(*self.vmctx_plus_offset::<usize>(offset) != INVALID_PTR); 558 (*self.vmctx_plus_offset::<Option<VmPtr<VMFuncRef>>>(offset)).map(|p| p.as_non_null()) 559 } 560 } 561 562 unsafe fn initialize_vmctx(&mut self) { 563 *self.vmctx_plus_offset_mut(self.offsets.magic()) = VMCOMPONENT_MAGIC; 564 *self.vmctx_plus_offset_mut(self.offsets.builtins()) = 565 VmPtr::from(NonNull::from(&libcalls::VMComponentBuiltins::INIT)); 566 *self.vmctx_plus_offset_mut(self.offsets.vm_store_context()) = 567 VmPtr::from(self.store.0.as_ref().vm_store_context_ptr()); 568 569 for i in 0..self.offsets.num_runtime_component_instances { 570 let i = RuntimeComponentInstanceIndex::from_u32(i); 571 let mut def = VMGlobalDefinition::new(); 572 *def.as_i32_mut() = FLAG_MAY_ENTER | FLAG_MAY_LEAVE; 573 self.instance_flags(i).as_raw().write(def); 574 } 575 576 // In debug mode set non-null bad values to all "pointer looking" bits 577 // and pieces related to lowering and such. This'll help detect any 578 // erroneous usage and enable debug assertions above as well to prevent 579 // loading these before they're configured or setting them twice. 580 if cfg!(debug_assertions) { 581 for i in 0..self.offsets.num_lowerings { 582 let i = LoweredIndex::from_u32(i); 583 let offset = self.offsets.lowering_callee(i); 584 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 585 let offset = self.offsets.lowering_data(i); 586 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 587 } 588 for i in 0..self.offsets.num_trampolines { 589 let i = TrampolineIndex::from_u32(i); 590 let offset = self.offsets.trampoline_func_ref(i); 591 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 592 } 593 for i in 0..self.offsets.num_runtime_memories { 594 let i = RuntimeMemoryIndex::from_u32(i); 595 let offset = self.offsets.runtime_memory(i); 596 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 597 } 598 for i in 0..self.offsets.num_runtime_reallocs { 599 let i = RuntimeReallocIndex::from_u32(i); 600 let offset = self.offsets.runtime_realloc(i); 601 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 602 } 603 for i in 0..self.offsets.num_runtime_callbacks { 604 let i = RuntimeCallbackIndex::from_u32(i); 605 let offset = self.offsets.runtime_callback(i); 606 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 607 } 608 for i in 0..self.offsets.num_runtime_post_returns { 609 let i = RuntimePostReturnIndex::from_u32(i); 610 let offset = self.offsets.runtime_post_return(i); 611 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 612 } 613 for i in 0..self.offsets.num_resources { 614 let i = ResourceIndex::from_u32(i); 615 let offset = self.offsets.resource_destructor(i); 616 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 617 } 618 for i in 0..self.offsets.num_runtime_tables { 619 let i = RuntimeTableIndex::from_u32(i); 620 let offset = self.offsets.runtime_table(i); 621 *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; 622 } 623 } 624 } 625 626 /// Returns a reference to the component type information for this 627 /// instance. 628 pub fn component(&self) -> &Component { 629 &self.component 630 } 631 632 /// Returns a reference to the resource type information. 633 pub fn resource_types(&self) -> &Arc<PrimaryMap<ResourceIndex, ResourceType>> { 634 &self.resource_types 635 } 636 637 /// Returns a mutable reference to the resource type information. 638 pub fn resource_types_mut(&mut self) -> &mut Arc<PrimaryMap<ResourceIndex, ResourceType>> { 639 &mut self.resource_types 640 } 641 642 /// Returns whether the resource that `ty` points to is owned by the 643 /// instance that `ty` correspond to. 644 /// 645 /// This is used when lowering borrows to skip table management and instead 646 /// thread through the underlying representation directly. 647 pub fn resource_owned_by_own_instance(&self, ty: TypeResourceTableIndex) -> bool { 648 let resource = &self.component.types()[ty]; 649 let component = self.component.env_component(); 650 let idx = match component.defined_resource_index(resource.ty) { 651 Some(idx) => idx, 652 None => return false, 653 }; 654 resource.instance == component.defined_resource_instances[idx] 655 } 656 657 /// Returns the runtime state of resources associated with this component. 658 #[inline] 659 pub fn guest_tables( 660 &mut self, 661 ) -> ( 662 &mut PrimaryMap<RuntimeComponentInstanceIndex, ResourceTable>, 663 &ComponentTypes, 664 ) { 665 (&mut self.instance_resource_tables, self.component.types()) 666 } 667 668 /// Returns the destructor and instance flags for the specified resource 669 /// table type. 670 /// 671 /// This will lookup the origin definition of the `ty` table and return the 672 /// destructor/flags for that. 673 pub fn dtor_and_flags( 674 &self, 675 ty: TypeResourceTableIndex, 676 ) -> (Option<NonNull<VMFuncRef>>, Option<InstanceFlags>) { 677 let resource = self.component.types()[ty].ty; 678 let dtor = self.resource_destructor(resource); 679 let component = self.component.env_component(); 680 let flags = component.defined_resource_index(resource).map(|i| { 681 let instance = component.defined_resource_instances[i]; 682 self.instance_flags(instance) 683 }); 684 (dtor, flags) 685 } 686 687 /// Returns the store-local id that points to this component. 688 pub fn id(&self) -> ComponentInstanceId { 689 self.id 690 } 691 692 /// Pushes a new runtime instance that's been created into 693 /// `self.instances`. 694 pub fn push_instance_id(&mut self, id: InstanceId) -> RuntimeInstanceIndex { 695 self.instances.push(id) 696 } 697 698 /// Translates a `CoreDef`, a definition of a core wasm item, to an 699 /// [`Export`] which is the runtime core wasm definition. 700 pub fn lookup_def(&self, store: &StoreOpaque, def: &CoreDef) -> Export { 701 match def { 702 CoreDef::Export(e) => self.lookup_export(store, e), 703 CoreDef::Trampoline(idx) => Export::Function(ExportFunction { 704 func_ref: self.trampoline_func_ref(*idx), 705 }), 706 CoreDef::InstanceFlags(idx) => Export::Global(ExportGlobal { 707 definition: self.instance_flags(*idx).as_raw(), 708 global: Global { 709 wasm_ty: WasmValType::I32, 710 mutability: true, 711 }, 712 kind: ExportGlobalKind::ComponentFlags(self.vmctx(), *idx), 713 }), 714 } 715 } 716 717 /// Translates a `CoreExport<T>`, an export of some core instance within 718 /// this component, to the actual runtime definition of that item. 719 pub fn lookup_export<T>(&self, store: &StoreOpaque, item: &CoreExport<T>) -> Export 720 where 721 T: Copy + Into<EntityIndex>, 722 { 723 let id = self.instances[item.instance]; 724 let instance = store.instance(id); 725 let idx = match &item.item { 726 ExportItem::Index(idx) => (*idx).into(), 727 728 // FIXME: ideally at runtime we don't actually do any name lookups 729 // here. This will only happen when the host supplies an imported 730 // module so while the structure can't be known at compile time we 731 // do know at `InstancePre` time, for example, what all the host 732 // imports are. In theory we should be able to, as part of 733 // `InstancePre` construction, perform all name=>index mappings 734 // during that phase so the actual instantiation of an `InstancePre` 735 // skips all string lookups. This should probably only be 736 // investigated if this becomes a performance issue though. 737 ExportItem::Name(name) => instance.env_module().exports[name], 738 }; 739 instance.get_export_by_index(idx) 740 } 741 742 /// Looks up the value used for `import` at runtime. 743 /// 744 /// # Panics 745 /// 746 /// Panics of `import` is out of bounds for this component. 747 pub(crate) fn runtime_import(&self, import: RuntimeImportIndex) -> &RuntimeImport { 748 &self.imports[import] 749 } 750 751 /// Returns an `InstancePre<T>` which can be used to re-instantiated this 752 /// component if desired. 753 /// 754 /// # Safety 755 /// 756 /// This function places no bounds on `T` so it's up to the caller to match 757 /// that up appropriately with the store that this instance resides within. 758 pub unsafe fn instance_pre<T>(&self) -> InstancePre<T> { 759 // SAFETY: The `T` part of `new_unchecked` is forwarded as a contract of 760 // this function, and otherwise the validity of the components of the 761 // InstancePre should be guaranteed as it's what we were built with 762 // ourselves. 763 unsafe { 764 InstancePre::new_unchecked( 765 self.component.clone(), 766 self.imports.clone(), 767 self.resource_types.clone(), 768 ) 769 } 770 } 771 772 /// Sets the cached argument for the canonical ABI option `post-return` to 773 /// the `arg` specified. 774 /// 775 /// This function is used in conjunction with function calls to record, 776 /// after a fuction call completes, the optional ABI return value. This 777 /// return value is cached within this instance for future use when the 778 /// `post_return` Rust-API-level function is invoked. 779 /// 780 /// Note that `index` here is the index of the export that was just 781 /// invoked, and this is used to ensure that `post_return` is called on the 782 /// same function afterwards. This restriction technically isn't necessary 783 /// though and may be one we want to lift in the future. 784 /// 785 /// # Panics 786 /// 787 /// This function will panic if `post_return_arg` is already set to `Some`. 788 pub fn post_return_arg_set(&mut self, index: ExportIndex, arg: ValRaw) { 789 assert!(self.post_return_arg.is_none()); 790 self.post_return_arg = Some((index, arg)); 791 } 792 793 /// Re-acquires the value originally saved via `post_return_arg_set`. 794 /// 795 /// This function will take a function `index` that's having its 796 /// `post_return` function called. If an argument was previously stored and 797 /// `index` matches the index that was stored then `Some(arg)` is returned. 798 /// Otherwise `None` is returned. 799 pub fn post_return_arg_take(&mut self, index: ExportIndex) -> Option<ValRaw> { 800 let (expected_index, arg) = self.post_return_arg.take()?; 801 if index != expected_index { 802 self.post_return_arg = Some((expected_index, arg)); 803 None 804 } else { 805 Some(arg) 806 } 807 } 808 } 809 810 impl VMComponentContext { 811 /// Moves the `self` pointer backwards to the `ComponentInstance` pointer 812 /// that this `VMComponentContext` trails. 813 pub fn instance(&self) -> *mut ComponentInstance { 814 unsafe { 815 (self as *const Self as *mut u8) 816 .offset(-(offset_of!(ComponentInstance, vmctx) as isize)) 817 as *mut ComponentInstance 818 } 819 } 820 } 821 822 /// An owned version of `ComponentInstance` which is akin to 823 /// `Box<ComponentInstance>`. 824 /// 825 /// This type can be dereferenced to `ComponentInstance` to access the 826 /// underlying methods. 827 pub struct OwnedComponentInstance { 828 ptr: SendSyncPtr<ComponentInstance>, 829 } 830 831 impl OwnedComponentInstance { 832 /// Allocates a new `ComponentInstance + VMComponentContext` pair on the 833 /// heap with `malloc` and configures it for the `component` specified. 834 pub fn new( 835 id: ComponentInstanceId, 836 component: &Component, 837 resource_types: Arc<PrimaryMap<ResourceIndex, ResourceType>>, 838 imports: &Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>, 839 store: NonNull<dyn VMStore>, 840 ) -> OwnedComponentInstance { 841 let offsets = VMComponentOffsets::new(HostPtr, component.env_component()); 842 let layout = ComponentInstance::alloc_layout(&offsets); 843 unsafe { 844 // Technically it is not required to `alloc_zeroed` here. The 845 // primary reason for doing this is because a component context 846 // start is a "partly initialized" state where pointers and such are 847 // configured as the instantiation process continues. The component 848 // model should guarantee that we never access uninitialized memory 849 // in the context, but to help protect against possible bugs a 850 // zeroed allocation is done here to try to contain 851 // use-before-initialized issues. 852 let ptr = alloc::alloc::alloc_zeroed(layout) as *mut ComponentInstance; 853 let ptr = NonNull::new(ptr).unwrap(); 854 855 ComponentInstance::new_at( 856 ptr, 857 layout.size(), 858 offsets, 859 id, 860 component, 861 resource_types, 862 imports, 863 store, 864 ); 865 866 let ptr = SendSyncPtr::new(ptr); 867 OwnedComponentInstance { ptr } 868 } 869 } 870 871 /// Returns the underlying component instance's raw pointer. 872 pub fn instance_ptr(&self) -> NonNull<ComponentInstance> { 873 self.ptr.as_non_null() 874 } 875 } 876 877 impl Deref for OwnedComponentInstance { 878 type Target = ComponentInstance; 879 fn deref(&self) -> &ComponentInstance { 880 unsafe { &*self.ptr.as_ptr() } 881 } 882 } 883 884 impl Drop for OwnedComponentInstance { 885 fn drop(&mut self) { 886 let layout = ComponentInstance::alloc_layout(&self.offsets); 887 unsafe { 888 ptr::drop_in_place(self.ptr.as_ptr()); 889 alloc::alloc::dealloc(self.ptr.as_ptr().cast(), layout); 890 } 891 } 892 } 893 894 impl VMComponentContext { 895 /// Helper function to cast between context types using a debug assertion to 896 /// protect against some mistakes. 897 #[inline] 898 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMComponentContext> { 899 // See comments in `VMContext::from_opaque` for this debug assert 900 debug_assert_eq!(opaque.as_ref().magic, VMCOMPONENT_MAGIC); 901 opaque.cast() 902 } 903 } 904 905 impl VMOpaqueContext { 906 /// Helper function to clearly indicate the cast desired 907 #[inline] 908 pub fn from_vmcomponent(ptr: NonNull<VMComponentContext>) -> NonNull<VMOpaqueContext> { 909 ptr.cast() 910 } 911 } 912 913 #[allow(missing_docs)] 914 #[repr(transparent)] 915 #[derive(Copy, Clone)] 916 pub struct InstanceFlags(SendSyncPtr<VMGlobalDefinition>); 917 918 #[allow(missing_docs)] 919 impl InstanceFlags { 920 /// Wraps the given pointer as an `InstanceFlags` 921 /// 922 /// # Unsafety 923 /// 924 /// This is a raw pointer argument which needs to be valid for the lifetime 925 /// that `InstanceFlags` is used. 926 pub unsafe fn from_raw(ptr: NonNull<VMGlobalDefinition>) -> InstanceFlags { 927 InstanceFlags(SendSyncPtr::from(ptr)) 928 } 929 930 #[inline] 931 pub unsafe fn may_leave(&self) -> bool { 932 *self.as_raw().as_ref().as_i32() & FLAG_MAY_LEAVE != 0 933 } 934 935 #[inline] 936 pub unsafe fn set_may_leave(&mut self, val: bool) { 937 if val { 938 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_LEAVE; 939 } else { 940 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_LEAVE; 941 } 942 } 943 944 #[inline] 945 pub unsafe fn may_enter(&self) -> bool { 946 *self.as_raw().as_ref().as_i32() & FLAG_MAY_ENTER != 0 947 } 948 949 #[inline] 950 pub unsafe fn set_may_enter(&mut self, val: bool) { 951 if val { 952 *self.as_raw().as_mut().as_i32_mut() |= FLAG_MAY_ENTER; 953 } else { 954 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_MAY_ENTER; 955 } 956 } 957 958 #[inline] 959 pub unsafe fn needs_post_return(&self) -> bool { 960 *self.as_raw().as_ref().as_i32() & FLAG_NEEDS_POST_RETURN != 0 961 } 962 963 #[inline] 964 pub unsafe fn set_needs_post_return(&mut self, val: bool) { 965 if val { 966 *self.as_raw().as_mut().as_i32_mut() |= FLAG_NEEDS_POST_RETURN; 967 } else { 968 *self.as_raw().as_mut().as_i32_mut() &= !FLAG_NEEDS_POST_RETURN; 969 } 970 } 971 972 #[inline] 973 pub fn as_raw(&self) -> NonNull<VMGlobalDefinition> { 974 self.0.as_non_null() 975 } 976 } 977