Getting Started
By the end of this page you will have compiled a C program to WebAssembly against lind-glibc and run it inside a Lind cage.
If you want the concepts first — cages, grates, 3i, RawPOSIX — read the Basics. If you are here to contribute, also see the Contributor Instructions.
There are two ways to get a working Lind:
- Option A: the prebuilt development image — Docker, no build step. Start here.
- Option B: build from source — build the image yourself, or install natively on Linux.
Prerequisites
Platform. Lind-Wasm targets linux/amd64 only. This is why every docker
command below passes --platform=linux/amd64. On an arm64 host — an Apple
Silicon Mac, for example — the image still runs under emulation, but noticeably
more slowly. A native (non-Docker) install requires x86-64 Linux.
Disk. Budget ~20 GB free. The toolchain is large: clang+LLVM and the Wasmtime build account for most of it.
Privileges. Running a program needs root. lind_run re-executes itself under
sudo because the runtime chroots into the Lind filesystem, so expect a
password prompt the first time.
Reference environment. The setup is tested on Ubuntu 22.04, x86-64, 16 GB RAM.
To check a machine against all of the above at once:
make checkenv
Each prerequisite is reported as OK or FAIL, and every FAIL comes with the
command that fixes it. On a fresh checkout that has not been built yet, use
make checkenv BUILD_ONLY=1 to skip the checks for build outputs.
Option A: prebuilt development image
This is the fastest path and the one to use if you are evaluating Lind.
docker pull --platform=linux/amd64 securesystemslab/lind-wasm-dev # this might take a while ...
docker run --platform=linux/amd64 -it --privileged --ipc=host --init \
--cap-add=SYS_PTRACE securesystemslab/lind-wasm-dev /bin/bash
The shell starts in /home/lind/lind-wasm, the prebuilt checkout.
What those flags are for:
| Flag | Why |
|---|---|
--platform=linux/amd64 |
see Prerequisites |
--privileged |
the runtime chroots into lindfs and runs as root |
--ipc=host |
shared-memory system calls |
--init |
reaps the processes that fork/exec tests leave behind |
--cap-add=SYS_PTRACE |
attaching gdb, strace or perf |
There is no build step
Run exactly as above and there is nothing to build. The image ships a
runtime, sysroot and lindfs that were built into /home/lind/lind-wasm
when the image was built (see RUN make lind-debug in
Docker/Dockerfile.dev).
That is the directory the shell starts in. Skip straight to
Run your first program.
You only need to rebuild after changing the source — see Option B.
A bind-mounted checkout has no build outputs
The prebuilt outputs live inside the image, at /home/lind/lind-wasm.
Mounting your own checkout over a different path gives you a tree that has
never been built:
# Your host checkout at /lind — no build/lind-boot, no sysroot, no lindfs
docker run --platform=linux/amd64 -v "$PWD:/lind" -w /lind -it \
securesystemslab/lind-wasm-dev /bin/bash
make test and lind_run then fail with lind-boot missing
(#1355). Either
run make build once inside the mounted tree, or drop -v/-w and use
the image's own checkout.
Option B: build from source
B1. Build the development image yourself
Useful for building a specific branch. See
Development setup for the docker build
invocation and its build args.
B2. Install natively on Linux
Lind-Wasm builds and runs directly on Ubuntu 22.04, both native and under WSL2. The Native Linux setup guide covers the dependencies, pinned toolchain versions and environment variables.
Building
Either way, one command builds everything:
make build
That builds the runtime (lind-boot, Wasmtime, RawPOSIX, 3i), the lind-glibc
sysroot, and the lindfs skeleton the runtime chroots into.
Use make build, not the individual targets
make lind-boot sysroot looks equivalent but skips the lindfs target, so
lindfs/etc, dev/null, the locale data and the timezone database are never
created, and programs fail in confusing ways. make sysroot on its own fails
outright on a clean checkout, because building the shared libc needs
lind-boot to already exist. make all is an alias for make build.
For a debug runtime, use make lind-debug instead — but do not mix the two,
see Troubleshooting. Other targets and build knobs
(FDTABLES_IMPL, NO_LOGGING, WITH_FPCAST) are documented in the
Makefile.
The checkout must live at /home/lind/lind-wasm
The path the runtime chroots into is currently compiled in as a constant
(LINDFS_ROOT in src/sysdefs/src/constants/lind_platform_const.rs), so a
checkout anywhere else panics at startup with
The configured lindfs does not exist. If yours is elsewhere, symlink it:
sudo mkdir -p /home/lind
sudo ln -sfnT "$PWD" /home/lind/lind-wasm
make checkenv checks this for you. This is a known limitation, not a design
goal; making the path configurable is tracked upstream.
Run your first program
Write a C program:
cat << EOF > hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
EOF
Compile and run it:
lind-clang hello.c
lind-wasm hello.cwasm
Hello, World!
lind-clang and lind-wasm are the names the development image installs. Outside
the container, call the scripts directly — scripts/bin/lind_compile and
scripts/bin/lind_run — or symlink them onto your PATH as the
Native Linux setup guide describes.
What just happened
lind_compilecompiledhello.cinto a WebAssembly binary linked against lind-glibc, optimized it with Lind's customwasm-opt, and ahead-of-time compiled the result tohello.cwasm. The output was copied into the Lind filesystem root,lindfs/.lind_runexecuted it on the Lind-Wasm runtime, with system calls mediated by 3i and serviced by the RawPOSIX microvisor.
Two details worth knowing early:
- Paths are relative to
lindfs, not your shell. The runtimechroots intolindfsand changes directory to/, sohello.cwasmand/hello.cwasmboth refer tolindfs/hello.cwasm. Your host working directory is invisible to the program. lind_compilebuilds dynamically by default, producing a position-independent executable that resolves lind-glibc fromlindfs/lib/libc.cwasmat run time. Pass-sfor a traditional statically linked binary instead. Both run the same way; the static build does not need the shared libc to be present.
Troubleshooting
The configured lindfs does not exist: /home/lind/lind-wasm/lindfs —
your checkout is not at the compiled-in path. Create the symlink shown in
Option B.
lind-clang: command not found — those names exist only inside the
development image. Use scripts/bin/lind_compile and scripts/bin/lind_run, or
add the symlinks.
An unexpected password prompt when running a program — expected. lind_run
re-executes itself under sudo -E because the runtime needs root to chroot.
WARNING: The requested image's platform (linux/amd64) does not match the
detected host platform — you are on an arm64 host. Add
--platform=linux/amd64; the image runs under emulation, more slowly.
unknown import: debug::lind_debug_num — a debug-built sysroot is being used
with a release runtime, or the reverse. Rebuild consistently: make build for
release, make lind-debug for debug — not one after the other.
[LIND DEBUG NUM] / [LIND DEBUG STR] lines before your output — not errors.
The published development image is built with make lind-debug, which enables
debug logging.
If none of these match, please
open an issue — including the
output of make checkenv makes it much easier to help.
What's next
- Compile a Rust crate against lind-glibc: Compiling Rust programs
- Run the test suite: Testing
- Understand the pieces: Internal Documentation
- Contribute a change: Contributor Instructions