This brief description of CO-RE was initially published in December 2020 by the Cilium community as part of the eBPF Updates #2 on ebpf.io.

CO-RE (Compile Once, Run Everywhere) is a mechanism used with eBPF to ensure portability of the programs, mainly those intended for tracing. It addresses the issue that arises when a given structure is modified between two kernel versions. Tracing programs may attempt to access a field from a given structure by reading at a specific offset in that structure. But if modifications occur in a later version and introduce a change for the length of the structure, or for the order of its member fields, then the program will not be compatible.

CO-RE solves this by relying on BTF objects (BPF Type Format). A BTF object contains debug information about a program. This is in fact a simplified version of the DWARF format, used for example by GDB. BTF objects are loaded into the kernel, and they often hold information on eBPF bytecode, for example to dump the C instructions from which the program was compiled. But BTF can also describe other objects like the kernel itself. In that case, BTF can provide, just before an eBPF program is loaded, the relevant information for accessing the kernel structures. The required adjustments are performed as “ELF relocation” steps, just before the bytecode is sent to the kernel.

The easiest way to use CO-RE is through libbpf. Note that because it needs the BTF information for the kernel, CO-RE is only available if the CONFIG_DEBUG_INFO_BTF option has been set when compiling the kernel. Recent tooling (LLVM, libbpf) is also necessary.

If you want more information on this topic, Andrii Nakryiko explains everything there is to know about CO-RE on his blog. The specification for BTF can be found in the kernel documentation, but you might also want to have a look at the bpftool-btf man page if you are curious to inspect BTF objects on your system.