From c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 7 Aug 2020 16:38:01 -0500 Subject: [PATCH] wasmtime: Refactor how imports are resolved (#2102) This commit removes all import resolution handling from the `wasmtime-jit` crate, instead moving the logic to the `wasmtime` crate. Previously `wasmtime-jit` had a generic `Resolver` trait and would do all the import type matching itself, but with the upcoming module-linking implementation this is going to get much trickier. The goal of this commit is to centralize all meaty "preparation" logic for instantiation into one location, probably the `wasmtime` crate itself. Instantiation will soon involve recursive instantiation and management of alias definitions as well. Having everything in one location, especially with access to `Store` so we can persist instances for safety, will be quite convenient. Additionally the `Resolver` trait isn't really necessary any more since imports are, at the lowest level, provided as a list rather than a map of some kind. More generic resolution functionality is provided via `Linker` or user layers on top of `Instance::new` itself. This makes matching up provided items to expected imports much easier as well. Overall this is largely just moving code around, but most of the code in the previous `resolve_imports` phase can be deleted since a lot of it is handled by surrounding pieces of `wasmtime` as well. --- crates/jit/src/imports.rs | 263 ------------------ crates/jit/src/instantiate.rs | 11 +- crates/jit/src/lib.rs | 3 - crates/jit/src/resolver.rs | 26 -- crates/runtime/src/imports.rs | 46 +-- crates/runtime/src/instance.rs | 16 +- crates/runtime/src/sig_registry.rs | 5 + crates/wasmtime/src/externals.rs | 74 ++++- crates/wasmtime/src/func.rs | 18 +- crates/wasmtime/src/instance.rs | 123 +++++--- .../wasmtime/src/trampoline/create_handle.rs | 12 +- crates/wasmtime/src/trampoline/func.rs | 11 +- crates/wasmtime/src/trampoline/global.rs | 4 +- crates/wasmtime/src/trampoline/memory.rs | 2 +- crates/wasmtime/src/trampoline/table.rs | 2 +- 15 files changed, 209 insertions(+), 407 deletions(-) delete mode 100644 crates/jit/src/imports.rs delete mode 100644 crates/jit/src/resolver.rs diff --git a/crates/jit/src/imports.rs b/crates/jit/src/imports.rs deleted file mode 100644 index 3c00ce4aeb..0000000000 --- a/crates/jit/src/imports.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! Module imports resolving logic. - -use crate::resolver::Resolver; -use more_asserts::assert_ge; -use std::convert::TryInto; -use wasmtime_environ::entity::PrimaryMap; -use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType}; -use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, TablePlan}; -use wasmtime_runtime::{ - Export, Imports, LinkError, SignatureRegistry, VMFunctionImport, VMGlobalImport, - VMMemoryImport, VMTableImport, -}; - -/// This function allows to match all imports of a `Module` with concrete definitions provided by -/// a `Resolver`. -/// -/// If all imports are satisfied returns an `Imports` instance required for a module instantiation. -pub fn resolve_imports( - module: &Module, - signatures: &SignatureRegistry, - resolver: &mut dyn Resolver, -) -> Result { - let mut function_imports = PrimaryMap::with_capacity(module.num_imported_funcs); - let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables); - let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories); - let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals); - - for (import_idx, (module_name, field_name, import)) in module.imports.iter().enumerate() { - let import_idx = import_idx.try_into().unwrap(); - let export = resolver.resolve(import_idx, module_name, field_name); - - match (import, &export) { - (EntityIndex::Function(func_index), Some(Export::Function(f))) => { - let import_signature = module.native_func_signature(*func_index); - let signature = signatures - .lookup_native(unsafe { f.anyfunc.as_ref().type_index }) - .unwrap(); - if signature != *import_signature { - // TODO: If the difference is in the calling convention, - // we could emit a wrapper function to fix it up. - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported function with signature {} \ - incompatible with function import with signature {}", - module_name, field_name, signature, import_signature - ))); - } - function_imports.push(VMFunctionImport { - body: unsafe { f.anyfunc.as_ref().func_ptr }, - vmctx: unsafe { f.anyfunc.as_ref().vmctx }, - }); - } - (EntityIndex::Function(_), Some(_)) => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with function import", - module_name, field_name - ))); - } - (EntityIndex::Function(_), None) => { - return Err(LinkError(format!( - "{}/{}: unknown import function: function not provided", - module_name, field_name - ))); - } - - (EntityIndex::Table(table_index), Some(Export::Table(t))) => { - let import_table = &module.table_plans[*table_index]; - if !is_table_compatible(&t.table, import_table) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported table incompatible with \ - table import", - module_name, field_name, - ))); - } - table_imports.push(VMTableImport { - from: t.definition, - vmctx: t.vmctx, - }); - } - (EntityIndex::Table(_), Some(_)) => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with table import", - module_name, field_name - ))); - } - (EntityIndex::Table(_), None) => { - return Err(LinkError(format!( - "{}/{}: unknown import table: table not provided", - module_name, field_name - ))); - } - - (EntityIndex::Memory(memory_index), Some(Export::Memory(m))) => { - let import_memory = &module.memory_plans[*memory_index]; - if !is_memory_compatible(&m.memory, import_memory) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported memory incompatible with \ - memory import", - module_name, field_name - ))); - } - - // Sanity-check: Ensure that the imported memory has at least - // guard-page protections the importing module expects it to have. - if let ( - MemoryStyle::Static { bound }, - MemoryStyle::Static { - bound: import_bound, - }, - ) = (&m.memory.style, &import_memory.style) - { - assert_ge!(*bound, *import_bound); - } - assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size); - - memory_imports.push(VMMemoryImport { - from: m.definition, - vmctx: m.vmctx, - }); - } - (EntityIndex::Memory(_), Some(_)) => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with memory import", - module_name, field_name - ))); - } - (EntityIndex::Memory(_), None) => { - return Err(LinkError(format!( - "{}/{}: unknown import memory: memory not provided", - module_name, field_name - ))); - } - - (EntityIndex::Global(global_index), Some(Export::Global(g))) => { - let imported_global = module.globals[*global_index]; - if !is_global_compatible(&g.global, &imported_global) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported global incompatible with \ - global import", - module_name, field_name - ))); - } - global_imports.push(VMGlobalImport { from: g.definition }); - } - (EntityIndex::Global(_), Some(_)) => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with global import", - module_name, field_name - ))); - } - (EntityIndex::Global(_), None) => { - return Err(LinkError(format!( - "{}/{}: unknown import global: global not provided", - module_name, field_name - ))); - } - } - } - - Ok(Imports::new( - function_imports, - table_imports, - memory_imports, - global_imports, - )) -} - -fn is_global_compatible(exported: &Global, imported: &Global) -> bool { - match imported.initializer { - GlobalInit::Import => (), - _ => panic!("imported Global should have an Imported initializer"), - } - - let Global { - wasm_ty: exported_wasm_ty, - ty: exported_ty, - mutability: exported_mutability, - initializer: _exported_initializer, - } = exported; - let Global { - wasm_ty: imported_wasm_ty, - ty: imported_ty, - mutability: imported_mutability, - initializer: _imported_initializer, - } = imported; - exported_wasm_ty == imported_wasm_ty - && exported_ty == imported_ty - && imported_mutability == exported_mutability -} - -fn is_table_element_type_compatible( - exported_type: TableElementType, - imported_type: TableElementType, -) -> bool { - match exported_type { - TableElementType::Func => match imported_type { - TableElementType::Func => true, - _ => false, - }, - TableElementType::Val(exported_val_ty) => match imported_type { - TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty, - _ => false, - }, - } -} - -fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool { - let TablePlan { - table: - Table { - wasm_ty: exported_wasm_ty, - ty: exported_ty, - minimum: exported_minimum, - maximum: exported_maximum, - }, - style: _exported_style, - } = exported; - let TablePlan { - table: - Table { - wasm_ty: imported_wasm_ty, - ty: imported_ty, - minimum: imported_minimum, - maximum: imported_maximum, - }, - style: _imported_style, - } = imported; - - exported_wasm_ty == imported_wasm_ty - && is_table_element_type_compatible(*exported_ty, *imported_ty) - && imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) -} - -fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool { - let MemoryPlan { - memory: - Memory { - minimum: exported_minimum, - maximum: exported_maximum, - shared: exported_shared, - }, - style: _exported_style, - offset_guard_size: _exported_offset_guard_size, - } = exported; - let MemoryPlan { - memory: - Memory { - minimum: imported_minimum, - maximum: imported_maximum, - shared: imported_shared, - }, - style: _imported_style, - offset_guard_size: _imported_offset_guard_size, - } = imported; - - imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) - && exported_shared == imported_shared -} diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index ee1bffdebe..71f81cb342 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -5,10 +5,8 @@ use crate::code_memory::CodeMemory; use crate::compiler::{Compilation, Compiler}; -use crate::imports::resolve_imports; use crate::link::link_module; use crate::object::ObjectUnwindInfo; -use crate::resolver::Resolver; use object::File as ObjectFile; use serde::{Deserialize, Serialize}; use std::any::Any; @@ -24,10 +22,10 @@ use wasmtime_environ::{ ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation, }; use wasmtime_profiling::ProfilingAgent; -use wasmtime_runtime::VMInterrupts; use wasmtime_runtime::{ - GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator, - SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMTrampoline, + GdbJitImageRegistration, Imports, InstanceHandle, InstantiationError, RuntimeMemoryCreator, + SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMInterrupts, + VMTrampoline, }; /// An error condition while setting up a wasm instance, be it validation, @@ -245,7 +243,7 @@ impl CompiledModule { /// See `InstanceHandle::new` pub unsafe fn instantiate( &self, - resolver: &mut dyn Resolver, + imports: Imports<'_>, signature_registry: &mut SignatureRegistry, mem_creator: Option<&dyn RuntimeMemoryCreator>, interrupts: Arc, @@ -271,7 +269,6 @@ impl CompiledModule { let finished_functions = self.finished_functions.0.clone(); - let imports = resolve_imports(&self.module, signature_registry, resolver)?; InstanceHandle::new( self.module.clone(), self.code.clone(), diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs index ca7d9fde06..28c1d701c4 100644 --- a/crates/jit/src/lib.rs +++ b/crates/jit/src/lib.rs @@ -23,11 +23,9 @@ mod code_memory; mod compiler; -mod imports; mod instantiate; mod link; mod object; -mod resolver; mod unwind; pub mod native; @@ -37,7 +35,6 @@ pub use crate::code_memory::CodeMemory; pub use crate::compiler::{Compilation, CompilationStrategy, Compiler}; pub use crate::instantiate::{CompilationArtifacts, CompiledModule, SetupError}; pub use crate::link::link_module; -pub use crate::resolver::{NullResolver, Resolver}; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/crates/jit/src/resolver.rs b/crates/jit/src/resolver.rs deleted file mode 100644 index 34f052c45a..0000000000 --- a/crates/jit/src/resolver.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Define the `Resolver` trait, allowing custom resolution for external -//! references. - -use wasmtime_runtime::Export; - -/// Import resolver connects imports with available exported values. -pub trait Resolver { - /// Resolves an import a WebAssembly module to an export it's hooked up to. - /// - /// The `index` provided is the index of the import in the wasm module - /// that's being resolved. For example 1 means that it's the second import - /// listed in the wasm module. - /// - /// The `module` and `field` arguments provided are the module/field names - /// listed on the import itself. - fn resolve(&mut self, index: u32, module: &str, field: &str) -> Option; -} - -/// `Resolver` implementation that always resolves to `None`. -pub struct NullResolver {} - -impl Resolver for NullResolver { - fn resolve(&mut self, _idx: u32, _module: &str, _field: &str) -> Option { - None - } -} diff --git a/crates/runtime/src/imports.rs b/crates/runtime/src/imports.rs index 14360a7036..2f85ba2201 100644 --- a/crates/runtime/src/imports.rs +++ b/crates/runtime/src/imports.rs @@ -1,46 +1,22 @@ use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; -use wasmtime_environ::entity::{BoxedSlice, PrimaryMap}; -use wasmtime_environ::wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex}; /// Resolved import pointers. -#[derive(Clone)] -pub struct Imports { +/// +/// Note that each of these fields are slices, not `PrimaryMap`. They should be +/// stored in index-order as with the module that we're providing the imports +/// for, and indexing is all done the same way as the main module's index +/// spaces. +#[derive(Clone, Default)] +pub struct Imports<'a> { /// Resolved addresses for imported functions. - pub functions: BoxedSlice, + pub functions: &'a [VMFunctionImport], /// Resolved addresses for imported tables. - pub tables: BoxedSlice, + pub tables: &'a [VMTableImport], /// Resolved addresses for imported memories. - pub memories: BoxedSlice, + pub memories: &'a [VMMemoryImport], /// Resolved addresses for imported globals. - pub globals: BoxedSlice, -} - -impl Imports { - /// Construct a new `Imports` instance. - pub fn new( - function_imports: PrimaryMap, - table_imports: PrimaryMap, - memory_imports: PrimaryMap, - global_imports: PrimaryMap, - ) -> Self { - Self { - functions: function_imports.into_boxed_slice(), - tables: table_imports.into_boxed_slice(), - memories: memory_imports.into_boxed_slice(), - globals: global_imports.into_boxed_slice(), - } - } - - /// Construct a new `Imports` instance with no imports. - pub fn none() -> Self { - Self { - functions: PrimaryMap::new().into_boxed_slice(), - tables: PrimaryMap::new().into_boxed_slice(), - memories: PrimaryMap::new().into_boxed_slice(), - globals: PrimaryMap::new().into_boxed_slice(), - } - } + pub globals: &'a [VMGlobalImport], } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index eac1954c79..e53b147cbe 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -897,28 +897,36 @@ impl InstanceHandle { }; let instance = handle.instance(); + debug_assert_eq!(vmshared_signatures.len(), handle.module().signatures.len()); ptr::copy( vmshared_signatures.values().as_slice().as_ptr(), instance.signature_ids_ptr() as *mut VMSharedSignatureIndex, vmshared_signatures.len(), ); + debug_assert_eq!(imports.functions.len(), handle.module().num_imported_funcs); ptr::copy( - imports.functions.values().as_slice().as_ptr(), + imports.functions.as_ptr(), instance.imported_functions_ptr() as *mut VMFunctionImport, imports.functions.len(), ); + debug_assert_eq!(imports.tables.len(), handle.module().num_imported_tables); ptr::copy( - imports.tables.values().as_slice().as_ptr(), + imports.tables.as_ptr(), instance.imported_tables_ptr() as *mut VMTableImport, imports.tables.len(), ); + debug_assert_eq!( + imports.memories.len(), + handle.module().num_imported_memories + ); ptr::copy( - imports.memories.values().as_slice().as_ptr(), + imports.memories.as_ptr(), instance.imported_memories_ptr() as *mut VMMemoryImport, imports.memories.len(), ); + debug_assert_eq!(imports.globals.len(), handle.module().num_imported_globals); ptr::copy( - imports.globals.values().as_slice().as_ptr(), + imports.globals.as_ptr(), instance.imported_globals_ptr() as *mut VMGlobalImport, imports.globals.len(), ); diff --git a/crates/runtime/src/sig_registry.rs b/crates/runtime/src/sig_registry.rs index 30e5514bbd..ca7ba57434 100644 --- a/crates/runtime/src/sig_registry.rs +++ b/crates/runtime/src/sig_registry.rs @@ -50,6 +50,11 @@ impl SignatureRegistry { } } + /// Looks up a shared index from the wasm signature itself. + pub fn lookup(&self, wasm: &WasmFuncType) -> Option { + self.wasm2index.get(wasm).cloned() + } + /// Looks up a shared native signature within this registry. /// /// Note that for this operation to be semantically correct the `idx` must diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index ad25a6370d..096cfd45c7 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -86,15 +86,6 @@ impl Extern { } } - pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export { - match self { - Extern::Func(f) => f.wasmtime_function().clone().into(), - Extern::Global(g) => g.wasmtime_export.clone().into(), - Extern::Memory(m) => m.wasmtime_export.clone().into(), - Extern::Table(t) => t.wasmtime_export.clone().into(), - } - } - pub(crate) fn from_wasmtime_export( wasmtime_export: wasmtime_runtime::Export, instance: StoreInstanceHandle, @@ -124,6 +115,15 @@ impl Extern { }; Store::same(my_store, store) } + + pub(crate) fn desc(&self) -> &'static str { + match self { + Extern::Func(_) => "function", + Extern::Table(_) => "table", + Extern::Memory(_) => "memory", + Extern::Global(_) => "global", + } + } } impl From for Extern { @@ -293,6 +293,19 @@ impl Global { wasmtime_export, } } + + pub(crate) fn matches_expected(&self, expected: &wasmtime_environ::wasm::Global) -> bool { + let actual = &self.wasmtime_export.global; + expected.ty == actual.ty + && expected.wasm_ty == actual.wasm_ty + && expected.mutability == actual.mutability + } + + pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport { + wasmtime_runtime::VMGlobalImport { + from: self.wasmtime_export.definition, + } + } } /// A WebAssembly `table`, or an array of values. @@ -524,6 +537,28 @@ impl Table { wasmtime_export, } } + + pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::TablePlan) -> bool { + let expected = &ty.table; + let actual = &self.wasmtime_export.table.table; + expected.wasm_ty == actual.wasm_ty + && expected.ty == actual.ty + && expected.minimum <= actual.minimum + && match expected.maximum { + Some(expected) => match actual.maximum { + Some(actual) => expected >= actual, + None => false, + }, + None => true, + } + } + + pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport { + wasmtime_runtime::VMTableImport { + from: self.wasmtime_export.definition, + vmctx: self.wasmtime_export.vmctx, + } + } } /// A WebAssembly linear memory. @@ -924,6 +959,27 @@ impl Memory { wasmtime_export, } } + + pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::MemoryPlan) -> bool { + let expected = &ty.memory; + let actual = &self.wasmtime_export.memory.memory; + expected.shared == actual.shared + && expected.minimum <= actual.minimum + && match expected.maximum { + Some(expected) => match actual.maximum { + Some(actual) => expected >= actual, + None => false, + }, + None => true, + } + } + + pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport { + wasmtime_runtime::VMMemoryImport { + from: self.wasmtime_export.definition, + vmctx: self.wasmtime_export.vmctx, + } + } } /// A linear memory. This trait provides an interface for raw memory buffers which are used diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 3ec3ddbb9e..fbed5174b1 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -635,10 +635,6 @@ impl Func { Ok(results.into()) } - pub(crate) fn wasmtime_function(&self) -> &wasmtime_runtime::ExportFunction { - &self.export - } - pub(crate) fn caller_checked_anyfunc( &self, ) -> NonNull { @@ -789,6 +785,20 @@ impl Func { pub fn store(&self) -> &Store { &self.instance.store } + + pub(crate) fn matches_expected(&self, expected: VMSharedSignatureIndex) -> bool { + self.sig_index() == expected + } + + pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMFunctionImport { + unsafe { + let f = self.caller_checked_anyfunc(); + wasmtime_runtime::VMFunctionImport { + body: f.as_ref().func_ptr, + vmctx: f.as_ref().vmctx, + } + } + } } impl fmt::Debug for Func { diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index f4ecffe972..c36fef4d1e 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -1,53 +1,25 @@ use crate::trampoline::StoreInstanceHandle; use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap}; -use anyhow::{bail, Error, Result}; +use anyhow::{anyhow, bail, Context, Error, Result}; use std::any::Any; use std::mem; use wasmtime_environ::EntityIndex; -use wasmtime_jit::{CompiledModule, Resolver}; +use wasmtime_jit::CompiledModule; use wasmtime_runtime::{ - InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody, + Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, + VMFunctionBody, }; -struct SimpleResolver<'a> { - imports: &'a [Extern], -} - -impl Resolver for SimpleResolver<'_> { - fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option { - self.imports - .get(idx as usize) - .map(|i| i.get_wasmtime_export()) - } -} - fn instantiate( store: &Store, compiled_module: &CompiledModule, - imports: &[Extern], + imports: Imports<'_>, host: Box, ) -> Result { - // For now we have a restriction that the `Store` that we're working - // with is the same for everything involved here. - for import in imports { - if !import.comes_from_same_store(store) { - bail!("cross-`Store` instantiation is not currently supported"); - } - } - - if imports.len() != compiled_module.module().imports.len() { - bail!( - "wrong number of imports provided, {} != {}", - imports.len(), - compiled_module.module().imports.len() - ); - } - - let mut resolver = SimpleResolver { imports }; let config = store.engine().config(); let instance = unsafe { let instance = compiled_module.instantiate( - &mut resolver, + imports, &mut store.signatures_mut(), config.memory_creator.as_ref().map(|a| a as _), store.interrupts().clone(), @@ -196,7 +168,9 @@ impl Instance { frame_info_registration }); - let handle = instantiate(store, module.compiled_module(), imports, host_info)?; + let handle = with_imports(store, module.compiled_module(), imports, |imports| { + instantiate(store, module.compiled_module(), imports, host_info) + })?; Ok(Instance { handle, @@ -267,3 +241,82 @@ impl Instance { self.get_export(name)?.into_global() } } + +fn with_imports( + store: &Store, + module: &CompiledModule, + externs: &[Extern], + f: impl FnOnce(Imports<'_>) -> Result, +) -> Result { + let m = module.module(); + if externs.len() != m.imports.len() { + bail!( + "wrong number of imports provided, {} != {}", + externs.len(), + m.imports.len() + ); + } + + let mut tables = Vec::new(); + let mut functions = Vec::new(); + let mut globals = Vec::new(); + let mut memories = Vec::new(); + + let mut process = |expected: &EntityIndex, actual: &Extern| { + // For now we have a restriction that the `Store` that we're working + // with is the same for everything involved here. + if !actual.comes_from_same_store(store) { + bail!("cross-`Store` instantiation is not currently supported"); + } + + match *expected { + EntityIndex::Table(i) => tables.push(match actual { + Extern::Table(e) if e.matches_expected(&m.table_plans[i]) => e.vmimport(), + Extern::Table(_) => bail!("table types incompatible"), + _ => bail!("expected table, but found {}", actual.desc()), + }), + EntityIndex::Memory(i) => memories.push(match actual { + Extern::Memory(e) if e.matches_expected(&m.memory_plans[i]) => e.vmimport(), + Extern::Memory(_) => bail!("memory types incompatible"), + _ => bail!("expected memory, but found {}", actual.desc()), + }), + EntityIndex::Global(i) => globals.push(match actual { + Extern::Global(e) if e.matches_expected(&m.globals[i]) => e.vmimport(), + Extern::Global(_) => bail!("global types incompatible"), + _ => bail!("expected global, but found {}", actual.desc()), + }), + EntityIndex::Function(i) => { + let func = match actual { + Extern::Func(e) => e, + _ => bail!("expected function, but found {}", actual.desc()), + }; + // Look up the `i`th function's type from the module in our + // signature registry. If it's not present then we have no + // functions registered with that type, so `func` is guaranteed + // to not match. + let ty = store + .signatures_mut() + .lookup(&m.signatures[m.functions[i]].0) + .ok_or_else(|| anyhow!("function types incompatible"))?; + if !func.matches_expected(ty) { + bail!("function types incompatible"); + } + functions.push(func.vmimport()); + } + } + Ok(()) + }; + + for (expected, actual) in m.imports.iter().zip(externs) { + process(&expected.2, actual).with_context(|| { + format!("incompatible import type for {}/{}", expected.0, expected.1) + })?; + } + + return f(Imports { + tables: &tables, + functions: &functions, + globals: &globals, + memories: &memories, + }); +} diff --git a/crates/wasmtime/src/trampoline/create_handle.rs b/crates/wasmtime/src/trampoline/create_handle.rs index b8fde1622b..e876240908 100644 --- a/crates/wasmtime/src/trampoline/create_handle.rs +++ b/crates/wasmtime/src/trampoline/create_handle.rs @@ -7,7 +7,7 @@ use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use wasmtime_environ::entity::PrimaryMap; -use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex}; +use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::Module; use wasmtime_runtime::{ Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, @@ -20,14 +20,10 @@ pub(crate) fn create_handle( finished_functions: PrimaryMap, trampolines: HashMap, state: Box, - func_imports: PrimaryMap, + func_imports: &[VMFunctionImport], ) -> Result { - let imports = Imports::new( - func_imports, - PrimaryMap::new(), - PrimaryMap::new(), - PrimaryMap::new(), - ); + let mut imports = Imports::default(); + imports.functions = func_imports; // Compute indices into the shared signature table. let signatures = module diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs index 81baa99b20..85ef15359a 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/trampoline/func.rs @@ -262,7 +262,7 @@ pub fn create_handle_with_function( finished_functions, trampolines, Box::new(trampoline_state), - PrimaryMap::new(), + &[], ) .map(|instance| (instance, trampoline)) } @@ -298,12 +298,5 @@ pub unsafe fn create_handle_with_raw_function( let sig_id = store.register_signature(ft.to_wasm_func_type(), sig); trampolines.insert(sig_id, trampoline); - create_handle( - module, - store, - finished_functions, - trampolines, - state, - PrimaryMap::new(), - ) + create_handle(module, store, finished_functions, trampolines, state, &[]) } diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 7093e65164..b0f060cc4d 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -8,7 +8,7 @@ use wasmtime_runtime::VMFunctionImport; pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result { let mut module = Module::new(); - let mut func_imports = PrimaryMap::new(); + let mut func_imports = Vec::new(); let mut externref_init = None; let global = wasm::Global { @@ -68,7 +68,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result Result