Install the toolchain
The shared setup for everything else in this guide. Both building packages and building the kernel compile through the same brintOS toolchain — do this once, then follow either guide.
HwjsCoroutinize — it's
what makes a wasm program suspendable, resumable, and forkable. The build arms and then verifies the pass ran on every compile; a build that drops the plugin fails loudly rather
than silently producing untransformed code. There is no Emscripten, Binaryen/Asyncify, or JSPI in
the picture. See The brintOS toolchain.What you'll build
- Pinned
clang+wasm-ld(frombrintos/llvm-project) — clang has no stable plugin ABI across releases, so the pass must load into the exact LLVM it was built against. - The
HwjsCoroutinizepass +wasmcc/wasmlddrivers (frombrintos/hwjs-cc). - Your libc — musl or glibc (from
brintos/muslorbrintos/glibc), cross-built into a sysroot — only needed for packages; the kernel is freestanding and doesn't link libc. Pick one (see step 3) — you don't need both.
Which pieces each workflow needs:
| Workflow | LLVM + HwjsCoroutinize | libc sysroot (musl or glibc) |
|---|---|---|
| Build packages | required | required |
| Build the kernel | required | not needed |
1. Check out the repos
Clone over HTTPS (or use git over SSH):
git clone https://brintos.io/brintos/llvm-project.git
git clone https://brintos.io/brintos/hwjs-cc.git
git clone https://brintos.io/brintos/musl.git # packages, if you choose musl
git clone https://brintos.io/brintos/glibc.git # packages, if you choose glibc 2. Build clang, wasm-ld, and the pass
This is the part both workflows need. First build the pinned LLVM — it must be built
with the LLVM dynamic-library and plugin flags so the coroutine pass can load into it, so use the
repo's build script rather than a plain cmake invocation. Then build the HwjsCoroutinize pass and the wasmcc/wasmld drivers against it.
Each repo ships a one-command build that carries the authoritative flags:
# Build the pinned clang + wasm-ld + opt + libLLVM (clang 22, commit 60513b8d)
# with the dylib + plugin flags the pass loader needs. See llvm-project/BRINTOS.md.
./llvm-project/build-brintos.sh # -> llvm-project/build/bin
# Build the HwjsCoroutinize pass plugin AND wire up the wasmcc/wasmld drivers.
# toolchain/build.sh auto-discovers the LLVM you just built.
cd hwjs-cc
toolchain/build.sh
export PATH="$PWD/bin:$PATH"
wasmcc --version This produces the wasmcc/wasmld drivers (now on your PATH, from hwjs-cc/bin) and the pass plugin at hwjs-cc/pass/coroutinize/build/HwjsCoroutinize.so. Packages compile through wasmcc, which emits LLVM bitcode per translation unit; wasmld then runs HwjsCoroutinize once over the whole merged program. (The kernel build, by
contrast, loads the same .so per translation unit via -fpass-plugin — see Build the Linux kernel.) For the full driver environment
interface — sysroots, the musl bitcode pool, the pinned plugin — see docs/BUILDING.md in brintos/hwjs-cc.
3. Build your libc — musl or glibc (packages only)
Skip this entirely if you only want to build the kernel (it's freestanding and links no libc). For
packages, cross-build one libc for wasm32 into a sysroot, using the
toolchain you just built. Pick the one that matches the userland you're targeting:
dlopen), NSS, and fuller locale/printf coverage. The two sysroots are
independent, and each has its own driver: musl links through wasmcc, glibc through wasmcc-glibc.Option A — musl
Cross-build our musl into a sysroot (see brintos/musl for the
exact configure flags):
cd ../musl
./configure --target=wasm32 CC=wasmcc --prefix="$HOME/brintos-sysroot"
make && make install
# Point the drivers at the musl artifacts and select the coroutinizing arm.
# The env tables in docs/BUILDING.md (brintos/hwjs-cc) are authoritative:
export WASMLD_HWJS=1 # required: the coroutinizing arm
export HWJS_TOOLS_DIR=/path/to/artifacts # base for startfiles + the musl
# bitcode pool + musl headers
# (individual overrides exist too: WASMLD_HWJS_LIBC_BC, WACKY_LIBC_INCLUDE, ...) Option B — glibc
Cross-build our wasm32 glibc port. It's heavier than musl: glibc is built as a coroutinized bitcode object pool plus native startfiles and installed headers, so each package
link pulls exactly the translation units it needs. Packages then compile through the glibc driver, wasmcc-glibc, in place of wasmcc. Follow brintos/glibc for the authoritative recipe, pins, and build
script — the shape is:
cd ../glibc
# Configure for wasm32, then build the coro bitcode pool + startfiles + headers
# into a sysroot. glibc's build is multi-stage (~1900 TUs), so brintos/glibc
# ships the build script that drives configure -> pool build -> install.
./configure --host=wasm32-hwjs-linux-gnu --build=x86_64-pc-linux-gnu \
CC=wasmcc-glibc --prefix="$HOME/brintos-glibc-sysroot"
make && make install
# Point the driver at the glibc build tree (sysroot/, glibc-bc/, startfiles/)
# — the env tables in docs/BUILDING.md (brintos/hwjs-cc) are authoritative:
export WASMLD_HWJS=1
export GLIBC_BUILD=/path/to/glibc/build
wasmcc-glibc --version Next
- Build locally, run on brintOS — compile a package, copy it onto a machine, and run it in the browser.
- Build the Linux kernel — build our Linux 6.12 wasm32
fork into a bootable
vmlinux.wasm.