Begriff
BTF (BPF Type Format)
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Wenn du ein eBPF-Programm schreibst (um Pakete zu filtern oder Crashes zu debuggen), greifst du auf Kernel-Strukturen zu (struct task_struct).
Problem: Diese Strukturen ändern sich in jedem Linux-Update!
Feld pid ist heute an Offset 0, morgen an Offset 4.
Früher musste man eBPF-Programme auf jedem Server neu kompilieren (mit den passenden Kernel-Headern). Hölle.
BTF ist wie "Metadaten für den Kernel".
Der Kernel beschreibt sich selbst: "Ich habe eine Struktur task_struct, und pid ist an Offset 4."
Das eBPF-Programm liest das zur Ladezeit und passt sich automatisch an (CO-RE: Compile Once - Run Everywhere).
Jetzt kannst du ein Monitoring-Tool binary bauen, und es läuft auf Ubuntu, Fedora und Arch, Kernel 5.8 bis 6.5.
Merksatz: Ein kompaktes Metadatenformat im Linux-Kernel, das Typinformationen (ähnlich Debug-Symbolen) speichert, um eBPF-Programmen zu ermöglichen, portabel über verschiedene Kernel-Versionen hinweg zu laufen (CO-RE).
Als Anwender von Tools wie Cilium, Falco oder BCC merkst du es:
Früher: Tool startet -> "Downloading Kernel Headers..." -> "Compiling..." -> (Wartet 2 Minuten) -> Läuft.
Heute (mit BTF): Tool startet -> Läuft. (Sofort).
Der Kernel muss mit CONFIG_DEBUG_INFO_BTF=y kompiliert sein (Standard bei allen modernen Distros).
1. Deduplizierung
Debugging-Infos (DWARF) sind riesig (hunderte MB).
BTF ist extrem schlau komprimiert und dedupliziert.
Die Typ-Infos für den ganzen Kernel sind nur ca. 2-4 MB groß.
Deshalb kann der Kernel sie immer im RAM halten (/sys/kernel/btf/vmlinux).
2. Relocations
Der libbpf-Loader schaut sich deinen Code an: task->pid.
Er sieht im BTF: "Ah, auf diesem System ist pid bei Offset 8."
Er patcht deinen Bytecode im Speicher um, bevor er ihn in den Kernel lädt.
1. BTF Internals: Strings & IDs
BTF speichert Informationen nicht als rohen Text (der viel Platz wegnimmt), sondern nutzt eine String-Tabelle und Typreferenzen. Ein Typ wie int hat eine ID. Eine Struktur hat eine Liste von IDs ihrer Member.
Wenn ein eBPF-Programm geladen wird, vergleicht der Kernel die IDs des Programms mit seinen eigenen. Da die Namen der Felder in der String-Tabelle liegen, kann der Kernel "fuzzy matching" betreiben. Selbst wenn sich die Position eines Feldes im Kernel-Sourcecode geändert hat, findet der BTF-Loader das Feld anhand seines Namens und korrigiert die Offsets im Binärcode "on the fly".
2. BTF-enabled Debugging (vmlinux.h)
Ein großer Durchbruch für Entwickler ist die Datei vmlinux.h.
Früher mussten eBPF-Programmierer dutzende Header inkludieren (#include <linux/sched.h>, etc.), was oft zu Konflikten führte.
Heute nutzt man das Tool bpftool:
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
Dies generiert ein einziges, riesiges C-Header-File, das alle Strukturen des aktuell laufenden Kernels enthält. Man inkludiert nur noch diese eine Datei. Das macht die Entwicklung von systemnahen Tools so einfach wie noch nie.
3. BTF for Kernel Modules
Nicht nur der Haupt-Kernel (vmlinux) hat BTF. Auch dynamisch geladene Kernel-Module (z. B. Grafiktreiber oder Filesystem-Treiber) können ihre eigenen BTF-Infos mitbringen.
Der Kernel merkt sich diese Metadaten beim Laden des Moduls. Ein eBPF-Programm kann so auch in die tiefsten Innenträger eines NVIDIA-Treibers oder eines ZFS-Dateisystems schauen und dort Performance-Metriken abgreifen, ohne dass der Treiber-Code dafür speziell vorbereitet sein muss.
Quick-Check
Nur für eBPF?
Hauptsächlich. Aber es hilft auch beim "Pretty Printing" von Kernel-Strukturen in Debuggern.Was wenn Felder fehlen?
Mit CO-RE kann man prüfen:if (bpf_core_field_exists(task->new_feature)). Dein Code kann sich also dynamisch anpassen, je nachdem, ob er auf einem alten oder neuen Kernel läuft.Voraussetzung?
Clang/LLVM 10+ und ein Kernel ab ca. 5.8.