linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Процессы и ресурсы/bpf-co-re

kb/processes ── Процессы и ресурсы ── advanced

BPF CO-RE - Compile Once Run Everywhere

CO-RE - один скомпилированный eBPF-объект работает на разных kernel благодаря BTF (BPF Type Format). vmlinux.h - dump структур ядра. libbpf на runtime перезаписывает offsets. Заменяет BCC, не нужен LLVM в проде.

view as markdownaka: co-re, bpf-co-re, btf, vmlinux, libbpf-bootstrap

Зачем CO-RE

Раньше [[ebpf-basics|eBPF]] программы привязывались к конкретному kernel - structure layout менялся между версиями. Если writeable (task_struct->pid) перенесли на другой offset - программа сломалась.

Решений было два:

  1. kernel-headers attached - программа собирается под целевой kernel (BCC: компиляция в runtime через LLVM в каждом deploy)
  2. CO-RE (Compile Once - Run Everywhere) - программа собирается один раз, на runtime libbpf делает relocations через BTF

Сегодня CO-RE - стандарт. BCC объявлен legacy, новые tracing-инструменты пишут на libbpf+CO-RE.

BTF - BPF Type Format

BTF - компактный формат метаданных о типах (как DWARF, но специально для BPF). Описывает structs, unions, enums, function signatures.

Kernel экспортирует свой BTF в /sys/kernel/btf/vmlinux (с CONFIG_DEBUG_INFO_BTF=y, в большинстве дистрибутивов с 5.4+).

Размер - около 5-7 МБ против 200+ МБ для DWARF debug-info. Это позволяет держать его постоянно в kernel.

Каждый загруженный module тоже имеет свой BTF в /sys/kernel/btf/<module>.

Userspace проверка:

ls /sys/kernel/btf/
bpftool btf dump file /sys/kernel/btf/vmlinux | head -20

vmlinux.h - заголовок всего kernel

Из BTF собирается vmlinux.h - один большой header со всеми структурами kernel:

bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

Эта строка - вся включенная: вместо #include <linux/sched.h> ты пишешь #include "vmlinux.h" и получаешь все типы. Не надо ставить kernel-headers, не надо думать про CONFIG_*.

Минусы: 50K+ строк, дольше компиляция. Зато не нужен kernel-source.

CO-RE relocations

Идея: clang при компиляции eBPF не "запекает" offset поля в bytecode, а оставляет специальный CO-RE relocation record. На runtime libbpf читает target-kernel BTF, находит соответствующее поле, переписывает offset в загруженной программе.

Помечаешь access через BPF_CORE_READ или __builtin_preserve_access_index:

c
#include <bpf/bpf_core_read.h>
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
pid_t pid = BPF_CORE_READ(task, pid);
pid_t parent_pid = BPF_CORE_READ(task, parent, pid);

В bytecode: вместо "load байты по offset 0x4F8" - "load по offset(struct task_struct, pid)" с relocation record. На runtime libbpf проверяет в target BTF где pid лежит в task_struct и подставляет правильный offset.

Если поле переименовано - можно использовать BPF_CORE_READ_INTO с fallback'ами, или __attribute__((preserve_access_index)) структуры.

Field-existence checks

CO-RE умеет работать с отсутствующими в target-kernel полями:

c
if (bpf_core_field_exists(task->some_new_field)) {
    val = BPF_CORE_READ(task, some_new_field);
} else {
    val = -1;  // graceful fallback
}

То же для enum значений:

c
if (bpf_core_enum_value_exists(enum cpu_state, CPU_STATE_NEW)) {
    ...
}

Это даёт настоящую portability - один бинарник работает от kernel 5.4 до 6.10, gracefully degraded'ит features.

libbpf-bootstrap - стартовый шаблон

github.com/libbpf/libbpf-bootstrap - канонический шаблон проекта на libbpf+CO-RE. Структура:

myproject/
  src/
    myprog.bpf.c       # eBPF-программа на C
    myprog.c           # userspace-loader
  Makefile             # генерирует skeleton, собирает оба
  libbpf/              # submodule с библиотекой
  bpftool/             # submodule

Workflow:

  1. clang -O2 -target bpf -c myprog.bpf.c -o myprog.bpf.o
  2. bpftool gen skeleton myprog.bpf.o > myprog.skel.h
  3. Userspace myprog.c includ'ит skeleton, вызывает myprog__open(), myprog__load(), myprog__attach()
  4. Финальный binary - один статически слинкованный исполняемый файл с embedded BPF-bytecode

Деплой: один файл, нет dependency на kernel-headers, BCC, LLVM.

CO-RE vs BCC - сравнение

СвойствоBCClibbpf+CO-RE
Что нужно на targetLLVM (~600 МБ), kernel-headersничего (только kernel с BTF)
Размер артефактаPython + C sourceодин statically-linked binary
Время стартасекунды (LLVM compile)миллисекунды
Portabilityпривязан к target kernelодин бинарник на много kernel
Сложность написанияпроще (high-level Python)сложнее (C, skeleton)
Языки userspacePythonC, Go (libbpfgo), Rust (libbpf-rs)

Современные tools (parca-agent, tetragon, hubble, beyla) - все на libbpf+CO-RE.

BTF на старых ядрах - BTFGen

