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 pub(super) recv_pending_operation_barrier: bus::BusReader<Arc<Barrier>>,
52 pub(super) recv_pending_operation: bus::BusReader<DlOperation>,
55}
56
57impl 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 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 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 trace!(?module_handle, "Existing module instantiated successfully");
278
279 Ok(())
280 }
281
282 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 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 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 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 .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 (_, 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 (_, 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}