eBPF 指令宏

linux 6.9.7 指令宏

/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* eBPF instruction mini library */
#ifndef __BPF_INSN_H
#define __BPF_INSN_Hstruct bpf_insn;/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
// BPF_ALU64_REG:dst_reg += src_reg
// BPF_ALU32_REG:dst_reg += src_reg
#define BPF_ALU64_REG(OP, DST, SRC)				\((struct bpf_insn) {					\.code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,	\.dst_reg = DST,					\.src_reg = SRC,					\.off   = 0,					\.imm   = 0 })#define BPF_ALU32_REG(OP, DST, SRC)				\((struct bpf_insn) {					\.code  = BPF_ALU | BPF_OP(OP) | BPF_X,		\.dst_reg = DST,					\.src_reg = SRC,					\.off   = 0,					\.imm   = 0 })/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
// BPF_ALU64_IMM:dst_reg += imm32
// BPF_ALU32_IMM:dst_reg += imm32
#define BPF_ALU64_IMM(OP, DST, IMM)				\((struct bpf_insn) {					\.code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,	\.dst_reg = DST,					\.src_reg = 0,					\.off   = 0,					\.imm   = IMM })#define BPF_ALU32_IMM(OP, DST, IMM)				\((struct bpf_insn) {					\.code  = BPF_ALU | BPF_OP(OP) | BPF_K,		\.dst_reg = DST,					\.src_reg = 0,					\.off   = 0,					\.imm   = IMM })/* Short form of mov, dst_reg = src_reg */
// BPF_MOV64_REG:dst_reg = src_reg
// BPF_MOV32_REG:dst_reg = src_reg
#define BPF_MOV64_REG(DST, SRC)					\((struct bpf_insn) {					\.code  = BPF_ALU64 | BPF_MOV | BPF_X,		\.dst_reg = DST,					\.src_reg = SRC,					\.off   = 0,					\.imm   = 0 })#define BPF_MOV32_REG(DST, SRC)					\((struct bpf_insn) {					\.code  = BPF_ALU | BPF_MOV | BPF_X,		\.dst_reg = DST,					\.src_reg = SRC,					\.off   = 0,					\.imm   = 0 })/* Short form of mov, dst_reg = imm32 */
// BPF_MOV64_IMM:dst_reg = imm32
// BPF_MOV32_IMM:dst_reg = imm32
#define BPF_MOV64_IMM(DST, IMM)					\((struct bpf_insn) {					\.code  = BPF_ALU64 | BPF_MOV | BPF_K,		\.dst_reg = DST,					\.src_reg = 0,					\.off   = 0,					\.imm   = IMM })#define BPF_MOV32_IMM(DST, IMM)					\((struct bpf_insn) {					\.code  = BPF_ALU | BPF_MOV | BPF_K,		\.dst_reg = DST,					\.src_reg = 0,					\.off   = 0,					\.imm   = IMM })/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
// BPF_LD_IMM64 和 BPF_LD_IMM64_RAW 都是用于将立即数加载到寄存器中的指令
// 差异:
// BPF_LD_IMM64:
// 这是一个加载立即数的指令,用于将一个 64 位的立即数直接加载到指定的寄存器中
// 这种类型的加载通常用于设置寄存器的初始值/将特定的常量值传递给后续的计算
// BPF_LD_IMM64_RAW:
// 这个指令也是用于加载立即数,但它是“原始”形式,意味着它可能允许加载的立即数包含一些特殊值或模式
// 这些在常规的 BPF_LD_IMM64 指令中可能不被允许
// BPF_LD_IMM64_RAW 通常用于加载那些不能直接用常量表达的值,或者需要绕过某些编译器优化的情况
//
// 在实际使用中,BPF_LD_IMM64 是更常见的指令,因为它适用于大多数需要加载立即数的场景
// BPF_LD_IMM64_RAW 可能在特定的上下文或特殊情况下使用,例如,当需要加载的立即数受到某些限制或需要特定的处理时
// 注意
// eBPF 指令集和可用的指令可能会根据不同的内核版本和 eBPF 特性而有所不同
// 因此,在使用这些指令时,应参考当前环境的文档和内核版本
#define BPF_LD_IMM64(DST, IMM)					\BPF_LD_IMM64_RAW(DST, 0, IMM)#define BPF_LD_IMM64_RAW(DST, SRC, IMM)				\((struct bpf_insn) {					\.code  = BPF_LD | BPF_DW | BPF_IMM,		\.dst_reg = DST,					\.src_reg = SRC,					\.off   = 0,					\.imm   = (__u32) (IMM) }),			\((struct bpf_insn) {					\.code  = 0, /* zero is reserved opcode */	\.dst_reg = 0,					\.src_reg = 0,					\.off   = 0,					\.imm   = ((__u64) (IMM)) >> 32 })#ifndef BPF_PSEUDO_MAP_FD
# define BPF_PSEUDO_MAP_FD	1
#endif/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
// MAP_FD 是映射的文件描述符,这是一个整数值,表示了要加载的映射
// 使用 BPF_LD_IMM64_RAW,将立即数加载到指定的寄存器 DST
// BPF_PSEUDO_MAP_FD:
// 这是一个伪指令,用于指示内核这是一个特殊的立即数加载,它不是直接加载一个立即数,而是加载一个映射的文件描述符
// 在 eBPF 程序中简化加载映射文件描述符的过程
// 在 eBPF 程序中,可能需要引用一个已经创建的映射
// 通过使用这个宏,可以轻松地将映射的文件描述符加载到一个寄存器中,然后使用该寄存器与其他 eBPF 指令一起操作映射
//
// 例如,在 eBPF 程序中,如果想加载映射文件描述符到寄存器 BPF_REG_1,可以这样使用这个宏:
// int map_fd = /* 假设这是从某个地方获取的映射文件描述符 */;
// BPF_LD_MAP_FD(BPF_REG_1, map_fd);
// 这行代码将映射的文件描述符加载到 BPF_REG_1 寄存器中
// 之后可以使用这个寄存器来进行映射查找或其他操作
#define BPF_LD_MAP_FD(DST, MAP_FD)				\BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */// 直接包访问
// BPF_LD_ABS:
// R0 = *(uint *) (skb->data + imm32)
//
// eBPF 指令,加载一个立即数到 eBPF 寄存器
// SIZE 表示加载数据的大小,可以是 BPF_W(表示 32 位)或 BPF_DW(表示 64 位)
// 可能的值有 BPF_B(8位字节,byte)、BPF_H(16位半字,half-word)、BPF_W(32位字,word)、BPF_DW(64位双字,double-word)
//
// IMM 是一个参数,表示要加载的立即数
//
// bpf_insn 是 eBPF 程序中表示单个指令的结构体
// 设置指令属性:
// .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS
// .code 是设置指令的操作码
// BPF_LD 表示这是一个加载操作
// BPF_SIZE(SIZE) 根据 SIZE 参数设置加载数据的大小
// BPF_ABS 表示使用绝对偏移量加载
//
// 设置目标寄存器:
// .dst_reg = 0,
// .dst_reg 是目标寄存器,这里设置为 0,表示使用寄存器 BPF_REG_0(在 eBPF 中,BPF_REG_0 通常用于存储加载的数据)
// 设置源寄存器:
// .src_reg = 0,
// .src_reg 是源寄存器,这里设置为 0,对于 BPF_LD 指令,源寄存器通常用于间接加载,但在这个宏中未使用。
// 设置偏移量:
// .off   = 0,
// .off 是指令的偏移量,这里设置为 0,表示从输入数据的开始位置加载数据
// 设置立即数:
// .imm   = IMM
// .imm 是指令的立即数字段,这里使用参数 IMM 填充
//
// 简化创建一个加载立即数的 eBPF 指令的过程
// 从输入数据的固定偏移量加载数据到寄存器,使用该宏可以方便构造指令
//
// 【示例】
// 加载位于输入数据偏移量 42 处的 32 位数据到寄存器 BPF_REG_0:
// BPF_LD_ABS(BPF_W, 42);
// 将创建一个 eBPF 指令,从输入数据的偏移量 42 处加载一个 32 位的数据到 BPF_REG_0 寄存器
//
// 在 BPF_PROG_TYPE_SOCKET_FILTER 类型的 eBPF 程序中,这种类型的加载操作通常用于访问网络数据包的特定部分
// 比如从数据包缓冲区中,加载数据到寄存器
// BPF_PROG_TYPE_SOCKET_FILTER 是 eBPF 程序的一种类型,用于在套接字上运行,可以过滤/修改经过套接字的网络数据包
// 这种类型的程序通常用于实现自定义的网络策略、监控或数据包处理逻辑
//
// 在 BPF_PROG_TYPE_SOCKET_FILTER 中使用 BPF_LD_ABS 指令的一般步骤如下:
// (1) 确定要访问的数据包部分:
// 首先,需要知道想要访问的数据包头部/字段的偏移量。
// 例如,如果访问 IP 头部的协议字段,需要知道该字段在数据包中的确切位置。
// (2) 编写 BPF_LD_ABS 指令:
// 使用 BPF_LD_ABS 指令将所需数据加载到寄存器。
// 需要指定加载的大小(如 BPF_W 表示 32 位,BPF_B 表示 8 位等),以及数据在数据包中的偏移量
// (3) 处理加载的数据:
// 加载数据后,使用其他 eBPF 指令来处理这些数据,例如根据协议类型进行条件分支或计数。
//
// 【示例】
//  如何在 BPF_PROG_TYPE_SOCKET_FILTER 程序中使用 BPF_LD_ABS 来加载 IP 头部的协议字段:
//
// struct bpf_insn prog[] = {
//     // 加载 IP 头部的协议字段(偏移量 ETH_HLEN + offsetof(struct iphdr, protocol))
//     BPF_LD_ABS(BPF_W, ETH_HLEN + offsetof(struct iphdr, protocol)),
//     // 假设只关心 TCP 流量
//     BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, IPPROTO_TCP, 0),
//     // 如果协议是 TCP,允许数据包通过
//     BPF_RET(BPF_K, 0),
//     // 如果不是 TCP,丢弃数据包
//     BPF_RET(BPF_K, -1),
// };
// 首先,使用 BPF_LD_ABS 指令加载 IP 头部的协议字段到 BPF_REG_0
// 然后,使用 BPF_JMP_IMM 指令检查协议字段是否等于 IPPROTO_TCP
// 如果是,程序返回 0 允许数据包通过;如果不是,程序返回 -1 丢弃数据包#define BPF_LD_ABS(SIZE, IMM)					\((struct bpf_insn) {					\.code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,	\.dst_reg = 0,					\.src_reg = 0,					\.off   = 0,					\.imm   = IMM })/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
// 把 内存MEM SRC+OFF 中的 SIZE 大小的数据加载到寄存器 DST
// dst_reg = *(uint *) (src_reg + off16)
//
// BPF_LDX 表示这是一个加载指令,并且加载目标是寄存器
// BPF_SIZE(SIZE) 表示加载数据的大小(字节、半字、字或双字)
// BPF_MEM 表示数据来源是内存
// dst_reg 目标寄存器,宏参数 DST 指定
// src_reg 源寄存器,宏参数 SRC 指定,包含内存基址
// off     内存地址偏移量,宏参数 OFF 指定
// imm     立即数部分,在这种类型的指令中这个字段不使用,所以设置为 0
//
// 如果要从内存地址加载一个字节到寄存器 r1,可以这么写
// struct bpf_insn instr = BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 10)
// 在内存地址 r0 + 10 处加载 1B 的数据量,然后将该字节存储到寄存器 r1 中
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)			\((struct bpf_insn) {					\.code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,	\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = 0 })/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
// *(uint *) (dst_reg + off16) = src_reg
// BPF_STX 表示这是一个存储指令,并且存储源是寄存器
// 将存储源是 SRC 寄存器的 SIZE 大小的内容,存储到内存 DST+OFF 处
//
// BPF_SIZE(SIZE) 表示存储数据的大小(字节、半字、字或双字)
// BPF_MEM 表示数据存储于内存
// dst_reg 目的寄存器,宏参数 DST 指定,它包含内存基址
// src_reg 源寄存器, 宏参数 SRC 指定,它包含要存储的值
// off     内存地址偏移量,宏参数 OFF 指定
//
// 如果要将寄存器 r2 中的一个字节存储到内存地址(r1 + 10)
// struct bpf_insn instr = BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_2, 10);
// 上述指令的含义是将寄存器 r2 中的一个字节值存储到内存地址 r1 + 10 处
#define BPF_STX_MEM(SIZE, DST, SRC, OFF)			\((struct bpf_insn) {					\.code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,	\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = 0 })/** Atomic operations:**   BPF_ADD                  *(uint *) (dst_reg + off16) += src_reg*   BPF_AND                  *(uint *) (dst_reg + off16) &= src_reg*   BPF_OR                   *(uint *) (dst_reg + off16) |= src_reg*   BPF_XOR                  *(uint *) (dst_reg + off16) ^= src_reg*   BPF_ADD | BPF_FETCH      src_reg = atomic_fetch_add(dst_reg + off16, src_reg);*   BPF_AND | BPF_FETCH      src_reg = atomic_fetch_and(dst_reg + off16, src_reg);*   BPF_OR | BPF_FETCH       src_reg = atomic_fetch_or(dst_reg + off16, src_reg);*   BPF_XOR | BPF_FETCH      src_reg = atomic_fetch_xor(dst_reg + off16, src_reg);*   BPF_XCHG                 src_reg = atomic_xchg(dst_reg + off16, src_reg)*   BPF_CMPXCHG              r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg)*/
// atomic_fetch_add 是一个用于原子操作的函数,主要用于在多线程编程中对共享变量进行无锁的加法操作,并且返回变量更新前的值
// 它用于避免在多线程环境中更新变量时可能出现的数据竞争问题// BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0)
// *(uint *) (BPF_REG_0 + 0) += BPF_REG_1
#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF)			\((struct bpf_insn) {					\.code  = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC,	\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = OP })/* Legacy alias */
#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF)/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
// 将 SIZE 大小的立即数 IMM ,保存到 DST + OFF 上
#define BPF_ST_MEM(SIZE, DST, OFF, IMM)				\((struct bpf_insn) {					\.code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,	\.dst_reg = DST,					\.src_reg = 0,					\.off   = OFF,					\.imm   = IMM })/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
// 条件跳转,如果 (dst_reg 'op' src_reg),则跳转到 pc + off16
#define BPF_JMP_REG(OP, DST, SRC, OFF)				\((struct bpf_insn) {					\.code  = BPF_JMP | BPF_OP(OP) | BPF_X,		\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = 0 })/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */#define BPF_JMP32_REG(OP, DST, SRC, OFF)			\((struct bpf_insn) {					\.code  = BPF_JMP32 | BPF_OP(OP) | BPF_X,	\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = 0 })/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
// 条件跳转,如果 (dst_reg 'op' imm32),则跳转到 pc + off16
#define BPF_JMP_IMM(OP, DST, IMM, OFF)				\((struct bpf_insn) {					\.code  = BPF_JMP | BPF_OP(OP) | BPF_K,		\.dst_reg = DST,					\.src_reg = 0,					\.off   = OFF,					\.imm   = IMM })/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */#define BPF_JMP32_IMM(OP, DST, IMM, OFF)			\((struct bpf_insn) {					\.code  = BPF_JMP32 | BPF_OP(OP) | BPF_K,	\.dst_reg = DST,					\.src_reg = 0,					\.off   = OFF,					\.imm   = IMM })/* Raw code statement block */#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)			\((struct bpf_insn) {					\.code  = CODE,					\.dst_reg = DST,					\.src_reg = SRC,					\.off   = OFF,					\.imm   = IMM })/* Program exit */#define BPF_EXIT_INSN()						\((struct bpf_insn) {					\.code  = BPF_JMP | BPF_EXIT,			\.dst_reg = 0,					\.src_reg = 0,					\.off   = 0,					\.imm   = 0 })#endif// BPF_W 和 BPF_B 是在 eBPF 编程中使用的宏,它们定义了字的大小
// 在 eBPF 指令集中,字的大小对于某些加载和存储操作是重要的
//
// BPF_W:
// BPF_W 代表 "word",通常用于表示一个 32 位的字
// 在 eBPF 指令集中,这个宏用于指定操作应该以 32 位宽的字进行
// 例如,在使用 BPF_LD 系列指令加载数据时,如果指定 BPF_W,那么加载的数据将是 32 位宽
// BPF_B:
// BPF_B 代表 "byte",用于表示一个 8 位的字节
// 在 eBPF 指令集中,这个宏用于指定操作应该以 8 位宽的字节进行
// 例如,在使用 BPF_LD 系列指令加载数据时,如果指定 BPF_B,那么加载的数据将是 8 位宽
//
// 在 eBPF 程序中,这些宏通常与指令的操作码结合使用,以指定操作的数据宽度
// 例如,BPF_LDW 指令可以与 BPF_W 结合使用来加载一个 32 位的字,或者与 BPF_B 结合使用来加载一个 8 位的字节
//
// struct bpf_insn load_32_bit_word = {
//     .code = BPF_LD | BPF_W | BPF_ABS, // 加载一个 32 位的字
//     .dst_reg = BPF_REG_0,               // 目标寄存器
//     .src_reg = 0,                       // 源寄存器(对于 BPF_ABS,这通常是 0)
//     .off = 0,                           // 偏移量
//     .imm = offsetof(struct some_struct, some_field) // 立即数,表示偏移量
// };
//
// struct bpf_insn load_8_bit_byte = {
//     .code = BPF_LD | BPF_B | BPF_ABS, // 加载一个 8 位的字节
//     .dst_reg = BPF_REG_0,
//     .src_reg = 0,
//     .off = 0,
//     .imm = offsetof(struct some_struct, some_byte_field)
// };
//
// load_32_bit_word 指令加载一个 32 位的字
// load_8_bit_byte 指令加载一个 8 位的字节
// 这些操作的数据宽度由 BPF_W 和 BPF_B 宏指定

