
Escreva seus programas BPF em Go, não em C. gobee transpila um subconjunto de Go para BPF C e gera bindings tipados para cilium/ebpf.
Escreva seus programas BPF em Go, não em C. gobee transpila um subconjunto restrito de Go para C BPF, gera bindings Go tipados para o lado do usuário e verifica a compatibilidade com o kernel em execução.
O ecossistema Go possui ferramentas sólidas para o lado do usuário em BPF. O lado do kernel sempre terminava com "agora escreva seu programa em C." Aya trouxe eBPF para Rust ao escrever um novo backend BPF no rustc. gobee chega lá de outra forma: transpilando para C e reutilizando o backend maduro do clang.
Um tracepoint que transmite cada execve para o espaço do usuário via um ringbuf:
| Sua entrada (Go) | O que o gobee gera (BPF C) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src produz ambos os arquivos, além de um mapa de fontes (events.bpf.c.map) para que erros do verificador apontem para as linhas Go, e um arquivo de bindings tipados (bpf/events_bindings.go) para que o driver do espaço do usuário escreva objs.Events, objs.AttachOnExec() e decodifique payloads do ringbuf diretamente em bpf.Event (a mesma struct que você vê acima, republicada em Go) em vez de buscas do tipo coll.Programs["..."].
O C é legível propositalmente. Se o gobee gerar algo estranho, você pode ver. Para tracepoints + kprobes + XDP combinados em um único binário, veja example/sysmon/.
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC | |
|---|---|---|---|---|---|
| Linguagem do lado do kernel | Subconjunto Go | C | Rust | DSL | C |
| Integração com o lado do usuário | bindings Go tipados + cilium/ebpf | bpf2go | aya-runtime | nenhum | python |
| CO-RE | ✅ via clang | ✅ | ✅ via LLVM | ✅ | ✅ |
| Cobertura de helpers | 200 wrappers Go tipados | completo (escreva em C) | completo | limitado | completo (escreva em C) |
| Erro do verificador → código fonte | ✅ arquivo Go:linha:col | ❌ C bruto | ✅ arquivo Rust:linha | ❌ | parcial |
| Verificação de versão do kernel no carregamento | ✅ via bpfvet | manual | manual | n/a | runtime |
| Dependências da toolchain | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| Artefato gerado | .bpf.o + binário Go | .bpf.o + binário Go | .bpf.o + binário Rust | JIT | JIT |
Se você já está em um fluxo de trabalho C / libbpf, gobee não pretende substituí-lo completamente. É para casos onde você quer o lado do kernel, o lado do usuário e o pipeline de build, tudo em um único módulo Go.
Veja docs/status.md para a matriz completa (subconjunto Go, instruções, expressões, todos os helpers, todos os tipos de mapa, todas as diretivas). Visão rápida:
| Superfície | Cobertura |
|---|---|
| Tipos de programa (8) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Tipos de mapa (19) | array, hash, lru_hash, variantes por 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 tipados gerados automaticamente a partir dos cabeçalhos libbpf v1.5.0. Os exercitados por example/helloworld/ e example/sysmon/ são testados em CI com kernel real; o restante não foi verificado. Abra uma issue se um stub não corresponder à assinatura do kernel |
| CO-RE | ✅ auto-detectado. BPF_CORE_READ para campos de struct internos do kernel (task_struct, sock, inode); acesso direto ctx->field para structs de contexto BPF da UAPI (xdp_md, __sk_buff, bpf_sock_ops). Testado no Linux 6.x (CI Ubuntu 24.04); kernels mais antigos ainda não estão na matriz de CI |
| Saída pronta para BTF | ✅ o C gerado inclui vmlinux.h e usa BPF_CORE_READ para leituras de campos internos do kernel, então o BTF que o clang gera a partir de clang -g carrega as realocações corretas. O clang continua sendo sua responsabilidade (os Makefiles de exemplo mostram a invocação canônica) |
| Helpers definidos pelo usuário | ✅ funções Go de nível superior sem //bpf:section são geradas como funções C static __always_inline |
| Bindings Go tipados | ✅ Load<Stem>, Close, por programa Attach<Name>, AttachAll, além dos tipos de struct e constantes do lado do kernel republicados em Go |
| Verificação de versão do kernel |
go/types sobre sua entrada primeiro, para que usos incorretos sejam apontados em arquivo:linha:col).<Stem>_bindings.go tipado ao lado do .bpf.c: bpf.LoadCounter(spec), objs.PerIface.Lookup(...), objs.AttachAll(ifindex), além dos tipos de struct e constantes do lado do kernel republicados em Go.*ebpf.VerifierError de LoadAndAssign com posições do código Go, sem necessidade de pipe manual gobee diagnose.Load<Stem> para que kernels antigos falhem rapidamente com bpf program needs kernel >= 5.8, host is 5.4.static __always_inline.cilium/ebpf. Os bindings gerados ficam sobre ele.gc, o compilador Go, não tem um backend BPF baseado em LLVM. Adicionar um é um projeto de compilador de vários anos. rustc é construído sobre LLVM e é por isso que Aya funciona. Então gobee gera C e reutiliza o backend BPF do clang, o que nos dá geração de código madura, BTF e realocações CO-RE de graça.
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
Você precisará de clang com o alvo BPF. No Linux, é o pacote da distribuição; no macOS, brew install llvm. O transpilador em si é Go puro e funciona em qualquer lugar.
seuprojeto/
├── bpf/ # Pacote Go, importável de qualquer lugar do seu projeto
│ ├── embed_amd64.go # //go:embed bin/x86/seu.bpf.o
│ ├── embed_arm64.go
│ ├── seu_bindings.go # gerado por gobee
│ ├── bin/{x86,arm64}/seu.bpf.o
│ └── src/ # não é um pacote Go; clang fica aqui
│ ├── seu.go # //go:build ignore: código-fonte BPF
│ ├── seu.bpf.c # gerado
│ ├── Makefile # clang por arquitetura
│ └── vmlinux.h # dump BTF fornecido
├── main.go # importa seuprojeto/bpf
└── Makefile
A separação mantém bpf/ como um pacote Go importável limpo (Go rejeita arquivos .c em pacotes sem cgo). Códigos-fonte do kernel e artefatos do clang ficam um nível abaixo em bpf/src/.
example/helloworld/: o contador de pacotes XDP canônico, ~40 linhas BPF, ~80 linhas espaço do usuário.example/sysmon/: XDP, dois tracepoints e um kprobe em um binário, compartilhando um ringbuf para eventos. Demonstra contextos tipados por syscall, funções helper definidas pelo usuário e o atalho AttachAll.GitHub Actions executa quatro camadas a cada push:
go test, go vet, testes golden do transpilador//bpf:section tem pelo menos um exemplobpfvetebpf.NewCollectionWithOptions em cada .bpf.o (executor Ubuntu 24.04, kernel 6.x)docs/design.md: arquitetura e justificativasdocs/go-subset.md: sintaxe Go aceita em arquivos-fonte BPFdocs/directives.md: referência //bpf:*docs/status.md: matriz de suporte (fonte única da verdade).bpf.o precisa de clang com o alvo BPF. O clang embutido da Apple não vem com ele; no macOS use brew install llvm ou compile dentro de uma VM Linux.MIT. Veja LICENSE.
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
✅ bpfvet executa no momento do carregamento. Falha rapidamente com bpf program needs kernel >= 5.8, host is 5.4 em vez de EINVAL opaco |
| Erro do verificador → código Go | ✅ anotado automaticamente dentro de Load<Stem>. Sem necessidade de pipe manual para gobee diagnose; *ebpf.VerifierError retorna com marcadores → counter.go:18:5 |
| Arquivo de mapa de fontes | ✅ <stem>.bpf.c.map é escrito ao lado de cada .bpf.c para uso offline com gobee diagnose também |
| Multi-arquitetura | ✅ Linux arm64 + amd64 |