概念
BTF(BPF Type Format)、CO-RE(Compile Once, Run Everywhere)、和 libbpf
之间有紧密的联系,它们共同协作使得 eBPF 程序在不同内核版本上运行更加便捷和可靠。以下是它们之间的关系和各自的功能:
##BTF(BPF Type Format)
BTF 是一种紧凑的类型信息格式,描述了内核数据结构的布局。它的主要作用包括:
1. 内核数据结构的描述:BTF 提供了内核数据结构的详细描述,使得 eBPF 程序可以在运行时了解这些结构的布局。
2. 调试和验证:BTF 信息可以用于调试和验证 eBPF 程序,确保程序访问内核数据时的正确性。
3. 不幸的是,BTF(以及 DWARF)不记录
#define
宏,因此一些常用的宏可能缺失。最常见的缺失宏可能作为 libbpf 的bpf_helpers.h (内核端“库”,由 libbpf 提供)vmlinux.h
的一部分提供 。
CO-RE(Compile Once, Run Everywhere)
CO-RE 是一种技术,使得 eBPF 程序可以在编译一次后在不同内核版本上运行。它依赖于 BTF 提供的类型信息来适应不同内核版本的数据结构变化。CO-RE 的主要优点包括:
1. 跨内核版本兼容性:使用 CO-RE 技术编写的 eBPF 程序可以在不同内核版本上运行,无需重新编译。
2. 简化开发流程:开发者不需要针对每个内核版本进行单独的编译和调整,减少了维护的复杂性。
libbpf
libbpf
是一个用于加载和管理 eBPF 程序的用户空间库。它提供了一整套 API,用于简化 eBPF 程序的开发、加载、和调试。libbpf
的主要功能包括:
1. 加载 eBPF 程序:通过
libbpf
提供的 API,可以方便地加载和附加 eBPF 程序。2. 管理 eBPF 资源:
libbpf
提供了管理 eBPF map、ring buffer 等资源的功能。3. 支持 CO-RE:
libbpf
完全集成了 CO-RE 技术,允许 eBPF 程序使用 BTF 信息来适应不同内核版本的数据结构。
三者之间的关系
1. BTF 与 CO-RE:BTF 提供了内核类型信息,使得 CO-RE 技术可以利用这些信息来实现 eBPF 程序的跨内核版本兼容性。通过 BTF,CO-RE 可以在运行时确定内核数据结构的实际布局,从而正确地访问这些数据。
2. CO-RE 与 libbpf:
libbpf
提供了对 CO-RE 的支持,使得开发者可以方便地编写、编译和加载支持 CO-RE 的 eBPF 程序。libbpf
利用 BTF 信息来解析和调整 eBPF 程序中的数据访问,使其适应不同的内核版本。3. libbpf 与 BTF:
libbpf
使用 BTF 信息来辅助 eBPF 程序的加载和验证。通过 BTF,libbpf
可以在加载 eBPF 程序时检查程序访问的内核数据结构是否正确,并进行必要的调整。
vmlinux
vmlinux
是 Linux 内核的非压缩可执行二进制文件,包含了内核代码和数据。它通常用于内核开发和调试,因为它保留了调试符号,可以通过工具如 GDB 进行详细的调试。vmlinux
文件通常位于内核构建目录中,包含完整的内核映像,但不经过任何压缩或打包,因此体积较大。调试信息和符号可以帮助开发人员更深入地了解内核行为,分析和修复内核中的问题。
主要用途
1. 内核调试:使用 GDB 等调试工具分析内核问题。
2. 性能分析:与
perf
等工具结合使用进行性能调优。3. eBPF 和 BTF:为 eBPF 程序生成 BTF 信息,用于类型检查和跨内核版本兼容。
生成 BTF(BPF Type Format)信息需要 vmlinux
文件,因为它包含了内核的完整调试信息,包括数据结构的详细布局。这些信息以 DWARF 格式存储在 vmlinux
文件中。pahole
工具从 vmlinux
中提取这些 DWARF 信息,并将其转换为更紧凑的 BTF 格式。
主要原因
1. 类型信息:BTF 需要详细的类型信息,这些信息在
vmlinux
的 DWARF 调试数据中。2. 内核数据结构:
vmlinux
包含了所有内核数据结构的定义和布局。3. 调试符号:调试符号提供了所需的元数据,以便正确地生成 BTF 信息。
生成 BTF
DebugInfo 包
Debuginfo 包主要用于调试目的,包含了程序的调试信息,如符号表、变量名、函数名和 DWARF 格式的调试数据。这些信息可以帮助开发人员和调试工具理解程序的内部结构和状态,进行调试和性能分析。
具体用途包括:
1. 调试程序:使用 GDB 等调试工具,调试运行中的程序。
2. 生成 BTF 文件:从 DWARF 调试信息生成 BTF 文件,供 eBPF 程序使用。
3. 性能分析:使用调试信息进行性能分析和代码优化。
Debuginfo 包通常与相应的二进制包一起提供,以便开发人员在需要时可以安装和使用。
获取 Ky10 SP1.1 Kernel debug info 包
1. 获取当前操作系统的内核
# uname -a
Linux master2 4.19.90-23.15.v2101.ky10.aarch64 #1 SMP Wed Sep 1 16:42:05 CST 2021 aarch64 aarch64 aarch64 GNU/Linux2. 下载对应 debuginfo 包,下载地址:https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/aarch64/debug/,如果不确定当前操作系统属于 SP几,可以去 https://update.cs2c.com.cn/NS/V10 下找一下,debuginfo 包只会在 debug 目录下。
3. 安装 debuginfo 包:
rpm -ivh kernel-debuginfo-4.19.90-23.15.v2101.ky10.aarch64.rpm
安装 cMake
1. 安装 cmak,地址:https://github.com/Kitware/CMake/releases/download/v3.29.6/cmake-3.29.6-linux-aarch64.tar.gz ,
2. 解压之后放到 /usr/bin 目录
编译 dwarves
1. 下载源码:
git clone https://github.com/acmel/dwarves.git
2. 下载依赖包:
git submodule update --init --recursive
3. 编译:
install cmake
mkdir build
cd build
cmake -D__LIB=lib ..
make install
生成 BTF
pahole --btf_encode_detached external.btf /usr/lib/debug/lib/modules/4.19.90-23.15.v2101.ky10.aarch64/vmlinux
生成 vmlinux.h
pahole --compile /usr/lib/debug/lib/modules/4.19.90-23.15.v2101.ky10.aarch64/vmlinux > vmlinux.h
生成 BTF 信息的工具
通常,BTF 信息编码在 .BTF 和 .BTF.ext ELF 节中,或在原始 BTF 文件中。主要有两种方法来编码 BTF 信息:
1. pahole 工具(来自 dwarves 项目):该工具利用包含 DWARF 调试数据 的非剥离 ELF 文件。输入的 ELF 文件可以是内核或标准 eBPF ELF 对象。该过程会在输入 ELF 文件中添加两个包含 BTF 编码的 ELF 节。最近,这种 BTF 编码也可以添加到外部原始 BTF 文件中,可以用作 libbpf 的输入。
2. LLVM:编译 eBPF 代码时,LLVM 会自动生成 .BTF 和 .BTF.ext ELF 节。值得注意的是,与 pahole 不同,LLVM 能生成 BTF 重定位信息。这对 libbpf 成功执行 CO-RE 重定位至关重要。
通过选择这些方法之一,可以根据应用程序的需求有效生成 BTF 信息。
读取 BTF 信息
1. 使用
bpftool
:如果当前运行的内核支持嵌入式 BTF(大多数现代 Linux 发行版都支持),则可以在/sys/kernel/btf/vmlinux
中找到相应的 BTF 信息。可以通过执行以下命令检查该文件的内容:$ bpftool btf dump file /sys/kernel/btf/vmlinux format raw
[1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
[2] CONST '(anon)' type_id=1
[3] ARRAY '(anon)' type_id=1 index_type_id=18 nr_elems=2
[4] PTR '(anon)' type_id=6
[5] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)
[6] CONST '(anon)' type_id=5
[7] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
[8] CONST '(anon)' type_id=7
[9] INT 'signed char' size=1 bits_offset=0 nr_bits=8 encoding=(none)
[10] TYPEDEF '__u8' type_id=11
...格式可以是 "c" 或 "raw",根据需要选择。
2. 使用
pahole
:$ pahole /sys/kernel/btf/vmlinux
struct list_head {
struct list_head * next; /* 0 8 */
struct list_head * prev; /* 8 8 */
/* size: 16, cachelines: 1, members: 2 */
/* last cacheline: 16 bytes */
};
...如果当前内核或打算支持 eBPF 应用程序的内核中没有
/sys/kernel/btf/vmlinux
文件,可能需要转向 BTFhub。这种情况通常发生在内核没有启用DEBUG_INFO_BTF
kconfig 选项时,这是 BTFhub-Archive 存储库中大多数内核的常见情况。因此,在这种情况下,BTFhub 可能对有所帮助。
Linux 内核如何生成自己的 BTF 信息?
BTFhub 和 BTFhub-Archive 的核心在于将内核调试包自动转换为 BTF 信息,这对于 libbpf 运行 CO-RE 能力的 eBPF 应用至关重要。通过 btfhub 应用程序(通过 make
编译),下载所有现有的调试内核包,并将嵌入的 DWARF 信息转换为 BTF 格式。
BTFhub 以类似于 cron 作业的方式操作 btfhub
应用程序,每天执行。生成的 BTF 文件随后上传到 BTFhub-Archive 存储库,可以在的项目中使用。
1. 实践中,可以通过执行以下命令将 .BTF ELF 节添加到非剥离的 vmlinuz(未压缩)内核文件:
pahole -J vmlinux
2. 或者,可以使用以下命令生成外部原始 BTF 文件:
pahole --btf_encode_detached external.btf vmlinux
3. 如果选择在 vmlinuz 文件中生成新的 .BTF ELF 节,稍后可以使用以下命令将其提取到外部原始 BTF 文件(例如 vmlinux.btf)中:
llvm-objcopy --only-section=.BTF --set-section-flags .BTF=alloc,readonly vmlinux vmlinux.btf
这种灵活性确保了 BTF 文件生成的定制方法,增强了不同内核版本和发行版之间的兼容性。