WebAssembly enables load-time and run-time (dlopen
) dynamic linking in the
MVP by having multiple instantiated modules
share functions, linear memories,
tables and constants
using module imports and exports. In
particular, since all (non-local) state that a module can access can be imported
and exported and thus shared between separate modules’ instances, toolchains
have the building blocks to implement dynamic loaders.
Since the manner in which modules are loaded and instantiated is defined by the host environment (e.g., the JavaScript API), dynamic linking requires use of host-specific functionality to link two modules. At a minimum, the host environment must provide a way to dynamically instantiate modules while connecting exports to imports.
The simplest load-time dynamic linking scheme between modules A and B can be achieved by having module A export functions, tables and memories that are imported by B. A C++ toolchain can expose this functionality by using the same function attributes currently used to export/import symbols from native DSOs/DLLs:
#ifdef _WIN32
# define EXPORT __declspec(dllexport)
# define IMPORT __declspec(dllimport)
#else
# define EXPORT __attribute__ ((visibility ("default")))
# define IMPORT __attribute__ ((visibility ("default")))
#endif
typedef void (**PF)();
IMPORT PF imp();
EXPORT void exp() { (*imp())(); }
This code would, at a minimum, generate a WebAssembly module with imports for:
imp
imp
and exports for:
exp
A more realistic module using libc would have more imports including:
i32
global import for the offset in linear memory to place
global data segments and later use as a constant
base address when loading and storing from globalsi32
global import for the offset into the indirect function
table at which to place the modules’ indirectly called functions and later
compute their indices for address-ofOne extra detail is what to use as the module name for
imports (since WebAssembly has a two-level namespace). One option is to have a
single default module name for all C/C++ imports/exports (which then allows the
toolchain to put implementation-internal names in a separate namespace, avoiding
the need for __
-prefix conventions).
To implement run-time dynamic linking (e.g., dlopen
and dlsym
):
dlopen
would compile and instantiate a new module, storing the compiled
instance in a host-environment table, returning the index to the caller.dlsym
would be given this index, pull the instance out of the table,
search the instances’s exports, append the found function to the function
table (using host-defined functionality in the MVP, but directly from
WebAssembly code in the
future ) and return the
table index of the appended element to the caller.Note that the representation of a C function-pointer in WebAssembly is an index
into a function table, so the above scheme lines up perfectly with the
function-pointer return value of dlsym
.
More complicated dynamic linking functionality (e.g., interposition, weak
symbols, etc) can be simulated efficiently by assigning a function table
index to each weak/mutable symbol, calling the symbol via call_indirect
on that
index, and mutating the underlying element as needed.
After the MVP, we would like to standardize a single ABI per source language, allowing for WebAssembly libraries to interface with each other regardless of compiler. Specifying an ABI requires that all ABI-related future features (like SIMD, multiple return values and exception handling) have been implemented. While it is highly recommended for compilers targeting WebAssembly to adhere to the specified ABI for interoperability, WebAssembly runtimes will be ABI agnostic, so it will be possible to use a non-standard ABI for specialized purposes.