eBPF指令集规范v1.0

本文档是eBPF指令集规范,版本 1.0

寄存器和使用规范

eBPF有10个通用寄存器、一个只读的栈帧寄存器,都是64-bit宽度

eBPF的寄存器使用规范为

  • R0:保存函数返回值和 eBPF 程序退出值
  • R1 - R5:用于函数调用参数
  • R6 - R9:callee函数负责进入时保存这几个寄存器到栈中,函数退出前再恢复寄存器原有值
    (callee saved registers that function calls will preserve)
  • R10: 只读的栈帧寄存器,用于访问栈

R0 - R5是临时寄存器,eBPF程序如果希望在函数调用后寄存器值不变,需要自己保存和恢复寄存器
(R0 - R5 are scratch registers and eBPF programs needs to spill/fill them if necessary across calls.)

Scratch register / temporary register:保存临时值或者中间值
Caller 和 Callee:A 函数中调用 B 函数,A 是 Caller,B 是 Callee

Caller saved registers 和 Callee saved registers

在函数调用时,某些寄存器的值需要被保存,这样函数调用做出的任何修改不会破坏调用函数已经存储在这些寄存器里的值

寄存器的保存分为两种主要的策略:Caller-saved(调用者保存)和 Callee-saved(被调用者保存)

Caller-saved Registers (volatile registers or call-clobbered registers)

