wasmer_wasix/state/linker/
instance_group.rs

1use std::{
2    collections::HashMap,
3    sync::{Arc, Barrier},
4};
5
6use tracing::trace;
7use wasmer::{AsStoreMut, FunctionEnv, Global, Instance, Memory, Table, Tag};
8
9use crate::{WasiEnv, import_object_for_all_wasi_versions};
10
11use super::{
12    DlModule, DlOperation, DylinkInfo, InProgressLinkState, InProgressSymbolResolution, LinkError,
13    LinkerState, MAIN_MODULE_HANDLE, ModuleHandle, NeededSymbolResolutionKey,
14    PartiallyResolvedExport, PendingFunctionResolutionFromLinkerState,
15    PendingResolutionsFromLinker, PendingTlsPointer, ResolveError, SymbolResolutionKey,
16    SymbolResolutionResult, UnresolvedGlobal, WasiModuleInstanceHandles,
17    call_initialization_function, define_integer_global_import, get_tls_base_export,
18    set_integer_global,
19};
20
21mod exports;
22mod imports;
23mod table;
24
25pub(super) struct DlInstance {
26    pub(super) instance: Instance,
27    #[allow(dead_code)]
28    pub(super) instance_handles: WasiModuleInstanceHandles,
29    pub(super) tls_base: Option<u64>,
30}
31
32pub(super) struct PreparedSideFromLinker {
33    pub(super) module_handle: ModuleHandle,
34    pub(super) instance: Instance,
35}
36
37pub(super) struct InstanceGroupState {
38    pub(super) main_instance: Option<Instance>,
39    pub(super) main_instance_tls_base: Option<u64>,
40
41    pub(super) side_instances: HashMap<ModuleHandle, DlInstance>,
42
43    pub(super) stack_pointer: Global,
44    pub(super) memory: Memory,
45    pub(super) indirect_function_table: Table,
46    pub(super) c_longjmp: Tag,
47    pub(super) cpp_exception: Tag,
48
49    // Once the dl_operation_pending flag is set, a barrier is created and broadcast
50    // by the instigating group, which others must use to rendezvous with it.
51    pub(super) recv_pending_operation_barrier: bus::BusReader<Arc<Barrier>>,
52    // The corresponding sender is stored in the shared linker state, and is used
53    // by the instigating instance group  to broadcast the results.
54    pub(super) recv_pending_operation: bus::BusReader<DlOperation>,
55}
56
57// TODO: split further
58impl InstanceGroupState {
59    fn main_instance(&self) -> Option<&Instance> {
60        self.main_instance.as_ref()
61    }
62
63    pub(super) fn tls_base(&self, module_handle: ModuleHandle) -> Option<u64> {
64        if module_handle == MAIN_MODULE_HANDLE {
65            // Main's TLS area is at the beginning of its memory
66            self.main_instance_tls_base
67        } else {
68            self.side_instances
69                .get(&module_handle)
70                .expect("Internal error: bad module handle")
71                .tls_base
72        }
73    }
74
75    fn try_instance(&self, handle: ModuleHandle) -> Option<&Instance> {
76        if handle == MAIN_MODULE_HANDLE {
77            self.main_instance.as_ref()
78        } else {
79            self.side_instances.get(&handle).map(|i| &i.instance)
80        }
81    }
82
83    fn instance(&self, handle: ModuleHandle) -> &Instance {
84        self.try_instance(handle)
85            .expect("Internal error: bad module handle or not instantiated in this group")
86    }
87
88    pub(super) fn instantiate_side_module_from_link_state(
89        &mut self,
90        linker_state: &mut LinkerState,
91        store: &mut impl AsStoreMut,
92        env: &FunctionEnv<WasiEnv>,
93        link_state: &mut InProgressLinkState,
94        module_handle: ModuleHandle,
95    ) -> Result<(), LinkError> {
96        let Some(pending_module) = link_state
97            .new_modules
98            .iter()
99            .find(|m| m.handle == module_handle)
100        else {
101            panic!(
102                "Only recently-loaded modules in the link state can be instantiated \
103                by instantiate_side_module_from_link_state"
104            )
105        };
106
107        trace!(
108            ?module_handle,
109            ?link_state,
110            "Instantiating module from link state"
111        );
112
113        let memory_base = linker_state.allocate_memory(
114            store,
115            &self.memory,
116            &pending_module.dylink_info.mem_info,
117        )?;
118        let table_base = self
119            .allocate_function_table(
120                store,
121                pending_module.dylink_info.mem_info.table_size,
122                pending_module.dylink_info.mem_info.table_alignment,
123            )
124            .map_err(LinkError::TableAllocationError)?;
125
126        trace!(
127            memory_base,
128            table_base, "Allocated memory and table for module"
129        );
130
131        let mut imports = import_object_for_all_wasi_versions(&pending_module.module, store, env);
132
133        let well_known_imports = [
134            ("env", "__memory_base", memory_base),
135            ("env", "__table_base", table_base),
136        ];
137
138        let module = pending_module.module.clone();
139        let dylink_info = pending_module.dylink_info.clone();
140
141        trace!(?module_handle, "Resolving symbols");
142        linker_state.resolve_symbols(
143            self,
144            store,
145            &module,
146            module_handle,
147            link_state,
148            &well_known_imports,
149        )?;
150
151        trace!(?module_handle, "Populating imports object");
152        self.populate_imports_from_link_state(
153            module_handle,
154            linker_state,
155            link_state,
156            store,
157            &module,
158            &mut imports,
159            env,
160            &well_known_imports,
161        )?;
162
163        let instance = Instance::new(store, &module, &imports)?;
164
165        let instance_handles = WasiModuleInstanceHandles::new(
166            self.memory.clone(),
167            store,
168            instance.clone(),
169            Some(self.indirect_function_table.clone()),
170        );
171
172        let dl_module = DlModule {
173            module,
174            dylink_info,
175            memory_base,
176            table_base,
177        };
178
179        let tls_base = get_tls_base_export(&instance, store)?;
180
181        let dl_instance = DlInstance {
182            instance: instance.clone(),
183            instance_handles,
184            // The TLS base of a side module's main instance is read from the module's
185            // `__tls_base` export via `get_tls_base_export`, and is not necessarily at the
186            // beginning of its memory.
187            tls_base,
188        };
189
190        linker_state.side_modules.insert(module_handle, dl_module);
191        self.side_instances.insert(module_handle, dl_instance);
192
193        trace!(?module_handle, "Module instantiated");
194
195        Ok(())
196    }
197
198    pub(super) fn prepare_side_module_from_linker(
199        &mut self,
200        linker_state: &LinkerState,
201        store: &mut impl AsStoreMut,
202        env: &FunctionEnv<WasiEnv>,
203        module_handle: ModuleHandle,
204        pending_resolutions: &mut PendingResolutionsFromLinker,
205    ) -> Result<PreparedSideFromLinker, LinkError> {
206        if self.side_instances.contains_key(&module_handle) {
207            panic!(
208                "Internal error: Module with handle {module_handle:?} \
209                was already instantiated in this group"
210            )
211        };
212
213        trace!(?module_handle, "Instantiating existing module from linker");
214
215        let dl_module = linker_state
216            .side_modules
217            .get(&module_handle)
218            .expect("Internal error: module not loaded into linker");
219
220        let mut imports = import_object_for_all_wasi_versions(&dl_module.module, store, env);
221
222        let well_known_imports = [
223            ("env", "__memory_base", dl_module.memory_base),
224            ("env", "__table_base", dl_module.table_base),
225        ];
226
227        trace!(?module_handle, "Populating imports object");
228        self.populate_imports_from_linker(
229            module_handle,
230            linker_state,
231            store,
232            &dl_module.module,
233            &mut imports,
234            env,
235            &well_known_imports,
236            pending_resolutions,
237        )?;
238
239        let instance = Instance::new(store, &dl_module.module, &imports)?;
240
241        Ok(PreparedSideFromLinker {
242            module_handle,
243            instance,
244        })
245    }
246
247    pub(super) fn complete_side_module_from_linker(
248        &mut self,
249        prepared: PreparedSideFromLinker,
250        tls_base: Option<u64>,
251        store: &mut impl AsStoreMut,
252    ) -> Result<(), LinkError> {
253        let PreparedSideFromLinker {
254            module_handle,
255            instance,
256        } = prepared;
257
258        let instance_handles = WasiModuleInstanceHandles::new(
259            self.memory.clone(),
260            store,
261            instance.clone(),
262            Some(self.indirect_function_table.clone()),
263        );
264
265        let dl_instance = DlInstance {
266            instance: instance.clone(),
267            instance_handles,
268            tls_base,
269        };
270
271        self.side_instances.insert(module_handle, dl_instance);
272
273        // Initialization logic must only be run once, so no init calls here; it is
274        // assumed that the module was instantiated and its init callbacks were called
275        // by whichever thread first called instantiate_side_module_from_link_state.
276
277        trace!(?module_handle, "Existing module instantiated successfully");
278
279        Ok(())
280    }
281
282    // For when we receive a module loaded DL operation
283    pub(super) fn instantiate_side_module_from_linker(
284        &mut self,
285        linker_state: &LinkerState,
286        store: &mut impl AsStoreMut,
287        env: &FunctionEnv<WasiEnv>,
288        module_handle: ModuleHandle,
289        pending_resolutions: &mut PendingResolutionsFromLinker,
290    ) -> Result<(), LinkError> {
291        let prepared = self.prepare_side_module_from_linker(
292            linker_state,
293            store,
294            env,
295            module_handle,
296            pending_resolutions,
297        )?;
298
299        // This is a non-main instance of a side module, so it needs a new TLS area
300        let tls_base =
301            call_initialization_function::<i32>(&prepared.instance, store, "__wasix_init_tls")?
302                .map(|v| v as u64);
303
304        self.complete_side_module_from_linker(prepared, tls_base, store)
305    }
306
307    pub(super) fn finalize_pending_resolutions_from_linker(
308        &self,
309        pending_resolutions: &PendingResolutionsFromLinker,
310        store: &mut impl AsStoreMut,
311    ) -> Result<(), LinkError> {
312        trace!("Finalizing pending functions");
313
314        for pending in &pending_resolutions.functions {
315            let func = self
316                .instance(pending.resolved_from)
317                .exports
318                .get_function(&pending.name)
319                .unwrap_or_else(|e| {
320                    panic!(
321                        "Internal error: failed to resolve exported function {}: {e:?}",
322                        pending.name
323                    )
324                });
325
326            self.place_in_function_table_at(store, func.clone(), pending.function_table_index)
327                .map_err(LinkError::TableAllocationError)?;
328
329            trace!(?pending, "Placed pending function in table");
330        }
331
332        for tls in &pending_resolutions.tls {
333            let Some(tls_base) = self.tls_base(tls.resolved_from) else {
334                // This is a panic since this error should have been caught when the symbol
335                // was originally resolved by the instigating instance group. We're just replaying
336                // the changes.
337                panic!(
338                    "Internal error: Tried to import TLS symbol from module {} that \
339                    has no TLS base",
340                    tls.resolved_from.0
341                );
342            };
343
344            let final_addr = tls_base + tls.offset;
345            set_integer_global(store, "<pending TLS global>", &tls.global, final_addr)?;
346            trace!(?tls, tls_base, final_addr, "Setting pending TLS global");
347        }
348
349        Ok(())
350    }
351
352    pub(super) fn apply_requested_symbols_from_linker(
353        &self,
354        store: &mut impl AsStoreMut,
355        linker_state: &LinkerState,
356    ) -> Result<(), LinkError> {
357        for (key, val) in &linker_state.symbol_resolution_records {
358            if let SymbolResolutionKey::Requested { name, .. } = key
359                && let SymbolResolutionResult::FunctionPointer {
360                    resolved_from,
361                    function_table_index,
362                } = val
363            {
364                self.apply_resolved_function(store, name, *resolved_from, *function_table_index)?;
365            }
366        }
367        Ok(())
368    }
369
370    pub(super) fn apply_dl_operation(
371        &mut self,
372        linker_state: &LinkerState,
373        operation: DlOperation,
374        store: &mut impl AsStoreMut,
375        env: &FunctionEnv<WasiEnv>,
376    ) -> Result<(), LinkError> {
377        trace!(?operation, "Applying operation");
378        match operation {
379            DlOperation::LoadModules(module_handles) => {
380                let mut pending_functions = PendingResolutionsFromLinker::default();
381                for handle in module_handles {
382                    // We need to do table allocation in exactly the same order as the instigating
383                    // group, which is:
384                    //   * Allocate module's own table space
385                    //   * Fill GOT.func entries (through instantiating the module)
386                    //   * Then repeat for the next module.
387                    self.allocate_function_table_for_existing_module(linker_state, store, handle)?;
388                    self.instantiate_side_module_from_linker(
389                        linker_state,
390                        store,
391                        env,
392                        handle,
393                        &mut pending_functions,
394                    )?;
395                }
396                self.finalize_pending_resolutions_from_linker(&pending_functions, store)?;
397            }
398            DlOperation::ResolveFunction {
399                name,
400                resolved_from,
401                function_table_index,
402            } => self.apply_resolved_function(store, &name, resolved_from, function_table_index)?,
403            DlOperation::AllocateFunctionTable { index, size } => {
404                self.apply_function_table_allocation(store, index, size)?
405            }
406        };
407        trace!("Operation applied successfully");
408        Ok(())
409    }
410
411    pub(super) fn finalize_pending_globals(
412        &self,
413        linker_state: &mut LinkerState,
414        store: &mut impl AsStoreMut,
415        unresolved_globals: &Vec<UnresolvedGlobal>,
416    ) -> Result<(), LinkError> {
417        trace!("Finalizing pending globals");
418
419        for unresolved in unresolved_globals {
420            let key = unresolved.key();
421            let import_metadata = &linker_state.dylink_info(key.module_handle).import_metadata;
422            let is_weak = import_metadata
423                .get(&(key.import_module.to_owned(), key.import_name.to_owned()))
424                // clang seems to like putting the import-info in the "env" module
425                // sometimes, so try that as well
426                .or_else(|| import_metadata.get(&("env".to_owned(), key.import_name.to_owned())))
427                .map(|flags| flags.contains(wasmparser::SymbolFlags::BINDING_WEAK))
428                .unwrap_or(false);
429            trace!(?unresolved, is_weak, "Resolving pending global");
430
431            match (
432                unresolved,
433                self.resolve_export(linker_state, store, None, &key.import_name, true),
434            ) {
435                (
436                    UnresolvedGlobal::Mem(key, global),
437                    Ok((PartiallyResolvedExport::Global(addr), resolved_from)),
438                ) => {
439                    trace!(
440                        ?unresolved,
441                        ?resolved_from,
442                        addr,
443                        "Resolved to memory address"
444                    );
445                    set_integer_global(store, &key.import_name, global, addr)?;
446                    linker_state.symbol_resolution_records.insert(
447                        SymbolResolutionKey::Needed(key.clone()),
448                        SymbolResolutionResult::Memory(addr),
449                    );
450                }
451
452                (
453                    UnresolvedGlobal::Mem(key, global),
454                    Ok((PartiallyResolvedExport::Tls { offset, final_addr }, resolved_from)),
455                ) => {
456                    trace!(
457                        ?unresolved,
458                        ?resolved_from,
459                        offset,
460                        final_addr,
461                        "Resolved to TLS address"
462                    );
463                    set_integer_global(store, &key.import_name, global, final_addr)?;
464                    linker_state.symbol_resolution_records.insert(
465                        SymbolResolutionKey::Needed(key.clone()),
466                        SymbolResolutionResult::Tls {
467                            resolved_from,
468                            offset,
469                        },
470                    );
471                }
472
473                (
474                    UnresolvedGlobal::Func(key, global),
475                    Ok((PartiallyResolvedExport::Function(func), resolved_from)),
476                ) => {
477                    let func_handle = self
478                        .append_to_function_table(store, func)
479                        .map_err(LinkError::TableAllocationError)?;
480                    trace!(
481                        ?unresolved,
482                        ?resolved_from,
483                        function_table_index = ?func_handle,
484                        "Resolved to function pointer"
485                    );
486                    set_integer_global(store, &key.import_name, global, func_handle as u64)?;
487                    linker_state.symbol_resolution_records.insert(
488                        SymbolResolutionKey::Needed(key.clone()),
489                        SymbolResolutionResult::FunctionPointer {
490                            resolved_from,
491                            function_table_index: func_handle,
492                        },
493                    );
494                }
495
496                // Expected memory address, resolved function or vice-versa
497                (_, Ok(_)) => {
498                    return Err(LinkError::UnresolvedGlobal(
499                        unresolved.import_module().to_string(),
500                        key.import_name.clone(),
501                        Box::new(ResolveError::MissingExport),
502                    ));
503                }
504
505                // Missing weak symbols get resolved to a null address
506                (_, Err(ResolveError::MissingExport)) if is_weak => {
507                    trace!(?unresolved, "Weak global not found");
508                    set_integer_global(store, &key.import_name, unresolved.global(), 0)?;
509                    linker_state.symbol_resolution_records.insert(
510                        SymbolResolutionKey::Needed(key.clone()),
511                        SymbolResolutionResult::Memory(0),
512                    );
513                }
514
515                (_, Err(e)) => {
516                    return Err(LinkError::UnresolvedGlobal(
517                        "GOT.mem".to_string(),
518                        key.import_name.clone(),
519                        Box::new(e),
520                    ));
521                }
522            }
523        }
524
525        Ok(())
526    }
527}