上一篇文章中,我们讲到如何通过 BPF 获取运行中的 C 程序的函数参数。
对于留下的几个问题,我们来做下解答:
为什么定义了 `struct params` ?
我们定义了一个新的结构体,它的布局和 C 程序的函数参数一致,字段名称无所谓,就是为了让它和用户输入的参数匹配,方便在程序中读取。
后面我们可能会讲解如何获取 C++ 类指针的参数,就能看到布局一致是多么重要,因为涉及到继承、虚函数等。
SEC("uprobe/user_func") 是什么 ?
这是一个 Section 的定义,向 libbpf 表明这是一个入口函数。Section 名称以 `/` 分隔,合法的前缀有 `uprobe`/`kprobe`/`tracepoint` 等。
readelf -S uprobe.bpf.o
There are 13 section headers, starting at offset 0x758:
Section Headers:
Name Type Address Offset
Size EntSize Flags Link Info Align
0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
1] .text PROGBITS 0000000000000000 00000040
0000000000000000 0000000000000000 AX 0 0 4
2] uprobe/user_func PROGBITS 0000000000000000 00000040
00000000000000a0 0000000000000000 AX 0 0 8
BPF_KPROBE 是什么 ?
这是 libbpf 中定义的一个宏,它隐藏了从上下文 `struct pt_regs *ctx` 中提取输入参数的细节。
本质上它就是调用 `PT_REGS_PARMn` 来提取参数。
int BPF_KPROBE(uprobe, struct params *x)
{
...
等价于
int uprobe(struct pt_regs *ctx)
{
struct params *x = PT_REGS_PARM1(ctx);
...
它还有一个变体 `BPF_KPROBE_SYSCALL`(这个宏上个月由笔者提交 :) ),用来提取系统调用的输入参数,用法和 `BPF_KPROBE` 一样,只是依赖于 libbpf CO-RE 的能力。
bpf_probe_read_user 是什么,作用是什么 ?
这是一个 BPF 的辅助函数,作用就是从用户地址空间读取常量大小的数据,其实现大致就是包装了 copy_from_user(参见 Linux阅码场 的文章:宋宝华:Linux为什么一定要copy_from_user)。
它的存在就是因为 BPF 存在很多限制,我们不能直接访问一个不安全的指针。
当然,这些限制在逐渐减少,后面我们会看到,新的内核下新的程序类型已经允许对指针进行解引用了。
为什么需要 LICENSE ?
IMNAL,请参见文档:https://github.com/torvalds/linux/blob/master/Documentation/bpf/bpf_licensing.rst#using-bpf-programs-in-the-linux-kernel
内核接受以下与 GPL 兼容的协议:
static inline int license_is_gpl_compatible(const char *license)
{
return (strcmp(license, "GPL") == 0
|| strcmp(license, "GPL v2") == 0
|| strcmp(license, "GPL and additional rights") == 0
|| strcmp(license, "Dual BSD/GPL") == 0
|| strcmp(license, "Dual MIT/GPL") == 0
|| strcmp(license, "Dual MPL/GPL") == 0);
}
在 BPF 程序加载(`BPF_PROG_LOAD`)阶段,内核会验证程序调用的辅助函数是否与声明的许可证协议兼容,如不兼容,则拒绝加载。