1 | //! Support for "pseudo-dynamic", shared-everything linking of Wasm modules into a component. |
2 | //! |
3 | //! This implements [shared-everything |
4 | //! linking](https://github.com/WebAssembly/component-model/blob/main/design/mvp/examples/SharedEverythingDynamicLinking.md), |
5 | //! taking as input one or more [dynamic |
6 | //! library](https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md) modules and producing a |
7 | //! component whose type is the union of any `component-type*` custom sections found in the input modules. |
8 | //! |
9 | //! The entry point into this process is `Linker::encode`, which analyzes and topologically sorts the input |
10 | //! modules, then sythesizes two additional modules: |
11 | //! |
12 | //! - `main` AKA `env`: hosts the component's single memory and function table and exports any functions needed to |
13 | //! break dependency cycles discovered in the input modules. Those functions use `call.indirect` to invoke the real |
14 | //! functions, references to which are placed in the table by the `init` module. |
15 | //! |
16 | //! - `init`: populates the function table as described above, initializes global variables per the dynamic linking |
17 | //! tool convention, and calls any static constructors and/or link-time fixup functions |
18 | //! |
19 | //! `Linker` also supports synthesizing `dlopen`/`dlsym` lookup tables which allow symbols to be resolved at |
20 | //! runtime. Note that this is not true dynamic linking, since all the code is baked into the component ahead of |
21 | //! time -- we simply allow runtime resolution of already-resident definitions. This is sufficient to support |
22 | //! dynamic language FFI features such as Python native extensions, provided the required libraries are linked |
23 | //! ahead-of-time. |
24 | |
25 | use { |
26 | crate::encoding::{ComponentEncoder, Instance, Item, LibraryInfo, MainOrAdapter}, |
27 | anyhow::{anyhow, bail, Context, Result}, |
28 | indexmap::{map::Entry, IndexMap, IndexSet}, |
29 | metadata::{Export, ExportKey, FunctionType, GlobalType, Metadata, Type, ValueType}, |
30 | std::{ |
31 | collections::{BTreeMap, HashMap, HashSet}, |
32 | fmt::Debug, |
33 | hash::Hash, |
34 | iter, |
35 | }, |
36 | wasm_encoder::{ |
37 | CodeSection, ConstExpr, DataSection, ElementSection, Elements, EntityType, ExportKind, |
38 | ExportSection, Function, FunctionSection, GlobalSection, ImportSection, Instruction as Ins, |
39 | MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, |
40 | TableSection, TableType, TypeSection, ValType, |
41 | }, |
42 | wasmparser::SymbolFlags, |
43 | }; |
44 | |
45 | mod metadata; |
46 | |
47 | const PAGE_SIZE_BYTES: u32 = 65536; |
48 | // This matches the default stack size LLVM produces: |
49 | pub const DEFAULT_STACK_SIZE_BYTES: u32 = 16 * PAGE_SIZE_BYTES; |
50 | const HEAP_ALIGNMENT_BYTES: u32 = 16; |
51 | |
52 | enum Address<'a> { |
53 | Function(u32), |
54 | Global(&'a str), |
55 | } |
56 | |
57 | /// Represents a `dlopen`/`dlsym` lookup table enabling runtime symbol resolution |
58 | /// |
59 | /// The top level of this table is a sorted list of library names and offsets, each pointing to a sorted list of |
60 | /// symbol names and offsets. See ../dl/src/lib.rs for how this is used at runtime. |
61 | struct DlOpenables<'a> { |
62 | /// Offset into the main module's table where function references will be stored |
63 | table_base: u32, |
64 | |
65 | /// Offset into the main module's memory where the lookup table will be stored |
66 | memory_base: u32, |
67 | |
68 | /// The lookup table itself |
69 | buffer: Vec<u8>, |
70 | |
71 | /// Linear memory addresses where global variable addresses will live |
72 | /// |
73 | /// The init module will fill in the correct values at insantiation time. |
74 | global_addresses: Vec<(&'a str, &'a str, u32)>, |
75 | |
76 | /// Number of function references to be stored in the main module's table |
77 | function_count: u32, |
78 | |
79 | /// Linear memory address where the root of the lookup table will reside |
80 | /// |
81 | /// This can be different from `memory_base` depending on how the tree of libraries and symbols is laid out in |
82 | /// memory. |
83 | libraries_address: u32, |
84 | } |
85 | |
86 | impl<'a> DlOpenables<'a> { |
87 | /// Construct a lookup table containing all "dlopen-able" libraries and their symbols using the specified table |
88 | /// and memory offsets. |
89 | fn new(table_base: u32, memory_base: u32, metadata: &'a [Metadata<'a>]) -> Self { |
90 | let mut function_count = 0; |
91 | let mut buffer = Vec::new(); |
92 | let mut global_addresses = Vec::new(); |
93 | let mut libraries = metadata |
94 | .iter() |
95 | .filter(|metadata| metadata.dl_openable) |
96 | .map(|metadata| { |
97 | let name_address = memory_base + u32::try_from(buffer.len()).unwrap(); |
98 | write_bytes_padded(&mut buffer, metadata.name.as_bytes()); |
99 | |
100 | let mut symbols = metadata |
101 | .exports |
102 | .iter() |
103 | .map(|export| { |
104 | let name_address = memory_base + u32::try_from(buffer.len()).unwrap(); |
105 | write_bytes_padded(&mut buffer, export.key.name.as_bytes()); |
106 | |
107 | let address = match &export.key.ty { |
108 | Type::Function(_) => Address::Function( |
109 | table_base + get_and_increment(&mut function_count), |
110 | ), |
111 | Type::Global(_) => Address::Global(export.key.name), |
112 | }; |
113 | |
114 | (export.key.name, name_address, address) |
115 | }) |
116 | .collect::<Vec<_>>(); |
117 | |
118 | symbols.sort_by_key(|(name, ..)| *name); |
119 | |
120 | let start = buffer.len(); |
121 | for (name, name_address, address) in symbols { |
122 | write_u32(&mut buffer, u32::try_from(name.len()).unwrap()); |
123 | write_u32(&mut buffer, name_address); |
124 | match address { |
125 | Address::Function(address) => write_u32(&mut buffer, address), |
126 | Address::Global(name) => { |
127 | global_addresses.push(( |
128 | metadata.name, |
129 | name, |
130 | memory_base + u32::try_from(buffer.len()).unwrap(), |
131 | )); |
132 | |
133 | write_u32(&mut buffer, 0); |
134 | } |
135 | } |
136 | } |
137 | |
138 | ( |
139 | metadata.name, |
140 | name_address, |
141 | metadata.exports.len(), |
142 | memory_base + u32::try_from(start).unwrap(), |
143 | ) |
144 | }) |
145 | .collect::<Vec<_>>(); |
146 | |
147 | libraries.sort_by_key(|(name, ..)| *name); |
148 | |
149 | let start = buffer.len(); |
150 | for (name, name_address, count, symbols) in &libraries { |
151 | write_u32(&mut buffer, u32::try_from(name.len()).unwrap()); |
152 | write_u32(&mut buffer, *name_address); |
153 | write_u32(&mut buffer, u32::try_from(*count).unwrap()); |
154 | write_u32(&mut buffer, *symbols); |
155 | } |
156 | |
157 | let libraries_address = memory_base + u32::try_from(buffer.len()).unwrap(); |
158 | write_u32(&mut buffer, u32::try_from(libraries.len()).unwrap()); |
159 | write_u32(&mut buffer, memory_base + u32::try_from(start).unwrap()); |
160 | |
161 | Self { |
162 | table_base, |
163 | memory_base, |
164 | buffer, |
165 | global_addresses, |
166 | function_count, |
167 | libraries_address, |
168 | } |
169 | } |
170 | } |
171 | |
172 | fn write_u32(buffer: &mut Vec<u8>, value: u32) { |
173 | buffer.extend(iter:value.to_le_bytes()); |
174 | } |
175 | |
176 | fn write_bytes_padded(buffer: &mut Vec<u8>, bytes: &[u8]) { |
177 | buffer.extend(iter:bytes); |
178 | |
179 | let len: u32 = u32::try_from(bytes.len()).unwrap(); |
180 | for _ in len..align(a:len, b:4) { |
181 | buffer.push(0); |
182 | } |
183 | } |
184 | |
185 | fn align(a: u32, b: u32) -> u32 { |
186 | assert!(b.is_power_of_two()); |
187 | (a + (b - 1)) & !(b - 1) |
188 | } |
189 | |
190 | fn get_and_increment(n: &mut u32) -> u32 { |
191 | let v: u32 = *n; |
192 | *n += 1; |
193 | v |
194 | } |
195 | |
196 | fn const_u32(a: u32) -> ConstExpr { |
197 | ConstExpr::i32_const(a as i32) |
198 | } |
199 | |
200 | /// Helper trait for determining the size of a set or map |
201 | trait Length { |
202 | fn len(&self) -> usize; |
203 | } |
204 | |
205 | impl<T> Length for HashSet<T> { |
206 | fn len(&self) -> usize { |
207 | HashSet::len(self) |
208 | } |
209 | } |
210 | |
211 | impl<K, V> Length for HashMap<K, V> { |
212 | fn len(&self) -> usize { |
213 | HashMap::len(self) |
214 | } |
215 | } |
216 | |
217 | impl<T> Length for IndexSet<T> { |
218 | fn len(&self) -> usize { |
219 | IndexSet::len(self) |
220 | } |
221 | } |
222 | |
223 | impl<K, V> Length for IndexMap<K, V> { |
224 | fn len(&self) -> usize { |
225 | IndexMap::len(self) |
226 | } |
227 | } |
228 | |
229 | /// Extension trait for collecting into a set or map and asserting that there were no duplicate entries in the |
230 | /// source iterator. |
231 | trait CollectUnique: Iterator + Sized { |
232 | fn collect_unique<T: FromIterator<Self::Item> + Length>(self) -> T { |
233 | let tmp: Vec<::Item> = self.collect::<Vec<_>>(); |
234 | let len: usize = tmp.len(); |
235 | let result: T = tmp.into_iter().collect::<T>(); |
236 | assert!( |
237 | result.len() == len, |
238 | "one or more duplicate items detected when collecting into set or map" |
239 | ); |
240 | result |
241 | } |
242 | } |
243 | |
244 | impl<T: Iterator> CollectUnique for T {} |
245 | |
246 | /// Extension trait for inserting into a map and asserting that an entry did not already exist for the key |
247 | trait InsertUnique { |
248 | type Key; |
249 | type Value; |
250 | |
251 | fn insert_unique(&mut self, k: Self::Key, v: Self::Value); |
252 | } |
253 | |
254 | impl<K: Hash + Eq + PartialEq + Debug, V: Debug> InsertUnique for HashMap<K, V> { |
255 | type Key = K; |
256 | type Value = V; |
257 | |
258 | fn insert_unique(&mut self, k: Self::Key, v: Self::Value) { |
259 | if let Some(old_v: &V) = self.get(&k) { |
260 | panic!("duplicate item inserted into map for key {k:?} (old value: {old_v:?}; new value: {v:?})" ); |
261 | } |
262 | self.insert(k, v); |
263 | } |
264 | } |
265 | |
266 | /// Synthesize the "main" module for the component, responsible for exporting functions which break cyclic |
267 | /// dependencies, as well as hosting the memory and function table. |
268 | fn make_env_module<'a>( |
269 | metadata: &'a [Metadata<'a>], |
270 | function_exports: &[(&str, &FunctionType, usize)], |
271 | cabi_realloc_exporter: Option<&str>, |
272 | stack_size_bytes: u32, |
273 | ) -> (Vec<u8>, DlOpenables<'a>, u32) { |
274 | // TODO: deduplicate types |
275 | let mut types = TypeSection::new(); |
276 | let mut imports = ImportSection::new(); |
277 | let mut import_map = IndexMap::new(); |
278 | let mut function_count = 0; |
279 | let mut global_offset = 0; |
280 | let mut wasi_start = None; |
281 | |
282 | for metadata in metadata { |
283 | for import in &metadata.imports { |
284 | if let Entry::Vacant(entry) = import_map.entry(import) { |
285 | imports.import( |
286 | import.module, |
287 | import.name, |
288 | match &import.ty { |
289 | Type::Function(ty) => { |
290 | let index = get_and_increment(&mut function_count); |
291 | entry.insert(index); |
292 | types.ty().function( |
293 | ty.parameters.iter().copied().map(ValType::from), |
294 | ty.results.iter().copied().map(ValType::from), |
295 | ); |
296 | EntityType::Function(index) |
297 | } |
298 | Type::Global(ty) => { |
299 | entry.insert(get_and_increment(&mut global_offset)); |
300 | EntityType::Global(wasm_encoder::GlobalType { |
301 | val_type: ty.ty.into(), |
302 | mutable: ty.mutable, |
303 | shared: ty.shared, |
304 | }) |
305 | } |
306 | }, |
307 | ); |
308 | } |
309 | } |
310 | |
311 | if metadata.has_wasi_start { |
312 | if wasi_start.is_some() { |
313 | panic!("multiple libraries export _start" ); |
314 | } |
315 | let index = get_and_increment(&mut function_count); |
316 | |
317 | types.ty().function(vec![], vec![]); |
318 | imports.import(metadata.name, "_start" , EntityType::Function(index)); |
319 | |
320 | wasi_start = Some(index); |
321 | } |
322 | } |
323 | |
324 | let mut memory_offset = stack_size_bytes; |
325 | |
326 | // Table offset 0 is reserved for the null function pointer. |
327 | // This convention follows wasm-ld's table layout: |
328 | // https://github.com/llvm/llvm-project/blob/913622d012f72edb5ac3a501cef8639d0ebe471b/lld/wasm/Driver.cpp#L581-L584 |
329 | let mut table_offset = 1; |
330 | let mut globals = GlobalSection::new(); |
331 | let mut exports = ExportSection::new(); |
332 | |
333 | if let Some(exporter) = cabi_realloc_exporter { |
334 | let index = get_and_increment(&mut function_count); |
335 | types.ty().function([ValType::I32; 4], [ValType::I32]); |
336 | imports.import(exporter, "cabi_realloc" , EntityType::Function(index)); |
337 | exports.export("cabi_realloc" , ExportKind::Func, index); |
338 | } |
339 | |
340 | let dl_openables = DlOpenables::new(table_offset, memory_offset, metadata); |
341 | |
342 | table_offset += dl_openables.function_count; |
343 | memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap(); |
344 | |
345 | let memory_size = { |
346 | let mut add_global_export = |name: &str, value, mutable| { |
347 | let index = globals.len(); |
348 | globals.global( |
349 | wasm_encoder::GlobalType { |
350 | val_type: ValType::I32, |
351 | mutable, |
352 | shared: false, |
353 | }, |
354 | &const_u32(value), |
355 | ); |
356 | exports.export(name, ExportKind::Global, index); |
357 | }; |
358 | |
359 | add_global_export("__stack_pointer" , stack_size_bytes, true); |
360 | |
361 | // Binaryen's Asyncify transform for shared everything linking requires these globals |
362 | // to be provided from env module |
363 | let has_asyncified_module = metadata.iter().any(|m| m.is_asyncified); |
364 | if has_asyncified_module { |
365 | add_global_export("__asyncify_state" , 0, true); |
366 | add_global_export("__asyncify_data" , 0, true); |
367 | } |
368 | |
369 | for metadata in metadata { |
370 | memory_offset = align(memory_offset, 1 << metadata.mem_info.memory_alignment); |
371 | table_offset = align(table_offset, 1 << metadata.mem_info.table_alignment); |
372 | |
373 | add_global_export( |
374 | &format!(" {}:memory_base" , metadata.name), |
375 | memory_offset, |
376 | false, |
377 | ); |
378 | add_global_export( |
379 | &format!(" {}:table_base" , metadata.name), |
380 | table_offset, |
381 | false, |
382 | ); |
383 | |
384 | memory_offset += metadata.mem_info.memory_size; |
385 | table_offset += metadata.mem_info.table_size; |
386 | |
387 | for import in &metadata.memory_address_imports { |
388 | // Note that we initialize this to zero and let the init module compute the real value at |
389 | // instantiation time. |
390 | add_global_export(&format!(" {}: {import}" , metadata.name), 0, true); |
391 | } |
392 | } |
393 | |
394 | { |
395 | let offsets = function_exports |
396 | .iter() |
397 | .enumerate() |
398 | .map(|(offset, (name, ..))| (*name, table_offset + u32::try_from(offset).unwrap())) |
399 | .collect_unique::<HashMap<_, _>>(); |
400 | |
401 | for metadata in metadata { |
402 | for import in &metadata.table_address_imports { |
403 | add_global_export( |
404 | &format!(" {}: {import}" , metadata.name), |
405 | *offsets.get(import).unwrap(), |
406 | true, |
407 | ); |
408 | } |
409 | } |
410 | } |
411 | |
412 | memory_offset = align(memory_offset, HEAP_ALIGNMENT_BYTES); |
413 | add_global_export("__heap_base" , memory_offset, true); |
414 | |
415 | let heap_end = align(memory_offset, PAGE_SIZE_BYTES); |
416 | add_global_export("__heap_end" , heap_end, true); |
417 | heap_end / PAGE_SIZE_BYTES |
418 | }; |
419 | |
420 | let indirection_table_base = table_offset; |
421 | |
422 | let mut functions = FunctionSection::new(); |
423 | let mut code = CodeSection::new(); |
424 | for (name, ty, _) in function_exports { |
425 | let index = get_and_increment(&mut function_count); |
426 | types.ty().function( |
427 | ty.parameters.iter().copied().map(ValType::from), |
428 | ty.results.iter().copied().map(ValType::from), |
429 | ); |
430 | functions.function(u32::try_from(index).unwrap()); |
431 | let mut function = Function::new([]); |
432 | for local in 0..ty.parameters.len() { |
433 | function.instruction(&Ins::LocalGet(u32::try_from(local).unwrap())); |
434 | } |
435 | function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap())); |
436 | function.instruction(&Ins::CallIndirect { |
437 | type_index: u32::try_from(index).unwrap(), |
438 | table_index: 0, |
439 | }); |
440 | function.instruction(&Ins::End); |
441 | code.function(&function); |
442 | exports.export(name, ExportKind::Func, index); |
443 | |
444 | table_offset += 1; |
445 | } |
446 | |
447 | for (import, offset) in import_map { |
448 | exports.export( |
449 | &format!(" {}: {}" , import.module, import.name), |
450 | ExportKind::from(&import.ty), |
451 | offset, |
452 | ); |
453 | } |
454 | if let Some(index) = wasi_start { |
455 | exports.export("_start" , ExportKind::Func, index); |
456 | } |
457 | |
458 | let mut module = Module::new(); |
459 | |
460 | module.section(&types); |
461 | module.section(&imports); |
462 | module.section(&functions); |
463 | |
464 | { |
465 | let mut tables = TableSection::new(); |
466 | tables.table(TableType { |
467 | element_type: RefType::FUNCREF, |
468 | minimum: table_offset.into(), |
469 | maximum: None, |
470 | table64: false, |
471 | shared: false, |
472 | }); |
473 | exports.export("__indirect_function_table" , ExportKind::Table, 0); |
474 | module.section(&tables); |
475 | } |
476 | |
477 | { |
478 | let mut memories = MemorySection::new(); |
479 | memories.memory(MemoryType { |
480 | minimum: u64::from(memory_size), |
481 | maximum: None, |
482 | memory64: false, |
483 | shared: false, |
484 | page_size_log2: None, |
485 | }); |
486 | exports.export("memory" , ExportKind::Memory, 0); |
487 | module.section(&memories); |
488 | } |
489 | |
490 | module.section(&globals); |
491 | module.section(&exports); |
492 | module.section(&code); |
493 | module.section(&RawCustomSection( |
494 | &crate::base_producers().raw_custom_section(), |
495 | )); |
496 | |
497 | let module = module.finish(); |
498 | wasmparser::validate(&module).unwrap(); |
499 | |
500 | (module, dl_openables, indirection_table_base) |
501 | } |
502 | |
503 | /// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool |
504 | /// convention and calling any static constructors and/or link-time fixup functions. |
505 | /// |
506 | /// This module also contains the data segment for the `dlopen`/`dlsym` lookup table. |
507 | fn make_init_module( |
508 | metadata: &[Metadata], |
509 | exporters: &IndexMap<&ExportKey, (&str, &Export)>, |
510 | function_exports: &[(&str, &FunctionType, usize)], |
511 | dl_openables: DlOpenables, |
512 | indirection_table_base: u32, |
513 | ) -> Result<Vec<u8>> { |
514 | let mut module = Module::new(); |
515 | |
516 | // TODO: deduplicate types |
517 | let mut types = TypeSection::new(); |
518 | types.ty().function([], []); |
519 | let thunk_ty = 0; |
520 | types.ty().function([ValType::I32], []); |
521 | let one_i32_param_ty = 1; |
522 | let mut type_offset = 2; |
523 | |
524 | for metadata in metadata { |
525 | if metadata.dl_openable { |
526 | for export in &metadata.exports { |
527 | if let Type::Function(ty) = &export.key.ty { |
528 | types.ty().function( |
529 | ty.parameters.iter().copied().map(ValType::from), |
530 | ty.results.iter().copied().map(ValType::from), |
531 | ); |
532 | } |
533 | } |
534 | } |
535 | } |
536 | for (_, ty, _) in function_exports { |
537 | types.ty().function( |
538 | ty.parameters.iter().copied().map(ValType::from), |
539 | ty.results.iter().copied().map(ValType::from), |
540 | ); |
541 | } |
542 | module.section(&types); |
543 | |
544 | let mut imports = ImportSection::new(); |
545 | imports.import( |
546 | "env" , |
547 | "memory" , |
548 | MemoryType { |
549 | minimum: 0, |
550 | maximum: None, |
551 | memory64: false, |
552 | shared: false, |
553 | page_size_log2: None, |
554 | }, |
555 | ); |
556 | imports.import( |
557 | "env" , |
558 | "__indirect_function_table" , |
559 | TableType { |
560 | element_type: RefType::FUNCREF, |
561 | minimum: 0, |
562 | maximum: None, |
563 | table64: false, |
564 | shared: false, |
565 | }, |
566 | ); |
567 | |
568 | let mut global_count = 0; |
569 | let mut global_map = HashMap::new(); |
570 | let mut add_global_import = |imports: &mut ImportSection, module: &str, name: &str, mutable| { |
571 | *global_map |
572 | .entry((module.to_owned(), name.to_owned())) |
573 | .or_insert_with(|| { |
574 | imports.import( |
575 | module, |
576 | name, |
577 | wasm_encoder::GlobalType { |
578 | val_type: ValType::I32, |
579 | mutable, |
580 | shared: false, |
581 | }, |
582 | ); |
583 | get_and_increment(&mut global_count) |
584 | }) |
585 | }; |
586 | |
587 | let mut function_count = 0; |
588 | let mut function_map = HashMap::new(); |
589 | let mut add_function_import = |imports: &mut ImportSection, module: &str, name: &str, ty| { |
590 | *function_map |
591 | .entry((module.to_owned(), name.to_owned())) |
592 | .or_insert_with(|| { |
593 | imports.import(module, name, EntityType::Function(ty)); |
594 | get_and_increment(&mut function_count) |
595 | }) |
596 | }; |
597 | |
598 | let mut memory_address_inits = Vec::new(); |
599 | let mut reloc_calls = Vec::new(); |
600 | let mut ctor_calls = Vec::new(); |
601 | let mut names = HashMap::new(); |
602 | |
603 | for (exporter, export, address) in dl_openables.global_addresses.iter() { |
604 | memory_address_inits.push(Ins::I32Const(i32::try_from(*address).unwrap())); |
605 | memory_address_inits.push(Ins::GlobalGet(add_global_import( |
606 | &mut imports, |
607 | "env" , |
608 | &format!(" {exporter}:memory_base" ), |
609 | false, |
610 | ))); |
611 | memory_address_inits.push(Ins::GlobalGet(add_global_import( |
612 | &mut imports, |
613 | exporter, |
614 | export, |
615 | false, |
616 | ))); |
617 | memory_address_inits.push(Ins::I32Add); |
618 | memory_address_inits.push(Ins::I32Store(MemArg { |
619 | offset: 0, |
620 | align: 2, |
621 | memory_index: 0, |
622 | })); |
623 | } |
624 | |
625 | for (index, metadata) in metadata.iter().enumerate() { |
626 | names.insert_unique(index, metadata.name); |
627 | |
628 | if metadata.has_data_relocs { |
629 | reloc_calls.push(Ins::Call(add_function_import( |
630 | &mut imports, |
631 | metadata.name, |
632 | "__wasm_apply_data_relocs" , |
633 | thunk_ty, |
634 | ))); |
635 | } |
636 | |
637 | if metadata.has_ctors && metadata.has_initialize { |
638 | bail!( |
639 | "library {} exports both `__wasm_call_ctors` and `_initialize`; \ |
640 | expected at most one of the two" , |
641 | metadata.name |
642 | ); |
643 | } |
644 | |
645 | if metadata.has_ctors { |
646 | ctor_calls.push(Ins::Call(add_function_import( |
647 | &mut imports, |
648 | metadata.name, |
649 | "__wasm_call_ctors" , |
650 | thunk_ty, |
651 | ))); |
652 | } |
653 | |
654 | if metadata.has_initialize { |
655 | ctor_calls.push(Ins::Call(add_function_import( |
656 | &mut imports, |
657 | metadata.name, |
658 | "_initialize" , |
659 | thunk_ty, |
660 | ))); |
661 | } |
662 | |
663 | if metadata.has_set_libraries { |
664 | ctor_calls.push(Ins::I32Const( |
665 | i32::try_from(dl_openables.libraries_address).unwrap(), |
666 | )); |
667 | ctor_calls.push(Ins::Call(add_function_import( |
668 | &mut imports, |
669 | metadata.name, |
670 | "__wasm_set_libraries" , |
671 | one_i32_param_ty, |
672 | ))); |
673 | } |
674 | |
675 | for import in &metadata.memory_address_imports { |
676 | let (exporter, _) = find_offset_exporter(import, exporters)?; |
677 | |
678 | memory_address_inits.push(Ins::GlobalGet(add_global_import( |
679 | &mut imports, |
680 | "env" , |
681 | &format!(" {exporter}:memory_base" ), |
682 | false, |
683 | ))); |
684 | memory_address_inits.push(Ins::GlobalGet(add_global_import( |
685 | &mut imports, |
686 | exporter, |
687 | import, |
688 | false, |
689 | ))); |
690 | memory_address_inits.push(Ins::I32Add); |
691 | memory_address_inits.push(Ins::GlobalSet(add_global_import( |
692 | &mut imports, |
693 | "env" , |
694 | &format!(" {}: {import}" , metadata.name), |
695 | true, |
696 | ))); |
697 | } |
698 | } |
699 | |
700 | let mut dl_openable_functions = Vec::new(); |
701 | for metadata in metadata { |
702 | if metadata.dl_openable { |
703 | for export in &metadata.exports { |
704 | if let Type::Function(_) = &export.key.ty { |
705 | dl_openable_functions.push(add_function_import( |
706 | &mut imports, |
707 | metadata.name, |
708 | export.key.name, |
709 | get_and_increment(&mut type_offset), |
710 | )); |
711 | } |
712 | } |
713 | } |
714 | } |
715 | |
716 | let indirections = function_exports |
717 | .iter() |
718 | .map(|(name, _, index)| { |
719 | add_function_import( |
720 | &mut imports, |
721 | names[index], |
722 | name, |
723 | get_and_increment(&mut type_offset), |
724 | ) |
725 | }) |
726 | .collect::<Vec<_>>(); |
727 | |
728 | module.section(&imports); |
729 | |
730 | { |
731 | let mut functions = FunctionSection::new(); |
732 | functions.function(thunk_ty); |
733 | module.section(&functions); |
734 | } |
735 | |
736 | module.section(&StartSection { |
737 | function_index: function_count, |
738 | }); |
739 | |
740 | { |
741 | let mut elements = ElementSection::new(); |
742 | elements.active( |
743 | None, |
744 | &const_u32(dl_openables.table_base), |
745 | Elements::Functions(dl_openable_functions.into()), |
746 | ); |
747 | elements.active( |
748 | None, |
749 | &const_u32(indirection_table_base), |
750 | Elements::Functions(indirections.into()), |
751 | ); |
752 | module.section(&elements); |
753 | } |
754 | |
755 | { |
756 | let mut code = CodeSection::new(); |
757 | let mut function = Function::new([]); |
758 | for ins in memory_address_inits |
759 | .iter() |
760 | .chain(&reloc_calls) |
761 | .chain(&ctor_calls) |
762 | { |
763 | function.instruction(ins); |
764 | } |
765 | function.instruction(&Ins::End); |
766 | code.function(&function); |
767 | module.section(&code); |
768 | } |
769 | |
770 | let mut data = DataSection::new(); |
771 | data.active(0, &const_u32(dl_openables.memory_base), dl_openables.buffer); |
772 | module.section(&data); |
773 | |
774 | module.section(&RawCustomSection( |
775 | &crate::base_producers().raw_custom_section(), |
776 | )); |
777 | |
778 | let module = module.finish(); |
779 | wasmparser::validate(&module)?; |
780 | |
781 | Ok(module) |
782 | } |
783 | |
784 | /// Find the library which exports the specified function or global address. |
785 | fn find_offset_exporter<'a>( |
786 | name: &str, |
787 | exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>, |
788 | ) -> Result<(&'a str, &'a Export<'a>)> { |
789 | let export: ExportKey<'_> = ExportKey { |
790 | name, |
791 | ty: Type::Global(GlobalType { |
792 | ty: ValueType::I32, |
793 | mutable: false, |
794 | shared: false, |
795 | }), |
796 | }; |
797 | |
798 | exporters |
799 | .get(&export) |
800 | .copied() |
801 | .ok_or_else(|| anyhow!("unable to find {export:?} in any library" )) |
802 | } |
803 | |
804 | /// Find the library which exports the specified function. |
805 | fn find_function_exporter<'a>( |
806 | name: &str, |
807 | ty: &FunctionType, |
808 | exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>, |
809 | ) -> Result<(&'a str, &'a Export<'a>)> { |
810 | let export: ExportKey<'_> = ExportKey { |
811 | name, |
812 | ty: Type::Function(ty.clone()), |
813 | }; |
814 | |
815 | exporters |
816 | .get(&export) |
817 | .copied() |
818 | .ok_or_else(|| anyhow!("unable to find {export:?} in any library" )) |
819 | } |
820 | |
821 | /// Analyze the specified library metadata, producing a symbol-to-library-name map of exports. |
822 | fn resolve_exporters<'a>( |
823 | metadata: &'a [Metadata<'a>], |
824 | ) -> Result<IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>> { |
825 | let mut exporters: IndexMap<&ExportKey<'_>, Vec<…>> = IndexMap::<_, Vec<_>>::new(); |
826 | for metadata: &'a Metadata<'_> in metadata { |
827 | for export: &Export<'_> in &metadata.exports { |
828 | exporters&mut Vec<(&'a str, &Export<'_>)> |
829 | .entry(&export.key) |
830 | .or_default() |
831 | .push((metadata.name, export)); |
832 | } |
833 | } |
834 | Ok(exporters) |
835 | } |
836 | |
837 | /// Match up all imported symbols to their corresponding exports, reporting any missing or duplicate symbols. |
838 | fn resolve_symbols<'a>( |
839 | metadata: &'a [Metadata<'a>], |
840 | exporters: &'a IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>, |
841 | ) -> ( |
842 | IndexMap<&'a ExportKey<'a>, (&'a str, &'a Export<'a>)>, |
843 | Vec<(&'a str, Export<'a>)>, |
844 | Vec<(&'a str, &'a ExportKey<'a>, &'a [(&'a str, &'a Export<'a>)])>, |
845 | ) { |
846 | let function_exporters = exporters |
847 | .iter() |
848 | .filter_map(|(export, exporters)| { |
849 | if let Type::Function(_) = &export.ty { |
850 | Some((export.name, (export, exporters))) |
851 | } else { |
852 | None |
853 | } |
854 | }) |
855 | .collect_unique::<IndexMap<_, _>>(); |
856 | |
857 | let mut resolved = IndexMap::new(); |
858 | let mut missing = Vec::new(); |
859 | let mut duplicates = Vec::new(); |
860 | |
861 | let mut triage = |metadata: &'a Metadata, export: Export<'a>| { |
862 | if let Some((key, value)) = exporters.get_key_value(&export.key) { |
863 | match value.as_slice() { |
864 | [] => unreachable!(), |
865 | [exporter] => { |
866 | // Note that we do not use `insert_unique` here since multiple libraries may import the same |
867 | // symbol, in which case we may redundantly insert the same value. |
868 | resolved.insert(*key, *exporter); |
869 | } |
870 | [exporter, ..] => { |
871 | resolved.insert(*key, *exporter); |
872 | duplicates.push((metadata.name, *key, value.as_slice())); |
873 | } |
874 | } |
875 | } else { |
876 | missing.push((metadata.name, export)); |
877 | } |
878 | }; |
879 | |
880 | for metadata in metadata { |
881 | for (name, (ty, flags)) in &metadata.env_imports { |
882 | triage( |
883 | metadata, |
884 | Export { |
885 | key: ExportKey { |
886 | name, |
887 | ty: Type::Function(ty.clone()), |
888 | }, |
889 | flags: *flags, |
890 | }, |
891 | ); |
892 | } |
893 | |
894 | for name in &metadata.memory_address_imports { |
895 | triage( |
896 | metadata, |
897 | Export { |
898 | key: ExportKey { |
899 | name, |
900 | ty: Type::Global(GlobalType { |
901 | ty: ValueType::I32, |
902 | mutable: false, |
903 | shared: false, |
904 | }), |
905 | }, |
906 | flags: SymbolFlags::empty(), |
907 | }, |
908 | ); |
909 | } |
910 | } |
911 | |
912 | for metadata in metadata { |
913 | for name in &metadata.table_address_imports { |
914 | if let Some((key, value)) = function_exporters.get(name) { |
915 | // Note that we do not use `insert_unique` here since multiple libraries may import the same |
916 | // symbol, in which case we may redundantly insert the same value. |
917 | match value.as_slice() { |
918 | [] => unreachable!(), |
919 | [exporter] => { |
920 | resolved.insert(key, *exporter); |
921 | } |
922 | [exporter, ..] => { |
923 | resolved.insert(key, *exporter); |
924 | duplicates.push((metadata.name, *key, value.as_slice())); |
925 | } |
926 | } |
927 | } else { |
928 | missing.push(( |
929 | metadata.name, |
930 | Export { |
931 | key: ExportKey { |
932 | name, |
933 | ty: Type::Function(FunctionType { |
934 | parameters: Vec::new(), |
935 | results: Vec::new(), |
936 | }), |
937 | }, |
938 | flags: SymbolFlags::empty(), |
939 | }, |
940 | )); |
941 | } |
942 | } |
943 | } |
944 | |
945 | (resolved, missing, duplicates) |
946 | } |
947 | |
948 | /// Recursively add a library (represented by its offset) and its dependency to the specified set, maintaining |
949 | /// topological order (modulo cycles). |
950 | fn topo_add<'a>( |
951 | sorted: &mut IndexSet<usize>, |
952 | dependencies: &IndexMap<usize, IndexSet<usize>>, |
953 | element: usize, |
954 | ) { |
955 | let empty: &IndexSet = &IndexSet::new(); |
956 | let deps: &IndexSet = dependencies.get(&element).unwrap_or(default:empty); |
957 | |
958 | // First, add any dependencies which do not depend on `element` |
959 | for &dep: usize in deps { |
960 | if !(sorted.contains(&dep) || dependencies.get(&dep).unwrap_or(default:empty).contains(&element)) { |
961 | topo_add(sorted, dependencies, element:dep); |
962 | } |
963 | } |
964 | |
965 | // Next, add the element |
966 | sorted.insert(element); |
967 | |
968 | // Finally, add any dependencies which depend on `element` |
969 | for &dep: usize in deps { |
970 | if !sorted.contains(&dep) && dependencies.get(&dep).unwrap_or(default:empty).contains(&element) { |
971 | topo_add(sorted, dependencies, element:dep); |
972 | } |
973 | } |
974 | } |
975 | |
976 | /// Topologically sort a set of libraries (represented by their offsets) according to their dependencies, modulo |
977 | /// cycles. |
978 | fn topo_sort(count: usize, dependencies: &IndexMap<usize, IndexSet<usize>>) -> Result<Vec<usize>> { |
979 | let mut sorted: IndexSet = IndexSet::new(); |
980 | for index: usize in 0..count { |
981 | topo_add(&mut sorted, &dependencies, element:index); |
982 | } |
983 | |
984 | Ok(sorted.into_iter().collect()) |
985 | } |
986 | |
987 | /// Analyze the specified library metadata, producing a map of transitive dependencies, where each library is |
988 | /// represented by its offset in the original metadata slice. |
989 | fn find_dependencies( |
990 | metadata: &[Metadata], |
991 | exporters: &IndexMap<&ExportKey, (&str, &Export)>, |
992 | ) -> Result<IndexMap<usize, IndexSet<usize>>> { |
993 | // First, generate a map of direct dependencies (i.e. depender to dependees) |
994 | let mut dependencies = IndexMap::<_, IndexSet<_>>::new(); |
995 | let mut indexes = HashMap::new(); |
996 | for (index, metadata) in metadata.iter().enumerate() { |
997 | indexes.insert_unique(metadata.name, index); |
998 | for &needed in &metadata.needed_libs { |
999 | dependencies |
1000 | .entry(metadata.name) |
1001 | .or_default() |
1002 | .insert(needed); |
1003 | } |
1004 | for (import_name, (ty, _)) in &metadata.env_imports { |
1005 | dependencies |
1006 | .entry(metadata.name) |
1007 | .or_default() |
1008 | .insert(find_function_exporter(import_name, ty, exporters)?.0); |
1009 | } |
1010 | } |
1011 | |
1012 | // Next, convert the map from names to offsets |
1013 | let mut dependencies = dependencies |
1014 | .into_iter() |
1015 | .map(|(k, v)| { |
1016 | ( |
1017 | indexes[k], |
1018 | v.into_iter() |
1019 | .map(|v| indexes[v]) |
1020 | .collect_unique::<IndexSet<_>>(), |
1021 | ) |
1022 | }) |
1023 | .collect_unique::<IndexMap<_, _>>(); |
1024 | |
1025 | // Finally, add all transitive dependencies to the map in a fixpoint loop, exiting when no new dependencies are |
1026 | // discovered. |
1027 | let empty = &IndexSet::new(); |
1028 | |
1029 | loop { |
1030 | let mut new = IndexMap::<_, IndexSet<_>>::new(); |
1031 | for (index, exporters) in &dependencies { |
1032 | for exporter in exporters { |
1033 | for exporter in dependencies.get(exporter).unwrap_or(empty) { |
1034 | if !exporters.contains(exporter) { |
1035 | new.entry(*index).or_default().insert(*exporter); |
1036 | } |
1037 | } |
1038 | } |
1039 | } |
1040 | |
1041 | if new.is_empty() { |
1042 | break Ok(dependencies); |
1043 | } else { |
1044 | for (index, exporters) in new { |
1045 | dependencies.entry(index).or_default().extend(exporters); |
1046 | } |
1047 | } |
1048 | } |
1049 | } |
1050 | |
1051 | struct EnvFunctionExports<'a> { |
1052 | exports: Vec<(&'a str, &'a FunctionType, usize)>, |
1053 | reexport_cabi_realloc: bool, |
1054 | } |
1055 | |
1056 | /// Analyze the specified metadata and generate a list of functions which should be re-exported as a |
1057 | /// `call.indirect`-based function by the main (AKA "env") module, including the offset of the library containing |
1058 | /// the original export. |
1059 | fn env_function_exports<'a>( |
1060 | metadata: &'a [Metadata<'a>], |
1061 | exporters: &'a IndexMap<&'a ExportKey, (&'a str, &Export)>, |
1062 | topo_sorted: &[usize], |
1063 | ) -> Result<EnvFunctionExports<'a>> { |
1064 | let function_exporters = exporters |
1065 | .iter() |
1066 | .filter_map(|(export, exporter)| { |
1067 | if let Type::Function(ty) = &export.ty { |
1068 | Some((export.name, (ty, *exporter))) |
1069 | } else { |
1070 | None |
1071 | } |
1072 | }) |
1073 | .collect_unique::<HashMap<_, _>>(); |
1074 | |
1075 | let indexes = metadata |
1076 | .iter() |
1077 | .enumerate() |
1078 | .map(|(index, metadata)| (metadata.name, index)) |
1079 | .collect_unique::<HashMap<_, _>>(); |
1080 | |
1081 | let mut result = Vec::new(); |
1082 | let mut exported = HashSet::new(); |
1083 | let mut seen = HashSet::new(); |
1084 | |
1085 | for &index in topo_sorted { |
1086 | let metadata = &metadata[index]; |
1087 | |
1088 | for name in &metadata.table_address_imports { |
1089 | if !exported.contains(name) { |
1090 | let (ty, (exporter, _)) = function_exporters |
1091 | .get(name) |
1092 | .ok_or_else(|| anyhow!("unable to find {name:?} in any library" ))?; |
1093 | |
1094 | result.push((*name, *ty, indexes[exporter])); |
1095 | exported.insert(*name); |
1096 | } |
1097 | } |
1098 | |
1099 | for (import_name, (ty, _)) in &metadata.env_imports { |
1100 | if !exported.contains(import_name) { |
1101 | let exporter = indexes[find_function_exporter(import_name, ty, exporters) |
1102 | .unwrap() |
1103 | .0]; |
1104 | if !seen.contains(&exporter) { |
1105 | result.push((*import_name, ty, exporter)); |
1106 | exported.insert(*import_name); |
1107 | } |
1108 | } |
1109 | } |
1110 | seen.insert(index); |
1111 | } |
1112 | |
1113 | let reexport_cabi_realloc = exported.contains("cabi_realloc" ); |
1114 | |
1115 | Ok(EnvFunctionExports { |
1116 | exports: result, |
1117 | reexport_cabi_realloc, |
1118 | }) |
1119 | } |
1120 | |
1121 | /// Synthesize a module which contains trapping stub exports for the specified functions. |
1122 | fn make_stubs_module(missing: &[(&str, Export)]) -> Vec<u8> { |
1123 | let mut types = TypeSection::new(); |
1124 | let mut exports = ExportSection::new(); |
1125 | let mut functions = FunctionSection::new(); |
1126 | let mut code = CodeSection::new(); |
1127 | for (offset, (_, export)) in missing.iter().enumerate() { |
1128 | let offset = u32::try_from(offset).unwrap(); |
1129 | |
1130 | let Export { |
1131 | key: |
1132 | ExportKey { |
1133 | name, |
1134 | ty: Type::Function(ty), |
1135 | }, |
1136 | .. |
1137 | } = export |
1138 | else { |
1139 | unreachable!(); |
1140 | }; |
1141 | |
1142 | types.ty().function( |
1143 | ty.parameters.iter().copied().map(ValType::from), |
1144 | ty.results.iter().copied().map(ValType::from), |
1145 | ); |
1146 | functions.function(offset); |
1147 | let mut function = Function::new([]); |
1148 | function.instruction(&Ins::Unreachable); |
1149 | function.instruction(&Ins::End); |
1150 | code.function(&function); |
1151 | exports.export(name, ExportKind::Func, offset); |
1152 | } |
1153 | |
1154 | let mut module = Module::new(); |
1155 | |
1156 | module.section(&types); |
1157 | module.section(&functions); |
1158 | module.section(&exports); |
1159 | module.section(&code); |
1160 | module.section(&RawCustomSection( |
1161 | &crate::base_producers().raw_custom_section(), |
1162 | )); |
1163 | |
1164 | let module = module.finish(); |
1165 | wasmparser::validate(&module).unwrap(); |
1166 | |
1167 | module |
1168 | } |
1169 | |
1170 | /// Determine which of the specified libraries are transitively reachable at runtime, i.e. reachable from a |
1171 | /// component export or via `dlopen`. |
1172 | fn find_reachable<'a>( |
1173 | metadata: &'a [Metadata<'a>], |
1174 | dependencies: &IndexMap<usize, IndexSet<usize>>, |
1175 | ) -> IndexSet<&'a str> { |
1176 | let reachable = metadata |
1177 | .iter() |
1178 | .enumerate() |
1179 | .filter_map(|(index, metadata)| { |
1180 | if metadata.has_component_exports || metadata.dl_openable || metadata.has_wasi_start { |
1181 | Some(index) |
1182 | } else { |
1183 | None |
1184 | } |
1185 | }) |
1186 | .collect_unique::<IndexSet<_>>(); |
1187 | |
1188 | let empty = &IndexSet::new(); |
1189 | |
1190 | reachable |
1191 | .iter() |
1192 | .chain( |
1193 | reachable |
1194 | .iter() |
1195 | .flat_map(|index| dependencies.get(index).unwrap_or(empty)), |
1196 | ) |
1197 | .map(|&index| metadata[index].name) |
1198 | .collect() |
1199 | } |
1200 | |
1201 | /// Builder type for composing dynamic library modules into a component |
1202 | #[derive (Default)] |
1203 | pub struct Linker { |
1204 | /// The `(name, module, dl_openable)` triple representing the libraries to be composed |
1205 | /// |
1206 | /// The order of this list determines priority in cases where more than one library exports the same symbol. |
1207 | libraries: Vec<(String, Vec<u8>, bool)>, |
1208 | |
1209 | /// The set of adapters to use when generating the component |
1210 | adapters: Vec<(String, Vec<u8>)>, |
1211 | |
1212 | /// Whether to validate the resulting component prior to returning it |
1213 | validate: bool, |
1214 | |
1215 | /// Whether to generate trapping stubs for any unresolved imports |
1216 | stub_missing_functions: bool, |
1217 | |
1218 | /// Whether to use a built-in implementation of `dlopen`/`dlsym`. |
1219 | use_built_in_libdl: bool, |
1220 | |
1221 | /// Size of stack (in bytes) to allocate in the synthesized main module |
1222 | /// |
1223 | /// If `None`, use `DEFAULT_STACK_SIZE_BYTES`. |
1224 | stack_size: Option<u32>, |
1225 | |
1226 | /// This affects how when to WIT worlds are merged together, for example |
1227 | /// from two different libraries, whether their imports are unified when the |
1228 | /// semver version ranges for interface allow it. |
1229 | merge_imports_based_on_semver: Option<bool>, |
1230 | } |
1231 | |
1232 | impl Linker { |
1233 | /// Add a dynamic library module to this linker. |
1234 | /// |
1235 | /// If `dl_openable` is true, all of the libraries exports will be added to the `dlopen`/`dlsym` lookup table |
1236 | /// for runtime resolution. |
1237 | pub fn library(mut self, name: &str, module: &[u8], dl_openable: bool) -> Result<Self> { |
1238 | self.libraries |
1239 | .push((name.to_owned(), module.to_vec(), dl_openable)); |
1240 | |
1241 | Ok(self) |
1242 | } |
1243 | |
1244 | /// Add an adapter to this linker. |
1245 | /// |
1246 | /// See [crate::encoding::ComponentEncoder::adapter] for details. |
1247 | pub fn adapter(mut self, name: &str, module: &[u8]) -> Result<Self> { |
1248 | self.adapters.push((name.to_owned(), module.to_vec())); |
1249 | |
1250 | Ok(self) |
1251 | } |
1252 | |
1253 | /// Specify whether to validate the resulting component prior to returning it |
1254 | pub fn validate(mut self, validate: bool) -> Self { |
1255 | self.validate = validate; |
1256 | self |
1257 | } |
1258 | |
1259 | /// Specify size of stack to allocate in the synthesized main module |
1260 | pub fn stack_size(mut self, stack_size: u32) -> Self { |
1261 | self.stack_size = Some(stack_size); |
1262 | self |
1263 | } |
1264 | |
1265 | /// Specify whether to generate trapping stubs for any unresolved imports |
1266 | pub fn stub_missing_functions(mut self, stub_missing_functions: bool) -> Self { |
1267 | self.stub_missing_functions = stub_missing_functions; |
1268 | self |
1269 | } |
1270 | |
1271 | /// Specify whether to use a built-in implementation of `dlopen`/`dlsym`. |
1272 | pub fn use_built_in_libdl(mut self, use_built_in_libdl: bool) -> Self { |
1273 | self.use_built_in_libdl = use_built_in_libdl; |
1274 | self |
1275 | } |
1276 | |
1277 | /// This affects how when to WIT worlds are merged together, for example |
1278 | /// from two different libraries, whether their imports are unified when the |
1279 | /// semver version ranges for interface allow it. |
1280 | /// |
1281 | /// This is enabled by default. |
1282 | pub fn merge_imports_based_on_semver(mut self, merge: bool) -> Self { |
1283 | self.merge_imports_based_on_semver = Some(merge); |
1284 | self |
1285 | } |
1286 | |
1287 | /// Encode the component and return the bytes |
1288 | pub fn encode(mut self) -> Result<Vec<u8>> { |
1289 | if self.use_built_in_libdl { |
1290 | self.use_built_in_libdl = false; |
1291 | self = self.library("libdl.so" , include_bytes!("../libdl.so" ), false)?; |
1292 | } |
1293 | |
1294 | let adapter_names = self |
1295 | .adapters |
1296 | .iter() |
1297 | .map(|(name, _)| name.as_str()) |
1298 | .collect_unique::<HashSet<_>>(); |
1299 | |
1300 | if adapter_names.len() != self.adapters.len() { |
1301 | bail!("duplicate adapter name" ); |
1302 | } |
1303 | |
1304 | let metadata = self |
1305 | .libraries |
1306 | .iter() |
1307 | .map(|(name, module, dl_openable)| { |
1308 | Metadata::try_new(name, *dl_openable, module, &adapter_names) |
1309 | .with_context(|| format!("failed to extract linking metadata from {name}" )) |
1310 | }) |
1311 | .collect::<Result<Vec<_>>>()?; |
1312 | |
1313 | { |
1314 | let names = self |
1315 | .libraries |
1316 | .iter() |
1317 | .map(|(name, ..)| name.as_str()) |
1318 | .collect_unique::<HashSet<_>>(); |
1319 | |
1320 | let missing = metadata |
1321 | .iter() |
1322 | .filter_map(|metadata| { |
1323 | let missing = metadata |
1324 | .needed_libs |
1325 | .iter() |
1326 | .copied() |
1327 | .filter(|name| !names.contains(*name)) |
1328 | .collect::<Vec<_>>(); |
1329 | |
1330 | if missing.is_empty() { |
1331 | None |
1332 | } else { |
1333 | Some((metadata.name, missing)) |
1334 | } |
1335 | }) |
1336 | .collect::<Vec<_>>(); |
1337 | |
1338 | if !missing.is_empty() { |
1339 | bail!( |
1340 | "missing libraries: \n{}" , |
1341 | missing |
1342 | .iter() |
1343 | .map(|(needed_by, missing)| format!( |
1344 | " \t{needed_by} needs {}" , |
1345 | missing.join(", " ) |
1346 | )) |
1347 | .collect::<Vec<_>>() |
1348 | .join(" \n" ) |
1349 | ); |
1350 | } |
1351 | } |
1352 | |
1353 | let exporters = resolve_exporters(&metadata)?; |
1354 | |
1355 | let cabi_realloc_exporter = exporters |
1356 | .get(&ExportKey { |
1357 | name: "cabi_realloc" , |
1358 | ty: Type::Function(FunctionType { |
1359 | parameters: vec![ValueType::I32; 4], |
1360 | results: vec![ValueType::I32], |
1361 | }), |
1362 | }) |
1363 | .map(|exporters| exporters.first().unwrap().0); |
1364 | |
1365 | let (exporters, missing, _) = resolve_symbols(&metadata, &exporters); |
1366 | |
1367 | if !missing.is_empty() { |
1368 | if missing |
1369 | .iter() |
1370 | .all(|(_, export)| matches!(&export.key.ty, Type::Function(_))) |
1371 | && (self.stub_missing_functions |
1372 | || missing |
1373 | .iter() |
1374 | .all(|(_, export)| export.flags.contains(SymbolFlags::BINDING_WEAK))) |
1375 | { |
1376 | self.stub_missing_functions = false; |
1377 | self.libraries.push(( |
1378 | "wit-component:stubs" .into(), |
1379 | make_stubs_module(&missing), |
1380 | false, |
1381 | )); |
1382 | return self.encode(); |
1383 | } else { |
1384 | bail!( |
1385 | "unresolved symbol(s): \n{}" , |
1386 | missing |
1387 | .iter() |
1388 | .filter(|(_, export)| !export.flags.contains(SymbolFlags::BINDING_WEAK)) |
1389 | .map(|(importer, export)| { format!(" \t{importer} needs {}" , export.key) }) |
1390 | .collect::<Vec<_>>() |
1391 | .join(" \n" ) |
1392 | ); |
1393 | } |
1394 | } |
1395 | |
1396 | let dependencies = find_dependencies(&metadata, &exporters)?; |
1397 | |
1398 | { |
1399 | let reachable = find_reachable(&metadata, &dependencies); |
1400 | let unreachable = self |
1401 | .libraries |
1402 | .iter() |
1403 | .filter_map(|(name, ..)| (!reachable.contains(name.as_str())).then(|| name.clone())) |
1404 | .collect_unique::<HashSet<_>>(); |
1405 | |
1406 | if !unreachable.is_empty() { |
1407 | self.libraries |
1408 | .retain(|(name, ..)| !unreachable.contains(name)); |
1409 | return self.encode(); |
1410 | } |
1411 | } |
1412 | |
1413 | let topo_sorted = topo_sort(metadata.len(), &dependencies)?; |
1414 | |
1415 | let EnvFunctionExports { |
1416 | exports: env_function_exports, |
1417 | reexport_cabi_realloc, |
1418 | } = env_function_exports(&metadata, &exporters, &topo_sorted)?; |
1419 | |
1420 | let (env_module, dl_openables, table_base) = make_env_module( |
1421 | &metadata, |
1422 | &env_function_exports, |
1423 | if reexport_cabi_realloc { |
1424 | // If "env" module already reexports "cabi_realloc", we don't need to |
1425 | // reexport it again. |
1426 | None |
1427 | } else { |
1428 | cabi_realloc_exporter |
1429 | }, |
1430 | self.stack_size.unwrap_or(DEFAULT_STACK_SIZE_BYTES), |
1431 | ); |
1432 | |
1433 | let mut encoder = ComponentEncoder::default().validate(self.validate); |
1434 | if let Some(merge) = self.merge_imports_based_on_semver { |
1435 | encoder = encoder.merge_imports_based_on_semver(merge); |
1436 | }; |
1437 | encoder = encoder.module(&env_module)?; |
1438 | |
1439 | for (name, module) in &self.adapters { |
1440 | encoder = encoder.adapter(name, module)?; |
1441 | } |
1442 | |
1443 | let default_env_items = [ |
1444 | Item { |
1445 | alias: "memory" .into(), |
1446 | kind: ExportKind::Memory, |
1447 | which: MainOrAdapter::Main, |
1448 | name: "memory" .into(), |
1449 | }, |
1450 | Item { |
1451 | alias: "__indirect_function_table" .into(), |
1452 | kind: ExportKind::Table, |
1453 | which: MainOrAdapter::Main, |
1454 | name: "__indirect_function_table" .into(), |
1455 | }, |
1456 | Item { |
1457 | alias: "__stack_pointer" .into(), |
1458 | kind: ExportKind::Global, |
1459 | which: MainOrAdapter::Main, |
1460 | name: "__stack_pointer" .into(), |
1461 | }, |
1462 | ]; |
1463 | |
1464 | let mut seen = HashSet::new(); |
1465 | for index in topo_sorted { |
1466 | let (name, module, _) = &self.libraries[index]; |
1467 | let metadata = &metadata[index]; |
1468 | |
1469 | let env_items = default_env_items |
1470 | .iter() |
1471 | .cloned() |
1472 | .chain([ |
1473 | Item { |
1474 | alias: "__memory_base" .into(), |
1475 | kind: ExportKind::Global, |
1476 | which: MainOrAdapter::Main, |
1477 | name: format!(" {name}:memory_base" ), |
1478 | }, |
1479 | Item { |
1480 | alias: "__table_base" .into(), |
1481 | kind: ExportKind::Global, |
1482 | which: MainOrAdapter::Main, |
1483 | name: format!(" {name}:table_base" ), |
1484 | }, |
1485 | ]) |
1486 | .chain(metadata.env_imports.iter().map(|(name, (ty, _))| { |
1487 | let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap(); |
1488 | |
1489 | Item { |
1490 | alias: (*name).into(), |
1491 | kind: ExportKind::Func, |
1492 | which: if seen.contains(exporter) { |
1493 | MainOrAdapter::Adapter(exporter.to_owned()) |
1494 | } else { |
1495 | MainOrAdapter::Main |
1496 | }, |
1497 | name: (*name).into(), |
1498 | } |
1499 | })) |
1500 | .chain(if metadata.is_asyncified { |
1501 | vec![ |
1502 | Item { |
1503 | alias: "__asyncify_state" .into(), |
1504 | kind: ExportKind::Global, |
1505 | which: MainOrAdapter::Main, |
1506 | name: "__asyncify_state" .into(), |
1507 | }, |
1508 | Item { |
1509 | alias: "__asyncify_data" .into(), |
1510 | kind: ExportKind::Global, |
1511 | which: MainOrAdapter::Main, |
1512 | name: "__asyncify_data" .into(), |
1513 | }, |
1514 | ] |
1515 | } else { |
1516 | vec![] |
1517 | }) |
1518 | .collect(); |
1519 | |
1520 | let global_item = |address_name: &str| Item { |
1521 | alias: address_name.into(), |
1522 | kind: ExportKind::Global, |
1523 | which: MainOrAdapter::Main, |
1524 | name: format!(" {name}: {address_name}" ), |
1525 | }; |
1526 | |
1527 | let mem_items = metadata |
1528 | .memory_address_imports |
1529 | .iter() |
1530 | .copied() |
1531 | .map(global_item) |
1532 | .chain(["__heap_base" , "__heap_end" ].into_iter().map(|name| Item { |
1533 | alias: name.into(), |
1534 | kind: ExportKind::Global, |
1535 | which: MainOrAdapter::Main, |
1536 | name: name.into(), |
1537 | })) |
1538 | .collect(); |
1539 | |
1540 | let func_items = metadata |
1541 | .table_address_imports |
1542 | .iter() |
1543 | .copied() |
1544 | .map(global_item) |
1545 | .collect(); |
1546 | |
1547 | let mut import_items = BTreeMap::<_, Vec<_>>::new(); |
1548 | for import in &metadata.imports { |
1549 | import_items.entry(import.module).or_default().push(Item { |
1550 | alias: import.name.into(), |
1551 | kind: ExportKind::from(&import.ty), |
1552 | which: MainOrAdapter::Main, |
1553 | name: format!(" {}: {}" , import.module, import.name), |
1554 | }); |
1555 | } |
1556 | |
1557 | encoder = encoder.library( |
1558 | name, |
1559 | module, |
1560 | LibraryInfo { |
1561 | instantiate_after_shims: false, |
1562 | arguments: [ |
1563 | ("GOT.mem" .into(), Instance::Items(mem_items)), |
1564 | ("GOT.func" .into(), Instance::Items(func_items)), |
1565 | ("env" .into(), Instance::Items(env_items)), |
1566 | ] |
1567 | .into_iter() |
1568 | .chain( |
1569 | import_items |
1570 | .into_iter() |
1571 | .map(|(k, v)| (k.into(), Instance::Items(v))), |
1572 | ) |
1573 | .collect(), |
1574 | }, |
1575 | )?; |
1576 | |
1577 | seen.insert(name.as_str()); |
1578 | } |
1579 | |
1580 | encoder |
1581 | .library( |
1582 | "__init" , |
1583 | &make_init_module( |
1584 | &metadata, |
1585 | &exporters, |
1586 | &env_function_exports, |
1587 | dl_openables, |
1588 | table_base, |
1589 | )?, |
1590 | LibraryInfo { |
1591 | instantiate_after_shims: true, |
1592 | arguments: iter::once(( |
1593 | "env" .into(), |
1594 | Instance::MainOrAdapter(MainOrAdapter::Main), |
1595 | )) |
1596 | .chain(self.libraries.iter().map(|(name, ..)| { |
1597 | ( |
1598 | name.clone(), |
1599 | Instance::MainOrAdapter(MainOrAdapter::Adapter(name.clone())), |
1600 | ) |
1601 | })) |
1602 | .collect(), |
1603 | }, |
1604 | )? |
1605 | .encode() |
1606 | } |
1607 | } |
1608 | |