登山则情满于山,观海则意溢于海。这篇文章主要讲述BPF BTF 详细介绍相关的知识,希望能为你提供帮助。
本文地址:https://www.ebpf.top/post/kernel_btf/
英文文档:https://www.kernel.org/doc/html/latest/bpf/btf.html
1. 介绍BTF(BPF 类型格式)是一种元数据格式,对与 BPF 程序 /map 有关的调试信息进行编码。BTF 这个名字最初是用来描述数据类型。后来,BTF 被扩展到包括已定义的子程序的函数信息和行信息。
调试信息可用于 map 的更好打印、函数签名等。函数签名能够更好地实现 bpf 程序/函数的内核符号。行信息有助于生成源注释的翻译字节码、JIT 代码和验证器的日志。
BTF 规范包含两个部分:
- BTF 内核 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_type
中的 name_off
字段指定了字符串表中的偏移。下面的章节将详细介绍每种类型的编码。
2.2.1 BTF_KIND_INT
struct btf_type
的编码如下:name_off
:任何有效的偏移量info.kind_flag
: 0info.kind
:BTF_KIND_INTinfo.vlen
: 0size
:int 类型的大小,单位是字节
btf_type
后面是一个 u32
,其位数排列如下::#define BTF_INT_ENCODING(VAL)(((VAL) &
0x0f000000)>
>
24)
#define BTF_INT_OFFSET(VAL)(((VAL) &
0x00ff0000)>
>
16)
#define BTF_INT_BITS(VAL)((VAL)&
0x000000ff)
BTF_INT_ENCODING
有以下属性:#define BTF_INT_SIGNED(1 <
<
0)
#define BTF_INT_CHAR(1 <
<
1)
#define BTF_INT_BOOL(1 <
<
2)
对于 int 类型,
BTF_INT_ENCODING()
提供了额外的信息: 带符号的 int, char, 或 bool。char 和 bool 编码主要用于良好的打印。对于 int 类型,最多可以指定一种编码。BTF_INT_BITS()
指定了这个 int 类型所持有的实际二进制位数。例如,一个 4 位的位域编码 BTF_INT_BITS()
等于 4。btf_type.size*8
必须等于或大于 BTF_INT_BITS()
,BTF_INT_BITS()
的最大值是 128。BTF_INT_OFFSET()
指定了计算该 int 值的起始位偏移。例如,一个位域结构成员有:- btf 成员的从结构体开始计算位偏移量为 100
- btf 成员指向一个 int 类型
- 该 int 类型有
BTF_INT_OFFSET()= 2
和BTF_INT_BITS() = 4
4
位,从 100+2
位开始。另外,比特域结构成员可以采用以下方式来访问与上述相同的比特位:
- btf 成员位偏移量 102
- btf 成员指向一个 int 类型
- int 类型有
BTF_INT_OFFSET()= 0
和BTF_INT_BITS() = 4
BTF_INT_OFFSET()
的初衷是为了提供灵活的位域编码的灵活性。目前, 对于所有的 int 类型,llvm 和 pahole 都生成了 BTF_INT_OFFSET() = 0
。文章图片
2.2.2 btf_kind_ptr
struct btf_type
编码要求:name_off
: 0info.kind_flag
: 0info.kind
: BTF_KIND_PTRinfo.vlen
: 0type
: 指针的指向性类型
btf_type
后面没有其他类型数据。2.2.3 BTF_KIND_ARRAY
struct btf_type
的编码要求:name_off
: 0info.kind_flag
: 0info.kind
: BTF_KIND_ARRAYinfo.vlen
: 0size/type
: 0,不使用
btf_type
后面有一个 struct btf_array
:struct btf_array {
__u32type;
__u32index_type;
__u32nelems;
};
struct btf_array
的编码:type
: 元素类型index_type
: 索引类型nelems\': 这个数组的元素数量(允许为
0\')
index_type
可以是任何常规的 int 类型(u8
, u16
, u32
, u64
, unsigned __int128
)。最初的设计包括了 index_type
,这是遵循了 DWARF 规范,它的数组类型有一个 index_type
。目前在 BTF 中,除了类型验证外,index_type
并没有使用。struct btf_array
允许通过元素类型链来表示多维数组。例如,对于 "int a[5][6]",下面的类型信息说明了这种链式关系。* [1]: int
* [2]: array, `btf_array.type = [1]`, `btf_array.nelems = 6`
* [3]: array, `btf_array.type = [2]`, `btf_array.nelems = 5`
目前,pahole 和 llvm 都将多维数组折叠成一维数组,例如,对于
a[5][6]
,btf_array.nelems
等于 30
。这是因为最初的用例是 map 良好打印,整个数组被打印出来,所以一维数组已经足够了。随着更多 BTF 使用的探索,pahole 和 llvm 可以被改变,以生成适当的多维数组的链式表示。2.2.4 btf_kind_struct 2.2.5 btf_kind_union
struct btf_type
编码要求:name_off
: 0 或偏移到一个有效的 C 标识符info.kind_flag
: 0 或 1info.kind
: BTF_KIND_STRUCT 或 BTF_KIND_UNIONinfo.vlen
: struct/union 成员的数量info.size
: struct/union 的大小,以字节为单位
btf_type
后面是 info.vlen
数量的 btf_member
的结构。struct btf_member {
__u32name_off;
__u32type;
__u32offset;
};
struct btf_member
编码:name_off
: 一个有效的 C 语言标识符的偏移type
: 成员的类型offset
: < 见下文>
kind_flag
没有设置,偏移量只包含成员的位偏移。注意,位域的基本类型只能是 int 或 enum 类型。如果比特字段大小为 32,基类型可以是 int 或 enum 类型。如果比特字段大小不是 32,基类型必须是 int,而 int 类型 BTF_INT_BITS()
编码比特字段的大小。如果
kind_flag
被设置,btf_member.offset
包含成员位域大小和位偏移。位域大小和位偏移的计算方法如下:#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >
>
24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val) &
0xffffff)
在这种情况下, 如果基础类型是一个 int 类型, 那么它必须是一个普通的 int 类型:
BTF_INT_OFFSET()
必须为 0。BTF_INT_BITS()
必须等于{1,2,4,8,16}. * 8
.
kind_flag
,并解释了两种模式存在的原因:https://github.com/torvalds/linux/commit/9d5f9f701b1891466fb3dbb1806ad97716f95cc3#diff-fa650a64fdd3968396883d2fe8215ff3
2.2.6 BTF_KIND_ENUM
struct btf_type
编码要求:name_off
: 0 或偏移到一个有效的 C 标识符info.kind_flag
: 0info.kind
: BTF_KIND_ENUMinfo.vlen
: 枚举值的数量size
: 4
btf_type
后面是 info.vlen
的数量的 struct btf_enum
结构。struct btf_enum {
__u32name_off;
__s32val;
};
btf_enum
的编码:name_off
: 偏移到一个有效的 C 标识符val
: 任何数值
struct btf_type
编码要求:name_off
: 一个有效的 C 语言标识符偏移info.kind_flag
: 0 代表 struct,1 代表 unioninfo.kind
: BTF_KIND_FWDinfo.vlen
: 0type
: 0
btf_type
后面没有其他类型数据。2.2.8 BTF_KIND_TYPEDEF
struct btf_type
编码要求:name_off
: 偏移到一个有效的 C 标识符。info.kind_flag
: 0info.kind
: BTF_KIND_TYPEDEFinfo.vlen
: 0type
: 在name_off
处可以通过名字引用的类型。
btf_type
后面没有其他类型数据。2.2.9 BTF_KIND_VOLATILE
struct btf_type
的编码要求:name_off
: 0info.kind_flag
: 0info.kind
: btf_kind_volatileinfo.vlen
: 0type
: 带有volatile
限定符的类型
btf_type
后面没有其他类型数据。2.2.10 BTF_KIND_CONST
struct btf_type
的编码要求:name_off
: 0info.kind_flag
: 0info.kind
: BTF_KIND_CONSTinfo.vlen
: 0type
: 带有const
限定词的类型
btf_type
后面没有其他类型数据。2.2.11 BTF_KIND_RESTRICT
struct btf_type
的编码要求:name_off
: 0info.kind_flag
: 0info.kind
: btf_kind_restrictinfo.vlen
: 0type
: 带有restrict
限定词的类型
btf_type
后面没有其他类型数据。2.2.12 BTF_KIND_FUNC
struct btf_type
的编码要求:name_off
: 偏移到一个有效的 C 标识符info.kind_flag
: 0info.kind
: BTF_KIND_FUNCinfo.vlen
: 0type
: 一个 BTF_KIND_FUNC_PROTO 类型
btf_type
后面没有其他类型数据。BTF_KIND_FUNC 定义的不是一个类型,而是一个子程序(函数),其签名由
type
定义。因此,该子程序是该类型的一个实例。BTF_KIND_FUNC 可以反过被
BTF_Ext_Section
(ELF) 中的 func_info 或 BPF_Prog_Load
的参数中引用 (ABI)。2.2.13 BTF_KIND_FUNC_PROTO
struct btf_type
编码要求:name_off
: 0info.kind_flag
: 0info.kind
: BTF_KIND_FUNC_PROTOinfo.vlen
: 参数的数量type
: 返回类型
btf_type
后面是 info.vlen
数量 struct btf_param
的结构。struct btf_param {
__u32 name_off;
_u32 type;
};
如果一个
BTF_KIND_FUNC_PROTO
类型被 BTF_KIND_FUNC
类型引用,那么 btf_param.name_off
必须指向一个有效的 C 标识符,除了可能代表变量参数的最后一个参数。btf_param.type
指的是参数类型。如果函数有可变参数,最后一个参数被编码为
name_off = 0
和 type = 0
。2.14 BTF_KIND_VAR
struct btf_type
编码要求:name_off
: 偏移到一个有效的 C 标识符info.kind_flag
: 0info.kind
: BTF_KIND_VARinfo.vlen
: 0type
: 变量的类型
btf_type
后面有一个 struct btf_variable
, 其数据如下:Sep 23, 2021struct btf_var {__u32linkage;
};
struct btf_var
的编码:linkage
: 目前只有静态变量 0,或全局分配的在 ELF 部分的变量 1
- 带或不带区段属性的静态变量
- 带有区段属性的全局变量
2.2.15 BTF_KIND_DATASEC
struct btf_type
编码要求:name_off
:与变量或.data/.bss
之一相关的有效名称的偏移量。
数据.bss/.rodata
中的一个。info.kind_flag
: 0info.kind
: BTF_KIND_DATASECinfo.vlen
: 变量的数量size
: 总的部分大小,以字节为单位(编译时为 0,由 BPF 加载器,如 libbp 修订为实际大小)
btf_type
后面是 info.vlen
数量的 struct btf_var_secinfo
结构。struct btf_var_secinfo {
__u32type;
__u32offset;
__u32size;
};
struct btf_var_secinfo
编码:type
: BTF_KIND_VAR 变量的类型offset
: 变量的段内偏移量size
: 变量的大小,以字节为单位
- BPF_BTF_LOAD:将 BTF 数据的 blob 数据加载到内核中
- BPF_MAP_CREATE:用 btf 键和值类型信息创建 map
- BPF_PROG_LOAD:用 btf 函数和行信息加载程序
- BPF_BTF_GET_FD_BY_ID:获得一个 btf fd
- BPF_OBJ_GET_INFO_BY_FD:返回 btf、func_info、line_info 和其它与 btf 有关的信息
Application:
BPF_BTF_LOAD
|
v
BPF_MAP_CREATE and BPF_PROG_LOAD
|
V
......
检查(introspection)工具:
......
BPF_{PROG,MAP}_GET_NEXT_ID (获取 prog/map ids)
|
V
BPF_{PROG,MAP}_GET_FD_BY_ID (获取 prog/map fd)
|
V
BPF_OBJ_GET_INFO_BY_FD (通过 fd 获取 bpf_prog_info/bpf_map_info)
||
V|
BPF_BTF_GET_FD_BY_ID (获取 btf_fd)|
||
V|
BPF_OBJ_GET_INFO_BY_FD (获取 btf 信息)|
||
VV
更好打印类型、函数签名和代码行信息等
3.1 BPF_BTF_LOAD
将一个 BTF 的 blob 数据加载到内核中。BTF_Type_String 中描述的 blob 数据,可以直接加载到内核中, 返回
btf_fd
至用户空间。3.2 BPF_MAP_CREATE
可以用
btf_fd
和指定的键/值类型 id 创建一个 map。__u32btf_fd;
/* fd pointing to a BTF type data */
__u32btf_key_type_id;
/* BTF type_id of the key */
__u32btf_value_type_id;
/* BTF type_id of the value */
在 libbpf 中,可以用额外的注解来定义 map,如下所示:
struct bpf_map_def SEC("maps") btf_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(struct ipv_counts),
.max_entries = 4,
};
BPF_ANNOTATE_KV_PAIR(btf_map, int, struct ipv_counts);
这里,宏 BPF_ANNOTATE_KV_PAIR 的参数是 map 的名称、键和值类型。在 ELF 解析期间,libbpf 能够提取 key/value type_id,并把它们自动地分配给 BPF_MAP_CREATE 属性。
3.3 BPF_PROG_LOAD
在 prog_load 过程中,func_info 和 line_info 可以被传递给 kernel,并为以下属性提供适当的值。
__u32insn_cnt;
__aligned_u64insns;
......
__u32prog_btf_fd;
/* fd pointing to BTF type data */
__u32func_info_rec_size;
/* userspace bpf_func_info size */
__aligned_u64func_info;
/* func info */
__u32func_info_cnt;
/* number of bpf_func_info records */
__u32line_info_rec_size;
/* userspace bpf_line_info size */
__aligned_u64line_info;
/* line info */
__u32line_info_cnt;
/* number of bpf_line_info records */
func_info 和 line_info 分别对应下面的数组:
struct bpf_func_info {
__u32insn_off;
/* [0, insn_cnt - 1] */
__u32type_id;
/* pointing to a BTF_KIND_FUNC type */
};
struct bpf_line_info {
__u32insn_off;
/* [0, insn_cnt - 1] */
__u32file_name_off;
/* offset to string table for the filename */
__u32line_off;
/* offset to string table for the source line */
__u32line_col;
/* line number and column number */
};
func_info_rec_size 是每个 func_info 记录的大小,而 line_info_rec_size 是每条 line_info 记录的大小。将记录的大小传递给内核,使其有可能在未来扩展记录本身。
下面是 func_info 的要求:
- func_info[0].insn_off 必须为 0。
- func_info 的 insn_off 严格按递增顺序排列,匹配 bpf func 的边界。
- 每个函数的第一个 insn 必须有一条指向它的 line_info 记录。
- line_info 的 insn_off 是严格递增的顺序。
#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >
>
10)
#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) &
0x3ff)
3.4 BPF_{PROG,MAP}_GET_NEXT_ID
在内核中,每个加载的程序、map 或 btf 都有一个唯一的 id。这个 id 在一个程序、map 或 btf 的生命周期内不会改变。
bpf 系统调用命令 BPF_{PROG,MAP}_GET_NEXT_ID 将 bpf 程序或 map 的所有 id,每个命令一个,分别返回到用户空间,因此,检查工具可以检查所有程序和 map。
3.5 BPF_{PROG,MAP}_GET_FD_BY_ID
检查工具不能使用 id 来获取程序或 map 的细节。需要首先获得一个文件描述符,以达到引用计数的目的。
3.6 BPF_OBJ_GET_INFO_BY_FD
一旦获得了一个程序/map fd,检查工具就可以从内核中获得这个 fd 的详细信息,其中一些是与 BTF 相关的。例如,
bpf_map_info
返回 btf_id
和键/值类型 id。bpf_prog_info
返回 btf_id
、func_info
、翻译后字节码的 line_info
,以及 jited_line_info
。3.7 BPF_BTF_GET_FD_BY_ID
有了从
bpf_map_info
和 bpf_prog_info
中获得的 btf_id
,bpf 系统调用命令 BPF_OBJ_GET_INFO_BY_FD 就可以获取到 btf fd。然后,使用命令 BPF_OBJ_GET_INFO_BY_FD,就可以重新获取最初使用 BPF_BTF_LOAD 命令加载到内核的 btf blob。有了 btf blob、
bpf_map_info
和 bpf_prog_info
,检查工具就拥有完整的 btf 信息,更好地打印出 map 键/值, func 签名和行信息,以及字节/jit 代码。4. ELF 文件格式接口 4.1 .BTF 部分
.BTF 部分包含 type 和 string 数据。这个部分的格式是与
BTF_Type_String
中描述的相同。4.2 .BTF.ext 部分
.BTF.ext 部分为 func_info 和 line_info 的编码,这些信息在加载到内核之前需要加载器处理。
.BTF.ext 部分的规定义在
tools/lib/bpf/btf.h
和 tools/lib/bpf/btf.c
文件中。目前.BTF.ext 部分的头部结构是:
struct btf_ext_header {
__u16magic;
__u8version;
__u8flags;
__u32hdr_len;
/* 偏移量基于本头部结构体的尾部 */
__u32func_info_off;
__u32func_info_len;
__u32line_info_off;
__u32line_info_len;
};
它与
.BTF
部分非常相似,它包含了 func_info
和 line_info
部分,而不是 type/string
部分。关于 func_info
和 line_info
记录格式的详细信息,请参见 BPF_Prog_Load
部分。func_info 的组织方式如下:
func_info_rec_size
btf_ext_info_sec for section #1 /* func_info for section #1 */
btf_ext_info_sec for section #2 /* func_info for section #2 */
...
func_info_rec_size
指定了生成 .BTF.ext
时 bpf_func_info
结构的大小。如下定义的
btf_ext_info_sec
是每个特定 ELF 部分的 func_info
的集合。struct btf_ext_info_sec {
__u32sec_name_off;
/* offset to section name */
__u32num_info;
/* Followed by num_info * record_size number of bytes */
__u8data[0];
};
这里,num_info 必须大于 0。
line_info 的组织方式如下:
line_info_rec_size
btf_ext_info_sec for section #1 /* line_info for section #1 */
btf_ext_info_sec for section #2 /* line_info for section #2 */
line_info_rec_size
指定了生成 .BTF.ext
时 bpf_line_info
结构的大小。bpf_func_info->
insn_off
和 bpf_line_info->
insn_off
的解释在内核 API 和 ELF API 之间是不同的。对于内核 API,
insn_off
是结构单元中的指令偏移量 bpf_insn
为单位的。对于 ELF API,insn_off
是部分(btf_ext_info_sec->
sec_name_off
)开始的字节偏移。5. 使用 BTF 5.1 bpftool map 更友好地打印
基于 BTF,map 的键/值可以根据字段来打印,而不只是打印简单的原始字节。这对于大的结构或如果数据结构有比特字段时,这一点特别有价值。例如,对于下面这个 map:
enum A {A1, A2, A3, A4, A5};
typedef enum A ___A;
struct tmp_t {
char a1:4;
inta2:4;
int:4;
__u32 a3:4;
int b;
___A b1:4;
enum A b2:4;
};
struct bpf_map_def SEC("maps") tmpmap = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct tmp_t),
.max_entries = 1,
};
BPF_ANNOTATE_KV_PAIR(tmpmap, int, struct tmp_t);
bpftool 可以像下面这样更好地打印:
[{
"key": 0,
"value": {
"a1": 0x2,
"a2": 0x4,
"a3": 0x6,
"b": 7,
"b1": 0x8,
"b2": 0xa
}
}
]
5.2 bpftool 程序打印(dump)
下面是一个例子,说明
func_info
和 line_info
如何帮助程序显示更好地显示内核符号名称、函数原型和行信息。$ bpftool prog dump jited pinned /sys/fs/bpf/test_btf_haskv
[...]
int test_long_fname_2(struct dummy_tracepoint_args * arg):
bpf_prog_44a040bf25481309_test_long_fname_2:
;
static int test_long_fname_2(struct dummy_tracepoint_args *arg)
0:push%rbp
1:mov%rsp,%rbp
4:sub$0x30,%rsp
b:sub$0x28,%rbp
f:mov%rbx,0x0(%rbp)
13:mov%r13,0x8(%rbp)
17:mov%r14,0x10(%rbp)
1b:mov%r15,0x18(%rbp)
1f:xor%eax,%eax
21:mov%rax,0x20(%rbp)
25:xor%esi,%esi
;
int key = 0;
27:mov%esi,-0x4(%rbp)
;
if (!arg->
sock)
2a:mov0x8(%rdi),%rdi
;
if (!arg->
sock)
2e:cmp$0x0,%rdi
32:je0x0000000000000070
34:mov%rbp,%rsi
;
counts = bpf_map_lookup_elem(&
btf_map, &
key);
[...]
5.3 验证器日志
下面是一个例子,说明
line_info
如何帮助调试验证失败的:/* The code at tools/testing/selftests/bpf/test_xdp_noinline.c
* is modified as below.
*/
data = https://www.songbingjia.com/android/(void *)(long)xdp->
data;
data_end = (void *)(long)xdp->
data_end;
/*
if (data + 4 >
data_end)
return XDP_DROP;
*/
*(u32 *)data = dst->
dst;
$ bpftool prog load ./test_xdp_noinline.o /sys/fs/bpf/test_xdp_noinline type xdp
;
data = (void *)(long)xdp->
data;
224: (79) r2 = *(u64 *)(r10 -112)
225: (61) r2 = *(u32 *)(r2 +0)
;
*(u32 *)data = dst->
dst;
226: (63) *(u32 *)(r2 +0) = r1
invalid access to packet, off=0 size=4, R2(id=0,off=0,r=0)
R2 offset is outside of the packet
6. BTF 生成你需要最新的 pahole 或 llvm(8.0 或更高版本)。pahole 作为一个 dwarf2btf 转换器。pahole 目前还不支持
.BTF.ext
和 btf BTF_KIND_FUNC
类型。例如:-bash-4.4$ cat t.c
struct t {
int a:2;
int b:3;
int c:2;
} g;
-bash-4.4$ gcc -c -O2 -g t.c
-bash-4.4$ pahole -JV t.o
File t.o:
[1] STRUCT t kind_flag=1 size=4 vlen=3# vlen 表示有 3 个成员变量
a type_id=2 bitfield_size=2 bits_offset=0# type_id = 2 即为 [2] 中的代表
b type_id=2 bitfield_size=3 bits_offset=2
c type_id=2 bitfield_size=2 bits_offset=5
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
对于编译对象为 bpf 目标的情况下,llvm 添加编译添加-g 选项参数可直接生成 .BTF 和 .BTF.ext。汇编代码(-S)能够以汇编格式显示 BTF 的编码。
-bash-4.4$ cat t2.c
typedef int __int32;
struct t2 {
int a2;
int (*f2)(char q1, __int32 q2, ...);
int (*f3)();
} g2;
int main(){ return 0;
}
int test(){ return 0;
}-bash-4.4$ clang -c -g -O2 -target bpf t2.c-bash-4.4$ readelf -S t2.o
......
[8] .BTFPROGBITS000000000000000000000247
000000000000016e0000000000000000001
[9] .BTF.extPROGBITS0000000000000000000003b5
00000000000000600000000000000000001
[10] .rel.BTF.extREL0000000000000000000007e0
000000000000004000000000000000101698
......-bash-4.4$ clang -S -g -O2 -target bpf t2.c
-bash-4.4$ cat t2.s
......
.section.BTF,"",@progbits
.short60319# magic0xeb9f btf_header
.byte1# version
.byte0# flags
.long24# hdr_len
.long0# type_off
.long220# type_len
.long220# str_off
.long122# str_len
.long0# BTF_KIND_FUNC_PROTO(id = 1)
.long218103808# 0xd000000
.long2
.long83# BTF_KIND_INT(id = 2)
.long16777216# 0x1000000
.long4
.long16777248# 0x1000020
......
.byte0# string offset=0
.ascii".text"# string offset=1
.byte0
.ascii"/home/yhs/tmp-pahole/t2.c" # string offset=7
.byte0
.ascii"int main(){ return 0;
}" # string offset=33
.byte0
.ascii"int test(){ return 0;
}" # string offset=58
.byte0
.ascii"int"# string offset=83
......
.section.BTF.ext,"",@progbits
.short60319# 0xeb9f
.byte1
.byte0
.long24
.long0
.long28
.long28
.long44
.long8# FuncInfo
.long1# FuncInfo section string offset=1
.long2
.long.Lfunc_begin0
.long3
.long.Lfunc_begin1
.long5
.long16# LineInfo
.long1# LineInfo section string offset=1
.long2
.long.Ltmp0
.long7
.long33
.long7182# Line 7 Col 14
.long.Ltmp3
.long7
.long58
.long8206# Line 8 Col 14
7. 测试内核 bpf selftest
test_btf.c
提供了大量与 BTF 相关的测试。推荐阅读
- psm_job缺库,程序无法正常启动
- 9.文本处理工具
- Spring Cloud Gateway 没有链路信息,我 TM 人傻了(上)
- 1.docker概述及其历史
- 云计算奇妙学习之旅第四期(华为计算虚拟化精讲)
- linux之systemctl命令
- 项目中Spring事务失效的场景问题排查
- mysql 查看用户的权限
- 树莓派安装JDK1.8并配置环境变量