libbpf-bootstrap学习

本文主要是是对Building BPF applications with libbpf-bootstrap (nakryiko.com)的个人理解的总结,这篇文章可以当成是libbpf-bootstrap这个项目的README ,介绍了它的目标、依赖和用法等。

BPF相关(1) 工具链libbpf入门指南 - 知乎 (zhihu.com) 是机器翻译版本,需要的同学可以中英对照自己看下。

文中涉及的ebpf代码在libbpf-bootstrap仓库的example中,建议clone下来对照查看。

前言

BPF是一种很cool的技术。有了它之后,不需要充分的内核开发经验,也不需要配置内核的开发环境,普通的开发人员也可以改进和追踪内核的功能。

但是,开发BPF程序仍然是一件很麻烦的事情,因为即使是开始一个hello-world的 bpf程序,也需要设置一大堆的环境和依赖软件,这使得初入BPF领域开发者十分的困惑。

libbpf-bootstrap 是一个脚手架项目,为初学者设置好了一切初学BPF所需要的环境与依赖配置,让用户可以直接切入开发而不用去关心那些烦人的配置问题。并且它借鉴了BPF社区开发提供的最佳方式,提供一套全新的现代的开发工作流水线。

并且libbpf支持CO-RE(一次编译,到处运行),用于在不通Linux发行版、不同内核版本运行。这依赖于 BPF CO-RE 和内核 BTF 支持,所以请确保您的 Linux 内核在编译时使用 CONFIG_DEBUG_INFO_BTF=y Kconfig了。当然,目前高版本的Linux发行版基本都内置了该支持。

libbpf-bootstrap仓库的example中有两个版本:

minimal:一个hello-word版本的ebpf程序,它hook了write系统调用,每次write时都打印一行日志。

bootstrap:一些有现实意义的ebpf程序,例如tc(traffic control)、profile等例子,他们用到了ebpf更高级的一些特性,相应的复杂度有一些提升。

使用libbpf库编写EBPF程序的依赖

  1. Clang编译器。至少需要Clang10,CO-RE需要Clang11或Clang12
  2. libbpf库
  3. bpftool可执行性文件,用来生成vmlinux.h和xx_skel.h
  4. zlib (libz-dev or zlib-devel ) and libelf (libelf-dev or elfutils-libelf-devel )

libbpf-bootstrap用git submodule提供了libbpfbpftool ,避免依赖系统全局的libbpf和bpftool,所以使用libbpf-bootstrap的话,不需要预先在系统中安装2和3。

Libbpf-bootstrap 概览

原文中提到的libbpf-bootstrap的文件树如下,当前已经发生了一些变化,但是不影响我们理解。

$ tree
.
├── libbpf
│   ├── ...
│   ... 
├── LICENSE
├── README.md
├── src
│   ├── bootstrap.bpf.c
│   ├── bootstrap.c
│   ├── bootstrap.h
│   ├── Makefile
│   ├── minimal.bpf.c
│   ├── minimal.c
│   ├── vmlinux_508.h
│   └── vmlinux.h -> vmlinux_508.h
└── tools
    ├── bpftool
    └── gen_vmlinux_h.sh

16 directories, 85 files

通过git submodule内置了libbpf库,来避免依赖系统全局的libbpf(就是/usr/lib下的那些)。

tools/ 下内置了bpftool的64位可执行文件,来避免依赖全局的bpftool 。bpftool可以从/sys/kernel/btf/vmlinux生成包含所有Linux kernel type definitions的头文件vmlinux.h。上面的文件树中,已经生成了5.08内核的vmlinux.h,一般情况下来说CO-RE特性不需要运行BPF程序的主机恰好时5.08版本的内核,所以大多数时候不需要重新生成vmlinux.h。因为有vmlinux.h,所以libbpf不依赖特定版本的kernel-header,这是CO-RE的基础。

另外bpftool还会在make过程中生成bpf c代码的xx_skel.h(骨架头文件)。怎么理解这个骨架头文件呢?BPF程序分为BPF c代码和用户态代码。BPF c代码运行在内核态,用户态代码需要与其交互。回忆一下RPC的交互流程,client要有桩文件才能调用server的服务。这个骨架头文件就是BPF c代码的桩,包含了BPF代码的类型定义,例如全局变量、BPF map、方法声明等。

一个BPF程序的源代码由三部分组成:

  • <app>.bpf.c files are the BPF C code that contain the logic which is to be executed in the kernel context;
  • <app>.c is the user-space C code, which loads BPF code and interacts with it throughout the lifetime of the application;
  • optional <app>.h is a header file with the common type definitions and is shared by both BPF and user-space code of the application.

这些代码将在MakeFile定义的编译规则中使用。

libbpf examples

  1. libbpf-rs/examples at master · libbpf/libbpf-rs · GitHub
  2. libbpf-bootstrap/examples at master · libbpf/libbpf-bootstrap (github.com)
  3. deepflow/agent at f76c73dde43e6f7919d72686b6cf79cae3d1d79d · deepflowio/deepflow · GitHub (在此commit之后去除了libbpf的依赖,因为GPL证书太严格了)

一些备忘

libbpf-bootstrap项目初始化

git submodule update --init --recursive
yum install -y zlib-devel elfutils-libelf-devel bpftool libbpf
bpftool btf dump file /sys/kernel/btf/vmlinux format c > ./vmlinux.h

寻找libc的动态库,用于uprobe

$ find / -name libc.so.6 
/var/lib/containers/storage/overlay/de102d108aeda5726e7c2794014f5c49caa079542e29c15d0f9dbb9ed9280fc1/diff/usr/lib64/libc.so.6
/var/lib/containers/storage/overlay/c34512f6bae849adb22b5cf34a4f42e8054431ca488cd754020c09a7664dbf46/merged/usr/lib64/libc.so.6
/usr/lib64/libc.so.6