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

> Discover bpftime compatibility with existing eBPF toolchains. Run clang and libbpf compiled programs unchanged in userspace with this drop-in companion.

- Repository: [eunomia-bpf/bpftime](https://github.com/eunomia-bpf/bpftime)
- Tags: deep-dive
- Published: 2026-03-01

---

**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`](https://github.com/eunomia-bpf/bpftime/blob/main/.bpf.c) source file.

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

```cmake
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`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/object/bpf_object.cpp) (lines 20–22), the code includes the standard libbpf headers:

```cpp
#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`](https://github.com/eunomia-bpf/bpftime/blob/main/tools/cli/main.cpp), provides a thin wrapper around libbpf's loading mechanism. Users can load existing eBPF binaries using the familiar workflow:

```bash
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:

```bash
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`](https://github.com/eunomia-bpf/bpftime/blob/main/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:

```bash
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`](https://github.com/eunomia-bpf/bpftime/blob/main/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.