eBPF指令集

寄存器及调用约定

通用的RISC指令集,11个64位寄存器,一个程序计数器和512字节的栈空间构成。

10个通用寄存器+1个只读FP(帧指针寄存器),所有寄存器64bit宽。操作模式默认为64位,32位子寄存器只能通过特殊的算数逻辑单元ALU操作访问。

栈帧的边界由SP和FP限定

SP一直指向栈顶

每个进程的栈空间为一帧,FP指向当前进程栈空间的栈底。

  • R0:函数返回值、程序退出值

  • R1-R5:函数调用参数

  • R6-R9:被调用者保存函数(调用保留的)寄存器

  • R10:只读FP用于访问栈

R0-R5是临时寄存器,eBPF程序在调用之间将它们从寄存器转移到内存或从内存转移到寄存器。(spill/fill,解释见https://www.geeksforgeeks.org/what-is-spilling/)

指令编码

  • 基础指令编码:一条指令64bit

  • 宽指令编码:在基础指令编码后附加一个64bit的立即数,一共128bit

基础指令编码结构:

32 bits (MSB)16 bits4 bits4 bits8 bits (LSB)
immediateoffsetsource registerdestination registeropcode

指令分类

Opcode低三位:

指令分类classvaluedescription
算数指令BPF_ALU0x0432-bit 算数操作
BPF_ALU640x0764-bit 算数操作
跳转指令BPF_JMP0x0564-bit 跳转操作
BPF_JMP320x0632-bit 跳转操作
载入指令BPF_LD0x00non-standard load operations
BPF_LDX0x01载入寄存器操作
存储指令BPF_ST0x02存储立即数操作
BPF_STX0x03存储寄存器操作

算数和跳转指令

包括:BPF_ALU, BPF_ALU64, BPF_JMP and BPF_JMP32

opcode分为三部分

4 bits (MSB)1 bit3 bits (LSB)
operation codesourceinstruction class

第四位编码了source操作数:

sourcevaluedescription
BPF_K0x00使用 32-bit 立即数作为源操作数
BPF_X0x08使用 ‘src_reg’ 寄存器作为源操作数

算数指令

operation code编码的操作

codevaluedescription
BPF_ADD0x00dst += src
BPF_SUB0x10dst -= src
BPF_MUL0x20dst *= src
BPF_DIV0x30dst /= src
BPF_OR0x40dst |= src
BPF_AND0x50dst &= src
BPF_LSH0x60dst <<= src
BPF_RSH0x70dst >>= src
BPF_NEG0x80dst = ~src
BPF_MOD0x90dst %= src
BPF_XOR0xa0dst ^= src
BPF_MOV0xb0dst = src
BPF_ARSH0xc0sign extending shift right
BPF_END0xd0byte swap operations (see separate section below)

例子:

BPF_XOR | BPF_K | BPF_ALU == src_reg = (u32) src_reg ^ (u32) imm32

字节交换指令

仅对目标寄存器进行操作,不使用源寄存器或立即数

source字段用于选择转换的字节序

sourcevaluedescription
BPF_TO_LE0x00转换主机字节序到小端
BPF_TO_BE0x08转换主机字节序到大端

立即数字段用于编码交换操作的宽度,可以是16/32/64

例子

BPF_ALU | BPF_TO_LE | BPF_END with imm = 16

dst_reg = htole16(dst_reg)

跳转指令

codevaluedescriptionnotes
BPF_JA0x00PC += offBPF_JMP only
BPF_JEQ0x10PC += off if dst == src
BPF_JGT0x20PC += off if dst > srcunsigned
BPF_JGE0x30PC += off if dst >= srcunsigned
BPF_JSET0x40PC += off if dst & src
BPF_JNE0x50PC += off if dst != src
BPF_JSGT0x60PC += off if dst > srcsigned
BPF_JSGE0x70PC += off if dst >= srcsigned
BPF_CALL0x80function call
BPF_EXIT0x90function / program returnBPF_JMP only
BPF_JLT0xa0PC += off if dst < srcunsigned
BPF_JLE0xb0PC += off if dst <= srcunsigned
BPF_JSLT0xc0PC += off if dst < srcsigned
BPF_JSLE0xd0PC += off if dst <= srcsigned

载入和存储指令

包括:BPF_LD, BPF_LDX, BPF_ST and BPF_STX

Opcode结构:

3 bits (MSB)2 bits3 bits (LSB)
modesizeinstruction class

size修饰符

size modifiervaluedescription
BPF_W0x00word (4 bytes)
BPF_H0x08half word (2 bytes)
BPF_B0x10byte
BPF_DW0x18double word (8 bytes)

mode修饰符

mode modifiervaluedescription
BPF_IMM0x0064-bit immediate instructions
BPF_ABS0x20legacy BPF packet access (absolute)
BPF_IND0x40legacy BPF packet access (indirect)
BPF_MEM0x60regular load and store operations,寄存器和内存间传递数据的标准载入和存储指令
BPF_ATOMIC0xc0atomic operations,原子操作

举例

把立即数的值放到dst_reg+off的内存位置

BPF_MEM | <size> | BPF_ST == *(size *) (dst_reg + off) = imm32

原子操作

在内存上的操作,不会被中断或破坏,使用mode修饰符BPF_ATOMIC,只支持32位和64位操作,不支持8/16位。

立即数字段用于编码实际的原子操作:

immvaluedescription
BPF_ADD0x00atomic add
BPF_OR0x40atomic or
BPF_AND0x50atomic and
BPF_XOR0xa0atomic xor

例子:

BPF_ATOMIC | BPF_W | BPF_STX with imm = BPF_ADD

*(u32 *)(dst_reg + off16) += src_reg

除了简单原子操作,还有一个修饰符和两个复杂原子操作

immvaluedescription
BPF_FETCH0x01modifier: return old value
BPF_XCHG0xe0 | BPF_FETCHatomic exchange
BPF_CMPXCHG0xf0 | BPF_FETCHatomic compare and exchange

如果设置了BPF_FETCH,会使用修改前内存中的值覆盖src_reg

BPF_XCHG以原子操作交换src_reg的值和dst_reg + off地址的值

BPF_CMPXCHG以原子操作将dst_reg + off地址的值和R0进行比较,如果相等,dst_reg + off地址的值将替换为src_reg。操作前dst_reg + off地址的值会被扩展零,然后加载回R0。

clang可以生成原子指令通过默认的 -mcpu=v3
如果较低版本的 -mcpu被设置,clang只能生成不带 BPF_FETCHBPF_ADD
如果需要启用原子特征,并保持较低版本的 -mcpu,可以使用 -Xclang -target-feature -Xclang +alu32

64位立即数指令

带有BPF_IMMmode修饰符的指令,对额外的64位立即数使用宽指令编码:

BPF_LD | BPF_DW | BPF_IMM means dst_reg = imm64

传统的BPF Packet访问指令

用于访问数据包数据,并且只能在程序上下文是指向网络数据包的指针时使用。

两种指令形式

  • BPF_ABS | <size> | BPF_LDBPF_ABS访问由立即数指定的绝对偏移的数据包数据
  • BPF_IND | <size> | BPF_LDBPF_IND访问除立即数外还包括寄存器值作为偏移的数据包数据。

七个隐式操作数:

  • R6,隐式输入,指向 struct sk_buff 的指针
  • R0,隐式输出,从数据包中获取的数据
  • R1-5,临时寄存器,在调用BPF_ABS | BPF_LDBPF_IND | BPF_LD后被破坏

隐式的程序退出条件:当eBPF程序试图访问数据包边界外的数据时,执行将被终止。

例子:

BPF_IND | BPF_W | BPF_LD

R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))

参考

eBPF Instruction Set ‒ The Linux Kernel documentation

BPF and XDP Reference Guide — Cilium 1.11.3 documentation

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏打呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值