Skip to main content

wit_component/
linking.rs

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 synthesizes 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
25use {
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;
48// This matches the default stack size LLVM produces:
49pub 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
58/// Represents a `dlopen`/`dlsym` lookup table enabling runtime symbol resolution
59///
60/// The top level of this table is a sorted list of library names and offsets, each pointing to a sorted list of
61/// symbol names and offsets.  See ../dl/src/lib.rs for how this is used at runtime.
62struct DlOpenables<'a> {
63    /// Offset into the main module's table where function references will be stored
64    table_base: u32,
65
66    /// Offset into the main module's memory where the lookup table will be stored
67    memory_base: u32,
68
69    /// The lookup table itself
70    buffer: Vec<u8>,
71
72    /// Linear memory addresses where global variable addresses will live
73    ///
74    /// The init module will fill in the correct values at instantiation time.
75    global_addresses: Vec<(&'a str, &'a str, u32)>,
76
77    /// Number of function references to be stored in the main module's table
78    function_count: u32,
79
80    /// Linear memory address where the root of the lookup table will reside
81    ///
82    /// This can be different from `memory_base` depending on how the tree of libraries and symbols is laid out in
83    /// memory.
84    libraries_address: u32,
85}
86
87impl<'a> DlOpenables<'a> {
88    /// Construct a lookup table containing all "dlopen-able" libraries and their symbols using the specified table
89    /// and memory offsets.
90    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
202/// Helper trait for determining the size of a set or map
203trait 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
231/// Extension trait for collecting into a set or map and asserting that there were no duplicate entries in the
232/// source iterator.
233trait 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
248/// Extension trait for inserting into a map and asserting that an entry did not already exist for the key
249trait 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
270/// Synthesize the "main" module for the component, responsible for exporting functions which break cyclic
271/// dependencies, as well as hosting the memory and function table.
272fn 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    // TODO: deduplicate types
279    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    // Table offset 0 is reserved for the null function pointer.
332    // This convention follows wasm-ld's table layout:
333    // https://github.com/llvm/llvm-project/blob/913622d012f72edb5ac3a501cef8639d0ebe471b/lld/wasm/Driver.cpp#L581-L584
334    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        // Binaryen's Asyncify transform for shared everything linking requires these globals
367        // to be provided from env module
368        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        // The libc.so in WASI-SDK 28+ requires these:
375        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                // Note that we initialize this to zero and let the init module compute the real value at
398                // instantiation time.
399                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
560/// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool
561/// convention and calling any static constructors and/or link-time fixup functions.
562///
563/// This module also contains the data segment for the `dlopen`/`dlsym` lookup table.
564fn 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    // TODO: deduplicate types
574    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
849/// Find the library which exports the specified function or global address.
850fn 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
869/// Find the library which exports the specified function.
870fn 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
886/// Analyze the specified library metadata, producing a symbol-to-library-name map of exports.
887fn 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
902/// Match up all imported symbols to their corresponding exports, reporting any missing or duplicate symbols.
903fn 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            // Note that we do not use `insert_unique` here since multiple libraries may import the same
926            // symbol, in which case we may redundantly insert the same value.
927            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                // Note that we do not use `insert_unique` here since multiple libraries may import the same
991                // symbol, in which case we may redundantly insert the same value.
992                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                // GOT entry for a function which is imported from the env module, but not exported by any library,
1004                // already handled above.
1005            } 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
1026/// Recursively add a library (represented by its offset) and its dependency to the specified set, maintaining
1027/// topological order (modulo cycles).
1028fn 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    // First, add any dependencies which do not depend on `element`
1037    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    // Next, add the element
1044    sorted.insert(element);
1045
1046    // Finally, add any dependencies which depend on `element`
1047    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
1054/// Topologically sort a set of libraries (represented by their offsets) according to their dependencies, modulo
1055/// cycles.
1056fn 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
1065/// Analyze the specified library metadata, producing a map of transitive dependencies, where each library is
1066/// represented by its offset in the original metadata slice.
1067fn find_dependencies(
1068    metadata: &[Metadata],
1069    exporters: &IndexMap<&ExportKey, (&str, &Export)>,
1070) -> Result<IndexMap<usize, IndexSet<usize>>> {
1071    // First, generate a map of direct dependencies (i.e. depender to dependees)
1072    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    // Next, convert the map from names to offsets
1091    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    // Finally, add all transitive dependencies to the map in a fixpoint loop, exiting when no new dependencies are
1104    // discovered.
1105    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
1146/// Analyze the specified metadata and generate what needs to be exported from
1147/// the main (aka "env") module.
1148///
1149/// This includes a list of functions which should be re-exported as a
1150/// `call.indirect`-based function including the offset of the library
1151/// containing the original export.
1152///
1153/// Additionally this includes any tags necessary that are shared amongst
1154/// modules.
1155fn 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
1235/// Synthesize a module which contains trapping stub exports for the specified functions.
1236fn 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
1284/// Determine which of the specified libraries are transitively reachable at runtime, i.e. reachable from a
1285/// component export or via `dlopen`.
1286fn 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/// Builder type for composing dynamic library modules into a component
1316#[derive(Default)]
1317pub struct Linker {
1318    /// The `(name, module, dl_openable)` triple representing the libraries to be composed
1319    ///
1320    /// The order of this list determines priority in cases where more than one library exports the same symbol.
1321    libraries: Vec<(String, Vec<u8>, bool)>,
1322
1323    /// The set of adapters to use when generating the component
1324    adapters: Vec<(String, Vec<u8>)>,
1325
1326    /// Whether to validate the resulting component prior to returning it
1327    validate: bool,
1328
1329    /// Whether to generate trapping stubs for any unresolved imports
1330    stub_missing_functions: bool,
1331
1332    /// Whether to use a built-in implementation of `dlopen`/`dlsym`.
1333    use_built_in_libdl: bool,
1334
1335    /// Whether to generate debug `name` sections.
1336    debug_names: bool,
1337
1338    /// Size of stack (in bytes) to allocate in the synthesized main module
1339    ///
1340    /// If `None`, use `DEFAULT_STACK_SIZE_BYTES`.
1341    stack_size: Option<u32>,
1342
1343    /// This affects how when to WIT worlds are merged together, for example
1344    /// from two different libraries, whether their imports are unified when the
1345    /// semver version ranges for interface allow it.
1346    merge_imports_based_on_semver: Option<bool>,
1347}
1348
1349impl Linker {
1350    /// Add a dynamic library module to this linker.
1351    ///
1352    /// If `dl_openable` is true, all of the libraries exports will be added to the `dlopen`/`dlsym` lookup table
1353    /// for runtime resolution.
1354    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    /// Add an adapter to this linker.
1362    ///
1363    /// See [crate::encoding::ComponentEncoder::adapter] for details.
1364    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    /// Specify whether to validate the resulting component prior to returning it
1371    pub fn validate(mut self, validate: bool) -> Self {
1372        self.validate = validate;
1373        self
1374    }
1375
1376    /// Specify size of stack to allocate in the synthesized main module
1377    pub fn stack_size(mut self, stack_size: u32) -> Self {
1378        self.stack_size = Some(stack_size);
1379        self
1380    }
1381
1382    /// Specify whether to generate trapping stubs for any unresolved imports
1383    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    /// Specify whether to use a built-in implementation of `dlopen`/`dlsym`.
1389    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    /// Whether or not to generate debug name sections.
1395    pub fn debug_names(mut self, enable: bool) -> Self {
1396        self.debug_names = enable;
1397        self
1398    }
1399
1400    /// This affects how when to WIT worlds are merged together, for example
1401    /// from two different libraries, whether their imports are unified when the
1402    /// semver version ranges for interface allow it.
1403    ///
1404    /// This is enabled by default.
1405    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    /// Encode the component and return the bytes
1411    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                // If "env" module already reexports "cabi_realloc", we don't need to
1545                // reexport it again.
1546                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}