compile libbpf from source
eBPF Tooling
William Patterson  

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.

Table of Contents

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.
ReasonRiskMitigation
Access to latest featuresDistribution packages lagBuild targeted toolchain with newer Clang/LLVM
Binary compatibilityGLIBC symbol errorsBuild on oldest supported glibc
Faster developmentPatch delays in distro reposLocal 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.

ItemWhy it mattersAction
BTFCO-RE portabilityEnable CONFIG_DEBUG_INFO_BTF or use vmlinux.h
libelf / zstdELF parsing and compressionInstall libelf-dev and libzstd-dev
ToolchainProper code generationInstall 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.
ActionWhyNotes
Clone repoKnown baselinegit clone and cd libbpf
Inspect layoutFind headers & MakefilesLook in top-level and src
Pick tagReproducible buildsMatch 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.

libbpf build process

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.

StepActionWhy it matters
Buildcd src && makeProduces object file and library artifacts
Installsudo make install [PREFIX=…]Places headers and library where apps can link
Verifypkg-config –modversion libbpfConfirms 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.
CheckCommandExpected Result
Versionpkg-config –modversion libbpfNumeric version string
Headersls /usr/local/includelibbpf headers present
Libraryls /usr/local/lib | grep libbpfShared 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.

SymptomLikely causeQuick fix
Verifier rejects typesMismatched kernel headers or missing BTFInstall matching kernel headers or generate vmlinux.h
Relocation errorsCO-RE lacks type infoEnable CONFIG_DEBUG_INFO_BTF or provide BTF file
Build OK, runtime failShared library version mismatchRebuild 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.

TaskToolBenefit
Skeleton generationbpftoolEmbed object into header for easy inclusion
PortabilityCO-RE + BTFAuto-adapts program types across kernels
Bootstrap applibbpf-bootstrapMinimal 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.

FAQ

What are the minimal prerequisites to build libbpf and BPF programs?

You need a recent Linux kernel with BPF support, kernel headers that match the running kernel or BTF for CO‑RE, a C toolchain (Clang/LLVM is preferred for BPF bytecode), pkg-config, make, libelf development files, and compression libs like zlib or zstd. On Ubuntu 22.04 that typically means install build-essential, clang, llvm, libelf-dev, libzstd-dev, and linux-headers-$(uname -r).

Why should I build libbpf now instead of using a distro package?

Building lets you pick a commit or tag that matches your kernel and BTF needs, get new features and bug fixes faster, and control install paths for development. It’s especially useful when you need CO‑RE support, recent API additions, or to test against a specific kernel version.

How do I choose between Clang/LLVM and GCC for BPF and user-space code?

Use Clang/LLVM to compile BPF programs because it emits correct BPF bytecode and debug info. GCC works for user-space apps, but Clang is the safe choice when producing .o BPF objects or skeletons. Keep toolchain versions reasonably recent to avoid incompatibilities.

Where should kernel headers and files live so BPF types resolve correctly?

Kernel headers live under /usr/src/linux-headers- or in distro-provided include paths. For CO‑RE you want BTF information—either vmlinux BTF from your distro or generate vmlinux.h with bpftool. Ensure the header search paths used when building match where those files reside.

How do I get the official project repository and pick the right branch or tag?

Clone the upstream repository with git and inspect the top-level layout: src/, include/, tools/, and examples/. Choose a release tag or branch that aligns with your kernel version or features you need. Tags often document supported kernels and API stability.

What are the typical steps to build the library in the src directory?

Enter the src/ directory, run make (or use the provided build script), then run make install with a PREFIX if you want a custom install location. The build produces libbpf.a/.so, headers, and pkg-config files you can expose to your system.

How do I install headers and the library to a custom PREFIX and make them discoverable?

Run make install DESTDIR=… or make install PREFIX=/your/prefix. Then update PKG_CONFIG_PATH to include /your/prefix/lib/pkgconfig and add /your/prefix/lib to LD_LIBRARY_PATH or configure /etc/ld.so.conf.d/ with the path and run ldconfig to expose the library system-wide.

How can I verify the installed libbpf version on my system?

