00 CO-RE neden gerekli — kernel versiyon bağımsızlığı sorunu
Geleneksel BPF programları kernel veri yapılarına doğrudan offset erişimiyle ulaşır; bu durum her kernel versiyonunda binary uyumsuzluğuna yol açar. CO-RE bu sorunu derleme zamanı meta verisi ve çalışma zamanı yeniden konumlandırma ile çözer.
Eski yaklaşımın kırılganlığı
BCC (BPF Compiler Collection) tabanlı araçlar, hedef makinede LLVM + kernel başlık dosyalarının kurulu olmasını gerektirdi. Gömülü sistemlerde bu mümkün değildir: 50–100 MB geliştirme araç zinciri üretim cihazına sığmaz. Daha da önemlisi, struct task_struct veya struct sk_buff içindeki bir alanın konumu kernel versiyonlar arasında değişir; sabit offset kullanan bir BPF programı yanlış veri okur ya da doğrulayıcı (verifier) tarafından reddedilir.
BCC yaklaşımı: hedef makinede kaynak → runtime LLVM compile → BPF bytecode Sorun: kernel başlıkları + LLVM hedef makinede şart CO-RE yaklaşımı: geliştirici makinesinde kaynak → Clang + BTF bilgisi → taşınabilir BPF object Hedef makinede: libbpf BTF farkını tespit eder → offset'leri çalışma zamanında düzeltir
CO-RE bileşenleri
Desteklenen kernel versiyonu
| Gereksinim | Minimum Kernel | Not |
|---|---|---|
| Temel eBPF | 3.18 | Sınırlı map ve program türleri |
| BTF kernel desteği | 4.18 | CONFIG_DEBUG_INFO_BTF gerekli |
| CO-RE + libbpf 0.1 | 5.2 | İlk production-ready CO-RE |
| BPF ring buffer | 5.8 | IORING_OP ile karıştırılmamalı |
| BPF skeleton v2 | 5.11 | bpftool gen skeleton tam destek |
| kfunc desteği | 5.15 | Kernel fonksiyon doğrudan çağrısı |
CO-RE olmadan neler olur
Kernel 5.10 üzerinde derlenen bir BPF programı task_struct->files alanına offset 0x8f0 ile erişiyorsa, kernel 5.15'te bu alan 0x918'e taşınmış olabilir. CO-RE relocation olmadan program hatalı veri okur; verifier strict modda ise yükleme bile başarısız olur. CO-RE ile libbpf, yükleme anında hedef kernel'ın BTF'inden doğru offset'i bulur ve instruction'ı yamalar.
01 BTF (BPF Type Format) — vmlinux.h üretimi
BTF, DWARF'ın BPF için optimize edilmiş hafif alternatifidir. Kernel tüm tip bilgisini sıkıştırılmış BTF formatında /sys/kernel/btf/vmlinux dosyasına yazar; bu dosya CO-RE'nin çalışma zamanı kaynağıdır.
BTF dosya yapısı
BTF ikili formatı bir başlık (btf_header), tip bölümü ve string tablosundan oluşur. Tipler struct, union, enum, typedef, pointer, array, function ve function prototype gibi kategorilerde kodlanır. DWARF'a kıyasla çok daha küçüktür: tipik bir kernel'da DWARF ~1 GB, BTF ise ~5 MB boyutundadır.
/sys/kernel/btf/vmlinux ← tüm kernel tipleri (struct, enum, typedef) /sys/kernel/btf/<modul> ← modüle özgü tipler (kernel 5.11+) bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
vmlinux.h üretimi
Geliştirici tek bir başlık dosyasıyla tüm kernel tiplerine erişebilir. Bu dosyayı hedef platformun kernel'ından üretmek gerekir; derleme makinasında üretilen vmlinux.h farklı yapı offset'leri taşıyabilir, bu nedenle CO-RE relocation her zaman açık tutulmalıdır.
# Hedef makinede (kernel BTF varsa):
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
# Cross-geliştirme için: vmlinux imajından üret
bpftool btf dump file /path/to/vmlinux format c > vmlinux.h
# Çıktı: tek büyük başlık dosyası (~50k satır)
# struct task_struct, struct sock, struct sk_buff, tüm kernel tipleri
BTF'nin içeriği — örnek struct
/* vmlinux.h'dan otomatik üretilen — elle yazılmaz */
struct task_struct {
struct thread_info thread_info;
unsigned int __state;
void *stack;
/* ... yüzlerce alan ... */
pid_t pid;
pid_t tgid;
struct task_struct *real_parent;
struct task_struct *parent;
char comm[16];
/* ... */
} __attribute__((preserve_access_index));
Dikkat: __attribute__((preserve_access_index)) Clang'a bu struct'a yapılan her erişim için BTF relocation kaydı üretmesini söyler. vmlinux.h tüm struct'lara bu attribute'ü zaten ekler.
BTF sağlık kontrolü
# BTF mevcut mu?
ls -la /sys/kernel/btf/vmlinux
# Kernel tiplerini listele
bpftool btf dump file /sys/kernel/btf/vmlinux | head -50
# Belirli bir struct'ı bul
bpftool btf dump file /sys/kernel/btf/vmlinux format raw | grep -A5 "task_struct"
# CONFIG kontrolü
zcat /proc/config.gz | grep BTF
# CONFIG_DEBUG_INFO_BTF=y
# CONFIG_DEBUG_INFO_BTF_MODULES=y (modül BTF desteği)
Modül BTF
Kernel 5.11 itibarıyla yüklü her modül kendi BTF bilgisini /sys/kernel/btf/<modül_adı> altında sunar. BPF programlar struct bpf_link ile modül hook'larına eklenebildiğinde modül BTF'inden CO-RE erişimi kullanabilir. Bu özellik gömülü sistemlerde özellikle değerlidir: out-of-tree modüllerle çalışan BPF araçları yazılabilir.
02 libbpf ile BPF program iskeleti
libbpf, BPF object dosyalarını yükleyen, map'leri oluşturan, programları çekirdeğe bağlayan ve CO-RE relocation uygulayan resmi C kütüphanesidir. BCC'nin yerini almış; modern BPF geliştirmenin standart API'sidir.
libbpf temel nesneleri
Tam yaşam döngüsü
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
int main(void)
{
struct bpf_object *obj;
struct bpf_program *prog;
struct bpf_link *link;
int err;
/* 1. ELF BPF dosyasını aç */
obj = bpf_object__open("myprog.bpf.o");
if (libbpf_get_error(obj)) {
fprintf(stderr, "bpf_object__open failed\n");
return 1;
}
/* 2. Kernel'a yükle: map'ler oluşturulur, CO-RE relocations uygulanır */
err = bpf_object__load(obj);
if (err) {
fprintf(stderr, "bpf_object__load: %d\n", err);
goto cleanup;
}
/* 3. Program'ı bul */
prog = bpf_object__find_program_by_name(obj, "handle_exec");
if (!prog) {
fprintf(stderr, "program bulunamadi\n");
goto cleanup;
}
/* 4. Hook noktasına bağla */
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "attach failed\n");
goto cleanup;
}
printf("BPF programi calisiyor...\n");
pause(); /* sinyal gelene kadar bekle */
bpf_link__destroy(link);
cleanup:
bpf_object__close(obj);
return err != 0;
}
libbpf log callback
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG)
return 0; /* debug mesajlarini sustur */
return vfprintf(stderr, format, args);
}
/* main() icinde: */
libbpf_set_print(libbpf_print_fn);
Derleme adımları
# BPF kernel tarafı: .bpf.c → .bpf.o
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \
-I/usr/include/bpf \
-c myprog.bpf.c -o myprog.bpf.o
# User-space tarafı: .c → binary
gcc -o myprog myprog.c \
-lbpf -lelf -lz
# Makefile ile (tipik):
CLANG ?= clang
ARCH := $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/')
BPF_CFLAGS := -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH)
03 BPF skeleton — bpftool gen skeleton
BPF skeleton, bpftool tarafından otomatik üretilen C başlık dosyasıdır. Tüm obje yönetimi, map ve program erişimini type-safe fonksiyonlara sarar; boilerplate kodu sıfıra indirir.
Skeleton üretimi
# 1. BPF kernel tarafını derle
clang -g -O2 -target bpf -c myprog.bpf.c -o myprog.bpf.o
# 2. Skeleton başlık dosyasını üret
bpftool gen skeleton myprog.bpf.o > myprog.skel.h
# Üretilen dosya: myprog.skel.h
# İçerir: struct myprog_bpf, open/load/attach/destroy fonksiyonları
Üretilen skeleton yapısı
/* myprog.skel.h — otomatik üretilir, elle düzenlenmez */
struct myprog_bpf {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_map *events; /* ring buffer map */
struct bpf_map *pid_map; /* pid filtre map */
} maps;
struct {
struct bpf_program *handle_exec;
struct bpf_program *handle_exit;
} progs;
struct {
struct bpf_link *handle_exec;
struct bpf_link *handle_exit;
} links;
struct myprog_bpf__rodata {
int target_pid;
} *rodata;
};
static inline struct myprog_bpf *myprog_bpf__open(void);
static inline int myprog_bpf__load(struct myprog_bpf *);
static inline int myprog_bpf__attach(struct myprog_bpf *);
static inline void myprog_bpf__destroy(struct myprog_bpf *);
Skeleton ile kullanım
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include "myprog.skel.h"
int main(int argc, char **argv)
{
struct myprog_bpf *skel;
int err;
/* 1. Ac ve konfigüre et */
skel = myprog_bpf__open();
if (!skel) { perror("open"); return 1; }
/* 2. rodata araciligiyla BPF tarafina parametre gonder */
skel->rodata->target_pid = atoi(argv[1]);
/* 3. Yükle */
err = myprog_bpf__load(skel);
if (err) { perror("load"); goto cleanup; }
/* 4. Otomatik olarak tüm programlari uygun hook'lara bagla */
err = myprog_bpf__attach(skel);
if (err) { perror("attach"); goto cleanup; }
/* 5. Map'e erişim: tip güvenceli */
int map_fd = bpf_map__fd(skel->maps.pid_map);
printf("Izleme basliyor, PID=%d\n", skel->rodata->target_pid);
pause();
cleanup:
myprog_bpf__destroy(skel);
return err != 0;
}
rodata ve global değişkenler
BPF programındaki global değişkenler skeleton üzerinden user-space'den yüklemeden önce ayarlanabilir. Yükleme sonrası rodata salt okunur olur; BPF programı bu değerlere sabit olarak erişir, verifier optimizasyon yapabilir.
/* myprog.bpf.c kernel tarafinda: */
const volatile int target_pid = 0; /* rodata — user-space'den set edilir */
volatile int event_count = 0; /* bss — okunabilir/yazılabilir */
SEC("tracepoint/syscalls/sys_enter_execve")
int handle_exec(struct trace_event_raw_sys_enter *ctx)
{
if (target_pid != 0 && bpf_get_current_pid_tgid() >> 32 != target_pid)
return 0;
__sync_fetch_and_add(&event_count, 1);
return 0;
}
04 CO-RE erişim makroları — BPF_CORE_READ
CO-RE erişim makroları, kernel struct alanlarına BTF relocation kaydederek erişim sağlar. Doğrudan pointer dereference yerine bu makroların kullanılması zorunludur; aksi hâlde versiyon bağımsızlığı kırılır.
BPF_CORE_READ
#include "vmlinux.h"
#include <bpf/bpf_core_read.h>
SEC("kprobe/do_sys_openat2")
int trace_openat(struct pt_regs *ctx)
{
struct task_struct *task;
pid_t pid;
char comm[16];
task = (struct task_struct *)bpf_get_current_task();
/* YANLIS: doğrudan pointer erişimi — CO-RE relocation yok */
/* pid = task->pid; */
/* DOGRU: BPF_CORE_READ makrosu — BTF relocation kaydedilir */
pid = BPF_CORE_READ(task, pid);
/* Zincirleme erişim: task->parent->pid */
pid_t ppid = BPF_CORE_READ(task, real_parent, pid);
/* Dizi erişimi: task->comm */
BPF_CORE_READ_STR_INTO(&comm, task, comm);
bpf_printk("openat: pid=%d ppid=%d comm=%s\n", pid, ppid, comm);
return 0;
}
Makro ailesi
| Makro | Kullanım | Açıklama |
|---|---|---|
BPF_CORE_READ(src, a, b, c) | Değer okuma | src->a->b->c pointer zinciri; her seviyede CO-RE relocation |
BPF_CORE_READ_INTO(dst, src, a) | Belleğe kopyala | Sonucu dst'e yazar; struct kopyalama için |
BPF_CORE_READ_STR_INTO(dst, src, a) | String kopyala | bpf_core_read_str() çağırır; null-terminated güvencesi |
BPF_CORE_READ_BITFIELD(src, field) | Bit field | Bit field'lara CO-RE erişim; mask ve shift otomatik |
bpf_core_field_exists(expr) | Alan varlığı | Kernel versiyonunda alan var mı? 1 veya 0 döner |
bpf_core_type_exists(type) | Tip varlığı | Verilen tip kernel BTF'de mevcut mu? |
bpf_core_field_size(expr) | Alan boyutu | Hedef kernel'daki gerçek sizeof |
Koşullu kernel versiyonu uyumu
SEC("kprobe/tcp_v4_connect")
int trace_connect(struct pt_regs *ctx)
{
struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
__u16 dport;
/* Kernel 5.x'te alan adı değişti: __sk_common.skc_dport */
if (bpf_core_field_exists(sk->__sk_common.skc_dport)) {
dport = BPF_CORE_READ(sk, __sk_common, skc_dport);
} else {
/* Eski kernel: struct sock doğrudan içeriyor */
dport = BPF_CORE_READ(sk, sk_dport);
}
bpf_printk("tcp connect dport=%u\n", bpf_ntohs(dport));
return 0;
}
Enum değer CO-RE
/* Enum değerinin kernel versiyonuna göre değişmesi durumu */
int flags = BPF_CORE_READ(task, __state);
/* Enum sabit değeri — TASK_RUNNING her versiyonda aynı ama garantilemek için: */
if (bpf_core_type_exists(enum task_state)) {
/* Modern kernel: enum tanımlı */
} else {
/* Eski kernel: #define sabitleri */
}
05 BPF ring buffer — ringbuf_map ve user-space consumer
BPF ring buffer (RINGBUF), kernel 5.8'de tanıtılan ve perf event buffer'ın yerini alan modern veri iletim mekanizmasıdır. Çok CPU'lu sistemlerde paylaşımlı tek buffer, sıralı olaylar ve sıfır kopya tüketim sunar.
perf buffer ile karşılaştırma
| Özellik | perf_event buffer | ring buffer |
|---|---|---|
| CPU başına buffer | Evet — her CPU ayrı | Hayır — tek paylaşımlı buffer |
| Sıralı olaylar | Hayır — CPU sırasını korumaz | Evet — timestamp ile sıralı |
| Bellek kullanımı | N_CPU × buffer_size | 1 × buffer_size |
| Sıfır kopya tüketim | Hayır — kopyalanır | Evet — mmap ile doğrudan erişim |
| Kayıt boyutu | Sabit yapı | Dinamik boyut |
| Minimum kernel | 4.15 | 5.8 |
Kernel tarafı: ring buffer tanımı ve yazma
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
/* Olay yapisi — user-space ile paylasimlı */
struct event {
__u32 pid;
__u32 ppid;
__u64 ts_ns;
char comm[16];
char filename[256];
};
/* Ring buffer map tanımı */
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024); /* 256 KB — 2'nin kati olmali */
} rb SEC(".maps");
SEC("tracepoint/syscalls/sys_enter_execve")
int handle_exec(struct trace_event_raw_sys_enter *ctx)
{
struct event *e;
struct task_struct *task;
/* Reserve: atomik olarak buffer alanı ayır */
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e) return 0; /* buffer dolu — kayıt at */
/* Alanları doldur */
task = (struct task_struct *)bpf_get_current_task();
e->pid = bpf_get_current_pid_tgid() >> 32;
e->ppid = BPF_CORE_READ(task, real_parent, pid);
e->ts_ns = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_probe_read_user_str(&e->filename, sizeof(e->filename),
(const char *)ctx->args[0]);
/* Submit: user-space'e ilet */
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
User-space: ring buffer consumer
#include <bpf/libbpf.h>
#include "myprog.skel.h"
#include "myprog.h" /* struct event tanımı */
static int handle_event(void *ctx, void *data, size_t data_sz)
{
const struct event *e = data;
struct tm *tm;
char ts[32];
time_t t;
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
printf("%-8s %-7d %-7d %-16s %s\n",
ts, e->pid, e->ppid, e->comm, e->filename);
return 0;
}
int main(void)
{
struct myprog_bpf *skel;
struct ring_buffer *rb = NULL;
int err;
skel = myprog_bpf__open_and_load();
if (!skel) return 1;
err = myprog_bpf__attach(skel);
if (err) goto cleanup;
/* Ring buffer oluştur: map_fd + callback */
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb),
handle_event, NULL, NULL);
if (!rb) { err = -1; goto cleanup; }
printf("%-8s %-7s %-7s %-16s %s\n",
"TIME", "PID", "PPID", "COMM", "FILENAME");
while (true) {
/* Olayları işle: poll + callback */
err = ring_buffer__poll(rb, 100 /* ms timeout */);
if (err == -EINTR) { err = 0; break; }
if (err < 0) {
fprintf(stderr, "ring_buffer__poll: %d\n", err);
break;
}
}
cleanup:
ring_buffer__free(rb);
myprog_bpf__destroy(skel);
return err < 0 ? 1 : 0;
}
ring_buffer__consume vs ring_buffer__poll
ring_buffer__poll(rb, timeout_ms) epoll tabanlıdır; kernel bildirimi bekler, CPU kullanmaz. ring_buffer__consume(rb) polling yapar; düşük latency gerekiyorsa tercih edilir ama CPU tüketir. Yüksek frekanslı olaylar için ring_buffer__poll(rb, 0) ile sıfır timeout kullanılabilir.
06 BPF map türleri karşılaştırması
BPF map'leri kernel ile user-space arasındaki veri köprüsüdür. Her türün farklı performans özellikleri, atomiklik garantileri ve kullanım senaryoları vardır; doğru seçim hem doğruluk hem de performans açısından kritiktir.
Map türleri özet tablosu
| Map Türü | Yapı | Arama O() | Max Giriş | Tipik Kullanım |
|---|---|---|---|---|
| HASH | Hash tablosu | O(1) amortize | 2^32 | PID/IP → istatistik eşleme |
| ARRAY | Düz dizi | O(1) | 2^32 | Sayaçlar, sabit konfigürasyon |
| PERCPU_HASH | CPU başına hash | O(1) | 2^32 | Yüksek hızlı sayaçlar, lock yok |
| PERCPU_ARRAY | CPU başına dizi | O(1) | 2^32 | Per-CPU istatistik, sıcak yol |
| LRU_HASH | Hash + LRU | O(1) | Otomatik evict | Bağlantı takibi, sınırsız akış |
| LRU_PERCPU_HASH | CPU başına LRU | O(1) | Otomatik evict | Per-CPU LRU, düşük contention |
| RINGBUF | Ring buffer | N/A (stream) | max_entries byte | Olayları user-space'e ilet |
| PERF_EVENT_ARRAY | perf event | N/A | CPU sayısı | Eski kernel'larda olay iletimi |
| STACK_TRACE | Stack ID | O(1) | 2^32 | Kernel/user stack profili |
| PROG_ARRAY | Program FD dizisi | O(1) | 2^32 | Tail call — program zinciri |
| HASH_OF_MAPS | İç içe map | O(1)×2 | 2^32 | Dinamik iç map yönetimi |
PERCPU map kullanımı
/* Kernel tarafı: per-CPU sayaç */
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u64);
} packet_count SEC(".maps");
SEC("xdp")
int count_packets(struct xdp_md *ctx)
{
__u32 key = 0;
__u64 *cnt = bpf_map_lookup_elem(&packet_count, &key);
if (cnt) (*cnt)++; /* lock free: her CPU kendi elemanina yazar */
return XDP_PASS;
}
/* User-space: tüm CPU'lardan topla */
__u64 values[MAX_CPUS];
__u32 key = 0;
bpf_map_lookup_elem(map_fd, &key, values);
__u64 total = 0;
for (int i = 0; i < nr_cpus; i++) total += values[i];
LRU_HASH — bağlantı takibi
struct conn_info {
__u32 saddr, daddr;
__u16 sport, dport;
__u64 bytes;
__u64 start_ns;
};
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 65536); /* en fazla 64K eş zamanlı bağlantı */
__type(key, __u64); /* socket pointer */
__type(value, struct conn_info);
} connections SEC(".maps");
/* max_entries dolunca en eski kullanılan giriş otomatik silinir */
HASH vs ARRAY seçim kriterleri
07 Gömülü sistemlerde eBPF — minimal kernel config
Gömülü Linux'ta eBPF kullanımı kaynak kısıtları nedeniyle dikkatli kernel yapılandırması gerektirir. BTF, JIT ve temel BPF altyapısı etkinleştirilmeli; gereksiz program türleri devre dışı bırakılmalıdır.
Zorunlu kernel konfigürasyonu
# Temel BPF altyapısı
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y # Interpreter'i devre dışı bırak (güvenlik + perf)
# BTF — CO-RE için şart
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=y # Modül BTF (opsiyonel)
# Tracing desteği
CONFIG_BPF_EVENTS=y
CONFIG_TRACEPOINTS=y
CONFIG_KPROBE_EVENTS=y
CONFIG_UPROBE_EVENTS=y
# Program türleri (ihtiyaca göre seç)
CONFIG_CGROUP_BPF=y # cgroup hook'ları
# CONFIG_NET_ACT_BPF=y # TC eBPF (ağ gerekliyse)
# CONFIG_XDP_SOCKETS=y # AF_XDP (yüksek hız ağ)
# Geliştirme araçları (üretimde gerekmeyebilir)
# CONFIG_BPF_PRELOAD=y
Bellek kısıtları ve sınırlar
| Kaynak | Varsayılan Sınır | Gömülü Öneri | Değiştirme |
|---|---|---|---|
| BPF program boyutu | 1M instruction | 64K instruction | /proc/sys/kernel/bpf_jit_limit |
| Stack boyutu | 512 byte | Aynı (sabit) | Değiştirilemez — verifier kısıtı |
| Map başına bellek | Sınırsız (ulimit) | max_entries sınırla | MAP tanımında |
| Kilitli bellek | RLIMIT_MEMLOCK | 8 MB minimum | ulimit -l veya CAP_BPF |
| Açık BPF fd sayısı | RLIMIT_NOFILE | 1024 yeterli | ulimit -n |
| Verifier karmaşıklığı | 1M instruction | Aynı | BPF_COMPLEXITY_LIMIT_INSNS |
ARM64 gömülü sistemde BTF
# Yocto meta-layer'da kernel fragment:
# meta-custom/recipes-kernel/linux/linux-%.bbappend
# files/bpf.cfg:
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_REDUCED=n # BTF için tam debug info şart
# Dikkat: DEBUG_INFO_BTF, paket boyutunu artırır
# vmlinux BTF ~2-5 MB; sadece /sys/kernel/btf/vmlinux'a yansır
# vmlinux imajının boyutu artmaz (BTF zaten debug bölümünde)
# Cross-derleme ile BTF üretimi:
bitbake linux-custom
# tmp/deploy/images/myboard/vmlinux içinden BTF extract:
bpftool btf dump file tmp/deploy/images/myboard/vmlinux format c \
> vmlinux-myboard.h
libbpf statik bağlama
# Gömülü sistemde dinamik libbpf olmayabilir; statik bağla:
# CMakeLists.txt veya Makefile:
# libbpf'yi statik derle:
git clone https://github.com/libbpf/libbpf.git
cd libbpf/src
make BUILD_STATIC_ONLY=1 OBJDIR=build
# Uygulamayı statik bağla:
gcc -static myprog.c -I libbpf/src \
libbpf/src/build/libbpf.a \
-lelf -lz -o myprog
# Binary boyutu: ~400 KB (stripped)
# Hedef cihazda libelf ve libz kurulu olmalı (veya statik bağla)
Kaynak tasarrufu — minimal BPF programı
Gömülü sistemlerde BPF programlarını küçük tutmak verifier süresini ve JIT belleğini azaltır. 4 KB'tan küçük BPF programları çoğu ARM Cortex-A gömülü sistemde sorunsuz çalışır. Ring buffer yerine PERCPU_ARRAY tercih etmek bellek tüketimini öngörülebilir kılar.
08 Hata ayıklama — bpftool, libbpf log, verifier
BPF verifier hataları, CO-RE relocation sorunları ve map erişim hataları en sık karşılaşılan sorunlardır. bpftool ve libbpf log mekanizmaları bu sorunları teşhis etmek için kapsamlı araçlar sunar.
bpftool: yüklü programları incele
# Çalışan tüm BPF programlarını listele
bpftool prog list
# 42: kprobe name handle_exec tag abc123def456 gpl
# loaded_at 2026-03-15T10:22:31+0000 uid 0
# xlated 312B jited 256B memlock 4096B map_ids 7,8
# Program bytecode'unu dök (xlated = verifier sonrası)
bpftool prog dump xlated id 42
# JIT edilmiş makine kodunu dök
bpftool prog dump jited id 42
# BTF bilgisiyle birlikte program dök
bpftool prog dump xlated id 42 linum
# Programı dosyaya kaydet (başka makinede analiz için)
bpftool prog dump xlated id 42 file prog.bpf
bpftool: map inceleme
# Tüm map'leri listele
bpftool map list
# 7: hash name pid_map flags 0x0
# key 4B value 8B max_entries 1024 memlock 81920B
# Map içeriğini dök
bpftool map dump id 7
# Belirli anahtarı sorgula
bpftool map lookup id 7 key 0x12 0x34 0x00 0x00
# Map'e değer yaz (test için)
bpftool map update id 7 key 0x01 0x00 0x00 0x00 value 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
bpftool: BTF inceleme
# Kernel BTF'deki tüm tipleri listele
bpftool btf dump id 1
# Belirli bir struct bul
bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A 20 "struct task_struct {"
# BPF program BTF'ini incele
bpftool prog dump xlated id 42 visual | dot -Tpng -o prog_cfg.png
# CO-RE relocation bilgisini göster
llvm-readelf -S myprog.bpf.o | grep BTF
llvm-objdump --section=.BTF.ext myprog.bpf.o
libbpf verbose log
/* Maksimum log seviyesi: verifier hatalarını da gösterir */
static int print_all(enum libbpf_print_level level,
const char *fmt, va_list args)
{
return vfprintf(stderr, fmt, args);
}
libbpf_set_print(print_all);
/* Verifier log'u doğrudan almak için: */
char log_buf[1 << 20]; /* 1 MB log tamponu */
struct bpf_object_open_opts opts = {
.sz = sizeof(opts),
};
struct bpf_program *prog = /* ... */;
bpf_program__set_log_buf(prog, log_buf, sizeof(log_buf));
bpf_program__set_log_level(prog, 2); /* 1=verifier, 2=verbose, 4=stats */
err = bpf_object__load(obj);
if (err)
fprintf(stderr, "Verifier log:\n%s\n", log_buf);
Yaygın verifier hataları ve çözümleri
| Hata Mesajı | Neden | Çözüm |
|---|---|---|
R0 unbounded memory access | Map lookup dönüş değeri NULL kontrolsüz kullanılmış | Pointer'ı NULL kontrolüyle kullan |
invalid access to map value | map value offset sınır dışı erişim | sizeof kontrolü ekle; bound check yap |
back-edge from insn N to M | Geri döngü — verifier geçişlerde izin vermez | Döngüyü bounded loop pragma ile yeniden yaz |
cannot pass map_type 3 into func | Map türü helper ile uyumsuz | Doğru map türünü seç |
Unreachable insn N | Ölü kod — verifier optimizasyon sonucu | Derleyici uyarılarını açık tut: -Wall |
CO-RE relocations: field not found | vmlinux.h, hedef kernel BTF ile uyumsuz | Hedef kernel'dan vmlinux.h üret |
libbpf: failed to load BTF | CONFIG_DEBUG_INFO_BTF=y değil | Kernel yeniden derle veya BTF sidecar kullan |
bpf_printk ile çalışma zamanı debug
/* Kernel tarafı: trace_pipe'e yazar */
bpf_printk("pid=%d comm=%s bytes=%llu\n",
e->pid, e->comm, e->bytes);
/* User-space: çıktıyı oku */
/* sudo cat /sys/kernel/debug/tracing/trace_pipe */
/* Dikkat: bpf_printk çok pahalıdır — sadece debug build'da kullan */
/* Üretimde ring buffer kullan */
#ifdef DEBUG
#define BPF_LOG(...) bpf_printk(__VA_ARGS__)
#else
#define BPF_LOG(...) do {} while (0)
#endif