A version of this post was also uploaded to Aqua's blog here
If you’ve been reading much bpf code recently, you’ve probably seen this:
vmlinux.h is generated code. it contains all of the type definitions that your running Linux kernel uses in it’s own source code. This is an important concept to wrap your head around so let me explain.
When you build Linux one of the output artifacts is a file called
vmlinux. It’s also typically packaged with major distributions. This is an ELF binary that contains the compiled kernel inside it.
There’s a tool, aptly named bpftool, that is maintained within the Linux source repository. It has a feature to read the
vmlinux file and generate a
vmlinux.h file. Since it contains every type-definition that the installed kernel uses, it’s a very large header file.
The actual command is:
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
It’s pretty common in bpf programs to read fields of data structures that are used in the kernel. When you import this header file your bpf program can read memory and know which bytes correspond to which fields of any struct that you want to be working with.
For example, Linux represents the concept of a process with a type called
task_struct. If you want to inspect values in a task_struct from your bpf program, you’re going to need to know the definition of it.
Since the vmlinux.h file is generated from your installed kernel, your bpf program could break if you try to run it on another machine without recompiling if it’s running a different kernel version. This is because, from version to version, definitions of internal structs change within the linux source code.
However, by using libbpf, you can enable something called “CO:RE” or “Compile once, run everywhere”. There are macros defined in libbpf (such as
BPF_CORE_READ) that will analyze what fields you’re trying to access in the types that are defined in your
vmlinux.h. If the field you want to access has been moved within the struct definition that the running kernel uses, the macro/helpers will find it for you. Therefore, it doesn’t matter if you compile your bpf program with the
vmlinux.h file you generated from your own kernel and then ran on a different one.
It also feels important to point out that macros defined in Linux will not be defined in DWARF/BTF, and therefore not be part of the generated vmlinux.h file.
Refer to Andrii Nakryiko’s blog post here on the subject of BPF + CO:RE.