eBPF程序注入到内核中的流程,现在就带你研究(上)

系列目录

1. 疑惑

2. vfsstat_bpf__open

2.1 bpf_object__open_skeleton

2.2 bpf_object__open_mem/bpf_object_open

2.3 OPTS_VALID检查参数合法性

2.4 bpf_object__new新建bpf_object对象

2.5 bpf_object__elf_init初始化elf文件

2.6 bpf_object__elf_collect收集各个段落信息

2.7 bpf_object__init_maps初始化maps

2.8 bpf_object_init_progs初始化程序programs

2.9 open bpf总结

3. bpf_object__load_skeleton加载bpf

4. bpf_object__attach_skeleton附着bpf程序

5. 触发bpf程序

6 .总结

1. 疑惑

学习bpf过程中是带着问题去学习的:

1、我们写的bpf程序为什么可以对内核生效,代码是怎么注入到内核的?

2、libbpf相当于一个框架,那它又是怎么设计构建的呢?

3、什么是elf文件格式?vfsstat.bpf.o的内容包括什么信息?

在学习bpf之前,是不知道什么是elf文件的(也没关注)。

4、bpf可以用来干嘛,它有什么价值,到底能够做哪些事情?

问题很多,下面还是老老实实先看代码,先把一条线弄清楚,

我们以libbpf-tools的vfsstat为例子,继续深入探究一下这个bpf程序都干了些啥?

2. vfsstat_bpf__open

vfsstat_bpf__open打开bpf程序,这里我们主要关注的是libbpf框架和elf文件格式的处理,选择下面的流程来作为讲解内容:

8e326e52f27a565366e0c2e61ab9a8b6.png

2.1 bpf_object__open_skeleton

1、构建bpf_object_open_opts skel_opts,默认只有sz(bpf_object_open_opts结构体的大小)、object_name = "vfsstat_bpf"(skeleton的名字)

2、bpf_object__open_mem会从根据vfsstat.bpf.o的内容构建bpf_object(关键流程)

3、对于s->maps[0].mmaped进行赋值

e6cfe167f2beebb5196096a4ec6ef8f6.png

85e7e984e602ff29963554aa0f513965.png

079914bc632e2f7e2db4f9b0a0586e83.png

5af810a6dd0fae7af96861eddd4130e8.png

2.2 bpf_object__open_mem/bpf_object_open

=> bpf_object__open_mem

检查obj_buf、obj_buf_sz是否合法,同时调用bpf_object_open

cbbe3e39d943d7b32facc5d101e70084.png

=> bpf_object_open

1、OPTS_VALID检查参数是否合法,参数大小opts->sz不能小于size_t,额外长度需要都是0(超过bpf_object_open_opts__last_field的属于额外参数)

2、bpf_object__new新建bpf_object对象

3、bpf_object__elf_init初始化elf文件

4、bpf_object__elf_collect收集每个段落的信息

5、bpf_object__init_maps初始化map相关数据

6、bpf_object_init_progs遍历初始化每个bpf_program

e93ae3078f5c3a4da44ec828c7786cea.png

3b168051d25ab42ea1fa175a738fd3fb.png

d1499a0f624462c314fad1a1dcdc2eb0.png

4343e71e6be63da4add75ec7f3677f02.png

521f4cce5ca1513f8fefc49b3f60211d.png

2.3 OPTS_VALID检查参数合法性

OPTS_VALID这是一个宏,用于检查bpf各类参数是否合法,后续有额外参数传入需要注意这点

1、关于OPTS_VALID和offsetofend的宏定义如下

49079d9119c77fef2aba9f2c5a3f23df.png

2、 OPTS_VALID(opts, bpf_object_open_opts)扩展之后是

f9f624ded7787abfbab57e95e9dbf041.png

3、bpf_object_open_opts__last_field是bpf_object_open_opts中的kernel_log_level元素

6ae9021e10da489a0ba807a9c02f89fe.png

4、于是offsetofend(struct bpf_object_open_opts, bpf_object_open_opts__last_field)代表

bpf_object_open_opts从开始到kernel_log_level结尾的偏移(包括kernel_log_level的大小)

0a4e0cb75e08591325a12b97150dd4aa.png

5、libbpf_validate_opts会检查:

bpf_object_open_opts的sz必须大于等于sizeof(size_t)、超过元素type##__last_field的sz都必须是0

afe60d2122dc3915932ab0b85575ced4.png

2.4 bpf_object__new新建bpf_object对象

