Embedding Wasm - part 1 - Introduction
WebAssembly (Wasm) is an emulation of a conceptual machine. True portability is achieved by making no assumptions on the host machine that actually executes the code. By compiling your rust, C/C++ ... code into Wasm target you can run your applications anywhere either on the web, standalone or embedded in other applications.
In this series I'll go through the process of embedding and integration between a native application and a Wasm application. WebAssembly applications and libraries needs to be compiled and packaged only once and then re-used in many contexts.
An application is compiled into a one or more Wasm modules. A Wasm module is the file that packages the code, it is similar to .exe and .dll files for Windows. Usually the module file has the extension .wasm. The module contains the following components:
- The list of functions and their code. The code is a list of instructions that manipulate the memory buffer, perform calculations and branch executions.
- The list of imports. Usually an application needs to perform more than accessing the internal memory and calculate some numbers. The imports are its window to access external word, They are the functions and globals that need to be provided by another module or the execution environment.
- The list of exports. They are the list of functions and globals that are provided by the module and can be used by other modules or the execution environment.
- The list of global values
- The memory buffer and its initial values. The module defines the minimum memory required and may grow dynamically up to the defined maximum value.
WAPM is a good starting point to look for a pre-compiled module. It is a registry of a public collection of packages of open-source code for WebAssembly, front-end web apps, mobile apps and servers. It is also a package manager for WebAssembly. You can download the Wasm modules directly from the web site or use the command line tool. The command line tool is bundled with wasmer.
NPM is another repository, some Wasm modules were pre-packaged inside nodejs modules. Onigasm is a random sample that packages a C library as a wasm module, but it is distributed as a nodejs package.
What if you need to build yourself a module for your own code or someone else's code that didn't bother to? There is a continuous effort to support different languages, most of them are directed for the web browsers, where WebAssembly started. In addition of generating the wasm module, they also generate some wrapper JS and HTML files. The wrapper JS provides some services to the code on top of the different services provided by the browser. This means that many of the generated modules can run only inside the browser. You can take a look at Awesome WebAssembly Languages and dig into the supplied links for more details per supported languages.
Using wasm modules in non-web scenarios requires a more generic solution that doesn't make assumptions about the execution environment. That is why WASI, the WebAssembly system interface, was created. It provides a standard and secure way to talk to the operating system, any operating system. Currently it provides filesystem, socket, clock, random number generation and environment variables access. So WASI can be considered the operating system of the WebAssembly application. The execution environment maps the WASI services to the acual host operating system services.
Enough theoretical talks! let's get our hands dirty and create our "Hello Wasm" application. In this example I'm using wasi-sdk, which provides a C/C++ compiler with wasm support (clang/llvm) and a libc implementation that supports WASI.
Create the following hello.c file:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello Wasm\n");
return 0;
}
Download and extract wasi-sdk from https://github.com/WebAssembly/wasi-sdk/releases:
$ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-10/wasi-sdk-10.0-linux.tar.gz
$ tar xvf wasi-sdk-10.0-linux.tar.gz
Compile the application:
$ export WASI_SDK_PATH=(pwd)/wasi-sdk-10.0
$ export CC="$WASI_SDK_PATH/bin/clang --sysroot=$WASI_SDK_PATH/share/wasi-sysroot"
$ $CC hello.c -o hello.wasm
$ ls -l
total 45160
-rw-rw-rw- 1 ahmed ahmed 98 May 29 11:56 hello.c
-rwxr-xr-x 1 ahmed ahmed 28739 May 29 16:52 hello.wasm
$ file hello.wasm
hello.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
As you can see, a WebAssembly module file was created. This is a standalone wasm executable module. A runtime environment is needed to execute the generated wasm module. Several are available like wasmer and wasmtime. In this example I'll use wasmtime.
Download and install:
$ curl https://wasmtime.dev/install.sh -sSf | bash
Then open a new shell to load the updated PATH.
And finally execute the wasm module:
$ wasmtime hello.wasm
Hello Wasm
Conclusion
In this post I gave a high-level idea about WebAssembly echo-system and went through compiling and executing a simple application. I plan in next posts to discuss in more details using WebAssembly in different execution environments.