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