wasmer_compiler/engine/
resolver.rs

1//! Custom resolution for external references.
2
3use crate::LinkError;
4use more_asserts::assert_ge;
5use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
6use wasmer_types::{
7    ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex,
8};
9
10use wasmer_vm::{
11    FunctionBodyPtr, Imports, LinearMemory, MemoryStyle, StoreObjects, TableStyle, VMExtern,
12    VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport,
13    VMTableImport,
14};
15
16/// Get an `ExternType` given a import index.
17fn get_extern_from_import(module: &ModuleInfo, import_index: &ImportIndex) -> ExternType {
18    match import_index {
19        ImportIndex::Function(index) => {
20            let func = module.signatures[module.functions[*index]].clone();
21            ExternType::Function(func)
22        }
23        ImportIndex::Table(index) => {
24            let table = module.tables[*index];
25            ExternType::Table(table)
26        }
27        ImportIndex::Memory(index) => {
28            let memory = module.memories[*index];
29            ExternType::Memory(memory)
30        }
31        ImportIndex::Global(index) => {
32            let global = module.globals[*index];
33            ExternType::Global(global)
34        }
35    }
36}
37
38/// Get an `ExternType` given an export (and Engine signatures in case is a function).
39fn get_extern_type(context: &StoreObjects, extern_: &VMExtern) -> ExternType {
40    match extern_ {
41        VMExtern::Function(f) => ExternType::Function(f.get(context).signature.clone()),
42        VMExtern::Table(t) => ExternType::Table(*t.get(context).ty()),
43        VMExtern::Memory(m) => ExternType::Memory(m.get(context).ty()),
44        VMExtern::Global(g) => {
45            let global = g.get(context).ty();
46            ExternType::Global(*global)
47        }
48    }
49}
50
51fn get_runtime_size(context: &StoreObjects, extern_: &VMExtern) -> Option<u32> {
52    match extern_ {
53        VMExtern::Table(t) => Some(t.get(context).get_runtime_size()),
54        VMExtern::Memory(m) => Some(m.get(context).get_runtime_size()),
55        _ => None,
56    }
57}
58
59/// This function allows to match all imports of a `ModuleInfo` with concrete definitions provided by
60/// a `Resolver`.
61///
62/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
63#[allow(clippy::result_large_err)]
64pub fn resolve_imports(
65    module: &ModuleInfo,
66    imports: &[VMExtern],
67    context: &mut StoreObjects,
68    finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
69    memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
70    _table_styles: &PrimaryMap<TableIndex, TableStyle>,
71) -> Result<Imports, LinkError> {
72    let mut function_imports = PrimaryMap::with_capacity(module.num_imported_functions);
73    let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
74    let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
75    let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals);
76
77    for (
78        wasmer_types::ImportKey {
79            module: module_name,
80            field,
81            import_idx,
82        },
83        import_index,
84    ) in module.imports.iter()
85    {
86        let import_extern = get_extern_from_import(module, import_index);
87        let resolved = if let Some(r) = imports.get(*import_idx as usize) {
88            r
89        } else {
90            return Err(LinkError::Import(
91                module_name.to_string(),
92                field.to_string(),
93                ImportError::UnknownImport(import_extern),
94            ));
95        };
96        let extern_type = get_extern_type(context, resolved);
97        let runtime_size = get_runtime_size(context, resolved);
98        if !extern_type.is_compatible_with(&import_extern, runtime_size) {
99            return Err(LinkError::Import(
100                module_name.to_string(),
101                field.to_string(),
102                ImportError::IncompatibleType(import_extern, extern_type),
103            ));
104        }
105        match *resolved {
106            VMExtern::Function(handle) => {
107                let f = handle.get_mut(context);
108                let address = match f.kind {
109                    VMFunctionKind::Dynamic => {
110                        // If this is a dynamic imported function,
111                        // the address of the function is the address of the
112                        // reverse trampoline.
113                        let index = FunctionIndex::new(function_imports.len());
114                        let ptr = finished_dynamic_function_trampolines[index].0
115                            as *mut VMFunctionBody as _;
116                        // The logic is currently handling the "resolution" of dynamic imported functions at instantiation time.
117                        // However, ideally it should be done even before then, as you may have dynamic imported functions that
118                        // are linked at runtime and not instantiation time. And those will not work properly with the current logic.
119                        // Ideally, this logic should be done directly in the `wasmer-vm` crate.
120                        // TODO (@syrusakbary): Get rid of `VMFunctionKind`
121                        unsafe { f.anyfunc.as_ptr().as_mut() }.func_ptr = ptr;
122                        ptr
123                    }
124                    VMFunctionKind::Static => unsafe { f.anyfunc.as_ptr().as_ref().func_ptr },
125                };
126
127                function_imports.push(VMFunctionImport {
128                    body: address,
129                    environment: unsafe { f.anyfunc.as_ptr().as_ref().vmctx },
130                    handle,
131                });
132            }
133            VMExtern::Table(handle) => {
134                let t = handle.get(context);
135                match import_index {
136                    ImportIndex::Table(index) => {
137                        let import_table_ty = t.ty();
138                        let expected_table_ty = &module.tables[*index];
139                        if import_table_ty.ty != expected_table_ty.ty {
140                            return Err(LinkError::Import(
141                                module_name.to_string(),
142                                field.to_string(),
143                                ImportError::IncompatibleType(import_extern, extern_type),
144                            ));
145                        }
146
147                        table_imports.push(VMTableImport {
148                            definition: t.vmtable(),
149                            handle,
150                        });
151                    }
152                    _ => {
153                        unreachable!("Table resolution did not match");
154                    }
155                }
156            }
157            VMExtern::Memory(handle) => {
158                let m = handle.get(context);
159                match import_index {
160                    ImportIndex::Memory(index) => {
161                        // Sanity-check: Ensure that the imported memory has at least
162                        // guard-page protections the importing module expects it to have.
163                        let export_memory_style = m.style();
164                        let import_memory_style = &memory_styles[*index];
165                        if let (
166                            MemoryStyle::Static { bound, .. },
167                            MemoryStyle::Static {
168                                bound: import_bound,
169                                ..
170                            },
171                        ) = (export_memory_style, &import_memory_style)
172                        {
173                            assert_ge!(bound, *import_bound);
174                        }
175                        assert_ge!(
176                            export_memory_style.offset_guard_size(),
177                            import_memory_style.offset_guard_size()
178                        );
179                    }
180                    _ => {
181                        // This should never be reached, as we did compatibility
182                        // checks before
183                        panic!("Memory resolution didn't matched");
184                    }
185                }
186
187                memory_imports.push(VMMemoryImport {
188                    definition: m.vmmemory(),
189                    handle,
190                });
191            }
192
193            VMExtern::Global(handle) => {
194                let g = handle.get(context);
195                global_imports.push(VMGlobalImport {
196                    definition: g.vmglobal(),
197                    handle,
198                });
199            }
200        }
201    }
202
203    Ok(Imports::new(
204        function_imports,
205        table_imports,
206        memory_imports,
207        global_imports,
208    ))
209}