本文最后更新于 2025-04-29T11:40:30+08:00
eBPF程序结构
本章展示了eBPF程序从源代码到执行的整个过程中经历的各个阶段

- eBPF程序是一组eBPF字节码指令,可以直接使用eBPF字节码编写,但是一般来说使用高级语言编写会更轻松
eBPF 虚拟机
- 计算机软件实现
- 以eBPF字节码(由一组指令组成)指令的形式接受程序,这些指令必须转换为在CPU上运行的本地机器指令。
eBPF寄存器
- 使用10个通用寄存器(0-9)
- 寄存器10用作栈指针
- eBPF程序的上下文参数在开始执行之前加载到寄存器1中,函数的返回值存储在寄存器0中
- eBPF调用函数之前,参数放到寄存器1-5中(传递参数)
eBPF指令
eBPF指令的结构:
1 2 3 4 5 6 7
| struct bpf_insn { __u8 code; __u8 dst_reg:4; __u8 src_reg:4; __s16 off; __s32 imm; };
|
- 如果一条指令长度超过了8byte,则可以使用宽指令编码(wide instruction encoding)
- 操作码类别:
- 将值写入寄存器(立即数或从内存中读取的值或从其他寄存器中读取的值)
- 存储寄存器中的值到内存中
- 进行数值计算
- 跳转到其他指令(满足一定条件时)
例子
example1:
当网络数据包到达时触发它并写入一行跟踪
XDP 的返回值
在 XDP 程序中,返回值决定了数据包的处理方式,常用的返回值包括:
XDP_DROP
: 丢弃数据包。
XDP_PASS
: 将数据包传递给内核网络栈进行正常处理。
XDP_TX
: 数据包直接回送到接收的网卡(即本地发送)。
XDP_REDIRECT
: 将数据包重定向到另一个网络接口或用户空间。
程序解释:
SEC("edp")
声明这是一个XDP程序,运行在网络驱动处理的阶段
bpf_printk()
用于向内核日志打印调试信息
编译
可以使用clang来编译
makefile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| TARGETS = hello hello-func
all: $(TARGETS)
.PHONY: all
$(TARGETS): %: %.bpf.o
%.bpf.o: %.bpf.c
clang \ -target bpf \ -I/usr/include/$(shell uname -m)-linux-gnu \ -g \ -O2 -o $@ -c $< clean: - rm *.bpf.o - rm -f /sys/fs/bpf/hello - rm -f /sys/fs/bpf/hello-func
|
检查eBPF对象文件
file hello.bpf.o
命令可以用于查看文件的内容
1 2
| root@PQCU:~/eBPF/learning-ebpf/chapter3 hello.bpf.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped
|
表明它是一个ELF文件,包含eBPF代码,适用于64bit架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| root@PQCU:~/eBPF/learning-ebpf/chapter3
hello.bpf.o: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <hello>: ; int hello(struct xdp_md *ctx) { 0: b7 01 00 00 00 00 00 00 r1 = 0 ; bpf_printk("Hello World %d", counter); 1: 73 1a fe ff 00 00 00 00 *(u8 *)(r10 - 2) = r1 2: b7 01 00 00 25 64 00 00 r1 = 25637 3: 6b 1a fc ff 00 00 00 00 *(u16 *)(r10 - 4) = r1 4: b7 01 00 00 72 6c 64 20 r1 = 543452274 5: 63 1a f8 ff 00 00 00 00 *(u32 *)(r10 - 8) = r1 6: 18 01 00 00 48 65 6c 6c 00 00 00 00 6f 20 57 6f r1 = 8022916924116329800 ll 8: 7b 1a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r1 9: 18 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r6 = 0 ll 11: 61 63 00 00 00 00 00 00 r3 = *(u32 *)(r6 + 0) 12: bf a1 00 00 00 00 00 00 r1 = r10 13: 07 01 00 00 f0 ff ff ff r1 += -16 ; bpf_printk("Hello World %d", counter); 14: b7 02 00 00 0f 00 00 00 r2 = 15 15: 85 00 00 00 06 00 00 00 call 6 ; counter++; 16: 61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0) 17: 07 01 00 00 01 00 00 00 r1 += 1 18: 63 16 00 00 00 00 00 00 *(u32 *)(r6 + 0) = r1 ; return XDP_PASS; 19: b7 00 00 00 02 00 00 00 r0 = 2 20: 95 00 00 00 00 00 00 00 exit
|
- llvm-objdump用于显示目标文件或可执行文件信息;可以显示文件头信息、符号表、反汇编代码等。