wasmer_wasix/state/
linker.rs

1// TODO: The linker *can* exist in the runtime, since technically, there's nothing that
2// prevents us from having a non-WASIX linker. However, there is currently no use-case
3// for a non-WASIX linker, so we'll refrain from making it generic for the time being.
4
5//! Linker for loading and linking dynamic modules at runtime. The linker is designed to
6//! work with output from clang (version 19 was used at the time of creating this code).
7//! Note that dynamic linking of WASM modules is considered unstable in clang/LLVM, so
8//! this code may need to be updated for future versions of clang.
9//!
10//! The linker doesn't care about where code exists and how modules call each other, but
11//! the way we have found to be most effective is:
12//!     * The main module carries with it all of wasix-libc, and exports everything
13//!     * Side module don't link wasix-libc in, instead importing it from the main module
14//!
15//! This way, we only need one instance of wasix-libc, and one instance of all the static
16//! data that it requires to function. Indeed, if there were multiple instances of its
17//! static data, it would more than likely just break completely; one needs only imagine
18//! what would happen if there were multiple memory allocators (malloc) running at the same
19//! time. Emscripten (the only WASM runtime that supports dynamic linking, at the time of
20//! this writing) takes the same approach.
21//!
22//! While locating modules by relative or absolute paths is possible, it is recommended
23//! to put every side module into /lib, where they can be located by name as well as by
24//! path.
25//!
26//! The linker starts from a dynamically-linked main module. It scans the dylink.0 section
27//! for memory and table-related information and the list of needed modules. The module
28//! tree requires a memory, an indirect function table, and stack-related parameters
29//! (including the __stack_pointer global), which are created. Since dynamically-linked
30//! modules use PIC (position-independent code), the stack is not fixed and can be resized
31//! at runtime.
32//!
33//! After the memory, function table and stack are created, the linker proceeds to load in
34//! needed modules. Needed modules are always loaded in and initialized before modules that
35//! asked for them, since it is expected that the needed module needs to be usable before
36//! the module that needs it can be initialized.
37//!
38//! However, we also need to support circular dependencies between the modules; the most
39//! common case is when the main needs a side module and imports function from it, and the
40//! side imports wasix-libc functions from the main. To support this, the linker generates
41//! stub functions for all the imports that cannot be resolved when a module is being
42//! loaded in. The stub functions will then resolve the function once (and only once) at
43//! runtime when they're first called. This *does*, however, mean that link errors can happen
44//! at runtime, after the linker has reported successful linking of the modules. Such errors
45//! are turned into a [`WasiError::DlSymbolResolutionFailed`] error and will terminate
46//! execution completely.
47//!
48//! # Threading Support
49//!
50//! The linker supports the concept of "Instance Groups", which are multiple instances
51//! of the same module tree. This corresponds very closely to WASIX threads, but is
52//! named an instance group so as to keep the logic decoupled from the threading logic
53//! in WASIX.
54//!
55//! Each instance group has its own store, indirect function table, and stack pointer,
56//! but shares its memory with every other instance group. Note that even though the
57//! underlying memory is the same, we need to create a new [`Memory`] instance
58//! for each group via [`Memory::share_in_store`]. Also, when placing a symbol
59//! in the function table, the linker always updates all function tables at the same
60//! time. This is because function "pointers" can be passed across instance groups
61//! (read: sent to other threads) by the guest code, so all function tables should
62//! have exactly the same content at all times.
63//!
64//! One important aspect of instance groups is that they do *not* share the same store;
65//! this lets us put different instance groups on different OS threads. However, this
66//! also means that one call to [`Linker::load_module`], etc. cannot update every
67//! instance group as each one has its own function table. To make the linker work
68//! across threads, we need a "stop-the-world" lock on every instance group. The group
69//! the load/resolve request originates from sets a flag, which other instance
70//! groups are required to check periodically by calling [`Linker::do_pending_link_operations`].
71//! Once all instance groups are stopped in that function, the original can proceed to
72//! perform the operation, and report its results to all other instance groups so they
73//! can make the same changes to their function table as well.
74//!
75//! In WASIX, the periodic check is performed at the start of most (but not all) syscalls.
76//! This means a thread that doesn't make any syscalls can potentially block all other
77//! threads if a DL operation is performed. This also means that two instance groups
78//! cannot co-exist on the same OS thread, as the first one will block the OS thread
79//! and the second can't enter the "lock" again to let the first continue its work.
80//!
81//! To also get cooperation from threads that are waiting in a syscall, a
82//! [`Signal::Sigwakeup`](wasmer_wasix_types::wasi::Signal::Sigwakeup) signal is sent to
83//! all threads when a DL operation needs to be synchronized.
84//!
85//! # About TLS
86//!
87//! Each instance of each group gets its own TLS area, so there are 4 cases to consider:
88//!     * Main instance of main module: TLS area will be allocated by the compiler, and be
89//!       placed at the start of the memory region requested by the `dylink.0` section.
90//!     * Main instance of side modules: Almost same as main module, but tls_base will be
91//!       non-zero because side modules get a non-zero memory_base. It is very important
92//!       to note that the main instance of a side module lives in the instance group
93//!       that initially loads it in. This **does not** have to be the main instance
94//!       group.
95//!     * Other instances of main module: Each worker thread gets its TLS area
96//!       allocated by the code in pthread_create, and a pointer to the TLS area is passed
97//!       through the thread start args. This pointer is read by the code in thread_spawn,
98//!       and passed through to us as part of the environment's memory layout.
99//!     * Other instances of side modules: This is where the linker comes in. When the
100//!       new instance is created, the linker will call its `__wasix_init_tls` function,
101//!       which is responsible for setting up the TLS area for the thread.
102//!
103//! Since we only want to call `__wasix_init_tls` for non-main instances of side modules,
104//! it is enough to call it only within [`InstanceGroupState::instantiate_side_module_from_linker`].
105//!
106//! # Module Loading
107//!
108//! Module loading happens as an orchestrated effort between the shared linker state, the
109//! state of the instance group that started (or "instigated") the operation, and other
110//! instance groups. Access to a set of instances is required for resolution of exports,
111//! which is why the linker state alone (which only stores modules) is not enough.
112//!
113//! Even though most (if not all) operations require access to both the shared linker state
114//! and a/the instance group state, they're separated into three sets:
115//!     * Operations that deal with metadata exist as impls on [`LinkerState`]. These take
116//!       a (read-only) instance group state for export resolution, as well as a
117//!       [`StoreRef`](wasmer::StoreRef). They're guaranteed not to alter the store or the
118//!       instance group state.
119//!     * Operations that deal with the actual instances (instantiating, putting symbols in the
120//!       function table, etc.) and are started by the instigating group exist as impls on
121//!       [`InstanceGroupState`] that also take a mutable reference to the shared linker state, and
122//!       require it to be locked for writing. These operations can and will update the linker state,
123//!       mainly to store symbol resolution records.
124//!     * Operations that deal with replicating changes to instances from another thread also exits
125//!       as impls on [`InstanceGroupState`], but take a read-only reference to the shared linker
126//!       state. This is important because all the information needed for replicating the change to
127//!       the instigating group's instances should already be in the linker state. See
128//!       [`InstanceGroupState::populate_imports_from_linker`] and
129//!       [`InstanceGroupState::instantiate_side_module_from_linker`] for the two most important ones.
130//!
131//! Module loading generally works by going through these steps:
132//!     * [`LinkerState::load_module_tree`] loads modules (and their needed modules) and assigns
133//!       module handles
134//!     * Then, for each new module:
135//!         * Memory and table space is allocated
136//!         * Imports are resolved (see next section)
137//!         * The module is instantiated
138//!     * After all modules have been instantiated, pending imports (resulting from circular
139//!       dependencies) are resolved
140//!     * Finally, module initializers are called
141//!
142//! ## Symbol resolution
143//!
144//! To support replicating operations from the instigating group to other groups, symbol resolution
145//! happens in 3 steps:
146//!     * [`LinkerState::resolve_symbols`] goes through the imports of a soon-to-be-loaded module,
147//!       recording the imports as [`NeededSymbolResolutionKey`]s and creating
148//!       [`InProgressSymbolResolution`]s in response to each one.
149//!     * [`InstanceGroupState::populate_imports_from_link_state`] then goes through the results
150//!       and resolves each import to its final value, while also recording enough information (in the
151//!       shape of [`SymbolResolutionResult`]s) for other groups to resolve the symbol from their own
152//!       instances.
153//!     * Finally, instances are created and finalized, and initializers are called.
154//!
155//! ## Stub functions
156//!
157//! As noted above, stub functions are generated in response to circular dependencies. The stub
158//! functions do take previous symbol resolution records into account, so that the stub corresponding
159//! to a single import cannot resolve to different exports in different groups. If no such record is
160//! found, then a new record is created by the stub function. However, there's a catch.
161//!
162//! It must be noted that, during initialization, the shared linker state has to remain write-locked
163//! so as to prevent other threads from starting another operation (the replication logic only works
164//! with one active operation at a time). Stub functions need a write lock on the shared linker state
165//! to store new resolution records, and as such, they can't store resolution records if they're
166//! called in response to a module's initialization routines. This can happen easily if:
167//! * A side module is needed by the main
168//! * That side module accesses any libc functions, such as printing something to stdout.
169//!
170//! To work around this, stub functions only *try* to lock the shared linker state, and if they can't,
171//! they won't store anything. A follow-up call to the stub function can resolve the symbol again,
172//! store it for use by further calls to the function, and also create a resolution record. This does
173//! create a few hard-to-reach edge cases:
174//!     * If the symbol happens to resolve differently between the two calls to the stub, unpredictable
175//!       behavior can happen; however, this is impossible in the current implementation.
176//!     * If the shared state is locked by a different instance group, then the stub won't store its
177//!       lookup results anyway, even though it could have if it had waited.
178//!
179//! ## Locating side modules
180//!
181//! Side modules are located according to these steps:
182//!     * If the name contains a slash (/), it is treated as a relative or absolute path.   
183//!     * Otherwise, the name is searched for in `/lib`, `/usr/lib` and `/usr/local/lib`.
184//!       LD_LIBRARY_PATH is not supported yet.
185//!
186//! # Building dynamically-linked modules
187//!
188//! Note that building modules that conform the specific requirements of this linker requires
189//! careful configuration of clang. A PIC sysroot is required. The steps to build a main
190//! module are:
191//!
192//! ```bash
193//! clang-19 \
194//!   --target=wasm32-wasi --sysroot=/path/to/sysroot32-pic \
195//!   -matomics -mbulk-memory -mmutable-globals -pthread \
196//!   -mthread-model posix -ftls-model=local-exec \
197//!   -fno-trapping-math -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_SIGNAL \
198//!   -D_WASI_EMULATED_PROCESS_CLOCKS \
199//!   # PIC is required for all modules, main and side
200//!   -fPIC \
201//!   # We need to compile to an object file we can manually link in the next step
202//!   -c main.c -o main.o
203//!
204//! wasm-ld-19 \
205//!   # To link needed side modules, assuming `libsidewasm.so` exists in the current directory:
206//!   -L. -lsidewasm \
207//!   -L/path/to/sysroot32-pic/lib \
208//!   -L/path/to/sysroot32-pic/lib/wasm32-wasi \
209//!   # Make wasm-ld search everywhere and export everything, needed for wasix-libc functions to
210//!   # be exported correctly from the main module
211//!   --whole-archive --export-all \
212//!   # The object file from the last step
213//!   main.o \
214//!   # The crt1.o file contains the _start and _main_void functions
215//!   /path/to/sysroot32-pic/lib/wasm32-wasi/crt1.o \
216//!   # Statically link the sysroot's libraries
217//!   -lc -lresolv -lrt -lm -lpthread -lwasi-emulated-mman \
218//!   # The usual linker config for wasix modules
219//!   --import-memory --shared-memory --extra-features=atomics,bulk-memory,mutable-globals \
220//!   --export=__wasm_signal --export=__tls_size --export=__tls_align \
221//!   --export=__tls_base --export=__wasm_call_ctors --export-if-defined=__wasm_apply_data_relocs \
222//!   # Again, PIC is very important, as well as producing a location-independent executable with -pie
223//!   --experimental-pic -pie \
224//!   -o main.wasm
225//! ```
226//!
227//! And the steps to build a side module are:
228//!
229//! ```bash
230//! clang-19 \
231//!   --target=wasm32-wasi --sysroot=/path/to/sysroot32-pic \
232//!   -matomics -mbulk-memory -mmutable-globals -pthread \
233//!   -mthread-model posix -ftls-model=local-exec \
234//!   -fno-trapping-math -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_SIGNAL \
235//!   -D_WASI_EMULATED_PROCESS_CLOCKS \
236//!   # We need PIC
237//!   -fPIC \
238//!   # Make it export everything that's not hidden explicitly
239//!   -fvisibility=default \
240//!   -c side.c -o side.o
241//!
242//! wasm-ld-19 \
243//!   # Note: we don't link against wasix-libc, so no -lc etc., because we want
244//!   # those symbols to be imported.
245//!   --extra-features=atomics,bulk-memory,mutable-globals \
246//!   --export=__wasm_call_ctors --export-if-defined=__wasm_apply_data_relocs \
247//!   # Need PIC
248//!   --experimental-pic \
249//!   # Import everything that's undefined, including wasix-libc functions
250//!   --unresolved-symbols=import-dynamic \
251//!   # build a shared library
252//!   -shared \
253//!   # Import a shared memory
254//!   --shared-memory \
255//!   # Conform to the libxxx.so naming so clang can find it via -lxxx
256//!   -o libsidewasm.so side.o
257//! ```
258
259#![allow(clippy::result_large_err)]
260
261use std::{
262    borrow::Cow,
263    collections::{BTreeMap, HashMap},
264    ffi::OsStr,
265    fmt::Display,
266    ops::{Deref, DerefMut},
267    path::{Path, PathBuf},
268    sync::{
269        Arc, Barrier, Mutex, MutexGuard, RwLock, RwLockWriteGuard, TryLockError,
270        atomic::{AtomicBool, Ordering},
271    },
272};
273
274use bus::Bus;
275use derive_more::Debug;
276use shared_buffer::OwnedBuffer;
277use tracing::trace;
278use virtual_fs::{AsyncReadExt, FileSystem, FsError};
279use virtual_mio::block_on;
280use wasmer::{
281    AsStoreMut, AsStoreRef, Engine, ExportError, Exportable, Extern, ExternType, Function,
282    FunctionEnv, FunctionEnvMut, FunctionType, Global, GlobalType, ImportType, Imports, Instance,
283    InstantiationError, Memory, MemoryError, Module, RuntimeError, StoreMut, Table, Tag, Type,
284    Value, WASM_PAGE_SIZE, WasmTypeList,
285};
286use wasmer_wasix_types::wasix::WasiMemoryLayout;
287
288use crate::{
289    Runtime, SpawnError, WasiEnv, WasiError, WasiFs, WasiFunctionEnv, WasiModuleTreeHandles,
290    WasiProcess, WasiThreadId, fs::WasiFsRoot, import_object_for_all_wasi_versions,
291    runtime::module_cache::HashedModuleData,
292};
293
294use super::{WasiModuleInstanceHandles, WasiState};
295
296// Module handle 1 is always the main module. Side modules get handles starting from the next one after the main module.
297pub static MAIN_MODULE_HANDLE: ModuleHandle = ModuleHandle(1);
298static INVALID_MODULE_HANDLE: ModuleHandle = ModuleHandle(u32::MAX);
299
300// Need to keep the zeroth index null to catch null function pointers at runtime
301static MAIN_MODULE_TABLE_BASE: u64 = 1;
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
304pub struct ModuleHandle(u32);
305
306impl From<ModuleHandle> for u32 {
307    fn from(handle: ModuleHandle) -> Self {
308        handle.0
309    }
310}
311
312impl From<u32> for ModuleHandle {
313    fn from(handle: u32) -> Self {
314        ModuleHandle(handle)
315    }
316}
317
318impl Display for ModuleHandle {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        write!(f, "{}", self.0)
321    }
322}
323
324const DEFAULT_RUNTIME_PATH: [&str; 3] = ["/lib", "/usr/lib", "/usr/local/lib"];
325
326struct AllocatedPage {
327    // The base_ptr is mutable, and will move forward as memory is allocated from the page.
328    base_ptr: u32,
329
330    // The amount of memory remaining until the end of the allocated region. Despite the
331    // name of this struct, the region does not have to be only one page.
332    remaining: u32,
333}
334
335// Used to allocate and manage memory for dynamic modules that are loaded in or
336// out, since each module may request a specific amount of memory to be allocated
337// for it before starting it up.
338// TODO: Only supports Memory32, should implement proper Memory64 support
339struct MemoryAllocator {
340    allocated_pages: Vec<AllocatedPage>,
341}
342
343impl MemoryAllocator {
344    pub fn new() -> Self {
345        Self {
346            allocated_pages: vec![],
347        }
348    }
349
350    pub fn allocate(
351        &mut self,
352        memory: &Memory,
353        store: &mut impl AsStoreMut,
354        size: u32,
355        alignment: u32,
356    ) -> Result<u32, MemoryError> {
357        match self.allocate_in_existing_pages(size, alignment) {
358            Some(base_ptr) => Ok(base_ptr),
359            None => self.allocate_new_page(memory, store, size),
360        }
361    }
362
363    // Finds a page which has enough free memory for the request, and allocates in it.
364    // Returns the address of the allocated region if one was found.
365    fn allocate_in_existing_pages(&mut self, size: u32, alignment: u32) -> Option<u32> {
366        // A type to hold intermediate search results. The idea is to allocate on the page
367        // that has the least amount of free space, so we can later satisfy larger allocation
368        // requests without having to allocate entire new pages.
369        struct CandidatePage {
370            index: usize,
371            base_ptr: u32,
372            to_add: u32,
373            remaining_free: u32,
374        }
375
376        impl PartialEq for CandidatePage {
377            fn eq(&self, other: &Self) -> bool {
378                self.remaining_free == other.remaining_free
379            }
380        }
381
382        impl Eq for CandidatePage {}
383
384        impl PartialOrd for CandidatePage {
385            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
386                Some(self.cmp(other))
387            }
388        }
389
390        impl Ord for CandidatePage {
391            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
392                self.remaining_free.cmp(&other.remaining_free)
393            }
394        }
395
396        let mut candidates = std::collections::BinaryHeap::new();
397
398        for (index, page) in self.allocated_pages.iter().enumerate() {
399            // Offset for proper alignment
400            let offset = if page.base_ptr % alignment == 0 {
401                0
402            } else {
403                alignment - (page.base_ptr % alignment)
404            };
405
406            if page.remaining >= offset + size {
407                candidates.push(std::cmp::Reverse(CandidatePage {
408                    index,
409                    base_ptr: page.base_ptr + offset,
410                    to_add: offset + size,
411                    remaining_free: page.remaining - offset - size,
412                }));
413            }
414        }
415
416        candidates.pop().map(|elected| {
417            let page = &mut self.allocated_pages[elected.0.index];
418
419            trace!(
420                free = page.remaining,
421                base_ptr = elected.0.base_ptr,
422                "Found existing memory page with sufficient space"
423            );
424
425            page.base_ptr += elected.0.to_add;
426            page.remaining -= elected.0.to_add;
427            elected.0.base_ptr
428        })
429    }
430
431    fn allocate_new_page(
432        &mut self,
433        memory: &Memory,
434        store: &mut impl AsStoreMut,
435        size: u32,
436    ) -> Result<u32, MemoryError> {
437        // No need to account for alignment here, as pages are already 64k-aligned
438        let to_grow = size.div_ceil(WASM_PAGE_SIZE as u32);
439        let pages = memory.grow(store, to_grow)?;
440
441        let base_ptr = pages.0 * WASM_PAGE_SIZE as u32;
442        let total_allocated = to_grow * WASM_PAGE_SIZE as u32;
443
444        // The initial size bytes are already allocated, rest goes into the list
445        if total_allocated > size {
446            self.allocated_pages.push(AllocatedPage {
447                base_ptr: base_ptr + size,
448                remaining: total_allocated - size,
449            });
450        }
451
452        trace!(
453            page_count = to_grow,
454            size, base_ptr, "Allocated new memory page(s) to accommodate requested memory"
455        );
456
457        Ok(base_ptr)
458    }
459}
460
461#[derive(thiserror::Error, Debug)]
462pub enum LinkError {
463    #[error("Cannot access linker through a dead instance group")]
464    InstanceGroupIsDead,
465
466    #[error("Main module is missing a required import: {0}")]
467    MissingMainModuleImport(String),
468
469    #[error("Failed to spawn module: {0}")]
470    SpawnError(#[from] SpawnError),
471
472    #[error("Failed to instantiate module: {0}")]
473    InstantiationError(#[from] InstantiationError),
474
475    #[error("Memory allocation error: {0}")]
476    MemoryAllocationError(#[from] MemoryError),
477
478    #[error("Failed to allocate function table indices: {0}")]
479    TableAllocationError(RuntimeError),
480
481    #[error("Failed to find shared library {0}: {1}")]
482    SharedLibraryMissing(String, LocateModuleError),
483
484    #[error("Module is not a dynamic library")]
485    NotDynamicLibrary,
486
487    #[error("Failed to parse dylink.0 section: {0}")]
488    Dylink0SectionParseError(#[from] wasmparser::BinaryReaderError),
489
490    #[error("Unresolved global '{0}'.{1} due to: {2}")]
491    UnresolvedGlobal(String, String, Box<ResolveError>),
492
493    #[error("Failed to update global {0} due to: {1}")]
494    GlobalUpdateFailed(String, RuntimeError),
495
496    #[error("Expected global to be of type I32 or I64: '{0}'.{1}")]
497    NonIntegerGlobal(String, String),
498
499    #[error("Bad known import: '{0}'.{1} of type {2:?}")]
500    BadImport(String, String, ExternType),
501
502    #[error(
503        "Import could not be satisfied because of type mismatch: '{0}'.{1}, expected {2:?}, found {3:?}"
504    )]
505    ImportTypeMismatch(String, String, ExternType, ExternType),
506
507    #[error("Expected import to be a function: '{0}'.{1}")]
508    ImportMustBeFunction(&'static str, String),
509
510    #[error("Expected export {0} to be a function, found: {1:?}")]
511    ExportMustBeFunction(String, ExternType),
512
513    #[error("Failed to initialize instance: {0}")]
514    InitializationError(anyhow::Error),
515
516    #[error("Initialization function has invalid signature: {0}")]
517    InitFuncWithInvalidSignature(String),
518
519    #[error("Initialization function {0} failed to run: {1}")]
520    InitFunctionFailed(String, RuntimeError),
521
522    #[error("Failed to initialize WASI(X) module handles: {0}")]
523    MainModuleHandleInitFailed(ExportError),
524
525    #[error("Bad __tls_base export, expected a global of type I32 or I64")]
526    BadTlsBaseExport,
527
528    #[error(
529        "TLS symbol {0} cannot be resolved from module {1} because it does not export its __tls_base"
530    )]
531    MissingTlsBaseExport(String, ModuleHandle),
532}
533
534#[derive(Debug)]
535pub enum LocateModuleError {
536    Single(FsError),
537    Multiple(Vec<(PathBuf, FsError)>),
538}
539
540impl std::fmt::Display for LocateModuleError {
541    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542        match self {
543            LocateModuleError::Single(e) => std::fmt::Display::fmt(&e, f),
544            LocateModuleError::Multiple(errors) => {
545                for (path, error) in errors {
546                    write!(f, "\n    {}: {}", path.display(), error)?;
547                }
548                Ok(())
549            }
550        }
551    }
552}
553
554#[derive(Debug)]
555enum PartiallyResolvedExport {
556    Function(Function),
557    Global(u64),
558    Tls {
559        // The offset relative to the TLS area of the instance. Kept so we
560        // can re-resolve for other instance groups.
561        offset: u64,
562        // The final address of the symbol for the current instance group.
563        final_addr: u64,
564    },
565}
566
567pub enum ResolvedExport {
568    Function { func_ptr: u64 },
569
570    // Contains the offset of the global in memory, with memory_base/tls_base accounted for
571    // See: https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md#exports
572    Global { data_ptr: u64 },
573}
574
575#[derive(thiserror::Error, Debug)]
576pub enum ResolveError {
577    #[error("Linker not initialized")]
578    NotInitialized,
579
580    #[error("Invalid module handle")]
581    InvalidModuleHandle,
582
583    #[error("Missing export")]
584    MissingExport,
585
586    #[error("Invalid export type: {0:?}")]
587    InvalidExportType(ExternType),
588
589    #[error("Failed to allocate function table indices: {0}")]
590    TableAllocationError(RuntimeError),
591
592    #[error("Cannot access linker through a dead instance group")]
593    InstanceGroupIsDead,
594
595    #[error("Failed to perform pending DL operation: {0}")]
596    PendingDlOperationFailed(#[from] LinkError),
597
598    #[error("Module must export its __tls_base for exported TLS symbols to be resolved correctly")]
599    NoTlsBaseGlobalExport,
600}
601
602#[derive(Debug, Clone)]
603pub struct DylinkInfo {
604    pub mem_info: wasmparser::MemInfo,
605    pub needed: Vec<String>,
606    pub import_metadata: HashMap<(String, String), wasmparser::SymbolFlags>,
607    pub export_metadata: HashMap<String, wasmparser::SymbolFlags>,
608    pub runtime_path: Vec<String>,
609}
610
611pub struct LinkedMainModule {
612    pub instance: Instance,
613    pub memory: Memory,
614    pub indirect_function_table: Table,
615    pub stack_low: u64,
616    pub stack_high: u64,
617}
618
619#[derive(Debug)]
620enum UnresolvedGlobal {
621    // A GOT.mem entry, should be resolved to an exported global from another module.
622    Mem(NeededSymbolResolutionKey, Global),
623    // A GOT.func entry, should be resolved to the address of an exported function
624    // from another module (e.g. an index into __indirect_function_table).
625    Func(NeededSymbolResolutionKey, Global),
626}
627
628impl UnresolvedGlobal {
629    fn key(&self) -> &NeededSymbolResolutionKey {
630        match self {
631            Self::Func(key, _) => key,
632            Self::Mem(key, _) => key,
633        }
634    }
635
636    fn global(&self) -> &Global {
637        match self {
638            Self::Func(_, global) => global,
639            Self::Mem(_, global) => global,
640        }
641    }
642
643    fn import_module(&self) -> &str {
644        match self {
645            Self::Func(..) => "GOT.func",
646            Self::Mem(..) => "GOT.mem",
647        }
648    }
649}
650
651#[derive(Debug)]
652struct PendingFunctionResolutionFromLinkerState {
653    resolved_from: ModuleHandle,
654    name: String,
655    function_table_index: u32,
656}
657
658#[derive(Debug)]
659struct PendingTlsPointer {
660    global: Global,
661    resolved_from: ModuleHandle,
662    offset: u64,
663}
664
665// Used only when processing a module load operation from another instance group.
666// Note: Non-TLS globals are constant across instance groups, and thus we store
667// their value, feeding it into new instance groups directly. In the case of TLS
668// symbols, they need to get new values based on each specific instance's __tls_base,
669// so they need to be tracked.
670// __wasm_apply_data_relocs operates on memory addresses, and so it needs to run
671// only once. __wasm_apply_tls_relocs does need to run once per instance group, but
672// it's run as part of __wasm_init_tls, which itself is called by __wasix_init_tls.
673// In either case, there's no need to call any relocation functions when spawning
674// further instances of a module that was already loaded and instantiated once.
675#[derive(Debug, Default)]
676struct PendingResolutionsFromLinker {
677    functions: Vec<PendingFunctionResolutionFromLinkerState>,
678    tls: Vec<PendingTlsPointer>,
679}
680
681#[derive(Debug, Clone, PartialEq, Eq, Hash)]
682pub struct NeededSymbolResolutionKey {
683    module_handle: ModuleHandle,
684    // Corresponds to the first identifier, such as env in env.memory. Both "module"
685    // names come from the WASM spec, unfortunately, so we can't change them.
686    // We only resolve from a well-known set of modules, namely "env", "GOT.mem" and
687    // "GOT.func", so this doesn't need to be an owned string.
688    import_module: String,
689    import_name: String,
690}
691
692#[derive(Debug)]
693enum InProgressSymbolResolution {
694    Function(ModuleHandle),
695    StubFunction(FunctionType),
696    // May or may not be a TLS symbol.
697    MemGlobal(ModuleHandle),
698    FuncGlobal(ModuleHandle),
699    UnresolvedMemGlobal,
700    UnresolvedFuncGlobal,
701}
702
703#[derive(Debug)]
704struct InProgressModuleLoad {
705    handle: ModuleHandle,
706    module: Module,
707    dylink_info: DylinkInfo,
708}
709
710#[derive(Default, Debug)]
711struct InProgressLinkState {
712    // All modules loaded in by this link operation, in the order they were loaded in.
713    new_modules: Vec<InProgressModuleLoad>,
714
715    // Modules that are currently being loaded in from the FS due to needed sections.
716    pending_module_paths: Vec<PathBuf>,
717
718    // Collection of intermediate symbol resolution results. This includes functions
719    // that have been found but not appended to the function tables yet, as well as
720    // unresolved globals.
721    symbols: HashMap<NeededSymbolResolutionKey, InProgressSymbolResolution>,
722
723    unresolved_globals: Vec<UnresolvedGlobal>,
724}
725
726#[derive(Debug, Clone, PartialEq, Eq, Hash)]
727pub enum SymbolResolutionKey {
728    Needed(NeededSymbolResolutionKey),
729    Requested {
730        // Note: since we don't support module unloading, resolving the same symbol
731        // from *every* module will find the same symbol every time, so we can cache
732        // the None case as well.
733        // TODO: once we implement RTLD_NEXT, that flag should also be taken into
734        // account here.
735        resolve_from: Option<ModuleHandle>,
736        name: String,
737    },
738}
739
740#[derive(Debug)]
741pub enum SymbolResolutionResult {
742    // The symbol was resolved to a global address. We don't resolve again because
743    // the value of globals and the memory_base for each module and all of its instances
744    // is fixed.
745    // The case of unresolved globals is not mentioned in this enum, since it can't exist
746    // once a link operation is finalized.
747    Memory(u64),
748    // The symbol was resolved to a global address, but the global is a TLS variable.
749    // Each instance of each module has a different TLS area, and TLS symbols must be
750    // resolved again every time.
751    Tls {
752        resolved_from: ModuleHandle,
753        offset: u64,
754    },
755    // The symbol was resolved to a function export with the same name from this module.
756    // it is expected that the symbol resolves to an export of the correct type.
757    Function {
758        ty: FunctionType,
759        resolved_from: ModuleHandle,
760    },
761    // Same deal as above, but a pointer was generated and placed in the function table.
762    FunctionPointer {
763        resolved_from: ModuleHandle,
764        function_table_index: u32,
765    },
766    // The symbol failed to resolve, but it's a function so we can create a stub. The
767    // first call to any stub associated with this symbol must update the resolution
768    // record to point to the module the function was resolved from.
769    StubFunction(FunctionType),
770}
771
772// Used to communicate the result of an operation that happened in one
773// instance group to all others
774#[derive(Debug, Clone)]
775enum DlOperation {
776    LoadModules(Vec<ModuleHandle>),
777    ResolveFunction {
778        name: String,
779        resolved_from: ModuleHandle,
780        // This should match the current length of each instance group's function table
781        // minus one. Otherwise, we're out of sync and an error has been encountered.
782        function_table_index: u32,
783    },
784    // Allocates slots in the function table
785    AllocateFunctionTable {
786        index: u32,
787        size: u32,
788    },
789}
790
791struct DlModule {
792    module: Module,
793    dylink_info: DylinkInfo,
794    memory_base: u64,
795    table_base: u64,
796}
797
798struct DlInstance {
799    instance: Instance,
800    #[allow(dead_code)]
801    instance_handles: WasiModuleInstanceHandles,
802    tls_base: Option<u64>,
803}
804
805struct InstanceGroupState {
806    main_instance: Option<Instance>,
807    main_instance_tls_base: Option<u64>,
808
809    side_instances: HashMap<ModuleHandle, DlInstance>,
810
811    stack_pointer: Global,
812    memory: Memory,
813    indirect_function_table: Table,
814    c_longjmp: Tag,
815    cpp_exception: Tag,
816
817    // Once the dl_operation_pending flag is set, a barrier is created and broadcast
818    // by the instigating group, which others must use to rendezvous with it.
819    recv_pending_operation_barrier: bus::BusReader<Arc<Barrier>>,
820    // The corresponding sender is stored in the shared linker state, and is used
821    // by the instigating instance group  to broadcast the results.
822    recv_pending_operation: bus::BusReader<DlOperation>,
823}
824
825// There is only one LinkerState for all instance groups
826struct LinkerState {
827    engine: Engine,
828
829    main_module: Module,
830    main_module_dylink_info: DylinkInfo,
831    main_module_memory_base: u64,
832
833    // We used to have an issue where spawning instances out-of-order in new threads
834    // would break globals. That has since been fixed. However, spawning in the same
835    // order helps with diagnosing potential linker issues, so we're keeping the
836    // hack from back then.
837    // To ensure the same order, we use a BTreeMap here, which means when we
838    // iterate over it, we'll get the modules from lowest handle to highest, and
839    // order is preserved.
840    side_modules: BTreeMap<ModuleHandle, DlModule>,
841    side_modules_by_name: HashMap<PathBuf, ModuleHandle>,
842    next_module_handle: u32,
843
844    memory_allocator: MemoryAllocator,
845    heap_base: u64,
846
847    /// Tracks which slots in the function table are currently used for closures
848    ///
849    /// True if the closure is currently in use, false otherwise.
850    allocated_closure_functions: BTreeMap<u32, bool>,
851    /// Slots in the indirect function table that were allocated for closures but are currently not in use.
852    /// These can be given out without needing to lock all threads.
853    available_closure_functions: Vec<u32>,
854
855    symbol_resolution_records: HashMap<SymbolResolutionKey, SymbolResolutionResult>,
856
857    send_pending_operation_barrier: bus::Bus<Arc<Barrier>>,
858    send_pending_operation: bus::Bus<DlOperation>,
859}
860
861/// The linker is responsible for loading and linking dynamic modules at runtime,
862/// and managing the shared memory and indirect function table.
863/// Each linker instance represents a specific instance group. Cloning a linker
864/// instance does *not* create a new instance group though; the clone will refer
865/// to the same group as the original.
866#[derive(Clone)]
867pub struct Linker {
868    linker_state: Arc<RwLock<LinkerState>>,
869    instance_group_state: Arc<Mutex<Option<InstanceGroupState>>>,
870
871    // Is a DL operation pending? This is the cheapest way I know of to let each
872    // instance group check if an operation is *not* pending, which is the case
873    // 99.99% of the time. Uses Relaxed ordering all the time, since we don't
874    // even particularly care about a missed read of this value. A later call can
875    // always pick the flag up and start waiting for the DL operation to complete.
876    // This should only be written after the linker state has been exclusively
877    // locked for writing.
878    dl_operation_pending: Arc<AtomicBool>,
879}
880
881// This macro exists to ensure we don't get into a deadlock with another pending
882// DL operation. The linker state must be locked for write *ONLY THROUGH THIS
883// MACRO*. Bad things happen otherwise.
884// We also need a lock on the specific group's state here, because if there is a
885// pending DL operation we need to apply, that'll require mutable access to the
886// group's state. Rather than just lock it within the macro and cause a potential
887// deadlock, the macro requires to lock be acquired beforehand and passed in.
888macro_rules! write_linker_state {
889    ($guard:ident, $linker:expr, $group_state:ident, $ctx:ident) => {
890        #[allow(unused_mut)]
891        let mut $guard = loop {
892            match $linker.linker_state.try_write() {
893                Ok(guard) => break guard,
894                Err(TryLockError::WouldBlock) => {
895                    // The group that holds the lock is most likely waiting for an op
896                    // to finish, so we should help it with that...
897                    let env = $ctx.as_ref();
898                    let mut store = $ctx.as_store_mut();
899                    $linker.do_pending_link_operations_internal($group_state, &mut store, &env)?;
900                    // ... and sleep for a while before attempting the lock again, so
901                    // everything has time to settle. We don't care too much about the
902                    // performance of the actual DL ops, since those will be few and
903                    // far in between (hopefully!).
904                    std::thread::sleep(std::time::Duration::from_millis(10));
905                }
906                Err(TryLockError::Poisoned(_)) => panic!("The linker state's lock is poisoned"),
907            }
908        };
909    };
910}
911
912macro_rules! lock_instance_group_state {
913    ($guard:ident, $state:ident, $linker:expr, $err:expr) => {
914        let mut $guard = $linker.instance_group_state.lock().unwrap();
915        if $guard.is_none() {
916            return Err($err);
917        }
918        let $state = $guard.deref_mut().as_mut().unwrap();
919    };
920}
921
922impl std::fmt::Debug for Linker {
923    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
924        f.debug_struct("Linker").finish()
925    }
926}
927
928pub enum DlModuleSpec<'a> {
929    FileSystem {
930        module_spec: &'a Path,
931        ld_library_path: &'a [&'a Path],
932    },
933    Memory {
934        module_name: &'a str,
935        bytes: &'a [u8],
936    },
937}
938
939impl std::fmt::Debug for DlModuleSpec<'_> {
940    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
941        match self {
942            Self::FileSystem { module_spec, .. } => f
943                .debug_struct("FileSystem")
944                .field("module_spec", module_spec)
945                .finish(),
946            Self::Memory { module_name, .. } => f
947                .debug_struct("Memory")
948                .field("module_name", module_name)
949                .finish(),
950        }
951    }
952}
953
954impl Linker {
955    /// Creates a new linker for the given main module. The module is expected to be a
956    /// PIE executable. Imports for the module will be fulfilled, so that it can start
957    /// running, and a Linker instance is returned which can then be used for the
958    /// loading/linking of further side modules.
959    pub fn new(
960        engine: Engine,
961        main_module: &Module,
962        store: &mut StoreMut<'_>,
963        memory: Option<Memory>,
964        func_env: &mut WasiFunctionEnv,
965        stack_size: u64,
966        ld_library_path: &[&Path],
967    ) -> Result<(Self, LinkedMainModule), LinkError> {
968        let dylink_section = parse_dylink0_section(main_module)?;
969
970        trace!(?dylink_section, "Loading main module");
971
972        let mut imports = import_object_for_all_wasi_versions(main_module, store, &func_env.env);
973
974        let function_table_type = main_module
975            .imports()
976            .tables()
977            .filter_map(|t| {
978                if t.ty().ty == Type::FuncRef
979                    && t.name() == "__indirect_function_table"
980                    && t.module() == "env"
981                {
982                    Some(*t.ty())
983                } else {
984                    None
985                }
986            })
987            .next()
988            .ok_or(LinkError::MissingMainModuleImport(
989                "env.__indirect_function_table".to_string(),
990            ))?;
991
992        trace!(
993            minimum_size = ?function_table_type.minimum,
994            "Creating indirect function table"
995        );
996        let indirect_function_table = Table::new(store, function_table_type, Value::FuncRef(None))
997            .map_err(LinkError::TableAllocationError)?;
998
999        let expected_table_length =
1000            dylink_section.mem_info.table_size + MAIN_MODULE_TABLE_BASE as u32;
1001        // Make sure the function table is as big as the dylink.0 section expects it to be
1002        if indirect_function_table.size(store) < expected_table_length {
1003            let current_size = indirect_function_table.size(store);
1004            let delta = expected_table_length - current_size;
1005            trace!(?current_size, ?delta, "Growing indirect function table");
1006            indirect_function_table
1007                .grow(store, delta, Value::FuncRef(None))
1008                .map_err(LinkError::TableAllocationError)?;
1009        }
1010
1011        trace!(
1012            size = indirect_function_table.size(store),
1013            "Indirect function table initial size"
1014        );
1015
1016        // Give modules a non-zero memory base, since we don't want
1017        // any valid pointers to point to the zero address
1018        let memory_base = 2u64.pow(dylink_section.mem_info.memory_alignment);
1019
1020        let memory_type = main_module
1021            .imports()
1022            .memories()
1023            .filter_map(|t| {
1024                if t.name() == "memory" && t.module() == "env" {
1025                    Some(*t.ty())
1026                } else {
1027                    None
1028                }
1029            })
1030            .next()
1031            .ok_or(LinkError::MissingMainModuleImport("env.memory".to_string()))?;
1032
1033        let memory = match memory {
1034            Some(m) => m,
1035            None => Memory::new(store, memory_type)?,
1036        };
1037
1038        let stack_low = {
1039            let data_end = memory_base + dylink_section.mem_info.memory_size as u64;
1040            if !data_end.is_multiple_of(1024) {
1041                data_end + 1024 - (data_end % 1024)
1042            } else {
1043                data_end
1044            }
1045        };
1046
1047        if !stack_size.is_multiple_of(1024) {
1048            panic!("Stack size must be 1024-bit aligned");
1049        }
1050
1051        let stack_high = stack_low + stack_size;
1052
1053        // Allocate memory for the stack. This does not need to go through the memory allocator
1054        // because it's always placed directly after the main module's data
1055        memory.grow_at_least(store, stack_high)?;
1056
1057        trace!(
1058            memory_pages = ?memory.grow(store, 0).unwrap(),
1059            memory_base,
1060            stack_low,
1061            stack_high,
1062            "Memory layout"
1063        );
1064
1065        let stack_pointer_import = main_module
1066            .imports()
1067            .find(|i| i.module() == "env" && i.name() == "__stack_pointer")
1068            .ok_or(LinkError::MissingMainModuleImport(
1069                "__stack_pointer".to_string(),
1070            ))?;
1071
1072        let stack_pointer = define_integer_global_import(store, &stack_pointer_import, stack_high)?;
1073
1074        let c_longjmp = Tag::new(store, vec![Type::I32]);
1075        let cpp_exception = Tag::new(store, vec![Type::I32]);
1076
1077        let mut barrier_tx = Bus::new(1);
1078        let barrier_rx = barrier_tx.add_rx();
1079        let mut operation_tx = Bus::new(1);
1080        let operation_rx = operation_tx.add_rx();
1081
1082        let mut instance_group = InstanceGroupState {
1083            main_instance: None,
1084            // The TLS base for the main instance is determined by reading the
1085            // `__tls_base` global export from the instance after instantiation.
1086            main_instance_tls_base: None,
1087            side_instances: HashMap::new(),
1088            stack_pointer,
1089            memory: memory.clone(),
1090            indirect_function_table: indirect_function_table.clone(),
1091            c_longjmp,
1092            cpp_exception,
1093            recv_pending_operation_barrier: barrier_rx,
1094            recv_pending_operation: operation_rx,
1095        };
1096
1097        let mut linker_state = LinkerState {
1098            engine,
1099            main_module: main_module.clone(),
1100            main_module_dylink_info: dylink_section,
1101            main_module_memory_base: memory_base,
1102            side_modules: BTreeMap::new(),
1103            side_modules_by_name: HashMap::new(),
1104            next_module_handle: MAIN_MODULE_HANDLE.0 + 1,
1105            memory_allocator: MemoryAllocator::new(),
1106            allocated_closure_functions: BTreeMap::new(),
1107            available_closure_functions: Vec::new(),
1108            heap_base: stack_high,
1109            symbol_resolution_records: HashMap::new(),
1110            send_pending_operation_barrier: barrier_tx,
1111            send_pending_operation: operation_tx,
1112        };
1113
1114        let mut link_state = InProgressLinkState::default();
1115
1116        let well_known_imports = [
1117            ("env", "__memory_base", memory_base),
1118            ("env", "__table_base", MAIN_MODULE_TABLE_BASE),
1119            ("GOT.mem", "__stack_high", stack_high),
1120            ("GOT.mem", "__stack_low", stack_low),
1121            ("GOT.mem", "__heap_base", stack_high),
1122        ];
1123
1124        trace!("Resolving main module's symbols");
1125        linker_state.resolve_symbols(
1126            &instance_group,
1127            store,
1128            main_module,
1129            MAIN_MODULE_HANDLE,
1130            &mut link_state,
1131            &well_known_imports,
1132        )?;
1133
1134        trace!("Populating main module's imports object");
1135        instance_group.populate_imports_from_link_state(
1136            MAIN_MODULE_HANDLE,
1137            &mut linker_state,
1138            &mut link_state,
1139            store,
1140            main_module,
1141            &mut imports,
1142            &func_env.env,
1143            &well_known_imports,
1144        )?;
1145
1146        // TODO: figure out which way is faster (stubs in main or stubs in sides),
1147        // use that ordering. My *guess* is that, since main exports all the libc
1148        // functions and those are called frequently by basically any code, then giving
1149        // stubs to main will be faster, but we need numbers before we decide this.
1150        let main_instance = Instance::new(store, main_module, &imports)?;
1151        instance_group.main_instance = Some(main_instance.clone());
1152
1153        let tls_base = get_tls_base_export(&main_instance, store)?;
1154        instance_group.main_instance_tls_base = tls_base;
1155
1156        let runtime_path = linker_state.main_module_dylink_info.runtime_path.clone();
1157        for needed in linker_state.main_module_dylink_info.needed.clone() {
1158            // A successful load_module will add the module to the side_modules list,
1159            // from which symbols can be resolved in the following call to
1160            // guard.resolve_imports.
1161            trace!(name = needed, "Loading module needed by main");
1162            let wasi_env = func_env.data(store);
1163            linker_state.load_module_tree(
1164                DlModuleSpec::FileSystem {
1165                    module_spec: Path::new(needed.as_str()),
1166                    ld_library_path,
1167                },
1168                &mut link_state,
1169                &wasi_env.runtime,
1170                &wasi_env.state,
1171                runtime_path.as_ref(),
1172                // HACK: The main module doesn't have to exist in the virtual FS at all; e.g.
1173                // if one runs `wasmer ../module.wasm --dir .`, we won't have access to the
1174                // main module's folder within the virtual FS. This is why we're picking PWD
1175                // as the $ORIGIN of the main module, which should at least be slightly
1176                // sensible. The `main.wasm` file name will be stripped and only the `./`
1177                // will be taken into account by `locate_module`.
1178                Some(Path::new("./main.wasm")),
1179            )?;
1180        }
1181
1182        for module_handle in link_state
1183            .new_modules
1184            .iter()
1185            .map(|m| m.handle)
1186            .collect::<Vec<_>>()
1187        {
1188            trace!(?module_handle, "Instantiating module");
1189            instance_group.instantiate_side_module_from_link_state(
1190                &mut linker_state,
1191                store,
1192                &func_env.env,
1193                &mut link_state,
1194                module_handle,
1195            )?;
1196        }
1197
1198        let linker = Self {
1199            linker_state: Arc::new(RwLock::new(linker_state)),
1200            instance_group_state: Arc::new(Mutex::new(Some(instance_group))),
1201            dl_operation_pending: Arc::new(AtomicBool::new(false)),
1202        };
1203
1204        let stack_layout = WasiMemoryLayout {
1205            stack_lower: stack_low,
1206            stack_upper: stack_high,
1207            stack_size: stack_high - stack_low,
1208            guard_size: 0,
1209            tls_base,
1210        };
1211        let module_handles = WasiModuleTreeHandles::Dynamic {
1212            linker: linker.clone(),
1213            main_module_instance_handles: WasiModuleInstanceHandles::new(
1214                memory.clone(),
1215                store,
1216                main_instance.clone(),
1217                Some(indirect_function_table.clone()),
1218            ),
1219        };
1220
1221        func_env
1222            .initialize_handles_and_layout(
1223                store,
1224                main_instance.clone(),
1225                module_handles,
1226                Some(stack_layout),
1227                true,
1228            )
1229            .map_err(LinkError::MainModuleHandleInitFailed)?;
1230
1231        {
1232            trace!(?link_state, "Finalizing linking of main module");
1233
1234            let mut group_guard = linker.instance_group_state.lock().unwrap();
1235            let mut linker_state = linker.linker_state.write().unwrap();
1236
1237            let group_state = group_guard.as_mut().unwrap();
1238            group_state.finalize_pending_globals(
1239                &mut linker_state,
1240                store,
1241                &link_state.unresolved_globals,
1242            )?;
1243
1244            // The main module isn't added to the link state's list of new modules, so we need to
1245            // call its initialization functions separately
1246            trace!("Calling data relocator function for main module");
1247            call_initialization_function::<()>(&main_instance, store, "__wasm_apply_data_relocs")?;
1248            call_initialization_function::<()>(&main_instance, store, "__wasm_apply_tls_relocs")?;
1249
1250            linker.initialize_new_modules(group_guard, store, link_state)?;
1251        }
1252
1253        trace!("Calling main module's _initialize function");
1254        call_initialization_function::<()>(&main_instance, store, "_initialize")?;
1255
1256        trace!("Link complete");
1257
1258        Ok((
1259            linker,
1260            LinkedMainModule {
1261                instance: main_instance,
1262                memory,
1263                indirect_function_table,
1264                stack_low,
1265                stack_high,
1266            },
1267        ))
1268    }
1269
1270    pub fn create_instance_group(
1271        &self,
1272        parent_ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1273        store: &mut StoreMut<'_>,
1274        func_env: &mut WasiFunctionEnv,
1275    ) -> Result<(Self, LinkedMainModule), LinkError> {
1276        trace!("Spawning new instance group");
1277
1278        lock_instance_group_state!(
1279            parent_group_state_guard,
1280            parent_group_state,
1281            self,
1282            LinkError::InstanceGroupIsDead
1283        );
1284
1285        // Can't have other groups do operations that don't get replicated to
1286        // the new group, so lock the linker state while we work.
1287        write_linker_state!(linker_state, self, parent_group_state, parent_ctx);
1288
1289        let parent_store = parent_ctx.as_store_mut();
1290
1291        let main_module = linker_state.main_module.clone();
1292        let memory = parent_group_state
1293            .memory
1294            .share_in_store(&parent_store, store)?;
1295
1296        let mut imports = import_object_for_all_wasi_versions(&main_module, store, &func_env.env);
1297
1298        let indirect_function_table_type =
1299            parent_group_state.indirect_function_table.ty(&parent_store);
1300        trace!(
1301            minimum_size = ?indirect_function_table_type.minimum,
1302            "Creating indirect function table"
1303        );
1304
1305        let indirect_function_table =
1306            Table::new(store, indirect_function_table_type, Value::FuncRef(None))
1307                .map_err(LinkError::TableAllocationError)?;
1308
1309        let expected_table_length = parent_group_state
1310            .indirect_function_table
1311            .size(&parent_store);
1312        // Grow the table to be as big as the parent's
1313        if indirect_function_table.size(store) < expected_table_length {
1314            let current_size = indirect_function_table.size(store);
1315            let delta = expected_table_length - current_size;
1316            trace!(?current_size, ?delta, "Growing indirect function table");
1317            indirect_function_table
1318                .grow(store, delta, Value::FuncRef(None))
1319                .map_err(LinkError::TableAllocationError)?;
1320        }
1321
1322        trace!(
1323            size = indirect_function_table.size(store),
1324            "Indirect function table initial size"
1325        );
1326
1327        // Since threads initialize their own stack space, we can only rely on the layout being
1328        // initialized beforehand, which is the case with the thread_spawn syscall.
1329        // FIXME: this needs to become a parameter if we ever decouple the linker from WASIX
1330        let (stack_low, stack_high, tls_base) = {
1331            let layout = &func_env.env.as_ref(store).layout;
1332            (
1333                layout.stack_lower,
1334                layout.stack_upper,
1335                layout.tls_base.expect(
1336                    "tls_base must be set in memory layout of new instance group's main instance",
1337                ),
1338            )
1339        };
1340
1341        trace!(stack_low, stack_high, "Memory layout");
1342
1343        let stack_pointer_import = main_module
1344            .imports()
1345            .find(|i| i.module() == "env" && i.name() == "__stack_pointer")
1346            .ok_or(LinkError::MissingMainModuleImport(
1347                "__stack_pointer".to_string(),
1348            ))?;
1349
1350        // WASIX threads initialize their own stack pointer global in wasi_thread_start,
1351        // so no need to initialize it to a value here.
1352        let stack_pointer = define_integer_global_import(store, &stack_pointer_import, 0)?;
1353
1354        let c_longjmp = Tag::new(store, vec![Type::I32]);
1355        let cpp_exception = Tag::new(store, vec![Type::I32]);
1356
1357        let barrier_rx = linker_state.send_pending_operation_barrier.add_rx();
1358        let operation_rx = linker_state.send_pending_operation.add_rx();
1359
1360        let mut instance_group = InstanceGroupState {
1361            main_instance: None,
1362            main_instance_tls_base: Some(tls_base),
1363            side_instances: HashMap::new(),
1364            stack_pointer,
1365            memory: memory.clone(),
1366            indirect_function_table: indirect_function_table.clone(),
1367            c_longjmp,
1368            cpp_exception,
1369            recv_pending_operation_barrier: barrier_rx,
1370            recv_pending_operation: operation_rx,
1371        };
1372
1373        let mut pending_resolutions = PendingResolutionsFromLinker::default();
1374
1375        let well_known_imports = [
1376            ("env", "__memory_base", linker_state.main_module_memory_base),
1377            ("env", "__table_base", MAIN_MODULE_TABLE_BASE),
1378            ("GOT.mem", "__stack_high", stack_high),
1379            ("GOT.mem", "__stack_low", stack_low),
1380            ("GOT.mem", "__heap_base", linker_state.heap_base),
1381        ];
1382
1383        trace!("Populating imports object for new instance group's main instance");
1384        instance_group.populate_imports_from_linker(
1385            MAIN_MODULE_HANDLE,
1386            &linker_state,
1387            store,
1388            &main_module,
1389            &mut imports,
1390            &func_env.env,
1391            &well_known_imports,
1392            &mut pending_resolutions,
1393        )?;
1394
1395        let main_instance = Instance::new(store, &main_module, &imports)?;
1396
1397        instance_group.main_instance = Some(main_instance.clone());
1398
1399        for side in &linker_state.side_modules {
1400            trace!(module_handle = ?side.0, "Instantiating existing side module");
1401            instance_group.instantiate_side_module_from_linker(
1402                &linker_state,
1403                store,
1404                &func_env.env,
1405                *side.0,
1406                &mut pending_resolutions,
1407            )?;
1408        }
1409
1410        trace!("Finalizing pending functions");
1411        instance_group.finalize_pending_resolutions_from_linker(&pending_resolutions, store)?;
1412
1413        trace!("Applying externally-requested function table entries");
1414        instance_group.apply_requested_symbols_from_linker(store, &linker_state)?;
1415
1416        let linker = Self {
1417            linker_state: self.linker_state.clone(),
1418            instance_group_state: Arc::new(Mutex::new(Some(instance_group))),
1419            dl_operation_pending: self.dl_operation_pending.clone(),
1420        };
1421
1422        let module_handles = WasiModuleTreeHandles::Dynamic {
1423            linker: linker.clone(),
1424            main_module_instance_handles: WasiModuleInstanceHandles::new(
1425                memory.clone(),
1426                store,
1427                main_instance.clone(),
1428                Some(indirect_function_table.clone()),
1429            ),
1430        };
1431
1432        func_env
1433            .initialize_handles_and_layout(
1434                store,
1435                main_instance.clone(),
1436                module_handles,
1437                None,
1438                false,
1439            )
1440            .map_err(LinkError::MainModuleHandleInitFailed)?;
1441
1442        trace!("Instance group spawned successfully");
1443
1444        Ok((
1445            linker,
1446            LinkedMainModule {
1447                instance: main_instance,
1448                memory,
1449                indirect_function_table,
1450                stack_low,
1451                stack_high,
1452            },
1453        ))
1454    }
1455
1456    pub fn shutdown_instance_group(
1457        &self,
1458        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1459    ) -> Result<(), LinkError> {
1460        trace!("Shutting instance group down");
1461
1462        let mut guard = self.instance_group_state.lock().unwrap();
1463        match guard.as_mut() {
1464            None => Ok(()),
1465            Some(group_state) => {
1466                // We need to do this even if the results of an incoming dl op will be thrown away;
1467                // this is because the instigating group will have counted us and we need to hit the
1468                // barrier twice to unblock everybody else.
1469                write_linker_state!(linker_state, self, group_state, ctx);
1470                guard.take();
1471                drop(linker_state);
1472
1473                trace!("Instance group shut down");
1474
1475                Ok(())
1476            }
1477        }
1478    }
1479
1480    /// Allocate a index for a closure in the indirect function table
1481    pub fn allocate_closure_index(
1482        &self,
1483        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1484    ) -> Result<u32, LinkError> {
1485        lock_instance_group_state!(
1486            group_state_guard,
1487            group_state,
1488            self,
1489            LinkError::InstanceGroupIsDead
1490        );
1491        write_linker_state!(linker_state, self, group_state, ctx);
1492
1493        let mut store = ctx.as_store_mut();
1494
1495        // Use a previously allocated slot if possible
1496        if let Some(function_index) = linker_state.available_closure_functions.pop() {
1497            linker_state
1498                .allocated_closure_functions
1499                .insert(function_index, true);
1500            return Ok(function_index);
1501        }
1502
1503        // Allocate more closures than we need to reduce the number of sync operations
1504        const CLOSURE_ALLOCATION_SIZE: u32 = 100;
1505
1506        let function_index = group_state
1507            .allocate_function_table(&mut store, CLOSURE_ALLOCATION_SIZE, 0)
1508            .map_err(LinkError::TableAllocationError)? as u32;
1509
1510        linker_state
1511            .available_closure_functions
1512            .reserve(CLOSURE_ALLOCATION_SIZE as usize - 1);
1513        for i in 1..CLOSURE_ALLOCATION_SIZE {
1514            linker_state
1515                .available_closure_functions
1516                .push(function_index + i);
1517            linker_state
1518                .allocated_closure_functions
1519                .insert(function_index + i, false);
1520        }
1521        linker_state
1522            .allocated_closure_functions
1523            .insert(function_index, true);
1524
1525        self.synchronize_link_operation(
1526            DlOperation::AllocateFunctionTable {
1527                index: function_index,
1528                size: CLOSURE_ALLOCATION_SIZE,
1529            },
1530            linker_state,
1531            group_state,
1532            &ctx.data().process,
1533            ctx.data().tid(),
1534        );
1535
1536        Ok(function_index)
1537    }
1538
1539    /// Remove a previously allocated slot for a closure in the indirect function table
1540    ///
1541    /// After calling this it is undefined behavior to call the function at the given index.
1542    pub fn free_closure_index(
1543        &self,
1544        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1545        function_id: u32,
1546    ) -> Result<(), LinkError> {
1547        lock_instance_group_state!(
1548            group_state_guard,
1549            group_state,
1550            self,
1551            LinkError::InstanceGroupIsDead
1552        );
1553        write_linker_state!(linker_state, self, group_state, ctx);
1554
1555        let Some(entry) = linker_state
1556            .allocated_closure_functions
1557            .get_mut(&function_id)
1558        else {
1559            // Not allocated
1560            return Ok(());
1561        };
1562        if !*entry {
1563            // Not used
1564            return Ok(());
1565        }
1566
1567        *entry = false;
1568        linker_state.available_closure_functions.push(function_id);
1569        Ok(())
1570    }
1571
1572    /// Check if an indirect_function_table entry is reserved for closures.
1573    /// Returns false if the entry is not reserved for closures.
1574    /// Requires a FunctionEnvMut because pending DL operations should always
1575    /// be processed before acquiring any lock on the linker.
1576    // TODO: we can cache this information within the group state so we don't
1577    // need a write lock on the linker state here
1578    pub fn is_closure(
1579        &self,
1580        function_id: u32,
1581        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1582    ) -> Result<bool, LinkError> {
1583        // If we can get a read lock on the linker state, do it
1584        if let Ok(linker_state) = self.linker_state.try_read() {
1585            return Ok(linker_state
1586                .allocated_closure_functions
1587                .contains_key(&function_id));
1588        }
1589
1590        // Otherwise, fall back to the path where we apply DL ops and acquire
1591        // a write lock afterwards
1592        lock_instance_group_state!(
1593            group_state_guard,
1594            group_state,
1595            self,
1596            LinkError::InstanceGroupIsDead
1597        );
1598        write_linker_state!(linker_state, self, group_state, ctx);
1599        Ok(linker_state
1600            .allocated_closure_functions
1601            .contains_key(&function_id))
1602    }
1603
1604    /// Loads a side module from the given path, linking it against the existing module tree
1605    /// and instantiating it. Symbols from the module can then be retrieved by calling
1606    /// [`Linker::resolve_export`].
1607    pub fn load_module(
1608        &self,
1609        module_spec: DlModuleSpec,
1610        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1611    ) -> Result<ModuleHandle, LinkError> {
1612        trace!(?module_spec, "Loading module");
1613
1614        lock_instance_group_state!(
1615            group_state_guard,
1616            group_state,
1617            self,
1618            LinkError::InstanceGroupIsDead
1619        );
1620
1621        // TODO: differentiate between an actual link error and an error that occurs as the
1622        // result of a pending operation that needs to be applied first. Currently, errors
1623        // from pending ops are treated as link errors and just reported to guest code rather
1624        // than terminating the process.
1625        write_linker_state!(linker_state, self, group_state, ctx);
1626
1627        let mut link_state = InProgressLinkState::default();
1628        let env = ctx.as_ref();
1629        let mut store = ctx.as_store_mut();
1630
1631        trace!("Loading module tree for requested module");
1632        let wasi_env = env.as_ref(&store);
1633        let runtime_path: &[String] = &[];
1634        let module_handle = linker_state.load_module_tree(
1635            module_spec,
1636            &mut link_state,
1637            &wasi_env.runtime,
1638            &wasi_env.state,
1639            runtime_path,          // No runtime path when loading a module via dlopen
1640            Option::<&Path>::None, // Empty runtime path means we don't need the module's path either
1641        )?;
1642
1643        let new_modules = link_state
1644            .new_modules
1645            .iter()
1646            .map(|m| m.handle)
1647            .collect::<Vec<_>>();
1648
1649        for handle in &new_modules {
1650            trace!(?module_handle, "Instantiating module");
1651            group_state.instantiate_side_module_from_link_state(
1652                &mut linker_state,
1653                &mut store,
1654                &env,
1655                &mut link_state,
1656                *handle,
1657            )?;
1658        }
1659
1660        trace!("Finalizing link");
1661        self.finalize_link_operation(group_state_guard, &mut linker_state, &mut store, link_state)?;
1662
1663        if !new_modules.is_empty() {
1664            // The group state is unlocked for stub functions, now lock it again
1665            lock_instance_group_state!(
1666                group_state_guard,
1667                group_state,
1668                self,
1669                LinkError::InstanceGroupIsDead
1670            );
1671
1672            self.synchronize_link_operation(
1673                DlOperation::LoadModules(new_modules),
1674                linker_state,
1675                group_state,
1676                &ctx.data().process,
1677                ctx.data().tid(),
1678            );
1679        }
1680
1681        // FIXME: If we fail at an intermediate step, we should reset the linker's state, a la:
1682        // if result.is_err() {
1683        //     let mut guard = self.state.lock().unwrap();
1684        //     let memory = guard.memory.clone();
1685
1686        //     for module_handle in link_state.module_handles.iter().cloned() {
1687        //         let module = guard.side_modules.remove(&module_handle).unwrap();
1688        //         guard
1689        //             .side_module_names
1690        //             .retain(|_, handle| *handle != module_handle);
1691        //         // We already have an error we need to report, so ignore memory deallocation errors
1692        //         _ = guard
1693        //             .memory_allocator
1694        //             .deallocate(&memory, store, module.memory_base);
1695        //     }
1696        // }
1697
1698        trace!("Module load complete");
1699
1700        Ok(module_handle)
1701    }
1702
1703    fn finalize_link_operation(
1704        &self,
1705        // Take ownership of the guard and drop it ourselves to ensure no deadlock can happen
1706        mut group_state_guard: MutexGuard<'_, Option<InstanceGroupState>>,
1707        linker_state: &mut LinkerState,
1708        store: &mut impl AsStoreMut,
1709        link_state: InProgressLinkState,
1710    ) -> Result<(), LinkError> {
1711        let group_state = group_state_guard.as_mut().unwrap();
1712
1713        trace!(?link_state, "Finalizing link operation");
1714
1715        group_state.finalize_pending_globals(
1716            linker_state,
1717            store,
1718            &link_state.unresolved_globals,
1719        )?;
1720
1721        self.initialize_new_modules(group_state_guard, store, link_state)
1722    }
1723
1724    fn initialize_new_modules(
1725        &self,
1726        // Take ownership of the guard and drop it ourselves to ensure no deadlock can happen
1727        mut group_state_guard: MutexGuard<'_, Option<InstanceGroupState>>,
1728        store: &mut impl AsStoreMut,
1729        link_state: InProgressLinkState,
1730    ) -> Result<(), LinkError> {
1731        let group_state = group_state_guard.as_mut().unwrap();
1732
1733        let new_instances = link_state
1734            .new_modules
1735            .iter()
1736            .map(|m| group_state.side_instances[&m.handle].instance.clone())
1737            .collect::<Vec<_>>();
1738
1739        // The instance group must be unlocked for the next step, since modules may need to resolve
1740        // stub functions and that requires a lock on the instance group's state
1741        drop(group_state_guard);
1742
1743        // These functions are exported from PIE executables, and need to be run before calling
1744        // _initialize or _start. More info:
1745        // https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
1746        trace!("Calling data relocation functions");
1747        for instance in &new_instances {
1748            call_initialization_function::<()>(instance, store, "__wasm_apply_data_relocs")?;
1749            call_initialization_function::<()>(instance, store, "__wasm_apply_tls_relocs")?;
1750        }
1751
1752        trace!("Calling ctor functions");
1753        for instance in &new_instances {
1754            call_initialization_function::<()>(instance, store, "__wasm_call_ctors")?;
1755        }
1756
1757        Ok(())
1758    }
1759
1760    // TODO: Support RTLD_NEXT
1761    /// Resolves an export from the module corresponding to the given module handle.
1762    /// Only functions and globals can be resolved.
1763    ///
1764    /// If the symbol is a global, the returned value will be the absolute address of
1765    /// the data corresponding to that global within the shared linear memory.
1766    ///
1767    /// If it's a function, it'll be placed into the indirect function table,
1768    /// which creates a "function pointer" that can be used from WASM code.
1769    pub fn resolve_export(
1770        &self,
1771        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1772        module_handle: Option<ModuleHandle>,
1773        symbol: &str,
1774    ) -> Result<ResolvedExport, ResolveError> {
1775        trace!(?module_handle, symbol, "Resolving symbol");
1776
1777        let resolution_key = SymbolResolutionKey::Requested {
1778            resolve_from: module_handle,
1779            name: symbol.to_string(),
1780        };
1781
1782        lock_instance_group_state!(guard, group_state, self, ResolveError::InstanceGroupIsDead);
1783
1784        if let Ok(linker_state) = self.linker_state.try_read()
1785            && let Some(resolution) = linker_state.symbol_resolution_records.get(&resolution_key)
1786        {
1787            trace!(?resolution, "Already have a resolution for this symbol");
1788            match resolution {
1789                SymbolResolutionResult::FunctionPointer {
1790                    function_table_index: addr,
1791                    ..
1792                } => {
1793                    return Ok(ResolvedExport::Function {
1794                        func_ptr: *addr as u64,
1795                    });
1796                }
1797                SymbolResolutionResult::Memory(addr) => {
1798                    return Ok(ResolvedExport::Global { data_ptr: *addr });
1799                }
1800                SymbolResolutionResult::Tls {
1801                    resolved_from,
1802                    offset,
1803                } => {
1804                    let Some(tls_base) = group_state.tls_base(*resolved_from) else {
1805                        return Err(ResolveError::NoTlsBaseGlobalExport);
1806                    };
1807                    return Ok(ResolvedExport::Global {
1808                        data_ptr: tls_base + offset,
1809                    });
1810                }
1811                r => panic!(
1812                    "Internal error: unexpected symbol resolution \
1813                        {r:?} for requested symbol {symbol}"
1814                ),
1815            }
1816        }
1817
1818        write_linker_state!(linker_state, self, group_state, ctx);
1819
1820        let mut store = ctx.as_store_mut();
1821
1822        trace!("Resolving export");
1823        let (export, resolved_from) =
1824            group_state.resolve_export(&linker_state, &mut store, module_handle, symbol, false)?;
1825
1826        trace!(?export, ?resolved_from, "Resolved export");
1827
1828        match export {
1829            PartiallyResolvedExport::Global(addr) => {
1830                linker_state
1831                    .symbol_resolution_records
1832                    .insert(resolution_key, SymbolResolutionResult::Memory(addr));
1833
1834                Ok(ResolvedExport::Global { data_ptr: addr })
1835            }
1836            PartiallyResolvedExport::Tls { offset, final_addr } => {
1837                linker_state.symbol_resolution_records.insert(
1838                    resolution_key,
1839                    SymbolResolutionResult::Tls {
1840                        resolved_from,
1841                        offset,
1842                    },
1843                );
1844
1845                Ok(ResolvedExport::Global {
1846                    data_ptr: final_addr,
1847                })
1848            }
1849            PartiallyResolvedExport::Function(func) => {
1850                let func_ptr = group_state
1851                    .append_to_function_table(&mut store, func.clone())
1852                    .map_err(ResolveError::TableAllocationError)?;
1853                trace!(
1854                    ?func_ptr,
1855                    table_size = group_state.indirect_function_table.size(&store),
1856                    "Placed resolved function into table"
1857                );
1858                linker_state.symbol_resolution_records.insert(
1859                    resolution_key,
1860                    SymbolResolutionResult::FunctionPointer {
1861                        resolved_from,
1862                        function_table_index: func_ptr,
1863                    },
1864                );
1865
1866                self.synchronize_link_operation(
1867                    DlOperation::ResolveFunction {
1868                        name: symbol.to_string(),
1869                        resolved_from,
1870                        function_table_index: func_ptr,
1871                    },
1872                    linker_state,
1873                    group_state,
1874                    &ctx.data().process,
1875                    ctx.data().tid(),
1876                );
1877
1878                Ok(ResolvedExport::Function {
1879                    func_ptr: func_ptr as u64,
1880                })
1881            }
1882        }
1883    }
1884
1885    pub fn is_handle_valid(
1886        &self,
1887        handle: ModuleHandle,
1888        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1889    ) -> Result<bool, LinkError> {
1890        // Remember, trying to get a read lock here can deadlock if a dl op is pending
1891        lock_instance_group_state!(guard, group_state, self, LinkError::InstanceGroupIsDead);
1892        write_linker_state!(linker_state, self, group_state, ctx);
1893        Ok(linker_state.side_modules.contains_key(&handle))
1894    }
1895
1896    // Note: the caller needs to have applied the link operation beforehand to ensure
1897    // there are no (recoverable) errors. This function can only have unrecoverable
1898    // errors (i.e. panics).
1899    fn synchronize_link_operation(
1900        &self,
1901        op: DlOperation,
1902        mut linker_state_write_lock: RwLockWriteGuard<LinkerState>,
1903        group_state: &mut InstanceGroupState,
1904        wasi_process: &WasiProcess,
1905        self_thread_id: WasiThreadId,
1906    ) {
1907        trace!(?op, "Synchronizing link operation");
1908
1909        let num_groups = linker_state_write_lock.send_pending_operation.rx_count();
1910
1911        if num_groups <= 1 {
1912            trace!("No other living instance groups, nothing to do");
1913            return;
1914        }
1915
1916        // Create and broadcast the barrier, so we have a rendezvous point
1917        let barrier = Arc::new(Barrier::new(num_groups));
1918        if linker_state_write_lock
1919            .send_pending_operation_barrier
1920            .try_broadcast(barrier.clone())
1921            .is_err()
1922        {
1923            // The bus is given a capacity of one to ensure we can't ever get here
1924            // more than once concurrently.
1925            panic!("Internal error: more than one synchronized link operation active")
1926        }
1927
1928        // Set the flag, so others know they should stop now
1929        self.dl_operation_pending.store(true, Ordering::SeqCst);
1930
1931        trace!("Signalling wasix threads to wake up");
1932        for thread in wasi_process
1933            .all_threads()
1934            .into_iter()
1935            .filter(|tid| *tid != self_thread_id)
1936        {
1937            // Signal all threads to wake them up if they're sleeping or idle
1938            wasi_process.signal_thread(&thread, wasmer_wasix_types::wasi::Signal::Sigwakeup);
1939        }
1940
1941        trace!("Waiting at barrier");
1942        // Wait for all other threads to hit the barrier
1943        barrier.wait();
1944
1945        trace!("All threads now processing dl op");
1946
1947        // Reset the flag once everybody's seen it
1948        self.dl_operation_pending.store(false, Ordering::SeqCst);
1949
1950        // Now we broadcast the actual operation. This has to happen before
1951        // we release the write lock, since exclusive access to the bus is
1952        // required.
1953        if linker_state_write_lock
1954            .send_pending_operation
1955            .try_broadcast(op.clone())
1956            .is_err()
1957        {
1958            // Same deal with the bus capacity
1959            panic!("Internal error: more than one synchronized link operation active")
1960        }
1961
1962        // Now that everyone's at a safe point, we can unlock the shared state
1963        // and take another read lock. This is safe because everybody else will
1964        // also be taking only a read lock between the two barrier waits, and
1965        // no write locks can happen.
1966        trace!("Unlocking linker state");
1967        drop(linker_state_write_lock);
1968        let linker_state_read_lock = self.linker_state.read().unwrap();
1969
1970        // Read and drop the barrier and operation from our own receivers, so
1971        // the bus is freed up
1972        _ = group_state.recv_pending_operation_barrier.recv().unwrap();
1973        _ = group_state.recv_pending_operation.recv().unwrap();
1974
1975        // Second barrier, to make sure everyone applied the change. Necessary
1976        // because another thread may exit do_pending_link_operations and acquire
1977        // a write lock before anybody else has had the chance to get a read lock
1978        // without this wait in place.
1979        trace!("Waiting for other threads to finish processing the dl op");
1980        barrier.wait();
1981
1982        // Drop the read lock after everyone is done.
1983        drop(linker_state_read_lock);
1984
1985        trace!("Synchronization complete");
1986    }
1987
1988    pub(crate) fn do_pending_link_operations(
1989        &self,
1990        ctx: &mut FunctionEnvMut<'_, WasiEnv>,
1991        fast: bool,
1992    ) -> Result<(), LinkError> {
1993        // If no operation is pending, we can return immediately. This is the
1994        // hot path. If we happen to miss an operation that we would have
1995        // caught, no big deal; this will be called again later. However,
1996        // in the case where we raise a signal and it's caught by another thread,
1997        // we can't have this read go missing, otherwise the other thread will
1998        // sleep again and miss the notification. Hence the option to pick fast or
1999        // slow.
2000        if !self.dl_operation_pending.load(if fast {
2001            Ordering::Relaxed
2002        } else {
2003            Ordering::SeqCst
2004        }) {
2005            return Ok(());
2006        }
2007
2008        lock_instance_group_state!(guard, group_state, self, LinkError::InstanceGroupIsDead);
2009
2010        let env = ctx.as_ref();
2011        let mut store = ctx.as_store_mut();
2012        self.do_pending_link_operations_internal(group_state, &mut store, &env)
2013    }
2014
2015    fn do_pending_link_operations_internal(
2016        &self,
2017        group_state: &mut InstanceGroupState,
2018        store: &mut impl AsStoreMut,
2019        env: &FunctionEnv<WasiEnv>,
2020    ) -> Result<(), LinkError> {
2021        if !self.dl_operation_pending.load(Ordering::SeqCst) {
2022            return Ok(());
2023        }
2024
2025        trace!("Pending link operation discovered, will process");
2026
2027        // Receive and wait for the barrier.
2028        let barrier = group_state.recv_pending_operation_barrier.recv().expect(
2029            "Failed to receive barrier while a DL operation was \
2030            in progress; this condition can't be recovered from",
2031        );
2032        barrier.wait();
2033
2034        trace!("Past the barrier, now processing operation");
2035
2036        // After everyone, including the instigating group has rendezvoused at
2037        // the first barrier, the operation should have been broadcast.
2038        let op = group_state.recv_pending_operation.recv().unwrap();
2039        // Once past the barrier, the instigating group will downgrade its
2040        // lock to a read lock, so we can also get a read lock here.
2041        let linker_state = self.linker_state.read().unwrap();
2042
2043        let result = group_state.apply_dl_operation(linker_state.deref(), op, store, env);
2044
2045        trace!("Operation applied, now waiting at second barrier");
2046
2047        // Rendezvous one more time to make sure everybody's done, and nobody's
2048        // going to start another DL operation before that happens.
2049        barrier.wait();
2050        // Drop the read lock after the
2051        drop(linker_state);
2052
2053        trace!("Pending link operation applied successfully");
2054
2055        result
2056    }
2057}
2058
2059impl LinkerState {
2060    fn allocate_memory(
2061        &mut self,
2062        store: &mut impl AsStoreMut,
2063        memory: &Memory,
2064        mem_info: &wasmparser::MemInfo,
2065    ) -> Result<u64, MemoryError> {
2066        trace!(?mem_info, "Allocating memory");
2067
2068        let new_size = if mem_info.memory_size == 0 {
2069            0
2070        } else {
2071            self.memory_allocator.allocate(
2072                memory,
2073                store,
2074                mem_info.memory_size,
2075                2_u32.pow(mem_info.memory_alignment),
2076            )? as u64
2077        };
2078
2079        trace!(new_size, "Final size");
2080
2081        Ok(new_size)
2082    }
2083
2084    fn memory_base(&self, module_handle: ModuleHandle) -> u64 {
2085        if module_handle == MAIN_MODULE_HANDLE {
2086            self.main_module_memory_base
2087        } else {
2088            self.side_modules
2089                .get(&module_handle)
2090                .expect("Internal error: bad module handle")
2091                .memory_base
2092        }
2093    }
2094
2095    fn dylink_info(&self, module_handle: ModuleHandle) -> &DylinkInfo {
2096        if module_handle == MAIN_MODULE_HANDLE {
2097            &self.main_module_dylink_info
2098        } else {
2099            &self
2100                .side_modules
2101                .get(&module_handle)
2102                .expect("Internal error: bad module handle")
2103                .dylink_info
2104        }
2105    }
2106
2107    // Resolves all imports for the given module, and places the results into
2108    // the in progress link state's symbol collection.
2109    // A follow-up call to [`InstanceGroupState::populate_imports_from_link_state`]
2110    // is needed to create a usable imports object, which needs to happen once per
2111    // instance group.
2112    // Each instance group has a different store, so the group ID corresponding
2113    // to the given store must be provided to resolve globals from the correct
2114    // instances.
2115    fn resolve_symbols(
2116        &self,
2117        group: &InstanceGroupState,
2118        store: &mut impl AsStoreMut,
2119        module: &Module,
2120        module_handle: ModuleHandle,
2121        link_state: &mut InProgressLinkState,
2122        // Used only to "skip over" well known imports, so we don't actually need the
2123        // u64 values. However, we use the same type as populate_imports to let calling
2124        // code construct the data only once.
2125        well_known_imports: &[(&str, &str, u64)],
2126    ) -> Result<(), LinkError> {
2127        trace!(?module_handle, "Resolving symbols");
2128        for import in module.imports() {
2129            // Skip over well known imports, since they'll be provided externally
2130            if well_known_imports
2131                .iter()
2132                .any(|i| i.0 == import.module() && i.1 == import.name())
2133            {
2134                trace!(?import, "Skipping resolution of well-known symbol");
2135                continue;
2136            }
2137
2138            // Skip over the memory, function table and stack pointer imports as well
2139            match import.name() {
2140                "memory"
2141                | "__indirect_function_table"
2142                | "__stack_pointer"
2143                | "__c_longjmp"
2144                | "__cpp_exception" => {
2145                    trace!(?import, "Skipping resolution of special symbol");
2146                    continue;
2147                }
2148                _ => (),
2149            }
2150
2151            match import.module() {
2152                "env" => {
2153                    let resolution = self.resolve_env_symbol(group, &import, store)?;
2154                    trace!(?import, ?resolution, "Symbol resolved");
2155                    link_state.symbols.insert(
2156                        NeededSymbolResolutionKey {
2157                            module_handle,
2158                            import_module: "env".to_owned(),
2159                            import_name: import.name().to_string(),
2160                        },
2161                        resolution,
2162                    );
2163                }
2164                "GOT.mem" => {
2165                    let resolution = self.resolve_got_mem_symbol(group, &import, store)?;
2166                    trace!(?import, ?resolution, "Symbol resolved");
2167                    link_state.symbols.insert(
2168                        NeededSymbolResolutionKey {
2169                            module_handle,
2170                            import_module: "GOT.mem".to_owned(),
2171                            import_name: import.name().to_string(),
2172                        },
2173                        resolution,
2174                    );
2175                }
2176                "GOT.func" => {
2177                    let resolution = self.resolve_got_func_symbol(group, &import, store)?;
2178                    trace!(?import, ?resolution, "Symbol resolved");
2179                    link_state.symbols.insert(
2180                        NeededSymbolResolutionKey {
2181                            module_handle,
2182                            import_module: "GOT.func".to_owned(),
2183                            import_name: import.name().to_string(),
2184                        },
2185                        resolution,
2186                    );
2187                }
2188                _ => (),
2189            }
2190        }
2191
2192        trace!(?module_handle, "All symbols resolved");
2193
2194        Ok(())
2195    }
2196
2197    // Imports from the env module are:
2198    //   * the memory and indirect function table
2199    //   * well-known addresses, such as __stack_pointer and __memory_base
2200    //   * functions that are imported directly
2201    // resolve_env_symbol only handles the imported functions.
2202    fn resolve_env_symbol(
2203        &self,
2204        group: &InstanceGroupState,
2205        import: &ImportType,
2206        store: &impl AsStoreRef,
2207    ) -> Result<InProgressSymbolResolution, LinkError> {
2208        let ExternType::Function(import_func_ty) = import.ty() else {
2209            return Err(LinkError::ImportMustBeFunction(
2210                "env",
2211                import.name().to_string(),
2212            ));
2213        };
2214
2215        let export = group.resolve_exported_symbol(import.name());
2216
2217        match export {
2218            Some((module_handle, export)) => {
2219                let Extern::Function(export_func) = export else {
2220                    return Err(LinkError::ImportTypeMismatch(
2221                        "env".to_string(),
2222                        import.name().to_string(),
2223                        ExternType::Function(import_func_ty.clone()),
2224                        export.ty(store).clone(),
2225                    ));
2226                };
2227
2228                if export_func.ty(store) != *import_func_ty {
2229                    return Err(LinkError::ImportTypeMismatch(
2230                        "env".to_string(),
2231                        import.name().to_string(),
2232                        ExternType::Function(import_func_ty.clone()),
2233                        export.ty(store).clone(),
2234                    ));
2235                }
2236
2237                Ok(InProgressSymbolResolution::Function(module_handle))
2238            }
2239            None => {
2240                // The function may be exported from a module we have yet to link in,
2241                // or otherwise not be used by the module at all. We provide a stub that,
2242                // when called, will try to resolve the symbol and call it. This lets
2243                // us resolve circular dependencies, as well as letting modules that don't
2244                // actually use their imports run successfully.
2245                Ok(InProgressSymbolResolution::StubFunction(
2246                    import_func_ty.clone(),
2247                ))
2248            }
2249        }
2250    }
2251
2252    // "Global" imports (i.e. imports from GOT.mem and GOT.func) are integer globals.
2253    // GOT.mem imports should point to the address of another module's data.
2254    fn resolve_got_mem_symbol(
2255        &self,
2256        group: &InstanceGroupState,
2257        import: &ImportType,
2258        store: &impl AsStoreRef,
2259    ) -> Result<InProgressSymbolResolution, LinkError> {
2260        let global_type = get_integer_global_type_from_import(import)?;
2261
2262        match group.resolve_exported_symbol(import.name()) {
2263            Some((module_handle, export)) => {
2264                let ExternType::Global(global_type) = export.ty(store) else {
2265                    return Err(LinkError::ImportTypeMismatch(
2266                        "GOT.mem".to_string(),
2267                        import.name().to_string(),
2268                        ExternType::Global(global_type),
2269                        export.ty(store).clone(),
2270                    ));
2271                };
2272
2273                if !matches!(global_type.ty, Type::I32 | Type::I64) {
2274                    return Err(LinkError::ImportTypeMismatch(
2275                        "GOT.mem".to_string(),
2276                        import.name().to_string(),
2277                        ExternType::Global(global_type),
2278                        export.ty(store).clone(),
2279                    ));
2280                }
2281
2282                Ok(InProgressSymbolResolution::MemGlobal(module_handle))
2283            }
2284            None => Ok(InProgressSymbolResolution::UnresolvedMemGlobal),
2285        }
2286    }
2287
2288    // "Global" imports (i.e. imports from GOT.mem and GOT.func) are integer globals.
2289    // GOT.func imports are function pointers (i.e. indices into the indirect function
2290    // table).
2291    fn resolve_got_func_symbol(
2292        &self,
2293        group: &InstanceGroupState,
2294        import: &ImportType,
2295        store: &impl AsStoreRef,
2296    ) -> Result<InProgressSymbolResolution, LinkError> {
2297        // Ensure the global is the correct type (i32 or i64)
2298        let _ = get_integer_global_type_from_import(import)?;
2299
2300        match group.resolve_exported_symbol(import.name()) {
2301            Some((module_handle, export)) => {
2302                let ExternType::Function(_) = export.ty(store) else {
2303                    return Err(LinkError::ExportMustBeFunction(
2304                        import.name().to_string(),
2305                        export.ty(store).clone(),
2306                    ));
2307                };
2308
2309                Ok(InProgressSymbolResolution::FuncGlobal(module_handle))
2310            }
2311            None => Ok(InProgressSymbolResolution::UnresolvedFuncGlobal),
2312        }
2313    }
2314
2315    // TODO: give loaded library a different wasi env that specifies its module handle
2316    // This function loads the module (and its needed modules) and puts the resulting `Module`s
2317    // in the linker state, while assigning handles and putting the handles in the in-progress
2318    // link state. The modules must then get their symbols resolved and be instantiated in the
2319    // order in which their handles exist in the link state.
2320    // Returns the handle of the originally requested module. This will be the last entry in
2321    // the link state's list of module handles, but only if the module was actually loaded; if
2322    // it was already loaded, the existing handle is returned.
2323    fn load_module_tree(
2324        &mut self,
2325        module_spec: DlModuleSpec,
2326        link_state: &mut InProgressLinkState,
2327        runtime: &Arc<dyn Runtime + Send + Sync + 'static>,
2328        wasi_state: &WasiState,
2329        runtime_path: &[impl AsRef<str>],
2330        calling_module_path: Option<impl AsRef<Path>>,
2331    ) -> Result<ModuleHandle, LinkError> {
2332        let module_name = match module_spec {
2333            DlModuleSpec::FileSystem { module_spec, .. } => Cow::Borrowed(module_spec),
2334            DlModuleSpec::Memory { module_name, .. } => {
2335                Cow::Owned(PathBuf::from(format!("::in-memory::{module_name}")))
2336            }
2337        };
2338        trace!(?module_name, "Locating and loading module");
2339
2340        if let Some(handle) = self.side_modules_by_name.get(module_name.as_ref()) {
2341            let handle = *handle;
2342
2343            trace!(?module_name, ?handle, "Module was already loaded");
2344
2345            return Ok(handle);
2346        }
2347
2348        // Locate and load the module bytes
2349        let (module_data, paths) = match module_spec {
2350            DlModuleSpec::FileSystem {
2351                module_spec,
2352                ld_library_path,
2353            } => {
2354                let (full_path, bytes) = block_on(locate_module(
2355                    module_spec,
2356                    ld_library_path,
2357                    runtime_path,
2358                    calling_module_path,
2359                    &wasi_state.fs,
2360                ))?;
2361                // TODO: this can be optimized by detecting early if the module is already
2362                // pending without loading its bytes
2363                if link_state.pending_module_paths.contains(&full_path) {
2364                    trace!("Module is already pending, won't load again");
2365                    // This is fine, since a non-empty pending_modules list means we are
2366                    // recursively resolving needed modules. We don't use the handle
2367                    // returned from this function for anything when running recursively
2368                    // (see self.load_module call below).
2369                    return Ok(INVALID_MODULE_HANDLE);
2370                }
2371
2372                (
2373                    HashedModuleData::new(bytes),
2374                    Some((full_path, ld_library_path)),
2375                )
2376            }
2377            DlModuleSpec::Memory { bytes, .. } => (HashedModuleData::new(bytes), None),
2378        };
2379
2380        let module = runtime.load_hashed_module_sync(module_data, Some(&self.engine))?;
2381
2382        let dylink_info = parse_dylink0_section(&module)?;
2383
2384        trace!(?dylink_info, "Loading side module");
2385
2386        if let Some((full_path, ld_library_path)) = paths {
2387            link_state.pending_module_paths.push(full_path.clone());
2388            let num_pending_modules = link_state.pending_module_paths.len();
2389            let pop_pending_module = |link_state: &mut InProgressLinkState| {
2390                assert_eq!(
2391                    num_pending_modules,
2392                    link_state.pending_module_paths.len(),
2393                    "Internal error: pending modules not maintained correctly"
2394                );
2395                link_state.pending_module_paths.pop().unwrap();
2396            };
2397
2398            for needed in &dylink_info.needed {
2399                trace!(needed, "Loading needed side module");
2400                match self.load_module_tree(
2401                    DlModuleSpec::FileSystem {
2402                        module_spec: Path::new(needed.as_str()),
2403                        ld_library_path,
2404                    },
2405                    link_state,
2406                    runtime,
2407                    wasi_state,
2408                    // RUNPATH, on which WASM_DYLINK_RUNTIME_PATH is based, is *not* applied
2409                    // recursively, so we discard the runtime_path parameter and
2410                    // only take the one from the module's dylink.0 section
2411                    dylink_info.runtime_path.as_ref(),
2412                    Some(&full_path),
2413                ) {
2414                    Ok(_) => (),
2415                    Err(e) => {
2416                        pop_pending_module(link_state);
2417                        return Err(e);
2418                    }
2419                }
2420            }
2421
2422            pop_pending_module(link_state);
2423        } else if !dylink_info.needed.is_empty() {
2424            unreachable!(
2425                "Internal error: in-memory modules with further needed modules not \
2426                    supported and no code paths can create such a module"
2427            );
2428        }
2429
2430        let handle = ModuleHandle(self.next_module_handle);
2431        self.next_module_handle += 1;
2432
2433        trace!(?module_name, ?handle, "Assigned handle to module");
2434
2435        link_state.new_modules.push(InProgressModuleLoad {
2436            handle,
2437            dylink_info,
2438            module,
2439        });
2440        // Put the name in the linker state - the actual DlModule must be
2441        // constructed later by the instance group once table addresses are
2442        // allocated for the module.
2443        // TODO: allocate table here (at least logically)?
2444        self.side_modules_by_name
2445            .insert(module_name.into_owned(), handle);
2446
2447        Ok(handle)
2448    }
2449}
2450
2451impl InstanceGroupState {
2452    fn main_instance(&self) -> Option<&Instance> {
2453        self.main_instance.as_ref()
2454    }
2455
2456    fn tls_base(&self, module_handle: ModuleHandle) -> Option<u64> {
2457        if module_handle == MAIN_MODULE_HANDLE {
2458            // Main's TLS area is at the beginning of its memory
2459            self.main_instance_tls_base
2460        } else {
2461            self.side_instances
2462                .get(&module_handle)
2463                .expect("Internal error: bad module handle")
2464                .tls_base
2465        }
2466    }
2467
2468    fn try_instance(&self, handle: ModuleHandle) -> Option<&Instance> {
2469        if handle == MAIN_MODULE_HANDLE {
2470            self.main_instance.as_ref()
2471        } else {
2472            self.side_instances.get(&handle).map(|i| &i.instance)
2473        }
2474    }
2475
2476    fn instance(&self, handle: ModuleHandle) -> &Instance {
2477        self.try_instance(handle)
2478            .expect("Internal error: bad module handle or not instantiated in this group")
2479    }
2480
2481    /// Allocate space on the indirect function table for the given number of functions
2482    ///
2483    /// table_alignment is the alignment of the table as a power of two.
2484    fn allocate_function_table(
2485        &mut self,
2486        store: &mut impl AsStoreMut,
2487        table_size: u32,
2488        table_alignment: u32,
2489    ) -> Result<u64, RuntimeError> {
2490        trace!(table_size, "Allocating table indices");
2491
2492        let base_index = if table_size == 0 {
2493            0
2494        } else {
2495            let current_size = self.indirect_function_table.size(store);
2496            let alignment = 2_u32.pow(table_alignment);
2497
2498            let offset = if !current_size.is_multiple_of(alignment) {
2499                alignment - (current_size % alignment)
2500            } else {
2501                0
2502            };
2503
2504            let delta = table_size + offset;
2505            trace!(?current_size, ?delta, "Growing indirect function table");
2506            let start = self
2507                .indirect_function_table
2508                .grow(store, delta, Value::FuncRef(None))?;
2509
2510            (start + offset) as u64
2511        };
2512
2513        trace!(
2514            base_index,
2515            new_table_size = ?self.indirect_function_table.size(store),
2516            "Allocated table indices"
2517        );
2518
2519        Ok(base_index)
2520    }
2521
2522    fn append_to_function_table(
2523        &self,
2524        store: &mut impl AsStoreMut,
2525        func: Function,
2526    ) -> Result<u32, RuntimeError> {
2527        let table = &self.indirect_function_table;
2528
2529        let ty = func.ty(store).to_string();
2530        let index: u32 = table.size(store);
2531        trace!(?index, ?ty, "Appending function in table");
2532
2533        table.grow(store, 1, func.into())
2534    }
2535
2536    fn place_in_function_table_at(
2537        &self,
2538        store: &mut impl AsStoreMut,
2539        func: Function,
2540        index: u32,
2541    ) -> Result<(), RuntimeError> {
2542        trace!(
2543            ?index,
2544            ?func,
2545            "Placing function in table at pre-defined index"
2546        );
2547
2548        let table = &self.indirect_function_table;
2549        let size = table.size(store);
2550
2551        if size <= index {
2552            let delta = index - size + 1;
2553            trace!(
2554                current_size = ?size,
2555                ?delta,
2556                "Growing indirect function table"
2557            );
2558            table.grow(store, delta, Value::FuncRef(None))?;
2559        } else {
2560            let existing = table.get(store, index).unwrap();
2561            if let Value::FuncRef(Some(_)) = existing {
2562                panic!("Internal error: function table index {index} already occupied");
2563            }
2564        }
2565
2566        let ty = func.ty(store).to_string();
2567        trace!(?index, ?ty, "Placing function in table at index");
2568        table.set(store, index, Value::FuncRef(Some(func)))
2569    }
2570
2571    fn instantiate_side_module_from_link_state(
2572        &mut self,
2573        linker_state: &mut LinkerState,
2574        store: &mut impl AsStoreMut,
2575        env: &FunctionEnv<WasiEnv>,
2576        link_state: &mut InProgressLinkState,
2577        module_handle: ModuleHandle,
2578    ) -> Result<(), LinkError> {
2579        let Some(pending_module) = link_state
2580            .new_modules
2581            .iter()
2582            .find(|m| m.handle == module_handle)
2583        else {
2584            panic!(
2585                "Only recently-loaded modules in the link state can be instantiated \
2586                by instantiate_side_module_from_link_state"
2587            )
2588        };
2589
2590        trace!(
2591            ?module_handle,
2592            ?link_state,
2593            "Instantiating module from link state"
2594        );
2595
2596        let memory_base = linker_state.allocate_memory(
2597            store,
2598            &self.memory,
2599            &pending_module.dylink_info.mem_info,
2600        )?;
2601        let table_base = self
2602            .allocate_function_table(
2603                store,
2604                pending_module.dylink_info.mem_info.table_size,
2605                pending_module.dylink_info.mem_info.table_alignment,
2606            )
2607            .map_err(LinkError::TableAllocationError)?;
2608
2609        trace!(
2610            memory_base,
2611            table_base, "Allocated memory and table for module"
2612        );
2613
2614        let mut imports = import_object_for_all_wasi_versions(&pending_module.module, store, env);
2615
2616        let well_known_imports = [
2617            ("env", "__memory_base", memory_base),
2618            ("env", "__table_base", table_base),
2619        ];
2620
2621        let module = pending_module.module.clone();
2622        let dylink_info = pending_module.dylink_info.clone();
2623
2624        trace!(?module_handle, "Resolving symbols");
2625        linker_state.resolve_symbols(
2626            self,
2627            store,
2628            &module,
2629            module_handle,
2630            link_state,
2631            &well_known_imports,
2632        )?;
2633
2634        trace!(?module_handle, "Populating imports object");
2635        self.populate_imports_from_link_state(
2636            module_handle,
2637            linker_state,
2638            link_state,
2639            store,
2640            &module,
2641            &mut imports,
2642            env,
2643            &well_known_imports,
2644        )?;
2645
2646        let instance = Instance::new(store, &module, &imports)?;
2647
2648        let instance_handles = WasiModuleInstanceHandles::new(
2649            self.memory.clone(),
2650            store,
2651            instance.clone(),
2652            Some(self.indirect_function_table.clone()),
2653        );
2654
2655        let dl_module = DlModule {
2656            module,
2657            dylink_info,
2658            memory_base,
2659            table_base,
2660        };
2661
2662        let tls_base = get_tls_base_export(&instance, store)?;
2663
2664        let dl_instance = DlInstance {
2665            instance: instance.clone(),
2666            instance_handles,
2667            // The TLS base of a side module's main instance is read from the module's
2668            // `__tls_base` export via `get_tls_base_export`, and is not necessarily at the
2669            // beginning of its memory.
2670            tls_base,
2671        };
2672
2673        linker_state.side_modules.insert(module_handle, dl_module);
2674        self.side_instances.insert(module_handle, dl_instance);
2675
2676        trace!(?module_handle, "Module instantiated");
2677
2678        Ok(())
2679    }
2680
2681    // For when we receive a module loaded DL operation
2682    fn allocate_function_table_for_existing_module(
2683        &mut self,
2684        linker_state: &LinkerState,
2685        store: &mut impl AsStoreMut,
2686        module_handle: ModuleHandle,
2687    ) -> Result<(), LinkError> {
2688        if self.side_instances.contains_key(&module_handle) {
2689            panic!(
2690                "Internal error: Module with handle {module_handle:?} \
2691                was already instantiated in this group"
2692            )
2693        };
2694
2695        let dl_module = linker_state
2696            .side_modules
2697            .get(&module_handle)
2698            .expect("Internal error: module not loaded into linker");
2699
2700        let table_base = self
2701            .allocate_function_table(
2702                store,
2703                dl_module.dylink_info.mem_info.table_size,
2704                dl_module.dylink_info.mem_info.table_alignment,
2705            )
2706            .map_err(LinkError::TableAllocationError)?;
2707
2708        if table_base != dl_module.table_base {
2709            panic!("Internal error: table base out of sync with linker state");
2710        }
2711
2712        trace!(table_base, "Allocated table indices for existing module");
2713
2714        Ok(())
2715    }
2716
2717    // For when we receive a module loaded DL operation
2718    fn instantiate_side_module_from_linker(
2719        &mut self,
2720        linker_state: &LinkerState,
2721        store: &mut impl AsStoreMut,
2722        env: &FunctionEnv<WasiEnv>,
2723        module_handle: ModuleHandle,
2724        pending_resolutions: &mut PendingResolutionsFromLinker,
2725    ) -> Result<(), LinkError> {
2726        if self.side_instances.contains_key(&module_handle) {
2727            panic!(
2728                "Internal error: Module with handle {module_handle:?} \
2729                was already instantiated in this group"
2730            )
2731        };
2732
2733        trace!(?module_handle, "Instantiating existing module from linker");
2734
2735        let dl_module = linker_state
2736            .side_modules
2737            .get(&module_handle)
2738            .expect("Internal error: module not loaded into linker");
2739
2740        let mut imports = import_object_for_all_wasi_versions(&dl_module.module, store, env);
2741
2742        let well_known_imports = [
2743            ("env", "__memory_base", dl_module.memory_base),
2744            ("env", "__table_base", dl_module.table_base),
2745        ];
2746
2747        trace!(?module_handle, "Populating imports object");
2748        self.populate_imports_from_linker(
2749            module_handle,
2750            linker_state,
2751            store,
2752            &dl_module.module,
2753            &mut imports,
2754            env,
2755            &well_known_imports,
2756            pending_resolutions,
2757        )?;
2758
2759        let instance = Instance::new(store, &dl_module.module, &imports)?;
2760
2761        // This is a non-main instance of a side module, so it needs a new TLS area
2762        let tls_base = call_initialization_function::<i32>(&instance, store, "__wasix_init_tls")?
2763            .map(|v| v as u64);
2764
2765        let instance_handles = WasiModuleInstanceHandles::new(
2766            self.memory.clone(),
2767            store,
2768            instance.clone(),
2769            Some(self.indirect_function_table.clone()),
2770        );
2771
2772        let dl_instance = DlInstance {
2773            instance: instance.clone(),
2774            instance_handles,
2775            tls_base,
2776        };
2777
2778        self.side_instances.insert(module_handle, dl_instance);
2779
2780        // Initialization logic must only be run once, so no init calls here; it is
2781        // assumed that the module was instantiated and its init callbacks were called
2782        // by whichever thread first called instantiate_side_module_from_link_state.
2783
2784        trace!(?module_handle, "Existing module instantiated successfully");
2785
2786        Ok(())
2787    }
2788
2789    fn finalize_pending_resolutions_from_linker(
2790        &self,
2791        pending_resolutions: &PendingResolutionsFromLinker,
2792        store: &mut impl AsStoreMut,
2793    ) -> Result<(), LinkError> {
2794        trace!("Finalizing pending functions");
2795
2796        for pending in &pending_resolutions.functions {
2797            let func = self
2798                .instance(pending.resolved_from)
2799                .exports
2800                .get_function(&pending.name)
2801                .unwrap_or_else(|e| {
2802                    panic!(
2803                        "Internal error: failed to resolve exported function {}: {e:?}",
2804                        pending.name
2805                    )
2806                });
2807
2808            self.place_in_function_table_at(store, func.clone(), pending.function_table_index)
2809                .map_err(LinkError::TableAllocationError)?;
2810
2811            trace!(?pending, "Placed pending function in table");
2812        }
2813
2814        for tls in &pending_resolutions.tls {
2815            let Some(tls_base) = self.tls_base(tls.resolved_from) else {
2816                // This is a panic since this error should have been caught when the symbol
2817                // was originally resolved by the instigating instance group. We're just replaying
2818                // the changes.
2819                panic!(
2820                    "Internal error: Tried to import TLS symbol from module {} that \
2821                    has no TLS base",
2822                    tls.resolved_from.0
2823                );
2824            };
2825
2826            let final_addr = tls_base + tls.offset;
2827            set_integer_global(store, "<pending TLS global>", &tls.global, final_addr)?;
2828            trace!(?tls, tls_base, final_addr, "Setting pending TLS global");
2829        }
2830
2831        Ok(())
2832    }
2833
2834    fn apply_resolved_function(
2835        &self,
2836        store: &mut impl AsStoreMut,
2837        name: &str,
2838        resolved_from: ModuleHandle,
2839        function_table_index: u32,
2840    ) -> Result<(), LinkError> {
2841        trace!(
2842            ?name,
2843            ?resolved_from,
2844            function_table_index,
2845            "Applying resolved function"
2846        );
2847
2848        let instance = &self.try_instance(resolved_from).unwrap_or_else(|| {
2849            panic!("Internal error: module {resolved_from:?} not loaded by this group")
2850        });
2851
2852        let func = instance.exports.get_function(name).unwrap_or_else(|e| {
2853            panic!("Internal error: failed to resolve exported function {name}: {e:?}")
2854        });
2855
2856        self.place_in_function_table_at(store, func.clone(), function_table_index)
2857            .map_err(LinkError::TableAllocationError)?;
2858
2859        Ok(())
2860    }
2861
2862    pub fn apply_function_table_allocation(
2863        &mut self,
2864        store: &mut impl AsStoreMut,
2865        index: u32,
2866        size: u32,
2867    ) -> Result<(), LinkError> {
2868        trace!(index, "Applying function table allocation");
2869        let allocated_index = self
2870            .allocate_function_table(store, size, 0)
2871            .map_err(LinkError::TableAllocationError)? as u32;
2872        if allocated_index != index {
2873            panic!(
2874                "Internal error: allocated index {allocated_index} does not match expected index {index}"
2875            );
2876        }
2877        Ok(())
2878    }
2879
2880    fn apply_dl_operation(
2881        &mut self,
2882        linker_state: &LinkerState,
2883        operation: DlOperation,
2884        store: &mut impl AsStoreMut,
2885        env: &FunctionEnv<WasiEnv>,
2886    ) -> Result<(), LinkError> {
2887        trace!(?operation, "Applying operation");
2888        match operation {
2889            DlOperation::LoadModules(module_handles) => {
2890                let mut pending_functions = PendingResolutionsFromLinker::default();
2891                for handle in module_handles {
2892                    // We need to do table allocation in exactly the same order as the instigating
2893                    // group, which is:
2894                    //   * Allocate module's own table space
2895                    //   * Fill GOT.func entries (through instantiating the module)
2896                    //   * Then repeat for the next module.
2897                    self.allocate_function_table_for_existing_module(linker_state, store, handle)?;
2898                    self.instantiate_side_module_from_linker(
2899                        linker_state,
2900                        store,
2901                        env,
2902                        handle,
2903                        &mut pending_functions,
2904                    )?;
2905                }
2906                self.finalize_pending_resolutions_from_linker(&pending_functions, store)?;
2907            }
2908            DlOperation::ResolveFunction {
2909                name,
2910                resolved_from,
2911                function_table_index,
2912            } => self.apply_resolved_function(store, &name, resolved_from, function_table_index)?,
2913            DlOperation::AllocateFunctionTable { index, size } => {
2914                self.apply_function_table_allocation(store, index, size)?
2915            }
2916        };
2917        trace!("Operation applied successfully");
2918        Ok(())
2919    }
2920
2921    fn apply_requested_symbols_from_linker(
2922        &self,
2923        store: &mut impl AsStoreMut,
2924        linker_state: &LinkerState,
2925    ) -> Result<(), LinkError> {
2926        for (key, val) in &linker_state.symbol_resolution_records {
2927            if let SymbolResolutionKey::Requested { name, .. } = key
2928                && let SymbolResolutionResult::FunctionPointer {
2929                    resolved_from,
2930                    function_table_index,
2931                } = val
2932            {
2933                self.apply_resolved_function(store, name, *resolved_from, *function_table_index)?;
2934            }
2935        }
2936        Ok(())
2937    }
2938
2939    fn resolve_exported_symbol(&self, symbol: &str) -> Option<(ModuleHandle, &Extern)> {
2940        if let Some(export) = self
2941            .main_instance()
2942            .and_then(|instance| instance.exports.get_extern(symbol))
2943        {
2944            trace!(symbol, from = ?MAIN_MODULE_HANDLE, ?export, "Resolved exported symbol");
2945            Some((MAIN_MODULE_HANDLE, export))
2946        } else {
2947            for (handle, dl_instance) in &self.side_instances {
2948                if let Some(export) = dl_instance.instance.exports.get_extern(symbol) {
2949                    trace!(symbol, from = ?handle, ?export, "Resolved exported symbol");
2950                    return Some((*handle, export));
2951                }
2952            }
2953
2954            trace!(symbol, "Failed to resolve exported symbol");
2955            None
2956        }
2957    }
2958
2959    // This function populates the imports object for a single module from the given
2960    // in-progress link state.
2961    fn populate_imports_from_link_state(
2962        &self,
2963        module_handle: ModuleHandle,
2964        linker_state: &mut LinkerState,
2965        link_state: &mut InProgressLinkState,
2966        store: &mut impl AsStoreMut,
2967        module: &Module,
2968        imports: &mut Imports,
2969        env: &FunctionEnv<WasiEnv>,
2970        well_known_imports: &[(&str, &str, u64)],
2971    ) -> Result<(), LinkError> {
2972        trace!(?module_handle, "Populating imports object from link state");
2973
2974        for import in module.imports() {
2975            // Skip non-DL-related import modules
2976            if !matches!(import.module(), "env" | "GOT.mem" | "GOT.func") {
2977                continue;
2978            }
2979
2980            // Important env imports first!
2981            if import.module() == "env" {
2982                match import.name() {
2983                    "memory" => {
2984                        let ExternType::Memory(memory_ty) = import.ty() else {
2985                            return Err(LinkError::BadImport(
2986                                import.module().to_string(),
2987                                import.name().to_string(),
2988                                import.ty().clone(),
2989                            ));
2990                        };
2991                        trace!(?module_handle, ?import, "Main memory");
2992
2993                        // Make sure the memory is big enough for the module being instantiated
2994                        let current_size = self.memory.grow(store, 0)?;
2995                        if current_size < memory_ty.minimum {
2996                            self.memory.grow(store, memory_ty.minimum - current_size)?;
2997                        }
2998
2999                        imports.define(
3000                            import.module(),
3001                            import.name(),
3002                            Extern::Memory(self.memory.clone()),
3003                        );
3004                        continue;
3005                    }
3006                    "__indirect_function_table" => {
3007                        if !matches!(import.ty(), ExternType::Table(ty) if ty.ty == Type::FuncRef) {
3008                            return Err(LinkError::BadImport(
3009                                import.module().to_string(),
3010                                import.name().to_string(),
3011                                import.ty().clone(),
3012                            ));
3013                        }
3014                        trace!(?module_handle, ?import, "Function table");
3015                        imports.define(
3016                            import.module(),
3017                            import.name(),
3018                            Extern::Table(self.indirect_function_table.clone()),
3019                        );
3020                        continue;
3021                    }
3022                    "__stack_pointer" => {
3023                        if !matches!(import.ty(), ExternType::Global(ty) if *ty == self.stack_pointer.ty(store))
3024                        {
3025                            return Err(LinkError::BadImport(
3026                                import.module().to_string(),
3027                                import.name().to_string(),
3028                                import.ty().clone(),
3029                            ));
3030                        }
3031                        trace!(?module_handle, ?import, "Stack pointer");
3032                        imports.define(
3033                            import.module(),
3034                            import.name(),
3035                            Extern::Global(self.stack_pointer.clone()),
3036                        );
3037                        continue;
3038                    }
3039                    // Clang generates this symbol when building modules that use EH-based sjlj.
3040                    "__c_longjmp" => {
3041                        if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32])
3042                        {
3043                            return Err(LinkError::BadImport(
3044                                import.module().to_string(),
3045                                import.name().to_string(),
3046                                import.ty().clone(),
3047                            ));
3048                        }
3049                        trace!(?module_handle, ?import, "setjmp/longjmp exception tag");
3050                        imports.define(import.module(), import.name(), self.c_longjmp.clone());
3051                        continue;
3052                    }
3053                    // Clang generates this symbol when building C++ code that uses exception handling.
3054                    "__cpp_exception" => {
3055                        if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32])
3056                        {
3057                            return Err(LinkError::BadImport(
3058                                import.module().to_string(),
3059                                import.name().to_string(),
3060                                import.ty().clone(),
3061                            ));
3062                        }
3063                        trace!(?module_handle, ?import, "C++ exception tag");
3064                        imports.define(import.module(), import.name(), self.cpp_exception.clone());
3065                        continue;
3066                    }
3067                    _ => (),
3068                }
3069            }
3070
3071            // Next, go over the well-known imports
3072            if let Some(well_known_value) = well_known_imports.iter().find_map(|i| {
3073                if i.0 == import.module() && i.1 == import.name() {
3074                    Some(i.2)
3075                } else {
3076                    None
3077                }
3078            }) {
3079                trace!(
3080                    ?module_handle,
3081                    ?import,
3082                    well_known_value,
3083                    "Well-known value"
3084                );
3085                imports.define(
3086                    import.module(),
3087                    import.name(),
3088                    define_integer_global_import(store, &import, well_known_value)?,
3089                );
3090                continue;
3091            }
3092
3093            let key = NeededSymbolResolutionKey {
3094                module_handle,
3095                import_module: import.module().to_owned(),
3096                import_name: import.name().to_owned(),
3097            };
3098
3099            // Finally, go through the resolution results
3100            let resolution = link_state.symbols.get(&key).unwrap_or_else(|| {
3101                panic!(
3102                    "Internal error: missing import resolution '{0}'.{1}",
3103                    key.import_module, key.import_name
3104                )
3105            });
3106
3107            trace!(?module_handle, ?import, ?resolution, "Resolution");
3108
3109            match resolution {
3110                InProgressSymbolResolution::Function(module_handle) => {
3111                    let func = self
3112                        .instance(*module_handle)
3113                        .exports
3114                        .get_function(import.name())
3115                        .expect("Internal error: bad in-progress symbol resolution");
3116                    imports.define(import.module(), import.name(), func.clone());
3117                    linker_state.symbol_resolution_records.insert(
3118                        SymbolResolutionKey::Needed(key.clone()),
3119                        SymbolResolutionResult::Function {
3120                            ty: func.ty(store),
3121                            resolved_from: *module_handle,
3122                        },
3123                    );
3124                }
3125
3126                InProgressSymbolResolution::StubFunction(func_ty) => {
3127                    let func = self.generate_stub_function(
3128                        store,
3129                        func_ty,
3130                        env,
3131                        module_handle,
3132                        import.name().to_string(),
3133                    );
3134                    imports.define(import.module(), import.name(), func);
3135                    linker_state.symbol_resolution_records.insert(
3136                        SymbolResolutionKey::Needed(key.clone()),
3137                        SymbolResolutionResult::StubFunction(func_ty.clone()),
3138                    );
3139                }
3140
3141                InProgressSymbolResolution::MemGlobal(module_handle) => {
3142                    let export = match self.resolve_export_from(
3143                        store,
3144                        *module_handle,
3145                        import.name(),
3146                        self.instance(*module_handle),
3147                        linker_state.dylink_info(*module_handle),
3148                        linker_state.memory_base(*module_handle),
3149                        self.tls_base(*module_handle),
3150                        true,
3151                    ) {
3152                        Ok(export) => export,
3153                        Err(ResolveError::NoTlsBaseGlobalExport) => {
3154                            return Err(LinkError::MissingTlsBaseExport(
3155                                import.name().to_string(),
3156                                *module_handle,
3157                            ));
3158                        }
3159                        Err(e) => {
3160                            panic!("Internal error: bad in-progress symbol resolution: {e:?}")
3161                        }
3162                    };
3163
3164                    match export {
3165                        PartiallyResolvedExport::Global(addr) => {
3166                            trace!(?module_handle, ?import, addr, "Memory address");
3167
3168                            let global =
3169                                define_integer_global_import(store, &import, addr).unwrap();
3170
3171                            imports.define(import.module(), import.name(), global);
3172                            linker_state.symbol_resolution_records.insert(
3173                                SymbolResolutionKey::Needed(key.clone()),
3174                                SymbolResolutionResult::Memory(addr),
3175                            );
3176                        }
3177
3178                        PartiallyResolvedExport::Tls { offset, final_addr } => {
3179                            trace!(?module_handle, ?import, offset, final_addr, "TLS address");
3180
3181                            let global =
3182                                define_integer_global_import(store, &import, final_addr).unwrap();
3183
3184                            imports.define(import.module(), import.name(), global);
3185                            linker_state.symbol_resolution_records.insert(
3186                                SymbolResolutionKey::Needed(key.clone()),
3187                                SymbolResolutionResult::Tls {
3188                                    resolved_from: *module_handle,
3189                                    offset,
3190                                },
3191                            );
3192                        }
3193
3194                        PartiallyResolvedExport::Function(_) => {
3195                            panic!("Internal error: bad in-progress symbol resolution")
3196                        }
3197                    }
3198                }
3199
3200                InProgressSymbolResolution::UnresolvedMemGlobal => {
3201                    let global = define_integer_global_import(store, &import, 0).unwrap();
3202                    imports.define(import.module(), import.name(), global.clone());
3203
3204                    link_state
3205                        .unresolved_globals
3206                        .push(UnresolvedGlobal::Mem(key, global));
3207                }
3208
3209                InProgressSymbolResolution::FuncGlobal(module_handle) => {
3210                    let func = self
3211                        .instance(*module_handle)
3212                        .exports
3213                        .get_function(import.name())
3214                        .expect("Internal error: bad in-progress symbol resolution");
3215
3216                    let func_handle = self
3217                        .append_to_function_table(store, func.clone())
3218                        .map_err(LinkError::TableAllocationError)?;
3219                    trace!(
3220                        ?module_handle,
3221                        ?import,
3222                        index = func_handle,
3223                        "Allocated function table index"
3224                    );
3225                    let global =
3226                        define_integer_global_import(store, &import, func_handle as u64).unwrap();
3227
3228                    imports.define(import.module(), import.name(), global);
3229                    linker_state.symbol_resolution_records.insert(
3230                        SymbolResolutionKey::Needed(key.clone()),
3231                        SymbolResolutionResult::FunctionPointer {
3232                            resolved_from: *module_handle,
3233                            function_table_index: func_handle,
3234                        },
3235                    );
3236                }
3237
3238                InProgressSymbolResolution::UnresolvedFuncGlobal => {
3239                    let global = define_integer_global_import(store, &import, 0).unwrap();
3240                    imports.define(import.module(), import.name(), global.clone());
3241
3242                    link_state
3243                        .unresolved_globals
3244                        .push(UnresolvedGlobal::Func(key, global));
3245                }
3246            }
3247        }
3248
3249        trace!(?module_handle, "Imports object populated successfully");
3250
3251        Ok(())
3252    }
3253
3254    // For when we receive a module loaded DL operation
3255    fn populate_imports_from_linker(
3256        &self,
3257        module_handle: ModuleHandle,
3258        linker_state: &LinkerState,
3259        store: &mut impl AsStoreMut,
3260        module: &Module,
3261        imports: &mut Imports,
3262        env: &FunctionEnv<WasiEnv>,
3263        well_known_imports: &[(&str, &str, u64)],
3264        pending_resolutions: &mut PendingResolutionsFromLinker,
3265    ) -> Result<(), LinkError> {
3266        trace!(
3267            ?module_handle,
3268            "Populating imports object for existing module from linker state"
3269        );
3270
3271        for import in module.imports() {
3272            // Skip non-DL-related import modules
3273            if !matches!(import.module(), "env" | "GOT.mem" | "GOT.func") {
3274                continue;
3275            }
3276
3277            // Important env imports first!
3278            if import.module() == "env" {
3279                match import.name() {
3280                    "memory" => {
3281                        if !matches!(import.ty(), ExternType::Memory(_)) {
3282                            return Err(LinkError::BadImport(
3283                                import.module().to_string(),
3284                                import.name().to_string(),
3285                                import.ty().clone(),
3286                            ));
3287                        }
3288                        trace!(?module_handle, ?import, "Main memory");
3289                        imports.define(
3290                            import.module(),
3291                            import.name(),
3292                            Extern::Memory(self.memory.clone()),
3293                        );
3294                        continue;
3295                    }
3296                    "__indirect_function_table" => {
3297                        if !matches!(import.ty(), ExternType::Table(ty) if ty.ty == Type::FuncRef) {
3298                            return Err(LinkError::BadImport(
3299                                import.module().to_string(),
3300                                import.name().to_string(),
3301                                import.ty().clone(),
3302                            ));
3303                        }
3304                        trace!(?module_handle, ?import, "Function table");
3305                        imports.define(
3306                            import.module(),
3307                            import.name(),
3308                            Extern::Table(self.indirect_function_table.clone()),
3309                        );
3310                        continue;
3311                    }
3312                    "__stack_pointer" => {
3313                        if !matches!(import.ty(), ExternType::Global(ty) if *ty == self.stack_pointer.ty(store))
3314                        {
3315                            return Err(LinkError::BadImport(
3316                                import.module().to_string(),
3317                                import.name().to_string(),
3318                                import.ty().clone(),
3319                            ));
3320                        }
3321                        trace!(?module_handle, ?import, "Stack pointer");
3322                        imports.define(
3323                            import.module(),
3324                            import.name(),
3325                            Extern::Global(self.stack_pointer.clone()),
3326                        );
3327                        continue;
3328                    }
3329                    "__c_longjmp" => {
3330                        if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32])
3331                        {
3332                            return Err(LinkError::BadImport(
3333                                import.module().to_string(),
3334                                import.name().to_string(),
3335                                import.ty().clone(),
3336                            ));
3337                        }
3338                        trace!(?module_handle, ?import, "setjmp/longjmp exception tag");
3339                        imports.define(import.module(), import.name(), self.c_longjmp.clone());
3340                        continue;
3341                    }
3342                    "__cpp_exception" => {
3343                        if !matches!(import.ty(), ExternType::Tag(ty) if *ty.params == [Type::I32])
3344                        {
3345                            return Err(LinkError::BadImport(
3346                                import.module().to_string(),
3347                                import.name().to_string(),
3348                                import.ty().clone(),
3349                            ));
3350                        }
3351                        trace!(?module_handle, ?import, "C++ exception tag");
3352                        imports.define(import.module(), import.name(), self.cpp_exception.clone());
3353                        continue;
3354                    }
3355                    _ => (),
3356                }
3357            }
3358
3359            // Next, go over the well-known imports
3360            if let Some(well_known_value) = well_known_imports.iter().find_map(|i| {
3361                if i.0 == import.module() && i.1 == import.name() {
3362                    Some(i.2)
3363                } else {
3364                    None
3365                }
3366            }) {
3367                trace!(
3368                    ?module_handle,
3369                    ?import,
3370                    well_known_value,
3371                    "Well-known value"
3372                );
3373                imports.define(
3374                    import.module(),
3375                    import.name(),
3376                    define_integer_global_import(store, &import, well_known_value)?,
3377                );
3378                continue;
3379            }
3380
3381            let key = SymbolResolutionKey::Needed(NeededSymbolResolutionKey {
3382                module_handle,
3383                import_module: import.module().to_owned(),
3384                import_name: import.name().to_owned(),
3385            });
3386
3387            // Finally, go through the resolution results
3388            let resolution = linker_state
3389                .symbol_resolution_records
3390                .get(&key)
3391                .unwrap_or_else(|| {
3392                    panic!(
3393                        "Internal error: missing symbol resolution record for '{0}'.{1}",
3394                        import.module(),
3395                        import.name()
3396                    )
3397                });
3398
3399            trace!(?module_handle, ?import, ?resolution, "Resolution");
3400
3401            match resolution {
3402                SymbolResolutionResult::Function { ty, resolved_from } => {
3403                    let func = match self.try_instance(*resolved_from) {
3404                        Some(instance) => {
3405                            trace!(
3406                                ?module_handle,
3407                                ?import,
3408                                ?resolved_from,
3409                                "Already have instance to resolve from"
3410                            );
3411                            instance
3412                                .exports
3413                                .get_function(import.name())
3414                                .expect("Internal error: failed to get exported function")
3415                                .clone()
3416                        }
3417                        // We may be loading a module tree, and the instance from which
3418                        // we're supposed to import the function may not exist yet, so
3419                        // we add in a stub, which will later use the resolution records
3420                        // to locate the function.
3421                        None => {
3422                            trace!(
3423                                ?module_handle,
3424                                ?import,
3425                                ?resolved_from,
3426                                "Don't have instance yet"
3427                            );
3428
3429                            self.generate_stub_function(
3430                                store,
3431                                ty,
3432                                env,
3433                                module_handle,
3434                                import.name().to_owned(),
3435                            )
3436                        }
3437                    };
3438                    imports.define(import.module(), import.name(), func);
3439                }
3440                SymbolResolutionResult::StubFunction(ty) => {
3441                    let func = self.generate_stub_function(
3442                        store,
3443                        ty,
3444                        env,
3445                        module_handle,
3446                        import.name().to_owned(),
3447                    );
3448                    imports.define(import.module(), import.name(), func.clone());
3449                }
3450                SymbolResolutionResult::FunctionPointer {
3451                    resolved_from,
3452                    function_table_index,
3453                } => {
3454                    let func = self.try_instance(*resolved_from).map(|instance| {
3455                        instance
3456                            .exports
3457                            .get_function(import.name())
3458                            .unwrap_or_else(|e| {
3459                                panic!(
3460                                    "Internal error: failed to resolve function {}: {e:?}",
3461                                    import.name()
3462                                )
3463                            })
3464                    });
3465                    match func {
3466                        Some(func) => {
3467                            trace!(
3468                                ?module_handle,
3469                                ?import,
3470                                function_table_index,
3471                                "Placing function pointer into table"
3472                            );
3473                            self.place_in_function_table_at(
3474                                store,
3475                                func.clone(),
3476                                *function_table_index,
3477                            )
3478                            .map_err(LinkError::TableAllocationError)?;
3479                        }
3480                        None => {
3481                            trace!(
3482                                ?module_handle,
3483                                ?import,
3484                                function_table_index,
3485                                "Don't have instance yet, creating a pending function"
3486                            );
3487                            // Since we know the final value of the global, we can create it
3488                            // and just fill the function table in later
3489                            pending_resolutions.functions.push(
3490                                PendingFunctionResolutionFromLinkerState {
3491                                    resolved_from: *resolved_from,
3492                                    name: import.name().to_string(),
3493                                    function_table_index: *function_table_index,
3494                                },
3495                            );
3496                        }
3497                    };
3498                    let global =
3499                        define_integer_global_import(store, &import, *function_table_index as u64)?;
3500                    imports.define(import.module(), import.name(), global);
3501                }
3502                SymbolResolutionResult::Memory(addr) => {
3503                    let global = define_integer_global_import(store, &import, *addr)?;
3504                    imports.define(import.module(), import.name(), global);
3505                }
3506                SymbolResolutionResult::Tls {
3507                    resolved_from,
3508                    offset,
3509                } => {
3510                    let global = define_integer_global_import(store, &import, 0)?;
3511                    pending_resolutions.tls.push(PendingTlsPointer {
3512                        global: global.clone(),
3513                        resolved_from: *resolved_from,
3514                        offset: *offset,
3515                    });
3516                    imports.define(import.module(), import.name(), global);
3517                }
3518            }
3519        }
3520
3521        Ok(())
3522    }
3523
3524    // Resolve an export down to the "memory address" of the symbol. This is different from
3525    // `resolve_symbol`, which resolves a WASM export but does not care about its type and
3526    // does no further processing on the export itself.
3527    fn resolve_export(
3528        &self,
3529        linker_state: &LinkerState,
3530        store: &mut impl AsStoreMut,
3531        module_handle: Option<ModuleHandle>,
3532        symbol: &str,
3533        allow_hidden: bool,
3534    ) -> Result<(PartiallyResolvedExport, ModuleHandle), ResolveError> {
3535        trace!(?module_handle, ?symbol, "Resolving export");
3536        match module_handle {
3537            Some(module_handle) => {
3538                let instance = self
3539                    .try_instance(module_handle)
3540                    .ok_or(ResolveError::InvalidModuleHandle)?;
3541                let tls_base = self.tls_base(module_handle);
3542                let memory_base = linker_state.memory_base(module_handle);
3543                let dylink_info = linker_state.dylink_info(module_handle);
3544                Ok((
3545                    self.resolve_export_from(
3546                        store,
3547                        module_handle,
3548                        symbol,
3549                        instance,
3550                        dylink_info,
3551                        memory_base,
3552                        tls_base,
3553                        allow_hidden,
3554                    )?,
3555                    module_handle,
3556                ))
3557            }
3558
3559            None => {
3560                // TODO: this would be the place to support RTLD_NEXT
3561                if let Some(instance) = self.main_instance() {
3562                    match self.resolve_export_from(
3563                        store,
3564                        MAIN_MODULE_HANDLE,
3565                        symbol,
3566                        instance,
3567                        &linker_state.main_module_dylink_info,
3568                        linker_state.memory_base(MAIN_MODULE_HANDLE),
3569                        self.main_instance_tls_base,
3570                        allow_hidden,
3571                    ) {
3572                        Ok(export) => return Ok((export, MAIN_MODULE_HANDLE)),
3573                        Err(ResolveError::MissingExport) => (),
3574                        Err(e) => return Err(e),
3575                    }
3576                }
3577
3578                // Iterate over linker.side_modules to ensure we're going over the
3579                // modules in increasing order of module_handle, A.K.A. the order
3580                // in which modules were loaded. linker.side_modules is a BTreeMap
3581                // whereas self.side_instances is a HashMap with undetermined
3582                // iteration order.
3583                for (handle, module) in &linker_state.side_modules {
3584                    let instance = &self.side_instances[handle];
3585                    match self.resolve_export_from(
3586                        store,
3587                        *handle,
3588                        symbol,
3589                        &instance.instance,
3590                        &module.dylink_info,
3591                        linker_state.memory_base(*handle),
3592                        instance.tls_base,
3593                        allow_hidden,
3594                    ) {
3595                        Ok(export) => return Ok((export, *handle)),
3596                        Err(ResolveError::MissingExport) => (),
3597                        Err(e) => return Err(e),
3598                    }
3599                }
3600
3601                trace!(
3602                    ?module_handle,
3603                    ?symbol,
3604                    "Failed to locate symbol after searching all instances"
3605                );
3606                Err(ResolveError::MissingExport)
3607            }
3608        }
3609    }
3610
3611    fn resolve_export_from(
3612        &self,
3613        store: &mut impl AsStoreMut,
3614        module_handle: ModuleHandle,
3615        symbol: &str,
3616        instance: &Instance,
3617        dylink_info: &DylinkInfo,
3618        memory_base: u64,
3619        tls_base: Option<u64>,
3620        allow_hidden: bool,
3621    ) -> Result<PartiallyResolvedExport, ResolveError> {
3622        trace!(from = ?module_handle, symbol, "Resolving export from instance");
3623        let export = instance.exports.get_extern(symbol).ok_or_else(|| {
3624            trace!(from = ?module_handle, symbol, "Not found");
3625            ResolveError::MissingExport
3626        })?;
3627
3628        if !allow_hidden
3629            && dylink_info
3630                .export_metadata
3631                .get(symbol)
3632                .map(|flags| flags.contains(wasmparser::SymbolFlags::VISIBILITY_HIDDEN))
3633                .unwrap_or(false)
3634        {
3635            return Err(ResolveError::MissingExport);
3636        }
3637
3638        match export.ty(store) {
3639            ExternType::Function(_) => {
3640                trace!(from = ?module_handle, symbol, "Found function");
3641                Ok(PartiallyResolvedExport::Function(
3642                    Function::get_self_from_extern(export).unwrap().clone(),
3643                ))
3644            }
3645            ty @ ExternType::Global(_) => {
3646                let global = Global::get_self_from_extern(export).unwrap();
3647                let value = match global.get(store) {
3648                    Value::I32(value) => value as u64,
3649                    Value::I64(value) => value as u64,
3650                    _ => return Err(ResolveError::InvalidExportType(ty.clone())),
3651                };
3652
3653                let is_tls = dylink_info
3654                    .export_metadata
3655                    .get(symbol)
3656                    .map(|flags| flags.contains(wasmparser::SymbolFlags::TLS))
3657                    .unwrap_or(false);
3658
3659                if is_tls {
3660                    let Some(tls_base) = tls_base else {
3661                        return Err(ResolveError::NoTlsBaseGlobalExport);
3662                    };
3663                    let final_value = value + tls_base;
3664                    trace!(
3665                        from = ?module_handle,
3666                        symbol,
3667                        value,
3668                        offset = value,
3669                        final_value,
3670                        "Found TLS global"
3671                    );
3672                    Ok(PartiallyResolvedExport::Tls {
3673                        offset: value,
3674                        final_addr: final_value,
3675                    })
3676                } else {
3677                    let final_value = value + memory_base;
3678                    trace!(from = ?module_handle, symbol, value, final_value, "Found global");
3679                    Ok(PartiallyResolvedExport::Global(final_value))
3680                }
3681            }
3682            ty => Err(ResolveError::InvalidExportType(ty.clone())),
3683        }
3684    }
3685
3686    fn generate_stub_function(
3687        &self,
3688        store: &mut impl AsStoreMut,
3689        ty: &FunctionType,
3690        env: &FunctionEnv<WasiEnv>,
3691        requesting_module: ModuleHandle,
3692        name: String,
3693    ) -> Function {
3694        // TODO: only search through needed modules for the symbol. This requires the implementation
3695        // of needing/needed relationships between modules.
3696        trace!(?requesting_module, name, "Generating stub function");
3697
3698        let ty = ty.clone();
3699        let resolved: Mutex<Option<Option<Function>>> = Mutex::new(None);
3700
3701        Function::new_with_env(
3702            store,
3703            env,
3704            ty.clone(),
3705            move |mut env: FunctionEnvMut<'_, WasiEnv>, params: &[Value]| {
3706                let mk_error = || {
3707                    RuntimeError::user(Box::new(WasiError::DlSymbolResolutionFailed(name.clone())))
3708                };
3709
3710                let mut resolved_guard = resolved.lock().unwrap();
3711                let func = match *resolved_guard {
3712                    None => {
3713                        trace!(?requesting_module, name, "Resolving stub function");
3714
3715                        let (data, store) = env.data_and_store_mut();
3716                        let env_inner = data.inner();
3717                        // Safe to unwrap since we already know we're doing DL
3718                        let linker = env_inner.linker().unwrap();
3719
3720                        // Try to lock the linker state. This *can* fail if a stub
3721                        // is called as part of the init logic for a module. If we
3722                        // can't lock the linker state, we just resolve the symbol
3723                        // but don't store the resolved function anywhere; a later
3724                        // call to the stub function can then resolve again. Since
3725                        // this module and the one that has the symbol have to be
3726                        // part of the same module tree, it's super-duper-unlikely
3727                        // that a second resolution of the symbol would return a
3728                        // different result and would indicate a problem with the
3729                        // implementation of the linker.
3730                        let linker_state = match linker.linker_state.try_write() {
3731                            Ok(guard) => {
3732                                trace!(
3733                                    ?requesting_module,
3734                                    name, "Locked linker state successfully"
3735                                );
3736                                Some(guard)
3737                            }
3738                            Err(TryLockError::WouldBlock) => {
3739                                trace!(?requesting_module, name, "Failed to lock linker state");
3740                                None
3741                            }
3742                            Err(TryLockError::Poisoned(_)) => {
3743                                *resolved_guard = Some(None);
3744                                return Err(mk_error());
3745                            }
3746                        };
3747
3748                        let group_guard = linker.instance_group_state.lock().unwrap();
3749                        let Some(group_state) = group_guard.as_ref() else {
3750                            trace!(?requesting_module, name, "Instance group is already dead");
3751                            *resolved_guard = Some(None);
3752                            return Err(mk_error());
3753                        };
3754
3755                        let resolution_key =
3756                            SymbolResolutionKey::Needed(NeededSymbolResolutionKey {
3757                                module_handle: requesting_module,
3758                                import_module: "env".to_owned(),
3759                                import_name: name.clone(),
3760                            });
3761
3762                        match linker_state
3763                            .as_ref()
3764                            .and_then(|l| l.symbol_resolution_records.get(&resolution_key))
3765                        {
3766                            Some(SymbolResolutionResult::Function {
3767                                resolved_from,
3768                                ty: resolved_ty,
3769                            }) => {
3770                                trace!(
3771                                    ?requesting_module,
3772                                    name, "Function was already resolved in the linker"
3773                                );
3774
3775                                if ty != *resolved_ty {
3776                                    *resolved_guard = Some(None);
3777                                    return Err(mk_error());
3778                                }
3779
3780                                let func = group_state
3781                                    .instance(*resolved_from)
3782                                    .exports
3783                                    .get_function(&name)
3784                                    .unwrap()
3785                                    .clone();
3786                                *resolved_guard = Some(Some(func.clone()));
3787                                func
3788                            }
3789                            Some(SymbolResolutionResult::StubFunction(_)) | None => {
3790                                trace!(?requesting_module, name, "Resolving function");
3791
3792                                let Some((resolved_from, export)) =
3793                                    group_state.resolve_exported_symbol(name.as_str())
3794                                else {
3795                                    trace!(?requesting_module, name, "Failed to resolve symbol");
3796                                    *resolved_guard = Some(None);
3797                                    return Err(mk_error());
3798                                };
3799                                let Extern::Function(func) = export else {
3800                                    trace!(
3801                                        ?requesting_module,
3802                                        name,
3803                                        ?resolved_from,
3804                                        "Resolved symbol is not a function"
3805                                    );
3806                                    *resolved_guard = Some(None);
3807                                    return Err(mk_error());
3808                                };
3809                                if func.ty(&store) != ty {
3810                                    trace!(
3811                                        ?requesting_module,
3812                                        name,
3813                                        ?resolved_from,
3814                                        "Resolved function has bad type"
3815                                    );
3816                                    *resolved_guard = Some(None);
3817                                    return Err(mk_error());
3818                                }
3819
3820                                trace!(
3821                                    ?requesting_module,
3822                                    name,
3823                                    ?resolved_from,
3824                                    "Function resolved successfully"
3825                                );
3826
3827                                // Only store the result if we can also put it in the linker's
3828                                // resolution records for other groups to find.
3829                                if let Some(mut linker_state) = linker_state {
3830                                    trace!(
3831                                        ?requesting_module,
3832                                        name,
3833                                        ?resolved_from,
3834                                        "Updating linker state with this resolution"
3835                                    );
3836
3837                                    *resolved_guard = Some(Some(func.clone()));
3838                                    linker_state.symbol_resolution_records.insert(
3839                                        resolution_key,
3840                                        SymbolResolutionResult::Function {
3841                                            ty: func.ty(&store),
3842                                            resolved_from,
3843                                        },
3844                                    );
3845                                }
3846
3847                                func.clone()
3848                            }
3849                            Some(resolution) => panic!(
3850                                "Internal error: resolution record for symbol \
3851                                {name} indicates non-function resolution {resolution:?}"
3852                            ),
3853                        }
3854                    }
3855                    Some(None) => return Err(mk_error()),
3856                    Some(Some(ref func)) => func.clone(),
3857                };
3858                drop(resolved_guard);
3859
3860                let mut store = env.as_store_mut();
3861                func.call(&mut store, params)
3862                    .map(|ret| ret.into())
3863                    .map_err(crate::flatten_runtime_error)
3864            },
3865        )
3866    }
3867
3868    fn finalize_pending_globals(
3869        &self,
3870        linker_state: &mut LinkerState,
3871        store: &mut impl AsStoreMut,
3872        unresolved_globals: &Vec<UnresolvedGlobal>,
3873    ) -> Result<(), LinkError> {
3874        trace!("Finalizing pending globals");
3875
3876        for unresolved in unresolved_globals {
3877            let key = unresolved.key();
3878            let import_metadata = &linker_state.dylink_info(key.module_handle).import_metadata;
3879            let is_weak = import_metadata
3880                .get(&(key.import_module.to_owned(), key.import_name.to_owned()))
3881                // clang seems to like putting the import-info in the "env" module
3882                // sometimes, so try that as well
3883                .or_else(|| import_metadata.get(&("env".to_owned(), key.import_name.to_owned())))
3884                .map(|flags| flags.contains(wasmparser::SymbolFlags::BINDING_WEAK))
3885                .unwrap_or(false);
3886            trace!(?unresolved, is_weak, "Resolving pending global");
3887
3888            match (
3889                unresolved,
3890                self.resolve_export(linker_state, store, None, &key.import_name, true),
3891            ) {
3892                (
3893                    UnresolvedGlobal::Mem(key, global),
3894                    Ok((PartiallyResolvedExport::Global(addr), resolved_from)),
3895                ) => {
3896                    trace!(
3897                        ?unresolved,
3898                        ?resolved_from,
3899                        addr,
3900                        "Resolved to memory address"
3901                    );
3902                    set_integer_global(store, &key.import_name, global, addr)?;
3903                    linker_state.symbol_resolution_records.insert(
3904                        SymbolResolutionKey::Needed(key.clone()),
3905                        SymbolResolutionResult::Memory(addr),
3906                    );
3907                }
3908
3909                (
3910                    UnresolvedGlobal::Mem(key, global),
3911                    Ok((PartiallyResolvedExport::Tls { offset, final_addr }, resolved_from)),
3912                ) => {
3913                    trace!(
3914                        ?unresolved,
3915                        ?resolved_from,
3916                        offset,
3917                        final_addr,
3918                        "Resolved to TLS address"
3919                    );
3920                    set_integer_global(store, &key.import_name, global, final_addr)?;
3921                    linker_state.symbol_resolution_records.insert(
3922                        SymbolResolutionKey::Needed(key.clone()),
3923                        SymbolResolutionResult::Tls {
3924                            resolved_from,
3925                            offset,
3926                        },
3927                    );
3928                }
3929
3930                (
3931                    UnresolvedGlobal::Func(key, global),
3932                    Ok((PartiallyResolvedExport::Function(func), resolved_from)),
3933                ) => {
3934                    let func_handle = self
3935                        .append_to_function_table(store, func)
3936                        .map_err(LinkError::TableAllocationError)?;
3937                    trace!(
3938                        ?unresolved,
3939                        ?resolved_from,
3940                        function_table_index = ?func_handle,
3941                        "Resolved to function pointer"
3942                    );
3943                    set_integer_global(store, &key.import_name, global, func_handle as u64)?;
3944                    linker_state.symbol_resolution_records.insert(
3945                        SymbolResolutionKey::Needed(key.clone()),
3946                        SymbolResolutionResult::FunctionPointer {
3947                            resolved_from,
3948                            function_table_index: func_handle,
3949                        },
3950                    );
3951                }
3952
3953                // Expected memory address, resolved function or vice-versa
3954                (_, Ok(_)) => {
3955                    return Err(LinkError::UnresolvedGlobal(
3956                        unresolved.import_module().to_string(),
3957                        key.import_name.clone(),
3958                        Box::new(ResolveError::MissingExport),
3959                    ));
3960                }
3961
3962                // Missing weak symbols get resolved to a null address
3963                (_, Err(ResolveError::MissingExport)) if is_weak => {
3964                    trace!(?unresolved, "Weak global not found");
3965                    set_integer_global(store, &key.import_name, unresolved.global(), 0)?;
3966                    linker_state.symbol_resolution_records.insert(
3967                        SymbolResolutionKey::Needed(key.clone()),
3968                        SymbolResolutionResult::Memory(0),
3969                    );
3970                }
3971
3972                (_, Err(e)) => {
3973                    return Err(LinkError::UnresolvedGlobal(
3974                        "GOT.mem".to_string(),
3975                        key.import_name.clone(),
3976                        Box::new(e),
3977                    ));
3978                }
3979            }
3980        }
3981
3982        Ok(())
3983    }
3984}
3985
3986async fn locate_module(
3987    module_path: &Path,
3988    library_path: &[impl AsRef<Path>],
3989    runtime_path: &[impl AsRef<str>],
3990    calling_module_path: Option<impl AsRef<Path>>,
3991    fs: &WasiFs,
3992) -> Result<(PathBuf, OwnedBuffer), LinkError> {
3993    async fn try_load(
3994        fs: &WasiFsRoot,
3995        path: impl AsRef<Path>,
3996    ) -> Result<(PathBuf, OwnedBuffer), FsError> {
3997        let mut file = match fs.new_open_options().read(true).open(path.as_ref()) {
3998            Ok(f) => f,
3999            // Fallback for cases where the module thinks it's running on unix,
4000            // but the compiled side module is a .wasm file
4001            Err(_) if path.as_ref().extension() == Some(OsStr::new("so")) => fs
4002                .new_open_options()
4003                .read(true)
4004                .open(path.as_ref().with_extension("wasm"))?,
4005            Err(e) => return Err(e),
4006        };
4007
4008        let buf = if let Some(buf) = file.as_owned_buffer() {
4009            buf
4010        } else {
4011            let mut buf = Vec::new();
4012            file.read_to_end(&mut buf).await?;
4013            OwnedBuffer::from(buf)
4014        };
4015
4016        Ok((path.as_ref().to_owned(), buf))
4017    }
4018
4019    if module_path.is_absolute() {
4020        trace!(?module_path, "Locating module with absolute path");
4021        try_load(&fs.root_fs, module_path).await.map_err(|e| {
4022            LinkError::SharedLibraryMissing(
4023                module_path.to_string_lossy().into_owned(),
4024                LocateModuleError::Single(e),
4025            )
4026        })
4027    } else if module_path.components().count() > 1 {
4028        trace!(?module_path, "Locating module with relative path");
4029        try_load(
4030            &fs.root_fs,
4031            fs.relative_path_to_absolute(module_path.to_string_lossy().into_owned()),
4032        )
4033        .await
4034        .map_err(|e| {
4035            LinkError::SharedLibraryMissing(
4036                module_path.to_string_lossy().into_owned(),
4037                LocateModuleError::Single(e),
4038            )
4039        })
4040    } else {
4041        // Go through all dynamic library lookup paths
4042        // Note: a path without a slash does *not* look at the current directory. This is by design.
4043
4044        trace!(
4045            ?module_path,
4046            "Locating module by name in default runtime path"
4047        );
4048
4049        let calling_module_dir = calling_module_path
4050            .as_ref()
4051            .map(|p| p.as_ref().parent().unwrap_or_else(|| p.as_ref()));
4052
4053        let runtime_path = runtime_path.iter().map(|path| {
4054            let path = path.as_ref();
4055
4056            let relative = path
4057                .strip_prefix("$ORIGIN")
4058                .or_else(|| path.strip_prefix("${ORIGIN}"));
4059
4060            match relative {
4061                Some(relative) => {
4062                    let Some(calling_module_dir) = calling_module_dir else {
4063                        // This is an internal error because the only time calling_module_path
4064                        // should be empty is when loading a module through dlopen, and a
4065                        // dlopen'ed module isn't being required by another module so we don't
4066                        // have a RUNPATH to consider at all. See the invocation of
4067                        // `load_module_tree` in `load_module`.
4068                        panic!(
4069                            "Internal error: $ORIGIN or ${{ORIGIN}} in RUNPATH, but \
4070                            no calling module path provided"
4071                        );
4072                    };
4073                    Cow::Owned(PathBuf::from(
4074                        fs.relative_path_to_absolute(
4075                            calling_module_dir
4076                                .join(relative)
4077                                .to_string_lossy()
4078                                .into_owned(),
4079                        ),
4080                    ))
4081                }
4082                None => Cow::Borrowed(Path::new(path)),
4083            }
4084        });
4085
4086        // Search order is: LD_LIBRARY_PATH -> RUNPATH -> system default folders
4087        let search_paths = library_path
4088            .iter()
4089            .map(|path| Cow::Borrowed(path.as_ref()))
4090            .chain(runtime_path)
4091            .chain(
4092                DEFAULT_RUNTIME_PATH
4093                    .iter()
4094                    .map(|path| Cow::Borrowed(Path::new(path))),
4095            );
4096
4097        let mut errors: Vec<(PathBuf, FsError)> = Vec::new();
4098        for path in search_paths {
4099            let full_path = path.join(module_path);
4100            trace!(search_path = ?path, full_path = ?full_path, "Searching module");
4101            match try_load(&fs.root_fs, &full_path).await {
4102                Ok(ret) => {
4103                    trace!(?module_path, full_path = ?ret.0, "Located module");
4104                    return Ok(ret);
4105                }
4106                Err(e) => errors.push((full_path, e)),
4107            };
4108        }
4109
4110        trace!(?module_path, "Failed to locate module");
4111        Err(LinkError::SharedLibraryMissing(
4112            module_path.to_string_lossy().into_owned(),
4113            LocateModuleError::Multiple(errors),
4114        ))
4115    }
4116}
4117
4118pub fn is_dynamically_linked(module: &Module) -> bool {
4119    module.custom_sections("dylink.0").next().is_some()
4120}
4121
4122pub fn parse_dylink0_section(module: &Module) -> Result<DylinkInfo, LinkError> {
4123    let mut sections = module.custom_sections("dylink.0");
4124
4125    let Some(section) = sections.next() else {
4126        return Err(LinkError::NotDynamicLibrary);
4127    };
4128
4129    // Verify the module contains exactly one dylink.0 section
4130    let None = sections.next() else {
4131        return Err(LinkError::NotDynamicLibrary);
4132    };
4133
4134    let reader = wasmparser::Dylink0SectionReader::new(wasmparser::BinaryReader::new(&section, 0));
4135
4136    let mut mem_info = None;
4137    let mut needed = None;
4138    let mut import_metadata = HashMap::new();
4139    let mut export_metadata = HashMap::new();
4140    let mut runtime_path = Vec::new();
4141
4142    for subsection in reader {
4143        let subsection = subsection?;
4144        match subsection {
4145            wasmparser::Dylink0Subsection::MemInfo(m) => {
4146                mem_info = Some(m);
4147            }
4148            wasmparser::Dylink0Subsection::Needed(n) => {
4149                needed = Some(n.iter().map(|s| s.to_string()).collect::<Vec<_>>());
4150            }
4151            wasmparser::Dylink0Subsection::ImportInfo(i) => {
4152                for i in i {
4153                    import_metadata.insert((i.module.to_owned(), i.field.to_owned()), i.flags);
4154                }
4155            }
4156            wasmparser::Dylink0Subsection::ExportInfo(e) => {
4157                for e in e {
4158                    export_metadata.insert(e.name.to_owned(), e.flags);
4159                }
4160            }
4161            wasmparser::Dylink0Subsection::Unknown { ty, .. } => {
4162                tracing::warn!("Skipping unknown dylink.0 subsection {ty}");
4163            }
4164            wasmparser::Dylink0Subsection::RuntimePath(path) => {
4165                runtime_path.extend(path.into_iter().map(|path| path.to_string()));
4166            }
4167        }
4168    }
4169
4170    Ok(DylinkInfo {
4171        mem_info: mem_info.unwrap_or(wasmparser::MemInfo {
4172            memory_size: 0,
4173            memory_alignment: 0,
4174            table_size: 0,
4175            table_alignment: 0,
4176        }),
4177        needed: needed.unwrap_or_default(),
4178        import_metadata,
4179        export_metadata,
4180        runtime_path,
4181    })
4182}
4183
4184fn get_integer_global_type_from_import(import: &ImportType) -> Result<GlobalType, LinkError> {
4185    let import_type = import.ty();
4186    let ExternType::Global(ty) = import_type else {
4187        return Err(LinkError::BadImport(
4188            import.module().to_owned(),
4189            import.name().to_owned(),
4190            import_type.clone(),
4191        ));
4192    };
4193
4194    if !matches!(ty.ty, Type::I32 | Type::I64) {
4195        return Err(LinkError::NonIntegerGlobal(
4196            import.module().to_owned(),
4197            import.name().to_owned(),
4198        ));
4199    }
4200
4201    Ok(*ty)
4202}
4203
4204fn define_integer_global_import(
4205    store: &mut impl AsStoreMut,
4206    import: &ImportType,
4207    value: u64,
4208) -> Result<Global, LinkError> {
4209    let ExternType::Global(GlobalType { ty, mutability }) = import.ty() else {
4210        return Err(LinkError::BadImport(
4211            import.module().to_string(),
4212            import.name().to_string(),
4213            import.ty().clone(),
4214        ));
4215    };
4216
4217    let new_global = if mutability.is_mutable() {
4218        Global::new_mut
4219    } else {
4220        Global::new
4221    };
4222
4223    let global = match ty {
4224        Type::I32 => new_global(store, wasmer::Value::I32(value as i32)),
4225        Type::I64 => new_global(store, wasmer::Value::I64(value as i64)),
4226        _ => {
4227            return Err(LinkError::BadImport(
4228                import.module().to_string(),
4229                import.name().to_string(),
4230                import.ty().clone(),
4231            ));
4232        }
4233    };
4234
4235    Ok(global)
4236}
4237
4238fn set_integer_global(
4239    store: &mut impl AsStoreMut,
4240    name: &str,
4241    global: &Global,
4242    value: u64,
4243) -> Result<(), LinkError> {
4244    match global.ty(store).ty {
4245        Type::I32 => global
4246            .set(store, Value::I32(value as i32))
4247            .map_err(|e| LinkError::GlobalUpdateFailed(name.to_owned(), e))?,
4248        Type::I64 => global
4249            .set(store, Value::I64(value as i64))
4250            .map_err(|e| LinkError::GlobalUpdateFailed(name.to_owned(), e))?,
4251        _ => {
4252            // This should be caught by resolve_global_import, so just panic here
4253            unreachable!("Internal error: expected global of type I32 or I64");
4254        }
4255    }
4256
4257    Ok(())
4258}
4259
4260fn call_initialization_function<Ret: WasmTypeList>(
4261    instance: &Instance,
4262    store: &mut impl AsStoreMut,
4263    name: &str,
4264) -> Result<Option<Ret>, LinkError> {
4265    match instance.exports.get_typed_function::<(), Ret>(store, name) {
4266        Ok(f) => {
4267            let ret = f
4268                .call(store)
4269                .map_err(|e| LinkError::InitFunctionFailed(name.to_string(), e))?;
4270            Ok(Some(ret))
4271        }
4272        Err(ExportError::Missing(_)) => Ok(None),
4273        Err(ExportError::IncompatibleType) => {
4274            Err(LinkError::InitFuncWithInvalidSignature(name.to_string()))
4275        }
4276    }
4277}
4278
4279fn get_tls_base_export(
4280    instance: &Instance,
4281    store: &mut impl AsStoreMut,
4282) -> Result<Option<u64>, LinkError> {
4283    match instance.exports.get_global("__tls_base") {
4284        Ok(global) => match global.get(store) {
4285            Value::I32(x) => Ok(Some(x as u64)),
4286            Value::I64(x) => Ok(Some(x as u64)),
4287            _ => Err(LinkError::BadTlsBaseExport),
4288        },
4289        Err(ExportError::Missing(_)) => Ok(None),
4290        Err(ExportError::IncompatibleType) => Err(LinkError::BadTlsBaseExport),
4291    }
4292}
4293
4294#[cfg(test)]
4295mod memory_allocator_tests {
4296    use wasmer::{Engine, Memory, Store};
4297
4298    use super::MemoryAllocator;
4299
4300    const WASM_PAGE_SIZE: u32 = wasmer::WASM_PAGE_SIZE as u32;
4301
4302    #[test]
4303    fn test_memory_allocator() {
4304        let engine = Engine::default();
4305        let mut store = Store::new(engine);
4306        let memory = Memory::new(
4307            &mut store,
4308            wasmer::MemoryType {
4309                minimum: wasmer::Pages(2),
4310                maximum: None,
4311                shared: true,
4312            },
4313        )
4314        .unwrap();
4315        let mut allocator = MemoryAllocator::new();
4316
4317        // Small allocation in new page
4318        let addr = allocator.allocate(&memory, &mut store, 24, 4).unwrap();
4319        assert_eq!(addr, 2 * WASM_PAGE_SIZE);
4320        assert_eq!(memory.grow(&mut store, 0).unwrap().0, 3);
4321
4322        // Small allocation in existing page
4323        let addr = allocator.allocate(&memory, &mut store, 16, 4).unwrap();
4324        assert_eq!(addr, 2 * WASM_PAGE_SIZE + 24);
4325
4326        // Small allocation in existing page, with bigger alignment
4327        let addr = allocator.allocate(&memory, &mut store, 64, 32).unwrap();
4328        assert_eq!(addr, 2 * WASM_PAGE_SIZE + 64);
4329        // Should still have 3 pages
4330        assert_eq!(memory.grow(&mut store, 0).unwrap().0, 3);
4331
4332        // Big allocation in new pages
4333        let addr = allocator
4334            .allocate(&memory, &mut store, 2 * WASM_PAGE_SIZE + 256, 1024)
4335            .unwrap();
4336        assert_eq!(addr, WASM_PAGE_SIZE * 3);
4337        assert_eq!(memory.grow(&mut store, 0).unwrap().0, 6);
4338
4339        // Small allocation with multiple empty pages
4340        // page 2 has 128 bytes allocated, page 5 has 256, allocation should go
4341        // to page 5 (we should allocate from the page with the least free space)
4342        let addr = allocator
4343            .allocate(&memory, &mut store, 1024 * 63, 64)
4344            .unwrap();
4345        assert_eq!(addr, 5 * WASM_PAGE_SIZE + 256);
4346
4347        // Another small allocation, but this time it won't fit on page 5
4348        let addr = allocator.allocate(&memory, &mut store, 4096, 512).unwrap();
4349        assert_eq!(addr, 2 * WASM_PAGE_SIZE + 512);
4350    }
4351}