BPF CO-RE¶
🔧 Learning Objectives
- Understand BPF CO-RE (Compile Once - Run Everywhere)
- Write portable eBPF programs
- Use BTF for type information
- Handle kernel structure changes
- Build distribution-agnostic programs
Introduction to CO-RE¶
BPF CO-RE allows eBPF programs to run on different kernel versions without recompilation by using BTF (BPF Type Format) for type information.
CO-RE Benefits
- Write once, run on multiple kernel versions
- No kernel headers needed at runtime
- Automatic handling of structure changes
- Better portability
BTF (BPF Type Format)¶
BTF provides type information about kernel data structures, enabling CO-RE.
Checking BTF Support¶
# Check if kernel has BTF
ls /sys/kernel/btf/vmlinux
# Check BTF in object file
readelf -S program.o | grep BTF
CO-RE Features¶
1. Field Existence Checks¶
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
struct task_struct___old {
// Old structure definition
} __attribute__((preserve_access_index));
SEC("kprobe/do_sys_openat2")
int trace_openat(struct pt_regs *ctx) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
u32 pid;
// CO-RE: Check if field exists
if (bpf_core_field_exists(task->pid)) {
pid = BPF_CORE_READ(task, pid);
} else {
// Fallback for older kernels
pid = bpf_get_current_pid_tgid() >> 32;
}
return 0;
}
2. Field Offset Relocation¶
// Automatically handles structure layout changes
u32 pid = BPF_CORE_READ(task, pid);
u64 start_time = BPF_CORE_READ(task, start_time);
3. Type Existence Checks¶
Compiling CO-RE Programs¶
Using clang with CO-RE¶
clang -O2 -target bpf \
-D__TARGET_ARCH_x86 \
-I/usr/include/linux \
-I/usr/include/bpf \
-g \ # Enable debug info for BTF
-c program.c -o program.o
Using libbpf¶
// libbpf automatically handles CO-RE
struct bpf_object *obj = bpf_object__open_file("program.o", NULL);
bpf_object__load(obj); // CO-RE relocations happen here
Practical Example¶
Portable Process Monitor¶
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, u32);
__type(value, u64);
} process_start_time SEC(".maps");
SEC("tracepoint/sched/sched_process_exec")
int trace_exec(struct trace_event_raw_sched_process_exec *ctx) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
u32 pid;
u64 start_time = bpf_ktime_get_ns();
// CO-RE: Works across kernel versions
if (bpf_core_field_exists(task->pid)) {
pid = BPF_CORE_READ(task, pid);
} else {
pid = bpf_get_current_pid_tgid() >> 32;
}
bpf_map_update_elem(&process_start_time, &pid, &start_time, BPF_ANY);
return 0;
}
Best Practices¶
CO-RE Best Practices
- Always use -g flag: Required for BTF generation
- Use BPF_CORE_READ: For structure field access
- Check field existence: Handle kernel version differences
- Test on multiple kernels: Ensure portability
- Use libbpf: Automatic CO-RE support
Next Steps: - Learn advanced techniques (Chapter 13) - Review research papers (Chapter 14) - Explore tools and libraries (Chapter 17)
Previous: XDP Programming
Next: Advanced Techniques