这些寄存器在函数调用时,调用者(caller)需要自己负责保存它们的值。
如果需要在函数调用后继续使用这些寄存器中的值,调用者在调用函数之前,需要将它们的值保存到堆栈或其他内存区域中
并在函数调用之后恢复它们

特点:
调用者负责保存和恢复
高效性:许多情况下,这些寄存器可以直接用于临时存储,不需要额外的保存和恢复操作

Callee-saved Registers (non-volatile registers or call-preserved registers)

这些寄存器在函数调用时,由被调用者(callee)负责保存它们的值
在函数执行之前,被调用者需要将这些寄存器的当前值保存在栈上,并在函数返回之前恢复它们的值

特点:
被调用者负责保存和恢复。
持久性:这些寄存器的值在函数调用之间保持不变

示例

x86-64 ABI(应用二进制接口)

Caller-saved Registers
RAX (Accumulator): 返回值寄存器
RCX, RDX (Counter, Data): 函数参数寄存器
R8, R9, R10, R11: 函数参数寄存器和临时寄存器
这些寄存器的内容在函数调用时可能被覆盖,因此调用者在调用函数之前需要保存它们的值以便在函数返回后继续使用

Callee-saved Registers
RBX: 通用寄存器
RBP (Base Pointer): 栈基指针
R12, R13, R14, R15: 通用寄存器
RSP (Stack Pointer): 栈指针
这些寄存器的值在函数调用前需要被保存,并在函数返回前恢复,以确保调用者在函数调用前后的寄存器状态一致

