eBPF结构体梳理


源码梳理

从bpf_check函数https://elixir.bootlin.com/linux/v6.14-rc5/source/kernel/bpf/verifier.c#L23039开始看起,这里我们会有一个全局的env:

然后经过若干检查之后会进入到do_check_subprogs函数,然后对于每一个subprog会执行一次do_check_common函数https://elixir.bootlin.com/linux/v6.14-rc5/source/kernel/bpf/verifier.c#L22162,这里会分配一个state结构体(相当于每一个subprog有一个):

关于state的几个成员进行解释:

curframe:当前栈帧

frame[]: 走到这个位置走过的所有调用栈,可以理解成backtrace;

frame[i]->regs:

first_insn_idx: 该subprog开始的位置;

last_insn_idx:

关于env此时我们需要关注:

cur_state:

subprog_info[]: 在check_subprogs函数中根据调用(call)划分子程序,这里边记录了所有子程序的开始与结束位置;

关于frame

curframe增加的地方在这里:https://elixir.bootlin.com/linux/v6.14-rc5/source/kernel/bpf/verifier.c#L9977

setup_func_entry函数:

可以看到,当发生调用的时候会增加curframe的值,并启用下一个frame结构来存放新的栈帧,所以基本可以确定frame就是用来记录函数调用栈的;就是发生call指令的时候才会走到这里,那么就相当于进入一个新的subprog了啊,也就是说每一个subprog会有一个state,但是并不意味着这个state不会走到别的subprog,这个state的frame就是作为一个栈,用来记录从该subprog开始走到的所有的subprog(函数调用);

函数调用时reg的关系

在setup_func_entry中调用了init_func_statehttps://elixir.bootlin.com/linux/v6.14-rc5/source/kernel/bpf/verifier.c#L2672:

重点关注其中的init_reg_state:

除了栈指针都标记为了未初始化的;并没有涉及参数的传递的问题啊;

在处理call指令的位置(check_func_call函数)发现setup_func_entry执行完之后会调用clear_caller_saved_regs函数:

该函数将r0 ~ r5标记为not_init,然后检查参数:

这里有一个赋值,像是参数的传递:

其实set_func_entry的第四个参数,会在初始化func_state之后调用:

附录

env

https://elixir.bootlin.com/linux/v6.14-rc5/source/include/linux/bpf_verifier.h#L701

state

https://elixir.bootlin.com/linux/v6.14-rc5/source/include/linux/bpf_verifier.h#L368

func_state(frame)


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录