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