void callee_function() {// some operations
}void caller_function() {int i = 5;// potentially need to save caller-saved registers herecallee_function(); // May use caller-saved registers// potentially need to restore caller-saved registers here
}

编译器在处理 caller_function 时,根据使用的寄存器,可能会插入代码来保存和恢复 caller-saved 寄存器的值

总结
Caller-saved Registers:调用者保存,volatile,函数调用可能覆盖其值,需要调用者在调用前保存并在调用后恢复
Callee-saved Registers:被调用者保存,non-volatile,函数调用需要保存现有值,并在返回前恢复,以确保调用者的环境不受干扰

指令编码

eBPF 有两种编码模式:
基础编码,64bit 固定长度编码
宽指令编码,在基础编码后增加了一个 64bit 的立即数 (imm64) ,这样指令为 128bit

基础编码格式,每一列约定为 field
在这里插入图片描述
绝大多数指令不会使用所有的field,不使用的field被置0
宽指令编码目前只有 64-bit 立即数指令使用

指令类型(class)

基础编码中的 field 的 opcode,一共 8 bit,其中最低位 3bit 用来保存指令类型:
在这里插入图片描述

算术和跳转指令

(BPF_ALU、BPF_ALU64、BPF_JMP、BPF_JMP32) 称为算术和跳转指令

