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