1、给bpf_object对象分配空间,

2、初始化结构体elf_state efile中的obj_buf(vfsstat.bpf.o的文件实际内容)、obj_buf_sz(vfsstat.bpf.o的文件实际内容的大小)

1922d66da4ed3479ef374d1d8285a663.png

50144b4539fef68ef6f56bcda1c1e036.png

2.5 bpf_object__elf_init初始化elf文件

1、efile.elf需要是NULL(没有初始化过)

2、elf_memory从obj_buf中读取elf文件(efile.elf),里面包括Elf64_Ehdr(elf文件头)、段落详细数据、段落简要数据数组

3、elf->kin必须是ELF_K_ELF类型

4、获取字符串表的id(elf_getshdrstrndx),并初始化字符串表的原始数据elf_rawdata

5、ebpf程序必须满足e_type = 1(ET_REL可重定向的文件), e_machine = 247(EM_BPF bpf的程序)

ca4e291769cd156960354dab00b9af19.png

4d807d9bbbb4f597e016f3669a4a399c.png

08f1759e7bb99ceb6ae073c49d6e758d.png

e15ff10052ff7a168206f85fa3bbd446.png

b53395bf2db5e9ee44309f2a1302e444.png

2.5.1 elf_memory/__libelf_read_mmaped_file

1、elf_memory这里已经转入libelf库中

146f2fa4412f2f10cbc2d4591b09863c.png

2、从内存中读取elf文件

1) determine_kind确定一下elf的文件类型

2) file_read_elf读取elf的文件

e9f1280df2b5ff3b66b2cb6e66d19c70.png

615e7cf8418e7a920e8961f38ebe8fbf.png

3、查找elf文件的类型

1) elf分为ELF_K_ELF(前面4个字节是"\177ELF"开头)、ELF_K_AR(前面8个字节是"!\n")

2) elf文件(后面指的elf文件都是ELF_K_ELF类型),文件开头是Elf64_Ehdr,

前面4个字节是"\177ELF"开头、第5个字节是eclass(文件类型,此处是0x02代表64bit)、

第6个字节是data(代表大端小端,此处是0x01代表小端格式)、第7个字节version(代表elf的版本号,此处是0x01代表版本号)。

elf的文件头具体如图:“ELF file header”

4d40a85ee784bed4550771fccae671e8.png

3fe84c4122d7f38635f68cb1d90c440f.png

7f6b88c432661b838ad30c7472e3071c.png

2.5.2 file_read_elf

1、先将vfsstat.bpf.o中的Elf64_Ehdr贴出来看一下,

=>如它的二进制数据如下图:

c7036f11999274431a56dede4e8641fa.png

=>转换成Elf64_Ehdr如下

45332fd73d6213c977d38806955bfe47.png

2、file_read_elf函数

1) 读取elf文件的时候,做32/64位、大端/小端检查

2) allocate_elf给elf分配内存和初始化话elf对象(extra额外需要分配的内存是44 * sizeof (Elf_Scn),

用来存储struct Elf_Scn data[0]的内容(elf->state.elf32.scns.data))

3) 根据map_address(obj_buf)和offset(0)获取Elf64_Ehdr *ehdr(elf的文件头)并赋值给elf->state.elf64.ehdr

4) ehdr(elf的文件头) + e_shoff(elf的文件头结束位置) => 获取elf->state.elf64.shdr(elf的section文件头)

5) 用shdr(elf的section文件头)初始化elf->state.elf64.scns.data[](section data)

dea12a87c3eb1e783edebd155adfc1ab.png

a18ee226c088cf59f48a846608ac628e.png

082508c02c3b30804aa978481316a7b8.png

f370a5816dfbc4b4ca76edea8cad7bcf.png

e01a64621623b3c246a55106ffd4111e.png

a384b54b540966193ad7f85b51ec8835.png

3c155607ca64877a0ed0f4e0e64283d3.png

76e7eb4fdcbf255cfd1bb85ea360539d.png

d03f024e37cfe2cff279a98781bb1dc1.png

3、关于Elf64_Shdr *shdr的格式如下图:

a7b7c3c4101aba5f2bf71bef716dd906.png

4、关于shdr段落头的二进制如下图:

5b8b7a8d4f2cf580e709f912819e0cb9.png

=> 实际前面4个段落头的内容如下

6a3b717f2ae5bb7abd0e882eca94a4ea.png

c4a149893def8c437aba0e0eeba4a560.png

2.5.3 elf_rawdata