8bit 的 opcode 被分为 3 个部分:
在这里插入图片描述
第4个bit(source)含义:
在这里插入图片描述
4个 bit 的 operation code 用来存储具体指令操作码

算术指令(BPF_ALU, BPF_ALU64)

BPF_ALU 操作数为 32bit,BPF_ALU64 操作数为 64bit

4个 bit 的 operation code 编码如下操作
在这里插入图片描述
译者注:
上表中 dst 一定是指目的寄存器,不支持内存地址
上表中 src 可能是源寄存器,也可能是 imm32,根据 source 位 (BPF_K/BPF_X) 来区分
eBPF 寄存器都是 64bit,根据操作数类型,可以使用全部 64bit,也可以只使用其中 32bit

BPF_ADD | BPF_X | BPF_ALU :
dst_reg = (u32) dst_reg + (u32) src_reg;BPF_XOR | BPF_K | BPF_ALU64 :
dst_reg = dst_reg + src_regBPF_XOR | BPF_K | BPF_ALU :
dst_reg = (u32) dst_reg ^ (u32) imm32BPF_XOR | BPF_K | BPF_ALU64 :
dst_reg = dst_reg ^ imm32

字节swap指令

字节 swap 指令,属于 BPF_ALU 分类,操作码为 BPF_END
字节 swap 指令操作数只有 dst_reg,不操作 src_reg 和 imm32

