wasmer_engine/
resolver.rs

1//! Define the `Resolver` trait, allowing custom resolution for external
2//! references.
3
4use crate::{Engine, ImportError, LinkError};
5use more_asserts::assert_ge;
6use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
7use wasmer_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType};
8
9use wasmer_vm::{
10    Export, ExportFunctionMetadata, FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle,
11    Resolver, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind,
12    VMGlobalImport, VMImport, VMImportType, VMMemoryImport, VMTableImport,
13};
14
15fn is_compatible_table(ex: &TableType, im: &TableType) -> bool {
16    (ex.ty == wasmer_types::Type::FuncRef || ex.ty == im.ty)
17        && im.minimum <= ex.minimum
18        && (im.maximum.is_none()
19            || (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap()))
20}
21
22fn is_compatible_memory(ex: &MemoryType, im: &MemoryType) -> bool {
23    im.minimum <= ex.minimum
24        && (im.maximum.is_none()
25            || (!ex.maximum.is_none() && im.maximum.unwrap() >= ex.maximum.unwrap()))
26        && ex.shared == im.shared
27}
28
29/// This function allows to match all imports of a `ModuleInfo` with concrete definitions provided by
30/// a `Resolver`.
31///
32/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
33pub fn resolve_imports(
34    engine: &dyn Engine,
35    resolver: &dyn Resolver,
36    import_counts: &ImportCounts,
37    imports: &[VMImport],
38    finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
39) -> Result<Imports, LinkError> {
40    let mut function_imports = PrimaryMap::with_capacity(import_counts.functions as _);
41    let mut host_function_env_initializers =
42        PrimaryMap::with_capacity(import_counts.functions as _);
43    let mut table_imports = PrimaryMap::with_capacity(import_counts.tables as _);
44    let mut memory_imports = PrimaryMap::with_capacity(import_counts.memories as _);
45    let mut global_imports = PrimaryMap::with_capacity(import_counts.globals as _);
46    for VMImport {
47        import_no,
48        module,
49        field,
50        ty,
51    } in imports
52    {
53        let resolved = resolver.resolve(*import_no, module, field);
54        let import_extern = || match ty {
55            &VMImportType::Table(t) => ExternType::Table(t),
56            &VMImportType::Memory(t, _) => ExternType::Memory(t),
57            &VMImportType::Global(t) => ExternType::Global(t),
58            &VMImportType::Function {
59                sig,
60                static_trampoline: _,
61            } => ExternType::Function(
62                engine
63                    .lookup_signature(sig)
64                    .expect("VMSharedSignatureIndex is not valid?"),
65            ),
66        };
67        let resolved = match resolved {
68            Some(r) => r,
69            None => {
70                return Err(LinkError::Import(
71                    module.to_string(),
72                    field.to_string(),
73                    ImportError::UnknownImport(import_extern()),
74                ));
75            }
76        };
77        let export_extern = || match resolved {
78            Export::Function(ref f) => ExternType::Function(
79                engine
80                    .lookup_signature(f.vm_function.signature)
81                    .expect("VMSharedSignatureIndex not registered with engine (wrong engine?)")
82                    .clone(),
83            ),
84            Export::Table(ref t) => ExternType::Table(*t.ty()),
85            Export::Memory(ref m) => ExternType::Memory(m.ty()),
86            Export::Global(ref g) => {
87                let global = g.from.ty();
88                ExternType::Global(*global)
89            }
90        };
91        match (&resolved, ty) {
92            (
93                Export::Function(ex),
94                VMImportType::Function {
95                    sig,
96                    static_trampoline,
97                },
98            ) if ex.vm_function.signature == *sig => {
99                let address = match ex.vm_function.kind {
100                    VMFunctionKind::Dynamic => {
101                        // If this is a dynamic imported function,
102                        // the address of the function is the address of the
103                        // reverse trampoline.
104                        let index = FunctionIndex::new(function_imports.len());
105                        finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
106
107                        // TODO: We should check that the f.vmctx actually matches
108                        // the shape of `VMDynamicFunctionImportContext`
109                    }
110                    VMFunctionKind::Static => ex.vm_function.address,
111                };
112
113                // Clone the host env for this `Instance`.
114                let env = if let Some(ExportFunctionMetadata {
115                    host_env_clone_fn: clone,
116                    ..
117                }) = ex.metadata.as_deref()
118                {
119                    // TODO: maybe start adding asserts in all these
120                    // unsafe blocks to prevent future changes from
121                    // horribly breaking things.
122                    unsafe {
123                        assert!(!ex.vm_function.vmctx.host_env.is_null());
124                        (clone)(ex.vm_function.vmctx.host_env)
125                    }
126                } else {
127                    // No `clone` function means we're dealing with some
128                    // other kind of `vmctx`, not a host env of any
129                    // kind.
130                    unsafe { ex.vm_function.vmctx.host_env }
131                };
132
133                let trampoline = if let Some(t) = ex.vm_function.call_trampoline {
134                    Some(t)
135                } else if let VMFunctionKind::Static = ex.vm_function.kind {
136                    // Look up a trampoline by finding one by the signature and fill it in.
137                    Some(*static_trampoline)
138                } else {
139                    // FIXME: remove this possibility entirely.
140                    None
141                };
142
143                function_imports.push(VMFunctionImport {
144                    body: FunctionBodyPtr(address),
145                    signature: *sig,
146                    environment: VMFunctionEnvironment { host_env: env },
147                    trampoline,
148                });
149
150                let initializer = ex
151                    .metadata
152                    .as_ref()
153                    .and_then(|m| m.import_init_function_ptr);
154                let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn);
155                let destructor = ex.metadata.as_ref().map(|m| m.host_env_drop_fn);
156                let import_function_env =
157                    if let (Some(clone), Some(destructor)) = (clone, destructor) {
158                        ImportFunctionEnv::Env {
159                            env,
160                            clone,
161                            initializer,
162                            destructor,
163                        }
164                    } else {
165                        ImportFunctionEnv::NoEnv
166                    };
167
168                host_function_env_initializers.push(import_function_env);
169            }
170            (Export::Table(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => {
171                let import_table_ty = ex.from.ty();
172                if import_table_ty.ty != im.ty {
173                    return Err(LinkError::Import(
174                        module.to_string(),
175                        field.to_string(),
176                        ImportError::IncompatibleType(import_extern(), export_extern()),
177                    ));
178                }
179                table_imports.push(VMTableImport {
180                    definition: ex.from.vmtable(),
181                    from: ex.from.clone(),
182                });
183            }
184            (Export::Memory(ex), VMImportType::Memory(im, import_memory_style))
185                if is_compatible_memory(&ex.ty(), im) =>
186            {
187                // Sanity-check: Ensure that the imported memory has at least
188                // guard-page protections the importing module expects it to have.
189                let export_memory_style = ex.style();
190                if let (
191                    MemoryStyle::Static { bound, .. },
192                    MemoryStyle::Static {
193                        bound: import_bound,
194                        ..
195                    },
196                ) = (export_memory_style.clone(), &import_memory_style)
197                {
198                    assert_ge!(bound, *import_bound);
199                }
200                assert_ge!(
201                    export_memory_style.offset_guard_size(),
202                    import_memory_style.offset_guard_size()
203                );
204                memory_imports.push(VMMemoryImport {
205                    definition: ex.from.vmmemory(),
206                    from: ex.from.clone(),
207                });
208            }
209
210            (Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => {
211                global_imports.push(VMGlobalImport {
212                    definition: ex.from.vmglobal(),
213                    from: ex.from.clone(),
214                });
215            }
216            _ => {
217                return Err(LinkError::Import(
218                    module.to_string(),
219                    field.to_string(),
220                    ImportError::IncompatibleType(import_extern(), export_extern()),
221                ));
222            }
223        }
224    }
225    Ok(Imports::new(
226        function_imports,
227        host_function_env_initializers,
228        table_imports,
229        memory_imports,
230        global_imports,
231    ))
232}