elf_getscn函数其实就是取出的file_read_elf中的elf->state.elf64.scns.data[cnt],

其中cnt就是elf_getscn传入的idx(对应此处的字符串表的id:obj->efile.shstrndx = 1)

3dc83a8ac8f59f588bba29ddbf2bf663.png

1、elf_rawdata

如果该段落没有读取过,则调用__libelf_set_rawdata进行原始数据rawdata的读取

b17fe16ca39f115489c9a928f0db07d7.png

2、__libelf_set_rawdata_wrlock读取段落

以字符串表格为例

1) 读取原始数据的基地址scn->rawdata_base = scn->rawdata.d.d_buf = obj_buf + 0 + 10600(shdr[1]中的offset)

2) 设置原始数据Elf_Data的大小scn->rawdata.d.d_size = 607,类型scn->rawdata.d.d_type = ELF_T_BYTE(0),

偏移量scn->rawdata.d.d_off = 0,版本scn->rawdata.d.d_version = 1

3) scn->rawdata.s指向Elf_Scn *scn自己,设置已经读取了data_read = 1,修改Elf_Scn的flags = ELF_F_FILEDATA = 0x100

a83f09bcc3a2d50f1dd53e8dbfe33871.png

d5acfe78eefec0c9bd8289d9528c80e3.png

7cb902f7fb1a787b7da35e2b5024ebd8.png

fecc7c073ec463453451e0b855076a27.png

422e4edcf4bc01e8c0f653f33c9f153e.png

d34a8a7950a43417c2c2d5a1ab1fb64e.png

9e7dac08a4a03f6315f6604b88e42b5a.png

2.6 bpf_object__elf_collect收集各个段落信息

1、elf_nextscn先遍历一次elf的section找到对应的符号表的段落,如本例子中的shdr[43]

2、通过elf_sec_data获取字符串段落转换后的数据,然后初始化obj->efile的symbols(elf_sec_data取得的数据)、

symbols_shndx(符号表的段落ID)、strtabidx(符号表中字符串表的段落ID)

3、有了符号表之后,再次遍历所有的段落(ignore_elf_section跳过.strtab、.text(section size = 0)、.debug_、.rel.debug_***、".rel.BTF" ".rel.BTF.ext"),

elf_sec_data读取每个段落数据,根据不同的段落名字name(使用elf_sec_str读取)、段落类型sh_type做处理

4、针对程序段落,通过bpf_object__add_programs初始化程序段落

c60e61f727560fb5ead618efd8dec82a.png

021a513cd65af463680389416de6a31a.png

768b409b77e4579ddb2d920360b2efc6.png

2d08ae8d12fc3cacd5656260a3816aa4.png

3be00a0c5003d836d747e25b7c202e91.png

fb6059ce36884857c02849154c5e5149.png

2c654c6f5700f23a99a9d94faf3f9415.png

72bfed8eecc63ee3fa33a31c01f606d4.png

223087b3a067054c801eced9e94f4d3c.png

20f444cd738d69ae8758efbe4d067b10.png

2a7e77bc4340ac46a5d7eddc06bcd504.png

ee6dbc06286cdf8eca2a3f6a4a033241.png

8fccfe4dd7443d486faabfeab0382b92.png

0d058a368388725910b1dd7326400ba2.png

4d51cafa6c3978a80718f8ca1b12b763.png

2.6.1 elf_sec_data获取段落数据

1、传入的是scn = scns.data[43],读取的是符号表段落(".symtab")的信息

大致流程如下

elf_sec_data(libbpf.c) -> elf_getdata(elf_getdata.c) -> __elf_getdata_rdlock-> __libelf_set_rawdata_wrlock/__libelf_set_data_list_rdlock

__libelf_set_data_list_rdlock -> convert_data

385647f775348146818028cd3c16986d.png

2、__elf_getdata_rdlock

1) 如果该段路还没有初始化过原始数据rawdata,则调用__libelf_set_rawdata_wrlock进行初始化

2) 接着调用设置段落数据函数__libelf_set_data_list_rdlock

c8c063a3cadb22c87d41f19a73c0d3af.png

fc96d56985f9336f3976bf7c91a65515.png

3、__libelf_set_data_list_rdlock

如果存在段落数据则通过convert_data读取段落数据

686c5fea383971c9acb306c41a4a16f5.png

c5ff21bdfd8185347180b4a016e57686.png

4、convert_data

根据大小端对齐初始化转换后的数据scn->data_list.data.d

