
Пишите свои BPF-программы на Go, а не на C. gobee транспилирует подмножество Go в BPF C и генерирует типизированные привязки cilium/ebpf.
Пишите свои BPF-программы на Go, а не на C. gobee транслирует строгое подмножество Go в BPF C, генерирует типизированные Go-привязки для пользовательской стороны и проверяет загрузку на соответствие версии работающего ядра.
Экосистема Go имеет надежные пользовательские инструменты для BPF. Но сторона ядра всегда заканчивалась фразой «теперь напишите свою программу на C». Aya привнесла eBPF в Rust, написав новый бэкенд BPF в rustc. gobee добивается этого иначе: транслируя в C и используя зрелый бэкенд clang.
Точка трассировки, которая отправляет каждый execve в пользовательское пространство через ringbuf:
| Ваш вход (Go) | Что выдает gobee (BPF C) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src создает оба файла, а также sourcemap (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/.
| 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 |
Если вы уже используете рабочий процесс C / libbpf, gobee не пытается полностью его заменить. Он предназначен для случаев, когда вы хотите иметь сторону ядра, пользовательскую сторону и конвейер сборки в одном Go-модуле.
См. docs/status.md для полной матрицы (подмножество Go, операторы, выражения, каждый хелпер, каждый тип карты, каждая директива). Краткий обзор:
| Поверхность | Покрытие |
|---|---|
| Типы программ (8) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Типы карт (19) | array, hash, lru_hash, варианты на CPU, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, sk/task/inode storage, devmap/cpumap/xskmap |
| Хелперы BPF | ~200 типизированных Go-заглушек, автоматически сгенерированных из заголовков libbpf v1.5.0. Те, что используются в example/helloworld/ и example/sysmon/, протестированы в CI на реальном ядре; остальные непроверены. Создайте issue, если заглушка не соответствует сигнатуре ядра |
| CO-RE | ✅ автоопределение. BPF_CORE_READ для полей внутренних структур ядра (task_struct, sock, inode); прямой ctx->field для UAPI-структур контекста BPF (xdp_md, __sk_buff, bpf_sock_ops). Протестировано на Linux 6.x (Ubuntu 24.04 CI); старые ядра пока не в матрице CI |
| BTF-готовый вывод | ✅ генерируемый C включает vmlinux.h и использует BPF_CORE_READ для чтения полей внутри ядра, так что BTF, создаваемый clang из clang -g, содержит правильные релокации. Сам clang остаётся вашей ответственностью (примеры Makefile показывают канонический вызов) |
| Пользовательские хелперы | ✅ функции Go верхнего уровня без //bpf:section генерируются как static __always_inline C-функции |
| Типизированные Go-привязки | ✅ Load<Stem>, Close, per-program Attach<Name>, AttachAll, а также типы структур и константы стороны ядра, переизданные в Go |
| Шлюзование по версии ядра | ✅ bpfvet выполняется во время загрузки. Быстро завершается с ошибкой вместо непонятного |
go/types над вашим входом, чтобы ошибки использования отображались с file:line:col).<Stem>_bindings.go рядом с .bpf.c: bpf.LoadCounter(spec), objs.PerIface.Lookup(...), objs.AttachAll(ifindex), а также типы структур и константы стороны ядра, переизданные в Go.*ebpf.VerifierError от LoadAndAssign с позициями в Go-исходнике; ручной вызов gobee diagnose не требуется.Load<Stem>, так что старые ядра быстро завершаются с bpf program needs kernel >= 5.8, host is 5.4.static __always_inline.cilium/ebpf. Сгенерированные привязки работают поверх него.gc, компилятор Go, не имеет бэкенда BPF на основе LLVM. Добавление такого бэкенда — многолетний проект компилятора. rustc построен на LLVM, поэтому Aya работает. Итак, gobee генерирует C и использует бэкенд BPF от clang, что даёт нам зрелую генерацию кода, 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
Вам понадобится clang с поддержкой BPF. В 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 отвергает .c-файлы в пакетах без cgo). Исходники для ядра и артефакты clang находятся уровнем ниже в bpf/src/.
example/helloworld/: канонический XDP-счётчик пакетов, ~40 строк BPF, ~80 строк пользовательского кода.example/sysmon/: XDP, две точки трассировки и один kprobe в одном бинарнике, совместно использующие ringbuf для событий. Демонстрирует типизированные контексты для каждого системного вызова, пользовательские функции-хелперы и сокращение AttachAll.GitHub Actions запускает четыре слоя при каждом push:
go test, go vet, золотые тесты трансплятора//bpf:section имеет хотя бы один примерebpf.NewCollectionWithOptions для каждого .bpf.o (раннер Ubuntu 24.04, ядро 6.x)docs/design.md: архитектура и обоснованиеdocs/go-subset.md: принимаемый синтаксис Go в BPF-исходникахdocs/directives.md: справка по //bpf:*docs/status.md: матрица поддержки (единый источник истины).bpf.o требуется clang с поддержкой BPF. Встроенный в macOS clang не содержит этой поддержки; на macOS используйте brew install llvm или собирайте внутри Linux-виртуалки.MIT. См. LICENSE.
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
bpf program needs kernel >= 5.8, host is 5.4EINVAL| Ошибка верификатора → Go-исходник | ✅ автоматически аннотируется внутри Load<Stem>. Без ручного вызова gobee diagnose; *ebpf.VerifierError возвращается с маркерами → counter.go:18:5 |
| Файл sourcemap | ✅ <stem>.bpf.c.map записывается рядом с каждым .bpf.c для офлайн-использования также через gobee diagnose |
| Кроссплатформенность | ✅ Linux arm64 + amd64 |