wasmer_wasix/state/linker/instance_group/
imports.rs

1use std::sync::{Mutex, TryLockError};
2
3use tracing::trace;
4use wasmer::{
5    AsStoreMut, Extern, ExternType, Function, FunctionEnv, FunctionEnvMut, FunctionType,
6    ImportType, Imports, Module, RuntimeError, Type, Value,
7};
8
9use crate::{WasiEnv, WasiError, flatten_runtime_error};
10
11use super::{
12    InProgressLinkState, InProgressSymbolResolution, InstanceGroupState, LinkError, LinkerState,
13    ModuleHandle, NeededSymbolResolutionKey, PartiallyResolvedExport,
14    PendingFunctionResolutionFromLinkerState, PendingResolutionsFromLinker, PendingTlsPointer,
15    ResolveError, SymbolResolutionKey, SymbolResolutionResult, UnresolvedGlobal,
16    define_integer_global_import,
17};
18
19#[derive(Clone, Copy)]
20enum MemoryImportMode {
21    GrowToMinimum,
22    DefineOnly,
23}
24
25impl InstanceGroupState {
26    // This function populates the imports object for a single module from the given
27    // in-progress link state.
28    pub(in crate::state::linker) fn populate_imports_from_link_state(
29        &self,
30        module_handle: ModuleHandle,
31        linker_state: &mut LinkerState,
32        link_state: &mut InProgressLinkState,
33        store: &mut impl AsStoreMut,
34        module: &Module,
35        imports: &mut Imports,
36        env: &FunctionEnv<WasiEnv>,
37        well_known_imports: &[(&str, &str, u64)],
38    ) -> Result<(), LinkError> {
39        trace!(?module_handle, "Populating imports object from link state");
40
41        for import in module.imports() {
42            // Skip non-DL-related import modules
43            if !matches!(import.module(), "env" | "GOT.mem" | "GOT.func") {
44                continue;
45            }
46
47            if self.define_common_import(
48                module_handle,
49                store,
50                &import,
51                imports,
52                well_known_imports,
53                MemoryImportMode::GrowToMinimum,
54            )? {
55                continue;
56            }
57
58            let key = NeededSymbolResolutionKey {
59                module_handle,
60                import_module: import.module().to_owned(),
61                import_name: import.name().to_owned(),
62            };
63
64            // Finally, go through the resolution results
65            let resolution = link_state.symbols.get(&key).unwrap_or_else(|| {
66                panic!(
67                    "Internal error: missing import resolution '{0}'.{1}",
68                    key.import_module, key.import_name
69                )
70            });
71
72            trace!(?module_handle, ?import, ?resolution, "Resolution");
73
74            match resolution {
75                InProgressSymbolResolution::Function(module_handle) => {
76                    let func = self
77                        .instance(*module_handle)
78                        .exports
79                        .get_function(import.name())
80                        .expect("Internal error: bad in-progress symbol resolution");
81                    imports.define(import.module(), import.name(), func.clone());
82                    linker_state.symbol_resolution_records.insert(
83                        SymbolResolutionKey::Needed(key.clone()),
84                        SymbolResolutionResult::Function {
85                            ty: func.ty(store),
86                            resolved_from: *module_handle,
87                        },
88                    );
89                }
90
91                InProgressSymbolResolution::StubFunction(func_ty) => {
92                    let func = self.generate_stub_function(
93                        store,
94                        func_ty,
95                        env,
96                        module_handle,
97                        import.name().to_string(),
98                    );
99                    imports.define(import.module(), import.name(), func);
100                    linker_state.symbol_resolution_records.insert(
101                        SymbolResolutionKey::Needed(key.clone()),
102                        SymbolResolutionResult::StubFunction(func_ty.clone()),
103                    );
104                }
105
106                InProgressSymbolResolution::MemGlobal(module_handle) => {
107                    let export = match self.resolve_export_from(
108                        store,
109                        *module_handle,
110                        import.name(),
111                        self.instance(*module_handle),
112                        linker_state.dylink_info(*module_handle),
113                        linker_state.memory_base(*module_handle),
114                        self.tls_base(*module_handle),
115                        true,
116                    ) {
117                        Ok(export) => export,
118                        Err(ResolveError::NoTlsBaseGlobalExport) => {
119                            return Err(LinkError::MissingTlsBaseExport(
120                                import.name().to_string(),
121                                *module_handle,
122                            ));
123                        }
124                        Err(e) => {
125                            panic!("Internal error: bad in-progress symbol resolution: {e:?}")
126                        }
127                    };
128
129                    match export {
130                        PartiallyResolvedExport::Global(addr) => {
131                            trace!(?module_handle, ?import, addr, "Memory address");
132
133                            let global =
134                                define_integer_global_import(store, &import, addr).unwrap();
135
136                            imports.define(import.module(), import.name(), global);
137                            linker_state.symbol_resolution_records.insert(
138                                SymbolResolutionKey::Needed(key.clone()),
139                                SymbolResolutionResult::Memory(addr),
140                            );
141                        }
142
143                        PartiallyResolvedExport::Tls { offset, final_addr } => {
144                            trace!(?module_handle, ?import, offset, final_addr, "TLS address");
145
146                            let global =
147                                define_integer_global_import(store, &import, final_addr).unwrap();
148
149                            imports.define(import.module(), import.name(), global);
150                            linker_state.symbol_resolution_records.insert(
151                                SymbolResolutionKey::Needed(key.clone()),
152                                SymbolResolutionResult::Tls {
153                                    resolved_from: *module_handle,
154                                    offset,
155                                },
156                            );
157                        }
158
159                        PartiallyResolvedExport::Function(_) => {
160                            panic!("Internal error: bad in-progress symbol resolution")
161                        }
162                    }
163                }
164
165                InProgressSymbolResolution::UnresolvedMemGlobal => {
166                    let global = define_integer_global_import(store, &import, 0).unwrap();
167                    imports.define(import.module(), import.name(), global.clone());
168
169                    link_state
170                        .unresolved_globals
171                        .push(UnresolvedGlobal::Mem(key, global));
172                }
173
174                InProgressSymbolResolution::FuncGlobal(module_handle) => {
175                    let func = self
176                        .instance(*module_handle)
177                        .exports
178                        .get_function(import.name())
179                        .expect("Internal error: bad in-progress symbol resolution");
180
181                    let func_handle = self
182                        .append_to_function_table(store, func.clone())
183                        .map_err(LinkError::TableAllocationError)?;
184                    trace!(
185                        ?module_handle,
186                        ?import,
187                        index = func_handle,
188                        "Allocated function table index"
189                    );
190                    let global =
191                        define_integer_global_import(store, &import, func_handle as u64).unwrap();
192
193                    imports.define(import.module(), import.name(), global);
194                    linker_state.symbol_resolution_records.insert(
195                        SymbolResolutionKey::Needed(key.clone()),
196                        SymbolResolutionResult::FunctionPointer {
197                            resolved_from: *module_handle,
198                            function_table_index: func_handle,
199                        },
200                    );
201                }
202
203                InProgressSymbolResolution::UnresolvedFuncGlobal => {
204                    let global = define_integer_global_import(store, &import, 0).unwrap();
205                    imports.define(import.module(), import.name(), global.clone());
206
207                    link_state
208                        .unresolved_globals
209                        .push(UnresolvedGlobal::Func(key, global));
210                }
211            }
212        }
213
214        trace!(?module_handle, "Imports object populated successfully");
215
216        Ok(())
217    }
218
219    // For when we receive a module loaded DL operation
220    pub(in crate::state::linker) fn populate_imports_from_linker(
221        &self,
222        module_handle: ModuleHandle,
223        linker_state: &LinkerState,
224        store: &mut impl AsStoreMut,
225        module: &Module,
226        imports: &mut Imports,
227        env: &FunctionEnv<WasiEnv>,
228        well_known_imports: &[(&str, &str, u64)],
229        pending_resolutions: &mut PendingResolutionsFromLinker,
230    ) -> Result<(), LinkError> {
231        trace!(
232            ?module_handle,
233            "Populating imports object for existing module from linker state"
234        );
235
236        for import in module.imports() {
237            // Skip non-DL-related import modules
238            if !matches!(import.module(), "env" | "GOT.mem" | "GOT.func") {
239                continue;
240            }
241
242            if self.define_common_import(
243                module_handle,
244                store,
245                &import,
246                imports,
247                well_known_imports,
248                MemoryImportMode::DefineOnly,
249            )? {
250                continue;
251            }
252
253            let key = SymbolResolutionKey::Needed(NeededSymbolResolutionKey {
254                module_handle,
255                import_module: import.module().to_owned(),
256                import_name: import.name().to_owned(),
257            });
258
259            // Finally, go through the resolution results
260            let resolution = linker_state
261                .symbol_resolution_records
262                .get(&key)
263                .unwrap_or_else(|| {
264                    panic!(
265                        "Internal error: missing symbol resolution record for '{0}'.{1}",
266                        import.module(),
267                        import.name()
268                    )
269                });
270
271            trace!(?module_handle, ?import, ?resolution, "Resolution");
272
273            match resolution {
274                SymbolResolutionResult::Function { ty, resolved_from } => {
275                    let func = match self.try_instance(*resolved_from) {
276                        Some(instance) => {
277                            trace!(
278                                ?module_handle,
279                                ?import,
280                                ?resolved_from,
281                                "Already have instance to resolve from"
282                            );
283                            instance
284                                .exports
285                                .get_function(import.name())
286                                .expect("Internal error: failed to get exported function")
287                                .clone()
288                        }
289                        // We may be loading a module tree, and the instance from which
290                        // we're supposed to import the function may not exist yet, so
291                        // we add in a stub, which will later use the resolution records
292                        // to locate the function.
293                        None => {
294                            trace!(
295                                ?module_handle,
296                                ?import,
297                                ?resolved_from,
298                                "Don't have instance yet"
299                            );
300
301                            self.generate_stub_function(
302                                store,
303                                ty,
304                                env,
305                                module_handle,
306                                import.name().to_owned(),
307                            )
308                        }
309                    };
310                    imports.define(import.module(), import.name(), func);
311                }
312                SymbolResolutionResult::StubFunction(ty) => {
313                    let func = self.generate_stub_function(
314                        store,
315                        ty,
316                        env,
317                        module_handle,
318                        import.name().to_owned(),
319                    );
320                    imports.define(import.module(), import.name(), func.clone());
321                }
322                SymbolResolutionResult::FunctionPointer {
323                    resolved_from,
324                    function_table_index,
325                } => {
326                    let func = self.try_instance(*resolved_from).map(|instance| {
327                        instance
328                            .exports
329                            .get_function(import.name())
330                            .unwrap_or_else(|e| {
331                                panic!(
332                                    "Internal error: failed to resolve function {}: {e:?}",
333                                    import.name()
334                                )
335                            })
336                    });
337                    match func {
338                        Some(func) => {
339                            trace!(
340                                ?module_handle,
341                                ?import,
342                                function_table_index,
343                                "Placing function pointer into table"
344                            );
345                            self.place_in_function_table_at(
346                                store,
347                                func.clone(),
348                                *function_table_index,
349                            )
350                            .map_err(LinkError::TableAllocationError)?;
351                        }
352                        None => {
353                            trace!(
354                                ?module_handle,
355                                ?import,
356                                function_table_index,
357                                "Don't have instance yet, creating a pending function"
358                            );
359                            // Since we know the final value of the global, we can create it
360                            // and just fill the function table in later
361                            pending_resolutions.functions.push(
362                                PendingFunctionResolutionFromLinkerState {
363                                    resolved_from: *resolved_from,
364                                    name: import.name().to_string(),
365                                    function_table_index: *function_table_index,
366                                },
367                            );
368                        }
369                    };
370                    let global =
371                        define_integer_global_import(store, &import, *function_table_index as u64)?;
372                    imports.define(import.module(), import.name(), global);
373                }
374                SymbolResolutionResult::Memory(addr) => {
375                    let global = define_integer_global_import(store, &import, *addr)?;
376                    imports.define(import.module(), import.name(), global);
377                }
378                SymbolResolutionResult::Tls {
379                    resolved_from,
380                    offset,
381                } => {
382                    let global = define_integer_global_import(store, &import, 0)?;
383                    pending_resolutions.tls.push(PendingTlsPointer {
384                        global: global.clone(),
385                        resolved_from: *resolved_from,
386                        offset: *offset,
387                    });
388                    imports.define(import.module(), import.name(), global);
389                }
390            }
391        }
392
393        Ok(())
394    }
395
396    fn define_common_import(
397        &self,
398        module_handle: ModuleHandle,
399        store: &mut impl AsStoreMut,
400        import: &ImportType,
401        imports: &mut Imports,
402        well_known_imports: &[(&str, &str, u64)],
403        memory_import_mode: MemoryImportMode,
404    ) -> Result<bool, LinkError> {
405        // Important env imports first.
406        if import.module() == "env" {
407            match import.name() {
408                "memory" => {
409                    let ExternType::Memory(memory_ty) = import.ty() else {
410                        return Err(LinkError::BadImport(
411                            import.module().to_string(),
412                            import.name().to_string(),
413                            import.ty().clone(),
414                        ));
415                    };
416                    trace!(?module_handle, ?import, "Main memory");
417
418                    if matches!(memory_import_mode, MemoryImportMode::GrowToMinimum) {
419                        // Make sure the memory is big enough for the module being instantiated.
420                        let current_size = self.memory.grow(store, 0)?;
421                        if current_size < memory_ty.minimum {
422                            self.memory.grow(store, memory_ty.minimum - current_size)?;
423                        }
424                    }
425
426                    imports.define(
427                        import.module(),
428                        import.name(),
429                        Extern::Memory(self.memory.clone()),
430                    );
431                    return Ok(true);
432                }
433                "__indirect_function_table" => {
434                    if !matches!(import.ty(), ExternType::Table(ty) if ty.ty == Type::FuncRef) {
435                        return Err(LinkError::BadImport(
436                            import.module().to_string(),
437                            import.name().to_string(),
438                            import.ty().clone(),
439                        ));
440                    }
441                    trace!(?module_handle, ?import, "Function table");
442                    imports.define(
443                        import.module(),
444                        import.name(),
445                        Extern::Table(self.indirect_function_table.clone()),
446                    );
447                    return Ok(true);
448                }
449                "__stack_pointer" => {
450                    if !matches!(import.ty(), ExternType::Global(ty) if *ty == self.stack_pointer.ty(store))
451                    {
452                        return Err(LinkError::BadImport(
453                            import.module().to_string(),
454                            import.name().to_string(),
455                            import.ty().clone(),
456                        ));
457                    }
458                    trace!(?module_handle, ?import, "Stack pointer");
459                    imports.define(
460                        import.module(),
461                        import.name(),
462                        Extern::Global(self.stack_pointer.clone()),
463                    );
464                    return Ok(true);
465                }
466                // Clang generates this symbol when building modules that use EH-based sjlj.
467                "__c_longjmp" => {
468                    if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32]) {
469                        return Err(LinkError::BadImport(
470                            import.module().to_string(),
471                            import.name().to_string(),
472                            import.ty().clone(),
473                        ));
474                    }
475                    trace!(?module_handle, ?import, "setjmp/longjmp exception tag");
476                    imports.define(import.module(), import.name(), self.c_longjmp.clone());
477                    return Ok(true);
478                }
479                // Clang generates this symbol when building C++ code that uses exception handling.
480                "__cpp_exception" => {
481                    if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32]) {
482                        return Err(LinkError::BadImport(
483                            import.module().to_string(),
484                            import.name().to_string(),
485                            import.ty().clone(),
486                        ));
487                    }
488                    trace!(?module_handle, ?import, "C++ exception tag");
489                    imports.define(import.module(), import.name(), self.cpp_exception.clone());
490                    return Ok(true);
491                }
492                _ => (),
493            }
494        }
495
496        if let Some(well_known_value) = well_known_imports.iter().find_map(|i| {
497            if i.0 == import.module() && i.1 == import.name() {
498                Some(i.2)
499            } else {
500                None
501            }
502        }) {
503            trace!(
504                ?module_handle,
505                ?import,
506                well_known_value,
507                "Well-known value"
508            );
509            imports.define(
510                import.module(),
511                import.name(),
512                define_integer_global_import(store, import, well_known_value)?,
513            );
514            return Ok(true);
515        }
516
517        Ok(false)
518    }
519
520    fn generate_stub_function(
521        &self,
522        store: &mut impl AsStoreMut,
523        ty: &FunctionType,
524        env: &FunctionEnv<WasiEnv>,
525        requesting_module: ModuleHandle,
526        name: String,
527    ) -> Function {
528        // TODO: only search through needed modules for the symbol. This requires the implementation
529        // of needing/needed relationships between modules.
530        trace!(?requesting_module, name, "Generating stub function");
531
532        let ty = ty.clone();
533        let resolved: Mutex<Option<Option<Function>>> = Mutex::new(None);
534
535        Function::new_with_env(
536            store,
537            env,
538            ty.clone(),
539            move |mut env: FunctionEnvMut<'_, WasiEnv>, params: &[Value]| {
540                let mk_error = || {
541                    RuntimeError::user(Box::new(WasiError::DlSymbolResolutionFailed(name.clone())))
542                };
543
544                let mut resolved_guard = resolved.lock().unwrap();
545                let func = match *resolved_guard {
546                    None => {
547                        trace!(?requesting_module, name, "Resolving stub function");
548
549                        let (data, store) = env.data_and_store_mut();
550                        let env_inner = data.inner();
551                        // Safe to unwrap since we already know we're doing DL
552                        let linker = env_inner.linker().unwrap();
553
554                        // Best-effort lock only: stubs can run during cross-module init while
555                        // another group holds the linker write lock. Cooperative write would block
556                        // here; if we can't lock, we resolve but skip recording for other groups.
557                        let linker_state = match linker.shared.try_write_linker_state() {
558                            Ok(guard) => {
559                                trace!(
560                                    ?requesting_module,
561                                    name, "Locked linker state successfully"
562                                );
563                                Some(guard)
564                            }
565                            Err(TryLockError::WouldBlock) => {
566                                trace!(?requesting_module, name, "Failed to lock linker state");
567                                None
568                            }
569                            Err(TryLockError::Poisoned(_)) => {
570                                *resolved_guard = Some(None);
571                                return Err(mk_error());
572                            }
573                        };
574
575                        let group_guard = linker.instance_group_state.lock().unwrap();
576                        let Some(group_state) = group_guard.as_ref() else {
577                            trace!(?requesting_module, name, "Instance group is already dead");
578                            *resolved_guard = Some(None);
579                            return Err(mk_error());
580                        };
581
582                        let resolution_key =
583                            SymbolResolutionKey::Needed(NeededSymbolResolutionKey {
584                                module_handle: requesting_module,
585                                import_module: "env".to_owned(),
586                                import_name: name.clone(),
587                            });
588
589                        match linker_state
590                            .as_ref()
591                            .and_then(|l| l.symbol_resolution_records.get(&resolution_key))
592                        {
593                            Some(SymbolResolutionResult::Function {
594                                resolved_from,
595                                ty: resolved_ty,
596                            }) => {
597                                trace!(
598                                    ?requesting_module,
599                                    name, "Function was already resolved in the linker"
600                                );
601
602                                if ty != *resolved_ty {
603                                    *resolved_guard = Some(None);
604                                    return Err(mk_error());
605                                }
606
607                                let func = group_state
608                                    .instance(*resolved_from)
609                                    .exports
610                                    .get_function(&name)
611                                    .unwrap()
612                                    .clone();
613                                *resolved_guard = Some(Some(func.clone()));
614                                func
615                            }
616                            Some(SymbolResolutionResult::StubFunction(_)) | None => {
617                                trace!(?requesting_module, name, "Resolving function");
618
619                                let Some((resolved_from, export)) =
620                                    group_state.resolve_exported_symbol(name.as_str())
621                                else {
622                                    trace!(?requesting_module, name, "Failed to resolve symbol");
623                                    *resolved_guard = Some(None);
624                                    return Err(mk_error());
625                                };
626                                let Extern::Function(func) = export else {
627                                    trace!(
628                                        ?requesting_module,
629                                        name,
630                                        ?resolved_from,
631                                        "Resolved symbol is not a function"
632                                    );
633                                    *resolved_guard = Some(None);
634                                    return Err(mk_error());
635                                };
636                                if func.ty(&store) != ty {
637                                    trace!(
638                                        ?requesting_module,
639                                        name,
640                                        ?resolved_from,
641                                        "Resolved function has bad type"
642                                    );
643                                    *resolved_guard = Some(None);
644                                    return Err(mk_error());
645                                }
646
647                                trace!(
648                                    ?requesting_module,
649                                    name,
650                                    ?resolved_from,
651                                    "Function resolved successfully"
652                                );
653
654                                // Only store the result if we can also put it in the linker's
655                                // resolution records for other groups to find.
656                                if let Some(mut linker_state) = linker_state {
657                                    trace!(
658                                        ?requesting_module,
659                                        name,
660                                        ?resolved_from,
661                                        "Updating linker state with this resolution"
662                                    );
663
664                                    *resolved_guard = Some(Some(func.clone()));
665                                    linker_state.symbol_resolution_records.insert(
666                                        resolution_key,
667                                        SymbolResolutionResult::Function {
668                                            ty: func.ty(&store),
669                                            resolved_from,
670                                        },
671                                    );
672                                }
673
674                                func.clone()
675                            }
676                            Some(resolution) => panic!(
677                                "Internal error: resolution record for symbol \
678                                {name} indicates non-function resolution {resolution:?}"
679                            ),
680                        }
681                    }
682                    Some(None) => return Err(mk_error()),
683                    Some(Some(ref func)) => func.clone(),
684                };
685                drop(resolved_guard);
686
687                let mut store = env.as_store_mut();
688                func.call(&mut store, params)
689                    .map(|ret| ret.into())
690                    .map_err(flatten_runtime_error)
691            },
692        )
693    }
694}