
Write your BPF programs in Go, not C. gobee transpiles a Go subset to BPF C and generates typed cilium/ebpf bindings.
Écrivez vos programmes BPF en Go, pas en C. gobee transpile un sous-ensemble strict de Go en C BPF, génère des bindings Go typés pour le côté espace utilisateur, et vérifie les charges par rapport au noyau en cours d'exécution.
L'écosystème Go dispose d'outils solides pour l'espace utilisateur BPF. Le côté noyau s'est toujours terminé par « maintenant écrivez votre programme en C. » Aya a apporté eBPF à Rust en écrivant un nouveau backend BPF dans rustc. gobee y parvient différemment : en transpilant vers C et en réutilisant le backend mature de clang.
Une tracepoint qui diffuse chaque execve vers l'espace utilisateur via un ringbuf :
| Votre entrée (Go) | Ce que gobee génère (C BPF) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src produit les deux fichiers, plus une sourcemap (events.bpf.c.map) pour que les erreurs du vérificateur correspondent aux lignes Go, et un fichier de bindings typés (bpf/events_bindings.go) pour que le pilote espace utilisateur écrive objs.Events, objs.AttachOnExec() et décode les payloads du ringbuf directement dans bpf.Event (la même structure que ci-dessus, republiée en Go) au lieu de recherches coll.Programs["..."] basées sur des chaînes.
Le C est intentionnellement lisible. Si gobee émet quelque chose d'étrange, vous pouvez le voir. Pour les tracepoints + kprobes + XDP combinés en un seul binaire, voir example/sysmon/.
Si vous êtes déjà dans un workflow C / libbpf, gobee ne cherche pas à le remplacer entièrement. Il est destiné aux cas où vous voulez le côté noyau, le côté espace utilisateur et le pipeline de construction dans un seul module Go.
Voir docs/status.md pour la matrice complète (sous-ensemble Go, instructions, expressions, chaque helper, chaque type de map, chaque directive). Aperçu rapide :
go/types sur votre entrée, de sorte que les mauvaises utilisations apparaissent à fichier:ligne:col).<Stem>_bindings.go typé à côté du .bpf.c : bpf.LoadCounter(spec), objs.PerIface.Lookup(...), objs.AttachAll(ifindex), plus vos types de structures et constantes côté noyau republiés en Go.*ebpf.VerifierError de LoadAndAssign avec des positions source Go, sans nécessiter de pipe manuel gobee diagnose.Load<Stem> pour que les anciens noyaux échouent rapidement avec « bpf program needs kernel >= 5.8, host is 5.4 ».cilium/ebpf. Les bindings générés reposent dessus.gc, le compilateur Go, n'a pas de backend BPF basé sur LLVM. En ajouter un est un projet de compilateur de plusieurs années. rustc est construit sur LLVM et c'est pourquoi Aya fonctionne. Donc gobee émet du C et réutilise le backend BPF de clang, ce qui nous donne une génération de code mature, du BTF et des relocalisations CO-RE gratuitement.
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
Vous aurez besoin de clang avec la cible BPF. Sous Linux, c'est le paquet de la distribution ; sous macOS, brew install llvm. Le transpileur lui-même est du Go pur et fonctionne partout.
yourproject/
├── bpf/ # Package Go, importable depuis n'importe où dans votre projet
│ ├── embed_amd64.go # //go:embed bin/x86/your.bpf.o
│ ├── embed_arm64.go
│ ├── your_bindings.go # généré par gobee
│ ├── bin/{x86,arm64}/your.bpf.o
│ └── src/ # pas un package Go ; clang vit ici
│ ├── your.go # //go:build ignore : source BPF
│ ├── your.bpf.c # généré
│ ├── Makefile # clang par arch
│ └── vmlinux.h # dump BTF vendored
├── main.go # importe yourproject/bpf
└── Makefile
La séparation maintient bpf/ comme un package Go propre et importable (Go rejette les fichiers .c dans les packages non-cgo). Les sources du noyau et les artefacts clang vivent un niveau plus bas dans bpf/src/.
example/helloworld/ : le compteur de paquets XDP canonique, ~40 lignes BPF, ~80 lignes espace utilisateur.example/sysmon/ : XDP, deux tracepoints et un kprobe dans un seul binaire, partageant un ringbuf pour les événements. Montre les contextes typés par appel système, les fonctions helpers définies par l'utilisateur et le raccourci AttachAll.GitHub Actions exécute quatre couches à chaque push :
go test, go vet, tests dorés du transpileur//bpf:section a au moins un exempleebpf.NewCollectionWithOptions sur chaque .bpf.o (runner Ubuntu 24.04, noyau 6.x)docs/design.md : architecture et justificationdocs/go-subset.md : syntaxe Go acceptée dans les fichiers source BPFdocs/directives.md : référence //bpf:*docs/status.md : matrice de support (source unique de vérité).bpf.o nécessite clang avec la cible BPF. Le clang fourni par Apple ne l'inclut pas ; sous macOS, utilisez brew install llvm ou compilez dans une VM Linux.MIT. Voir LICENSE.
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC |
|---|
| Langage côté noyau | Sous-ensemble Go | C | Rust | DSL | C |
| Intégration espace utilisateur | bindings Go typés + cilium/ebpf | bpf2go | aya-runtime | aucun | python |
| CO-RE | ✅ via clang | ✅ | ✅ via LLVM | ✅ | ✅ |
| Couverture des helpers | 200 wrappers Go typés | complet (écrire en C) | complet | limité | complet (écrire en C) |
| Erreur du vérificateur → source | ✅ fichier Go:ligne:col | ❌ C brut | ✅ fichier Rust:ligne | ❌ | partiel |
| Vérification de version noyau au chargement | ✅ via bpfvet | manuel | manuel | n/a | runtime |
| Dépendances de la toolchain | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| Artefact généré | .bpf.o + binaire Go | .bpf.o + binaire Go | .bpf.o + binaire Rust | JIT | JIT |
| Surface | Couverture |
|---|
| Types de programmes (8) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Types de maps (19) | array, hash, lru_hash, variantes per-CPU, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, sk/task/inode storage, devmap/cpumap/xskmap |
| Helpers BPF | ~200 stubs Go typés générés automatiquement à partir des en-têtes libbpf v1.5.0. Ceux utilisés par example/helloworld/ et example/sysmon/ sont testés dans une CI sur noyau réel ; les autres ne sont pas vérifiés. Signalez un problème si un stub ne correspond pas à la signature du noyau |
| CO-RE | ✅ détection automatique. BPF_CORE_READ pour les champs internes du noyau (task_struct, sock, inode) ; ctx->field direct pour les structures de contexte BPF de l'UAPI (xdp_md, __sk_buff, bpf_sock_ops). Testé sur Linux 6.x (CI Ubuntu 24.04) ; les noyaux plus anciens ne sont pas encore dans la matrice CI |
| Sortie compatible BTF | ✅ Le C généré inclut vmlinux.h et utilise BPF_CORE_READ pour les lectures de champs internes du noyau, de sorte que le BTF que clang génère à partir de clang -g porte les bonnes relocalisations. clang lui-même reste votre responsabilité (les Makefiles d'exemple montrent l'invocation canonique) |
| Helpers définis par l'utilisateur | ✅ Les fonctions Go de premier niveau sans //bpf:section sont émises comme des fonctions static __always_inline en C |
| Bindings Go typés | ✅ Load<Stem>, Close, Attach<Name> par programme, AttachAll, plus vos types de structures et constantes côté noyau republiés en Go |
| Vérification de version noyau | ✅ bpfvet s'exécute au chargement. Échec rapide avec « bpf program needs kernel >= 5.8, host is 5.4 » au lieu d'un EINVAL opaque |
| Erreur du vérificateur → source Go | ✅ annoté automatiquement dans Load<Stem>. Pas de pipe manuel vers gobee diagnose ; *ebpf.VerifierError revient avec des marqueurs → counter.go:18:5 |
| Fichier sourcemap | ✅ <stem>.bpf.c.map écrit à côté de chaque .bpf.c pour une utilisation hors ligne de gobee diagnose |
| Multi-architecture | ✅ Linux arm64 + amd64 |
static __always_inline