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::fixup::{AddressDest, FixupModule, ImportedInstance, StartAction},
27    crate::encoding::{ComponentEncoder, Instance, Item, LibraryInfo, MainOrAdapter},
28    anyhow::{Context, Result, anyhow, bail},
29    indexmap::{IndexMap, IndexSet, map::Entry},
30    metadata::{Export, ExportKey, FunctionType, GlobalType, Metadata, Type, ValueType},
31    std::{
32        borrow::Cow,
33        collections::{BTreeMap, HashMap, HashSet},
34        fmt::Debug,
35        hash::Hash,
36    },
37    wasm_encoder::{
38        CodeSection, ConstExpr, Elements, EntityType, ExportKind, ExportSection, Function,
39        FunctionSection, GlobalSection, ImportSection, MemArg, MemorySection, MemoryType, Module,
40        RawCustomSection, RefType, TableSection, TableType, TypeSection, ValType,
41    },
42    wasmparser::SymbolFlags,
43};
44
45pub(crate) mod 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";
52const CABI_REALLOC: &str = "cabi_realloc";
53
54/// Symbols to re-export from the `env` module regardless of whether any
55/// libraries import them, since
56/// `EncodingState::create_export_task_initialization_wrappers` needs to be able
57/// to call them.
58static ENV_REEXPORTS: &[(&str, &[ValueType], &[ValueType])] =
59    &[(metadata::TASK_HOOK, &[ValueType::I32], &[])];
60
61enum Address<'a> {
62    Function(u32),
63    Global(&'a str),
64}
65
66/// Represents a `dlopen`/`dlsym` lookup table enabling runtime symbol resolution
67///
68/// The top level of this table is a sorted list of library names and offsets, each pointing to a sorted list of
69/// symbol names and offsets.  See ../dl/src/lib.rs for how this is used at runtime.
70struct DlOpenables<'a> {
71    /// Offset into the main module's table where function references will be stored
72    table_base: u32,
73
74    /// Offset into the main module's memory where the lookup table will be stored
75    memory_base: u32,
76
77    /// The lookup table itself
78    buffer: Vec<u8>,
79
80    /// Linear memory addresses where global variable addresses will live
81    ///
82    /// The init module will fill in the correct values at instantiation time.
83    global_addresses: Vec<(&'a str, &'a str, u32)>,
84
85    /// Number of function references to be stored in the main module's table
86    function_count: u32,
87
88    /// Linear memory address where the root of the lookup table will reside
89    ///
90    /// This can be different from `memory_base` depending on how the tree of libraries and symbols is laid out in
91    /// memory.
92    libraries_address: u32,
93}
94
95impl<'a> DlOpenables<'a> {
96    /// Construct a lookup table containing all "dlopen-able" libraries and their symbols using the specified table
97    /// and memory offsets.
98    fn new(table_base: u32, memory_base: u32, metadata: &'a [Metadata<'a>]) -> Self {
99        let mut function_count = 0;
100        let mut buffer = Vec::new();
101        let mut global_addresses = Vec::new();
102        let mut libraries = metadata
103            .iter()
104            .filter(|metadata| metadata.dl_openable)
105            .map(|metadata| {
106                let name_address = memory_base + u32::try_from(buffer.len()).unwrap();
107                write_bytes_padded(&mut buffer, metadata.name.as_bytes());
108
109                let mut symbols = metadata
110                    .exports
111                    .iter()
112                    .filter_map(|export| {
113                        let name_address = memory_base + u32::try_from(buffer.len()).unwrap();
114                        write_bytes_padded(&mut buffer, export.key.name.as_bytes());
115
116                        let address = match &export.key.ty {
117                            Type::Function(_) => Address::Function(
118                                table_base + get_and_increment(&mut function_count),
119                            ),
120                            Type::Global(_) => Address::Global(export.key.name),
121                            Type::Tag(_) => return None,
122                        };
123
124                        Some((export.key.name, name_address, address))
125                    })
126                    .collect::<Vec<_>>();
127
128                symbols.sort_by_key(|(name, ..)| *name);
129
130                let start = buffer.len();
131                for (name, name_address, address) in symbols {
132                    write_u32(&mut buffer, u32::try_from(name.len()).unwrap());
133                    write_u32(&mut buffer, name_address);
134                    match address {
135                        Address::Function(address) => write_u32(&mut buffer, address),
136                        Address::Global(name) => {
137                            global_addresses.push((
138                                metadata.name,
139                                name,
140                                memory_base + u32::try_from(buffer.len()).unwrap(),
141                            ));
142
143                            write_u32(&mut buffer, 0);
144                        }
145                    }
146                }
147
148                (
149                    metadata.name,
150                    name_address,
151                    metadata.exports.len(),
152                    memory_base + u32::try_from(start).unwrap(),
153                )
154            })
155            .collect::<Vec<_>>();
156
157        libraries.sort_by_key(|(name, ..)| *name);
158
159        let start = buffer.len();
160        for (name, name_address, count, symbols) in &libraries {
161            write_u32(&mut buffer, u32::try_from(name.len()).unwrap());
162            write_u32(&mut buffer, *name_address);
163            write_u32(&mut buffer, u32::try_from(*count).unwrap());
164            write_u32(&mut buffer, *symbols);
165        }
166
167        let libraries_address = memory_base + u32::try_from(buffer.len()).unwrap();
168        write_u32(&mut buffer, u32::try_from(libraries.len()).unwrap());
169        write_u32(&mut buffer, memory_base + u32::try_from(start).unwrap());
170
171        Self {
172            table_base,
173            memory_base,
174            buffer,
175            global_addresses,
176            function_count,
177            libraries_address,
178        }
179    }
180}
181
182/// The layout of the whole program's thread-local storage bookkeeping.
183///
184/// This generates a C structure that matches this layout:
185///
186/// ```c
187/// struct {
188///     size_t num_libraries;
189///     void **library_info;
190///     void **main_thread_tls_base;
191/// } __wasm_program_tls_info;
192/// ```
193///
194/// where the `main_thread_tls_base` array is placed first, then the
195/// `library_info` array, then this structure itself.
196#[derive(Default)]
197struct TlsLayout {
198    /// Address of the `main_thread_tls_base` array.
199    ///
200    /// This is left zero-initialized; no data segment covers it.
201    main_thread_tls_base: u32,
202
203    /// Address of the `library_info` array.
204    library_info: u32,
205
206    /// Address of the `__wasm_program_tls_info` struct itself.
207    program_info: u32,
208
209    /// For each library, the slot it uses in the array of TLS base pointers, or
210    /// `None` if it has no thread-local storage of its own.
211    slots: Vec<Option<u32>>,
212
213    /// Static contents of the `__wasm_program_tls_info`.
214    buffer: Vec<u8>,
215}
216
217impl TlsLayout {
218    /// Reserve linear memory and table space for the layout described above,
219    /// advancing `memory_offset` and `table_offset` past what's used.
220    ///
221    /// Nothing is reserved for a program which doesn't use thread-local storage
222    /// at all, and the `__wasm_program_tls_info` half is skipped unless some
223    /// library actually asks for it, which is only the case when the program
224    /// might spawn a thread.
225    fn new(metadata: &[Metadata], memory_offset: &mut u32) -> Self {
226        let needs_tls_base = metadata
227            .iter()
228            .any(|m| m.needs_get_tls_base || m.needs_set_tls_base);
229        let needs_program_info = metadata.iter().any(|m| m.needs_program_tls_info);
230        if !needs_tls_base && !needs_program_info {
231            return Self::default();
232        }
233
234        // Filter out libraries that don't have TLS.
235        let libraries = metadata
236            .iter()
237            .enumerate()
238            .filter(|(_, metadata)| metadata.has_library_tls_info)
239            .map(|(index, _)| index)
240            .collect::<Vec<_>>();
241
242        let mut slots = vec![None; metadata.len()];
243        for (slot, index) in libraries.iter().enumerate() {
244            slots[*index] = Some(u32::try_from(slot).unwrap());
245        }
246        let count = u32::try_from(libraries.len()).unwrap();
247
248        // Allocate space for `main_thread_tls_base`
249        *memory_offset = align(*memory_offset, 4);
250        let main_thread_tls_base = *memory_offset;
251        *memory_offset += count * 4;
252
253        let mut library_info = 0;
254        let mut program_info = 0;
255        let mut buffer = Vec::new();
256        if needs_program_info {
257            library_info = *memory_offset;
258            *memory_offset += count * 4;
259            program_info = *memory_offset;
260            *memory_offset += 12;
261
262            write_u32(&mut buffer, count);
263            write_u32(&mut buffer, library_info);
264            write_u32(&mut buffer, main_thread_tls_base);
265        }
266
267        Self {
268            main_thread_tls_base,
269            library_info,
270            program_info,
271            slots,
272            buffer,
273        }
274    }
275
276    /// The slot library `index` uses in the array of TLS base pointers.
277    fn slot(&self, index: usize) -> Option<u32> {
278        self.slots.get(index).copied().flatten()
279    }
280}
281
282fn write_u32(buffer: &mut Vec<u8>, value: u32) {
283    buffer.extend(value.to_le_bytes());
284}
285
286fn write_bytes_padded(buffer: &mut Vec<u8>, bytes: &[u8]) {
287    buffer.extend(bytes);
288
289    let len = u32::try_from(bytes.len()).unwrap();
290    for _ in len..align(len, 4) {
291        buffer.push(0);
292    }
293}
294
295fn align(a: u32, b: u32) -> u32 {
296    assert!(b.is_power_of_two());
297    (a + (b - 1)) & !(b - 1)
298}
299
300fn get_and_increment(n: &mut u32) -> u32 {
301    let v = *n;
302    *n += 1;
303    v
304}
305
306fn const_u32(a: u32) -> ConstExpr {
307    ConstExpr::i32_const(a as i32)
308}
309
310/// Helper trait for determining the size of a set or map
311trait Length {
312    fn len(&self) -> usize;
313}
314
315impl<T> Length for HashSet<T> {
316    fn len(&self) -> usize {
317        HashSet::len(self)
318    }
319}
320
321impl<K, V> Length for HashMap<K, V> {
322    fn len(&self) -> usize {
323        HashMap::len(self)
324    }
325}
326
327impl<T> Length for IndexSet<T> {
328    fn len(&self) -> usize {
329        IndexSet::len(self)
330    }
331}
332
333impl<K, V> Length for IndexMap<K, V> {
334    fn len(&self) -> usize {
335        IndexMap::len(self)
336    }
337}
338
339/// Extension trait for collecting into a set or map and asserting that there were no duplicate entries in the
340/// source iterator.
341trait CollectUnique: Iterator + Sized {
342    fn collect_unique<T: FromIterator<Self::Item> + Length>(self) -> T {
343        let tmp = self.collect::<Vec<_>>();
344        let len = tmp.len();
345        let result = tmp.into_iter().collect::<T>();
346        assert!(
347            result.len() == len,
348            "one or more duplicate items detected when collecting into set or map"
349        );
350        result
351    }
352}
353
354impl<T: Iterator> CollectUnique for T {}
355
356/// Extension trait for inserting into a map and asserting that an entry did not already exist for the key
357trait InsertUnique {
358    type Key;
359    type Value;
360
361    fn insert_unique(&mut self, k: Self::Key, v: Self::Value);
362}
363
364impl<K: Hash + Eq + PartialEq + Debug, V: Debug> InsertUnique for HashMap<K, V> {
365    type Key = K;
366    type Value = V;
367
368    fn insert_unique(&mut self, k: Self::Key, v: Self::Value) {
369        if let Some(old_v) = self.get(&k) {
370            panic!(
371                "duplicate item inserted into map for key {k:?} (old value: {old_v:?}; new value: {v:?})"
372            );
373        }
374        self.insert(k, v);
375    }
376}
377
378/// Synthesize the "main" module for the component, responsible for exporting functions which break cyclic
379/// dependencies, as well as hosting the memory and function table.
380fn make_env_module<'a>(
381    metadata: &'a [Metadata<'a>],
382    env_exports: &[EnvExport<'_>],
383    cabi_realloc_exporter: Option<&str>,
384    stack_size_bytes: u32,
385) -> (Vec<u8>, DlOpenables<'a>, TlsLayout, u32) {
386    // TODO: deduplicate types
387    let mut types = TypeSection::new();
388    let mut imports = ImportSection::new();
389    let mut import_map = IndexMap::new();
390    let mut function_count = 0;
391    let mut global_offset = 0;
392    let mut wasi_start = None;
393
394    for metadata in metadata {
395        for import in &metadata.imports {
396            if let Entry::Vacant(entry) = import_map.entry(import) {
397                imports.import(
398                    import.module,
399                    import.name,
400                    match &import.ty {
401                        Type::Function(ty) => {
402                            let index = get_and_increment(&mut function_count);
403                            entry.insert(index);
404                            types.ty().function(
405                                ty.parameters.iter().copied().map(ValType::from),
406                                ty.results.iter().copied().map(ValType::from),
407                            );
408                            EntityType::Function(index)
409                        }
410                        Type::Global(ty) => {
411                            entry.insert(get_and_increment(&mut global_offset));
412                            EntityType::Global(wasm_encoder::GlobalType {
413                                val_type: ty.ty.into(),
414                                mutable: ty.mutable,
415                                shared: ty.shared,
416                            })
417                        }
418                        Type::Tag(_) => continue,
419                    },
420                );
421            }
422        }
423
424        if metadata.has_wasi_start {
425            if wasi_start.is_some() {
426                panic!("multiple libraries export {}", metadata::START);
427            }
428            let index = get_and_increment(&mut function_count);
429
430            types.ty().function(vec![], vec![]);
431            imports.import(metadata.name, metadata::START, EntityType::Function(index));
432
433            wasi_start = Some(index);
434        }
435    }
436
437    let mut memory_offset = stack_size_bytes;
438
439    // Table offset 0 is reserved for the null function pointer.
440    // This convention follows wasm-ld's table layout:
441    // https://github.com/llvm/llvm-project/blob/913622d012f72edb5ac3a501cef8639d0ebe471b/lld/wasm/Driver.cpp#L581-L584
442    let mut table_offset = 1;
443    let mut globals = GlobalSection::new();
444    let mut exports = ExportSection::new();
445
446    if let Some(exporter) = cabi_realloc_exporter {
447        let index = get_and_increment(&mut function_count);
448        types.ty().function([ValType::I32; 4], [ValType::I32]);
449        imports.import(exporter, CABI_REALLOC, EntityType::Function(index));
450        exports.export(CABI_REALLOC, ExportKind::Func, index);
451    }
452
453    // If tls base shims are being generated, and something might spawn a
454    // thread, then the shims generated will need access to `context.get 1`.
455    let indirect_tls_base = metadata
456        .iter()
457        .any(|m| m.needs_get_tls_base || m.needs_set_tls_base)
458        && metadata.iter().any(|m| m.uses_thread_new_indirect);
459    let tls_context_get = if indirect_tls_base {
460        let index = get_and_increment(&mut function_count);
461        types.ty().function([], [ValType::I32]);
462        imports.import(
463            metadata::ROOT,
464            metadata::CONTEXT_GET_1,
465            EntityType::Function(index),
466        );
467        Some(index)
468    } else {
469        None
470    };
471
472    let mut add_global_export = |name: &str, value, mutable| {
473        let index = globals.len();
474        globals.global(
475            wasm_encoder::GlobalType {
476                val_type: ValType::I32,
477                mutable,
478                shared: false,
479            },
480            &const_u32(value),
481        );
482        exports.export(name, ExportKind::Global, index);
483    };
484
485    let dl_openables = DlOpenables::new(table_offset, memory_offset, metadata);
486
487    if metadata.iter().any(|m| m.needs_libdl_libraries) {
488        add_global_export(
489            metadata::LIBDL_LIBRARIES,
490            dl_openables.libraries_address,
491            true,
492        );
493    }
494
495    table_offset += dl_openables.function_count;
496    memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap();
497
498    let tls = TlsLayout::new(metadata, &mut memory_offset);
499
500    if metadata.iter().any(|m| m.needs_program_tls_info) {
501        add_global_export(metadata::PROGRAM_TLS_INFO, tls.program_info, true);
502    }
503
504    let memory_size = {
505        if metadata.iter().any(|m| m.needs_stack_pointer) {
506            add_global_export(metadata::STACK_POINTER, stack_size_bytes, true);
507        }
508        if metadata.iter().any(|m| m.needs_init_stack_pointer) {
509            add_global_export(metadata::INIT_STACK_POINTER, stack_size_bytes, false);
510        }
511
512        // Binaryen's Asyncify transform for shared everything linking requires these globals
513        // to be provided from env module
514        let has_asyncified_module = metadata.iter().any(|m| m.is_asyncified);
515        if has_asyncified_module {
516            add_global_export(metadata::ASYNCIFY_STATE, 0, true);
517            add_global_export(metadata::ASYNCIFY_DATA, 0, true);
518        }
519
520        // The libc.so in WASI-SDK 28+ requires these:
521        if metadata.iter().any(|m| m.needs_stack_high) {
522            add_global_export(metadata::STACK_HIGH, stack_size_bytes, true);
523        }
524        if metadata.iter().any(|m| m.needs_stack_low) {
525            add_global_export(metadata::STACK_LOW, 0, true);
526        }
527
528        for metadata in metadata {
529            memory_offset = align(memory_offset, 1 << metadata.mem_info.memory_alignment);
530            table_offset = align(table_offset, 1 << metadata.mem_info.table_alignment);
531
532            add_global_export(
533                &format!("{}:memory_base", metadata.name),
534                memory_offset,
535                false,
536            );
537            add_global_export(
538                &format!("{}:table_base", metadata.name),
539                table_offset,
540                false,
541            );
542
543            memory_offset += metadata.mem_info.memory_size;
544            table_offset += metadata.mem_info.table_size;
545
546            for import in &metadata.memory_address_imports {
547                // Note that we initialize this to zero and let the init module compute the real value at
548                // instantiation time.
549                add_global_export(&format!("{}:{import}", metadata.name), 0, true);
550            }
551        }
552
553        {
554            let offsets = env_exports
555                .iter()
556                .enumerate()
557                .map(|(offset, EnvExport { name, exporter, .. })| {
558                    (
559                        *name,
560                        (
561                            table_offset + u32::try_from(offset).unwrap(),
562                            metadata[*exporter].name == STUB_LIBRARY_NAME,
563                        ),
564                    )
565                })
566                .collect_unique::<HashMap<_, _>>();
567
568            for metadata in metadata {
569                for import in &metadata.table_address_imports {
570                    let &(offset, is_stub) = offsets.get(import).unwrap();
571                    if is_stub
572                        && metadata
573                            .env_imports
574                            .iter()
575                            .any(|e| e.0 == *import && e.1.1.contains(SymbolFlags::BINDING_WEAK))
576                    {
577                        add_global_export(&format!("{}:{import}", metadata.name), 0, true);
578                    } else {
579                        add_global_export(&format!("{}:{import}", metadata.name), offset, true);
580                    }
581                }
582            }
583        }
584
585        memory_offset = align(memory_offset, HEAP_ALIGNMENT_BYTES);
586        if metadata.iter().any(|m| m.needs_heap_base) {
587            add_global_export(metadata::HEAP_BASE, memory_offset, true);
588        }
589
590        let heap_end = align(memory_offset, PAGE_SIZE_BYTES);
591        if metadata.iter().any(|m| m.needs_heap_end) {
592            add_global_export(metadata::HEAP_END, heap_end, true);
593        }
594        heap_end / PAGE_SIZE_BYTES
595    };
596
597    let indirection_table_base = table_offset;
598
599    let mut functions = FunctionSection::new();
600    let mut code = CodeSection::new();
601    for export in env_exports {
602        let index = get_and_increment(&mut function_count);
603        types.ty().function(
604            export.ty.parameters.iter().copied().map(ValType::from),
605            export.ty.results.iter().copied().map(ValType::from),
606        );
607        functions.function(u32::try_from(index).unwrap());
608        let mut function = Function::new([]);
609        for local in 0..export.ty.parameters.len() {
610            function
611                .instructions()
612                .local_get(u32::try_from(local).unwrap());
613        }
614        function
615            .instructions()
616            .i32_const(i32::try_from(table_offset).unwrap())
617            .call_indirect(0, u32::try_from(index).unwrap())
618            .end();
619        code.function(&function);
620        exports.export(export.name, ExportKind::Func, index);
621
622        table_offset += 1;
623    }
624
625    // Define a distinct `__wasm_{get,set}_tls_base` pair for each library that
626    // needs one. Each pair reads and writes that library's slot of the array of
627    // pointers described by `TlsLayout`.
628    for (index, metadata) in metadata.iter().enumerate() {
629        // A library with no thread-local storage of its own has no slot in the
630        // array. If it still imports these then synthesize functions that trap
631        // since they shouldn't ever be called.
632        let Some(slot) = tls.slot(index) else {
633            for (needed, name, params, results) in [
634                (
635                    metadata.needs_get_tls_base,
636                    metadata::GET_TLS_BASE,
637                    &[][..],
638                    &[ValType::I32][..],
639                ),
640                (
641                    metadata.needs_set_tls_base,
642                    metadata::SET_TLS_BASE,
643                    &[ValType::I32][..],
644                    &[][..],
645                ),
646            ] {
647                if !needed {
648                    continue;
649                }
650                let func = get_and_increment(&mut function_count);
651                types
652                    .ty()
653                    .function(params.iter().copied(), results.iter().copied());
654                functions.function(func);
655                let mut function = Function::new([]);
656                function.instructions().unreachable().end();
657                code.function(&function);
658                exports.export(&format!("{}:{name}", metadata.name), ExportKind::Func, func);
659            }
660            continue;
661        };
662
663        let mem_arg = MemArg {
664            offset: u64::from(slot * 4),
665            align: 2,
666            target: Default::default(),
667            ordering: None,
668        };
669
670        if metadata.needs_get_tls_base {
671            let func = get_and_increment(&mut function_count);
672            types.ty().function([], [ValType::I32]);
673            functions.function(func);
674            let mut function = Function::new([]);
675            // With coop threads the base pointer is in `context.get 1`. Without
676            // coop threads the base pointer is `main_thread_tls_base` itself.
677            match tls_context_get {
678                Some(get) => {
679                    function.instructions().call(get);
680                }
681                None => {
682                    function
683                        .instructions()
684                        .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap());
685                }
686            }
687            function.instructions().i32_load(mem_arg).end();
688            code.function(&function);
689            exports.export(
690                &format!("{}:{}", metadata.name, metadata::GET_TLS_BASE),
691                ExportKind::Func,
692                func,
693            );
694        }
695
696        if metadata.needs_set_tls_base {
697            let func = get_and_increment(&mut function_count);
698            types.ty().function([ValType::I32], []);
699            functions.function(func);
700            let mut function = Function::new_with_locals_types(if tls_context_get.is_some() {
701                vec![ValType::I32]
702            } else {
703                vec![]
704            });
705            // With coop threads this intrinsic conditionally initializes
706            // `main_thread_tls_base` based on `context.get 1`. Otherwise it
707            // writes through to it if it's set.
708            //
709            // Without coop threads this is updating `main_thread_tls_base`.
710            match tls_context_get {
711                Some(get) => {
712                    function
713                        .instructions()
714                        .call(get)
715                        .local_tee(1)
716                        .i32_eqz()
717                        .if_(wasm_encoder::BlockType::Empty)
718                        .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap())
719                        .local_get(0)
720                        .i32_store(mem_arg)
721                        .else_()
722                        .local_get(1)
723                        .local_get(0)
724                        .i32_store(mem_arg)
725                        .end()
726                        .end();
727                }
728                None => {
729                    function
730                        .instructions()
731                        .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap())
732                        .local_get(0)
733                        .i32_store(mem_arg)
734                        .end();
735                }
736            }
737            code.function(&function);
738            exports.export(
739                &format!("{}:{}", metadata.name, metadata::SET_TLS_BASE),
740                ExportKind::Func,
741                func,
742            );
743        }
744    }
745
746    for (import, offset) in import_map {
747        exports.export(
748            &format!("{}:{}", import.module, import.name),
749            ExportKind::from(&import.ty),
750            offset,
751        );
752    }
753    if let Some(index) = wasi_start {
754        exports.export(metadata::START, ExportKind::Func, index);
755    }
756
757    let mut module = Module::new();
758
759    module.section(&types);
760    module.section(&imports);
761    module.section(&functions);
762
763    {
764        let mut tables = TableSection::new();
765        tables.table(TableType {
766            element_type: RefType::FUNCREF,
767            minimum: table_offset.into(),
768            maximum: None,
769            table64: false,
770            shared: false,
771        });
772        exports.export(metadata::INDIRECT_FUNCTION_TABLE, ExportKind::Table, 0);
773        module.section(&tables);
774    }
775
776    {
777        let mut memories = MemorySection::new();
778        memories.memory(MemoryType {
779            minimum: u64::from(memory_size),
780            maximum: None,
781            memory64: false,
782            shared: false,
783            page_size_log2: None,
784        });
785        exports.export(metadata::MEMORY, ExportKind::Memory, 0);
786        module.section(&memories);
787    }
788
789    module.section(&globals);
790    module.section(&exports);
791    module.section(&code);
792    module.section(&RawCustomSection(
793        &crate::base_producers().raw_custom_section(),
794    ));
795
796    let module = module.finish();
797    wasmparser::validate(&module).unwrap();
798
799    (module, dl_openables, tls, indirection_table_base)
800}
801
802/// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool
803/// convention and calling any static constructors and/or link-time fixup functions.
804///
805/// This module also contains the data segment for the `dlopen`/`dlsym` lookup table.
806fn make_init_module(
807    fixups: &mut FixupModule,
808    metadata: &[Metadata],
809    exporters: &IndexMap<&ExportKey, (&str, &Export)>,
810    env_exports: &[EnvExport<'_>],
811    dl_openables: &DlOpenables,
812    tls: &TlsLayout,
813    indirection_table_base: u32,
814) -> Result<()> {
815    let thunk_ty = fixups.type_thunk();
816    let memory = fixups.import(
817        &ImportedInstance::Main,
818        metadata::MEMORY,
819        MemoryType {
820            minimum: 0,
821            maximum: None,
822            memory64: false,
823            shared: false,
824            page_size_log2: None,
825        }
826        .into(),
827    )?;
828    let table = fixups.import(
829        &ImportedInstance::Main,
830        metadata::INDIRECT_FUNCTION_TABLE,
831        TableType {
832            element_type: RefType::FUNCREF,
833            minimum: 0,
834            maximum: None,
835            table64: false,
836            shared: false,
837        }
838        .into(),
839    )?;
840
841    let module_name_to_instance = |module: &str| match module {
842        metadata::ENV => ImportedInstance::Main,
843        other => ImportedInstance::Adapter(other.to_string()),
844    };
845
846    let mut global_map = HashMap::new();
847    let mut add_global_import = |fixups: &mut FixupModule, module: &str, name: &str, mutable| {
848        *global_map
849            .entry((module.to_owned(), name.to_owned()))
850            .or_insert_with(|| {
851                fixups
852                    .import_global(
853                        &module_name_to_instance(module),
854                        name,
855                        wasm_encoder::GlobalType {
856                            val_type: ValType::I32,
857                            mutable,
858                            shared: false,
859                        },
860                    )
861                    .unwrap()
862            })
863    };
864
865    let mut function_map = HashMap::new();
866    let mut add_function_import = |fixups: &mut FixupModule, module: &str, name: &str, ty| {
867        *function_map
868            .entry((module.to_owned(), name.to_owned()))
869            .or_insert_with(|| {
870                fixups
871                    .import_func(&module_name_to_instance(module), name, ty)
872                    .unwrap()
873            })
874    };
875
876    let mut names = HashMap::new();
877    for (index, metadata) in metadata.iter().enumerate() {
878        names.insert_unique(index, metadata.name);
879    }
880
881    for (exporter, export, address) in dl_openables.global_addresses.iter() {
882        let memory_base = add_global_import(
883            fixups,
884            metadata::ENV,
885            &format!("{exporter}:memory_base"),
886            false,
887        );
888        let export = add_global_import(fixups, exporter, export, false);
889
890        fixups.add_start_abi_detail(StartAction::InitializeAddress {
891            memory_base,
892            export,
893            dest: AddressDest::LinearMemory {
894                address: *address,
895                memory,
896            },
897        });
898    }
899
900    for metadata in metadata {
901        for import in &metadata.memory_address_imports {
902            let (exporter, _) = find_offset_exporter(import, exporters)?;
903
904            let memory_base = add_global_import(
905                fixups,
906                metadata::ENV,
907                &format!("{exporter}:memory_base"),
908                false,
909            );
910            let offset = add_global_import(fixups, exporter, import, false);
911            let address = add_global_import(
912                fixups,
913                metadata::ENV,
914                &format!("{}:{import}", metadata.name),
915                true,
916            );
917            fixups.add_start_abi_detail(StartAction::InitializeAddress {
918                memory_base,
919                export: offset,
920                dest: AddressDest::Global(address),
921            });
922        }
923    }
924
925    for metadata in metadata {
926        if metadata.has_data_relocs {
927            let func =
928                add_function_import(fixups, metadata.name, metadata::APPLY_DATA_RELOCS, thunk_ty);
929            fixups.add_start_abi_detail(StartAction::Call(func));
930        }
931    }
932
933    for metadata in metadata {
934        if metadata.has_ctors && metadata.has_initialize {
935            bail!(
936                "library {} exports both `{}` and `{}`; \
937                 expected at most one of the two",
938                metadata.name,
939                metadata::CALL_CTORS,
940                metadata::INITIALIZE
941            );
942        }
943
944        if metadata.has_ctors {
945            let func = add_function_import(fixups, metadata.name, metadata::CALL_CTORS, thunk_ty);
946            fixups.add_start_user_func(StartAction::Call(func));
947        }
948
949        if metadata.has_initialize {
950            let func = add_function_import(fixups, metadata.name, metadata::INITIALIZE, thunk_ty);
951            fixups.add_start_user_func(StartAction::Call(func));
952        }
953    }
954
955    let intern_type = |fixups: &mut FixupModule, ty: &FunctionType| {
956        fixups.type_intern(
957            ty.parameters.iter().copied().map(|t| t.into()).collect(),
958            ty.results.iter().copied().map(|t| t.into()).collect(),
959        )
960    };
961
962    let mut dl_openable_functions = Vec::new();
963    for metadata in metadata {
964        if metadata.dl_openable {
965            for export in &metadata.exports {
966                if let Type::Function(fty) = &export.key.ty {
967                    let ty = intern_type(fixups, fty);
968                    dl_openable_functions.push(add_function_import(
969                        fixups,
970                        metadata.name,
971                        export.key.name,
972                        ty,
973                    ));
974                }
975            }
976        }
977    }
978
979    let indirections = env_exports
980        .iter()
981        .map(|EnvExport { name, exporter, ty }| {
982            let ty = intern_type(fixups, ty);
983            add_function_import(fixups, names[exporter], name, ty)
984        })
985        .collect::<Vec<_>>();
986
987    // For all libraries that export TLS information the initialization function
988    // here will setup the in-memory `__wasm_program_tls_info` structure by
989    // storing the pointers to each `__wasm_library_tls_info` structure in a
990    // sequence.
991    if tls.library_info != 0 {
992        for (lib_index, tls_index) in tls
993            .slots
994            .iter()
995            .enumerate()
996            .filter_map(|(i, slot)| slot.map(|j| (i, j)))
997        {
998            let metadata = &metadata[lib_index];
999            let memory_base = add_global_import(
1000                fixups,
1001                metadata::ENV,
1002                &format!("{}:memory_base", metadata.name),
1003                false,
1004            );
1005            let i = add_global_import(fixups, metadata.name, metadata::LIBRARY_TLS_INFO, false);
1006
1007            fixups.add_start_abi_detail(StartAction::InitializeAddress {
1008                memory_base,
1009                export: i,
1010                dest: AddressDest::LinearMemory {
1011                    address: tls.library_info + tls_index * 4,
1012                    memory,
1013                },
1014            });
1015        }
1016    }
1017
1018    fixups.elements().active(
1019        if table == 0 { None } else { Some(table) },
1020        &const_u32(dl_openables.table_base),
1021        Elements::Functions(dl_openable_functions.into()),
1022    );
1023    fixups.elements().active(
1024        if table == 0 { None } else { Some(table) },
1025        &const_u32(indirection_table_base),
1026        Elements::Functions(indirections.into()),
1027    );
1028
1029    let data = fixups.data();
1030    data.active(
1031        memory,
1032        &const_u32(dl_openables.memory_base),
1033        dl_openables.buffer.clone(),
1034    );
1035    if !tls.buffer.is_empty() {
1036        data.active(memory, &const_u32(tls.program_info), tls.buffer.clone());
1037    }
1038
1039    Ok(())
1040}
1041
1042/// Find the library which exports the specified function or global address.
1043fn find_offset_exporter<'a>(
1044    name: &str,
1045    exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>,
1046) -> Result<(&'a str, &'a Export<'a>)> {
1047    let export = ExportKey {
1048        name,
1049        ty: Type::Global(GlobalType {
1050            ty: ValueType::I32,
1051            mutable: false,
1052            shared: false,
1053        }),
1054    };
1055
1056    exporters
1057        .get(&export)
1058        .copied()
1059        .ok_or_else(|| anyhow!("unable to find {export:?} in any library"))
1060}
1061
1062/// Find the library which exports the specified function.
1063fn find_function_exporter<'a>(
1064    name: &str,
1065    ty: &FunctionType,
1066    exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>,
1067) -> Result<(&'a str, &'a Export<'a>)> {
1068    let export = ExportKey {
1069        name,
1070        ty: Type::Function(ty.clone()),
1071    };
1072
1073    exporters
1074        .get(&export)
1075        .copied()
1076        .ok_or_else(|| anyhow!("unable to find {export:?} in any library"))
1077}
1078
1079/// Find the library which exports the specified tag.
1080fn find_tag_exporter<'a>(
1081    name: &str,
1082    ty: &FunctionType,
1083    exporters: &IndexMap<&ExportKey, (&'a str, &'a Export<'a>)>,
1084) -> Result<(&'a str, &'a Export<'a>)> {
1085    let export = ExportKey {
1086        name,
1087        ty: Type::Tag(ty.clone()),
1088    };
1089
1090    exporters
1091        .get(&export)
1092        .copied()
1093        .ok_or_else(|| anyhow!("unable to find {export:?} in any library"))
1094}
1095
1096/// Analyze the specified library metadata, producing a symbol-to-library-name map of exports.
1097fn resolve_exporters<'a>(
1098    metadata: &'a [Metadata<'a>],
1099) -> Result<IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>> {
1100    let mut exporters = IndexMap::<_, Vec<_>>::new();
1101    for metadata in metadata {
1102        for export in &metadata.exports {
1103            exporters
1104                .entry(&export.key)
1105                .or_default()
1106                .push((metadata.name, export));
1107        }
1108    }
1109    Ok(exporters)
1110}
1111
1112/// Match up all imported symbols to their corresponding exports, reporting any missing or duplicate symbols.
1113fn resolve_symbols<'a>(
1114    metadata: &'a [Metadata<'a>],
1115    exporters: &'a IndexMap<&'a ExportKey<'a>, Vec<(&'a str, &'a Export<'a>)>>,
1116) -> (
1117    IndexMap<&'a ExportKey<'a>, (&'a str, &'a Export<'a>)>,
1118    Vec<(&'a str, Export<'a>)>,
1119    Vec<(&'a str, &'a ExportKey<'a>, &'a [(&'a str, &'a Export<'a>)])>,
1120) {
1121    let function_exporters = exporters
1122        .iter()
1123        .filter_map(|(export, exporters)| match &export.ty {
1124            Type::Function(_) => Some((export.name, (export, exporters))),
1125            Type::Global(_) | Type::Tag(_) => None,
1126        })
1127        .collect_unique::<IndexMap<_, _>>();
1128
1129    let mut resolved = IndexMap::new();
1130    let mut missing = Vec::new();
1131    let mut duplicates = Vec::new();
1132
1133    let mut triage = |metadata: &'a Metadata, export: Export<'a>| {
1134        if let Some((key, value)) = exporters.get_key_value(&export.key) {
1135            // Note that we do not use `insert_unique` here since multiple libraries may import the same
1136            // symbol, in which case we may redundantly insert the same value.
1137            match value.as_slice() {
1138                [] => unreachable!(),
1139                [exporter] => {
1140                    resolved.insert(*key, *exporter);
1141                }
1142                [exporter, ..] => {
1143                    resolved.insert(*key, *exporter);
1144                    duplicates.push((metadata.name, *key, value.as_slice()));
1145                }
1146            }
1147        } else {
1148            missing.push((metadata.name, export));
1149        }
1150    };
1151
1152    for metadata in metadata {
1153        for (name, (ty, flags)) in &metadata.env_imports {
1154            triage(
1155                metadata,
1156                Export {
1157                    key: ExportKey {
1158                        name,
1159                        ty: Type::Function(ty.clone()),
1160                    },
1161                    flags: *flags,
1162                },
1163            );
1164        }
1165
1166        for name in &metadata.memory_address_imports {
1167            triage(
1168                metadata,
1169                Export {
1170                    key: ExportKey {
1171                        name,
1172                        ty: Type::Global(GlobalType {
1173                            ty: ValueType::I32,
1174                            mutable: false,
1175                            shared: false,
1176                        }),
1177                    },
1178                    flags: SymbolFlags::empty(),
1179                },
1180            );
1181        }
1182
1183        for (name, ty) in &metadata.tag_imports {
1184            triage(
1185                metadata,
1186                Export {
1187                    key: ExportKey {
1188                        name,
1189                        ty: Type::Tag(ty.clone()),
1190                    },
1191                    flags: SymbolFlags::empty(),
1192                },
1193            );
1194        }
1195    }
1196
1197    for metadata in metadata {
1198        for name in &metadata.table_address_imports {
1199            if let Some((key, value)) = function_exporters.get(name) {
1200                // Note that we do not use `insert_unique` here since multiple libraries may import the same
1201                // symbol, in which case we may redundantly insert the same value.
1202                match value.as_slice() {
1203                    [] => unreachable!(),
1204                    [exporter] => {
1205                        resolved.insert(key, *exporter);
1206                    }
1207                    [exporter, ..] => {
1208                        resolved.insert(key, *exporter);
1209                        duplicates.push((metadata.name, *key, value.as_slice()));
1210                    }
1211                }
1212            } else if metadata.env_imports.iter().any(|(n, _)| n == name) {
1213                // GOT entry for a function which is imported from the env module, but not exported by any library,
1214                // already handled above.
1215            } else {
1216                missing.push((
1217                    metadata.name,
1218                    Export {
1219                        key: ExportKey {
1220                            name,
1221                            ty: Type::Function(FunctionType {
1222                                parameters: Vec::new(),
1223                                results: Vec::new(),
1224                            }),
1225                        },
1226                        flags: SymbolFlags::empty(),
1227                    },
1228                ));
1229            }
1230        }
1231    }
1232
1233    // Even if no library imports these symbols, we re-export them from the
1234    // `env` module so that
1235    // `EncodingState::create_export_task_initialization_wrappers` can find
1236    // and use them:
1237    for (name, params, results) in ENV_REEXPORTS {
1238        let export = Export {
1239            key: ExportKey {
1240                name,
1241                ty: Type::Function(FunctionType {
1242                    parameters: params.to_vec(),
1243                    results: results.to_vec(),
1244                }),
1245            },
1246            flags: SymbolFlags::empty(),
1247        };
1248
1249        if let Some((key, value)) = exporters.get_key_value(&export.key) {
1250            // Note that we do not use `insert_unique` here since multiple
1251            // libraries may import the same symbol, in which case we may
1252            // redundantly insert the same value.
1253            match value.as_slice() {
1254                [] => unreachable!(),
1255                [exporter] | [exporter, ..] => {
1256                    resolved.insert(*key, *exporter);
1257                }
1258            }
1259        }
1260    }
1261
1262    (resolved, missing, duplicates)
1263}
1264
1265/// Recursively add a library (represented by its offset) and its dependency to the specified set, maintaining
1266/// topological order (modulo cycles).
1267fn topo_add(
1268    sorted: &mut IndexSet<usize>,
1269    dependencies: &IndexMap<usize, IndexSet<usize>>,
1270    element: usize,
1271) {
1272    let empty = &IndexSet::new();
1273    let deps = dependencies.get(&element).unwrap_or(empty);
1274
1275    // First, add any dependencies which do not depend on `element`
1276    for &dep in deps {
1277        if !(sorted.contains(&dep) || dependencies.get(&dep).unwrap_or(empty).contains(&element)) {
1278            topo_add(sorted, dependencies, dep);
1279        }
1280    }
1281
1282    // Next, add the element
1283    sorted.insert(element);
1284
1285    // Finally, add any dependencies which depend on `element`
1286    for &dep in deps {
1287        if !sorted.contains(&dep) && dependencies.get(&dep).unwrap_or(empty).contains(&element) {
1288            topo_add(sorted, dependencies, dep);
1289        }
1290    }
1291}
1292
1293/// Topologically sort a set of libraries (represented by their offsets) according to their dependencies, modulo
1294/// cycles.
1295fn topo_sort(count: usize, dependencies: &IndexMap<usize, IndexSet<usize>>) -> Result<Vec<usize>> {
1296    let mut sorted = IndexSet::new();
1297    for index in 0..count {
1298        topo_add(&mut sorted, &dependencies, index);
1299    }
1300
1301    Ok(sorted.into_iter().collect())
1302}
1303
1304/// Analyze the specified library metadata, producing a map of transitive dependencies, where each library is
1305/// represented by its offset in the original metadata slice.
1306fn find_dependencies(
1307    metadata: &[Metadata],
1308    exporters: &IndexMap<&ExportKey, (&str, &Export)>,
1309) -> Result<IndexMap<usize, IndexSet<usize>>> {
1310    // First, generate a map of direct dependencies (i.e. depender to dependees)
1311    let mut dependencies = IndexMap::<_, IndexSet<_>>::new();
1312    let mut indexes = HashMap::new();
1313    for (index, metadata) in metadata.iter().enumerate() {
1314        indexes.insert_unique(metadata.name, index);
1315        for &needed in &metadata.needed_libs {
1316            dependencies
1317                .entry(metadata.name)
1318                .or_default()
1319                .insert(needed);
1320        }
1321        for (import_name, (ty, _)) in &metadata.env_imports {
1322            dependencies
1323                .entry(metadata.name)
1324                .or_default()
1325                .insert(find_function_exporter(import_name, ty, exporters)?.0);
1326        }
1327    }
1328
1329    // Next, convert the map from names to offsets
1330    let mut dependencies = dependencies
1331        .into_iter()
1332        .map(|(k, v)| {
1333            (
1334                indexes[k],
1335                v.into_iter()
1336                    .map(|v| indexes[v])
1337                    .collect_unique::<IndexSet<_>>(),
1338            )
1339        })
1340        .collect_unique::<IndexMap<_, _>>();
1341
1342    // Finally, add all transitive dependencies to the map in a fixpoint loop, exiting when no new dependencies are
1343    // discovered.
1344    let empty = &IndexSet::new();
1345
1346    loop {
1347        let mut new = IndexMap::<_, IndexSet<_>>::new();
1348        for (index, exporters) in &dependencies {
1349            for exporter in exporters {
1350                for exporter in dependencies.get(exporter).unwrap_or(empty) {
1351                    if !exporters.contains(exporter) {
1352                        new.entry(*index).or_default().insert(*exporter);
1353                    }
1354                }
1355            }
1356        }
1357
1358        if new.is_empty() {
1359            break Ok(dependencies);
1360        } else {
1361            for (index, exporters) in new {
1362                dependencies.entry(index).or_default().extend(exporters);
1363            }
1364        }
1365    }
1366}
1367
1368struct EnvExports<'a> {
1369    exports: Vec<EnvExport<'a>>,
1370    reexport_cabi_realloc: bool,
1371}
1372
1373struct EnvExport<'a> {
1374    name: &'a str,
1375    ty: Cow<'a, FunctionType>,
1376    exporter: usize,
1377}
1378
1379/// Analyze the specified metadata and generate what needs to be exported from
1380/// the main (aka "env") module.
1381///
1382/// This includes a list of functions which should be re-exported as a
1383/// `call.indirect`-based function including the offset of the library
1384/// containing the original export.
1385///
1386/// Additionally this includes any tags necessary that are shared amongst
1387/// modules.
1388fn env_exports<'a>(
1389    metadata: &'a [Metadata<'a>],
1390    exporters: &'a IndexMap<&'a ExportKey, (&'a str, &Export)>,
1391    topo_sorted: &[usize],
1392) -> Result<EnvExports<'a>> {
1393    let function_exporters = exporters
1394        .iter()
1395        .filter_map(|(export, exporter)| {
1396            if let Type::Function(ty) = &export.ty {
1397                Some((export.name, (ty, *exporter)))
1398            } else {
1399                None
1400            }
1401        })
1402        .collect_unique::<HashMap<_, _>>();
1403
1404    let indexes = metadata
1405        .iter()
1406        .enumerate()
1407        .map(|(index, metadata)| (metadata.name, index))
1408        .collect_unique::<HashMap<_, _>>();
1409
1410    let mut result = Vec::new();
1411    let mut exported = HashSet::new();
1412    let mut seen = HashSet::new();
1413
1414    for &index in topo_sorted {
1415        let metadata = &metadata[index];
1416
1417        for name in &metadata.table_address_imports {
1418            if !exported.contains(name) {
1419                let (ty, (exporter, _)) = function_exporters
1420                    .get(name)
1421                    .ok_or_else(|| anyhow!("unable to find {name:?} in any library"))?;
1422
1423                result.push(EnvExport {
1424                    name: *name,
1425                    ty: Cow::Borrowed(*ty),
1426                    exporter: indexes[exporter],
1427                });
1428                exported.insert(*name);
1429            }
1430        }
1431
1432        for (import_name, (ty, _)) in &metadata.env_imports {
1433            if !exported.contains(import_name) {
1434                let exporter = indexes[find_function_exporter(import_name, ty, exporters)
1435                    .unwrap()
1436                    .0];
1437                if !seen.contains(&exporter) {
1438                    result.push(EnvExport {
1439                        name: *import_name,
1440                        ty: Cow::Borrowed(ty),
1441                        exporter,
1442                    });
1443                    exported.insert(*import_name);
1444                }
1445            }
1446        }
1447
1448        seen.insert(index);
1449    }
1450
1451    // Even if no library imports these symbols, we re-export them from the
1452    // `env` module so that
1453    // `EncodingState::create_export_task_initialization_wrappers` can find
1454    // and use them:
1455    for (name, params, results) in ENV_REEXPORTS {
1456        if !exported.contains(name) {
1457            let ty = FunctionType {
1458                parameters: params.to_vec(),
1459                results: results.to_vec(),
1460            };
1461            if let Some(exporter) = exporters.get(&ExportKey {
1462                name,
1463                ty: Type::Function(ty.clone()),
1464            }) {
1465                result.push(EnvExport {
1466                    name,
1467                    ty: Cow::Owned(ty),
1468                    exporter: indexes[exporter.0],
1469                });
1470                exported.insert(name);
1471            }
1472        }
1473    }
1474
1475    let reexport_cabi_realloc = exported.contains(CABI_REALLOC);
1476
1477    Ok(EnvExports {
1478        exports: result,
1479        reexport_cabi_realloc,
1480    })
1481}
1482
1483/// Synthesize a module which contains trapping stub exports for the specified functions.
1484fn make_stubs_module(missing: &[(&str, Export)]) -> Vec<u8> {
1485    let mut types = TypeSection::new();
1486    let mut exports = ExportSection::new();
1487    let mut functions = FunctionSection::new();
1488    let mut code = CodeSection::new();
1489    for (offset, (_, export)) in missing.iter().enumerate() {
1490        let offset = u32::try_from(offset).unwrap();
1491
1492        let Export {
1493            key:
1494                ExportKey {
1495                    name,
1496                    ty: Type::Function(ty),
1497                },
1498            ..
1499        } = export
1500        else {
1501            unreachable!();
1502        };
1503
1504        types.ty().function(
1505            ty.parameters.iter().copied().map(ValType::from),
1506            ty.results.iter().copied().map(ValType::from),
1507        );
1508        functions.function(offset);
1509        let mut function = Function::new([]);
1510        function.instructions().unreachable().end();
1511        code.function(&function);
1512        exports.export(name, ExportKind::Func, offset);
1513    }
1514
1515    let mut module = Module::new();
1516
1517    module.section(&types);
1518    module.section(&functions);
1519    module.section(&exports);
1520    module.section(&code);
1521    module.section(&RawCustomSection(
1522        &crate::base_producers().raw_custom_section(),
1523    ));
1524
1525    let module = module.finish();
1526    wasmparser::validate(&module).unwrap();
1527
1528    module
1529}
1530
1531/// Determine which of the specified libraries are transitively reachable at runtime, i.e. reachable from a
1532/// component export or via `dlopen`.
1533fn find_reachable<'a>(
1534    metadata: &'a [Metadata<'a>],
1535    dependencies: &IndexMap<usize, IndexSet<usize>>,
1536) -> IndexSet<&'a str> {
1537    let reachable = metadata
1538        .iter()
1539        .enumerate()
1540        .filter_map(|(index, metadata)| {
1541            if metadata.has_component_exports || metadata.dl_openable || metadata.has_wasi_start {
1542                Some(index)
1543            } else {
1544                None
1545            }
1546        })
1547        .collect_unique::<IndexSet<_>>();
1548
1549    let empty = &IndexSet::new();
1550
1551    reachable
1552        .iter()
1553        .chain(
1554            reachable
1555                .iter()
1556                .flat_map(|index| dependencies.get(index).unwrap_or(empty)),
1557        )
1558        .map(|&index| metadata[index].name)
1559        .collect()
1560}
1561
1562/// Builder type for composing dynamic library modules into a component
1563#[derive(Default)]
1564pub struct Linker {
1565    /// The `(name, module, dl_openable)` triple representing the libraries to be composed
1566    ///
1567    /// The order of this list determines priority in cases where more than one library exports the same symbol.
1568    libraries: Vec<(String, Vec<u8>, bool)>,
1569
1570    /// Whether to generate trapping stubs for any unresolved imports
1571    stub_missing_functions: bool,
1572
1573    /// Whether to use a built-in implementation of `dlopen`/`dlsym`.
1574    use_built_in_libdl: bool,
1575
1576    /// Size of stack (in bytes) to allocate in the synthesized main module
1577    ///
1578    /// If `None`, use `DEFAULT_STACK_SIZE_BYTES`.
1579    stack_size: Option<u32>,
1580
1581    encoder: ComponentEncoder,
1582}
1583
1584impl Linker {
1585    /// Add a dynamic library module to this linker.
1586    ///
1587    /// If `dl_openable` is true, all of the library's exports will be added to the `dlopen`/`dlsym` lookup table
1588    /// for runtime resolution.
1589    pub fn library(&mut self, name: &str, module: &[u8], dl_openable: bool) -> Result<&mut Self> {
1590        self.libraries
1591            .push((name.to_owned(), module.to_vec(), dl_openable));
1592
1593        Ok(self)
1594    }
1595
1596    /// Specify size of stack to allocate in the synthesized main module
1597    pub fn stack_size(&mut self, stack_size: u32) -> &mut Self {
1598        self.stack_size = Some(stack_size);
1599        self
1600    }
1601
1602    /// Specify whether to generate trapping stubs for any unresolved imports
1603    pub fn stub_missing_functions(&mut self, stub_missing_functions: bool) -> &mut Self {
1604        self.stub_missing_functions = stub_missing_functions;
1605        self
1606    }
1607
1608    /// Specify whether to use a built-in implementation of `dlopen`/`dlsym`.
1609    pub fn use_built_in_libdl(&mut self, use_built_in_libdl: bool) -> &mut Self {
1610        self.use_built_in_libdl = use_built_in_libdl;
1611        self
1612    }
1613
1614    /// Returns a reference to the internal [`ComponentEncoder`] that can be
1615    /// configured.
1616    pub fn encoder(&mut self) -> &mut ComponentEncoder {
1617        &mut self.encoder
1618    }
1619
1620    /// Encode the component and return the bytes
1621    pub fn encode(mut self) -> Result<Vec<u8>> {
1622        if self.use_built_in_libdl {
1623            self.use_built_in_libdl = false;
1624            self.library("libdl.so", include_bytes!("../libdl.so"), false)?;
1625        }
1626
1627        let adapter_names = self
1628            .encoder
1629            .adapters
1630            .keys()
1631            .map(|name| name.as_str())
1632            .collect::<HashSet<_>>();
1633
1634        let metadata = self
1635            .libraries
1636            .iter()
1637            .map(|(name, module, dl_openable)| {
1638                Metadata::try_new(name, *dl_openable, module, &adapter_names)
1639                    .with_context(|| format!("failed to extract linking metadata from {name}"))
1640            })
1641            .collect::<Result<Vec<_>>>()?;
1642
1643        {
1644            let names = self
1645                .libraries
1646                .iter()
1647                .map(|(name, ..)| name.as_str())
1648                .collect_unique::<HashSet<_>>();
1649
1650            let missing = metadata
1651                .iter()
1652                .filter_map(|metadata| {
1653                    let missing = metadata
1654                        .needed_libs
1655                        .iter()
1656                        .copied()
1657                        .filter(|name| !names.contains(*name))
1658                        .collect::<Vec<_>>();
1659
1660                    if missing.is_empty() {
1661                        None
1662                    } else {
1663                        Some((metadata.name, missing))
1664                    }
1665                })
1666                .collect::<Vec<_>>();
1667
1668            if !missing.is_empty() {
1669                bail!(
1670                    "missing libraries:\n{}",
1671                    missing
1672                        .iter()
1673                        .map(|(needed_by, missing)| format!(
1674                            "\t{needed_by} needs {}",
1675                            missing.join(", ")
1676                        ))
1677                        .collect::<Vec<_>>()
1678                        .join("\n")
1679                );
1680            }
1681        }
1682
1683        let exporters = resolve_exporters(&metadata)?;
1684
1685        let cabi_realloc_exporter = exporters
1686            .get(&ExportKey {
1687                name: "cabi_realloc",
1688                ty: Type::Function(FunctionType {
1689                    parameters: vec![ValueType::I32; 4],
1690                    results: vec![ValueType::I32],
1691                }),
1692            })
1693            .map(|exporters| exporters.first().unwrap().0);
1694
1695        let (exporters, missing, _) = resolve_symbols(&metadata, &exporters);
1696
1697        if !missing.is_empty() {
1698            if missing
1699                .iter()
1700                .all(|(_, export)| matches!(&export.key.ty, Type::Function(_)))
1701                && (self.stub_missing_functions
1702                    || missing
1703                        .iter()
1704                        .all(|(_, export)| export.flags.contains(SymbolFlags::BINDING_WEAK)))
1705            {
1706                self.stub_missing_functions = false;
1707                self.libraries
1708                    .push((STUB_LIBRARY_NAME.into(), make_stubs_module(&missing), false));
1709                return self.encode();
1710            } else {
1711                bail!(
1712                    "unresolved symbol(s):\n{}",
1713                    missing
1714                        .iter()
1715                        .filter(|(_, export)| !export.flags.contains(SymbolFlags::BINDING_WEAK))
1716                        .map(|(importer, export)| { format!("\t{importer} needs {}", export.key) })
1717                        .collect::<Vec<_>>()
1718                        .join("\n")
1719                );
1720            }
1721        }
1722
1723        let dependencies = find_dependencies(&metadata, &exporters)?;
1724
1725        {
1726            let reachable = find_reachable(&metadata, &dependencies);
1727            let unreachable = self
1728                .libraries
1729                .iter()
1730                .filter_map(|(name, ..)| (!reachable.contains(name.as_str())).then(|| name.clone()))
1731                .collect_unique::<HashSet<_>>();
1732
1733            if !unreachable.is_empty() {
1734                self.libraries
1735                    .retain(|(name, ..)| !unreachable.contains(name));
1736                return self.encode();
1737            }
1738        }
1739
1740        let topo_sorted = topo_sort(metadata.len(), &dependencies)?;
1741
1742        let EnvExports {
1743            exports: env_exports,
1744            reexport_cabi_realloc,
1745        } = env_exports(&metadata, &exporters, &topo_sorted)?;
1746
1747        let (env_module, dl_openables, tls, table_base) = make_env_module(
1748            &metadata,
1749            &env_exports,
1750            if reexport_cabi_realloc {
1751                // If "env" module already reexports "cabi_realloc", we don't need to
1752                // reexport it again.
1753                None
1754            } else {
1755                cabi_realloc_exporter
1756            },
1757            self.stack_size.unwrap_or(DEFAULT_STACK_SIZE_BYTES),
1758        );
1759
1760        self.encoder.module(&env_module)?;
1761
1762        let default_env_items = [
1763            Item {
1764                alias: metadata::MEMORY.into(),
1765                kind: ExportKind::Memory,
1766                which: MainOrAdapter::Main,
1767                name: metadata::MEMORY.into(),
1768            },
1769            Item {
1770                alias: metadata::INDIRECT_FUNCTION_TABLE.into(),
1771                kind: ExportKind::Table,
1772                which: MainOrAdapter::Main,
1773                name: metadata::INDIRECT_FUNCTION_TABLE.into(),
1774            },
1775            Item {
1776                alias: metadata::STACK_POINTER.into(),
1777                kind: ExportKind::Global,
1778                which: MainOrAdapter::Main,
1779                name: metadata::STACK_POINTER.into(),
1780            },
1781            Item {
1782                alias: metadata::INIT_STACK_POINTER.into(),
1783                kind: ExportKind::Global,
1784                which: MainOrAdapter::Main,
1785                name: metadata::INIT_STACK_POINTER.into(),
1786            },
1787        ];
1788
1789        let mut seen = HashSet::new();
1790        for index in topo_sorted {
1791            let (name, module, _) = &self.libraries[index];
1792            let metadata = &metadata[index];
1793
1794            let env_items = default_env_items
1795                .iter()
1796                .cloned()
1797                .chain([
1798                    Item {
1799                        alias: metadata::MEMORY_BASE.into(),
1800                        kind: ExportKind::Global,
1801                        which: MainOrAdapter::Main,
1802                        name: format!("{name}:memory_base"),
1803                    },
1804                    Item {
1805                        alias: metadata::TABLE_BASE.into(),
1806                        kind: ExportKind::Global,
1807                        which: MainOrAdapter::Main,
1808                        name: format!("{name}:table_base"),
1809                    },
1810                ])
1811                .chain(
1812                    [
1813                        (metadata.needs_get_tls_base, metadata::GET_TLS_BASE),
1814                        (metadata.needs_set_tls_base, metadata::SET_TLS_BASE),
1815                    ]
1816                    .into_iter()
1817                    .filter(|(needed, _)| *needed)
1818                    .map(|(_, intrinsic)| Item {
1819                        alias: intrinsic.into(),
1820                        kind: ExportKind::Func,
1821                        which: MainOrAdapter::Main,
1822                        name: format!("{name}:{intrinsic}"),
1823                    }),
1824                )
1825                .chain(metadata.env_imports.iter().map(|(name, (ty, _))| {
1826                    let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap();
1827
1828                    Item {
1829                        alias: (*name).into(),
1830                        kind: ExportKind::Func,
1831                        which: if seen.contains(exporter) {
1832                            MainOrAdapter::Adapter(exporter.to_owned())
1833                        } else {
1834                            MainOrAdapter::Main
1835                        },
1836                        name: (*name).into(),
1837                    }
1838                }))
1839                .chain(
1840                    metadata
1841                        .tag_imports
1842                        .iter()
1843                        .map(|(name, ty)| {
1844                            let (exporter, _) = find_tag_exporter(name, ty, &exporters).unwrap();
1845
1846                            Ok(Item {
1847                                alias: (*name).into(),
1848                                kind: ExportKind::Tag,
1849                                which: if seen.contains(exporter) {
1850                                    MainOrAdapter::Adapter(exporter.to_owned())
1851                                } else {
1852                                    // As of this writing, LLVM-produced shared
1853                                    // libraries which use C++ exceptions import
1854                                    // a `cpp_exception` tag which is defined in
1855                                    // `libunwind.so`.  Presumably
1856                                    // `libunwind.so` will not import anything
1857                                    // circularly from such shared libraries, so
1858                                    // this case shouldn't be hit in practice
1859                                    // unless we're dealing with some other,
1860                                    // non-LLVM toolchain that does weird
1861                                    // circular things with imports and
1862                                    // exception tags.
1863                                    bail!(
1864                                        "circular dependency prevents direct tag import from `{}`",
1865                                        exporter.to_owned()
1866                                    )
1867                                },
1868                                name: (*name).into(),
1869                            })
1870                        })
1871                        .collect::<Result<Vec<_>>>()?,
1872                )
1873                .chain(if metadata.is_asyncified {
1874                    vec![
1875                        Item {
1876                            alias: metadata::ASYNCIFY_STATE.into(),
1877                            kind: ExportKind::Global,
1878                            which: MainOrAdapter::Main,
1879                            name: metadata::ASYNCIFY_STATE.into(),
1880                        },
1881                        Item {
1882                            alias: metadata::ASYNCIFY_DATA.into(),
1883                            kind: ExportKind::Global,
1884                            which: MainOrAdapter::Main,
1885                            name: metadata::ASYNCIFY_DATA.into(),
1886                        },
1887                    ]
1888                } else {
1889                    vec![]
1890                })
1891                .collect();
1892
1893            let global_item = |address_name: &str| Item {
1894                alias: address_name.into(),
1895                kind: ExportKind::Global,
1896                which: MainOrAdapter::Main,
1897                name: format!("{name}:{address_name}"),
1898            };
1899
1900            let mem_items = metadata
1901                .memory_address_imports
1902                .iter()
1903                .copied()
1904                .map(global_item)
1905                .chain(
1906                    [
1907                        metadata::HEAP_BASE,
1908                        metadata::HEAP_END,
1909                        metadata::STACK_HIGH,
1910                        metadata::STACK_LOW,
1911                        metadata::LIBDL_LIBRARIES,
1912                        metadata::PROGRAM_TLS_INFO,
1913                    ]
1914                    .into_iter()
1915                    .map(|name| Item {
1916                        alias: name.into(),
1917                        kind: ExportKind::Global,
1918                        which: MainOrAdapter::Main,
1919                        name: name.into(),
1920                    }),
1921                )
1922                .collect();
1923
1924            let func_items = metadata
1925                .table_address_imports
1926                .iter()
1927                .copied()
1928                .map(global_item)
1929                .collect();
1930
1931            let mut import_items = BTreeMap::<_, Vec<_>>::new();
1932            for import in &metadata.imports {
1933                import_items.entry(import.module).or_default().push(Item {
1934                    alias: import.name.into(),
1935                    kind: ExportKind::from(&import.ty),
1936                    which: MainOrAdapter::Main,
1937                    name: format!("{}:{}", import.module, import.name),
1938                });
1939            }
1940
1941            self.encoder.library(
1942                name,
1943                module,
1944                LibraryInfo {
1945                    arguments: [
1946                        (metadata::GOT_MEM.into(), Instance::Items(mem_items)),
1947                        (metadata::GOT_FUNC.into(), Instance::Items(func_items)),
1948                        (metadata::ENV.into(), Instance::Items(env_items)),
1949                    ]
1950                    .into_iter()
1951                    .chain(
1952                        import_items
1953                            .into_iter()
1954                            .map(|(k, v)| (k.into(), Instance::Items(v))),
1955                    )
1956                    .collect(),
1957                },
1958            )?;
1959
1960            seen.insert(name.as_str());
1961        }
1962
1963        self.encoder.encode_with_fixups(Some(&mut |fixups| {
1964            make_init_module(
1965                fixups,
1966                &metadata,
1967                &exporters,
1968                &env_exports,
1969                &dl_openables,
1970                &tls,
1971                table_base,
1972            )
1973        }))
1974    }
1975}