-
Notifications
You must be signed in to change notification settings - Fork 13
Cross compiling eBPF Programs
There are several ways of building eBPF applications for Linux. The main and most straightforward way consists of these steps:
- Build the eBPF part using Clang (it's a regular Object in ELF format).
- Generate a special C file named "skeleton" which contains an entire blob
for the eBPF part and the functions for loading it using
libbpf
. - Write a C program which uses these functions for opening the eBPF application, loading it into the kernel and attaching it to events.
- Build your C program using GCC toolchain and run on your target.
Consider the minimal
program from libbpf-bootstrap
repository. It consists of
2 files:
minimal.c
and minimal.bpf.c
. Ensure that libbpf
and the
corresponding development files are installed (e.g., for Fedora you need to
install libbpf
and libbpf-devel
packages). Also, a recent version of
bpftool
utility must be installed. Then you can build the program using
these commands:
# Replace -D__TARGET_ARCH_x86 to an appropriate one for your platform
clang -g \
-O2 \
-Wall \
-target bpf \
-D__TARGET_ARCH_x86 \
-c minimal.bpf.c \
-o minimal.bpf.o
bpftool gen skeleton minimal.bpf.o > minimal.skel.h
gcc minimal.c -lbpf \
-lelf \
-lz \
-o minimal
Then you can run this program with root
privileges:
# The 2 commands below are a one-time setup. Run them with root privileges.
mount -t debugfs debugfs /sys/kernel/debug
sysctl net.core.bpf_jit_enable=1 # Keep 0, if you don't want JIT.
$ sudo ./minimal
[sudo] password for user:
libbpf: loading object 'minimal_bpf' from buffer
libbpf: elf: section(3) tp/syscalls/sys_enter_write, size 104, link 0, flags 6, type=1
libbpf: sec 'tp/syscalls/sys_enter_write': found program 'handle_tp' at insn offset 0 (0 bytes), code size 13 insns (104 bytes)
libbpf: elf: section(4) .reltp/syscalls/sys_enter_write, size 32, link 22, flags 40, type=9
libbpf: elf: section(5) license, size 13, link 0, flags 3, type=1
libbpf: license of minimal_bpf is Dual BSD/GPL
libbpf: elf: section(6) .bss, size 4, link 0, flags 3, type=8
libbpf: elf: section(7) .rodata, size 28, link 0, flags 2, type=1
libbpf: elf: section(13) .BTF, size 609, link 0, flags 0, type=1
libbpf: elf: section(15) .BTF.ext, size 160, link 0, flags 0, type=1
libbpf: elf: section(22) .symtab, size 336, link 1, flags 0, type=2
libbpf: looking for externs among 14 symbols...
libbpf: collected 0 externs total
libbpf: map 'minimal_.bss' (global data): at sec_idx 6, offset 0, flags 400.
libbpf: map 0 is "minimal_.bss"
libbpf: map 'minimal_.rodata' (global data): at sec_idx 7, offset 0, flags 480.
libbpf: map 1 is "minimal_.rodata"
libbpf: sec '.reltp/syscalls/sys_enter_write': collecting relocation for section(3) 'tp/syscalls/sys_enter_write'
libbpf: sec '.reltp/syscalls/sys_enter_write': relo #0: insn #2 against 'my_pid'
libbpf: prog 'handle_tp': found data map 0 (minimal_.bss, sec 6, off 0) for insn 2
libbpf: sec '.reltp/syscalls/sys_enter_write': relo #1: insn #6 against '.rodata'
libbpf: prog 'handle_tp': found data map 1 (minimal_.rodata, sec 7, off 0) for insn 6
libbpf: map 'minimal_.bss': created successfully, fd=4
libbpf: map 'minimal_.rodata': created successfully, fd=5
Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF programs.
Some examples from libbpf-bootstrap
use vmlinux.h
- It's a header file
with the definitions of all the data structures for that particular kernel. The
easiest way to obtain it on the host system is by using bpftool
, if the
kernel was built with CONFIG_DEBUG_INFO_BTF=y
:
bpftool --debug btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
Else, you can generate vmlinux.h
from any vmlinux
image with BTF
information:
bpftool --debug btf dump file path/to/your/vmlinux format c > vmlinux.h
Suppose that the root directory of the toolchain for ARC HS 4x is
/tools/arc-linux-gnu
. /tools/arc-linux-gnu/bin
must be in PATH
variable. bpftool
must be available too.
Create a directory for ARC's libraries and includes:
mkdir /tools/sysroot
Export common variables:
export CFLAGS="-Og -g3 -fPIC"
export CXXFLAGS="-Og -g3 -fPIC"
export CPPFLAGS="-I/tools/sysroot/include"
export LDFLAGS="-L/tools/sysroot/lib"
We are going to use the latest releases of libraries at the moment of writing of this manual instead of development branches.
git clone -b v1.2.13 https://github.com/madler/zlib
cd zlib
./configure CHOST="arc-linux-gnu" --static --prefix="/tools/sysroot"
make install
git clone -b elfutils-0.189 https://sourceware.org/git/elfutils.git
cd elfutils
autoreconf -i -f
./configure --host=arc-linux-gnu \
--target=arc-linux-gnu \
--disable-dependency-tracking \
--disable-nls \
--program-prefix="eu-" \
--disable-libdebuginfod \
--disable-debuginfod \
--without-bzlib \
--without-lzma \
--without-zstd \
--prefix="/tools/sysroot" \
--enable-maintainer-mode
make -C libelf install-includeHEADERS install-libLIBRARIES
git clone -b v1.1.0 https://github.com/libbpf/libbpf
cd libbpf/src
make BUILD_STATIC_ONLY="y" \
PREFIX="/" \
DESTDIR="/tools/sysroot" \
CROSS_COMPILE="arc-linux-gnu-" \
install install_uapi_headers
Make sure that you have a recent version of bpftool (≥7).
bpftool --debug btf dump file path/to/your/vmlinux format c > /tools/sysroot/include/vmlinux.h
After building and installing all libraries /tools/sysroot
should look like this:
$ tree -L 3 /tools/sysroot
/tools/sysroot
|-- include
| |-- bpf
| | |-- bpf_core_read.h
| | |-- bpf_endian.h
| | |-- bpf.h
| | |-- bpf_helper_defs.h
| | |-- bpf_helpers.h
| | |-- bpf_tracing.h
| | |-- btf.h
| | |-- libbpf_common.h
| | |-- libbpf.h
| | |-- libbpf_legacy.h
| | |-- libbpf_version.h
| | |-- skel_internal.h
| | `-- usdt.bpf.h
| |-- gelf.h
| |-- libelf.h
| |-- linux
| | |-- bpf.h
| | |-- bpf_common.h
| | `-- btf.h
| |-- nlist.h
| |-- zconf.h
| `-- zlib.h
|-- lib
| |-- libbpf.a
| |-- libelf.a
| |-- libz.a
| `-- pkgconfig
| |-- libbpf.pc
| `-- zlib.pc
`-- share
`-- man
`-- man3
9 directories, 26 files
# The paths where the native ARC tools and the cross ARC toolchain are installed
readonly NATIVE_SYSROOT=/tools/sysroot
readonly TOOLCHAIN_SYSROOT=/tools/arc-linux-gnu/sysroot
# Clang uses host's Linux headers if we don't pass toolchain's includes.
# Also, the -XClang arguments below are optional.
clang -g \
-O2 \
-Wall \
-target bpf \
-D__TARGET_ARCH_arc \
-I${NATIVE_SYSROOT}/usr/include \
-I${TOOLCHAIN_SYSROOT}/usr/include \
-Xclang -target-feature \
-Xclang +alu32 \
-c minimal.bpf.c \
-o minimal.bpf.o
# bpftool must be a recent version (≥7)
bpftool gen skeleton minimal.bpf.o > minimal.skel.h
# shared library build (-latomic is listed for future reference)
arc-linux-gcc minimal.c \
-I${NATIVE_SYSROOT}/usr/include \
-L${NATIVE_SYSROOT}/usr/lib \
-lbpf \
-lelf \
-lz \
-latomic \
-o minimal
# static build (-latomic is listed for future reference)
arc-linux-gcc minimal.c \
-I${SYSROOT}/usr/include \
${NATIVE_SYSROOT}/usr/lib/libbpf.a \
${NATIVE_SYSROOT}/usr/lib/libelf.a \
${NATIVE_SYSROOT}/usr/lib/libz.a \
-latomic \
-o minimal
You can use a testbench which automates all the above steps for building eBPF programs for ARC: