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