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