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