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