
Compile libbpf from Source Code
You want to compile libbpf from source so your bpf programs work reliably on your system and match the kernel and application needs.
I’ll walk you through why building the library yourself can give you tighter control over dependencies, better compatibility with the linux kernel, and a reproducible process for development.
We’ll start with the practical step flow: install prerequisites, fetch the code, build in the src directory, and install the library and headers so your applications can link cleanly.
Key Takeaways
- Building locally gives you the latest bpf features and control over dependency versions.
- On Ubuntu 22.04, install git, make, gcc, libelf-dev, and libzstd-dev before you build.
- The core steps are: fetch the repo, run make in src, then sudo make install.
- Verify installation with pkg-config –modversion libbpf to confirm the version.
- Targeting an older glibc can improve binary compatibility across systems.
Why build libbpf from source right now
I see three practical reasons to take control today: access to new bpf features, predictable binaries across systems, and faster development cycles. When distributions lag, the easiest way to use modern bpf capabilities is to build the library yourself.
Real projects hit errors like libc.so.6: version ‘GLIBC_2.27’ not found. That happens when a binary links to newer symbols than the target system provides. Statically linking glibc is discouraged, so a safer way is to build on the oldest glibc you intend to support.
- Get new features that packages may not include yet.
- Reduce runtime symbol errors by aligning build and target environments.
- Shorten the patch-test-ship loop for bpf programs and user applications.
Reason | Risk | Mitigation |
---|---|---|
Access to latest features | Distribution packages lag | Build targeted toolchain with newer Clang/LLVM |
Binary compatibility | GLIBC symbol errors | Build on oldest supported glibc |
Faster development | Patch delays in distro repos | Local builds and CI integration |
Prerequisites: kernel headers, toolchain, and packages
Start by validating your system has the right headers, compilers, and packages for BPF work. I’ll walk you through what matters and why each piece matters to get reliable builds and portable programs.
Supported systems and BTF expectations
Many mainstream distributions already ship BTF support (CONFIG_DEBUG_INFO_BTF). With BTF and CO-RE, your bpf program adapts to kernel layout differences.
If BTF is missing, portability drops and you’ll need matching header files for the running kernel.
Install build tools and headers on Ubuntu 22.04
On Ubuntu 22.04, update the system and install essentials:
- sudo apt-get update && sudo apt-get upgrade
- sudo apt-get install -y git make gcc libelf-dev libzstd-dev linux-headers-generic
Clang/LLVM vs GCC
Use Clang/LLVM to compile BPF kernel-side code. GCC works well for user-space code and tools. Many projects use both toolchains in the same process.
libelf and compression libraries
libelf-dev and libzstd-dev are required—libbpf reads ELF objects and uses zstd/zlib for compression. These packages come from the distro repository and ensure correct object handling.
Kernel headers and header locations
Match kernel headers to your running linux kernel for tracepoints, kprobes, and other types. Header mismatches cause build-time or runtime errors, so prefer linux-headers-generic or pinned header versions for controlled development.
Item | Why it matters | Action |
---|---|---|
BTF | CO-RE portability | Enable CONFIG_DEBUG_INFO_BTF or use vmlinux.h |
libelf / zstd | ELF parsing and compression | Install libelf-dev and libzstd-dev |
Toolchain | Proper code generation | Install clang for kernel code, keep gcc for user-space |
Get the libbpf source code
Grab a clean checkout of the official repo so you know exactly which code and files your build will use.
Clone and inspect the repository
Run git clone https://github.com/libbpf/libbpf.git and cd libbpf. This gives you a known-good repository you can update and track over time.
The top level contains docs, headers, and a src directory. The Makefile in src is where the object files and library artifacts are produced.
Choose a branch or tag
- If you are getting started, tracking the default branch is fine for getting started quickly.
- For stability, pick a tagged version that matches your kernel and feature needs.
- Pin a commit or tag in your project docs so your application builds remain reproducible.
Action | Why | Notes |
---|---|---|
Clone repo | Known baseline | git clone and cd libbpf |
Inspect layout | Find headers & Makefiles | Look in top-level and src |
Pick tag | Reproducible builds | Match kernel features |
How to compile libbpf from source
I’ll walk you through the core build steps, a safe install pattern, and how to make the system find the files your bpf programs need.
Build steps in the src directory
From the repository root, change into the src folder and run the standard make command.
- cd libbpf/src && make — this produces object files, static/shared library, and other build artifacts.
- Watch warnings during the build — they often flag header or object file issues early.
Install headers and library to a custom PREFIX
Use sudo make install to copy headers and the library to system locations.
Defaults go to /usr/local/lib and /usr/local/include. For isolation, set a custom PREFIX:
- sudo make install PREFIX=/opt/libbpf
- This keeps system libraries untouched and simplifies rollback or testing.
Expose pkg-config files and library paths system-wide
Confirm installation with pkg-config –modversion libbpf so your application can discover the library.
If you installed outside default paths, add the install lib path to ldconfig or set LD_LIBRARY_PATH while developing. That ensures your binary locates the file at runtime.
Step | Action | Why it matters |
---|---|---|
Build | cd src && make | Produces object file and library artifacts |
Install | sudo make install [PREFIX=…] | Places headers and library where apps can link |
Verify | pkg-config –modversion libbpf | Confirms pkg-config and .pc files are exposed |
Install, verify, and use the library in your applications
With the library installed, verify the packaging metadata and make sure your user programs can find headers and the shared library. A quick check proves the install and avoids runtime surprises.
Validate with pkg-config –modversion libbpf
Run pkg-config –modversion libbpf to confirm the version and that the .pc file is reachable. If it returns a version number, your bpf toolchain and application can query include and link flags automatically.
Linking your user-space program
Use pkg-config when compiling: gcc $(pkg-config –cflags libbpf) -o app app.c $(pkg-config –libs libbpf). This pulls cflags and libs and avoids hand-editing include paths.
Remember to add libelf and zstd/zlib to your link line if your code uses them.
Checking installed files and versions across systems
- Confirm headers exist under /usr/local/include and the library under /usr/local/lib unless you used a custom PREFIX.
- If the binary fails at runtime, run sudo ldconfig or set LD_LIBRARY_PATH during testing.
- When you upgrade the library, rebuild your application to keep ABI compatibility.
Check | Command | Expected Result |
---|---|---|
Version | pkg-config –modversion libbpf | Numeric version string |
Headers | ls /usr/local/include | libbpf headers present |
Library | ls /usr/local/lib | grep libbpf | Shared or static lib visible |
Troubleshooting builds across kernel and glibc versions
Errors like “libc.so.6: version ‘GLIBC_2.27’ not found” usually point to a mismatch between the build environment and the deployment target. I’ll show practical checks and fixes so your bpf tools and the libbpf library behave across systems.
Symbol versioning and shared library errors
If you see a “version not found” message at runtime, the binary linked newer shared-library symbols than the target provides. The simplest way to fix this is to rebuild on an older baseline or use a container that matches the target glibc.
- Rebuild on an OS image that matches the target version.
- Or use a pinned toolchain to avoid accidental upgrades.
Kernel headers, BTF, and CO-RE mismatches
CO-RE relies on correct kernel headers and BTF data. If relocations fail, check that kernel headers match the running kernel and that BTF is available.
Symptom | Likely cause | Quick fix |
---|---|---|
Verifier rejects types | Mismatched kernel headers or missing BTF | Install matching kernel headers or generate vmlinux.h |
Relocation errors | CO-RE lacks type info | Enable CONFIG_DEBUG_INFO_BTF or provide BTF file |
Build OK, runtime fail | Shared library version mismatch | Rebuild on older glibc or use cross toolchain |
Toolchain, make versions, and container nuances
Tool versions matter. For example, make 4.4 caused hangs when building older glibc in some cases; downgrading to make 4.3 fixed the problem.
- In Gentoo containers, disable sandboxing with FEATURES flags during bootstrap to avoid unshare failures.
- When you must target older linux kernel and glibc versions, a cross toolchain gives control over GCC, glibc, and kernel headers.
Integrate libbpf into a modern BPF development workflow
Integrating the right tools makes it simple to build, inspect, and adapt bpf programs across kernels. I recommend a small toolkit: bpftool for object work, CO-RE with BTF for portability, and a bootstrap repo to start projects quickly.
bpftool: skeletons, object inspection, and vmlinux.h
Use bpftool to auto-generate BPF skeleton headers (for example, minimal.skel.h). That embeds the object file into a header file and keeps user code and bpf code synchronized.
bpftool also inspects object files so you can validate maps, sections, and relocations before running the program. When needed, generate a tailored vmlinux.h to resolve kernel types.
CO-RE, BTF, and global variables for robust types
CO-RE plus BTF lets program types adapt across kernels. That reduces friction when you use bpf programs on different machines.
Modern kernels support global variables—these let your application configure behavior without extra maps or syscalls, simplifying data flow between user and kernel.
Kickstart with libbpf-bootstrap
Use libbpf-bootstrap to jump-start development. It bundles libbpf as a submodule and includes bpftool plus a prebuilt vmlinux.h so you can write bpf code immediately.
Task | Tool | Benefit |
---|---|---|
Skeleton generation | bpftool | Embed object into header for easy inclusion |
Portability | CO-RE + BTF | Auto-adapts program types across kernels |
Bootstrap app | libbpf-bootstrap | Minimal app and realistic examples for fast start |
Build with confidence and move on to real BPF programs
With a stable install and pkg-config linking, you can start writing small bpf programs and test them on your target kernel. I recommend pairing a known libbpf version with your repository so builds stay reproducible.
Baseline builds against an older glibc often prevent runtime “version not found” failures on other systems. If you want a minimal dev setup, try libbpf-bootstrap — it speeds iteration by bundling tools and example files.
Document the build and run process, run periodic tests across kernels, and ship incremental changes. Repeatability and clear install paths make it easier for teammates and CI to validate your application.