Mastering eBPF with CO-RE: From Basics to Go Implementation
This article introduces eBPF fundamentals, explains the Compile‑Once‑Run‑Everywhere (CO‑RE) approach, compares it with traditional eBPF, outlines best practices, and walks through a complete Go‑based example using the Cilium/eBPF library and the eunomia‑bpf runtime.
eBPF Introduction
Official documentation: https://ebpf.io/
Kernel‑related docs: https://prototype-kernel.readthedocs.io/en/latest/bpf/
Chinese beginner guide: https://www.ebpf.top/post/ebpf_intro
Curated list of resources: https://github.com/zoidbergwill/awesome-ebpf
Kernel version compatibility table: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
eBPF Toolchain
bpftrace tutorial (simplest entry point): https://eunomia.dev/zh/tutorials/bpftrace-tutorial/
BCC example collection: https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md
libbpf bootstrap examples: https://github.com/libbpf/libbpf-bootstrap
libbpf + eunomia‑bpf tutorial: https://github.com/eunomia-bpf/bpf-developer-tutorial
XDP tutorial: https://github.com/xdp-project/xdp-tutorial
Linux kernel sample programs: https://github.com/torvalds/linux/tree/master/samples/bpf
Go eBPF Libraries
cilium/ebpf – widely used Go library for loading and managing eBPF programs.
iovisor/gobpf – Go bindings for BCC.
libbpfgo – thin Go wrapper around libbpf.
dropbox/goebpf – simple Go eBPF library from Dropbox.
eBPF CO‑RE: Compile Once – Run Everywhere
CO‑RE solves the portability problem by embedding BTF (BPF Type Format) information in the eBPF object. At load time the kernel provides type and symbol data, allowing the loader to relocate memory accesses without recompiling the program for each kernel version.
Traditional eBPF vs CO‑RE eBPF
Traditional eBPF limitations
Poor portability – recompilation required for every target kernel.
Full kernel headers needed at compile time.
Programs often tied to a specific kernel version.
CO‑RE advantages
Compile once, run on many kernels.
No need for complete kernel headers; BTF supplies the necessary type information.
Adaptable to different kernel configurations.
Simplified deployment – a single binary can be distributed.
Best Practices
Use the latest libbpf release to obtain the newest CO‑RE support.
Enable BTF generation with the -g compile flag.
Generate a skeleton header via bpftool gen skeleton to simplify loading.
Avoid hard‑coded struct offsets; rely on BPF helpers and CO‑RE relocations.
Test the program on multiple kernel versions to verify correct relocation.
eunomia‑bpf Overview
eunomia‑bpf is an open‑source runtime and toolchain built on libbpf’s CO‑RE support. It abstracts away the need to write user‑space loading code while remaining 100 % compatible with libbpf, libbpfgo, and libbpf‑rs APIs.
Only kernel‑mode code is written; eunomia‑bpf extracts exported symbols and loads the program dynamically.
User‑space logic can be written in WebAssembly, enabling multi‑language development.
Pre‑compiled eBPF objects can be packaged as JSON or WASM modules, allowing cross‑architecture and cross‑kernel distribution without recompilation.
Cilium/eBPF Library
Cilium/eBPF provides a safe, high‑performance Go API for loading, managing, and interacting with eBPF programs.
Program loading – load pre‑compiled eBPF object files.
Map management – create, access, and manage eBPF maps.
Program attachment – attach programs to various kernel hook points (kprobes, tracepoints, XDP, etc.).
BTF support – enables CO‑RE compatibility across kernel versions.
CO‑RE support – write once, run everywhere.
Error handling – detailed error messages for debugging.
Security checks – prevents unsafe operations at load time.
Basic Cilium/eBPF Example
The following Go program loads a simple kprobe that tracks the execve system call. The C source kprobe.c is compiled to an eBPF object with bpf2go (triggered by the go:generate directive).
package main
import (
"fmt"
"log"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/rlimit"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf kprobe.c -- -I../headers
func main() {
// Allow the process to lock memory for eBPF resources
if err := rlimit.RemoveMemlock(); err != nil {
log.Fatal(err)
}
// Load the pre‑compiled eBPF program
objs := bpfObjects{}
if err := loadBpfObjects(&objs, nil); err != nil {
log.Fatalf("loading objects: %v", err)
}
defer objs.Close()
// Create a kprobe program
kp, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Name: "kprobe_exec",
Type: ebpf.Kprobe,
License: "GPL",
})
if err != nil {
log.Fatalf("creating program: %v", err)
}
defer kp.Close()
// Attach the kprobe to the execve syscall
if err := kp.Attach("__x64_sys_execve"); err != nil {
log.Fatalf("attaching program: %v", err)
}
fmt.Println("Successfully loaded and attached eBPF program")
// Keep the program running
select {}
}The accompanying C file defines the kernel‑mode logic:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
struct event {
u32 pid;
u8 comm[80];
};
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} events SEC(".maps");
SEC("kprobe/__x64_sys_execve")
int kprobe_exec(struct pt_regs *ctx) {
struct event event = {};
event.pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&event.comm, sizeof(event.comm));
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
return 0;
}Program flow:
Retrieve the current process ID with bpf_get_current_pid_tgid().
Fetch the process name via bpf_get_current_comm().
Emit the event struct to user space through a perf event array.
Additional Notes
Requires a recent Linux kernel (5.4+ recommended).
Root privileges are typically needed to load eBPF programs.
Some features depend on kernel configuration options (e.g., CONFIG_BPF, CONFIG_DEBUG_INFO_BTF).
References
[1] bpf‑developer‑tutorial: https://github.com/eunomia-bpf/bpf-developer-tutorial/blob/main/src/0-introduce/README.md
[2] cilium/ebpf: https://github.com/cilium/ebpf
[3] iovisor/gobpf: https://github.com/iovisor/gobpf
[4] libbpfgo: https://github.com/aquasecurity/libbpfgo
[5] dropbox/goebpf: https://github.com/dropbox/goebpf
[6] eunomia‑bpf: https://github.com/eunomia-bpf/eunomia-bpf
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
BirdNest Tech Talk
Author of the rpcx microservice framework, original book author, and chair of Baidu's Go CMC committee.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
