
Scrivi i tuoi programmi BPF in Go, non in C. gobee transpila un sottoinsieme di Go in BPF C e genera binding cilium/ebpf tipizzati.
Scrivi i tuoi programmi BPF in Go, non in C. gobee transpila un sottoinsieme rigoroso di Go in BPF C, genera binding Go tipizzati per il lato userspace e controlla i load rispetto al kernel in esecuzione.
L'ecosistema Go ha strumenti userspace solidi per BPF. Il lato kernel si è sempre concluso con "ora scrivi il tuo programma in C." Aya ha portato eBPF su Rust scrivendo un nuovo backend BPF in rustc. gobee ci arriva in un altro modo: transpilando in C e riusando il backend maturo di clang.
Un tracepoint che invia in streaming ogni execve allo userspace tramite un ringbuf:
| Il tuo input (Go) | Ciò che gobee genera (BPF C) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src produce entrambi i file, più una sourcemap (events.bpf.c.map) così gli errori del verifier vengono mappati sulle righe Go e un file di binding tipizzati (bpf/events_bindings.go) così il driver userspace scrive objs.Events, objs.AttachOnExec() e decodifica i payload del ringbuf direttamente in bpf.Event (la stessa struct che vedi sopra, ripubblicata in Go) invece di lookup coll.Programs["..."] tipizzati come stringhe.
Il C è leggibile di proposito. Se gobee emette qualcosa di strano, puoi vederlo. Per tracepoint + kprobe + XDP combinati in un unico binario, vedi example/sysmon/.
Se sei già in un workflow C / libbpf, gobee non cerca di sostituirlo del tutto. È pensato per i casi in cui vuoi il lato kernel, il lato userspace e la pipeline di build tutti in un unico modulo Go.
Vedi docs/status.md per la matrice completa (sottoinsieme di Go, statement, espressioni, ogni helper, ogni tipo di mappa, ogni direttiva). Vista rapida:
go/types sul tuo input prima, così gli usi errati emergono a file:line:col).<Stem>_bindings.go tipizzato accanto al .bpf.c: bpf.LoadCounter(spec), objs.PerIface.Lookup(...), objs.AttachAll(ifindex), più i tuoi tipi di struct lato kernel e le costanti ripubblicati in Go.*ebpf.VerifierError di LoadAndAssign con posizioni nel sorgente Go, senza bisogno di pipe manuale verso gobee diagnose.Load<Stem> così i kernel vecchi falliscono rapidamente con bpf program needs kernel >= 5.8, host is 5.4.cilium/ebpf. I binding generati si appoggiano su di esso.gc, il compilatore Go, non ha un backend BPF basato su LLVM. Aggiungerne uno è un progetto di compilatore pluriennale. rustc è costruito su LLVM ed è per questo che Aya funziona. Quindi gobee emette C e riusa il backend BPF di clang, che ci dà gratuitamente codegen maturo, BTF e rilocazioni CO-RE.
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
Ti servirà clang con il target BPF. Su Linux è il pacchetto della distribuzione; su macOS, brew install llvm. Il transpiler stesso è Go puro e gira ovunque.
yourproject/
├── bpf/ # pacchetto Go, importabile da qualsiasi punto del progetto
│ ├── embed_amd64.go # //go:embed bin/x86/your.bpf.o
│ ├── embed_arm64.go
│ ├── your_bindings.go # generato da gobee
│ ├── bin/{x86,arm64}/your.bpf.o
│ └── src/ # non è un pacchetto Go; qui vive clang
│ ├── your.go # //go:build ignore: sorgente BPF
│ ├── your.bpf.c # generato
│ ├── Makefile # clang per architettura
│ └── vmlinux.h # dump BTF venduto
├── main.go # importa yourproject/bpf
└── Makefile
La separazione mantiene bpf/ un pacchetto Go pulito e importabile (Go rifiuta i file .c nei pacchetti non-cgo). I sorgenti kernel e gli artefatti di clang vivono un livello più in basso in bpf/src/.
example/helloworld/: il canonico contatore di pacchetti XDP, ~40 righe di BPF, ~80 righe di userspace.example/sysmon/: XDP, due tracepoint e un kprobe in un unico binario, che condividono un ringbuf per gli eventi. Dimostra contesti tipizzati per syscall, funzioni helper definite dall'utente e la scorciatoia AttachAll.GitHub Actions esegue quattro livelli a ogni push:
go test, go vet, golden test del transpiler//bpf:section ha almeno un esempiobpfvetebpf.NewCollectionWithOptions su ogni .bpf.o (runner Ubuntu 24.04, kernel 6.x)docs/design.md: architettura e motivazionidocs/go-subset.md: sintassi Go accettata nei file sorgente BPFdocs/directives.md: riferimento //bpf:*docs/status.md: matrice di supporto (unica fonte di verità).bpf.o richiede clang con il target BPF. Il clang incluso da Apple non lo include; su macOS usa brew install llvm o compila dentro una VM Linux.MIT. Vedi LICENSE.
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC |
|---|
| Linguaggio lato kernel | sottoinsieme di Go | C | Rust | DSL | C |
| Integrazione userspace | binding Go tipizzati + cilium/ebpf | bpf2go | aya-runtime | nessuno | python |
| CO-RE | ✅ via clang | ✅ | ✅ via LLVM | ✅ | ✅ |
| Copertura helper | 200 wrapper Go tipizzati | completo (scrivi C) | completo | limitato | completo (scrivi C) |
| Errore del verifier → sorgente | ✅ Go file:line:col | ❌ C grezzo | ✅ Rust file:line | ❌ | parziale |
| Controllo versione kernel al load | ✅ via bpfvet | manuale | manuale | n/a | runtime |
| Dipendenze toolchain | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| Artefatto generato | .bpf.o + binario Go | .bpf.o + binario Go | .bpf.o + binario Rust | JIT | JIT |
| Ambito | Copertura |
|---|
| Tipi di programma (8) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Tipi di mappa (19) | array, hash, lru_hash, varianti per-CPU, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, storage sk/task/inode, devmap/cpumap/xskmap |
| Helper BPF | ~200 stub Go tipizzati auto-generati dagli header libbpf v1.5.0. Quelli esercitati da example/helloworld/ e example/sysmon/ sono testati in CI su kernel reale; il resto non è verificato. Apri una issue se uno stub non corrisponde alla firma del kernel |
| CO-RE | ✅ rilevato automaticamente. BPF_CORE_READ per i campi delle struct interne al kernel (task_struct, sock, inode); ctx->field diretto per le struct di contesto BPF UAPI (xdp_md, __sk_buff, bpf_sock_ops). Testato su Linux 6.x (CI Ubuntu 24.04); i kernel meno recenti non sono ancora nella matrice CI |
| Output pronto per BTF | ✅ il C emesso include vmlinux.h e usa BPF_CORE_READ per le letture dei campi interni al kernel, così il BTF che clang genera da clang -g porta le giuste rilocazioni. clang stesso resta una tua responsabilità (i Makefile di esempio mostrano l'invocazione canonica) |
| Helper definiti dall'utente | ✅ le func Go di primo livello senza //bpf:section vengono emesse come funzioni C static __always_inline |
| Binding Go tipizzati | ✅ Load<Stem>, Close, Attach<Name> per programma, AttachAll, più i tuoi tipi di struct lato kernel e le costanti ripubblicati in Go |
| Controllo versione kernel | ✅ bpfvet viene eseguito al momento del load. Fallisce rapidamente con bpf program needs kernel >= 5.8, host is 5.4 invece dell'opaco EINVAL |
| Errore del verifier → sorgente Go | ✅ annotato automaticamente dentro Load<Stem>. Nessun pipe manuale verso gobee diagnose; *ebpf.VerifierError ritorna con marcatori → counter.go:18:5 |
| Sourcemap sidecar | ✅ <stem>.bpf.c.map scritto accanto a ogni .bpf.c anche per l'uso offline di gobee diagnose |
| Cross-arch | ✅ Linux arm64 + amd64 |
static __always_inline.