
BPFプログラムをCではなくGoで書きましょう。 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 は両方のファイルに加えて、verifierエラーをGoの行にマッピングするためのソースマップ(events.bpf.c.map)と、型付きバインディングファイル(bpf/events_bindings.go)を生成します。これによりユーザー空間ドライバは、文字列で型付けされたcoll.Programs["..."]ルックアップの代わりに、objs.Events、objs.AttachOnExec()と書き、ringbufペイロードを直接bpf.Event(上で見たのと同じ構造体がGoでも再公開されたもの)にデコードできます。
Cコードは意図的に読みやすくしています。gobeeが変なものを出力したら、それを目で確認できます。tracepoint + kprobe + XDPを1つのバイナリにまとめる方法は、example/sysmon/を参照してください。
| 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で記述) |
| Verifierエラー → ソース | ✅ Go file:line:col | ❌ 生のC | ✅ Rust file:line | ❌ | 部分的 |
| ロード時のカーネルバージョンゲート | ✅ bpfvet経由 | 手動 | 手動 | 該当なし | ランタイム |
| ツールチェーン依存 | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| 生成成果物 | .bpf.o + Goバイナリ | .bpf.o + Goバイナリ | .bpf.o + Rustバイナリ | JIT | JIT |
すでにC / libbpfのワークフローを使っている場合、gobeeはそれを丸ごと置き換えようとするものではありません。カーネル側、ユーザー空間側、ビルドパイプラインをすべて1つのGoモジュールにまとめたい場合のためのものです。
完全なマトリクス(Goサブセット、文、式、すべてのヘルパー、すべてのマップ型、すべてのディレクティブ)はdocs/status.mdを参照してください。早見:
| 対象 | 対応状況 |
|---|---|
| プログラムタイプ (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 ストレージ、devmap/cpumap/xskmap |
| BPFヘルパー | libbpf v1.5.0ヘッダーから自動生成された約200の型付きGoスタブ。example/helloworld/とexample/sysmon/で使用されているものは実カーネルCIでテスト済みですが、残りは未検証です。スタブがカーネルのシグネチャと一致しない場合はissueを報告してください |
| CO-RE | ✅ 自動検出。カーネル内部の構造体フィールドにはBPF_CORE_READ(task_struct、sock、inode)。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 -gからclangが生成するBTFには正しいリロケーションが含まれます。clang自体の実行はあなたの責任です(サンプルのMakefileに標準的な呼び出し方が示されています) |
| ユーザー定義ヘルパー | ✅ //bpf:sectionを持たないトップレベルGo関数は、static __always_inline C関数として出力されます |
| 型付きGoバインディング | ✅ Load<Stem>、Close、プログラムごとのAttach<Name>、AttachAllに加え、カーネル側の構造体型と定数がGoで再公開されます |
| カーネルバージョンゲート | ✅ ロード時にbpfvetが実行されます。不透明なEINVALの代わりに、bpf program needs kernel >= 5.8, host is 5.4というメッセージで即座に失敗します |
| Verifierエラー → Goソース | ✅ Load<Stem>内で自動的に注釈付けされます。gobee diagnoseへの手動パイプは不要で、にマーカーが付いて返ってきます |
go/typesを実行するため、誤用はfile:line:colで表面化します)。.bpf.cの隣に型付き<Stem>_bindings.goを生成します: 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を置き換えること。 生成されるバインディングはその上に成り立っています。gc(Goコンパイラ)には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 package, importable from anywhere in your project
│ ├── embed_amd64.go # //go:embed bin/x86/your.bpf.o
│ ├── embed_arm64.go
│ ├── your_bindings.go # generated by gobee
│ ├── bin/{x86,arm64}/your.bpf.o
│ └── src/ # not a Go package; clang lives here
│ ├── your.go # //go:build ignore: BPF source
│ ├── your.bpf.c # generated
│ ├── Makefile # clang per arch
│ └── vmlinux.h # vendored BTF dump
├── main.go # imports yourproject/bpf
└── Makefile
この分割により、bpf/はクリーンでインポート可能なGoパッケージとして保たれます(Goは非cgoパッケージ内の.cファイルを拒否します)。カーネルソースとclangの生成物は、1階層下のbpf/src/に置かれます。
example/helloworld/: 標準的なXDPパケットカウンタです。BPFは約40行、ユーザー空間は約80行です。example/sysmon/: XDP、2つのtracepoint、1つのkprobeを1つのバイナリにまとめ、イベント用のringbufを共有します。システムコールごとの型付きコンテキスト、ユーザー定義ヘルパー関数、AttachAllショートカットを示しています。GitHub Actionsはプッシュのたびに4つのレイヤーを実行します:
go test、go vet、トランスパイラのゴールデンテスト//bpf:sectionの種類について、少なくとも1つの例があることbpfvet移植性レポート.bpf.oに対するebpf.NewCollectionWithOptions(Ubuntu 24.04ランナー、カーネル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 VM内でビルドしてください。MIT。 LICENSEを参照してください。
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
*ebpf.VerifierError→ counter.go:18:5| ソースマップサイドカー | ✅ すべての.bpf.cの横に<stem>.bpf.c.mapが書き出されます。オフラインのgobee diagnose利用にも対応します |
| クロスアーキテクチャ | ✅ Linux arm64 + amd64 |