724c184c7c9a688adf9fa07724a7d37a.png

232354b439dd6017c639e27ef7c535ef.png

db0721929e44d1630e8a17e01170f424.png

579ffed8d7ba38951a3ced818d8ea719.png

2.6.2 elf_sec_str通过名字的偏移地址获得对应字符串段落中的名字

关于段落名字是从哪里来的,这里解释一下

1、elf_sec_str传入的是sh_name(段落头的名字的偏移地址,是一个数字)

其中bpf_object__elf_init的elf_getshdrstrndx获取shstrndx(字符串的段落ID) = e_shstrndx(elf文件头中的字符串段落id元素) = 1

c91269d986012373f3a776f2efff1d05.png

2、根据偏移offset从字符串段落中读取对应的字符串地址,如&strscn->rawdata_base[offset]

3fac384146bf307bdcca3135cab7f23c.png

46f503a9be59798212cbb2d8791df916.png

68bad420b3b7b4b70a833b89474bb413.png

713679b66895ce4a7fff5e91a8dffc22.png

9d988b782be0e61e361c75e491d22a9f.png

2639ac9f73760ad69bb7e645e7cea0e9.png

3、关于找到的如字符串段落本身shdr[1]的sh_name = 574(字符串段落中的偏移位置),

而字符串段落本身shdr[1]的sh_offset = 10600(字符串段落本身在bfp.o中偏移位置),

那么这个字符串对应*.bpf.o的位置是10600 + 574 = 11174‬ = 0x2BA6,

我们来看一下*.bpf.o 0x2BA6这个位置的是什么内容,如下图"字符串表的名字.strstab":

91066df73c08305baa451f49935caa3b.png

=>

从上面可以知道

sh_name = {int} 574代表的就是.strtab,也就是字符串段落shdr[1]的名字就是“.strtab”,

其它段落名字也是一样的,可以从这里的偏移找到字符串

2.6.3 bpf_object__add_programs初始化程序段落

1、该函数主要做了这些事情:

1) 遍历所有的符号Elf64_Sym数组(此处32个),找到shdr[3]对应的符号Elf64_Sym d_buf[20]

(st_shndx需要等于sec_idx,st_info后4位需要是STT_FUNC)

2) 根据符号Elf64_Sym d_buf[20]的st_name查找字符串段落找到该符号的函数名字,并打印如:

“libbpf: sec 'kprobe/vfs_read': found program 'kprobe_vfs_read' at insn offset 0 (0 bytes), code size 6 insns (48 bytes)”

上面的意思是找到段落'kprobe/vfs_read'程序,名字是kprobe_vfs_read,指令集偏移地址是0,一共有6条指令。

其中符号Elf64_Sym中st_size代表指令集总大小、st_value代表指令集的偏移量(此处是0,则代表段落原始数据shdr[3]开始就是指令数据)

3) 指令集大概如下面形式,每个指令8个字节:

f49995b64b4e1604414642a818b19b75.png

4) 所有bpf程序的都依次存放在obj->programs中

5) data(shdr[3]的sh_offset) + sec_off(d_buf[20]的st_value)得到的就是insn_data(该程序的指令集的基地址)

6) 调用bpf_object__init_prog初始化bpf_program(bpf程序)

5bdb0b79a28b44e0bcd79ac0a1137ddf.png

da8426a9a4518eef10c774fd53cee626.png

efb0633972b8d667d929bc5dc96b085d.png

34ef0b6941fb4514990f10f6b549eeb8.png

c1c56c25fd310587211f9a4c8edfef7d.png

d1de5903f0d89a4d21680a90c1ce343b.png

2、bpf_object__init_prog初始化bfp程序

=> 设置段落ID(sec_idx)、指令偏移(sec_insn_off)、指令个数(sec_insn_cnt)、是否加载标签(load)、段落名字(sec_name)、

函数名字(name)、报错指令集合的位置insns

df96a5b4a07f247c4497db85349f6d32.png

9fc77c0bef8411bb4561cd51aa7565d8.png

43209e64229d37870c991c6f2761addb.png

3、来看一下shdr[3]: sec_name = kprobe/vfs_read的指令数据是怎么样的

=> 指令原始数据如下图:

04df0f116eca726c24bdde6aa59d164e.png

=> 再来看一下这个函数(宏定义转换过后的):

75d33852fdc6d64c624a3da42e892aa3.png

=> 从sec_data->d_buf = sh_offset = 64 = 0x40开始的48个字节如下,这就是函数的指令,

