I discussed this with @andrewrk at Zig Day Portland, and I'm creating this issue to track the work and get feedback on the approach.
Background
eBPF is a niche topic that most people aren't familiar with, so I'll provide some background information.
eBPF programs run in the Linux kernel on a dedicated bytecode virtual machine and are used to instrument or control various operations within the Linux kernel. eBPF programs have access to kernel data structures and often dig through them to access process state, filesystem information, etc. The layout, size, alignment, etc of kernel data structures may change from one release to the next or as a result of vendor patches, which means that an eBPF program compiled for one kernel isn't guaranteed to work properly on another kernel.
eBPF "Compile Once - Run Everywhere" (CO-RE) is a mechanism that allows you to compile an eBPF program for one kernel and have it run on another. eBPF CO-RE is an industry standard, and eBPF usage without it is extremely limited.
There are a few moving pieces that make this work:
- BPF Type Format (BTF): debug information for the symbols and data structures in the Linux kernel.
- An eBPF program compiled into an ELF object has BTF sections embedded into it to keep track of which kernel data structures are used and how they're accessed. In short, BTF is used to keep track of relocations that must be fixed up at load-time.
- libbpf: library developed in-kernel for handling eBPF programs.
- Performs the necessary relocations before submitting the eBPF program to the kernel.
- Used by a userspace program on the target machine to perform the necessary relocations before submitting the eBPF program to the kernel.
- Uses the BTF from the target machine's kernel.
- See yours using
bpftool (warning: produces ~3MB header): bpftool btf dump file /sys/kernel/btf/vmlinux format c >vmlinux.h
Most eBPF programs are written in C, since this is the language that libbpf is written in, and libbpf provides macros (BPF_CORE_READ, etc) for accessing kernel data structures safely. These macros are used to emit compiler intrinsics that emit BTF relocation information.
The LLVM documentation for one of those intrinsics is here: llvm.preserve.struct.access.index. There are corresponding intrinsics for arrays, unions, etc.
Problem
The Zig compiler doesn't emit the intrinsics that tell LLVM to produce the BTF relocations necessary for CO-RE to work properly, which means that Zig can't realistically be used for writing eBPF programs.
Approach
Zig has a concept of pointer address space via the addrspace keyword. The default "userspace" address space is .generic.
The approach here is:
- Add a new
.kernel address space.
- Make
.kernel the default address space for the bpfel and bpfeb backends.
- Emit the necessary LLVM intrinsics for
.kernel pointers.
- Use the existing
@addrSpaceCast builtin when creating pointers to userspace memory.
I discussed this with @andrewrk at Zig Day Portland, and I'm creating this issue to track the work and get feedback on the approach.
## Background
eBPF is a niche topic that most people aren't familiar with, so I'll provide some background information.
eBPF programs run in the Linux kernel on a dedicated bytecode virtual machine and are used to instrument or control various operations within the Linux kernel. eBPF programs have access to kernel data structures and often dig through them to access process state, filesystem information, etc. The layout, size, alignment, etc of kernel data structures may change from one release to the next or as a result of vendor patches, which means that an eBPF program compiled for one kernel isn't guaranteed to work properly on another kernel.
eBPF "Compile Once - Run Everywhere" (CO-RE) is a mechanism that allows you to compile an eBPF program for one kernel and have it run on another. eBPF CO-RE is an industry standard, and eBPF usage without it is extremely limited.
There are a few moving pieces that make this work:
- [BPF Type Format (BTF)](https://www.kernel.org/doc/html/latest/bpf/btf.html): debug information for the symbols and data structures in the Linux kernel.
- An eBPF program compiled into an ELF object has BTF sections embedded into it to keep track of which kernel data structures are used and how they're accessed. In short, BTF is used to keep track of relocations that must be fixed up at load-time.
- [libbpf](https://libbpf.readthedocs.io/en/latest/): library developed in-kernel for handling eBPF programs.
- Performs the necessary relocations before submitting the eBPF program to the kernel.
- Used by a userspace program on the target machine to perform the necessary relocations before submitting the eBPF program to the kernel.
- Uses the BTF from the target machine's kernel.
- See yours using `bpftool` (warning: produces ~3MB header): `bpftool btf dump file /sys/kernel/btf/vmlinux format c >vmlinux.h`
Most eBPF programs are written in C, since this is the language that `libbpf` is written in, and `libbpf` provides macros (`BPF_CORE_READ`, etc) for accessing kernel data structures safely. These macros are used to emit compiler intrinsics that emit BTF relocation information.
The LLVM documentation for one of those intrinsics is here: [`llvm.preserve.struct.access.index`](https://llvm.org/docs/LangRef.html#llvm-preserve-struct-access-index-intrinsic). There are corresponding intrinsics for arrays, unions, etc.
## Problem
The Zig compiler doesn't emit the intrinsics that tell LLVM to produce the BTF relocations necessary for CO-RE to work properly, which means that Zig can't realistically be used for writing eBPF programs.
## Approach
Zig has a concept of pointer address space via the `addrspace` keyword. The default "userspace" address space is `.generic`.
The approach here is:
- Add a new `.kernel` address space.
- Make `.kernel` the default address space for the `bpfel` and `bpfeb` backends.
- Emit the necessary LLVM intrinsics for `.kernel` pointers.
- Use the existing `@addrSpaceCast` builtin when creating pointers to userspace memory.