1use {
26 crate::encoding::{ComponentEncoder, Instance, Item, LibraryInfo, MainOrAdapter},
27 anyhow::{Context, Result, anyhow, bail},
28 indexmap::{IndexMap, IndexSet, map::Entry},
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, TagKind, TagSection, TagType, TypeSection, ValType,
41 },
42 wasmparser::SymbolFlags,
43};
44
45mod metadata;
46
47const PAGE_SIZE_BYTES: u32 = 65536;
48pub const DEFAULT_STACK_SIZE_BYTES: u32 = 16 * PAGE_SIZE_BYTES;
50const HEAP_ALIGNMENT_BYTES: u32 = 16;
51const STUB_LIBRARY_NAME: &str = "wit-component:stubs";
52
53enum Address<'a> {
54 Function(u32),
55 Global(&'a str),
56}
57
58struct DlOpenables<'a> {
63 table_base: u32,
65
66 memory_base: u32,
68
69 buffer: Vec<u8>,
71
72 global_addresses: Vec<(&'a str, &'a str, u32)>,
76
77 function_count: u32,
79
80 libraries_address: u32,
85}
86
87impl<'a> DlOpenables<'a> {
88 fn new(table_base: u32, memory_base: u32, metadata: &'a [Metadata<'a>]) -> Self {
91 let mut function_count = 0;
92 let mut buffer = Vec::new();
93 let mut global_addresses = Vec::new();
94 let mut libraries = metadata
95 .iter()
96 .filter(|metadata| metadata.dl_openable)
97 .map(|metadata| {
98 let name_address = memory_base + u32::try_from(buffer.len()).unwrap();
99 write_bytes_padded(&mut buffer, metadata.name.as_bytes());
100
101 let mut symbols = metadata
102 .exports
103 .iter()
104 .filter_map(|export| {
105 let name_address = memory_base + u32::try_from(buffer.len()).unwrap();
106 write_bytes_padded(&mut buffer, export.key.name.as_bytes());
107
108 let address = match &export.key.ty {
109 Type::Function(_) => Address::Function(
110 table_base + get_and_increment(&mut function_count),
111 ),
112 Type::Global(_) => Address::Global(export.key.name),
113 Type::Tag(_) => return None,
114 };
115
116 Some((export.key.name, name_address, address))
117 })
118 .collect::<Vec<_>>();
119
120 symbols.sort_by_key(|(name, ..)| *name);
121
122 let start = buffer.len();
123 for (name, name_address, address) in symbols {
124 write_u32(&mut buffer, u32::try_from(name.len()).unwrap());
125 write_u32(&mut buffer, name_address);
126 match address {
127 Address::Function(address) => write_u32(&mut buffer, address),
128 Address::Global(name) => {
129 global_addresses.push((
130 metadata.name,
131 name,
132 memory_base + u32::try_from(buffer.len()).unwrap(),
133 ));
134
135 write_u32(&mut buffer, 0);
136 }
137 }
138 }
139
140 (
141 metadata.name,
142 name_address,
143 metadata.exports.len(),
144 memory_base + u32::try_from(start).unwrap(),
145 )
146 })
147 .collect::<Vec<_>>();
148
149 libraries.sort_by_key(|(name, ..)| *name);
150
151 let start = buffer.len();
152 for (name, name_address, count, symbols) in &libraries {
153 write_u32(&mut buffer, u32::try_from(name.len()).unwrap());
154 write_u32(&mut buffer, *name_address);
155 write_u32(&mut buffer, u32::try_from(*count).unwrap());
156 write_u32(&mut buffer, *symbols);
157 }
158
159 let libraries_address = memory_base + u32::try_from(buffer.len()).unwrap();
160 write_u32(&mut buffer, u32::try_from(libraries.len()).unwrap());
161 write_u32(&mut buffer, memory_base + u32::try_from(start).unwrap());
162
163 Self {
164 table_base,
165 memory_base,
166 buffer,
167 global_addresses,
168 function_count,
169 libraries_address,
170 }
171 }
172}
173
174fn write_u32(buffer: &mut Vec<u8>, value: u32) {
175 buffer.extend(value.to_le_bytes());
176}
177
178fn write_bytes_padded(buffer: &mut Vec<u8>, bytes: &[u8]) {
179 buffer.extend(bytes);
180
181 let len = u32::try_from(bytes.len()).unwrap();
182 for _ in len..align(len, 4) {
183 buffer.push(0);
184 }
185}
186
187fn align(a: u32, b: u32) -> u32 {
188 assert!(b.is_power_of_two());
189 (a + (b - 1)) & !(b - 1)
190}
191
192fn get_and_increment(n: &mut u32) -> u32 {
193 let v = *n;
194 *n += 1;
195 v
196}
197
198fn const_u32(a: u32) -> ConstExpr {
199 ConstExpr::i32_const(a as i32)
200}
201
202trait Length {
204 fn len(&self) -> usize;
205}
206
207impl<T> Length for HashSet<T> {
208 fn len(&self) -> usize {
209 HashSet::len(self)
210 }
211}
212
213impl<K, V> Length for HashMap<K, V> {
214 fn len(&self) -> usize {
215 HashMap::len(self)
216 }
217}
218
219impl<T> Length for IndexSet<T> {
220 fn len(&self) -> usize {
221 IndexSet::len(self)
222 }
223}
224
225impl<K, V> Length for IndexMap<K, V> {
226 fn len(&self) -> usize {
227 IndexMap::len(self)
228 }
229}
230
231trait CollectUnique: Iterator + Sized {
234 fn collect_unique<T: FromIterator<Self::Item> + Length>(self) -> T {
235 let tmp = self.collect::<Vec<_>>();
236 let len = tmp.len();
237 let result = tmp.into_iter().collect::<T>();
238 assert!(
239 result.len() == len,
240 "one or more duplicate items detected when collecting into set or map"
241 );
242 result
243 }
244}
245
246impl<T: Iterator> CollectUnique for T {}
247
248trait InsertUnique {
250 type Key;
251 type Value;
252
253 fn insert_unique(&mut self, k: Self::Key, v: Self::Value);
254}
255
256impl<K: Hash + Eq + PartialEq + Debug, V: Debug> InsertUnique for HashMap<K, V> {
257 type Key = K;
258 type Value = V;
259
260 fn insert_unique(&mut self, k: Self::Key, v: Self::Value) {
261 if let Some(old_v) = self.get(&k) {
262 panic!(
263 "duplicate item inserted into map for key {k:?} (old value: {old_v:?}; new value: {v:?})"
264 );
265 }
266 self.insert(k, v);
267 }
268}
269
270fn make_env_module<'a>(
273 metadata: &'a [Metadata<'a>],
274 env_exports: &[EnvExport<'_>],
275 cabi_realloc_exporter: Option<&str>,
276 stack_size_bytes: u32,
277) -> (Vec<u8>, DlOpenables<'a>, u32) {
278 let mut types = TypeSection::new();
280 let mut imports = ImportSection::new();
281 let mut import_map = IndexMap::new();
282 let mut function_count = 0;
283 let mut global_offset = 0;
284 let mut wasi_start = None;
285
286 for metadata in metadata {
287 for import in &metadata.imports {
288 if let Entry::Vacant(entry) = import_map.entry(import) {
289 imports.import(
290 import.module,
291 import.name,
292 match &import.ty {
293 Type::Function(ty) => {
294 let index = get_and_increment(&mut function_count);
295 entry.insert(index);
296 types.ty().function(
297 ty.parameters.iter().copied().map(ValType::from),
298 ty.results.iter().copied().map(ValType::from),
299 );
300 EntityType::Function(index)
301 }
302 Type::Global(ty) => {
303 entry.insert(get_and_increment(&mut global_offset));
304 EntityType::Global(wasm_encoder::GlobalType {
305 val_type: ty.ty.into(),
306 mutable: ty.mutable,
307 shared: ty.shared,
308 })
309 }
310 Type::Tag(_) => continue,
311 },
312 );
313 }
314 }
315
316 if metadata.has_wasi_start {
317 if wasi_start.is_some() {
318 panic!("multiple libraries export _start");
319 }
320 let index = get_and_increment(&mut function_count);
321
322 types.ty().function(vec![], vec![]);
323 imports.import(metadata.name, "_start", EntityType::Function(index));
324
325 wasi_start = Some(index);
326 }
327 }
328
329 let mut memory_offset = stack_size_bytes;
330
331 let mut table_offset = 1;
335 let mut globals = GlobalSection::new();
336 let mut exports = ExportSection::new();
337
338 if let Some(exporter) = cabi_realloc_exporter {
339 let index = get_and_increment(&mut function_count);
340 types.ty().function([ValType::I32; 4], [ValType::I32]);
341 imports.import(exporter, "cabi_realloc", EntityType::Function(index));
342 exports.export("cabi_realloc", ExportKind::Func, index);
343 }
344
345 let dl_openables = DlOpenables::new(table_offset, memory_offset, metadata);
346
347 table_offset += dl_openables.function_count;
348 memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap();
349
350 let memory_size = {
351 let mut add_global_export = |name: &str, value, mutable| {
352 let index = globals.len();
353 globals.global(
354 wasm_encoder::GlobalType {
355 val_type: ValType::I32,
356 mutable,
357 shared: false,
358 },
359 &const_u32(value),
360 );
361 exports.export(name, ExportKind::Global, index);
362 };
363
364 add_global_export("__stack_pointer", stack_size_bytes, true);
365
366 let has_asyncified_module = metadata.iter().any(|m| m.is_asyncified);
369 if has_asyncified_module {
370 add_global_export("__asyncify_state", 0, true);
371 add_global_export("__asyncify_data", 0, true);
372 }
373
374 add_global_export("__stack_high", stack_size_bytes, true);
376 add_global_export("__stack_low", 0, true);
377
378 for metadata in metadata {
379 memory_offset = align(memory_offset, 1 << metadata.mem_info.memory_alignment);
380 table_offset = align(table_offset, 1 << metadata.mem_info.table_alignment);
381
382 add_global_export(
383 &format!("{}:memory_base", metadata.name),
384 memory_offset,
385 false,
386 );
387 add_global_export(
388 &format!("{}:table_base", metadata.name),
389 table_offset,
390 false,
391 );
392
393 memory_offset += metadata.mem_info.memory_size;
394 table_offset += metadata.mem_info.table_size;
395
396 for import in &metadata.memory_address_imports {
397 add_global_export(&format!("{}:{import}", metadata.name), 0, true);
400 }
401 }
402
403 {
404 let offsets = env_exports
405 .iter()
406 .filter_map(|export| match export {
407 EnvExport::Func { name, exporter, .. } => Some((name, exporter)),
408 EnvExport::Tag { .. } => None,
409 })
410 .enumerate()
411 .map(|(offset, (name, exporter))| {
412 (
413 *name,
414 (
415 table_offset + u32::try_from(offset).unwrap(),
416 metadata[*exporter].name == STUB_LIBRARY_NAME,
417 ),
418 )
419 })
420 .collect_unique::<HashMap<_, _>>();
421
422 for metadata in metadata {
423 for import in &metadata.table_address_imports {
424 let &(offset, is_stub) = offsets.get(import).unwrap();
425 if is_stub
426 && metadata
427 .env_imports
428 .iter()
429 .any(|e| e.0 == *import && e.1.1.contains(SymbolFlags::BINDING_WEAK))
430 {
431 add_global_export(&format!("{}:{import}", metadata.name), 0, true);
432 } else {
433 add_global_export(&format!("{}:{import}", metadata.name), offset, true);
434 }
435 }
436 }
437 }
438
439 memory_offset = align(memory_offset, HEAP_ALIGNMENT_BYTES);
440 add_global_export("__heap_base", memory_offset, true);
441
442 let heap_end = align(memory_offset, PAGE_SIZE_BYTES);
443 add_global_export("__heap_end", heap_end, true);
444 heap_end / PAGE_SIZE_BYTES
445 };
446
447 let indirection_table_base = table_offset;
448
449 let mut functions = FunctionSection::new();
450 let mut code = CodeSection::new();
451 for export in env_exports {
452 let (name, ty) = match export {
453 EnvExport::Func { name, ty, .. } => (name, ty),
454 _ => continue,
455 };
456 let index = get_and_increment(&mut function_count);
457 types.ty().function(
458 ty.parameters.iter().copied().map(ValType::from),
459 ty.results.iter().copied().map(ValType::from),
460 );
461 functions.function(u32::try_from(index).unwrap());
462 let mut function = Function::new([]);
463 for local in 0..ty.parameters.len() {
464 function.instruction(&Ins::LocalGet(u32::try_from(local).unwrap()));
465 }
466 function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap()));
467 function.instruction(&Ins::CallIndirect {
468 type_index: u32::try_from(index).unwrap(),
469 table_index: 0,
470 });
471 function.instruction(&Ins::End);
472 code.function(&function);
473 exports.export(name, ExportKind::Func, index);
474
475 table_offset += 1;
476 }
477
478 for (import, offset) in import_map {
479 exports.export(
480 &format!("{}:{}", import.module, import.name),
481 ExportKind::from(&import.ty),
482 offset,
483 );
484 }
485 if let Some(index) = wasi_start {
486 exports.export("_start", ExportKind::Func, index);
487 }
488
489 let tags = {
490 let mut tags = TagSection::new();
491 for export in env_exports.iter() {
492 let (name, ty) = match export {
493 EnvExport::Tag { name, ty } => (name, ty),
494 _ => continue,
495 };
496
497 let func_type_idx = types.len();
498 types.ty().function(
499 ty.parameters.iter().copied().map(ValType::from),
500 ty.results.iter().copied().map(ValType::from),
501 );
502 let tag_idx = tags.len();
503 tags.tag(TagType {
504 kind: TagKind::Exception,
505 func_type_idx,
506 });
507 exports.export(name, ExportKind::Tag, tag_idx);
508 }
509 tags
510 };
511
512 let mut module = Module::new();
513
514 module.section(&types);
515 module.section(&imports);
516 module.section(&functions);
517
518 {
519 let mut tables = TableSection::new();
520 tables.table(TableType {
521 element_type: RefType::FUNCREF,
522 minimum: table_offset.into(),
523 maximum: None,
524 table64: false,
525 shared: false,
526 });
527 exports.export("__indirect_function_table", ExportKind::Table, 0);
528 module.section(&tables);
529 }
530
531 {
532 let mut memories = MemorySection::new();
533 memories.memory(MemoryType {
534 minimum: u64::from(memory_size),
535 maximum: None,
536 memory64: false,
537 shared: false,
538 page_size_log2: None,
539 });
540 exports.export("memory", ExportKind::Memory, 0);
541 module.section(&memories);
542 }
543
544 if !tags.is_empty() {
545 module.section(&tags);
546 }
547 module.section(&globals);
548 module.section(&exports);
549 module.section(&code);
550 module.section(&RawCustomSection(
551 &crate::base_producers().raw_custom_section(),
552 ));
553
554 let module = module.finish();
555 wasmparser::validate(&module).unwrap();
556
557 (module, dl_openables, indirection_table_base)
558}
559
560fn make_init_module(
565 metadata: &[Metadata],
566 exporters: &IndexMap<&ExportKey, (&str, &Export)>,
567 env_exports: &[EnvExport<'_>],
568 dl_openables: DlOpenables,
569 indirection_table_base: u32,
570) -> Result<Vec<u8>> {
571 let mut module = Module::new();
572
573 let mut types = TypeSection::new();
575 types.ty().function([], []);
576 let thunk_ty = 0;
577 types.ty().function([ValType::I32], []);
578 let one_i32_param_ty = 1;
579 let mut type_offset = 2;
580
581 for metadata in metadata {
582 if metadata.dl_openable {
583 for export in &metadata.exports {
584 if let Type::Function(ty) = &export.key.ty {
585 types.ty().function(
586 ty.parameters.iter().copied().map(ValType::from),
587 ty.results.iter().copied().map(ValType::from),
588 );
589 }
590 }
591 }
592 }
593 for export in env_exports {
594 let ty = match export {
595 EnvExport::Func { ty, .. } => ty,
596 _ => continue,
597 };
598 types.ty().function(
599 ty.parameters.iter().copied().map(ValType::from),
600 ty.results.iter().copied().map(ValType::from),
601 );
602 }
603 module.section(&types);
604
605 let mut imports = ImportSection::new();
606 imports.import(
607 "env",
608 "memory",
609 MemoryType {
610 minimum: 0,
611 maximum: None,
612 memory64: false,
613 shared: false,
614 page_size_log2: None,
615 },
616 );
617 imports.import(
618 "env",
619 "__indirect_function_table",
620 TableType {
621 element_type: RefType::FUNCREF,
622 minimum: 0,
623 maximum: None,
624 table64: false,
625 shared: false,
626 },
627 );
628
629 let mut global_count = 0;
630 let mut global_map = HashMap::new();
631 let mut add_global_import = |imports: &mut ImportSection, module: &str, name: &str, mutable| {
632 *global_map
633 .entry((module.to_owned(), name.to_owned()))
634 .or_insert_with(|| {
635 imports.import(
636 module,
637 name,
638 wasm_encoder::GlobalType {
639 val_type: ValType::I32,
640 mutable,
641 shared: false,
642 },
643 );
644 get_and_increment(&mut global_count)
645 })
646 };
647
648 let mut function_count = 0;
649 let mut function_map = HashMap::new();
650 let mut add_function_import = |imports: &mut ImportSection, module: &str, name: &str, ty| {
651 *function_map
652 .entry((module.to_owned(), name.to_owned()))
653 .or_insert_with(|| {
654 imports.import(module, name, EntityType::Function(ty));
655 get_and_increment(&mut function_count)
656 })
657 };
658
659 let mut memory_address_inits = Vec::new();
660 let mut reloc_calls = Vec::new();
661 let mut ctor_calls = Vec::new();
662 let mut names = HashMap::new();
663
664 for (exporter, export, address) in dl_openables.global_addresses.iter() {
665 memory_address_inits.push(Ins::I32Const(i32::try_from(*address).unwrap()));
666 memory_address_inits.push(Ins::GlobalGet(add_global_import(
667 &mut imports,
668 "env",
669 &format!("{exporter}:memory_base"),
670 false,
671 )));
672 memory_address_inits.push(Ins::GlobalGet(add_global_import(
673 &mut imports,
674 exporter,
675 export,
676 false,
677 )));
678 memory_address_inits.push(Ins::I32Add);
679 memory_address_inits.push(Ins::I32Store(MemArg {
680 offset: 0,
681 align: 2,
682 memory_index: 0,
683 }));
684 }
685
686 for (index, metadata) in metadata.iter().enumerate() {
687 names.insert_unique(index, metadata.name);
688
689 if metadata.has_data_relocs {
690 reloc_calls.push(Ins::Call(add_function_import(
691 &mut imports,
692 metadata.name,
693 "__wasm_apply_data_relocs",
694 thunk_ty,
695 )));
696 }
697
698 if metadata.has_ctors && metadata.has_initialize {
699 bail!(
700 "library {} exports both `__wasm_call_ctors` and `_initialize`; \
701 expected at most one of the two",
702 metadata.name
703 );
704 }
705
706 if metadata.has_ctors {
707 ctor_calls.push(Ins::Call(add_function_import(
708 &mut imports,
709 metadata.name,
710 "__wasm_call_ctors",
711 thunk_ty,
712 )));
713 }
714
715 if metadata.has_initialize {
716 ctor_calls.push(Ins::Call(add_function_import(
717 &mut imports,
718 metadata.name,
719 "_initialize",
720 thunk_ty,
721 )));
722 }
723
724 if metadata.has_set_libraries {
725 ctor_calls.push(Ins::I32Const(
726 i32::try_from(dl_openables.libraries_address).unwrap(),
727 ));
728 ctor_calls.push(Ins::Call(add_function_import(
729 &mut imports,
730 metadata.name,
731 "__wasm_set_libraries",
732 one_i32_param_ty,
733 )));
734 }
735
736 for import in &metadata.memory_address_imports {
737 let (exporter, _) = find_offset_exporter(import, exporters)?;
738
739 memory_address_inits.push(Ins::GlobalGet(add_global_import(
740 &mut imports,
741 "env",
742 &format!("{exporter}:memory_base"),
743 false,
744 )));
745 memory_address_inits.push(Ins::GlobalGet(add_global_import(
746 &mut imports,
747 exporter,
748 import,
749 false,
750 )));
751 memory_address_inits.push(Ins::I32Add);
752 memory_address_inits.push(Ins::GlobalSet(add_global_import(
753 &mut imports,
754 "env",
755 &format!("{}:{import}", metadata.name),
756 true,
757 )));
758 }
759 }
760
761 let mut dl_openable_functions = Vec::new();
762 for metadata in metadata {
763 if metadata.dl_openable {
764 for export in &metadata.exports {
765 if let Type::Function(_) = &export.key.ty {
766 dl_openable_functions.push(add_function_import(
767 &mut imports,
768 metadata.name,
769 export.key.name,
770 get_and_increment(&mut type_offset),
771 ));
772 }
773 }
774 }
775 }
776
777 let indirections = env_exports
778 .iter()
779 .filter_map(|export| match export {
780 EnvExport::Func { name, exporter, .. } => Some((name, exporter)),
781 _ => None,
782 })
783 .map(|(name, index)| {
784 add_function_import(
785 &mut imports,
786 names[index],
787 name,
788 get_and_increment(&mut type_offset),
789 )
790 })
791 .collect::<Vec<_>>();
792
793 module.section(&imports);
794
795 {
796 let mut functions = FunctionSection::new();
797 functions.function(thunk_ty);
798 module.section(&functions);
799 }
800
801 module.section(&StartSection {
802 function_index: function_count,
803 });
804
805 {
806 let mut elements = ElementSection::new();
807 elements.active(
808 None,
809 &const_u32(dl_openables.table_base),
810 Elements::Functions(dl_openable_functions.into()),
811 );
812 elements.active(
813 None,
814 &const_u32(indirection_table_base),
815 Elements::Functions(indirections.into()),
816 );
817 module.section(&elements);
818 }
819
820 {
821 let mut code = CodeSection::new();
822 let mut function = Function::new([]);
823 for ins in memory_address_inits
824 .iter()
825 .chain(&reloc_calls)
826 .chain(&ctor_calls)
827 {
828 function.instruction(ins);
829 }
830 function.instruction(&Ins::End);
831 code.function(&function);
832 module.section(&code);
833 }
834
835 let mut data = DataSection::new();
836 data.active(0, &const_u32(dl_openables.memory_base), dl_openables.buffer);
837 module.section(&data);
838
839 module.section(&RawCustomSection(
840 &crate::base_producers().raw_custom_section(),
841 ));
842
843 let module = module.finish();
844 wasmparser::validate(&module)?;
845
846 Ok(module)
847}
848
849fn find_offset_exporter<'a>(
851 name: &str,
852 exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>,
853) -> Result<(&'a str, &'a Export<'a>)> {
854 let export = ExportKey {
855 name,
856 ty: Type::Global(GlobalType {
857 ty: ValueType::I32,
858 mutable: false,
859 shared: false,
860 }),
861 };
862
863 exporters
864 .get(&export)
865 .copied()
866 .ok_or_else(|| anyhow!("unable to find {export:?} in any library"))
867}
868
869fn find_function_exporter<'a>(
871 name: &str,
872 ty: &FunctionType,
873 exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>,
874) -> Result<(&'a str, &'a Export<'a>)> {
875 let export = ExportKey {
876 name,
877 ty: Type::Function(ty.clone()),
878 };
879
880 exporters
881 .get(&export)
882 .copied()
883 .ok_or_else(|| anyhow!("unable to find {export:?} in any library"))
884}
885
886fn resolve_exporters<'a>(
888 metadata: &'a [Metadata<'a>],
889) -> Result<IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>> {
890 let mut exporters = IndexMap::<_, Vec<_>>::new();
891 for metadata in metadata {
892 for export in &metadata.exports {
893 exporters
894 .entry(&export.key)
895 .or_default()
896 .push((metadata.name, export));
897 }
898 }
899 Ok(exporters)
900}
901
902fn resolve_symbols<'a>(
904 metadata: &'a [Metadata<'a>],
905 exporters: &'a IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>,
906) -> (
907 IndexMap<&'a ExportKey<'a>, (&'a str, &'a Export<'a>)>,
908 Vec<(&'a str, Export<'a>)>,
909 Vec<(&'a str, &'a ExportKey<'a>, &'a [(&'a str, &'a Export<'a>)])>,
910) {
911 let function_exporters = exporters
912 .iter()
913 .filter_map(|(export, exporters)| match &export.ty {
914 Type::Function(_) => Some((export.name, (export, exporters))),
915 Type::Global(_) | Type::Tag(_) => None,
916 })
917 .collect_unique::<IndexMap<_, _>>();
918
919 let mut resolved = IndexMap::new();
920 let mut missing = Vec::new();
921 let mut duplicates = Vec::new();
922
923 let mut triage = |metadata: &'a Metadata, export: Export<'a>| {
924 if let Some((key, value)) = exporters.get_key_value(&export.key) {
925 match value.as_slice() {
928 [] => unreachable!(),
929 [exporter] => {
930 resolved.insert(*key, *exporter);
931 }
932 [exporter, ..] => {
933 resolved.insert(*key, *exporter);
934 duplicates.push((metadata.name, *key, value.as_slice()));
935 }
936 }
937 } else {
938 missing.push((metadata.name, export));
939 }
940 };
941
942 for metadata in metadata {
943 for (name, (ty, flags)) in &metadata.env_imports {
944 triage(
945 metadata,
946 Export {
947 key: ExportKey {
948 name,
949 ty: Type::Function(ty.clone()),
950 },
951 flags: *flags,
952 },
953 );
954 }
955
956 for name in &metadata.memory_address_imports {
957 triage(
958 metadata,
959 Export {
960 key: ExportKey {
961 name,
962 ty: Type::Global(GlobalType {
963 ty: ValueType::I32,
964 mutable: false,
965 shared: false,
966 }),
967 },
968 flags: SymbolFlags::empty(),
969 },
970 );
971 }
972
973 for (name, ty) in &metadata.tag_imports {
974 triage(
975 metadata,
976 Export {
977 key: ExportKey {
978 name,
979 ty: Type::Tag(ty.clone()),
980 },
981 flags: SymbolFlags::empty(),
982 },
983 );
984 }
985 }
986
987 for metadata in metadata {
988 for name in &metadata.table_address_imports {
989 if let Some((key, value)) = function_exporters.get(name) {
990 match value.as_slice() {
993 [] => unreachable!(),
994 [exporter] => {
995 resolved.insert(key, *exporter);
996 }
997 [exporter, ..] => {
998 resolved.insert(key, *exporter);
999 duplicates.push((metadata.name, *key, value.as_slice()));
1000 }
1001 }
1002 } else if metadata.env_imports.iter().any(|(n, _)| n == name) {
1003 } else {
1006 missing.push((
1007 metadata.name,
1008 Export {
1009 key: ExportKey {
1010 name,
1011 ty: Type::Function(FunctionType {
1012 parameters: Vec::new(),
1013 results: Vec::new(),
1014 }),
1015 },
1016 flags: SymbolFlags::empty(),
1017 },
1018 ));
1019 }
1020 }
1021 }
1022
1023 (resolved, missing, duplicates)
1024}
1025
1026fn topo_add(
1029 sorted: &mut IndexSet<usize>,
1030 dependencies: &IndexMap<usize, IndexSet<usize>>,
1031 element: usize,
1032) {
1033 let empty = &IndexSet::new();
1034 let deps = dependencies.get(&element).unwrap_or(empty);
1035
1036 for &dep in deps {
1038 if !(sorted.contains(&dep) || dependencies.get(&dep).unwrap_or(empty).contains(&element)) {
1039 topo_add(sorted, dependencies, dep);
1040 }
1041 }
1042
1043 sorted.insert(element);
1045
1046 for &dep in deps {
1048 if !sorted.contains(&dep) && dependencies.get(&dep).unwrap_or(empty).contains(&element) {
1049 topo_add(sorted, dependencies, dep);
1050 }
1051 }
1052}
1053
1054fn topo_sort(count: usize, dependencies: &IndexMap<usize, IndexSet<usize>>) -> Result<Vec<usize>> {
1057 let mut sorted = IndexSet::new();
1058 for index in 0..count {
1059 topo_add(&mut sorted, &dependencies, index);
1060 }
1061
1062 Ok(sorted.into_iter().collect())
1063}
1064
1065fn find_dependencies(
1068 metadata: &[Metadata],
1069 exporters: &IndexMap<&ExportKey, (&str, &Export)>,
1070) -> Result<IndexMap<usize, IndexSet<usize>>> {
1071 let mut dependencies = IndexMap::<_, IndexSet<_>>::new();
1073 let mut indexes = HashMap::new();
1074 for (index, metadata) in metadata.iter().enumerate() {
1075 indexes.insert_unique(metadata.name, index);
1076 for &needed in &metadata.needed_libs {
1077 dependencies
1078 .entry(metadata.name)
1079 .or_default()
1080 .insert(needed);
1081 }
1082 for (import_name, (ty, _)) in &metadata.env_imports {
1083 dependencies
1084 .entry(metadata.name)
1085 .or_default()
1086 .insert(find_function_exporter(import_name, ty, exporters)?.0);
1087 }
1088 }
1089
1090 let mut dependencies = dependencies
1092 .into_iter()
1093 .map(|(k, v)| {
1094 (
1095 indexes[k],
1096 v.into_iter()
1097 .map(|v| indexes[v])
1098 .collect_unique::<IndexSet<_>>(),
1099 )
1100 })
1101 .collect_unique::<IndexMap<_, _>>();
1102
1103 let empty = &IndexSet::new();
1106
1107 loop {
1108 let mut new = IndexMap::<_, IndexSet<_>>::new();
1109 for (index, exporters) in &dependencies {
1110 for exporter in exporters {
1111 for exporter in dependencies.get(exporter).unwrap_or(empty) {
1112 if !exporters.contains(exporter) {
1113 new.entry(*index).or_default().insert(*exporter);
1114 }
1115 }
1116 }
1117 }
1118
1119 if new.is_empty() {
1120 break Ok(dependencies);
1121 } else {
1122 for (index, exporters) in new {
1123 dependencies.entry(index).or_default().extend(exporters);
1124 }
1125 }
1126 }
1127}
1128
1129struct EnvExports<'a> {
1130 exports: Vec<EnvExport<'a>>,
1131 reexport_cabi_realloc: bool,
1132}
1133
1134enum EnvExport<'a> {
1135 Func {
1136 name: &'a str,
1137 ty: &'a FunctionType,
1138 exporter: usize,
1139 },
1140 Tag {
1141 name: &'a str,
1142 ty: &'a FunctionType,
1143 },
1144}
1145
1146fn env_exports<'a>(
1156 metadata: &'a [Metadata<'a>],
1157 exporters: &'a IndexMap<&'a ExportKey, (&'a str, &Export)>,
1158 topo_sorted: &[usize],
1159) -> Result<EnvExports<'a>> {
1160 let function_exporters = exporters
1161 .iter()
1162 .filter_map(|(export, exporter)| {
1163 if let Type::Function(ty) = &export.ty {
1164 Some((export.name, (ty, *exporter)))
1165 } else {
1166 None
1167 }
1168 })
1169 .collect_unique::<HashMap<_, _>>();
1170
1171 let indexes = metadata
1172 .iter()
1173 .enumerate()
1174 .map(|(index, metadata)| (metadata.name, index))
1175 .collect_unique::<HashMap<_, _>>();
1176
1177 let mut result = Vec::new();
1178 let mut exported = HashSet::new();
1179 let mut seen = HashSet::new();
1180
1181 for &index in topo_sorted {
1182 let metadata = &metadata[index];
1183
1184 for name in &metadata.table_address_imports {
1185 if !exported.contains(name) {
1186 let (ty, (exporter, _)) = function_exporters
1187 .get(name)
1188 .ok_or_else(|| anyhow!("unable to find {name:?} in any library"))?;
1189
1190 result.push(EnvExport::Func {
1191 name: *name,
1192 ty: *ty,
1193 exporter: indexes[exporter],
1194 });
1195 exported.insert(*name);
1196 }
1197 }
1198
1199 for (import_name, (ty, _)) in &metadata.env_imports {
1200 if !exported.contains(import_name) {
1201 let exporter = indexes[find_function_exporter(import_name, ty, exporters)
1202 .unwrap()
1203 .0];
1204 if !seen.contains(&exporter) {
1205 result.push(EnvExport::Func {
1206 name: *import_name,
1207 ty,
1208 exporter,
1209 });
1210 exported.insert(*import_name);
1211 }
1212 }
1213 }
1214
1215 for (import_name, ty) in &metadata.tag_imports {
1216 if exported.insert(import_name) {
1217 result.push(EnvExport::Tag {
1218 name: *import_name,
1219 ty,
1220 });
1221 }
1222 }
1223
1224 seen.insert(index);
1225 }
1226
1227 let reexport_cabi_realloc = exported.contains("cabi_realloc");
1228
1229 Ok(EnvExports {
1230 exports: result,
1231 reexport_cabi_realloc,
1232 })
1233}
1234
1235fn make_stubs_module(missing: &[(&str, Export)]) -> Vec<u8> {
1237 let mut types = TypeSection::new();
1238 let mut exports = ExportSection::new();
1239 let mut functions = FunctionSection::new();
1240 let mut code = CodeSection::new();
1241 for (offset, (_, export)) in missing.iter().enumerate() {
1242 let offset = u32::try_from(offset).unwrap();
1243
1244 let Export {
1245 key:
1246 ExportKey {
1247 name,
1248 ty: Type::Function(ty),
1249 },
1250 ..
1251 } = export
1252 else {
1253 unreachable!();
1254 };
1255
1256 types.ty().function(
1257 ty.parameters.iter().copied().map(ValType::from),
1258 ty.results.iter().copied().map(ValType::from),
1259 );
1260 functions.function(offset);
1261 let mut function = Function::new([]);
1262 function.instruction(&Ins::Unreachable);
1263 function.instruction(&Ins::End);
1264 code.function(&function);
1265 exports.export(name, ExportKind::Func, offset);
1266 }
1267
1268 let mut module = Module::new();
1269
1270 module.section(&types);
1271 module.section(&functions);
1272 module.section(&exports);
1273 module.section(&code);
1274 module.section(&RawCustomSection(
1275 &crate::base_producers().raw_custom_section(),
1276 ));
1277
1278 let module = module.finish();
1279 wasmparser::validate(&module).unwrap();
1280
1281 module
1282}
1283
1284fn find_reachable<'a>(
1287 metadata: &'a [Metadata<'a>],
1288 dependencies: &IndexMap<usize, IndexSet<usize>>,
1289) -> IndexSet<&'a str> {
1290 let reachable = metadata
1291 .iter()
1292 .enumerate()
1293 .filter_map(|(index, metadata)| {
1294 if metadata.has_component_exports || metadata.dl_openable || metadata.has_wasi_start {
1295 Some(index)
1296 } else {
1297 None
1298 }
1299 })
1300 .collect_unique::<IndexSet<_>>();
1301
1302 let empty = &IndexSet::new();
1303
1304 reachable
1305 .iter()
1306 .chain(
1307 reachable
1308 .iter()
1309 .flat_map(|index| dependencies.get(index).unwrap_or(empty)),
1310 )
1311 .map(|&index| metadata[index].name)
1312 .collect()
1313}
1314
1315#[derive(Default)]
1317pub struct Linker {
1318 libraries: Vec<(String, Vec<u8>, bool)>,
1322
1323 adapters: Vec<(String, Vec<u8>)>,
1325
1326 validate: bool,
1328
1329 stub_missing_functions: bool,
1331
1332 use_built_in_libdl: bool,
1334
1335 debug_names: bool,
1337
1338 stack_size: Option<u32>,
1342
1343 merge_imports_based_on_semver: Option<bool>,
1347}
1348
1349impl Linker {
1350 pub fn library(mut self, name: &str, module: &[u8], dl_openable: bool) -> Result<Self> {
1355 self.libraries
1356 .push((name.to_owned(), module.to_vec(), dl_openable));
1357
1358 Ok(self)
1359 }
1360
1361 pub fn adapter(mut self, name: &str, module: &[u8]) -> Result<Self> {
1365 self.adapters.push((name.to_owned(), module.to_vec()));
1366
1367 Ok(self)
1368 }
1369
1370 pub fn validate(mut self, validate: bool) -> Self {
1372 self.validate = validate;
1373 self
1374 }
1375
1376 pub fn stack_size(mut self, stack_size: u32) -> Self {
1378 self.stack_size = Some(stack_size);
1379 self
1380 }
1381
1382 pub fn stub_missing_functions(mut self, stub_missing_functions: bool) -> Self {
1384 self.stub_missing_functions = stub_missing_functions;
1385 self
1386 }
1387
1388 pub fn use_built_in_libdl(mut self, use_built_in_libdl: bool) -> Self {
1390 self.use_built_in_libdl = use_built_in_libdl;
1391 self
1392 }
1393
1394 pub fn debug_names(mut self, enable: bool) -> Self {
1396 self.debug_names = enable;
1397 self
1398 }
1399
1400 pub fn merge_imports_based_on_semver(mut self, merge: bool) -> Self {
1406 self.merge_imports_based_on_semver = Some(merge);
1407 self
1408 }
1409
1410 pub fn encode(mut self) -> Result<Vec<u8>> {
1412 if self.use_built_in_libdl {
1413 self.use_built_in_libdl = false;
1414 self = self.library("libdl.so", include_bytes!("../libdl.so"), false)?;
1415 }
1416
1417 let adapter_names = self
1418 .adapters
1419 .iter()
1420 .map(|(name, _)| name.as_str())
1421 .collect_unique::<HashSet<_>>();
1422
1423 if adapter_names.len() != self.adapters.len() {
1424 bail!("duplicate adapter name");
1425 }
1426
1427 let metadata = self
1428 .libraries
1429 .iter()
1430 .map(|(name, module, dl_openable)| {
1431 Metadata::try_new(name, *dl_openable, module, &adapter_names)
1432 .with_context(|| format!("failed to extract linking metadata from {name}"))
1433 })
1434 .collect::<Result<Vec<_>>>()?;
1435
1436 {
1437 let names = self
1438 .libraries
1439 .iter()
1440 .map(|(name, ..)| name.as_str())
1441 .collect_unique::<HashSet<_>>();
1442
1443 let missing = metadata
1444 .iter()
1445 .filter_map(|metadata| {
1446 let missing = metadata
1447 .needed_libs
1448 .iter()
1449 .copied()
1450 .filter(|name| !names.contains(*name))
1451 .collect::<Vec<_>>();
1452
1453 if missing.is_empty() {
1454 None
1455 } else {
1456 Some((metadata.name, missing))
1457 }
1458 })
1459 .collect::<Vec<_>>();
1460
1461 if !missing.is_empty() {
1462 bail!(
1463 "missing libraries:\n{}",
1464 missing
1465 .iter()
1466 .map(|(needed_by, missing)| format!(
1467 "\t{needed_by} needs {}",
1468 missing.join(", ")
1469 ))
1470 .collect::<Vec<_>>()
1471 .join("\n")
1472 );
1473 }
1474 }
1475
1476 let exporters = resolve_exporters(&metadata)?;
1477
1478 let cabi_realloc_exporter = exporters
1479 .get(&ExportKey {
1480 name: "cabi_realloc",
1481 ty: Type::Function(FunctionType {
1482 parameters: vec![ValueType::I32; 4],
1483 results: vec![ValueType::I32],
1484 }),
1485 })
1486 .map(|exporters| exporters.first().unwrap().0);
1487
1488 let (exporters, missing, _) = resolve_symbols(&metadata, &exporters);
1489
1490 if !missing.is_empty() {
1491 if missing
1492 .iter()
1493 .all(|(_, export)| matches!(&export.key.ty, Type::Function(_)))
1494 && (self.stub_missing_functions
1495 || missing
1496 .iter()
1497 .all(|(_, export)| export.flags.contains(SymbolFlags::BINDING_WEAK)))
1498 {
1499 self.stub_missing_functions = false;
1500 self.libraries
1501 .push((STUB_LIBRARY_NAME.into(), make_stubs_module(&missing), false));
1502 return self.encode();
1503 } else {
1504 bail!(
1505 "unresolved symbol(s):\n{}",
1506 missing
1507 .iter()
1508 .filter(|(_, export)| !export.flags.contains(SymbolFlags::BINDING_WEAK))
1509 .map(|(importer, export)| { format!("\t{importer} needs {}", export.key) })
1510 .collect::<Vec<_>>()
1511 .join("\n")
1512 );
1513 }
1514 }
1515
1516 let dependencies = find_dependencies(&metadata, &exporters)?;
1517
1518 {
1519 let reachable = find_reachable(&metadata, &dependencies);
1520 let unreachable = self
1521 .libraries
1522 .iter()
1523 .filter_map(|(name, ..)| (!reachable.contains(name.as_str())).then(|| name.clone()))
1524 .collect_unique::<HashSet<_>>();
1525
1526 if !unreachable.is_empty() {
1527 self.libraries
1528 .retain(|(name, ..)| !unreachable.contains(name));
1529 return self.encode();
1530 }
1531 }
1532
1533 let topo_sorted = topo_sort(metadata.len(), &dependencies)?;
1534
1535 let EnvExports {
1536 exports: env_exports,
1537 reexport_cabi_realloc,
1538 } = env_exports(&metadata, &exporters, &topo_sorted)?;
1539
1540 let (env_module, dl_openables, table_base) = make_env_module(
1541 &metadata,
1542 &env_exports,
1543 if reexport_cabi_realloc {
1544 None
1547 } else {
1548 cabi_realloc_exporter
1549 },
1550 self.stack_size.unwrap_or(DEFAULT_STACK_SIZE_BYTES),
1551 );
1552
1553 let mut encoder = ComponentEncoder::default()
1554 .validate(self.validate)
1555 .debug_names(self.debug_names);
1556 if let Some(merge) = self.merge_imports_based_on_semver {
1557 encoder = encoder.merge_imports_based_on_semver(merge);
1558 };
1559 encoder = encoder.module(&env_module)?;
1560
1561 for (name, module) in &self.adapters {
1562 encoder = encoder.adapter(name, module)?;
1563 }
1564
1565 let default_env_items = [
1566 Item {
1567 alias: "memory".into(),
1568 kind: ExportKind::Memory,
1569 which: MainOrAdapter::Main,
1570 name: "memory".into(),
1571 },
1572 Item {
1573 alias: "__indirect_function_table".into(),
1574 kind: ExportKind::Table,
1575 which: MainOrAdapter::Main,
1576 name: "__indirect_function_table".into(),
1577 },
1578 Item {
1579 alias: "__stack_pointer".into(),
1580 kind: ExportKind::Global,
1581 which: MainOrAdapter::Main,
1582 name: "__stack_pointer".into(),
1583 },
1584 ];
1585
1586 let mut seen = HashSet::new();
1587 for index in topo_sorted {
1588 let (name, module, _) = &self.libraries[index];
1589 let metadata = &metadata[index];
1590
1591 let env_items = default_env_items
1592 .iter()
1593 .cloned()
1594 .chain([
1595 Item {
1596 alias: "__memory_base".into(),
1597 kind: ExportKind::Global,
1598 which: MainOrAdapter::Main,
1599 name: format!("{name}:memory_base"),
1600 },
1601 Item {
1602 alias: "__table_base".into(),
1603 kind: ExportKind::Global,
1604 which: MainOrAdapter::Main,
1605 name: format!("{name}:table_base"),
1606 },
1607 ])
1608 .chain(metadata.env_imports.iter().map(|(name, (ty, _))| {
1609 let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap();
1610
1611 Item {
1612 alias: (*name).into(),
1613 kind: ExportKind::Func,
1614 which: if seen.contains(exporter) {
1615 MainOrAdapter::Adapter(exporter.to_owned())
1616 } else {
1617 MainOrAdapter::Main
1618 },
1619 name: (*name).into(),
1620 }
1621 }))
1622 .chain(metadata.tag_imports.iter().map(|(name, _ty)| Item {
1623 alias: (*name).into(),
1624 kind: ExportKind::Tag,
1625 which: MainOrAdapter::Main,
1626 name: (*name).into(),
1627 }))
1628 .chain(if metadata.is_asyncified {
1629 vec![
1630 Item {
1631 alias: "__asyncify_state".into(),
1632 kind: ExportKind::Global,
1633 which: MainOrAdapter::Main,
1634 name: "__asyncify_state".into(),
1635 },
1636 Item {
1637 alias: "__asyncify_data".into(),
1638 kind: ExportKind::Global,
1639 which: MainOrAdapter::Main,
1640 name: "__asyncify_data".into(),
1641 },
1642 ]
1643 } else {
1644 vec![]
1645 })
1646 .collect();
1647
1648 let global_item = |address_name: &str| Item {
1649 alias: address_name.into(),
1650 kind: ExportKind::Global,
1651 which: MainOrAdapter::Main,
1652 name: format!("{name}:{address_name}"),
1653 };
1654
1655 let mem_items = metadata
1656 .memory_address_imports
1657 .iter()
1658 .copied()
1659 .map(global_item)
1660 .chain(
1661 ["__heap_base", "__heap_end", "__stack_high", "__stack_low"]
1662 .into_iter()
1663 .map(|name| Item {
1664 alias: name.into(),
1665 kind: ExportKind::Global,
1666 which: MainOrAdapter::Main,
1667 name: name.into(),
1668 }),
1669 )
1670 .collect();
1671
1672 let func_items = metadata
1673 .table_address_imports
1674 .iter()
1675 .copied()
1676 .map(global_item)
1677 .collect();
1678
1679 let mut import_items = BTreeMap::<_, Vec<_>>::new();
1680 for import in &metadata.imports {
1681 import_items.entry(import.module).or_default().push(Item {
1682 alias: import.name.into(),
1683 kind: ExportKind::from(&import.ty),
1684 which: MainOrAdapter::Main,
1685 name: format!("{}:{}", import.module, import.name),
1686 });
1687 }
1688
1689 encoder = encoder.library(
1690 name,
1691 module,
1692 LibraryInfo {
1693 instantiate_after_shims: false,
1694 arguments: [
1695 ("GOT.mem".into(), Instance::Items(mem_items)),
1696 ("GOT.func".into(), Instance::Items(func_items)),
1697 ("env".into(), Instance::Items(env_items)),
1698 ]
1699 .into_iter()
1700 .chain(
1701 import_items
1702 .into_iter()
1703 .map(|(k, v)| (k.into(), Instance::Items(v))),
1704 )
1705 .collect(),
1706 },
1707 )?;
1708
1709 seen.insert(name.as_str());
1710 }
1711
1712 encoder
1713 .library(
1714 "__init",
1715 &make_init_module(
1716 &metadata,
1717 &exporters,
1718 &env_exports,
1719 dl_openables,
1720 table_base,
1721 )?,
1722 LibraryInfo {
1723 instantiate_after_shims: true,
1724 arguments: iter::once((
1725 "env".into(),
1726 Instance::MainOrAdapter(MainOrAdapter::Main),
1727 ))
1728 .chain(self.libraries.iter().map(|(name, ..)| {
1729 (
1730 name.clone(),
1731 Instance::MainOrAdapter(MainOrAdapter::Adapter(name.clone())),
1732 )
1733 }))
1734 .collect(),
1735 },
1736 )?
1737 .encode()
1738 }
1739}