用Go编写BPF程序,而不是C。 gobee将Go的一个严格子集转译成BPF C,为用户空间生成类型化的Go绑定,并根据正在运行的内核进行加载校验。
Go生态系统拥有可靠的BPF用户空间工具。而内核端总是以"现在用C编写你的程序"结束。Aya通过在rustc中编写一个新的BPF后端,将eBPF带到了Rust。gobee以另一种方式实现:通过转译成C并复用clang成熟的后端。
一个通过ringbuf将每次execve流式传输到用户空间的tracepoint:
| 你的输入(Go) | gobee生成的(BPF C) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src 生成这两个文件,外加一个源代码映射(events.bpf.c.map),使得验证器错误能映射回Go代码行,以及一个类型化的绑定文件(bpf/events_bindings.go),这样用户空间驱动可以写 objs.Events、objs.AttachOnExec(),并直接从ringbuf负载解码为 bpf.Event(与你上面看到的相同结构体,重新发布到Go中),而不是字符串类型的coll.Programs["..."] 查找。
C代码是有意保持可读的。如果gobee生成了奇怪的东西,你可以看到它。关于将tracepoints、kprobes和XDP组合成一个二进制文件的示例,请参见 example/sysmon/。
如果你已经在使用C / libbpf工作流,gobee并不是要完全取代它。它适用于那些希望内核端、用户空间端和构建流水线都放在一个Go模块中的场景。
查看 docs/status.md 了解完整矩阵(Go子集、语句、表达式、每个辅助函数、每个映射类型、每个指令)。快速概览:
go/types检查,这样误用在file:line:col处就会暴露)。<Stem>_bindings.go,放在.bpf.c旁边:bpf.LoadCounter(spec)、objs.PerIface.Lookup(...)、objs.AttachAll(ifindex),以及你的内核端结构体类型和常量重新发布到Go中。LoadAndAssign的*ebpf.VerifierError,包含Go源代码位置,无需手动gobee diagnose管道。Load<Stem>内部运行bpfvet,使旧内核以bpf program needs kernel >= 5.8, host is 5.4快速失败。static __always_inline)。cilium/ebpf。 生成的绑定构建在其之上。Go编译器gc没有基于LLVM的BPF后端。添加一个是一个多年的编译器项目。rustc构建在LLVM之上,这就是Aya能工作的原因。因此gobee生成C并复用clang的BPF后端,这为我们免费提供了成熟的代码生成、BTF和CO-RE重定位。
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
你需要具有BPF目标的clang。在Linux上,这是发行版包;在macOS上,brew install llvm。转译器本身是纯Go的,可以运行在任何地方。
yourproject/
├── bpf/ # Go包,可从项目任何位置导入
│ ├── embed_amd64.go # //go:embed bin/x86/your.bpf.o
│ ├── embed_arm64.go
│ ├── your_bindings.go # 由gobee生成
│ ├── bin/{x86,arm64}/your.bpf.o
│ └── src/ # 不是Go包;clang在这里
│ ├── your.go # //go:build ignore: BPF源代码
│ ├── your.bpf.c # 生成的文件
│ ├── Makefile # 按架构的clang命令
│ └── vmlinux.h # 供应商BTF转储
├── main.go # 导入 yourproject/bpf
└── Makefile
这种分离使bpf/成为一个干净的、可导入的Go包(Go拒绝在非cgo包中的.c文件)。内核源代码和clang产物位于bpf/src/下一层。
example/helloworld/:经典的XDP数据包计数器,约40行BPF,约80行用户空间。example/sysmon/:XDP、两个tracepoint和一个kprobe在同一个二进制文件中,共享一个ringbuf用于事件。演示了每个系统调用的类型化上下文、用户定义的辅助函数以及AttachAll快捷方式。GitHub Actions 在每次推送时运行四层:
go test、go vet、转译器快照测试//bpf:section种类至少有一个示例.bpf.o上运行ebpf.NewCollectionWithOptions(Ubuntu 24.04 runner,内核6.x)docs/design.md:架构和原理docs/go-subset.md:BPF源文件中接受的Go语法docs/directives.md://bpf:*参考docs/status.md:支持矩阵(唯一真实信息源).bpf.o需要具有BPF目标的clang。Apple自带的clang不包含该目标;在macOS上使用brew install llvm或在Linux虚拟机内构建。MIT。参见 LICENSE。
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC |
|---|
| 内核端语言 | Go子集 | C | Rust | DSL | C |
| 用户空间集成 | 类型化Go绑定 + cilium/ebpf | bpf2go | aya-runtime | 无 | python |
| CO-RE | ✅ 通过clang | ✅ | ✅ 通过LLVM | ✅ | ✅ |
| 辅助函数覆盖 | 200个类型化Go包装器 | 完整(写C) | 完整 | 有限 | 完整(写C) |
| 验证器错误 → 源 | ✅ Go文件:行:列 | ❌ 原始C | ✅ Rust文件:行 | ❌ | 部分 |
| 加载时内核版本门控 | ✅ 通过bpfvet | 手动 | 手动 | 不适用 | 运行时 |
| 工具链依赖 | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| 生成产物 | .bpf.o + Go二进制 | .bpf.o + Go二进制 | .bpf.o + Rust二进制 | JIT | JIT |
| 功能面 | 覆盖情况 |
|---|
| 程序类型(8种) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| 映射类型(19种) | array, hash, lru_hash, per-CPU变体, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, sk/task/inode storage, devmap/cpumap/xskmap |
| BPF辅助函数 | 从libbpf v1.5.0头文件自动生成约200个类型化Go桩。由example/helloworld/和example/sysmon/练习的桩已在真实内核CI中测试;其余未验证。如果某个桩与内核签名不匹配,请提交issue |
| CO-RE | ✅ 自动检测。对内核内部结构体字段(task_struct, sock, inode)使用BPF_CORE_READ;对UAPI BPF上下文结构体(xdp_md, __sk_buff, bpf_sock_ops)使用直接的ctx->field。在Linux 6.x(Ubuntu 24.04 CI)上经过测试;较老的内核尚未纳入CI矩阵 |
| BTF就绪输出 | ✅ 生成的C包含vmlinux.h,并对内核内部字段读取使用BPF_CORE_READ,因此clang通过clang -g生成的BTF携带正确的重定位。clang本身仍由你负责(示例Makefile展示了规范调用) |
| 用户定义辅助函数 | ✅ 没有//bpf:section的顶层Go函数被生成为static __always_inline C函数 |
| 类型化Go绑定 | ✅ Load<Stem>, Close, 每个程序的 Attach<Name>, AttachAll,以及你的内核端结构体类型和常量重新发布到Go中 |
| 内核版本门控 | ✅ bpfvet 在加载时运行。以明确的bpf program needs kernel >= 5.8, host is 5.4快速失败,而不是模糊的EINVAL |
| 验证器错误 → Go源代码 | ✅ 自动注释在Load<Stem>内部。无需手动管道到gobee diagnose;*ebpf.VerifierError返回带有→ counter.go:18:5标记的错误 |
| 源代码映射侧车 | ✅ 每个.bpf.c旁边生成<stem>.bpf.c.map,也用于离线gobee diagnose |
| 跨架构 | ✅ Linux arm64 + amd64 |