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 字段指定了字符串表中的偏移。

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