CO-RE требует BTF в kernel. Старые ядра (RHEL 7, Ubuntu 18.04) - CONFIG_DEBUG_INFO_BTF не было. Решение - BTFGen (от Aqua Security): генерится "минимальный" BTF под конкретное приложение, доставляется в его deploy. На target libbpf использует этот мини-BTF вместо vmlinux.

Packaged в libbpfgo/btfgen или через bpftool gen min_core_btf. Для observability на legacy kernels - спасение.

Миграция с BCC на libbpf+CO-RE

Steps:

  1. Скачать vmlinux.h от target kernel (или generate из своего):
    bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
  2. Заменить #include <linux/...> на #include "vmlinux.h"
  3. Заменить direct field access (task->pid) на BPF_CORE_READ(task, pid)
  4. Userspace - переписать с Python BCC на C libbpf
  5. Сгенерить skeleton, использовать myprog__attach() вместо BCC's attach_kprobe
  6. Опубликовать как один static binary

Утилита bcc-to-libbpf (community) делает базовое преобразование но не идеально - правки руками всё равно нужны.

Когда CO-RE ломается

  • field 'foo' not found in target kernel - поле было переименовано/ удалено. Используй bpf_core_field_exists + fallback.
  • type 'struct foo' not found - struct переименована. Старые кейсы: request_queue → структура изменена в 6.x. Альтернатива - BPF_CORE_READ_BITFIELD_PROBED или прямой kprobe.
  • kernel без BTF (/sys/kernel/btf/vmlinux нет) - используй BTFGen или собирай custom kernel с CONFIG_DEBUG_INFO_BTF=y.
  • Verifier rejected - тот же verifier что и в ebpf-basics, CO-RE сам по себе не помогает с safety checks.
  • Crash после kernel-update - вероятно тип, который ты читаешь, кардинально изменился (struct rebuilt). Re-test on new kernel, добавь field-existence check.

Где почитать

  • https://nakryiko.com/posts/bpf-portability-and-co-re/ - оригинальная статья от Andrii Nakryiko (автора libbpf)
  • https://github.com/libbpf/libbpf-bootstrap - стартовый шаблон
  • https://docs.kernel.org/bpf/btf.html - спецификация BTF
  • https://ebpf.io/applications/ - real-world проекты на CO-RE

§ команды

bash
ls /sys/kernel/btf/

Какие BTF доступны в kernel - vmlinux + все loaded modules

bash
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

Сгенерить vmlinux.h из BTF текущего kernel - один include для всех типов

bash
bpftool btf dump file myprog.bpf.o

Показать BTF из скомпилированной BPF-программы (для debug)

bash
clang -O2 -g -target bpf -c myprog.bpf.c -o myprog.bpf.o

Компиляция eBPF с -g (DWARF/BTF) - обязательно для CO-RE relocations

bash
bpftool gen skeleton myprog.bpf.o > myprog.skel.h

Сгенерить C skeleton из BPF-объекта - удобный API для userspace

bash
bpftool prog load myprog.bpf.o /sys/fs/bpf/myprog

Загрузить и pin программу - relocations происходят при load

bash
bpftool gen min_core_btf /sys/kernel/btf/vmlinux mini.btf prog1.bpf.o prog2.bpf.o

Сгенерить минимальный BTF для распространения на legacy kernels

bash
git clone --recursive https://github.com/libbpf/libbpf-bootstrap

Стартовый шаблон проекта на libbpf+CO-RE

§ см. также

  • ebpf-basicseBPF - программируемый kerneleBPF - запуск sandboxed-программ в kernel без kernel-modules. Прицепляются к hooks (kprobe, uprobe, tracepoint, perf, socket, XDP). Verifier гарантирует завершение и safety. JIT компилирует в native. bpftool/libbpf/BCC - userspace tooling.
  • kernel-modulesKernel modules - LKM, modprobe, signing, DKMSLKM - код, динамически загружаемый в kernel. modprobe резолвит зависимости через depmod. Подпись модуля для Secure Boot. DKMS пересобирает out-of-tree модули после kernel-upgrade. Lockdown mode запрещает загрузку неподписанных.
  • ebpf-xdpeBPF XDP - kernel data-planeXDP - eBPF-программа на самом раннем RX-hook (до skb_alloc). Actions: DROP, PASS, TX, REDIRECT. Native-mode в драйвере, generic в стеке. AF_XDP - zero-copy в userspace. Use cases - DDoS-фильтр, L4 load balancer, cilium kube-proxy.
  • cmd-stracestrace - какие syscall'ы делает процесс`strace` показывает в реальном времени какие системные вызовы делает процесс и с какими аргументами. Главный инструмент когда «процесс молчит».
  • namespacesLinux namespacesNamespaces - механизм ядра, который даёт процессу собственный изолированный view на ресурс (сеть, mount-points, PID, UID, IPC, hostname, time). На них построены все контейнеры.
  • pyroscope-continuous-profilingContinuous profiling: Pyroscope, eBPF, flame graphs в продеContinuous profiling - always-on CPU/memory profiler в проде через eBPF. 1-2% overhead. Flame graphs показывают hot path. Pyroscope (Grafana), Parca, Polar Signals. Замена ad-hoc perf для production debug.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки