Write eBPF programs in Go. Compile with TinyGo. Run in the Linux kernel.
eBPF lets sandboxed programs run inside the Linux kernel. Today those programs must be written in C or Rust. tinybpf lets you write them in Go instead.
graph LR
A["Go source"] --> B["TinyGo"]
B --> C["LLVM IR"]
C --> D["tinybpf"]
D --> E["bpf.o"]
The output is a standard BPF ELF object compatible with cilium/ebpf, libbpf, and bpftool.
Standard Go compiles to native machine code via its own backend — there is no LLVM IR to retarget to BPF, and its runtime (GC, goroutines, channels) cannot run in the kernel. TinyGo compiles through LLVM and supports bare-metal mode (-gc=none -scheduler=none -panic=trap) with no runtime, producing IR that tinybpf transforms into verifier-friendly BPF bytecode.
tinybpf sits between TinyGo's LLVM IR output and the final BPF ELF. It retargets the IR to BPF, strips the TinyGo runtime, rewrites helper calls to kernel form, and injects the metadata that loaders expect. See Architecture for the full pipeline.
go install github.com/kyleseneker/tinybpf/cmd/tinybpf@latest| Dependency | Version | Required |
|---|---|---|
| Go | 1.24+ | Yes |
| TinyGo | 0.40+ | Yes |
LLVM (llvm-link, opt, llc) |
20+ (>= TinyGo's bundled LLVM) | Yes |
llvm-ar, llvm-objcopy |
20+ | For .a / .o inputs |
pahole |
For BTF injection |
make setup # install everything
make doctor # verify toolchainA tracepoint probe that captures outbound TCP connections, written entirely in Go:
//go:extern bpf_get_current_pid_tgid
func bpfGetCurrentPidTgid() uint64
//go:extern bpf_probe_read_user
func bpfProbeReadUser(dst unsafe.Pointer, size uint32, src unsafe.Pointer) int64
//go:extern bpf_ringbuf_output
func bpfRingbufOutput(mapPtr unsafe.Pointer, data unsafe.Pointer, size uint64, flags uint64) int64
//export handle_connect
func handle_connect(ctx unsafe.Pointer) int32 {
args := (*tpConnectArgs)(ctx)
var sa sockaddrIn
bpfProbeReadUser(unsafe.Pointer(&sa), uint32(unsafe.Sizeof(sa)), unsafe.Pointer(uintptr(args.Uservaddr)))
if sa.Family != afINET {
return 0
}
pid := uint32(bpfGetCurrentPidTgid() >> 32)
ev := connectEvent{PID: pid, DstAddrBE: sa.AddrBE, DstPortBE: sa.PortBE, Proto: ipProtoTCP}
bpfRingbufOutput(unsafe.Pointer(&events), unsafe.Pointer(&ev), uint64(unsafe.Sizeof(ev)), 0)
return 0
}Build:
tinybpf build --output program.o \
--section handle_connect=tracepoint/syscalls/sys_enter_connect \
./bpftracepoint-connect/— tracepoint + ring buffer +cilium/ebpfloaderxdp-filter/— XDP packet filter with hash map blocklistkprobe-openat/— kprobe tracingopenatwith ring buffertc-filter/— TC classifier that drops packets by portcgroup-connect/— cgroup/connect4 connection blockerfentry-open/— fentry tracingopenat2with ring bufferrawtp-sched/— raw tracepoint exec tracer with CO-RE portable struct access
tinybpf init xdp_filterGenerates a BPF source file, stub file for IDE compatibility, and a Makefile.
Run tinybpf --help or tinybpf <command> --help for details.
| Command | Description |
|---|---|
build |
Compile Go source to a BPF ELF object in one step |
link |
Link pre-compiled LLVM IR into a BPF ELF object |
init |
Scaffold a new BPF project |
verify |
Validate a BPF ELF object offline |
doctor |
Check toolchain installation |
version |
Print version information |
tinybpf build [flags] <package>| Flag | Default | Description |
|---|---|---|
--tinygo |
(PATH) | Path to tinygo binary |
Also accepts all shared pipeline flags and tool path overrides.
tinybpf link --input <file> [flags]| Flag | Default | Description |
|---|---|---|
--input, -i |
(required) | Input file .ll, .bc, .o, .a (repeatable) |
--config |
Path to linker-config.json for custom passes |
|
--jobs, -j |
1 |
Parallel input normalization workers |
Also accepts all shared pipeline flags and tool path overrides.
tinybpf verify --input <file>| Flag | Default | Description |
|---|---|---|
--input, -i |
(required) | Path to the BPF ELF object to validate |
tinybpf doctor [flags]| Flag | Default | Description |
|---|---|---|
--timeout |
10s |
Timeout for each version check |
Also accepts tool path overrides.
tinybpf init <name>Takes a single project name argument. No additional flags.
These flags are accepted by both build and link.
| Flag | Default | Description |
|---|---|---|
--output, -o |
bpf.o |
Output ELF path |
--program |
(auto-detect) | Program function to keep (repeatable) |
--section |
Program-to-section mapping name=section (repeatable) |
|
--cpu |
v3 |
BPF CPU version for llc -mcpu |
--opt-profile |
default |
conservative, default, aggressive, or verifier-safe |
--pass-pipeline |
Explicit opt pass pipeline (overrides profile) |
|
--btf |
false |
Inject BTF via pahole |
--verbose, -v |
false |
Print each pipeline stage |
--timeout |
30s |
Per-stage timeout |
--dump-ir |
false |
Write intermediate IR after each transform stage |
--program-type |
Validate sections match a BPF program type (e.g. kprobe, xdp) |
|
--keep-temp |
false |
Preserve intermediate files |
--tmpdir |
Directory for intermediate files |
These flags are accepted by build, link, and doctor.
| Flag | Default | Description |
|---|---|---|
--llvm-link |
Path to llvm-link binary |
|
--opt |
Path to opt binary |
|
--llc |
Path to llc binary |
|
--llvm-ar |
Path to llvm-ar binary |
|
--llvm-objcopy |
Path to llvm-objcopy binary |
|
--pahole |
Path to pahole binary |
| Document | Description |
|---|---|
| Writing Go for eBPF | Language constraints, BPF concepts, helpers, patterns, and FAQ |
| Architecture | Pipeline design and the 15-step IR transformation |
| Support Matrix | Tested toolchain versions and platforms |
| Troubleshooting | Setup issues, pipeline errors, and verifier debugging |
| Contributing | Development setup, testing, and PR process |
| Project | Relationship |
|---|---|
| cilium/ebpf | Go library for loading eBPF programs; loads tinybpf output |
| bpf2go | Compiles C eBPF and generates Go bindings; replaced when the program is Go |
| libbpf | C loader library; compatible with tinybpf output |
| TinyGo | Go compiler targeting LLVM; provides the IR that tinybpf transforms |
| Aya | eBPF in Rust; similar goal, different language |
| miekg/bpf | Prior effort to add BPF to TinyGo's LLVM; archived |