下面是用llvm-objdump-11 -d /data/vfsstat.bpf.o截取出来的信息(可以看到部分指令的操作):

a59b3262abf92f76f6ad23b23ee89e1c.png

=> 转换成指令集的形式:

3ff12a72194fa62af5be4b60f7bd8815.png

334cfa1efc14c0b56a9b22f54ffa62ff.png

4、各个指令的含义

1) 第一条指令:

544a8199ed3a965f95ca17a23abf1a10.png

=>使用strace -e bpf -v -s 256 /data/vfsstat_bin 2 3指令查看指令操作码

{code=BPF_ALU64(64 bit)|BPF_K(32位立即数)|BPF_MOV(移动), dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0x1},

意思是:

BPF_ALU64: 0x07,64位计算指令(指令详情可以查看https://www.kernel.org/doc/html/latest/bpf/instruction-set.html)

BPF_K: 0x00,基于32位立即数作为源操作数

BPF_MOV: 0xb0,移动指令dst(目的操作寄存器) = src(源操作寄存器/数)

code = 0xb7 = BPF_ALU64(0x07)|BPF_K(0x00)|BPF_MOV(0xb0)

=> 于是上面的意思就变成了: dst_reg(r1) = imm(1) => r1 = 1

2、第二、三条指令

c75f0cc67100b2686dd41ebc65f4666d.png

=> 指令操作码如下:

{code=BPF_LD(0x00)|BPF_DW(0x18)|BPF_IMM(0x00), dst_reg=BPF_REG_2, src_reg=BPF_REG_2, off=0, imm=0x6},

{code=BPF_LD(0x00)|BPF_W(0x00)|BPF_IMM(0x00), dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0},

BPF_LD: 0x00装载操作,code的格式是:mode(3 bits) + size(2 bits) + instruction class(3 bits)

(

BPF_LD, BPF_LDX, BPF_ST, and BPF_STX这几个存储加载寄存器都是这个格式,

其中mode必须是BPF_IMM(0x00立即数)、BPF_ABS(0x20绝对的)、BPF_IND(0x40间接的)、BPF_MEM(0x60常规加载存储)、BPF_ATOMIC(0xc0自动操作)之一,

其中size必须是BPF_W(0x00一个字节)、BPF_H(0x08半个字节)、BPF_B(0x10一个byte)、BPF_DW(0x18双字节)

)

=> 于是上面的意思就变成了

dst_reg(r2) = imm64(这个值是在运行的时候生成的立即数)

3、第4条指令

91b438128013c0935d603246ad438eb0.png

=> 指令操作码如下:

{code=BPF_STX(0x03)|BPF_DW(64 bit的操作)(0x18)|BPF_XADD(加)(0xc0), dst_reg=BPF_REG_2, src_reg=BPF_REG_1, off=0, imm=0},

BPF_STX: 0x03,存储寄存器的值

(

LDR, [

]: load是将源数据address装入目的寄存器Destination

STR, [

]: store是将寄存器Destination的内容,存储在内存里面address

)

BPF_XADD:0xc0,在内核类似于atomic_add(),原子(lock)的相加

BPF_DW:0x18,双字节64位操作

=> 上面的意思是:

lock *(u64 *)(dst_reg(目标寄存器r2) + off(0)) += src_reg(源寄存器r1)

//如果是BPF_ADD,则不带lock:*(u64 *)(dst_reg + off16) += src_reg

4、第5条指令

3eaa8b90529f9662727b07d7586651e1.png

=> 指令操作码如下:

{code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0},

=> 上面的意思是:

BPF_ALU64|BPF_K(32位立即数)|BPF_MOV(移动)

dst_reg(r0) = imm(0)

//r0也是保存返回值的寄存器

5、第6条指令

f6c9efb4962093d025cdaa9f7c750448.png

=> 指令操作码如下:

{code=BPF_JMP|BPF_K|BPF_EXIT, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}],

BPF_JMP: 0x05,64位的跳转指令

BPF_K: 0x00,32位立即数操作

BPF_EXIT:0x90,函数或者程序返回

=> 上面的意思是:

return函数返回

6、再回头解释一下上面指令对应的代码

5c911ee9e46884ab0ffdef00468d7c31.png

2.6.4 bpf_object__init_btf初始化btf段落

1、btf段落(".BTF")是shdr[34]、btf ext段落(".BTF.ext")是shdr[36]

7c3cb247edf44e70049d27359ae13346.png

2、bpf_object__init_btf

