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