libbpf-bootstrap: A Starting Point for BPF CO-RE Applications
Scaffolding for BPF application development with libbpf and BPF CO-RE
At a glance
- What is it?
- libbpf-bootstrap is a C and Rust repository of BPF application examples, including a process-lifecycle tracer built with libbpf, BPF CO-RE, vmlinux.h, maps, and a BPF ring buffer. It is most useful as a starting point for developers who already understand the BPF execution model and need a concrete user-space data path.
- Who is it for?
- Choose libbpf-bootstrap when you need a small, inspectable reference for a libbpf-based BPF application, especially process tracing with CO-RE or user-space probes. Do not choose it as a complete monitoring product or for environments where kernel feature availability and root-level tracing access are unresolved.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly CMake, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What libbpf-bootstrap solves
libbpf-bootstrap addresses a specific gap between a small BPF tracepoint program and an application that can be useful in user space. The repository includes examples rather than a general-purpose monitoring framework. That distinction matters for adoption. A developer can inspect how a BPF program attaches to kernel or user-space events, retain state in a map, send records to user space, parse command-line options, and exit cleanly when interrupted.
The main `bootstrap` example is aimed at developers building process-observation tools. According to the README, it tracks the `exec()` family of syscalls and process exits, then emits process names, process IDs, parent process IDs, filenames when available, exit status, and process duration. Its `-d <min-duration-ms>` option suppresses short-lived process-start records and reports processes that meet the selected duration. The `minimal` example is smaller and is described as a practical playground for BPF features rather than a process monitor.
The repository is not limited to one tracing style. `uprobe` demonstrates user-space entry and return probes, while `minimal_ns` addresses process identifiers seen inside namespaces. `minimal_Legacy` and `bootstrap_legacy` provide older-kernel variants. This range makes the project useful for comparing feature requirements and selecting an example that matches the kernel and environment being targeted.
Inside the bootstrap process tracer
The documented architecture of `bootstrap` is based on cooperating BPF programs. Separate tracepoint handlers observe process `exec` and `exit` events. BPF maps hold the state needed to associate a process with later exit information, and a BPF ring buffer carries event records into user space. The README also identifies global variables as the mechanism used to parameterize application behavior.
The BPF side uses BPF CO-RE and `vmlinux.h` to read additional information from the kernel's `struct task_struct`. This is different from writing offsets directly against one fixed kernel build. The user-space side handles the pieces required for a usable command-line program: loading the BPF object, reading ring-buffer records, formatting output, parsing arguments, and handling Ctrl-C. The README presents those mundane tasks as part of the example's value.
The legacy variant changes the transport layer. `bootstrap_legacy` replaces the ring buffer with a `BPF_MAP_TYPE_PERF_EVENT_ARRAY`, because the ring-buffer map type was introduced in kernel 5.8. The legacy version therefore keeps the broad process-tracking example available to older kernels, but it is not the same data path. The difference gives developers a concrete compatibility decision: choose the ring-buffer implementation when the target kernel supports it, or inspect the perf-event-array implementation when supporting an older kernel is the priority.
Installing and running the minimal example
The README does not provide a package-install command or enumerate every system dependency. It does say to clone the repository with submodules and install the dependencies before building the examples. The documented C example workflow is shown below.
From the repository root, enter the C examples directory and build the smallest example. The `minimal` program installs a tracepoint handler and uses `bpf_printk()` to report that the handler was triggered. Run it with root privileges, then read the kernel trace pipe in a separate privileged shell.
$ cd examples/c
$ make minimal
$ sudo ./minimal
$ sudo cat /sys/kernel/debug/tracing/trace_pipeThe trace-pipe output in the README contains repeated `bpf_trace_printk` messages with the process ID. The reader should expect output only while the example is running or while the trace pipe is being observed. Access to `/sys/kernel/debug/tracing/trace_pipe`, debug tracing availability, and the necessary kernel configuration are operational prerequisites that the README does not enumerate.
For a namespaced environment such as a container or WSL2, the README specifically directs developers to use `minimal_ns` instead of `minimal`. This is because the process ID visible inside the namespace can differ from the process ID perceived by the host. The repository treats namespace identity as part of the example's correctness rather than as a separate configuration option.
Building a process event stream with bootstrap
After building `bootstrap`, the documented minimum-duration mode records processes that live at least the requested number of milliseconds. The command-line parser is part of the starter application, so this is the first useful operation to try after the minimal tracepoint example.
$ cd examples/c
$ sudo ./bootstrap -d 50The README shows a table with columns for time, event type, command name, PID, PPID, and filename or exit code. In minimum-duration mode, process-start events are not printed, so the output is centered on qualifying exits. A row such as an `EXIT` event for `timeout` shows its exit code in square brackets and its duration in parentheses. A nonzero exit code is also represented in the output.
The example is intentionally narrow. It observes process lifecycle events and reports selected fields, but the README does not claim that it aggregates events, stores historical records, exports data, or manages a long-running service. Developers should treat the command-line output as a demonstration of the BPF-to-user-space pipeline. Any production behavior beyond that requires additional application code and operational design.
User-space probes and namespace-specific behavior
The `uprobe` example extends the repository beyond kernel tracepoints. It attaches both `uprobe` and `uretprobe` BPF programs to functions belonging to the example itself, specifically `uprobed_add()` and `uprobed_sub()`. The entry probe logs input arguments, and the return probe logs the result through `bpf_printk()`. The user-space function is triggered once per second, which makes the example useful for seeing how entry and return events relate to one operation.
$ cd examples/c
$ sudo ./uprobe
$ sudo cat /sys/kernel/debug/tracing/trace_pipeThe README reports a startup message and then trace output containing entry and exit records such as `uprobed_add ENTRY` and `uprobed_add EXIT`. As with `minimal`, the example communicates through the kernel trace pipe rather than a documented structured output file.
`minimal_ns` addresses a concrete correctness issue. In a namespaced environment, a PID may be translated between the namespace and the host view. Using the ordinary `minimal` example can therefore produce a process ID that does not correspond to the process ID perceived by the surrounding environment. The namespace-specific variant is the documented choice for that case. This is a useful reminder that BPF examples are sensitive to the execution context in which they are attached and observed.
Compatibility limits and maintenance signals
The examples expose different kernel feature boundaries. `minimal` does not use or require BPF CO-RE and is described as suitable for quite old kernels. `minimal_Legacy` goes further for kernels that do not allow global variables. It defines `BPF_NO_GLOBAL_DATA` before including `bpf_helpers.h` and replaces the global `my_pid` variable with a one-element array. `bootstrap_legacy` replaces `BPF_MAP_TYPE_RINGBUF` with `BPF_MAP_TYPE_PERF_EVENT_ARRAY` for kernels older than the ring-buffer introduction in kernel 5.8.
These are meaningful constraints, but the README does not provide a complete kernel compatibility matrix. It also does not document behavior for every architecture, distribution, security policy, or hardened kernel configuration. BPF program loading, tracepoint access, debugfs tracing access, and map types can be restricted outside the example's normal environment. The repository's GitHub Actions workflows are shown for builds, including an Android workflow, but the README excerpt does not explain their full coverage.
The repository is marked as not archived, and its last push was on 2026-10-09. It has no GitHub releases, so there is no release history here to use as an upgrade signal. The project is licensed under BSD-3-Clause. That license should be reviewed for the intended distribution and modification context, but this article does not provide legal advice. The absence of releases also means that repository changes should be assessed through its commit history and build configuration rather than through published versions.
Where libbpf-bootstrap differs from BCC examples
The README positions `bootstrap` in relation to the `libbpf-tools` examples from the BCC package, while emphasizing that libbpf-bootstrap is more stand-alone and has a simpler Makefile intended to ease adaptation to a developer's own needs. The practical difference is the project boundary. libbpf-bootstrap keeps the libbpf user-space loader, BPF object, `vmlinux.h`, BPF CO-RE, map definitions, ring-buffer consumption, and command-line application in one repository layout. A developer can inspect the full path from BPF attachment to formatted terminal output.
That does not make it a replacement for BCC tooling or a monitoring suite. The examples focus on learning and adaptation through copy-and-rename workflows. They do not document a general library API, a stable compatibility promise, a daemon, or a broad collection of probes. BCC examples may serve a different development model, but the README only makes a limited comparison and does not document enough to make a feature-by-feature claim about the BCC package.
The main alternative approach is to start directly from a larger tracing tool and modify its existing probe, map, and output layers. That can reduce initial assembly work when the desired observability model already matches that tool. libbpf-bootstrap is the more direct choice when the developer specifically wants a small libbpf example with CO-RE and a visible user-space consumer.
Editorial conclusion
Choose libbpf-bootstrap when you need a small, inspectable reference for a libbpf-based BPF application, especially process tracing with CO-RE or user-space probes. Do not choose it as a complete monitoring product or for environments where kernel feature availability and root-level tracing access are unresolved. Verify the target kernel's BPF features, the build dependencies, namespace behavior, and the map and ring-buffer requirements before adopting its patterns.
Frequently asked questions
What is libbpf-bootstrap used for?
It provides C and Rust examples for developing BPF applications with libbpf and BPF CO-RE. The examples cover minimal tracing, process lifecycle tracking, user-space probes, namespaces, and older-kernel compatibility.
How do I run the minimal libbpf-bootstrap example?
Clone the repository with `--recurse-submodules`, install the dependencies, then run `make minimal` from `examples/c`. Start the program with `sudo ./minimal` and inspect `/sys/kernel/debug/tracing/trace_pipe` for `bpf_printk()` output.
Which libbpf-bootstrap example works in containers or WSL2?
The README says to use `minimal_ns` in namespaced environments such as containers and WSL2. It addresses PID visibility differences between the namespace and the host, while ordinary `minimal` may report a perceived PID that differs from the actual host process ID.
Does bootstrap require a BPF ring buffer on older kernels?
No. The `bootstrap` example uses a BPF ring buffer, while `bootstrap_legacy` replaces it with a `BPF_MAP_TYPE_PERF_EVENT_ARRAY`. The README identifies the ring-buffer map type as having been introduced in kernel 5.8.
What does the libbpf-bootstrap uprobe example demonstrate?
It attaches `uprobe` and `uretprobe` programs to the example's `uprobed_add()` and `uprobed_sub()` functions. It logs function inputs and return results through `bpf_printk()` and displays them through the kernel trace pipe.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/libbpf-libbpf-bootstrap)Was this useful?