Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX EBPF / CO-RE 2026

eBPF CO-RE & libbpf
Taşınabilir BPF Programlama

Compile Once — Run Everywhere yaklaşımıyla kernel versiyon bağımsız BPF programları yaz; BTF, libbpf ve BPF skeleton ile üretim kalitesinde gözlemlenebilirlik araçları geliştir.

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

BTF (BPF Type Format)Kernel ve BPF programı tip bilgisini taşıyan kompakt format; vmlinux imajına veya ayrı dosyaya gömülür
Clang CO-RE desteği-g -target bpf ile derlenen programlara BTF relocations gömülür; hangi alan hangi struct'tan erişildiği kayıt altına alınır
libbpf çalışma zamanıBPF object'i yüklerken kernel BTF ile program BTF'ini karşılaştırır; alan offset'lerini, boyutlarını ve varlığını gerçek kernel'a göre düzeltir
BPF_CORE_READ makrosuPointer zinciri boyunca güvenli CO-RE erişim sağlar; her seviyede BTF relocation kaydı üretir

Desteklenen kernel versiyonu

GereksinimMinimum KernelNot
Temel eBPF3.18Sınırlı map ve program türleri
BTF kernel desteği4.18CONFIG_DEBUG_INFO_BTF gerekli
CO-RE + libbpf 0.15.2İlk production-ready CO-RE
BPF ring buffer5.8IORING_OP ile karıştırılmamalı
BPF skeleton v25.11bpftool gen skeleton tam destek
kfunc desteği5.15Kernel 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

bpf_objectELF BPF dosyasını temsil eder; tüm program ve map tanımlarını içerir. bpf_object__open() ile açılır, bpf_object__load() ile kernel'a yüklenir
bpf_programTek bir BPF programını temsil eder (örn. kprobe veya tracepoint handler). bpf_object__find_program_by_name() ile bulunur
bpf_mapBPF map'i temsil eder — kernel ve user-space arasında veri paylaşım noktası. bpf_object__find_map_by_name() ile erişilir
bpf_linkProgramı hook noktasına bağlar — kprobe, tracepoint, perf_event. bpf_program__attach() ile oluşturulur; destroy edilince hook kaldırılır

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

MakroKullanımAçıklama
BPF_CORE_READ(src, a, b, c)Değer okumasrc->a->b->c pointer zinciri; her seviyede CO-RE relocation
BPF_CORE_READ_INTO(dst, src, a)Belleğe kopyalaSonucu dst'e yazar; struct kopyalama için
BPF_CORE_READ_STR_INTO(dst, src, a)String kopyalabpf_core_read_str() çağırır; null-terminated güvencesi
BPF_CORE_READ_BITFIELD(src, field)Bit fieldBit 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 boyutuHedef 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

Özellikperf_event bufferring buffer
CPU başına bufferEvet — her CPU ayrıHayır — tek paylaşımlı buffer
Sıralı olaylarHayır — CPU sırasını korumazEvet — timestamp ile sıralı
Bellek kullanımıN_CPU × buffer_size1 × buffer_size
Sıfır kopya tüketimHayır — kopyalanırEvet — mmap ile doğrudan erişim
Kayıt boyutuSabit yapıDinamik boyut
Minimum kernel4.155.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
HASHHash tablosuO(1) amortize2^32PID/IP → istatistik eşleme
ARRAYDüz diziO(1)2^32Sayaçlar, sabit konfigürasyon
PERCPU_HASHCPU başına hashO(1)2^32Yüksek hızlı sayaçlar, lock yok
PERCPU_ARRAYCPU başına diziO(1)2^32Per-CPU istatistik, sıcak yol
LRU_HASHHash + LRUO(1)Otomatik evictBağlantı takibi, sınırsız akış
LRU_PERCPU_HASHCPU başına LRUO(1)Otomatik evictPer-CPU LRU, düşük contention
RINGBUFRing bufferN/A (stream)max_entries byteOlayları user-space'e ilet
PERF_EVENT_ARRAYperf eventN/ACPU sayısıEski kernel'larda olay iletimi
STACK_TRACEStack IDO(1)2^32Kernel/user stack profili
PROG_ARRAYProgram FD dizisiO(1)2^32Tail call — program zinciri
HASH_OF_MAPSİç içe mapO(1)×22^32Dinamik 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

ARRAY kullanAnahtar 0..N-1 tam sayı ise; tüm elemanlar sıklıkla erişiliyorsa; cache locality kritikse
HASH kullanAnahtar sparse dağılımlı ise (IP, PID); değer sayısı önceden bilinmiyorsa
PERCPU tercih etSayaçlar veya toplamlar için; atomic işlem maliyeti önemliyse; her CPU bağımsız güncelliyorsa
LRU_HASH tercih etSınırsız anahtar kümesi varken bellek sınırı korunacaksa; eski girişlerin otomatik temizlenmesi isteniyorsa

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

KaynakVarsayılan SınırGömülü ÖneriDeğiştirme
BPF program boyutu1M instruction64K instruction/proc/sys/kernel/bpf_jit_limit
Stack boyutu512 byteAynı (sabit)Değiştirilemez — verifier kısıtı
Map başına bellekSınırsız (ulimit)max_entries sınırlaMAP tanımında
Kilitli bellekRLIMIT_MEMLOCK8 MB minimumulimit -l veya CAP_BPF
Açık BPF fd sayısıRLIMIT_NOFILE1024 yeterliulimit -n
Verifier karmaşıklığı1M instructionAynı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 accessMap lookup dönüş değeri NULL kontrolsüz kullanılmışPointer'ı NULL kontrolüyle kullan
invalid access to map valuemap value offset sınır dışı erişimsizeof kontrolü ekle; bound check yap
back-edge from insn N to MGeri döngü — verifier geçişlerde izin vermezDöngüyü bounded loop pragma ile yeniden yaz
cannot pass map_type 3 into funcMap türü helper ile uyumsuzDoğru map türünü seç
Unreachable insn NÖlü kod — verifier optimizasyon sonucuDerleyici uyarılarını açık tut: -Wall
CO-RE relocations: field not foundvmlinux.h, hedef kernel BTF ile uyumsuzHedef kernel'dan vmlinux.h üret
libbpf: failed to load BTFCONFIG_DEBUG_INFO_BTF=y değilKernel 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