在这里插入图片描述
基础编码格式中的 imm32,此时编码了 swap 操作的位宽。支持:16、32、64bit

BPF_ALU | BPF_TO_LE | BPF_END,并且imm32 = 16:
dst_reg = htole16(dst_reg)BPF_ALU | BPF_TO_LE | BPF_END,并且imm32 = 64:
dst_reg = htole64(dst_reg)

跳转指令(BPF_JMP32, BPF_JMP)

操作数为寄存器,BPF_JMP32 使用 32bit,BPF_JMP 使用 64bit,其它行为都一样
在这里插入图片描述
eBPF 程序在调用 BPF_EXIT 前,需要把返回值保存在 R0 寄存器中

PC:程序计数器
off:基础编码格式中的 off16
src、dst:都是指的寄存器的值

Load 和 Store指令

BPF_LD、BPF_LDX、BPF_ST、BPF_STX指令类型中,8bit 的 opcode 含义为:
在这里插入图片描述

标准 load 和 store 指令

BPF_MEM 代表了标准 load 和 store 指令,这些指令用于寄存器和内存之间传递数据。

BPF_MEM | <size> | BPF_STX:
*(size *) (dst_reg + off16) = src_regBPF_MEM | <size> | BPF_ST:
*(size *) (dst_reg + off16) = imm32BPF_MEM | <size> | BPF_LDX:
dst_reg = *(size *) (src_reg + off16)size 可选值:BPF_B, BPF_H, BPF_W, or BPF_DW

原子操作

原子操作,指的的是对内存的操作,不会被其他 eBPF 程序中途扰乱

eBPF 所有的原子操作由 BPF_ATOMIC 指定,例如:

BPF_ATOMIC | BPF_W | BPF_STX :32-bit原子操作
BPF_ATOMIC | BPF_DW | BPF_STX :64-bit原子操作

8-bit 和 16-bit 原子操作不支持

基本编码格式的 imm32 用来编码真正的原子操作, 以下是简单原子指令:
在这里插入图片描述

BPF_ATOMIC | BPF_W | BPF_STX, imm32 = BPF_ADD
*(u32 *)(dst_reg + off16) += src_regBPF_ATOMIC | BPF_DW | BPF_STX, imm32 = BPF_ADD
*(u64 *)(dst_reg + off16) += src_reg

