Home / Alt manpages / bpftool-gen(8)

  • bpftool-gen(8)
  • Admin command
  • linux

Build a BPF Skeleton with bpftool gen

Hand-writing the libbpf boilerplate for a BPF program is a fast way to lose an afternoon, so let bpftool gen write it. It turns compiled BPF ELF objects into a combined object and a generated C header, and it also covers subskeletons and reduced CO-RE BTF files. Allow 20 to 30 minutes for a first run.

You need bpftool, LLVM or Clang with BPF support, libbpf development headers and a working C build environment.

The examples follow the installed linux-tools-common package version 6.8.0-139.139 and its bpftool-gen(8) manual page. The package wrapper on this host reports that the kernel-specific bpftool binary is missing, so run the commands on a machine where bpftool works. This guide only creates build artefacts. Loading or attaching a BPF program is a separate, potentially privileged operation.

1. Check the local command and toolchain

Check the executable and the command syntax first. These are ordinary read-only commands:

$ command -v bpftool
$ bpftool version
$ bpftool gen help

Record the version before you copy examples into a build system. The local 6.8 manual documents object, skeleton, subskeleton and min_core_btf, with -L or --use-loader for a light skeleton. Upstream documentation moves faster than a distribution package, so do not add an option just because a newer online manual shows it.

Checkpoint

Continue only when bpftool gen help lists the subcommand you intend to use. A missing executable or an unsupported subcommand is a package or version issue, not a BPF source error.

2. Compile each BPF source file

Keep source files separate when that makes the program easier to maintain. Compile each one to a BPF ELF object with debug information:

$ clang --target=bpf -g example1.bpf.c -o example1.bpf.o
$ clang --target=bpf -g example2.bpf.c -o example2.bpf.o
$ file example1.bpf.o example2.bpf.o
  • Feed it ELF, not C. The input to bpftool gen object must be BPF ELF, not C source.
  • The -g option is safe. It supplies information useful to BTF and later diagnostics. It does not load anything into the kernel.
  • Fix the compiler first. If Clang cannot find BPF headers or rejects the target, sort out the compiler and development packages before invoking bpftool.
  • Keep the original objects. They show which compilation step failed, and they let you repeat the link without rebuilding source.

3. Combine objects into one BPF ELF file

Use gen object when a program is split across compilation units:

$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o
$ file example.bpf.o
$ test -s example.bpf.o && echo "combined object exists"

This statically links the BPF instruction and data sections. It also combines available .BTF and .BTF.ext data, deduplicating common BTF types.

Warning

The output file is newly generated, so choose a path that is not a valuable existing object. If you must replace one, copy it first.

$ cp --preserve=all example.bpf.o example.bpf.o.backup

If the command fails, the inputs remain available. Remove only a partial output that you have confirmed is disposable, or use a new output name on the next attempt. Do not run the build as root just to write into a directory with unsuitable ownership.

Checkpoint

file example.bpf.o should identify an ELF object for BPF. A file that exists but is empty, or is identified as plain data, is not a successful link.

4. Generate and inspect a normal skeleton

A skeleton embeds the object in a generated C header. It gives your user-space program named access to maps, programs, links and supported global data. Generate it with an explicit object name so the C API is predictable:

$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h
$ grep -E 'example__(open|load|attach|detach|destroy)' example.skel.h
  • Header and object are a pair. The header is derived from the input object, so re-run the command whenever the BPF object changes.
  • Name it explicitly. The explicit name example makes functions such as example__open(), example__load() and example__destroy() easy to recognise. Without it, the object filename supplies the name.
  • Check the licence. Generated code contains LGPL-2.1 and BSD-2-Clause licensing notices. Review the file before distributing it, and keep the notice.
  • It does not run anything. The header does not itself load or attach the program. Your C application must call the generated functions and handle errors.

For a loader-style light skeleton, use the documented local option:

$ bpftool gen skeleton --use-loader example.bpf.o name example | tee example-loader.skel.h

Tip

Use this only when its reduced libbpf and libelf requirements fit your application, and verify the option with the local help output first.

5. Use a subskeleton for a library object

A subskeleton is for code that joins an already opened BPF object. It does not own the maps, programs or global variables, and destroying it does not unload those resources:

$ bpftool gen subskeleton library.bpf.o name library | tee library.subskel.h
$ grep -E 'library__(open|destroy)' library.subskel.h

Use it when a library is embedded in a larger BPF application and that application's bpf_object owns the load lifecycle. Do not swap a subskeleton in for a normal skeleton unless that ownership is deliberate.

In C, the generated open function takes an already opened bpf_object. The generated destroy function frees subskeleton storage but does not unload the parent object.

6. Create a smaller CO-RE BTF file

When a target kernel lacks CONFIG_DEBUG_INFO_BTF, libbpf may need an external BTF file to resolve CO-RE relocations. min_core_btf derives a smaller file from an input BTF file and one or more BPF objects:

$ bpftool gen min_core_btf 5.4.0-example.btf 5.4.0-smaller.btf one.bpf.o
$ test -s 5.4.0-smaller.btf && echo "minimal BTF exists"
$ bpftool btf dump file 5.4.0-smaller.btf format raw | sed -n '1,12p'

The input BTF must suit the kernel, and the object list must include every CO-RE object you plan to load. Add more objects as needed:

$ bpftool gen min_core_btf kernel.btf application.btf collector.bpf.o policy.bpf.o

The result is deliberately specific. A minimal BTF file made for one object is not a general replacement for the full kernel BTF, and it may not satisfy another object's relocations. Pass it to libbpf as the custom BTF path while opening the object, then test loading on the target kernel.

Warning

Loading can require elevated privileges and can affect kernel state. Do that step only in an approved test environment with a rollback plan.

7. Diagnose the common mistakes

  • Unknown option. Compare bpftool version and bpftool gen help with the documentation you are reading. Distribution versions differ.
  • Invalid input. Inspect every input with file. Source files, stripped unrelated ELF files and a BTF file are not interchangeable with BPF ELF objects.
  • Missing generated names. Pass name OBJECT_NAME and use a C identifier-friendly name. Then regenerate the header and rebuild the user-space program.
  • CO-RE still fails. Check that the reduced BTF came from the right kernel's full BTF and was generated with every object that will be loaded. A successful generation command does not prove that a later verifier load will succeed.

Tip

Do not use sudo for compilation or generation unless filesystem permissions genuinely require it. If an earlier command left root-owned build files, restore ownership with your normal administrator procedure rather than running the whole build as root.

Done means

  • Syntax matches. bpftool gen help lists the subcommands and options you used.
  • Combined object is ELF. file identifies the output as a BPF ELF object.
  • Header is fresh. It was rebuilt from the current object and exposes the expected object-name prefix.
  • Right skeleton chosen. Normal, sub or light, according to ownership and runtime requirements.
  • Minimal BTF is documented. If you generated one, you recorded exactly which objects it covers and kept the full input BTF available.