Use pkg-config –modversion libbpf to show the installed version. You can also check headers in the install include directory and list library files in the install lib directory to confirm file versions and timestamps.

What do I need to link a user-space program against the library and BPF object files?

Include the libbpf headers in your build, link with -lbpf and any additional libraries (libelf, zlib), and include the generated BPF object files (compiled with Clang) or use libbpf’s skeleton helper to load the .o and attach programs. pkg-config –cflags –libs libbpf simplifies flags.

What common build errors occur across kernel and glibc versions, and how do I fix them?

Errors include symbol versioning “version not found” from mismatched glibc, missing BTF for CO‑RE, or kernel header mismatches. Fix by using a matching toolchain or cross toolchain for target glibc, installing correct kernel headers or vmlinux BTF, and rebuilding libbpf against the correct environment.

How do symbol versioning problems show up and how can I resolve them?

You may see runtime errors about undefined or wrong symbol versions when loading shared libs. Resolve by rebuilding libbpf and dependent libraries with a compatible glibc version or by statically linking where appropriate. Using a container or chroot matching the target environment helps reproduce and fix issues.

Why does BTF matter for CO‑RE and how do I obtain it?

BTF provides type metadata so CO‑RE can relocate and adapt BPF programs across kernel builds. Get BTF from your distribution’s vmlinux or generate it with bpftool and build vmlinux.h for compilation. Without BTF, CO‑RE cannot apply kernel type fixes reliably.

What are strategies for building against older GNU libc versions?

Use a cross toolchain or build container with the older glibc installed so the produced shared objects match target symbol versions. Alternatively, build statically or use musl if that fits your deployment. Test on the target runtime to catch ABI issues early.

Which tools help integrate libbpf into a modern BPF workflow?

bpftool inspects object files, generates skeletons, and creates vmlinux.h. libbpf-bootstrap provides a starter project layout. Use these tools plus Clang, llvm-strip, and standard build systems to iterate quickly on programs and user-space loaders.

How do I inspect BPF object files, skeletons, and vmlinux headers?

Use bpftool to dump sections, show map definitions, and extract CO‑RE relocations. The generated skeleton C files include loader code. vmlinux.h provides kernel type definitions for compiling BPF programs that target system structs.

What should I check if make fails in the src directory?

Check that required dev packages (libelf-dev, zlib/zstd dev), correct Clang/LLVM, and kernel headers are present. Review the Makefile output for missing headers or tools, and confirm PKG_CONFIG_PATH and compiler paths. Running make V=1 can reveal the failing command.

How do I confirm installed files and versions across different systems?

Use pkg-config and list headers and libs in the install prefix. Compare libbpf.so sonames, pkg-config metadata, and header version macros. For reproducibility, record git commit or release tag used to build the library.

What is the recommended way to kickstart a BPF project with libbpf?

Start from libbpf-bootstrap or an example in the repo, use Clang to build .o BPF programs, generate skeletons with bpftool or libbpf’s Makefile helpers, and wire a small user-space loader that uses libbpf’s API to load and attach programs. This gives a clear path to testing and iteration.

How do I expose pkg-config files and library paths after a custom install?

Place .pc files in /your/prefix/lib/pkgconfig and export PKG_CONFIG_PATH to include that path. Add /your/prefix/lib to LD_LIBRARY_PATH or add a config file under /etc/ld.so.conf.d/ and run ldconfig. That makes linking and runtime loading seamless.

Are there sandbox or container nuances when building with Make 4.4 or older tools?

Yes—older make or sandboxed build environments may create incompatible artifacts or omit rpath/soname handling. Reproduce your target environment in a container, ensure build tool versions match expectations, and adjust make flags to control sandbox behaviors.

What BPF program types and sections do I need to know for development?

Common types include kprobe, tracepoint, XDP, tc/classifier, and cgroup programs. Each maps to specific ELF sections in the .o file. Match the section name and verifier requirements when writing programs and use helpers appropriate to the program type.

How do I keep my development environment aligned with kernel features and versions?

Track kernel releases you target, use matching BTF or kernel headers, pick a libbpf tag compatible with those kernels, and use containers or toolchains to reproduce build environments. Automated CI that builds and tests against multiple kernel versions helps catch regressions early.