根据btf/btf_ext的源数据地址和大小新建btf对象(btf__new)、btf_ext对象(btf_ext__new)

af8ad021463939854e5dd5a428fc6abb.png

3、btf__new新建btf对象

1) btf_parse_hdr解析btf的头

2) btf_parse_str_sec判断btf的string段落是否合法

3) btf_parse_type_sec解析的type类型

fc491039db69486ac6168b72e2b32bd6.png

d99ee90b52eb38c7ad667ab100d6338a.png

f3b6eae573831b8d27825f3c6a0e4bef.png

4、btf_parse_hdr解析btf的头

=> btf头的格式是下面形式:

fb8cb7ec05d5a32edff572a8e9d54620.png

=> 本例中的btf头如下:

e97743ba81c786e3e96a0fc27f85529d.png

=> 解析btf数据头,查看是否合法,如`btf魔术头必须是0xeB9F`

f462aef57aa6da53966dad33c1ca8388.png

a9ad7bb0c75b34b1f43d89e2b99636ad.png

5、btf_parse_type_sec解析btf的type

=> 每个btf type的类型是,其中btf类型判断使用的就是info

094e9a5f59bed1341c4013fc92953b95.png

=> btf_parse_type_sec遍历每一个btf type(btf_type_size),并将数据保存起来

ecc3f80ab95df062e37a8195ea1e426d.png

b2dadea7703a6ddadf664b05b52840c2.png

=> btf_type_size根据info中的不同内容获取btf type的size的大小

098af11020b96f22859c751ddd44c6f2.png

6bc8a912d40bf76076b3f27e90af4514.png

如本例中前面2个btf type如下:

20ce7abe0a0bb62c244ecd9a0b09670f.png

2.7 bpf_object__init_maps初始化maps

在bpf程序中,maps是非常重要的,这个是bpf程序传输数据的通道,这里简单提一下

1) bpf_object__init_user_maps根据符号表初始化bpf_map

2) bpf_object__init_user_btf_maps初始化btf的map相关的

3) bpf_object__init_global_data_maps初始化SEC_DATA、SEC_RODATA、SEC_BSS段落的map数据(如全局变量__u64 stats[]就在SEC_BSS中),

构建bpf_map,对象mmaped存储的是maps的数据

4) bpf_object__init_kconfig_map初始化EXT_KCFG(.kconfig)相关的map

5) bpf_object__init_struct_ops_maps初始化.struct_ops相关的maps

202f47c01d71bbb8a9275006506aa1f4.png

2.8 bpf_object_init_progs初始化程序programs

这里将会出现SEC("kprobe/vfs_read")的处理流程

1、bpf_object_init_progs遍历所有的programs(bpf_program数组)

find_sec_def主要是通过sec_name(段落名字)去找到对应的程序处理函数、程序类型prog_type

95bfcdacab44b830b2ffaad5b166f307.png

6d8ffcbcf050fbf2d1a9acb1ec64fa8a.png

2、find_sec_def从section_defs找到和段落名字匹配的bpf_sec_def(bfp段落默认处理结构体)

5de6ec2acebb0d68620fca992365a54d.png

3、section_defs数组目前支持如下:

其中kprobe函数对应的是attach_kprobe

ac0295ea4c3a98aa3d7d605b1af2b641.png

d1ef470c0e3bdf3ec83035aa8a6e905f.png

ab793413a20e735ce02fc62bab219db3.png

b8539cc2e829b08adecd71437e039e79.png

4、bpf_sec_def(bfp段落默认处理结构体)

dde0076ac682fc6767ed8ab8b8e4b7d7.png

5、kprobe宏定义展开

#define SEC_DEF("kprobe/", KPROBE, 0, SEC_NONE, attach_kprobe)

相当于 =>

955b7811e31e3d263dbe88e69ec045f3.png

2.9 open bpf总结

上面讲完了open部分,open主要是libbpf从vfsstat.bpf.o源数据(读取使用libelf)中构建bpf程序、bpf maps等,

这部分不涉与内核沟通,只是准备环境

下章将在下周五发布,请持续关注~

一文搞定Android VSync机制来龙去脉

一文了解Vulkan在移动端渲染中的带宽与同步

AMD高保真超分算法1.0解密

ed107ec427d445675521a9fbff5e7289.gif

长按关注内核工匠微信

Linux内核黑科技| 技术文章| 精选教程

猜你喜欢

转载自blog.csdn.net/feelabclihu/article/details/132506088
今日推荐