Skip to content

Latest commit

 

History

History
206 lines (152 loc) · 6.59 KB

File metadata and controls

206 lines (152 loc) · 6.59 KB

Building bpf-linker from source

Building bpf-linker with Bazel

Bazel can build bpf-linker and its LLVM dependency in a single graph without requiring a separately installed LLVM. It uses the pinned Rust toolchain and builds the pinned LLVM revision from source:

bazel build //:bpf-linker --config=release

The resulting executable is available under bazel-bin/.

To build release archives for every supported platform, run:

bazel build //:release-archives --config=release

Building bpf-linker with Cargo

LLVM

bpf-linker is a bitcode linker that uses libLLVM to link bitcode inputs. That means the LLVM version used by bpf-linker must match the LLVM version used by the Rust toolchain you intend to use.

There are several recommended ways of obtaining an appropriate LLVM.

Our prebuilt LLVM on ghcr.io

We regularly build LLVM in CI and publish the binary artifacts on ghcr.io. They can be retrieved using oras.

First, pick an appropriate image from our LLVM container page. The tags mention the LLVM version, the platform, and our custom revision, e.g.

  • 23-x86_64-unknown-linux-gnu-4 - LLVM 23, x86_64 Linux, glibc, revision 4
  • 21-aarch64-unknown-linux-musl-4 - LLVM 21, aarch64 Linux, musl, revision 4
  • 22-aarch64-apple-darwin-4 - LLVM 22, aarch64 macOS, revision 4

Always pick the latest revision available, if there are multiple.

After picking an appropriate image, it can be downloaded with oras, e.g.

oras pull ghcr.io/aya-rs/llvm:23-x86_64-unknown-linux-gnu-4

And the resulting tarball unpacked to a directory:

mkdir llvm
tar --zstd -xpf llvm-archive-x86_64-unknown-linux-gnu.tar.zst \
    -C llvm/

Building LLVM with Bazel

The repository's Bazel graph builds the pinned LLVM revision from source and can package its outputs in the same layout as the ghcr.io artifact. It's recommended to disable ThinLTO to avoid linker errors if your system linker uses a different LLVM version than the one you're building.

bazel build //:llvm-archive --config=release --features=-thin_lto
mkdir llvm
tar --zstd -xpf bazel-bin/llvm-archive.tar.zst -C llvm/

The archive contains FileCheck, the shared LLVM library, and its static component libraries. It is intended for building and debugging bpf-linker, rather than as a complete LLVM developer installation.

Building LLVM with CMake

If you prefer to build LLVM the way it is built upstream, check out the appropriate branch from rust-lang/llvm-project for the LLVM version used by your Rust toolchain, then configure and install it with CMake:

cmake -S llvm-project/llvm -B llvm-build -G Ninja \
    -DCMAKE_BUILD_TYPE=RelWithDebInfo \
    -DCMAKE_INSTALL_PREFIX="$PWD/llvm" \
    -DCMAKE_INSTALL_LIBDIR=lib \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DLLVM_BUILD_LLVM_DYLIB=ON \
    -DLLVM_ENABLE_ASSERTIONS=ON \
    -DLLVM_ENABLE_PROJECTS= \
    -DLLVM_ENABLE_RUNTIMES= \
    -DLLVM_INSTALL_UTILS=ON \
    -DLLVM_LINK_LLVM_DYLIB=ON \
    -DLLVM_TARGETS_TO_BUILD=BPF \
    -DLLVM_USE_LINKER=lld
cmake --build llvm-build --target install
export LLVM_PREFIX="$PWD/llvm"

System packages

On Debian-based distributions, you can install the llvm-<version>-dev and libclang-<version>-dev packages from the official LLVM repository at https://apt.llvm.org.

Different operating systems and Linux distributions might provide their own LLVM packages. If you're interested in packaging bpf-linker, you may also need to ensure that the correct LLVM version is packaged for that environment.

bpf-linker

bpf-linker uses Cargo features to select the LLVM version, via llvm-* features such as llvm-23. By default, LLVM and its dependencies are linked dynamically. Static linking can be enabled with the llvm-link-static feature.

If you are using either a prebuilt LLVM archive from ghcr.io or one built locally with Bazel, set LLVM_PREFIX to the directory where you extracted the archive. When linking statically, also configure the names and locations of the Bazel-built libraries:

export LLVM_PREFIX="$PWD/llvm"
export PATH="$LLVM_PREFIX/bin:$PATH"
export CXXSTDLIB=libcxx,libcxxabi
export CXXSTDLIB_PATH="$PWD/llvm/lib"
export ZLIB_PATH="$PWD/llvm/lib"
export LIBZSTD_PATH="$PWD/llvm/lib"

If LLVM_PREFIX is not set, build.rs searches PATH for llvm-config and uses its location to find LLVM libraries installed under a versioned prefix, as is common with Linux distribution packages. For example, if LLVM is installed under /usr/lib/llvm/<VERSION> and its bin/llvm-config is exposed through PATH, build.rs searches /usr/lib/llvm/<VERSION>/lib64 and /usr/lib/llvm/<VERSION>/lib for the libraries.

Examples:

# Dynamic linking
cargo build --no-default-features --features llvm-23
cargo install bpf-linker --no-default-features --features llvm-23
cargo install --path . --no-default-features --features llvm-23

# Static linking
cargo build --no-default-features --features llvm-23,llvm-link-static
cargo install bpf-linker --no-default-features --features llvm-23,llvm-link-static
cargo install --path . --no-default-features --features llvm-23,llvm-link-static

Running tests

bpf-linker comes with compiletests, similar to the ones in Rust and LLVM, that compile the code to LLVM IR (or BTF) and assert the output matches the expected IR.

With Rust nightly

Use cargo test with same arguments as used for build, e.g.:

cargo +nightly test --no-default-features --features llvm-23

With Rust stable

BPF targets are Tier 3 in Rust and therefore rustup does not provide BPF targets in stable editions of Rust. There are two ways to overcome that.

Prebuilding the BPF sysroot

Build the BPF sysroot with:

RUSTC_SRC="$(rustc --print sysroot)/lib/rustlib/src/rust/library"
BPFEL_SYSROOT_DIR="$(pwd)/bpf-sysroot"
RUSTC_BOOTSTRAP=1 cargo xtask build-std \
  --rustc-src "$RUSTC_SRC" \
  --sysroot-dir "$BPFEL_SYSROOT_DIR" \
  --target bpfel-unknown-none

Then point the tests to the sysroot using the BPFEL_SYSROOT_DIR variable:

BPFEL_SYSROOT_DIR="$(pwd)/bpf-sysroot" \
    cargo test --no-default-features --features llvm-23
Building the sysroot on demand

It's done by the tests automatically when BPFEL_SYSROOT_DIR is not defined, but in case of Rust stable it requires RUSTC_BOOTSTRAP=1:

RUSTC_BOOTSTRAP=1 cargo test --no-default-features --features llvm-23