unc_vm_engine/
resolver.rs

1//! Define the `Resolver` trait, allowing custom resolution for external
2//! references.
3
4use crate::{ImportError, LinkError};
5use more_asserts::assert_ge;
6use unc_vm_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
7use unc_vm_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType};
8
9use unc_vm_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 == unc_vm_types::Type::FuncRef || ex.ty == im.ty)
17        && im.minimum <= ex.minimum
18        && (im.maximum.is_none()
19            || (ex.maximum.is_some() && 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_some() && 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: &crate::universal::UniversalEngine,
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 { import_no, module, field, ty } in imports {
47        let resolved = resolver.resolve(*import_no, module, field);
48        let import_extern = || match ty {
49            &VMImportType::Table(t) => ExternType::Table(t),
50            &VMImportType::Memory(t, _) => ExternType::Memory(t),
51            &VMImportType::Global(t) => ExternType::Global(t),
52            &VMImportType::Function { sig, static_trampoline: _ } => ExternType::Function(
53                engine.lookup_signature(sig).expect("VMSharedSignatureIndex is not valid?"),
54            ),
55        };
56        let resolved = match resolved {
57            Some(r) => r,
58            None => {
59                return Err(LinkError::Import(
60                    module.to_string(),
61                    field.to_string(),
62                    ImportError::UnknownImport(import_extern()).into(),
63                ));
64            }
65        };
66        let export_extern = || match resolved {
67            Export::Function(ref f) => ExternType::Function(
68                engine
69                    .lookup_signature(f.vm_function.signature)
70                    .expect("VMSharedSignatureIndex not registered with engine (wrong engine?)"),
71            ),
72            Export::Table(ref t) => ExternType::Table(*t.ty()),
73            Export::Memory(ref m) => ExternType::Memory(m.ty()),
74            Export::Global(ref g) => {
75                let global = g.from.ty();
76                ExternType::Global(*global)
77            }
78        };
79        match (&resolved, ty) {
80            (Export::Function(ex), VMImportType::Function { sig, static_trampoline })
81                if ex.vm_function.signature == *sig =>
82            {
83                let address = match ex.vm_function.kind {
84                    VMFunctionKind::Dynamic => {
85                        // If this is a dynamic imported function,
86                        // the address of the function is the address of the
87                        // reverse trampoline.
88                        let index = FunctionIndex::new(function_imports.len());
89                        finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
90
91                        // TODO: We should check that the f.vmctx actually matches
92                        // the shape of `VMDynamicFunctionImportContext`
93                    }
94                    VMFunctionKind::Static => ex.vm_function.address,
95                };
96
97                // Clone the host env for this `Instance`.
98                let env = if let Some(ExportFunctionMetadata { host_env_clone_fn: clone, .. }) =
99                    ex.metadata.as_deref()
100                {
101                    // TODO: maybe start adding asserts in all these
102                    // unsafe blocks to prevent future changes from
103                    // horribly breaking things.
104                    unsafe {
105                        assert!(!ex.vm_function.vmctx.host_env.is_null());
106                        (clone)(ex.vm_function.vmctx.host_env)
107                    }
108                } else {
109                    // No `clone` function means we're dealing with some
110                    // other kind of `vmctx`, not a host env of any
111                    // kind.
112                    unsafe { ex.vm_function.vmctx.host_env }
113                };
114
115                let trampoline = if let Some(t) = ex.vm_function.call_trampoline {
116                    Some(t)
117                } else if let VMFunctionKind::Static = ex.vm_function.kind {
118                    // Look up a trampoline by finding one by the signature and fill it in.
119                    Some(*static_trampoline)
120                } else {
121                    // FIXME: remove this possibility entirely.
122                    None
123                };
124
125                function_imports.push(VMFunctionImport {
126                    body: FunctionBodyPtr(address),
127                    signature: *sig,
128                    environment: VMFunctionEnvironment { host_env: env },
129                    trampoline,
130                });
131
132                let initializer = ex.metadata.as_ref().and_then(|m| m.import_init_function_ptr);
133                let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn);
134                let destructor = ex.metadata.as_ref().map(|m| m.host_env_drop_fn);
135                let import_function_env =
136                    if let (Some(clone), Some(destructor)) = (clone, destructor) {
137                        ImportFunctionEnv::Env { env, clone, initializer, destructor }
138                    } else {
139                        ImportFunctionEnv::NoEnv
140                    };
141
142                host_function_env_initializers.push(import_function_env);
143            }
144            (Export::Table(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => {
145                let import_table_ty = ex.from.ty();
146                if import_table_ty.ty != im.ty {
147                    return Err(LinkError::Import(
148                        module.to_string(),
149                        field.to_string(),
150                        ImportError::IncompatibleType(import_extern(), export_extern()).into(),
151                    ));
152                }
153                table_imports
154                    .push(VMTableImport { definition: ex.from.vmtable(), from: ex.from.clone() });
155            }
156            (Export::Memory(ex), VMImportType::Memory(im, import_memory_style))
157                if is_compatible_memory(&ex.ty(), im) =>
158            {
159                // Sanity-check: Ensure that the imported memory has at least
160                // guard-page protections the importing module expects it to have.
161                let export_memory_style = ex.style();
162                if let (
163                    MemoryStyle::Static { bound, .. },
164                    MemoryStyle::Static { bound: import_bound, .. },
165                ) = (export_memory_style.clone(), &import_memory_style)
166                {
167                    assert_ge!(bound, *import_bound);
168                }
169                assert_ge!(
170                    export_memory_style.offset_guard_size(),
171                    import_memory_style.offset_guard_size()
172                );
173                memory_imports
174                    .push(VMMemoryImport { definition: ex.from.vmmemory(), from: ex.from.clone() });
175            }
176
177            (Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => {
178                global_imports
179                    .push(VMGlobalImport { definition: ex.from.vmglobal(), from: ex.from.clone() });
180            }
181            _ => {
182                return Err(LinkError::Import(
183                    module.to_string(),
184                    field.to_string(),
185                    ImportError::IncompatibleType(import_extern(), export_extern()).into(),
186                ));
187            }
188        }
189    }
190    Ok(Imports::new(
191        function_imports,
192        host_function_env_initializers,
193        table_imports,
194        memory_imports,
195        global_imports,
196    ))
197}