1 use crate::component::matching::InstanceType; 2 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 3 use crate::component::{Instance, ResourceType}; 4 use crate::prelude::*; 5 use crate::runtime::vm::component::{ 6 CallContexts, ComponentInstance, InstanceFlags, ResourceTable, ResourceTables, 7 }; 8 use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition}; 9 use crate::store::{StoreId, StoreOpaque}; 10 use crate::{FuncType, StoreContextMut}; 11 use alloc::sync::Arc; 12 use core::pin::Pin; 13 use core::ptr::NonNull; 14 use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeResourceTableIndex}; 15 16 /// Runtime representation of canonical ABI options in the component model. 17 /// 18 /// This structure packages up the runtime representation of each option from 19 /// memories to reallocs to string encodings. Note that this is a "standalone" 20 /// structure which has raw pointers internally. This allows it to be created 21 /// out of thin air for a host function import, for example. The `store_id` 22 /// field, however, is what is used to pair this set of options with a store 23 /// reference to actually use the pointers. 24 #[derive(Copy, Clone)] 25 pub struct Options { 26 /// The store from which this options originated. 27 store_id: StoreId, 28 29 /// An optional pointer for the memory that this set of options is referring 30 /// to. This option is not required to be specified in the canonical ABI 31 /// hence the `Option`. 32 /// 33 /// Note that this pointer cannot be safely dereferenced unless a store, 34 /// verified with `self.store_id`, has the appropriate borrow available. 35 memory: Option<NonNull<VMMemoryDefinition>>, 36 37 /// Similar to `memory` but corresponds to the `canonical_abi_realloc` 38 /// function. 39 /// 40 /// Safely using this pointer has the same restrictions as `memory` above. 41 realloc: Option<NonNull<VMFuncRef>>, 42 43 /// The encoding used for strings, if found. 44 /// 45 /// This defaults to utf-8 but can be changed if necessary. 46 string_encoding: StringEncoding, 47 } 48 49 // The `Options` structure stores raw pointers but they're never used unless a 50 // `Store` is available so this should be threadsafe and largely inherit the 51 // thread-safety story of `Store<T>` itself. 52 unsafe impl Send for Options {} 53 unsafe impl Sync for Options {} 54 55 impl Options { 56 // FIXME(#4311): prevent a ctor where the memory is memory64 57 58 /// Creates a new set of options with the specified components. 59 /// 60 /// # Unsafety 61 /// 62 /// This is unsafety as there is no way to statically verify the validity of 63 /// the arguments. For example pointers must be valid pointers, the 64 /// `StoreId` must be valid for the pointers, etc. 65 pub unsafe fn new( 66 store_id: StoreId, 67 memory: Option<NonNull<VMMemoryDefinition>>, 68 realloc: Option<NonNull<VMFuncRef>>, 69 string_encoding: StringEncoding, 70 ) -> Options { 71 Options { 72 store_id, 73 memory, 74 realloc, 75 string_encoding, 76 } 77 } 78 79 fn realloc<'a, T>( 80 &self, 81 store: &'a mut StoreContextMut<'_, T>, 82 realloc_ty: &FuncType, 83 old: usize, 84 old_size: usize, 85 old_align: u32, 86 new_size: usize, 87 ) -> Result<(&'a mut [u8], usize)> { 88 self.store_id.assert_belongs_to(store.0.id()); 89 90 let realloc = self.realloc.unwrap(); 91 92 let params = ( 93 u32::try_from(old)?, 94 u32::try_from(old_size)?, 95 old_align, 96 u32::try_from(new_size)?, 97 ); 98 99 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>; 100 101 // This call doesn't take any GC refs, and therefore we shouldn't ever 102 // need to GC before entering Wasm. 103 #[cfg(feature = "gc")] 104 debug_assert!(!ReallocFunc::need_gc_before_call_raw(store.0, ¶ms)); 105 106 // Invoke the wasm malloc function using its raw and statically known 107 // signature. 108 let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? }; 109 110 if result % old_align != 0 { 111 bail!("realloc return: result not aligned"); 112 } 113 let result = usize::try_from(result)?; 114 115 let memory = self.memory_mut(store.0); 116 117 let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) { 118 Some(end) => end, 119 None => bail!("realloc return: beyond end of memory"), 120 }; 121 122 Ok((result_slice, result)) 123 } 124 125 /// Asserts that this function has an associated memory attached to it and 126 /// then returns the slice of memory tied to the lifetime of the provided 127 /// store. 128 pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] { 129 self.store_id.assert_belongs_to(store.id()); 130 131 // The unsafety here is intended to be encapsulated by the two 132 // preceding assertions. Namely we assert that the `store` is the same 133 // as the original store of this `Options`, meaning that we safely have 134 // either a shared reference or a mutable reference (as below) which 135 // means it's safe to view the memory (aka it's not a different store 136 // where our original store is on some other thread or something like 137 // that). 138 // 139 // Additionally the memory itself is asserted to be present as memory 140 // is an optional configuration in canonical ABI options. 141 unsafe { 142 let memory = self.memory.unwrap().as_ref(); 143 core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length()) 144 } 145 } 146 147 /// Same as above, just `_mut` 148 pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] { 149 self.store_id.assert_belongs_to(store.id()); 150 151 // See comments in `memory` about the unsafety 152 unsafe { 153 let memory = self.memory.unwrap().as_ref(); 154 core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length()) 155 } 156 } 157 158 /// Returns the underlying encoding used for strings in this 159 /// lifting/lowering. 160 pub fn string_encoding(&self) -> StringEncoding { 161 self.string_encoding 162 } 163 164 /// Returns the id of the store that this `Options` is connected to. 165 pub fn store_id(&self) -> StoreId { 166 self.store_id 167 } 168 } 169 170 /// A helper structure which is a "package" of the context used during lowering 171 /// values into a component (or storing them into memory). 172 /// 173 /// This type is used by the `Lower` trait extensively and contains any 174 /// contextual information necessary related to the context in which the 175 /// lowering is happening. 176 #[doc(hidden)] 177 pub struct LowerContext<'a, T: 'static> { 178 /// Lowering may involve invoking memory allocation functions so part of the 179 /// context here is carrying access to the entire store that wasm is 180 /// executing within. This store serves as proof-of-ability to actually 181 /// execute wasm safely. 182 pub store: StoreContextMut<'a, T>, 183 184 /// Lowering always happens into a function that's been `canon lift`'d or 185 /// `canon lower`'d, both of which specify a set of options for the 186 /// canonical ABI. For example details like string encoding are contained 187 /// here along with which memory pointers are relative to or what the memory 188 /// allocation function is. 189 pub options: &'a Options, 190 191 /// Lowering happens within the context of a component instance and this 192 /// field stores the type information of that component instance. This is 193 /// used for type lookups and general type queries during the 194 /// lifting/lowering process. 195 pub types: &'a ComponentTypes, 196 197 /// Index of the component instance that's being lowered into. 198 instance: Instance, 199 } 200 201 #[doc(hidden)] 202 impl<'a, T: 'static> LowerContext<'a, T> { 203 /// Creates a new lowering context from the specified parameters. 204 pub fn new( 205 store: StoreContextMut<'a, T>, 206 options: &'a Options, 207 types: &'a ComponentTypes, 208 instance: Instance, 209 ) -> LowerContext<'a, T> { 210 LowerContext { 211 store, 212 options, 213 types, 214 instance, 215 } 216 } 217 218 /// Returns the `&ComponentInstance` that's being lowered into. 219 pub fn instance(&self) -> &ComponentInstance { 220 self.instance.id().get(self.store.0) 221 } 222 223 /// Returns the `&mut ComponentInstance` that's being lowered into. 224 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 225 self.instance.id().get_mut(self.store.0) 226 } 227 228 /// Returns a view into memory as a mutable slice of bytes. 229 /// 230 /// # Panics 231 /// 232 /// This will panic if memory has not been configured for this lowering 233 /// (e.g. it wasn't present during the specification of canonical options). 234 pub fn as_slice_mut(&mut self) -> &mut [u8] { 235 self.options.memory_mut(self.store.0) 236 } 237 238 /// Invokes the memory allocation function (which is style after `realloc`) 239 /// with the specified parameters. 240 /// 241 /// # Panics 242 /// 243 /// This will panic if realloc hasn't been configured for this lowering via 244 /// its canonical options. 245 pub fn realloc( 246 &mut self, 247 old: usize, 248 old_size: usize, 249 old_align: u32, 250 new_size: usize, 251 ) -> Result<usize> { 252 let realloc_func_ty = Arc::clone(self.instance().component().realloc_func_ty()); 253 self.options 254 .realloc( 255 &mut self.store, 256 &realloc_func_ty, 257 old, 258 old_size, 259 old_align, 260 new_size, 261 ) 262 .map(|(_, ptr)| ptr) 263 } 264 265 /// Returns a fixed mutable slice of memory `N` bytes large starting at 266 /// offset `N`, panicking on out-of-bounds. 267 /// 268 /// It should be previously verified that `offset` is in-bounds via 269 /// bounds-checks. 270 /// 271 /// # Panics 272 /// 273 /// This will panic if memory has not been configured for this lowering 274 /// (e.g. it wasn't present during the specification of canonical options). 275 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] { 276 // FIXME: this bounds check shouldn't actually be necessary, all 277 // callers of `ComponentType::store` have already performed a bounds 278 // check so we're guaranteed that `offset..offset+N` is in-bounds. That 279 // being said we at least should do bounds checks in debug mode and 280 // it's not clear to me how to easily structure this so that it's 281 // "statically obvious" the bounds check isn't necessary. 282 // 283 // For now I figure we can leave in this bounds check and if it becomes 284 // an issue we can optimize further later, probably with judicious use 285 // of `unsafe`. 286 self.as_slice_mut()[offset..].first_chunk_mut().unwrap() 287 } 288 289 /// Lowers an `own` resource into the guest, converting the `rep` specified 290 /// into a guest-local index. 291 /// 292 /// The `ty` provided is which table to put this into. 293 pub fn guest_resource_lower_own( 294 &mut self, 295 ty: TypeResourceTableIndex, 296 rep: u32, 297 ) -> Result<u32> { 298 self.resource_tables().guest_resource_lower_own(rep, ty) 299 } 300 301 /// Lowers a `borrow` resource into the guest, converting the `rep` to a 302 /// guest-local index in the `ty` table specified. 303 pub fn guest_resource_lower_borrow( 304 &mut self, 305 ty: TypeResourceTableIndex, 306 rep: u32, 307 ) -> Result<u32> { 308 // Implement `lower_borrow`'s special case here where if a borrow is 309 // inserted into a table owned by the instance which implemented the 310 // original resource then no borrow tracking is employed and instead the 311 // `rep` is returned "raw". 312 // 313 // This check is performed by comparing the owning instance of `ty` 314 // against the owning instance of the resource that `ty` is working 315 // with. 316 if self.instance().resource_owned_by_own_instance(ty) { 317 return Ok(rep); 318 } 319 self.resource_tables().guest_resource_lower_borrow(rep, ty) 320 } 321 322 /// Lifts a host-owned `own` resource at the `idx` specified into the 323 /// representation of that resource. 324 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> { 325 self.resource_tables().host_resource_lift_own(idx) 326 } 327 328 /// Lifts a host-owned `borrow` resource at the `idx` specified into the 329 /// representation of that resource. 330 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> { 331 self.resource_tables().host_resource_lift_borrow(idx) 332 } 333 334 /// Lowers a resource into the host-owned table, returning the index it was 335 /// inserted at. 336 /// 337 /// Note that this is a special case for `Resource<T>`. Most of the time a 338 /// host value shouldn't be lowered with a lowering context. 339 pub fn host_resource_lower_own( 340 &mut self, 341 rep: u32, 342 dtor: Option<NonNull<VMFuncRef>>, 343 flags: Option<InstanceFlags>, 344 ) -> Result<HostResourceIndex> { 345 self.resource_tables() 346 .host_resource_lower_own(rep, dtor, flags) 347 } 348 349 /// Returns the underlying resource type for the `ty` table specified. 350 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 351 self.instance_type().resource_type(ty) 352 } 353 354 /// Returns the instance type information corresponding to the instance that 355 /// this context is lowering into. 356 pub fn instance_type(&self) -> InstanceType<'_> { 357 InstanceType::new(self.instance()) 358 } 359 360 fn resource_tables(&mut self) -> HostResourceTables<'_> { 361 let (calls, host_table, host_resource_data, instance) = self 362 .store 363 .0 364 .component_resource_state_with_instance(self.instance); 365 HostResourceTables::from_parts( 366 ResourceTables { 367 host_table: Some(host_table), 368 calls, 369 guest: Some(instance.guest_tables()), 370 }, 371 host_resource_data, 372 ) 373 } 374 375 /// See [`HostResourceTables::enter_call`]. 376 #[inline] 377 pub fn enter_call(&mut self) { 378 self.resource_tables().enter_call() 379 } 380 381 /// See [`HostResourceTables::exit_call`]. 382 #[inline] 383 pub fn exit_call(&mut self) -> Result<()> { 384 self.resource_tables().exit_call() 385 } 386 } 387 388 /// Contextual information used when lifting a type from a component into the 389 /// host. 390 /// 391 /// This structure is the analogue of `LowerContext` except used during lifting 392 /// operations (or loading from memory). 393 #[doc(hidden)] 394 pub struct LiftContext<'a> { 395 /// Like lowering, lifting always has options configured. 396 pub options: &'a Options, 397 398 /// Instance type information, like with lowering. 399 pub types: &'a Arc<ComponentTypes>, 400 401 memory: Option<&'a [u8]>, 402 403 instance: Pin<&'a mut ComponentInstance>, 404 instance_handle: Instance, 405 406 host_table: &'a mut ResourceTable, 407 host_resource_data: &'a mut HostResourceData, 408 409 calls: &'a mut CallContexts, 410 } 411 412 #[doc(hidden)] 413 impl<'a> LiftContext<'a> { 414 /// Creates a new lifting context given the provided context. 415 #[inline] 416 pub fn new( 417 store: &'a mut StoreOpaque, 418 options: &'a Options, 419 types: &'a Arc<ComponentTypes>, 420 instance_handle: Instance, 421 ) -> LiftContext<'a> { 422 // From `&mut StoreOpaque` provided the goal here is to project out 423 // three different disjoint fields owned by the store: memory, 424 // `CallContexts`, and `ResourceTable`. There's no native API for that 425 // so it's hacked around a bit. This unsafe pointer cast could be fixed 426 // with more methods in more places, but it doesn't seem worth doing it 427 // at this time. 428 let memory = options 429 .memory 430 .map(|_| options.memory(unsafe { &*(store as *const StoreOpaque) })); 431 let (calls, host_table, host_resource_data, instance) = 432 store.component_resource_state_with_instance(instance_handle); 433 434 LiftContext { 435 memory, 436 options, 437 types, 438 instance, 439 instance_handle, 440 calls, 441 host_table, 442 host_resource_data, 443 } 444 } 445 446 /// Returns the entire contents of linear memory for this set of lifting 447 /// options. 448 /// 449 /// # Panics 450 /// 451 /// This will panic if memory has not been configured for this lifting 452 /// operation. 453 pub fn memory(&self) -> &'a [u8] { 454 self.memory.unwrap() 455 } 456 457 /// Returns an identifier for the store from which this `LiftContext` was 458 /// created. 459 pub fn store_id(&self) -> StoreId { 460 self.options.store_id 461 } 462 463 /// Returns the component instance that is being lifted from. 464 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 465 self.instance.as_mut() 466 } 467 /// Returns the component instance that is being lifted from. 468 pub fn instance_handle(&self) -> Instance { 469 self.instance_handle 470 } 471 472 /// Lifts an `own` resource from the guest at the `idx` specified into its 473 /// representation. 474 /// 475 /// Additionally returns a destructor/instance flags to go along with the 476 /// representation so the host knows how to destroy this resource. 477 pub fn guest_resource_lift_own( 478 &mut self, 479 ty: TypeResourceTableIndex, 480 idx: u32, 481 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> { 482 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 483 let (dtor, flags) = self.instance.dtor_and_flags(ty); 484 Ok((idx, dtor, flags)) 485 } 486 487 /// Lifts a `borrow` resource from the guest at the `idx` specified. 488 pub fn guest_resource_lift_borrow( 489 &mut self, 490 ty: TypeResourceTableIndex, 491 idx: u32, 492 ) -> Result<u32> { 493 self.resource_tables().guest_resource_lift_borrow(idx, ty) 494 } 495 496 /// Lowers a resource into the host-owned table, returning the index it was 497 /// inserted at. 498 pub fn host_resource_lower_own( 499 &mut self, 500 rep: u32, 501 dtor: Option<NonNull<VMFuncRef>>, 502 flags: Option<InstanceFlags>, 503 ) -> Result<HostResourceIndex> { 504 self.resource_tables() 505 .host_resource_lower_own(rep, dtor, flags) 506 } 507 508 /// Lowers a resource into the host-owned table, returning the index it was 509 /// inserted at. 510 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 511 self.resource_tables().host_resource_lower_borrow(rep) 512 } 513 514 /// Returns the underlying type of the resource table specified by `ty`. 515 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 516 self.instance_type().resource_type(ty) 517 } 518 519 /// Returns instance type information for the component instance that is 520 /// being lifted from. 521 pub fn instance_type(&self) -> InstanceType<'_> { 522 InstanceType::new(&self.instance) 523 } 524 525 fn resource_tables(&mut self) -> HostResourceTables<'_> { 526 HostResourceTables::from_parts( 527 ResourceTables { 528 host_table: Some(self.host_table), 529 calls: self.calls, 530 // Note that the unsafety here should be valid given the contract of 531 // `LiftContext::new`. 532 guest: Some(self.instance.as_mut().guest_tables()), 533 }, 534 self.host_resource_data, 535 ) 536 } 537 538 /// See [`HostResourceTables::enter_call`]. 539 #[inline] 540 pub fn enter_call(&mut self) { 541 self.resource_tables().enter_call() 542 } 543 544 /// See [`HostResourceTables::exit_call`]. 545 #[inline] 546 pub fn exit_call(&mut self) -> Result<()> { 547 self.resource_tables().exit_call() 548 } 549 } 550