Revisiting WebAssembly Cross Compilation in January 2024

I was recently contacted by someone trying to follow a
previous blog
on cross compiling C to WebAssembly. Things weren’t working; it seemed that things had changed.

This is a short post to look at how to cross compile an open source library to WebAssembly
using the current tooling (as of 2024-01-07).

As before I’ll use the GNU Scientific Library,
which (at the time of writing) is version 2.7.1.

The toolchain

As before I used the clang toolchain from wasi-sdk.
I downloaded wasi-sdk-21.0-linux.tar.gz
and unpacked it to $HOME/local/wasi-sdk-21.0.

I put the following shell script in the root folder of the GSL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
WASI_SDK_HOME=$HOME/local/wasi-sdk-21.0

export PATH=$WASI_SDK_HOME/bin:$PATH

# Copy the wasm aware config files.
cp $WASI_SDK_HOME/share/misc/config.* .

rm -rf ./build
mkdir build
cd build

CC=clang \
CFLAGS="-fno-trapping-math --sysroot=$WASI_SDK_HOME/share/wasi-sysroot -mthread-model single" \
CPP=clang-cpp \
AR=llvm-ar \
RANLIB=llvm-ranlib \
NM=llvm-nm \
LD=wasm-ld \
../configure \
--prefix=$HOME/local/gsl-2.7 \
--host=wasm32-wasi \
--enable-shared=no

# build and install it.
make
make install

There are three key changes from the previous method.

First the autoconf config files in the root folder GSL are overwritten by
those in the was-sdk with cp $WASI_SDK_HOME/share/misc/config.* ..
This provides the information autoconf needs to configure the project for
WebAssembly.

Second the flags to the compiler are now --sysroot=$WASI_SDK_HOME/share/wasi-sysroot,
rather than the previous --target=wasm32-wasi.

Lastly make should be called before make install for the project to build correctly.

Testing

I used the same example program as before.

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <gsl/gsl_sf_bessel.h>

int main (int argc, char** argv)
{
double x = 5.0;
double y = gsl_sf_bessel_J0 (x);
printf ("J0(%g) = %.18e\n", x, y);
return 0;
}

The following script was used to compile it.

1
2
3
4
5
6
7
8
9
10
11
set -x

WASI_SDK_HOME=$HOME/local/wasi-sdk-21.0
GSL_HOME=$HOME/local/gsl-2.7

export PATH=$WASI_SDK_HOME/bin:$PATH

clang \
example.c -o example \
-O2 --sysroot=$WASI_SDK_HOME/share/wasi-sysroot \
-I$GSL_HOME/include -L$GSL_HOME/lib -lgsl -lm -lc

The executable was indeed WebAssembly.

1
2
$ file example
example: WebAssembly (wasm) binary module version 0x1 (MVP)

As before we can test it with wasmer,
using wasmer-linux-amd64.tar.gz.
version 4.2.5, and unpacked it to $HOME/local/wasmer-4.2.5.

1
2
$ $HOME/local/wasmer-4.2.5/bin/wasmer example 
J0(5) = -1.775967713143382642e-01

Success!

Thoughts

It was worth the effort re-visiting this. I think my original
post was incomplete (I suspect I had copied the config files
in the previous attempt and forgotten), but also there were
some real changes.