BPF BTF 详解

1. 介绍

BTF(BPF Type Format)是内嵌在BPF(Berkeley Packet Filter)程序中的数据结构描述信息。BPF原本是用于数据包过滤的编程语言,但随着eBPF(extended BPF)的发展,它的用途已经扩展到多种内核子系统中,包括性能监测、网络安全和配置管理等。
BTF是为了实现更复杂的eBPF程序而设计的。其提供了一种机制,通过它可以将编程时使用的数据结构(如C语言中的结构体、联合体、枚举等)的信息嵌入到eBPF程序中。这样做的主要目的是为了让eBPF程序在运行时能够具有类型安全(Type Safety),同时也便于内核和用户空间的程序理解和操作这些数据结构。
在eBPF程序开发过程中,用户通常会在用户空间编写C代码,然后使用特定的编译器(如clang)编译这些代码为eBPF字节码。由于C程序中定义的复杂数据结构信息在编译为eBPF字节码过程中会丢失,因此BTF被设计来保留这些信息。当eBPF程序加载到内核时,BTF信息可以被内核使用,以确保程序操作的数据结构与内核预期的一致,从而保证程序的正确运行。
举个例子,如果eBPF程序需要访问内核数据结构,BTF就能够提供这些内核数据结构的确切布局,让eBPF程序能够安全而准确地读取或修改这些数据。
总之,BTF使得eBPF程序能更安全且方便地与复杂的数据类型互动,并有助于提高eBPF程序与内核间的兼容性和稳定性。

BTF(BPF 类型格式)是一种元数据格式,对与 BPF 程序 /map 有关的调试信息进行编码。BTF 这个名字最初是用来描述数据类型。后来,BTF 被扩展到包括已定义的子程序的函数信息和行信息。

调试信息可用于 map 的更好打印、函数签名等。函数签名能够更好地实现 bpf 程序/函数的内核符号。行信息有助于生成源注释的翻译字节码、JIT 代码和验证器的日志。

BTF 规范包含两个部分:

  • BTF 内核 API
  • BTF ELF 文件格式
    内核 API 是用户空间和内核之间的约定。内核在使用之前使用 BTF 信息对其进行验证。ELF 文件格式是一个用户空间 ELF 文件和 libbpf 加载器之间的约定。

类型和字符串部分(section)是 BTF 内核 API 的一部分,描述了 bpf 程序所引用的调试信息(主要是与类型有关的)。这两个部分将在 BTF_Type_String 章节中详细讨论。

2. BTF 类型和字符串编码

文件 include/uapi/linux/btf.h 提供了关于类型/字符串如何编码的更高层次的定义。

数据块(blob)的开头必须是:

struct btf_header {
    __u16 magic;
    __u8 version;
    __u8 flags;
    __u32 hdr_len;


    /* 所有的偏移量都是相对于这个头的末尾的字节 */
    __u32 type_off; /* 类型部分的偏移量 */
    __u32 type_len; /* 类型部分的长度 */
    __u32 str_off;  /* 字符串部分的偏移量 */
    __u32 str_len;  /* 字符串部分的长度 */
};

magic 数值是 0xeB9F,其在对大、小端系统上的编码有所不同,这可以用来测试 BTF 所在系统是否为大、小端系统。btf_header 被设计为可扩展的,当数据 blob 生成时, hdr_len 等于 sizeof(struct btf_header)。

2.1 字符串编码

字符串部分的第一个字符串必须以 null 结尾字符串。字符串表的其他部分有其他非 null 结尾的字符串连接而成。

2.2 类型编码

类型标识 0 是为 void 类型保留的。类型部分(section)是按顺序解析,每个类型以 ID 从 1 开始的进行编码。目前,支持以下类型:

#define BTF_KIND_INT 1          /* 整数 */
#define BTF_KIND_PTR 2          /* 指针 */
#define BTF_KIND_ARRAY 3        /* 数组 */
#define BTF_KIND_STRUCT 4       /* 结构体 */
#define BTF_KIND_UNION 5        /* 联合体 */
#define BTF_KIND_ENUM 6         /* 枚举 */
#define BTF_KIND_FWD 7          /* 前向引用 */
#define BTF_KIND_TYPEDEF 8      /* 类型定义 */
#define BTF_KIND_VOLATILE 9     /* VOLATILE 变量 */
#define BTF_KIND_CONST 10       /* 常量 */
#define BTF_KIND_RESTRICT 11    /* 限制性 */
#define BTF_KIND_FUNC 12        /* 函数 */
#define BTF_KIND_FUNC_PROTO 13  /* 函数原型 */
#define BTF_KIND_VAR 14         /* 变量 */
#define BTF_KIND_DATASEC 15     /* 数据部分 */

注意,类型部分是对调试信息进行编码的,而不是类型自身。BTF_KIND_FUNC 不是一个类型, 它代表一个已定义的子程序。

每个类型都包含以下常见的数据:

struct btf_type {
    __u32 name_off;
    /* "info" 位值设置如下:
     * 第 0-15 位:vlen(例如结构的成员)
     * bits 16-23: unused
     * bits 24-27: kind (e.g. int, ptr, array...etc)
     * bits 28-30 位:未使用
     * bits 31: kind_flag, 目前被 struct, union 和 fwd 使用
     */
    __u32 info;
    
    /* "size" 被 INT、ENUM、STRUCT  和 UNION 使用
     * "size" 用于描述类型的大小
     *
     * "type“ 被  PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, FUNC 和 FUNC_PROTO 使用。
     * "type" 是指另一个类型的 type_id
     */
    union {
            __u32 size;
            __u32 type;
    };
};

libbpf 库底层使用的结构:

struct btf {
	void *data;
	struct btf_type **types;
	u32 *resolved_ids;
	u32 *resolved_sizes;
	const char *strings;
	void *nohdr_data;
	struct btf_header hdr;
	u32 nr_types;
	u32 types_size;
	u32 data_size;
	refcount_t refcnt;
	u32 id;
	struct rcu_head rcu;
};

对于某些类别来讲,通用数据之后是特定类型的数据。在 struct btf_type 中的 name_off 字段指定了字符串表中的偏移。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章