除了以上比较简单的原子操作,还有 2 个复杂原子指令:
在这里插入图片描述
在这里插入图片描述
对于简单原子指令是可选的,如果设置了,src_reg 将保存 (dst_reg + off16) 指向的内存中被修改前的原始值
对于复杂原子指令是必选的

64bit立即数指令

mode 为 BPF_IMM 的指令,用于 eBPF 宽指令,有额外的一个 imm64 值
在这里插入图片描述
含义为:dst_reg = imm64

经典BPF数据包访问指令

eBPF 之前为了兼容经典 BPF,引入了一些访问数据包的指令,现在已经废弃不再使用

学习链接

https://heapdump.cn/article/5420563

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1473482.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Drools开源业务规则引擎(三)- 事件模型(Event Model)

文章目录 Drools开源业务规则引擎&#xff08;三&#xff09;- 事件模型&#xff08;Event Model&#xff09;1.org.kie.api.event2.RuleRuntimeEventManager3.RuleRuntimeEventListener接口说明示例规则文件规则执行日志输出 4.AgentaEventListener接口说明示例监听器实现类My…

Linux——学习Linux基本工具安装教程视频链接

本篇文章就是记录一下学习Linux需要用到的基本工具的视频教程链接&#xff0c;方便以后查看 VMware15.5安装 安装视频教程&#xff1a;VMware15.5安装教程 centos7.6安装&#xff08;这个视频教程真的很nice&#xff09; 视频教程&#xff1a;centos7.6 虚拟机克隆、快照、…

医疗器械FDA | FDA如何对医疗器械网络安全认证进行审查?

FDA医械网络安全文件出具​https://link.zhihu.com/?targethttps%3A//www.wanyun.cn/Support%3Fshare%3D24315_ea8a0e47-b38d-4cd6-8ed1-9e7711a8ad5e FDA对医疗器械的网络安全认证进行审查时&#xff0c;主要关注以下几个方面&#xff0c;以确保医疗器械在网络环境中的安全性…

7 系列 FPGA 引脚及封装(参考ug475)

目录 I/O BankPins引脚定义I/O and Multi-Function PinsPower Supply PinsDedicated XADC PinsTransceiver PinsDedicated Configuration PinsTemperature Sensor Pins Device 视图整个 FPGAIOBILOGIC,OLOGIC,IDELAY,ODELAYBUFIO,BUFR,IDELAYCTRLBUFMRCEBRAM,DSPIBUFDS_GTE2CLB…

vscode远程连接linux(配置免密)

远程连接 1.首先保证物理机和虚拟机网络可以ping通 2.查看ubuntu得ip地址 ifconfig IP为&#xff1a;192.168.52.133 3.连接远程主机 配置免密 1.打开cmd运行ssh-keygen -t rsa 一路回车就行 2.打开window文件夹C:\Users\xbj\.ssh 3.用记事本打开id_rsa.pub文件复制公…

[数据集][目标检测]护目镜检测数据集VOC+YOLO格式888张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;888 标注数量(xml文件个数)&#xff1a;888 标注数量(txt文件个数)&#xff1a;888 标注类别…

美光科技在2024年1γ工艺技术在10纳米级别启动EUV试产

美光科技&#xff08;Micron&#xff09;在2024年针对其1γ&#xff08;1-gamma&#xff09;工艺技术在10纳米级别启动EUV&#xff08;极紫外光刻&#xff09;试产&#xff0c;这标志着存储行业巨头在EUV采用上的重要一步&#xff0c;尽管相比英特尔和台积电等其他半导体制造商…

Linux--USB驱动开发(一)USB简介

一、什么是 USB&#xff1f; USB 全称为 Universal Serial Bus &#xff0c;翻译过来就是通用串行总线。由英特尔与众多电脑公司提出来&#xff0c;用于规范电脑与外部设备的连接与通讯。目前 USB 接口已经得到了大范围的应用&#xff0c;已经是电脑、手机等终端设备的必配…

Android面试题自定义View之Window、ViewRootImpl和View的三大流程

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 View的三大流程指的是measure(测量)、layout(布局)、draw(绘制)。 下面我们来分别看看这三大流程 View的measure(测量) MeasureSpec Measur…

