bpftime Compatibility with Existing eBPF Toolchains: A Complete Guide to Clang and libbpf Integration

bpftime is designed as a drop-in companion for the standard eBPF toolchain, allowing any program compiled with clang -target bpf and loaded via libbpf to run unchanged in userspace.

The eunomia-bpf/bpftime repository provides a userspace eBPF runtime that mirrors the kernel's execution environment. This compatibility ensures developers can reuse their existing workflows, build scripts, and eBPF source code without modification when targeting userspace execution.

How bpftime Maintains Compatibility with Clang Compilation

bpftime utilizes the exact same compilation pipeline as kernel-based eBPF development. The build system automatically generates the standard clang -target bpf invocation for every .bpf.c source file.

In cmake/libbpf.cmake (lines 1110–1115), the compilation command is defined as:

add_custom_command(OUTPUT ${BPF_OBJECT}
    COMMAND ${CLANG} -O2 -target bpf -c -g -D__TARGET_ARCH_x86
        -Ithird_party/vmlinux/x86
        -Ithird_party/libbpf/include/uapi
        -Ithird_party/libbpf/include
        ${BPF_SOURCE} -o ${BPF_OBJECT}
    DEPENDS ${BPF_SOURCE}
)

This ensures that bytecode generated for bpftime is identical to what would run in the kernel, maintaining perfect binary compatibility with existing toolchains.

libbpf API Integration in bpftime

Vendored libbpf Headers and Build System

Rather than requiring system-wide installations, bpftime vendors the complete libbpf library under third_party/libbpf. The CMake module at cmake/libbpf.cmake handles fetching, building, and linking the static library automatically.

This approach guarantees that bpftime always uses a compatible version of libbpf and exposes the standard headers to all components. The build system copies required headers to the build directory, making them available via standard include paths.

Loading ELF Objects with libbpf

The runtime implements the standard libbpf workflow for loading eBPF programs. In runtime/object/bpf_object.cpp (lines 20–22), the code includes the standard libbpf headers:

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

The runtime uses bpf_object__open() and related APIs to parse ELF files, resolve map definitions, and prepare programs for execution. This means any eBPF object built for the kernel can be loaded directly into bpftime without recompilation or relinking.

Running Existing eBPF Programs Without Modification

CLI Wrapper and LD_PRELOAD Injection

The bpftime command-line tool, implemented in tools/cli/main.cpp, provides a thin wrapper around libbpf's loading mechanism. Users can load existing eBPF binaries using the familiar workflow:

bpftime load ./example/malloc/malloc.bpf.o

Behind the scenes, this invokes libbpf to parse the ELF object, then prepares the userspace runtime. For runtime injection, bpftime uses LD_PRELOAD to intercept system calls:

LD_PRELOAD=build/runtime/syscall-server/libbpftime-syscall-server.so \
    ./example/malloc/victim

The preloaded library implements the bpf() syscall interface, allowing unmodified eBPF programs to execute in userspace as if they were running in kernel context.

Feature Parity with Kernel eBPF

bpftime maintains API compatibility with standard libbpf features including maps, perf events, ring buffers, and BTF debugging information. The runtime implements userspace equivalents of kernel map types in runtime/src/bpf_map/shared/.

For example, hash map operations are implemented in runtime/src/bpf_map/shared/hash_map_kernel_user.cpp, providing the same bpf_map_lookup_elem() and bpf_map_update_elem() semantics as the kernel. This ensures that eBPF code using standard map APIs compiles and runs correctly without modification.

Optional Kernel Verifier for Maximum Compatibility

For scenarios requiring strict adherence to kernel verification rules, bpftime can optionally invoke the kernel's verifier before userspace execution. Setting the environment variable BPFTIME_RUN_WITH_KERNEL=true enables this mode:

BPFTIME_RUN_WITH_KERNEL=true \
LD_PRELOAD=~/.bpftime/libbpftime-syscall-server.so \
./example/malloc/malloc

In this configuration, bpftime uses libbpf to load the program into the kernel verifier first, ensuring the bytecode meets all kernel safety requirements before executing it in the userspace VM. This provides an additional compatibility guarantee for programs that may eventually target kernel deployment.

Summary

  • bpftime uses identical clang invocations as kernel eBPF workflows, defined in cmake/libbpf.cmake, ensuring bytecode compatibility.
  • The runtime vendors libbpf under third_party/libbpf and links against it statically, providing standard ELF loading via runtime/object/bpf_object.cpp.
  • Existing eBPF binaries load without modification using the bpftime CLI tool, which wraps libbpf's bpf_object__open() and related APIs.
  • Runtime injection uses LD_PRELOAD to intercept syscalls, allowing unmodified programs to execute in userspace via libbpftime-syscall-server.so.
  • Full feature parity with kernel maps, ring buffers, and BTF is maintained through userspace implementations in runtime/src/bpf_map/shared/.
  • Optional kernel verification via BPFTIME_RUN_WITH_KERNEL=true ensures programs meet kernel safety rules before userspace execution.

Frequently Asked Questions

Can I use my existing clang-compiled eBPF binaries with bpftime?

Yes. Any eBPF binary compiled with clang -target bpf can be loaded directly into bpftime without recompilation. The build system in cmake/libbpf.cmake uses the exact same compiler flags and target triple as kernel-based workflows, ensuring perfect binary compatibility.

Does bpftime require modifications to libbpf-based source code?

No. bpftime maintains API compatibility with standard libbpf functions. The runtime implements the same map operations, helper functions, and ELF loading procedures that libbpf provides for kernel eBPF. You can use standard libbpf headers and build scripts without conditional compilation or source changes.

How does bpftime handle kernel-specific eBPF features?

bpftime provides userspace implementations of kernel features in runtime/src/bpf_map/shared/. For hash maps, array maps, ring buffers, and perf events, the runtime offers compatible data structures that use the same APIs. When kernel-specific hardware features or verifier constraints are required, setting BPFTIME_RUN_WITH_KERNEL=true allows bpftime to validate programs against the kernel verifier before userspace execution.

Is the kernel verifier required to run programs in bpftime?

No. The kernel verifier is optional. By default, bpftime runs programs directly in its userspace VM without kernel involvement. However, for development and validation purposes, you can enable kernel verification by setting the environment variable BPFTIME_RUN_WITH_KERNEL=true. This loads the program into the kernel verifier first to ensure compatibility with kernel safety rules, then executes it in userspace.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →