}

Webassembly initial steps tutorial or how to start with wasm

Created:

Introduction

UPDATE: we create a new tutorial using emscripten, which is a more standard way of working with webassembly.

Webassembly is a new new standard being emerging that could be very used in the future, this technology is being developed in a W3C Community Group with Apple, Google, Microsoft and Mozilla.

The initial focus of the project is C/C++, so this is not a high level lenguage. Webassembly short name is wasm and is an improvement to Javascript and the browser. Webassembly is not a replace of Javascript, actualy complements it.

To use the features of webassembly you need to install chrome canary or firefox nigthly, but there is a microsoft edge preview of webassembly and Safari will support this technology also.

The main advantage is a faster execution but also web programming is not limited anymore to JavaScript only.

Remeber that webassembly is really new and browser is in the browser preview milestone as for 29th Nov, 2016.

In this tutorial we will setup a browser to execute webassembly, we are going to compile all the required tools to compile webassembly and finally we are going to do a Hell world example using webassembly to do a printf.

Let's get out hands dirty!

Step 1: Install the browser to check the demo

Since right now there is no support for canary on linux we are going to install firefox nighly. But if you are a macOs or Windows we recommend to use chrome canary. Remeber to execute firefox with the absolute path, if not you will be using your firefox installed version.

cd
wget https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-53.0a1.en-US.linux-x86_64.tar.bz2
tar xf firefox-53.0a1.en-US.linux-i686.tar.bz2
cd firefox
./firefox

Check that you are using the Firefox version nigthly, check the status bar to confirm this.

Configure the browsers to enable webassembly

  • For Firefox, you'll need to go to about:config in the url bar, and then tell it you'll be careful. After that, type wasm in the search bar and double click javascript.options.wasm until the value is set to true and then completely restart the browser.

  • For Chrome Canary, you'll need to go to chrome://flags and scroll down until you find Experimental WebAssembly, click the enable link and completely restart the browser.

Go to the demo page of webassembly

Step 2: Ubuntu requierements

Some libs are required to compile the binaryen toolchain:

sudo apt-get install git cmake make ninja-build

For building llvm with WebAssembly support you will need cmake 3.4.4 at least. Check appendix at the buttom of this tutorial for cmake installation from source code.

Step 3: Infrastructure and toolchain compilation

First we need to prepare LLVM with WebAssembly support. If you have an old cmake version, check the appendix at the buttom of this page.

git clone http://llvm.org/git/llvm.git
cd llvm/tools
git clone http://llvm.org/git/clang.git
cd ../projects
git clone http://llvm.org/git/compiler-rt.git
cd ..
mkdir build
cd build
cmake -G Ninja -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly ..
ninja
ninja install

if ninja eats all your memory, follow this recommendations (which will slowdown the compilation time):

  • Disable swap space with sudo swapoff -a during compilation, this will avoid freezing your system.
  • Check the output of echo $MAKEFLAFS and check how many processor the compilation uses. we recommend use ninja -j1
  • Use the gold linker, which uses less memory. To use gold linker change the symbolic link of ld (actually ld.bfd) to ld.gold.

To change ld.bdf to ld.gold, follow this steps

ls -lah /usr/bin/ld # check which ld you are using
sudo rm /usr/bin/ld
sudo ln -s /usr/bin/ld.gold /usr/bin/ld
ls -lah /usr/bin/ld # check that you are using gold linker

To reenable swap, use sudo sudo swapon -a

Now we are going to compile Binaryen, this will provide at least tree necessary binaries:

  • asm2wasm which compiles asm.js
  • s2wasm which compiles the LLVM WebAssembly's backend .s output format
  • mir2wasm which compiles Rust MIR
cd
git clone https://github.com/WebAssembly/binaryen
cd binaryen
cmake .
make

When the compilation finished you will have all the required executables in the bin directory. Optionally you can do a sudo make install.

Finnaly our last tool is WABT: The WebAssembly Binary Toolkit. Follow this steps for installation.

git clone https://github.com/WebAssembly/wabt.git
cd wabt
mkdir build
cd build
cmake -G Ninja -DBUILD_TESTS=OFF ..
ninja
sudo ninja install

Step 4: Hello world example

We need to prepare a html template with the required code to execute our Webassembly hello world application. Save this html to index.html to a new directory called hello_world.


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Hello world</title>
  <script>
     if ('WebAssembly' in window) {
        fetch('hello_world.wasm') // Fetch the binary
            .then(response => response.arrayBuffer())
            .then(buffer => WebAssembly.compile(buffer)) // Get a Module from the buffer
            .then(module => {
              // Get an Instance of the Module
              const instance = new WebAssembly.Instance(module);
              document.getElementById("btn").addEventListener("click", function() {
                    alert("count function result is : " + instance.exports.count());
                }, false);
            });
      } else {
        output.value = "Your browser doesn't support Web Assembly. You may need " +
        "to enable it in your browser's flags.";
      }
  </script>
  </head>
<body>
  <input type="button" id="btn" value="Click me to execute hello world!" />
</body>
</html>

Now lets do a print in C, save the following content to hello_world.c:

int counter = 100;

int count() {
    return counter + 1;
}

Now to compile the example:

clang -emit-llvm --target=wasm32 -S hello_world.c
llc hello_world.ll -march=wasm32
s2wasm hello_world.s > hello_world.wast
wast2wasm -o hello_world.wasm hello_world.wast

Step 5: Open the hello world example

Finally in this step we will open the example html with firefox:

cd hello_world
python -m SimpleHTTPServer 8000

alternatively you can use wasm-cli to check the wasm file only:

npm install -g wasm-cli
wasm hello_world.wasm

Open with firefox this url and open the index.html file.

Appendix

Error libgtk-3.so.0: cannot open shared object file: No such file or directory

When trying to launch firefox 53 you get the following error:

XPCOMGlueLoad error for file /home/parallels/firefox-32/libmozgtk.so:
libgtk-3.so.0: cannot open shared object file: No such file or directory
Couldn't load XPCOM.

Solution:

locate libgtk-3.so.0

if the output is

/usr/lib/x86_64-linux-gnu/libgtk-3.so.0
/usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1000.8

You need to download the 64-bit version of mozilla firefox.

Error: GDK_BACKEND does not match available displays

To solve this issue set the DISPLAY environment variables:

export DISPLAY=:0.0

Add this export to your .bashrc or .zshrc to always have this env. variable set to this value.

Error: CMake 3.4.3 or higher is required. You are running version 3.2.2

To fix this error, you need to download an compile cmake from source code.

wget https://cmake.org/files/v3.7/cmake-3.7.0.tar.gz
tar -xf cmake-3.7.0.tar.gz
cd cmake-3.7.0
./bootstrap
make
sudo make install

Error: Uncaught ReferenceError: Wasm is not defined

Instead of using the Wasm.instantiateModule() use the WebAssembly.Instance().

Error: Uncaught Error: memory access out of bounds

We will update this issue, but we manage to solve it in some cases using --allocate-stack s2wasm flag.