Ky10 SP1.1 Kernel debug info 生成 BTF

文摘   2024-07-07 09:31   江苏  

概念

BTF(BPF Type Format)、CO-RE(Compile Once, Run Everywhere)、和 libbpf 之间有紧密的联系,它们共同协作使得 eBPF 程序在不同内核版本上运行更加便捷和可靠。以下是它们之间的关系和各自的功能:

##BTF(BPF Type Format)

BTF 是一种紧凑的类型信息格式,描述了内核数据结构的布局。它的主要作用包括:

  1. 1. 内核数据结构的描述:BTF 提供了内核数据结构的详细描述,使得 eBPF 程序可以在运行时了解这些结构的布局。

  2. 2. 调试和验证:BTF 信息可以用于调试和验证 eBPF 程序,确保程序访问内核数据时的正确性。

  3. 3. 不幸的是,BTF(以及 DWARF)不记录#define宏,因此一些常用的宏可能缺失。最常见的缺失宏可能作为 libbpf 的bpf_helpers.h (内核端“库”,由 libbpf 提供)vmlinux.h的一部分提供 。

CO-RE(Compile Once, Run Everywhere)

CO-RE 是一种技术,使得 eBPF 程序可以在编译一次后在不同内核版本上运行。它依赖于 BTF 提供的类型信息来适应不同内核版本的数据结构变化。CO-RE 的主要优点包括:

  1. 1. 跨内核版本兼容性:使用 CO-RE 技术编写的 eBPF 程序可以在不同内核版本上运行,无需重新编译。

  2. 2. 简化开发流程:开发者不需要针对每个内核版本进行单独的编译和调整,减少了维护的复杂性。

libbpf

libbpf 是一个用于加载和管理 eBPF 程序的用户空间库。它提供了一整套 API,用于简化 eBPF 程序的开发、加载、和调试。libbpf 的主要功能包括:

  1. 1. 加载 eBPF 程序:通过 libbpf 提供的 API,可以方便地加载和附加 eBPF 程序。

  2. 2. 管理 eBPF 资源libbpf 提供了管理 eBPF map、ring buffer 等资源的功能。

  3. 3. 支持 CO-RElibbpf 完全集成了 CO-RE 技术,允许 eBPF 程序使用 BTF 信息来适应不同内核版本的数据结构。

三者之间的关系

  1. 1. BTF 与 CO-RE:BTF 提供了内核类型信息,使得 CO-RE 技术可以利用这些信息来实现 eBPF 程序的跨内核版本兼容性。通过 BTF,CO-RE 可以在运行时确定内核数据结构的实际布局,从而正确地访问这些数据。

  2. 2. CO-RE 与 libbpflibbpf 提供了对 CO-RE 的支持,使得开发者可以方便地编写、编译和加载支持 CO-RE 的 eBPF 程序。libbpf 利用 BTF 信息来解析和调整 eBPF 程序中的数据访问,使其适应不同的内核版本。

  3. 3. libbpf 与 BTFlibbpf 使用 BTF 信息来辅助 eBPF 程序的加载和验证。通过 BTF,libbpf 可以在加载 eBPF 程序时检查程序访问的内核数据结构是否正确,并进行必要的调整。

vmlinux

vmlinux 是 Linux 内核的非压缩可执行二进制文件,包含了内核代码和数据。它通常用于内核开发和调试,因为它保留了调试符号,可以通过工具如 GDB 进行详细的调试。vmlinux 文件通常位于内核构建目录中,包含完整的内核映像,但不经过任何压缩或打包,因此体积较大。调试信息和符号可以帮助开发人员更深入地了解内核行为,分析和修复内核中的问题。

主要用途

  1. 1. 内核调试:使用 GDB 等调试工具分析内核问题。

  2. 2. 性能分析:与 perf 等工具结合使用进行性能调优。

  3. 3. eBPF 和 BTF:为 eBPF 程序生成 BTF 信息,用于类型检查和跨内核版本兼容。

生成 BTF(BPF Type Format)信息需要 vmlinux 文件,因为它包含了内核的完整调试信息,包括数据结构的详细布局。这些信息以 DWARF 格式存储在 vmlinux 文件中。pahole 工具从 vmlinux 中提取这些 DWARF 信息,并将其转换为更紧凑的 BTF 格式。

主要原因

  1. 1. 类型信息:BTF 需要详细的类型信息,这些信息在 vmlinux 的 DWARF 调试数据中。

  2. 2. 内核数据结构vmlinux 包含了所有内核数据结构的定义和布局。

  3. 3. 调试符号:调试符号提供了所需的元数据,以便正确地生成 BTF 信息。

生成 BTF

DebugInfo 包

Debuginfo 包主要用于调试目的,包含了程序的调试信息,如符号表、变量名、函数名和 DWARF 格式的调试数据。这些信息可以帮助开发人员和调试工具理解程序的内部结构和状态,进行调试和性能分析。

具体用途包括:

  1. 1. 调试程序:使用 GDB 等调试工具,调试运行中的程序。

  2. 2. 生成 BTF 文件:从 DWARF 调试信息生成 BTF 文件,供 eBPF 程序使用。

  3. 3. 性能分析:使用调试信息进行性能分析和代码优化。

Debuginfo 包通常与相应的二进制包一起提供,以便开发人员在需要时可以安装和使用。

获取 Ky10 SP1.1 Kernel debug info 包

  1. 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/Linux
  2. 2. 下载对应 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. 3. 安装 debuginfo 包:

    rpm -ivh kernel-debuginfo-4.19.90-23.15.v2101.ky10.aarch64.rpm

安装 cMake

  1. 1. 安装 cmak,地址:https://github.com/Kitware/CMake/releases/download/v3.29.6/cmake-3.29.6-linux-aarch64.tar.gz ,

  2. 2. 解压之后放到 /usr/bin 目录

编译 dwarves

  1. 1. 下载源码:

    git clone https://github.com/acmel/dwarves.git
  2. 2. 下载依赖包:

    git submodule update --init --recursive
  3. 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. 1. pahole 工具(来自 dwarves 项目):该工具利用包含 DWARF 调试数据 的非剥离 ELF 文件。输入的 ELF 文件可以是内核或标准 eBPF ELF 对象。该过程会在输入 ELF 文件中添加两个包含 BTF 编码的 ELF 节。最近,这种 BTF 编码也可以添加到外部原始 BTF 文件中,可以用作 libbpf 的输入。

  2. 2. LLVM:编译 eBPF 代码时,LLVM 会自动生成 .BTF 和 .BTF.ext ELF 节。值得注意的是,与 pahole 不同,LLVM 能生成 BTF 重定位信息。这对 libbpf 成功执行 CO-RE 重定位至关重要。

通过选择这些方法之一,可以根据应用程序的需求有效生成 BTF 信息。

读取 BTF 信息

  1. 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. 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. 1. 实践中,可以通过执行以下命令将 .BTF ELF 节添加到非剥离的 vmlinuz(未压缩)内核文件:

    pahole -J vmlinux
  2. 2. 或者,可以使用以下命令生成外部原始 BTF 文件:

    pahole --btf_encode_detached external.btf vmlinux
  3. 3. 如果选择在 vmlinuz 文件中生成新的 .BTF ELF 节,稍后可以使用以下命令将其提取到外部原始 BTF 文件(例如 vmlinux.btf)中:

    llvm-objcopy --only-section=.BTF --set-section-flags .BTF=alloc,readonly vmlinux vmlinux.btf

这种灵活性确保了 BTF 文件生成的定制方法,增强了不同内核版本和发行版之间的兼容性。


朱慧君
大龄yaml工程师逼逼叨