-
Notifications
You must be signed in to change notification settings - Fork 3.3k
WebAssembly Standalone
When compiling to WebAssembly, emcc
by default emits both a JavaScript file and a WebAssembly file. The JavaScript loads the WebAssembly which contains the compiled code. This is necessary in many cases as WebAssembly currently depends on a JavaScript runtime for features like longjmp, C++ exceptions, checking the date or time, printing to the console, using an API like WebGL, etc., and Emscripten will emit a JavaScript runtime to support those things.
However, if you don't need those things - if you have mostly pure computational code - or if you want to write your own JavaScript runtime, you can tell Emscripten to emit a "standalone" wasm file, one which doesn't depend on an Emscripten JS runtime. That lets you write your own runtime if you want, and you may be able to run the wasm in a a non-JS environment such as Wasmer, WAVM, or wasmtime.
Emscripten has a STANDALONE_WASM
option that is the main way we support this (note that it requires the LLVM wasm backend). The flag is turned on automatically if you tell emcc to only emit a wasm file,
$ emcc source.c -o output.wasm
Otherwise, if you build with the flag,
$ emcc source.c -s STANDALONE_WASM
then it will emit a .js
and .wasm
file. The wasm is standalone as before, so you don't need the JS, but it may be convenient for running it on the Web.
Many APIs that emscripten uses depend on JS or Web APIs, though, like C++ exceptions as mentioned earlier, things like WebGL or WebAudio, etc. Calls to those APIs will still be emitted in standalone mode because we have no better alternative; this also gives you the option to add necessary APIs to a custom wasm embedding.
Aside from APIs that require JS or Web APIs, though, we try to use WASI APIs as much as possible so that the wasm can run in wasi-supporting runtimes.
For more details, see the blogpost on standalone wasm support, and the meshoptimizer example.
In normal emscripten output we create the Memory and Table in JS, then import them in the wasm (that lets us start to operate on them in JS in parallel to wasm compilation). In standalone wasm, they are created in the wasm itself, which lets it not depend on outside code.
The memory is exported by default. You can also export the table if you want, using a wasm-ld
flag, by passing -Wl,--export-table
to emcc.
emcc can optionally emit useful metadata in the wasm file, that a runtime can use. See the EMIT_EMSCRIPTEN_METADATA
option, added in #7815. The metadata includes versioning as well as things like the memory and table sizes the wasm needs, that the runtime needs to provide.
As of 1.37.29, emcc
's optimizer is powerful enough to remove all runtime elements that are not used (it does this using meta-dce, dead code elimination that crosses the JavaScript/WebAssembly boundary). This can be helpful as then the necessary runtime is smaller and easier to replace, making the output even more standalone. To try this, simply build with something like
emcc source.c -Os
-Os
is needed to make the optimizer work at full power. You can also use -Oz
or -O3
(but not -O2
or lower).
Notes:
- If you use C++ objects, the compiler must in many cases emit code to catch exceptions so that RAII destructors are called, and exceptions require a lot of runtime support. Build with
-fno-exceptions
to disable exceptions entirely. - More details on how this works.
Dynamic libraries have a formal definition, and are designed to be loadable in a standard way. They also do not link in system libraries like libc. For those reasons they may be useful in some cases, but dynamic libraries also have downsides, such as having relocations for memory and function pointers, which add overhead that may be unnecessary if you are only using one module (and not linking several together), so overall they are not recommended - use STANDALONE_WASM
as described earlier.
If you do want to build a dynamic library, use
emcc source.c -s SIDE_MODULE=1 -o target.wasm
That will emit the output dynamic library as target.wasm
.
To use a side module, see loadWebAssemblyModule
in src/runtime.js
for some example loading code.
- The conventions for a wasm dynamic library are here.
- Note that there is no special handling of a C stack. A module can have one internally if it wants one (it needs to ask for the memory for it, then handle it however it wants).
- Full example.
To just see your own compiled code you can build to a wasm object file,
emcc input.cpp -o output.wasm -c
You can inspect that wasm file using wabt's wasm2wat
or binaryen's wasm-dis
.
See also the next section on not linking in system libraries, which is another way to see just your own code.
Emscripten will normally link in libc and other system library code as needed. If you want to avoid that and implement system libraries in the runtime that loads the wasm (which lets the wasm files be smaller) then you can build with EMCC_ONLY_FORCED_STDLIBS=1
in the environment, which will link in only system libs that you specify (so if you specify none, none are linked in). Any calls to libc and other system libraries will end up as unresolved symbols, which error by default, but you can pass -s ERROR_ON_UNDEFINED_SYMBOLS=0
to turn those into just warnings, and the wasm will have imports for them.
For example:
EMCC_ONLY_FORCED_STDLIBS=1 ./emcc tests/hello_world.c -o a.wasm -s ERROR_ON_UNDEFINED_SYMBOLS=0 -O3
That emits a very small wasm file that has an import for puts
from libc. (For comparison, without EMCC_ONLY_FORCED_STDLIBS=1
libc is linked in, which makes the binary much larger.)
README.md ``