diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index 43ff9506c06..3d1a8a04b31 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -12,8 +12,8 @@ use wasmer_types::{ SignatureIndex, TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, - VMTrampoline, + FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, + VMSharedSignatureIndex, VMTrampoline, }; /// An `Artifact` is the product that the `Engine` @@ -95,7 +95,6 @@ pub trait Artifact: Send + Sync + Upcastable { self.preinstantiate()?; let module = self.module(); - let (instance_ptr, offsets) = InstanceHandle::allocate_instance(&module); let (imports, import_initializers) = { let mut imports = resolve_imports( &module, @@ -114,15 +113,14 @@ pub trait Artifact: Send + Sync + Upcastable { }; // Get pointers to where metadata about local memories should live in VM memory. - let memory_definition_locations = - InstanceHandle::memory_definition_locations(instance_ptr, &offsets); + // Get pointers to where metadata about local tables should live in VM memory. + + let (allocator, memory_definition_locations, table_definition_locations) = + InstanceAllocator::new(&*module); let finished_memories = tunables .create_memories(&module, self.memory_styles(), &memory_definition_locations) .map_err(InstantiationError::Link)? .into_boxed_slice(); - // Get pointers to where metadata about local tables should live in VM memory. - let table_definition_locations = - InstanceHandle::table_definition_locations(instance_ptr, &offsets); let finished_tables = tunables .create_tables(&module, self.table_styles(), &table_definition_locations) .map_err(InstantiationError::Link)? @@ -135,8 +133,7 @@ pub trait Artifact: Send + Sync + Upcastable { self.register_frame_info(); let handle = InstanceHandle::new( - instance_ptr, - offsets, + allocator, module, self.finished_functions().clone(), self.finished_function_call_trampolines().clone(), diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index 45b6e9be133..2dbd3bbda60 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -2,7 +2,7 @@ // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md use crate::global::Global; -use crate::instance::InstanceAllocator; +use crate::instance::InstanceRef; use crate::memory::{Memory, MemoryStyle}; use crate::table::{Table, TableStyle}; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; @@ -49,8 +49,8 @@ pub struct VMExportFunction { pub call_trampoline: Option, /// A “reference” to the instance through the - /// `InstanceAllocator`. `None` if it is a host function. - pub instance_allocator: Option, + /// `InstanceRef`. `None` if it is a host function. + pub instance_allocator: Option, } /// # Safety @@ -74,8 +74,8 @@ pub struct VMExportTable { pub from: Arc, /// A “reference” to the instance through the - /// `InstanceAllocator`. `None` if it is a host function. - pub instance_allocator: Option, + /// `InstanceRef`. `None` if it is a host function. + pub instance_allocator: Option, } /// # Safety @@ -120,8 +120,8 @@ pub struct VMExportMemory { pub from: Arc, /// A “reference” to the instance through the - /// `InstanceAllocator`. `None` if it is a host function. - pub instance_allocator: Option, + /// `InstanceRef`. `None` if it is a host function. + pub instance_allocator: Option, } /// # Safety @@ -166,8 +166,8 @@ pub struct VMExportGlobal { pub from: Arc, /// A “reference” to the instance through the - /// `InstanceAllocator`. `None` if it is a host function. - pub instance_allocator: Option, + /// `InstanceRef`. `None` if it is a host function. + pub instance_allocator: Option, } /// # Safety diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs new file mode 100644 index 00000000000..2cdcd3d8589 --- /dev/null +++ b/lib/vm/src/instance/allocator.rs @@ -0,0 +1,197 @@ +use super::{Instance, InstanceRef}; +use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; +use crate::{ModuleInfo, VMOffsets}; +use std::alloc::{self, Layout}; +use std::convert::TryFrom; +use std::mem; +use std::ptr::{self, NonNull}; +use wasmer_types::entity::EntityRef; +use wasmer_types::{LocalMemoryIndex, LocalTableIndex}; + +/// This is an intermediate type that manages the raw allocation and +/// metadata when creating an [`Instance`]. +/// +/// This type will free the allocated memory if it's dropped before +/// being used. +/// +/// The [`InstanceAllocator::instance_layout`] computes the correct +/// layout to represent the wanted [`Instance`]. +/// +/// Then we use this layout to allocate an empty `Instance` properly. +pub struct InstanceAllocator { + /// The buffer that will contain the [`Instance`] and dynamic fields. + instance_ptr: NonNull, + /// The layout of the `instance_ptr` buffer. + instance_layout: Layout, + /// Information about the offsets into the `instance_ptr` buffer for + /// the dynamic fields. + offsets: VMOffsets, + /// Whether or not this type has transferred ownership of the + /// `instance_ptr` buffer. If it has not when being dropped, + /// the buffer should be freed. + consumed: bool, +} + +impl Drop for InstanceAllocator { + fn drop(&mut self) { + if !self.consumed { + // If `consumed` has not been set, then we still have ownership + // over the buffer and must free it. + let instance_ptr = self.instance_ptr.as_ptr(); + unsafe { + std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout); + } + } + } +} + +impl InstanceAllocator { + /// Allocates instance data for use with [`InstanceHandle::new`]. + /// + /// Returns a wrapper type around the allocation and 2 vectors of + /// pointers into the allocated buffer. These lists of pointers + /// correspond to the location in memory for the local memories and + /// tables respectively. These pointers should be written to before + /// calling [`InstanceHandle::new`]. + pub fn new( + module: &ModuleInfo, + ) -> ( + InstanceAllocator, + Vec>, + Vec>, + ) { + let offsets = VMOffsets::new(mem::size_of::() as u8, module); + let instance_layout = Self::instance_layout(&offsets); + + #[allow(clippy::cast_ptr_alignment)] + let instance_ptr = unsafe { alloc::alloc(instance_layout) as *mut Instance }; + + let instance_ptr = if let Some(ptr) = NonNull::new(instance_ptr) { + ptr + } else { + alloc::handle_alloc_error(instance_layout); + }; + + let allocator = Self { + instance_ptr, + instance_layout, + offsets, + consumed: false, + }; + + // # Safety + // Both of these calls are safe because we allocate the pointer + // above with the same `offsets` that these functions use. + // Thus there will be enough valid memory for both of them. + let memories = unsafe { allocator.memory_definition_locations() }; + let tables = unsafe { allocator.table_definition_locations() }; + + (allocator, memories, tables) + } + + /// Calculate the appropriate layout for the [`Instance`]. + fn instance_layout(offsets: &VMOffsets) -> Layout { + let vmctx_size = usize::try_from(offsets.size_of_vmctx()) + .expect("Failed to convert the size of `vmctx` to a `usize`"); + + let instance_vmctx_layout = + Layout::array::(vmctx_size).expect("Failed to create a layout for `VMContext`"); + + let (instance_layout, _offset) = Layout::new::() + .extend(instance_vmctx_layout) + .expect("Failed to extend to `Instance` layout to include `VMContext`"); + + instance_layout.pad_to_align() + } + + /// Get the locations of where the local [`VMMemoryDefinition`]s should be stored. + /// + /// This function lets us create `Memory` objects on the host with backing + /// memory in the VM. + /// + /// # Safety + /// - `instance_ptr` must point to enough memory that all of the + /// offsets in `offsets` point to valid locations in memory, + /// i.e. `instance_ptr` must have been allocated by + /// `Self::new`. + unsafe fn memory_definition_locations(&self) -> Vec> { + let num_memories = self.offsets.num_local_memories; + let num_memories = usize::try_from(num_memories).unwrap(); + let mut out = Vec::with_capacity(num_memories); + + // We need to do some pointer arithmetic now. The unit is `u8`. + let ptr = self.instance_ptr.cast::().as_ptr(); + let base_ptr = ptr.add(mem::size_of::()); + + for i in 0..num_memories { + let mem_offset = self + .offsets + .vmctx_vmmemory_definition(LocalMemoryIndex::new(i)); + let mem_offset = usize::try_from(mem_offset).unwrap(); + + let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset)); + + out.push(new_ptr.cast()); + } + + out + } + + /// Get the locations of where the [`VMTableDefinition`]s should be stored. + /// + /// This function lets us create [`Table`] objects on the host with backing + /// memory in the VM. + /// + /// # Safety + /// - `instance_ptr` must point to enough memory that all of the + /// offsets in `offsets` point to valid locations in memory, + /// i.e. `instance_ptr` must have been allocated by + /// `Self::new`. + unsafe fn table_definition_locations(&self) -> Vec> { + let num_tables = self.offsets.num_local_tables; + let num_tables = usize::try_from(num_tables).unwrap(); + let mut out = Vec::with_capacity(num_tables); + + // We need to do some pointer arithmetic now. The unit is `u8`. + let ptr = self.instance_ptr.cast::().as_ptr(); + let base_ptr = ptr.add(std::mem::size_of::()); + + for i in 0..num_tables { + let table_offset = self + .offsets + .vmctx_vmtable_definition(LocalTableIndex::new(i)); + let table_offset = usize::try_from(table_offset).unwrap(); + + let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset)); + + out.push(new_ptr.cast()); + } + out + } + + /// Finish preparing by writing the [`Instance`] into memory. + pub(crate) fn write_instance(mut self, instance: Instance) -> InstanceRef { + // prevent the old state's drop logic from being called as we + // transition into the new state. + self.consumed = true; + + unsafe { + // `instance` is moved at `instance_ptr`. This pointer has + // been allocated by `Self::allocate_instance` (so by + // `InstanceRef::allocate_instance`. + ptr::write(self.instance_ptr.as_ptr(), instance); + // Now `instance_ptr` is correctly initialized! + } + let instance = self.instance_ptr; + let instance_layout = self.instance_layout; + + // This is correct because of the invariants of `Self` and + // because we write `Instance` to the pointer in this function. + unsafe { InstanceRef::new(instance, instance_layout) } + } + + /// Get the [`VMOffsets`] for the allocated buffer. + pub(crate) fn offsets(&self) -> &VMOffsets { + &self.offsets + } +} diff --git a/lib/vm/src/instance.rs b/lib/vm/src/instance/mod.rs similarity index 86% rename from lib/vm/src/instance.rs rename to lib/vm/src/instance/mod.rs index d8f546d59c8..dd417581aa3 100644 --- a/lib/vm/src/instance.rs +++ b/lib/vm/src/instance/mod.rs @@ -3,9 +3,13 @@ //! An `Instance` contains all the runtime state used by execution of //! a WebAssembly module (except its callstack and register state). An -//! `InstanceAllocator` is a wrapper around `Instance` that manages +//! `InstanceRef` is a wrapper around `Instance` that manages //! how it is allocated and deallocated. An `InstanceHandle` is a -//! wrapper around an `InstanceAllocator`. +//! wrapper around an `InstanceRef`. + +mod allocator; + +pub use allocator::InstanceAllocator; use crate::export::VMExport; use crate::global::Global; @@ -23,7 +27,7 @@ use crate::{FunctionBodyPtr, ModuleInfo, VMOffsets}; use crate::{VMExportFunction, VMExportGlobal, VMExportMemory, VMExportTable}; use memoffset::offset_of; use more_asserts::assert_lt; -use std::alloc::{self, Layout}; +use std::alloc::Layout; use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; @@ -688,15 +692,16 @@ impl Instance { } } -/// An `InstanceAllocator` is responsible to allocate, to deallocate, +/// TODO: update these docs +/// An `InstanceRef` is responsible to allocate, to deallocate, /// and to give access to an `Instance`, in such a way that `Instance` /// is unique, can be shared, safely, across threads, without -/// duplicating the pointer in multiple locations. `InstanceAllocator` +/// duplicating the pointer in multiple locations. `InstanceRef` /// must be the only “owner” of an `Instance`. /// /// Consequently, one must not share `Instance` but -/// `InstanceAllocator`. It acts like an Atomically Reference Counted -/// to `Instance`. In short, `InstanceAllocator` is roughly a +/// `InstanceRef`. It acts like an Atomically Reference Counted +/// to `Instance`. In short, `InstanceRef` is roughly a /// simplified version of `std::sync::Arc`. /// /// It is important to remind that `Instance` is dynamically-sized @@ -707,31 +712,25 @@ impl Instance { /// 1. Define the correct layout for `Instance` (size and alignment), /// 2. Allocate it properly. /// -/// The `InstanceAllocator::instance_layout` computes the correct -/// layout to represent the wanted `Instance`. -/// -/// Then `InstanceAllocator::allocate_instance` will use this layout -/// to allocate an empty `Instance` properly. This allocation must be -/// freed with `InstanceAllocator::deallocate_instance` if and only if -/// it has been set correctly. The `Drop` implementation of -/// `InstanceAllocator` calls its `deallocate_instance` method without -/// checking if this property holds, only when `Self.strong` is equal -/// to 1. +/// This allocation must be freed with [`InstanceRef::deallocate_instance`] +/// if and only if it has been set correctly. The `Drop` implementation of +/// [`InstanceRef`] calls its `deallocate_instance` method without +/// checking if this property holds, only when `Self.strong` is equal to 1. /// -/// Note for the curious reader: `InstanceHandle::allocate_instance` -/// and `InstanceHandle::new` will respectively allocate a proper +/// Note for the curious reader: [`InstanceAllocator::new`] +/// and [`InstanceHandle::new`] will respectively allocate a proper /// `Instance` and will fill it correctly. /// /// A little bit of background: The initial goal was to be able to -/// shared an `Instance` between an `InstanceHandle` and the module -/// exports, so that one can drop a `InstanceHandle` but still being +/// share an [`Instance`] between an [`InstanceHandle`] and the module +/// exports, so that one can drop a [`InstanceHandle`] but still being /// able to use the exports properly. /// /// This structure has a C representation because `Instance` is /// dynamically-sized, and the `instance` field must be last. #[derive(Debug)] #[repr(C)] -pub struct InstanceAllocator { +pub struct InstanceRef { /// Number of `Self` in the nature. It increases when `Self` is /// cloned, and it decreases when `Self` is dropped. strong: Arc, @@ -740,7 +739,7 @@ pub struct InstanceAllocator { instance_layout: Layout, /// The `Instance` itself. It must be the last field of - /// `InstanceAllocator` since `Instance` is dyamically-sized. + /// `InstanceRef` since `Instance` is dyamically-sized. /// /// `Instance` must not be dropped manually by Rust, because it's /// allocated manually with `alloc` and a specific layout (Rust @@ -752,14 +751,8 @@ pub struct InstanceAllocator { instance: NonNull, } -impl InstanceAllocator { - /// A soft limit on the amount of references that may be made to an `InstanceAllocator`. - /// - /// Going above this limit will make the program to panic at exactly - /// `MAX_REFCOUNT` references. - const MAX_REFCOUNT: usize = std::usize::MAX - 1; - - /// Create a new `InstanceAllocator`. It allocates nothing. It +impl InstanceRef { + /// Create a new `InstanceRef`. It allocates nothing. It /// fills nothing. The `Instance` must be already valid and /// filled. `self_ptr` and `self_layout` must be the pointer and /// the layout returned by `Self::allocate_self` used to build @@ -769,9 +762,10 @@ impl InstanceAllocator { /// /// `instance` must a non-null, non-dangling, properly aligned, /// and correctly initialized pointer to `Instance`. See - /// `InstanceHandle::new` for an example of how to correctly use + /// [`InstanceAllocator::new`] for an example of how to correctly use /// this API. - unsafe fn new(instance: NonNull, instance_layout: Layout) -> Self { + /// TODO: update docs + pub(crate) unsafe fn new(instance: NonNull, instance_layout: Layout) -> Self { Self { strong: Arc::new(atomic::AtomicUsize::new(1)), instance_layout, @@ -779,38 +773,11 @@ impl InstanceAllocator { } } - /// Calculate the appropriate layout for `Instance`. - fn instance_layout(offsets: &VMOffsets) -> Layout { - let vmctx_size = usize::try_from(offsets.size_of_vmctx()) - .expect("Failed to convert the size of `vmctx` to a `usize`"); - - let instance_vmctx_layout = - Layout::array::(vmctx_size).expect("Failed to create a layout for `VMContext`"); - - let (instance_layout, _offset) = Layout::new::() - .extend(instance_vmctx_layout) - .expect("Failed to extend to `Instance` layout to include `VMContext`"); - - instance_layout.pad_to_align() - } - - /// Allocate `Instance` (it is an uninitialized pointer). + /// A soft limit on the amount of references that may be made to an `InstanceRef`. /// - /// `offsets` is used to compute the layout with `Self::instance_layout`. - fn allocate_instance(offsets: &VMOffsets) -> (NonNull, Layout) { - let layout = Self::instance_layout(offsets); - - #[allow(clippy::cast_ptr_alignment)] - let instance_ptr = unsafe { alloc::alloc(layout) as *mut Instance }; - - let ptr = if let Some(ptr) = NonNull::new(instance_ptr) { - ptr - } else { - alloc::handle_alloc_error(layout); - }; - - (ptr, layout) - } + /// Going above this limit will make the program to panic at exactly + /// `MAX_REFCOUNT` references. + const MAX_REFCOUNT: usize = std::usize::MAX - 1; /// Deallocate `Instance`. /// @@ -826,7 +793,7 @@ impl InstanceAllocator { } /// Get the number of strong references pointing to this - /// `InstanceAllocator`. + /// `InstanceRef`. pub fn strong_count(&self) -> usize { self.strong.load(atomic::Ordering::SeqCst) } @@ -847,13 +814,13 @@ impl InstanceAllocator { } /// TODO: Review this super carefully. -unsafe impl Send for InstanceAllocator {} -unsafe impl Sync for InstanceAllocator {} +unsafe impl Send for InstanceRef {} +unsafe impl Sync for InstanceRef {} -impl Clone for InstanceAllocator { - /// Makes a clone of `InstanceAllocator`. +impl Clone for InstanceRef { + /// Makes a clone of `InstanceRef`. /// - /// This creates another `InstanceAllocator` using the same + /// This creates another `InstanceRef` using the same /// `instance` pointer, increasing the strong reference count. #[inline] fn clone(&self) -> Self { @@ -873,7 +840,7 @@ impl Clone for InstanceAllocator { let old_size = self.strong.fetch_add(1, atomic::Ordering::Relaxed); // However we need to guard against massive refcounts in case - // someone is `mem::forget`ing `InstanceAllocator`. If we + // someone is `mem::forget`ing `InstanceRef`. If we // don't do this the count can overflow and users will // use-after free. We racily saturate to `isize::MAX` on the // assumption that there aren't ~2 billion threads @@ -884,7 +851,7 @@ impl Clone for InstanceAllocator { // and we don't care to support it. if old_size > Self::MAX_REFCOUNT { - panic!("Too many references of `InstanceAllocator`"); + panic!("Too many references of `InstanceRef`"); } Self { @@ -895,16 +862,16 @@ impl Clone for InstanceAllocator { } } -impl PartialEq for InstanceAllocator { - /// Two `InstanceAllocator` are equal if and only if +impl PartialEq for InstanceRef { + /// Two `InstanceRef` are equal if and only if /// `Self.instance` points to the same location. fn eq(&self, other: &Self) -> bool { self.instance == other.instance } } -impl Drop for InstanceAllocator { - /// Drop the `InstanceAllocator`. +impl Drop for InstanceRef { + /// Drop the `InstanceRef`. /// /// This will decrement the strong reference count. If it reaches /// 1, then the `Self.instance` will be deallocated with @@ -938,39 +905,24 @@ impl Drop for InstanceAllocator { // Now we can deallocate the instance. Note that we don't // check the pointer to `Instance` is correctly initialized, // but the way `InstanceHandle` creates the - // `InstanceAllocator` ensures that. + // `InstanceRef` ensures that. unsafe { Self::deallocate_instance(self) }; } } -/// A handle holding an `InstanceAllocator`, which holds an `Instance` +/// A handle holding an `InstanceRef`, which holds an `Instance` /// of a WebAssembly module. /// /// This is more or less a public facade of the private `Instance`, /// providing useful higher-level API. #[derive(Debug, PartialEq)] pub struct InstanceHandle { - /// The `InstanceAllocator`. See its documentation to learn more. - instance: InstanceAllocator, + /// The `InstanceRef`. See its documentation to learn more. + instance: InstanceRef, } impl InstanceHandle { - /// Allocates an instance for use with `InstanceHandle::new`. - /// - /// Returns the instance pointer and the [`VMOffsets`] that describe the - /// memory buffer pointed to by the instance pointer. - /// - /// It should ideally return `NonNull` rather than - /// `NonNull`, however `Instance` is private, and we want to - /// keep it private. - pub fn allocate_instance(module: &ModuleInfo) -> (NonNull, VMOffsets) { - let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, module); - let (instance_ptr, _instance_layout) = InstanceAllocator::allocate_instance(&offsets); - - (instance_ptr.cast(), offsets) - } - - /// Create a new `InstanceHandle` pointing at a new `InstanceAllocator`. + /// Create a new `InstanceHandle` pointing at a new `InstanceRef`. /// /// # Safety /// @@ -987,17 +939,13 @@ impl InstanceHandle { /// safety. /// /// However the following must be taken care of before calling this function: - /// - `instance_ptr` must point to valid memory sufficiently large - /// for the `Instance`. `instance_ptr` will be owned by - /// `InstanceAllocator`, see `InstanceAllocator` to learn more. /// - The memory at `instance.tables_ptr()` must be initialized with data for /// all the local tables. /// - The memory at `instance.memories_ptr()` must be initialized with data for /// all the local memories. #[allow(clippy::too_many_arguments)] pub unsafe fn new( - instance_ptr: NonNull, - offsets: VMOffsets, + allocator: InstanceAllocator, module: Arc, finished_functions: BoxedSlice, finished_function_call_trampolines: BoxedSlice, @@ -1009,10 +957,6 @@ impl InstanceHandle { host_state: Box, import_initializers: ImportInitializerThunks, ) -> Result { - // `NonNull` here actually means `NonNull`. See - // `Self::allocate_instance` to understand why. - let instance_ptr: NonNull = instance_ptr.cast(); - let vmctx_globals = finished_globals .values() .map(|m| m.vmglobal()) @@ -1021,7 +965,7 @@ impl InstanceHandle { let passive_data = RefCell::new(module.passive_data.clone()); let handle = { - let instance_layout = InstanceAllocator::instance_layout(&offsets); + let offsets = allocator.offsets().clone(); // Create the `Instance`. The unique, the One. let instance = Instance { module, @@ -1039,20 +983,7 @@ impl InstanceHandle { vmctx: VMContext {}, }; - // `instance` is moved at `instance_ptr`. This pointer has - // been allocated by `Self::allocate_instance` (so by - // `InstanceAllocator::allocate_instance`. - ptr::write(instance_ptr.as_ptr(), instance); - - // Now `instance_ptr` is correctly initialized! - - // `instance_ptr` is passed to `InstanceAllocator`, which - // makes it the only “owner” (it doesn't own the value, - // it's just the semantics we define). - // - // SAFETY: `instance_ptr` fulfills all the requirement of - // `InstanceAllocator::new`. - let instance_allocator = InstanceAllocator::new(instance_ptr, instance_layout); + let instance_allocator = allocator.write_instance(instance); Self { instance: instance_allocator, @@ -1110,85 +1041,10 @@ impl InstanceHandle { } /// Return a reference to the contained `Instance`. - pub(crate) fn instance(&self) -> &InstanceAllocator { + pub(crate) fn instance(&self) -> &InstanceRef { &self.instance } - /// Get the locations of where the local `VMMemoryDefinition`s should be stored. - /// - /// This function lets us create `Memory` objects on the host with backing - /// memory in the VM. - /// - /// # Safety - /// - `instance_ptr` must point to enough memory that all of the - /// offsets in `offsets` point to valid locations in memory, - /// i.e. `instance_ptr` must have been allocated by - /// `InstanceHandle::allocate_instance`. - pub unsafe fn memory_definition_locations( - instance_ptr: NonNull, - offsets: &VMOffsets, - ) -> Vec> { - // `NonNull` here actually means `NonNull`. See - // `Self::allocate_instance` to understand why. - let instance_ptr: NonNull = instance_ptr.cast(); - - let num_memories = offsets.num_local_memories; - let num_memories = usize::try_from(num_memories).unwrap(); - let mut out = Vec::with_capacity(num_memories); - - // We need to do some pointer arithmetic now. The unit is `u8`. - let ptr = instance_ptr.cast::().as_ptr(); - let base_ptr = ptr.add(mem::size_of::()); - - for i in 0..num_memories { - let mem_offset = offsets.vmctx_vmmemory_definition(LocalMemoryIndex::new(i)); - let mem_offset = usize::try_from(mem_offset).unwrap(); - - let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset)); - - out.push(new_ptr.cast()); - } - - out - } - - /// Get the locations of where the `VMTableDefinition`s should be stored. - /// - /// This function lets us create `Table` objects on the host with backing - /// memory in the VM. - /// - /// # Safety - /// - `instance_ptr` must point to enough memory that all of the - /// offsets in `offsets` point to valid locations in memory, - /// i.e. `instance_ptr` must have been allocated by - /// `InstanceHandle::allocate_instance`. - pub unsafe fn table_definition_locations( - instance_ptr: NonNull, - offsets: &VMOffsets, - ) -> Vec> { - // `NonNull` here actually means `NonNull`. See - // `Self::allocate_instance` to understand why. - let instance_ptr: NonNull = instance_ptr.cast(); - - let num_tables = offsets.num_local_tables; - let num_tables = usize::try_from(num_tables).unwrap(); - let mut out = Vec::with_capacity(num_tables); - - // We need to do some pointer arithmetic now. The unit is `u8`. - let ptr = instance_ptr.cast::().as_ptr(); - let base_ptr = ptr.add(std::mem::size_of::()); - - for i in 0..num_tables { - let table_offset = offsets.vmctx_vmtable_definition(LocalTableIndex::new(i)); - let table_offset = usize::try_from(table_offset).unwrap(); - - let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset)); - - out.push(new_ptr.cast()); - } - out - } - /// Finishes the instantiation process started by `Instance::new`. /// /// # Safety diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index ecbadeca794..1d7a6358212 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -39,7 +39,7 @@ pub mod libcalls; pub use crate::export::*; pub use crate::global::*; pub use crate::imports::Imports; -pub use crate::instance::{ImportInitializerFuncPtr, InstanceHandle}; +pub use crate::instance::{ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle}; pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; pub use crate::mmap::Mmap; pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo}; diff --git a/lib/vm/src/vmoffsets.rs b/lib/vm/src/vmoffsets.rs index 3ca2073cb34..a786114705a 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/vm/src/vmoffsets.rs @@ -33,6 +33,7 @@ const fn align(offset: u32, width: u32) -> u32 { /// related structs that JIT code accesses directly. /// /// [`VMContext`]: crate::vmcontext::VMContext +#[derive(Clone, Debug)] pub struct VMOffsets { /// The size in bytes of a pointer on the target. pub pointer_size: u8,