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