ebpf: BPF_FUNC_map_lookup_elem 调用约定
ebpf: BPF_FUNC_map_lookup_elem calling convention
查看内核的 sample/bpf/sock_example.c
:
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol) /* R0 = ip->proto */),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */
BPF_EXIT_INSN(),
};
我知道 eBPF
设置寄存器 r1-r5 来保存 BPF 助手的参数。我不明白的是为什么要将地图 fd
传递给 BPF_FUNC_map_lookup_elem
?根据 helpers code :
const struct bpf_func_proto bpf_map_lookup_elem_proto = {
.func = bpf_map_lookup_elem,
.gpl_only = false,
.pkt_access = true,
.ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MAP_KEY,
};
表示两个参数都是指针,none是map fd。除非,我正在查找错误的代码?
在用户 space 中编写程序时的文件描述符,但后来由验证器替换为指向映射的指针。
编写 eBPF 程序时的文件描述符
您在用户 space 中编写您的 eBPF 程序,其中您没有任何指向地图的地址指针。因此,您使用文件描述符来引用该映射以执行您的程序可能 运行.
的各种操作(查找、更新、删除)
如果用 C 编写程序,而不是像您那样使用汇编指令,这通常是抽象的:程序使用 C 指针引用映射,但加载程序(通常依赖于 libbpf)执行一些重定位步骤以提取从对象文件的专用 ELF 部分获取关于映射的元数据,检索映射的文件描述符,并将其插入相关的字节码指令中。
内核验证器切换到指针
但你是对的:在内核中,BPF_FUNC_map_lookup_elem()
助手等使用指向映射的指针,而不是文件描述符。这是在加载时,在程序验证期间,验证器将文件描述符替换为指向与映射关联的内存区域的指针(请参阅 kernel/bpf/verifier.c 中的 resolve_pseudo_ldimm64()
)。此时可以获得指针:验证者确实可以访问那些映射的内核内存指针。
请注意,验证器实际上走得更远,对于某些地图类型(哈希、数组),它甚至完全取代了对地图查找助手的调用,而是使用指令直接从中的相关地址读取地图(搜索 map_gen_lookup
了解详情)。
查看内核的 sample/bpf/sock_example.c
:
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol) /* R0 = ip->proto */),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */
BPF_EXIT_INSN(),
};
我知道 eBPF
设置寄存器 r1-r5 来保存 BPF 助手的参数。我不明白的是为什么要将地图 fd
传递给 BPF_FUNC_map_lookup_elem
?根据 helpers code :
const struct bpf_func_proto bpf_map_lookup_elem_proto = {
.func = bpf_map_lookup_elem,
.gpl_only = false,
.pkt_access = true,
.ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MAP_KEY,
};
表示两个参数都是指针,none是map fd。除非,我正在查找错误的代码?
在用户 space 中编写程序时的文件描述符,但后来由验证器替换为指向映射的指针。
编写 eBPF 程序时的文件描述符
您在用户 space 中编写您的 eBPF 程序,其中您没有任何指向地图的地址指针。因此,您使用文件描述符来引用该映射以执行您的程序可能 运行.
的各种操作(查找、更新、删除)如果用 C 编写程序,而不是像您那样使用汇编指令,这通常是抽象的:程序使用 C 指针引用映射,但加载程序(通常依赖于 libbpf)执行一些重定位步骤以提取从对象文件的专用 ELF 部分获取关于映射的元数据,检索映射的文件描述符,并将其插入相关的字节码指令中。
内核验证器切换到指针
但你是对的:在内核中,BPF_FUNC_map_lookup_elem()
助手等使用指向映射的指针,而不是文件描述符。这是在加载时,在程序验证期间,验证器将文件描述符替换为指向与映射关联的内存区域的指针(请参阅 kernel/bpf/verifier.c 中的 resolve_pseudo_ldimm64()
)。此时可以获得指针:验证者确实可以访问那些映射的内核内存指针。
请注意,验证器实际上走得更远,对于某些地图类型(哈希、数组),它甚至完全取代了对地图查找助手的调用,而是使用指令直接从中的相关地址读取地图(搜索 map_gen_lookup
了解详情)。