blob: d0a2c3e1f5e25472a77d667891c402d09b4c6629 [file] [edit]
.. _Dynamic-Linking:
===============
Dynamic Linking
===============
.. note:: Dynamic linking in emscripten is still somewhat experimental and subject to change. This documentation is also curently somewhat outdated the in the process of being refreshed.
Emscripten supports linking object files (and ar archives that contain
object files) statically. This lets most build systems work with Emscripten
with little or no changes (see :ref:`Building-Projects`).
In addition, Emscripten also has support for a form of **dynamic** linking of
WebAssembly modules. This can add overhead, so for best performance static
linking should still be prefered. However, this overhead can can be reduced
with the use of certain command line flags, see below for details.
Background
==========
Before we get to dynamic linking, lets talk about static linking.
Emscriptens linking model is a little different than most native
platforms. To understand it, consider that native linking models work in
a setting where the following facts are true:
1. The application runs directly on the local system, and has access to
local system libraries, like C and C++ standard libraries, and
others.
2. Code size is not a big concern. In part this is because the system
libraries already exist on the system, so hello world in C++ can be
small, even if it uses a large amount of iostream code in the C++
standard library. But also, code size is perhaps a matter that
influences cold startup times, in that more code takes longer to load
from disk, but the cost is general not significant, and modern OSes
mitigate it in various ways, like caching apps they expect to be
loaded.
In Emscriptens case, code is typically going to run on the web. That
means the following:
1. The application is running in a sandbox. It has no local system
libraries to dynamically link to; it must ship its own system library
code.
2. Code size is a major concern, as the applications code is being
downloaded over the internet, which is many orders of magnitude
slower than an installed native app on ones local machine.
For that reason, Emscripten automatically handles system libraries for
you and automatically does dead code elimination etc. to do the best
possible job it can at getting them small.
An additional factor here is that Emscripten has js libraries - system
libraries written in JavaScript. Such system libraries are the way we
access APIs on the web. Its also a convenient way for people to connect
compiled code and handwritten code on the same page. This is another
reason for Emscripten to handle system libraries in a special way, and
in particular, in a way that lets it strip out as much of those js
libraries as it can, leaving only what is actually used, and again, that
works best in the context of statically linking a standalone app with no
external dependencies.
Overview of Dynamic Linking
===========================
Emscriptens dynamic linking is fairly simple: you build several
separate code modules containing JavaScript, and can link them at
runtime. The linking basically connects up the unresolved symbols in
each module with the implemented symbols in the others, in the simplest
of ways. It does not currently support some corner cases.
System libraries do utilize some more advanced linking features that
include such corner cases. For that reason, Emscripten tries to simplify
the problem as follows: There are two types of shared modules:
1. **Main modules**, which have system libraries linked in.
2. **Side modules**, which do not have system libraries linked in.
A project should contain **exactly one** main module. It can then be
linked at runtime to multiple side modules. This model also makes other
things simpler, like only the singleton main module has the general
JavaScript environment setup code to connect to the web page and so
forth; side modules contain just the pure compiled WebAssembly and
nothing more.
The one tricky aspect to this design is that a side module might need a
system library that the main doesnt know about. See the section on
system libraries, below, for how to handle that.
Note that the main module doesnt need to contain the ``main()``
function. It could just as easily be in a side module. What makes the
main module the main module is just that there is only one main
module, and only it has system libs linked in.
(Note that system libraries are linked in to the main module
*statically*. We still have some optimizations from doing it that way,
even if we cant dead code eliminate as well as wed like.)
Practical Details
=================
If you want to jump to see running code, you can look in the test suite.
There are ``test_dylink_*`` tests that test general dynamic linking, and
``test_dlfcn_*`` tests that test ``dlopen()`` specifically. Otherwise,
we describe the procedure now.
General Dynamic Linking
-----------------------
- Build one part of your code as the main module, using
``-s MAIN_MODULE=1``.
- Build other parts of your code as side modules, using
``-s SIDE_MODULE=1``.
Important: since 1.38.16 you need to set ``-s EXPORT_ALL=1`` (for
SIDE_MODULEs, as well as the MAIN_MODULE if it exposes functions to the
modules). Alternatively, use ``-s EXPORTED_FUNCTIONS`` to declare the
exported functions. Without either of them, modules are useless.
Note that both should have suffix ``.js`` or ``.wasm`` (``emcc`` uses
suffixes to know what to emit). If you want, you can then rename the
side modules to ``.so`` or such (but it is just a superficial change.)
You then need to tell the main module to load the sides. You can do that
using the ``Module`` object, with something like
::
Module.dynamicLibraries = ['libsomething.js'];
At runtime, when you run the main module, if it sees
``dynamicLibraries`` on ``Module``, then it loads them one by one and
links them. The running application then can access code from any of the
modules linked together.
``dlopen()`` Dynamic Linking
----------------------------
``dlopen()`` is slightly simpler than general dynamic linking. The
procedure begins in the same way, with the same flags used to build the
main and side modules. The difference is that you do not use
``Module.dynamicLibraries``; instead, you must load the side module into
the filesystem, so that ``dlopen`` (or ``fopen``, etc.) can access it
(except for ``dlopen(NULL)`` which means to open the current executable,
which just works without filesystem integration). Thats basically it -
you can then use ``dlopen(), dlsym()``, etc. normally.
System Libraries
================
As mentioned earlier, system libraries are handled in a special way by
the Emscripten linker, and in dynamic linking, only the main module is
linked against system libraries. A possible issue is if a side module
needs a system library that the main does not. If so, youll get a
runtime error. This section explains what to do to fix that.
To get around this, you can build the main module with
``EMCC_FORCE_STDLIBS=1`` in the environment to force inclusion of all
standard libs. A more refined approach is to build the side module with
``-v`` in order to see which system libs are actually needed - look for
``including lib[...]`` messages - and then building the main module with
something like ``EMCC_FORCE_STDLIBS=libcxx,libcxxabi`` (if you need
those two libs).
Code Size
=========
By default, main modules disable dead code elimination. That means that
all the code compiled remains in the output, including all system
libraries linked in, and also all the JS library code.
That is the default behavior since it is the least surprising. But it is
also possible to use normal dead code elimination, by building with
``-s MAIN_MODULE=2`` (instead of 1). In that mode, the main module is
built normally, with no special behavior for keeping code alive. It is
then your responsibility to make sure that code that side modules need
is kept alive. You can do this in the usual ways, like adding to
``EXPORTED_FUNCTIONS``. See ``other.test_minimal_dynamic`` for an
example of this in action. There is also the corresponding
``-s SIDE_MODULE=2`` for side modules.
Miscellaneous Notes
===================
Dynamic Checks
--------------
Native linkers generally only run code when all symbols are resolved.
Emscriptens dynamic linker hooks up symbols to unresolved references to
those symbols **dynamically**. As a result, we dont check if any
symbols remain unresolved, and code can start to run even if there are.
It will run successfully if they are not called in practice. If they
are, you will get a runtime error. What went wrong should be clear from
the stack trace (in an unminified build); building with
``-s ASSERTIONS=1`` can help some more.
Limitations
-----------
- Chromium does not support compiling >4kB WASM on the main thread, and
that includes side modules; you can use ``--use-preload-plugins`` (in
``emcc`` or ``file_packager.py``) to make Emscripten compile them on
startup
`[doc] <https://emscripten.org/docs/porting/files/packaging_files.html#preloading-files>`__
`[discuss] <https://groups.google.com/forum/#!topic/emscripten-discuss/cE3hUV3fDSw>`__.
- See also `webAssembly standalone <https://github.com/emscripten-core/emscripten/wiki/WebAssembly-Standalone>`_` for more on side modules in this context.
Pthreads support
----------------
Dynamic linking + pthreads is not supported. It would require new wasm
spec features (a way to share the Table), or some serious workarounds in
the toolchain.