【Axure高保真原型】中继器表格——移入显示详情卡片案例

今天和大家分享中继器表格——移入显示详情卡片的原型模板&#xff0c;鼠标移入员工号或姓名会弹出员工卡片&#xff0c;可以查看更详细的信息。这个表格是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;只需要维护中继器表格里的信息&#xff0c;即可自动生成交互效…

计算机网络--网络层

一、网络层的服务和功能 网络层主要为应用层提供端对端的数据传输服务 网络层接受运输层的报文段&#xff0c;添加自己的首部&#xff0c;形成网络层分组。分组是网络层的传输单元。网络层分组在各个站点的网络层之间传输&#xff0c;最终到达接收方的网络层。接收方网络层将运…

基于单片机的防酒驾控制系统设计

摘 要&#xff1a; 酒后驾车的危害十分巨大&#xff0c;因此&#xff0c;笔者介绍了一种基于单片机的防酒驾控制系统。系统由酒精传感器 MQ-3测量汽车驾驶员体内的酒精含量浓度&#xff0c;通过 A/D 转换器转换成数字信号传给单片机&#xff0c;经过单片机处理后显示酒精浓度&a…

2 极/2 零 (2P2Z) 补偿器

极/2 零 &#xff08;2P2Z&#xff09; 补偿器是模拟 II 型控制器的数字实现。它是一种滤波器&#xff0c;通过考虑两个极点和一个零点&#xff0c;将特定的增益和相位升压引入系统。您必须战略性地选择每个极点和零点的频率位置&#xff0c;这将有助于实现所需的系统性能。在该…

简单的手动实现spring中的自动装配案例

简简单单的实现一个spring中的自动装配和容器管理的小骚操作。 1&#xff0c;创建AutoSetBean.java 使用injectBeans静态方法&#xff0c;可以扫描指定包下的所有带MyInject注解的字段&#xff0c;如果在beans的Map中存在这个字段的实例化类&#xff0c;则执行装配。 import…

解决C++编译时的产生的skipping incompatible xxx 错误

问题 我在编译项目时&#xff0c;产生了一个 /usr/bin/ld: skipping incompatible ../../xxx/ when searching for -lxxx 的编译错误&#xff0c;如下图所示&#xff1a; 解决方法 由图中的错误可知&#xff0c;在编译时&#xff0c;是能够在我们指定目录下的 *.so 动态库的…

干冰运输与存储中的温度监测:确保药品安全与合规性

在制药行业&#xff0c;干冰对于运输和储存对温度敏感的药品&#xff0c;如原料药API、疫苗、冻干物质和人体组织样本等至关重要。虹科ELPRO LIBERO系列干冰温度记录仪&#xff0c;能够为您提供专业的解决方案&#xff0c;定期监测和记录干冰运输和存储过程中的温度&#xff0c…

通信软件开发之业务知识:PON口割接什么意思?

一 PON口割接&#xff08;原创总结&#xff09; 在通信领域&#xff0c;PON口割接指的是对无源光网络&#xff08;Passive Optical Network&#xff0c;PON&#xff09;端口进行的切换或调整操作。简单来说&#xff0c;就是对光纤网络中的某个端口进行重新连接或重新分配&…

2-25 基于matlab的语音信号降噪处理算法

基于matlab的语音信号降噪处理算法&#xff0c;采用谱减法&#xff0c;可以对强噪声背景下的语音信号进行去噪。输入原始信号及加噪信号&#xff0c;对加噪信号进行降噪&#xff0c;并提高信噪比。程序已调通&#xff0c;可直接运行。 2-25 语音信号降噪处理算法 谱减法 - 小红…

Vue3基础(二)

一、搭建工程(vite) ## 1.创建命令 npm create vuelatest## 2.具体配置 ## 配置项目名称 √ Project name: vue3_test ## 是否添加TypeScript支持 √ Add TypeScript? Yes ## 是否添加JSX支持 √ Add JSX Support? No ## 是否添加路由环境 √ Add Vue Router for Single P…

NSSCTF-Web题目24(RCE-空格绕过、过滤绕过)

目录 [MoeCTF 2021]babyRCE 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]funny_web 4、题目 5、知识点 6、思路 [MoeCTF 2021]babyRCE 1、题目 2、知识点 空格绕过、过滤绕过 3、思路 出现源码&#xff0c;进行代码审计 需要我们GET方式上传一个rce变量&#x…