本文为阅读 sbpf 的 interpreter.rs(Rust 语言) 的代码理解笔记。

sbpf 的 interpreter.rs 解读


编译器属性

1
#![allow(clippy::arithmetic_side_effects)]

这是一个 crate 级属性(注意 #! 表示应用于整个 crate),用于:

  • 禁用 clippy 的算术副作用检查:clippy 通常警告可能溢出或异常的算术操作
  • 在特定场景下需要:eBPF 解释器中很多算术操作使用 wrapping_* 方法,明确允许溢出行为
  • 出于性能考虑:避免额外的检查开销

版权声明、许可证声明

1
2
3
4
5
6
7
8
9
10
// Derived from uBPF <https://github.com/iovisor/ubpf>
// Copyright 2015 Big Switch Networks, Inc
// (uBPF: VM architecture, parts of the interpreter, originally in C)
// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
// (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for syscalls)
// Copyright 2020 Solana Maintainers <maintainers@solana.com>
//
// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

模块文档注释

1
//! Interpreter for eBPF programs.

注意: //! 代表模块级文档注释

描述当前文件的主要功能:eBPF程序的解释器

模块导入:解释器执行所需的所有核心组件。

1
2
3
4
5
6
7
use crate::{
ebpf,
elf::Executable,
error::{EbpfError, ProgramResult},
program::BuiltinFunction,
vm::{Config, ContextObject, EbpfVm},
};
  • ebpf - eBPF指令集定义和常量(操作码、结构体)
  • elf::Executable - 可执行文件加载和程序信息
  • error::{EbpfError, ProgramResult} - 错误类型和执行结果
  • program::BuiltinFunction - 系统调用函数定义
  • vm::{Config, ContextObject, EbpfVm} - 虚拟机配置、执行环境和状态管理

安全的内存访问包装器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// Virtual memory operation helper.
macro_rules! translate_memory_access {
(_impl, $self:ident, $op:ident, $vm_addr:ident, $T:ty, $($rest:expr),*) => {
match $self.vm.memory_mapping.$op::<$T>(
$($rest,)* // 可变参数:store 操作需要 value 参数
$vm_addr, // 虚拟内存地址
) {
ProgramResult::Ok(v) => v, // 成功:返回值
ProgramResult::Err(err) => {
throw_error!($self, err); // 失败:抛出错误终止程序(定义在下一个宏中)
},
}
};

// MemoryMapping::load() // 加载模式(load)
($self:ident, load, $vm_addr:ident, $T:ty) => {
translate_memory_access!(_impl, $self, load, $vm_addr, $T,)
};

// MemoryMapping::store() // 存储模式(store)
($self:ident, store, $value:expr, $vm_addr:ident, $T:ty) => {
translate_memory_access!(_impl, $self, store, $vm_addr, $T, ($value) as $T);
};
}

该宏使用格式:

  • load 指令:

    1
    translate_memory_access!($self, load, $vm_addr, $type)
    参数 类型 说明
    $self Interpreter实例 解释器状态
    load 字面量 固定关键字,表示加载操作
    $vm_addr u64 虚拟内存地址
    $type Rust类型 数据类型:u8, u16, u32, u64

    使用示例:

    1
    2
    3
    4
    5
    // 从内存读取1字节
    let value = translate_memory_access!(self, load, vm_addr, u8);

    // 从内存读取8字节
    let value = translate_memory_access!(self, load, vm_addr, u64);
  • store 指令

    1
    translate_memory_access!($self, store, $value, $vm_addr, $type)
    参数 类型 说明
    $self Interpreter实例 解释器状态
    store 字面量 固定关键字,表示存储操作
    $value 表达式 要存储的值
    $vm_addr u64 虚拟内存地址
    $type Rust类型 数据类型:u8, u16, u32, u64

    使用示例:

    1
    2
    3
    4
    5
    // 存储1字节
    translate_memory_access!(self, store, 0xFF, vm_addr, u8);

    // 存储4字节
    translate_memory_access!(self, store, self.reg[src], vm_addr, u32);

错误终止宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
macro_rules! throw_error {
($self:expr, $err:expr) => {{
$self.vm.registers[11] = $self.reg[11]; // 保存当前 PC ,便于调试和错误追踪
$self.vm.program_result = ProgramResult::Err($err); //设置错误结果
return false; // 终止程序
}};
(DivideByZero; $self:expr, $src:expr, $ty:ty) => { // 除零检查
if $src as $ty == 0 {
throw_error!($self, EbpfError::DivideByZero);
}
};
(DivideOverflow; $self:expr, $src:expr, $dst:expr, $ty:ty) => { // 除法溢出
if $dst as $ty == <$ty>::MIN && $src as $ty == -1 {
throw_error!($self, EbpfError::DivideOverflow);
}
};
}

除法溢出的含义为:如 i32::MIN ÷ (-1) 会导致溢出,因为 i32::MAX-i32::MAX 小 1 。

程序计数器验证宏

1
2
3
4
5
6
7
8
9
macro_rules! check_pc {
($self:expr, $next_pc:ident, $target_pc:expr) => {
if ebpf::is_pc_in_program($self.program, $target_pc as usize) {
$next_pc = $target_pc; // 地址有效:设置下一个PC
} else {
throw_error!($self, EbpfError::CallOutsideTextSegment); // 地址无效:报错
}
};
}

检查函数 ebpf::is_pc_in_program(program, target_pc) 的功能:

  • 检查 target_pc 是否指向合法的指令位置
  • 确保不会跳转到:
    • 程序开始之前
    • 程序结束之后
    • 指令中间(如LD_DW的第二部分)

枚举定义:定义调试器执行模式的两种状态

1
2
3
4
5
6
7
8
/// State of the interpreter during a debugging session    // 这是枚举的文档注释
#[cfg(feature = "debugger")] // 条件编译属性
pub enum DebugState {
/// Single step the interpreter // 单步执行解释器(类似于 gdb 的 n)
Step,
/// Continue execution till the end or till a breakpoint is hit
Continue, // 继续执行直到程序结束或遇到断点(类似于 gdb 的 c)
}

这个枚举用于控制调试器的执行模式。

其中 #[cfg(feature = "debugger")] 的含义:只有当启用 ‘debugger’ 编译特性时,下面的代码才会被编译和包含在最终程序中。

eBPF解释器的核心状态结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// State of an interpreter
pub struct Interpreter<'a, 'b, C: ContextObject> { // 结构体定义
// 各字段内容
pub(crate) vm: &'a mut EbpfVm<'b, C>,
pub(crate) executable: &'a Executable<C>,
pub(crate) program: &'a [u8],
pub(crate) program_vm_addr: u64,

/// General purpose registers and pc
pub reg: [u64; 12],

#[cfg(feature = "debugger")]
pub(crate) debug_state: DebugState,
#[cfg(feature = "debugger")]
pub(crate) breakpoints: Vec<u64>,
}

这段代码定义了 eBPF 解释器的核心状态结构体,其中除了寄存器公开,其余的各字段均为“仅当前 crate 内可见”

首先是结构体定义:

  • 'a 生命周期:解释器本身持有的引用的生命周期

  • 'b 生命周期:通过 EbpfVm 传递的更深层引用的生命周期
    (为什么需要两个生命周期?因为 'b >= 'a ,下面的 vm 字段持有对 EbpfVm 的可变引用( 'a ),而 EbpfVm 内部又持有对上下文对象的引用(生命周期 'b),这两个引用需要独立管理以确保内存安全。)

  • C : ContextObject :泛型参数,必须是实现了 ContextObject trait 的类型(代表执行上下文)。下面是该特征的定义:

    1
    2
    3
    4
    5
    6
    pub trait ContextObject {
    // Required methods
    fn trace(&mut self, state: [u64; 12]);
    fn consume(&mut self, amount: u64);
    fn get_remaining(&self) -> u64;
    }

    方法详解

    1. trace(&mut self, state: [u64; 12])
      调用时机:启用追踪时,每条指令执行后调用
      参数:state 是 12 个寄存器的当前值(r0-r11)
      用途:记录执行轨迹,用于调试和分析
    2. consume(&mut self, amount: u64)
      调用时机:每条指令执行时调用
      参数:amount 是要消耗的指令数(通常为 1)
      用途:从指令计量器中扣除已执行的指令数
    3. get_remaining(&self) -> u64
      调用时机:检查是否还有剩余指令配额时
      返回值:剩余允许执行的指令数
      用途:防止程序执行无限循环或超出资源限制

    因为 Rust 语言的特征就相当于接口,所以这个定义要求这个特征就是为了让 eBPF 程序在不同地方都能跑,比如 Solana 区块链、普通 Linux 等,只要实现这个接口就行。

然后是字段部分

  • 虚拟机引用 vm: &'a mut EbpfVm<'b, C> :这个字段持有虚拟机的核心状态(内存映射、调用栈、指令计数等),可变引用意味着解释器可以修改虚拟机状态,可以通过这个字段访问 memory_mapping、call_frames、registers 等。

  • 可执行文件 executable: &'a Executable<C> :包含已加载的 eBPF 程序的元数据,提供配置信息、版本信息、函数注册表、加载器等。

  • 程序字节码 program: &'a [u8] :存储原始的 eBPF 字节码,用于指令解码、验证 PC 范围。

  • 程序虚拟地址 program_vm_addr: u64 :这是个 64 为无符号整数,存储程序代码段在虚拟内存中的起始地址,或者用于地址转换(虚拟地址 ↔ 指令索引),或者在 CALL_REG 指令中用于计算目标 PC

  • 寄存器数组 reg: [u64; 12] :唯一的公开字段,便于调试。寄存器分配如下:

    索引 寄存器名 用途
    0 r0 返回值寄存器
    1-5 r1-r5 函数参数寄存器
    6-9 r6-r9 临时寄存器(调用者保存)
    10 r10 栈帧指针(frame pointer)
    11 r11 程序计数器(PC)
  • 调试状态 debug_state: DebugState :有条件编译 #[cfg(feature = "debugger")] ,所以仅调试时编译,有单步和连续两种执行行为。

  • 断点列表 breakpoints: Vec<u64>Vec<u64> 是动态数组。有条件编译 #[cfg(feature = "debugger")] ,所以仅调试时编译,存储断点的虚拟地址列表,程序执行到这些地址时暂停。

该结构体 Interpreter 的内存关系图:

1
2
3
4
5
6
Interpreter
├── vm ──────────→ EbpfVm (内存映射、调用栈、指令计数)
├── executable ──→ Executable (配置、版本、函数表)
├── program ─────→ 字节码切片
├── program_vm_addr (起始地址)
└── reg[12] (包含 PC)

Interpreter 的方法

1
2
3
impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
......
}

方法列表:

方法 可见性 说明
new() pub 构造函数,创建新的解释器实例
get_dbg_pc() pub(条件编译) 获取调试器使用的 PC 值
push_frame() 私有 压入调用栈帧
sign_extension() 私有 符号扩展处理
step() pub 核心方法,执行一条指令
dispatch_syscall() 私有 分发系统调用

new()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// Creates a new interpreter state
pub fn new(
vm: &'a mut EbpfVm<'b, C>,
executable: &'a Executable<C>,
registers: [u64; 12],
) -> Self {
let (program_vm_addr, program) = executable.get_text_bytes();
Self {
vm,
executable,
program,
program_vm_addr,
reg: registers,
#[cfg(feature = "debugger")]
debug_state: DebugState::Continue,
#[cfg(feature = "debugger")]
breakpoints: Vec::new(),
}
}

这是构造函数,创建新的解释器实例

  • 函数签名:

    1
    2
    3
    4
    5
    pub fn new(
    vm: &'a mut EbpfVm<'b, C>,
    executable: &'a Executable<C>,
    registers: [u64; 12],
    ) -> Self
    • vm: &'a mut EbpfVm<'b, C> :虚拟机的可变引用
    • executable: &'a Executable<C> :可执行文件的引用
    • registers: [u64; 12] :初始寄存器值数组,通常传入全0数组
    • 返回值 -> Self :返回 Interpreter 实例
  • 函数体:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    let (program_vm_addr, program) = executable.get_text_bytes();
    Self {
    vm, // 保存虚拟机引用
    executable, // 保存可执行文件引用
    program, // 保存程序字节码
    program_vm_addr, // 保存程序虚拟地址
    reg: registers, // 设置初始寄存器值

    // 条件编译:仅在启用 debugger 特性时包含
    #[cfg(feature = "debugger")]
    debug_state: DebugState::Continue, // 默认继续模式
    #[cfg(feature = "debugger")]
    breakpoints: Vec::new(), // 空断点列表
    }

    第一行的 get_text_bytes()Executable 结构体的一个方法,用于获取 eBPF 程序的代码段(text section)信息。该函数返回一个元组 (u64, &[u8])u64 为程序代码段在虚拟内存中的起始地址,&[u8] 为程序代码的字节码切片。

    后面的内容都是返回值 Self 的内容,在新建一个 Interpreter 时,除了 program_vm_addrprogram 两个值,其他值都来源于函数参数或者默认值。

get_dbg_pc()

1
2
3
4
5
/// Translate between the virtual machines' pc value and the pc value used by the debugger
#[cfg(feature = "debugger")]
pub fn get_dbg_pc(&self) -> u64 {
(self.reg[11] * ebpf::INSN_SIZE as u64) + self.executable.get_text_section_offset()
}

这个函数仅启用 debugger 特性时编译,返回调试器使用的 PC 地址。转换的地址是绝对地址文件偏移,GDB 等调试器只能通过这个进行设置断点等调试操作。

函数体仅一个返回值,即计算公式:reg[11] + offset ,其中 reg[11] 就是前面提到的程序计数器(PC)offset 就是代码段在整个文件或内存中的起始偏移地址,由 ELF 文件格式决定。

push_frame()

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
32
33
fn push_frame(&mut self, config: &Config) -> bool {
let frame = &mut self.vm.call_frames[self.vm.call_depth as usize];
frame.caller_saved_registers.copy_from_slice(
&self.reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS],
);
frame.frame_pointer = self.reg[ebpf::FRAME_PTR_REG];
frame.target_pc = self.reg[11] + 1;

self.vm.call_depth += 1;
if self.vm.call_depth as usize == config.max_call_depth {
throw_error!(self, EbpfError::CallDepthExceeded);
}

if self
.executable
.get_sbpf_version()
.automatic_stack_frame_bump()
{
// With fixed frames we start the new frame at the next fixed offset
let stack_frame_size = config.stack_frame_size
* if !self.executable.get_sbpf_version().manual_stack_frame_bump()
&& config.enable_stack_frame_gaps
{
2
} else {
1
};
self.reg[ebpf::FRAME_PTR_REG] =
self.reg[ebpf::FRAME_PTR_REG].wrapping_add(stack_frame_size as u64);
}

true
}

该函数用于处理函数调用时的栈帧压入操作。

  • 函数签名

    1
    fn push_frame(&mut self, config: &Config) -> bool
    • config :虚拟机配置,引用结构体 Config (定义在 vm.rs 里)的内容。
    • 输出结果是 bool 类型,所以要么是 true ,要么是 false 并被扔进 throw_error! 中。
  • 函数体

    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
    let frame = &mut self.vm.call_frames[self.vm.call_depth as usize];
    frame.caller_saved_registers.copy_from_slice(
    &self.reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS],
    );
    frame.frame_pointer = self.reg[ebpf::FRAME_PTR_REG];
    frame.target_pc = self.reg[11] + 1;

    self.vm.call_depth += 1;
    if self.vm.call_depth as usize == config.max_call_depth {
    throw_error!(self, EbpfError::CallDepthExceeded);
    }

    if self
    .executable
    .get_sbpf_version()
    .automatic_stack_frame_bump()
    {
    // With fixed frames we start the new frame at the next fixed offset
    let stack_frame_size = config.stack_frame_size
    * if !self.executable.get_sbpf_version().manual_stack_frame_bump()
    && config.enable_stack_frame_gaps
    {
    2
    } else {
    1
    };
    self.reg[ebpf::FRAME_PTR_REG] =
    self.reg[ebpf::FRAME_PTR_REG].wrapping_add(stack_frame_size as u64);
    }

    true
    • 获取过程:

      1
      let frame = &mut self.vm.call_frames[self.vm.call_depth as usize];

      call_framesEbpfVm 结构体中用于存储函数调用栈的向量(Vec<CallFrame>)。

      根据 CallFrame 的定义,每个帧保存了:

      1. 调用者保存的寄存器 (caller_saved_registers)
      2. 调用者的帧指针 (frame_pointer)
      3. 返回地址 (target_pc)

      call_depthEbpfVm 结构体中记录当前函数调用深度的字段(u64)。

      call_depth 既是深度,也是下一个空闲帧的索引(但是 u64 需要转换为 usize 才可以当数组索引,所以有 as usize)。

      这行代码完成了以下效果:

      1. 用当前调用深度 call_depth 作为索引
      2. 从预分配的 call_frames 数组中取出一个帧
      3. 获取这个帧的可变引用,准备写入数据(所以是 mut
      4. 这个帧将保存当前函数的上下文(见接下来的保存过程),为即将调用的新函数做准备
    • 保存过程:

      1
      2
      3
      4
      5
      frame.caller_saved_registers.copy_from_slice(
      &self.reg[ebpf::FIRST_SCRATCH_REG..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS],
      );
      frame.frame_pointer = self.reg[ebpf::FRAME_PTR_REG];
      frame.target_pc = self.reg[11] + 1;

      这段代码保存了以下内容:

      1. caller_saved_registers :调用者的临时寄存器(scratch registers,通过栈帧保存这些值,确保函数返回后能恢复原状。切片的上下限分别意思为:第一个临时寄存器的索引 .. 第一个临时寄存器的索引 + 总共有几个临时寄存器)
      2. frame_pointer :当前栈帧指针(r10)
      3. target_pc :返回地址(当前 PC + 1),函数执行完后要跳回的位置
    • 自增及检查过程:

      1
      2
      3
      4
      self.vm.call_depth += 1;
      if self.vm.call_depth as usize == config.max_call_depth {
      throw_error!(self, EbpfError::CallDepthExceeded);
      }

      这里实现调用深度 +1(这里就体现了为什么要 mut 这个栈帧),并检查是否超过最大调用深度限制(防止栈溢出)

    • 调整栈帧指针过程:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      if self
      .executable
      .get_sbpf_version()
      .automatic_stack_frame_bump()
      {
      // With fixed frames we start the new frame at the next fixed offset
      let stack_frame_size = config.stack_frame_size
      * if !self.executable.get_sbpf_version().manual_stack_frame_bump()
      && config.enable_stack_frame_gaps
      {
      2
      } else {
      1
      };
      self.reg[ebpf::FRAME_PTR_REG] =
      self.reg[ebpf::FRAME_PTR_REG].wrapping_add(stack_frame_size as u64);
      }

      这段涉及到一个版本问题,新版本情况 .get_sbpf_version().automatic_stack_frame_bump() 这个函数会返回 true ,那么会开启解释器自动管理栈帧的代码,注释以下的代码都是计算并调整栈帧指针的,简单来说就是:

      1
      2
      3
      4
      5
      6
      7
      if 新版本(自动调整) {
      // 计算每个栈帧要占多少空间
      let 帧大小 = 配置.栈帧大小 × 乘数;

      // 把帧指针往上移动,给新函数腾出空间
      r10 = r10 + 帧大小;
      }

      而计算指针偏移时要根据 enable_stack_frame_gaps 的返回值判断是否采用有间隙模式:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      无间隙模式(×1):
      ┌──────┐ ← r10(新)
      │ 帧2 │
      ├──────┤
      │ 帧1 │
      ├──────┤
      │ 帧0 │
      └──────┘

      有间隙模式(×2):
      ┌──────┐ ← r10(新)
      │ 帧2 │
      ├──────┤
      │ 间隙 │ ← 4KB 空区域(安全缓冲)
      ├──────┤
      │ 帧1 │
      ├──────┤
      │ 间隙 │
      ├──────┤
      │ 帧0 │
      └──────┘
    • 成功返回:

      1
      true

sign_extension()

1
2
3
4
5
6
7
8
9
10
11
fn sign_extension(&self, value: i32) -> u64 {
if self
.executable
.get_sbpf_version()
.explicit_sign_extension_of_results()
{
value as u32 as u64
} else {
value as i64 as u64
}
}

这个函数根据 sBPF 版本(.get_sbpf_version().explicit_sign_extension_of_results())决定 32 位运算结果如何存入 64 位寄存器:

  • 旧版本value as i64 as u64 - 将 32 位值符号扩展到 64 位(负数高位补1,正数高位补0)
  • 新版本value as u32 as u64 - 将 32 位值零扩展到 64 位(高位直接补0)

step()

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
/// Advances the interpreter state by one instruction
///
/// Returns false if the program terminated or threw an error.
#[rustfmt::skip]
#[inline(always)]
pub fn step(&mut self) -> bool {
let config = &self.executable.get_config();

if config.enable_instruction_meter && self.vm.due_insn_count >= self.vm.previous_instruction_meter {
throw_error!(self, EbpfError::ExceededMaxInstructions);
}
self.vm.due_insn_count += 1;
if self.reg[11] as usize * ebpf::INSN_SIZE >= self.program.len() {
throw_error!(self, EbpfError::ExecutionOverrun);
}
let mut next_pc = self.reg[11] + 1;
let mut insn = ebpf::get_insn_unchecked(self.program, self.reg[11] as usize);
let dst = insn.dst as usize;
let src = insn.src as usize;

if config.enable_register_tracing {
self.vm.register_trace.push(self.reg);
}

match insn.opc {
............
}

self.reg[11] = next_pc;
true
}

该函数实现执行一条指令的操作,分为检查和执行两部分

  • 属性:

    1
    2
    #[rustfmt::skip]    // 告诉 Rust 代码格式化工具(rustfmt)跳过这段代码,不要自动重新格式化。
    #[inline(always)] // 强制编译器总是内联这个函数,即把函数体直接插入到调用处,而不是生成函数调用。
  • 函数介绍(除指令匹配细节)

    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
    pub fn step(&mut self) -> bool {
    let config = &self.executable.get_config();

    if config.enable_instruction_meter && self.vm.due_insn_count >= self.vm.previous_instruction_meter {
    throw_error!(self, EbpfError::ExceededMaxInstructions);
    }
    self.vm.due_insn_count += 1;
    if self.reg[11] as usize * ebpf::INSN_SIZE >= self.program.len() {
    throw_error!(self, EbpfError::ExecutionOverrun);
    }
    let mut next_pc = self.reg[11] + 1;
    let mut insn = ebpf::get_insn_unchecked(self.program, self.reg[11] as usize);
    let dst = insn.dst as usize;
    let src = insn.src as usize;

    if config.enable_register_tracing {
    self.vm.register_trace.push(self.reg);
    }

    match insn.opc {
    ............
    }

    self.reg[11] = next_pc;
    true
    }

    这个函数返回一个布尔值(和栈帧压入操作函数类似),true 则指令运行成功,false 则失败,并被扔进 throw_error! 中。step 函数需要修改解释器内部状态,所以用 &mut self

    • 获取配置

      1
      let config = &self.executable.get_config();

      从可执行文件中获取虚拟机配置(指令计量、追踪等设置)

    • 指令计量检查与操作

      1
      2
      3
      4
      if config.enable_instruction_meter && self.vm.due_insn_count >= self.vm.previous_instruction_meter {
      throw_error!(self, EbpfError::ExceededMaxInstructions);
      }
      self.vm.due_insn_count += 1;

      指令执行数量是有上限的,如果已执行指令数达到上限,抛出”超过最大指令数”错误,否则就已执行指令数 +1 。

      设置指令数上限是为了防止程序无限循环或消耗过多计算资源,保证虚拟机安全。

    • PC 检查

      1
      2
      3
      if self.reg[11] as usize * ebpf::INSN_SIZE >= self.program.len() {
      throw_error!(self, EbpfError::ExecutionOverrun);
      }

      PC 值 * 字节偏移量(每条指令 8 字节),用于检查是否超过程序长度,超过则报错。

    • 指令解码、匹配以及返回可以成功执行的返回值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      let mut next_pc = self.reg[11] + 1;    // 保存下一个指令的 PC 值
      let mut insn = ebpf::get_insn_unchecked(self.program, self.reg[11] as usize);
      // 保存指令(不检查边界,因为前面已检查)
      let dst = insn.dst as usize; // 目标寄存器索引
      let src = insn.src as usize; // 源寄存器索引

      if config.enable_register_tracing {
      self.vm.register_trace.push(self.reg);
      // 如果启用了追踪,将当前所有寄存器的值保存到追踪记录中
      }

      match insn.opc { // 这里是一个巨大的 match,处理所有可能的指令
      ............
      }

      self.reg[11] = next_pc; // 更新 PC
      true // 返回:继续执行

      注:指令匹配请查看附录

dispatch_syscall()

1
2
3
4
5
6
7
fn dispatch_syscall(&mut self, function: BuiltinFunction<C>) -> &ProgramResult {
self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count; // 暂停指令计数
self.vm.registers[0..6].copy_from_slice(&self.reg[0..6]); // 传递参数
self.vm.invoke_function(function); // 执行系统函数
self.vm.due_insn_count = 0; // 重置计数
&self.vm.program_result //返回结果
}

这个函数负责调用系统功能(如打印、读写内存等):

  1. 暂停指令计数:进入系统调用前,先保存已执行的指令数
  2. 传递参数:把 eBPF 程序的 r0-r5 寄存器值传给系统函数
  3. 执行系统函数:调用真正的系统功能
  4. 重置计数:系统调用完成后,清零指令计数(准备重新开始)
  5. 返回结果:把系统函数的执行结果返回给 eBPF 程序

简单说:eBPF 程序请求操作系统帮忙做事时,通过这个函数中转。

附录:eBPF 指令集以及指令匹配代码

加载指令

指令 类别 作用 代码实现 注意事项
LD_DW_IMM 64位加载 加载64位立即数
到寄存器
ebpf::augment_lddw_unchecked()
self.reg[dst] = insn.imm as u64
self.reg[11] += 1
占2个指令槽,需合并两条指令
• PC需额外+1跳过第二条指令
• 版本检查 !disable_lddw()

内存加载指令 (BPF_LDX)

指令 类别 作用 代码实现 注意事项
LD_B_REG 内存加载 加载1字节到寄存器 let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u8);
• 地址计算用 wrapping_add 防溢出
• 宏统一处理内存错误
• 版本检查 !move_memory_instruction_classes()
LD_H_REG 内存加载 加载2字节到寄存器 同上,u16 • 同上
• 注意2字节对齐
LD_W_REG 内存加载 加载4字节到寄存器 同上,u32 • 同上
• 注意4字节对齐
LD_DW_REG 内存加载 加载8字节到寄存器 同上,u64 • 同上
• 注意8字节对齐

立即数存储指令 (BPF_ST)

指令 类别 作用 代码实现 注意事项
ST_B_IMM 立即数存储 存储1字节立即数 let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u8);
insn.imm 是32位,存储时截断为u8
• 版本检查 !move_memory_instruction_classes()
ST_H_IMM 立即数存储 存储2字节立即数 同上,u16 insn.imm 截断为u16
ST_W_IMM 立即数存储 存储4字节立即数 同上,u32 insn.imm 直接作为u32存储
ST_DW_IMM 立即数存储 存储8字节立即数 同上,u64 insn.imm 需要符号扩展到64位

寄存器存储指令 (BPF_STX)

指令 类别 作用 代码实现 注意事项
ST_B_REG 寄存器存储 存储寄存器值的
1字节到内存
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u8);
• 只存储低1字节
• 版本检查 !move_memory_instruction_classes()
ST_H_REG 寄存器存储 存储寄存器值的
2字节到内存
同上,u16 • 只存储低2字节
ST_W_REG 寄存器存储 存储寄存器值的
4字节到内存
同上,u32 • 只存储低4字节
ST_DW_REG 寄存器存储 存储寄存器值的
8字节到内存
同上,u64 • 存储完整8字节

32位算术逻辑指令 (BPF_ALU32)

指令 类别 作用 代码实现 注意事项
ADD32_IMM 算术 32位加法(立即数) self.sign_extension((self.reg[dst] as i32).wrapping_add(insn.imm as i32)) wrapping_add 防溢出
• 结果经 sign_extension 处理高32位
ADD32_REG 算术 32位加法(寄存器) self.sign_extension((self.reg[dst] as i32).wrapping_add(self.reg[src] as i32)) 同上
SUB32_IMM 算术 32位减法(立即数) if swap_sub_reg_imm_operands()
  self.sign_extension((insn.imm as i32).wrapping_sub(self.reg[dst] as i32))
else
  self.sign_extension((self.reg[dst] as i32).wrapping_sub(insn.imm as i32))
操作数顺序依赖版本
• 旧版 dst - imm,新版 imm - dst
SUB32_REG 算术 32位减法(寄存器) self.sign_extension((self.reg[dst] as i32).wrapping_sub(self.reg[src] as i32)) dst - src
MUL32_IMM 算术 32位乘法(立即数) self.sign_extension((self.reg[dst] as i32).wrapping_mul(insn.imm as i32)) • 非PQR版本
wrapping_mul 防溢出
MUL32_REG 算术 32位乘法(寄存器) self.sign_extension((self.reg[dst] as i32).wrapping_mul(self.reg[src] as i32)) 同上
DIV32_IMM 算术 32位无符号除法
(立即数)
(self.reg[dst] as u32 / insn.imm as u32) as u64 无除零检查(验证器保证)
DIV32_REG 算术 32位无符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u32);
(self.reg[dst] as u32 / self.reg[src] as u32) as u64
必须检查除零
OR32_IMM 逻辑 32位或运算(立即数) (self.reg[dst] as u32 | insn.imm as u32) as u64 • 按位或,高位清零
OR32_REG 逻辑 32位或运算(寄存器) (self.reg[dst] as u32 | self.reg[src] as u32) as u64 同上
AND32_IMM 逻辑 32位与运算(立即数) (self.reg[dst] as u32 & insn.imm as u32) as u64 • 按位与,高位清零
AND32_REG 逻辑 32位与运算(寄存器) (self.reg[dst] as u32 & self.reg[src] as u32) as u64 同上
LSH32_IMM 移位 32位左移(立即数) (self.reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64 wrapping_shl 防溢出
• 只移低5位
LSH32_REG 移位 32位左移(寄存器) (self.reg[dst] as u32).wrapping_shl(self.reg[src] as u32) as u64 同上
RSH32_IMM 移位 32位右移(立即数) (self.reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64 • 逻辑右移,高位补0
RSH32_REG 移位 32位右移(寄存器) (self.reg[dst] as u32).wrapping_shr(self.reg[src] as u32) as u64 同上
NEG32 算术 32位取反 (self.reg[dst] as i32).wrapping_neg() as u64 & (u32::MAX as u64) wrapping_neg 处理 i32::MIN
u32::MAX 确保高位清零
MOD32_IMM 算术 32位取模(立即数) (self.reg[dst] as u32 % insn.imm as u32) as u64 • 无除零检查
MOD32_REG 算术 32位取模(寄存器) throw_error!(DivideByZero; self, self.reg[src], u32);
(self.reg[dst] as u32 % self.reg[src] as u32) as u64
必须检查除零
XOR32_IMM 逻辑 32位异或(立即数) (self.reg[dst] as u32 ^ insn.imm as u32) as u64 • 按位异或
XOR32_REG 逻辑 32位异或(寄存器) (self.reg[dst] as u32 ^ self.reg[src] as u32) as u64 同上
MOV32_IMM 数据移动 32位立即数移动 insn.imm as u32 as u64 • 只取低32位,高位清零
MOV32_REG 数据移动 32位寄存器移动 if explicit_sign_extension
  self.reg[src] as i32 as i64 as u64
else
  self.reg[src] as u32 as u64
符号扩展行为依赖版本
ARSH32_IMM 移位 32位算术右移
(立即数)
(self.reg[dst] as i32).wrapping_shr(insn.imm as u32) as u32 as u64 保留符号位,高位补符号位
ARSH32_REG 移位 32位算术右移
(寄存器)
(self.reg[dst] as i32).wrapping_shr(self.reg[src] as u32) as u32 as u64 同上

字节序转换指令

指令 类别 作用 代码实现 注意事项
LE 字节序 转小端 match insn.imm {
  16 => (self.reg[dst] as u16).to_le(),
  32 => (self.reg[dst] as u32).to_le(),
  64 => self.reg[dst].to_le(),
  _ => throw_error!
}
imm 必须是 16/32/64
• 版本检查 !disable_le()
BE 字节序 转大端 match insn.imm {
  16 => (self.reg[dst] as u16).to_be(),
  32 => (self.reg[dst] as u32).to_be(),
  64 => self.reg[dst].to_be(),
  _ => throw_error!
}
imm 必须是 16/32/64

64位算术逻辑指令 (BPF_ALU64)

指令 类别 作用 代码实现 注意事项
ADD64_IMM 算术 64位加法(立即数) self.reg[dst].wrapping_add(insn.imm as u64) wrapping_add 防溢出
ADD64_REG 算术 64位加法(寄存器) self.reg[dst].wrapping_add(self.reg[src]) 同上
SUB64_IMM 算术 64位减法(立即数) if swap_sub_reg_imm_operands()
  (insn.imm as u64).wrapping_sub(self.reg[dst])
else
  self.reg[dst].wrapping_sub(insn.imm as u64)
操作数顺序依赖版本
SUB64_REG 算术 64位减法(寄存器) self.reg[dst].wrapping_sub(self.reg[src]) dst - src
MUL64_IMM 算术 64位乘法(立即数) self.reg[dst].wrapping_mul(insn.imm as u64) 非PQR版本
MUL64_REG 算术 64位乘法(寄存器) self.reg[dst].wrapping_mul(self.reg[src]) 非PQR版本
DIV64_IMM 算术 64位无符号除法
(立即数)
self.reg[dst] /= insn.imm as u64 无除零检查
DIV64_REG 算术 64位无符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] /= self.reg[src]
必须检查除零
OR64_IMM 逻辑 64位或运算(立即数) self.reg[dst] |= insn.imm as u64 按位或
OR64_REG 逻辑 64位或运算(寄存器) self.reg[dst] |= self.reg[src] 同上
AND64_IMM 逻辑 64位与运算(立即数) self.reg[dst] &= insn.imm as u64 按位与
AND64_REG 逻辑 64位与运算(寄存器) self.reg[dst] &= self.reg[src] 同上
LSH64_IMM 移位 64位左移(立即数) self.reg[dst].wrapping_shl(insn.imm as u32) wrapping_shl 防溢出
• 只移低6位
LSH64_REG 移位 64位左移(寄存器) self.reg[dst].wrapping_shl(self.reg[src] as u32) 同上
RSH64_IMM 移位 64位右移(立即数) self.reg[dst].wrapping_shr(insn.imm as u32) 逻辑右移
RSH64_REG 移位 64位右移(寄存器) self.reg[dst].wrapping_shr(self.reg[src] as u32) 同上
NEG64 算术 64位取反 (self.reg[dst] as i64).wrapping_neg() as u64 wrapping_neg 处理 i64::MIN
MOD64_IMM 算术 64位取模(立即数) self.reg[dst] %= insn.imm as u64 无除零检查
MOD64_REG 算术 64位取模(寄存器) throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] %= self.reg[src]
必须检查除零
XOR64_IMM 逻辑 64位异或(立即数) self.reg[dst] ^= insn.imm as u64 按位异或
XOR64_REG 逻辑 64位异或(寄存器) self.reg[dst] ^= self.reg[src] 同上
MOV64_IMM 数据移动 64位立即数移动 self.reg[dst] = insn.imm as u64 直接赋值
MOV64_REG 数据移动 64位寄存器移动 self.reg[dst] = self.reg[src] 直接复制
ARSH64_IMM 移位 64位算术右移
(立即数)
(self.reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64 保留符号位
ARSH64_REG 移位 64位算术右移
(寄存器)
(self.reg[dst] as i64).wrapping_shr(self.reg[src] as u32) as u64 同上

PQR 扩展指令

指令 类别 作用 代码实现 注意事项
LMUL32_IMM 乘法 32位低乘法(立即数) (self.reg[dst] as u32).wrapping_mul(insn.imm as u32) as u64 • PQR版本
• 取32位结果低32位
LMUL32_REG 乘法 32位低乘法(寄存器) (self.reg[dst] as u32).wrapping_mul(self.reg[src] as u32) as u64 同上
LMUL64_IMM 乘法 64位低乘法(立即数) self.reg[dst].wrapping_mul(insn.imm as u64) PQR版本
LMUL64_REG 乘法 64位低乘法(寄存器) self.reg[dst].wrapping_mul(self.reg[src]) 同上
UHMUL64_IMM 乘法 64位无符号高乘法
(立即数)
(self.reg[dst] as u128).wrapping_mul(insn.imm as u32 as u128).wrapping_shr(64) as u64 128位中间结果取高64位
UHMUL64_REG 乘法 64位无符号高乘法
(寄存器)
(self.reg[dst] as u128).wrapping_mul(self.reg[src] as u128).wrapping_shr(64) as u64 同上
SHMUL64_IMM 乘法 64位有符号高乘法
(立即数)
(self.reg[dst] as i64 as i128).wrapping_mul(insn.imm as i128).wrapping_shr(64) as u64 有符号扩展后相乘
SHMUL64_REG 乘法 64位有符号高乘法
(寄存器)
(self.reg[dst] as i64 as i128).wrapping_mul(self.reg[src] as i64 as i128).wrapping_shr(64) as u64 同上
UDIV32_IMM 除法 32位无符号除法
(立即数)
(self.reg[dst] as u32 / insn.imm as u32) as u64 PQR版本
UDIV32_REG 除法 32位无符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u32);
(self.reg[dst] as u32 / self.reg[src] as u32) as u64
必须检查除零
UDIV64_IMM 除法 64位无符号除法
(立即数)
self.reg[dst] /= insn.imm as u32 as u64 注意类型转换
UDIV64_REG 除法 64位无符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] /= self.reg[src]
必须检查除零
UREM32_IMM 取模 32位无符号取模
(立即数)
(self.reg[dst] as u32 % insn.imm as u32) as u64 PQR版本
UREM32_REG 取模 32位无符号取模
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u32);
(self.reg[dst] as u32 % self.reg[src] as u32) as u64
必须检查除零
UREM64_IMM 取模 64位无符号取模
(立即数)
self.reg[dst] %= insn.imm as u32 as u64 同上
UREM64_REG 取模 64位无符号取模
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] %= self.reg[src]
必须检查除零
SDIV32_IMM 除法 32位有符号除法
(立即数)
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i32);
(self.reg[dst] as i32 / insn.imm as i32) as u32 as u64
需检查溢出 (i32::MIN / -1)
SDIV32_REG 除法 32位有符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], i32);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i32);
(self.reg[dst] as i32 / self.reg[src] as i32) as u32 as u64
双重检查:除零和溢出
SDIV64_IMM 除法 64位有符号除法
(立即数)
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i64);
(self.reg[dst] as i64 / insn.imm) as u64
检查 i64::MIN / -1
SDIV64_REG 除法 64位有符号除法
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], i64);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i64);
(self.reg[dst] as i64 / self.reg[src] as i64) as u64
双重检查
SREM32_IMM 取模 32位有符号取模
(立即数)
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i32);
(self.reg[dst] as i32 % insn.imm as i32) as u32 as u64
检查溢出
SREM32_REG 取模 32位有符号取模
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], i32);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i32);
(self.reg[dst] as i32 % self.reg[src] as i32) as u32 as u64
双重检查
SREM64_IMM 取模 64位有符号取模
(立即数)
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i64);
(self.reg[dst] as i64 % insn.imm) as u64
检查溢出
SREM64_REG 取模 64位有符号取模
(寄存器)
throw_error!(DivideByZero; self, self.reg[src], i64);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i64);
(self.reg[dst] as i64 % self.reg[src] as i64) as u64
双重检查

32位跳转指令 (BPF_JMP32)

指令 类别 条件 代码实现 注意事项
JEQ32_IMM 条件跳转 (u32)dst == (u32)imm if (self.reg[dst] as u32) == insn.imm as u32 {
  next_pc = (next_pc as i64 + insn.off as i64) as u64;
}
需启用JMP32
• 跳转目标由 check_pc! 验证
JEQ32_REG 条件跳转 (u32)dst == (u32)src if (self.reg[dst] as u32) == self.reg[src] as u32 { ... } 同上
JGT32_IMM 条件跳转 (u32)dst > (u32)imm if (self.reg[dst] as u32) > insn.imm as u32 { ... } 无符号大于
JGT32_REG 条件跳转 (u32)dst > (u32)src if (self.reg[dst] as u32) > self.reg[src] as u32 { ... } 同上
JGE32_IMM 条件跳转 (u32)dst >= (u32)imm if (self.reg[dst] as u32) >= insn.imm as u32 { ... } 无符号大于等于
JGE32_REG 条件跳转 (u32)dst >= (u32)src if (self.reg[dst] as u32) >= self.reg[src] as u32 { ... } 同上
JLT32_IMM 条件跳转 (u32)dst < (u32)imm if (self.reg[dst] as u32) < insn.imm as u32 { ... } 无符号小于
JLT32_REG 条件跳转 (u32)dst < (u32)src if (self.reg[dst] as u32) < self.reg[src] as u32 { ... } 同上
JLE32_IMM 条件跳转 (u32)dst <= (u32)imm if (self.reg[dst] as u32) <= insn.imm as u32 { ... } 无符号小于等于
JLE32_REG 条件跳转 (u32)dst <= (u32)src if (self.reg[dst] as u32) <= self.reg[src] as u32 { ... } 同上
JSET32_IMM 条件跳转 (u32)dst & (u32)imm != 0 if (self.reg[dst] as u32) & insn.imm as u32 != 0 { ... } 位测试
JSET32_REG 条件跳转 (u32)dst & (u32)src != 0 if (self.reg[dst] as u32) & self.reg[src] as u32 != 0 { ... } 同上
JNE32_IMM 条件跳转 (u32)dst != (u32)imm if (self.reg[dst] as u32) != insn.imm as u32 { ... } 不等
JNE32_REG 条件跳转 (u32)dst != (u32)src if (self.reg[dst] as u32) != self.reg[src] as u32 { ... } 同上
JSGT32_IMM 条件跳转 (i32)dst > (i32)imm if (self.reg[dst] as i32) > insn.imm as i32 { ... } 有符号大于
JSGT32_REG 条件跳转 (i32)dst > (i32)src if (self.reg[dst] as i32) > self.reg[src] as i32 { ... } 同上
JSGE32_IMM 条件跳转 (i32)dst >= (i32)imm if (self.reg[dst] as i32) >= insn.imm as i32 { ... } 有符号大于等于
JSGE32_REG 条件跳转 (i32)dst >= (i32)src if (self.reg[dst] as i32) >= self.reg[src] as i32 { ... } 同上
JSLT32_IMM 条件跳转 (i32)dst < (i32)imm if (self.reg[dst] as i32) < insn.imm as i32 { ... } 有符号小于
JSLT32_REG 条件跳转 (i32)dst < (i32)src if (self.reg[dst] as i32) < self.reg[src] as i32 { ... } 同上
JSLE32_IMM 条件跳转 (i32)dst <= (i32)imm if (self.reg[dst] as i32) <= insn.imm as i32 { ... } 有符号小于等于
JSLE32_REG 条件跳转 (i32)dst <= (i32)src if (self.reg[dst] as i32) <= self.reg[src] as i32 { ... } 同上

64位跳转指令 (BPF_JMP64)

指令 类别 条件 代码实现 注意事项
JA 无条件跳转 总是跳转 next_pc = (next_pc as i64 + insn.off as i64) as u64; 直接加偏移
JEQ64_IMM 条件跳转 dst == imm if self.reg[dst] == insn.imm as u64 { next_pc = ... } 64位比较
JEQ64_REG 条件跳转 dst == src if self.reg[dst] == self.reg[src] { ... } 同上
JGT64_IMM 条件跳转 dst > imm if self.reg[dst] > insn.imm as u64 { ... } 无符号大于
JGT64_REG 条件跳转 dst > src if self.reg[dst] > self.reg[src] { ... } 同上
JGE64_IMM 条件跳转 dst >= imm if self.reg[dst] >= insn.imm as u64 { ... } 无符号大于等于
JGE64_REG 条件跳转 dst >= src if self.reg[dst] >= self.reg[src] { ... } 同上
JLT64_IMM 条件跳转 dst < imm if self.reg[dst] < insn.imm as u64 { ... } 无符号小于
JLT64_REG 条件跳转 dst < src if self.reg[dst] < self.reg[src] { ... } 同上
JLE64_IMM 条件跳转 dst <= imm if self.reg[dst] <= insn.imm as u64 { ... } 无符号小于等于
JLE64_REG 条件跳转 dst <= src if self.reg[dst] <= self.reg[src] { ... } 同上
JSET64_IMM 条件跳转 dst & imm != 0 if self.reg[dst] & insn.imm as u64 != 0 { ... } 位测试
JSET64_REG 条件跳转 dst & src != 0 if self.reg[dst] & self.reg[src] != 0 { ... } 同上
JNE64_IMM 条件跳转 dst != imm if self.reg[dst] != insn.imm as u64 { ... } 不等
JNE64_REG 条件跳转 dst != src if self.reg[dst] != self.reg[src] { ... } 同上
JSGT64_IMM 条件跳转 (i64)dst > imm if (self.reg[dst] as i64) > insn.imm { ... } 有符号大于
JSGT64_REG 条件跳转 (i64)dst > (i64)src if (self.reg[dst] as i64) > self.reg[src] as i64 { ... } 同上
JSGE64_IMM 条件跳转 (i64)dst >= imm if (self.reg[dst] as i64) >= insn.imm { ... } 有符号大于等于
JSGE64_REG 条件跳转 (i64)dst >= (i64)src if (self.reg[dst] as i64) >= self.reg[src] as i64 { ... } 同上
JSLT64_IMM 条件跳转 (i64)dst < imm if (self.reg[dst] as i64) < insn.imm { ... } 有符号小于
JSLT64_REG 条件跳转 (i64)dst < (i64)src if (self.reg[dst] as i64) < self.reg[src] as i64 { ... } 同上
JSLE64_IMM 条件跳转 (i64)dst <= imm if (self.reg[dst] as i64) <= insn.imm { ... } 有符号小于等于
JSLE64_REG 条件跳转 (i64)dst <= (i64)src if (self.reg[dst] as i64) <= self.reg[src] as i64 { ... } 同上

调用和返回指令

指令 类别 作用 代码实现 注意事项
CALL_REG 调用 通过寄存器
间接调用
let target_pc = if callx_uses_src_reg() { self.reg[src] }
  else if callx_uses_dst_reg() { self.reg[dst] }
  else { self.reg[insn.imm as usize] };
if !self.push_frame(config) { return false; }
check_pc!(self, next_pc, target_pc.wrapping_sub(self.program_vm_addr) / 8);
目标地址来源依赖版本
• 需 push_frame 保存状态
• 地址需转换为指令索引
CALL_IMM 调用 直接调用/
系统调用
复杂逻辑处理系统调用内部函数 • 可调用外部系统调用或内部函数
• 需查注册表
• 未找到时报错
EXIT 返回 从函数返回 if self.vm.call_depth == 0 {
  self.vm.program_result = Ok(self.reg[0]);
  return false;
} else {
  self.vm.call_depth -= 1;
  从帧恢复寄存器
  check_pc!(self, next_pc, frame.target_pc);
}
最外层返回时程序结束
• 内层返回需恢复寄存器
• PC从帧中恢复

新旧内存指令对照

新版指令 旧版指令 作用 版本条件
LD_1B_REG LD_B_REG 加载1字节 move_memory_instruction_classes()
LD_2B_REG LD_H_REG 加载2字节 同上
LD_4B_REG LD_W_REG 加载4字节 同上
LD_8B_REG LD_DW_REG 加载8字节 同上
ST_1B_IMM ST_B_IMM 存储1字节立即数 同上
ST_1B_REG ST_B_REG 存储1字节寄存器 同上
ST_2B_IMM ST_H_IMM 存储2字节立即数 同上
ST_2B_REG ST_H_REG 存储2字节寄存器 同上
ST_4B_IMM ST_W_IMM 存储4字节立即数 同上
ST_4B_REG ST_W_REG 存储4字节寄存器 同上
ST_8B_IMM ST_DW_IMM 存储8字节立即数 同上
ST_8B_REG ST_DW_REG 存储8字节寄存器 同上

其他指令

指令 类别 作用 代码实现 注意事项
HOR64_IMM 逻辑 64位高32位或运算 self.reg[dst] |= (insn.imm as u64).wrapping_shl(32); 仅在禁用LDDW时可用

版本特性说明

特性 作用 相关指令 代码检查
disable_lddw() 禁用64位立即数加载 LD_DW_IMM if !disable_lddw()
move_memory_instruction_classes() 切换新版内存指令 LD/ST新旧两组 if !move_memory_instruction_classes()(旧版)
if move_memory_instruction_classes()(新版)
enable_pqr() 启用PQR扩展指令 PQR类所有指令 if enable_pqr()
enable_jmp32() 启用32位跳转 JMP32类所有指令 if enable_jmp32()
disable_neg() 禁用取反指令 NEG32, NEG64 if !disable_neg()
disable_le() 禁用小端转换 LE if !disable_le()
explicit_sign_extension_of_results() 显式符号扩展 32位运算结果 if explicit_sign_extension()
swap_sub_reg_imm_operands() 交换减法操作数 SUB32_IMM, SUB64_IMM if swap_sub_reg_imm_operands()
callx_uses_src_reg() CALL_REG使用src CALL_REG if callx_uses_src_reg()
callx_uses_dst_reg() CALL_REG使用dst CALL_REG else if callx_uses_dst_reg()
static_syscalls() 静态系统调用 CALL_IMM if static_syscalls()

完整代码

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
ebpf::LD_DW_IMM if !self.executable.get_sbpf_version().disable_lddw() => {
ebpf::augment_lddw_unchecked(self.program, &mut insn);
self.reg[dst] = insn.imm as u64;
self.reg[11] += 1;
next_pc += 1;
},

// BPF_LDX class
ebpf::LD_B_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u8);
},
ebpf::LD_H_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u16);
},
ebpf::LD_W_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u32);
},
ebpf::LD_DW_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u64);
},

// BPF_ST class
ebpf::ST_B_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u8);
},
ebpf::ST_H_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u16);
},
ebpf::ST_W_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u32);
},
ebpf::ST_DW_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u64);
},

// BPF_STX class
ebpf::ST_B_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u8);
},
ebpf::ST_H_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u16);
},
ebpf::ST_W_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u32);
},
ebpf::ST_DW_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u64);
},

// BPF_ALU32_LOAD class
ebpf::ADD32_IMM => self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_add(insn.imm as i32)),
ebpf::ADD32_REG => self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_add(self.reg[src] as i32)),
ebpf::SUB32_IMM => if self.executable.get_sbpf_version().swap_sub_reg_imm_operands() {
self.reg[dst] = self.sign_extension((insn.imm as i32).wrapping_sub(self.reg[dst] as i32))
} else {
self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_sub(insn.imm as i32))
},
ebpf::SUB32_REG => self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_sub(self.reg[src] as i32)),
ebpf::MUL32_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_mul(insn.imm as i32)),
ebpf::MUL32_REG if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.sign_extension((self.reg[dst] as i32).wrapping_mul(self.reg[src] as i32)),
ebpf::LD_1B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u8);
},
ebpf::DIV32_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u32 / insn.imm as u32) as u64,
ebpf::DIV32_REG if !self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u32);
self.reg[dst] = (self.reg[dst] as u32 / self.reg[src] as u32) as u64;
},
ebpf::LD_2B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u16);
},
ebpf::OR32_IMM => self.reg[dst] = (self.reg[dst] as u32 | insn.imm as u32) as u64,
ebpf::OR32_REG => self.reg[dst] = (self.reg[dst] as u32 | self.reg[src] as u32) as u64,
ebpf::AND32_IMM => self.reg[dst] = (self.reg[dst] as u32 & insn.imm as u32) as u64,
ebpf::AND32_REG => self.reg[dst] = (self.reg[dst] as u32 & self.reg[src] as u32) as u64,
ebpf::LSH32_IMM => self.reg[dst] = (self.reg[dst] as u32).wrapping_shl(insn.imm as u32) as u64,
ebpf::LSH32_REG => self.reg[dst] = (self.reg[dst] as u32).wrapping_shl(self.reg[src] as u32) as u64,
ebpf::RSH32_IMM => self.reg[dst] = (self.reg[dst] as u32).wrapping_shr(insn.imm as u32) as u64,
ebpf::RSH32_REG => self.reg[dst] = (self.reg[dst] as u32).wrapping_shr(self.reg[src] as u32) as u64,
ebpf::NEG32 if !self.executable.get_sbpf_version().disable_neg() => self.reg[dst] = (self.reg[dst] as i32).wrapping_neg() as u64 & (u32::MAX as u64),
ebpf::LD_4B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u32);
},
ebpf::MOD32_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u32 % insn.imm as u32) as u64,
ebpf::MOD32_REG if !self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u32);
self.reg[dst] = (self.reg[dst] as u32 % self.reg[src] as u32) as u64;
},
ebpf::LD_8B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u64);
},
ebpf::XOR32_IMM => self.reg[dst] = (self.reg[dst] as u32 ^ insn.imm as u32) as u64,
ebpf::XOR32_REG => self.reg[dst] = (self.reg[dst] as u32 ^ self.reg[src] as u32) as u64,
ebpf::MOV32_IMM => self.reg[dst] = insn.imm as u32 as u64,
ebpf::MOV32_REG => if self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
self.reg[src] as i32 as i64 as u64
} else {
self.reg[src] as u32 as u64
},
ebpf::ARSH32_IMM => self.reg[dst] = (self.reg[dst] as i32).wrapping_shr(insn.imm as u32) as u32 as u64,
ebpf::ARSH32_REG => self.reg[dst] = (self.reg[dst] as i32).wrapping_shr(self.reg[src] as u32) as u32 as u64,
ebpf::LE if !self.executable.get_sbpf_version().disable_le() => {
self.reg[dst] = match insn.imm {
16 => (self.reg[dst] as u16).to_le() as u64,
32 => (self.reg[dst] as u32).to_le() as u64,
64 => self.reg[dst].to_le(),
_ => {
throw_error!(self, EbpfError::InvalidInstruction);
}
};
},
ebpf::BE => {
self.reg[dst] = match insn.imm {
16 => (self.reg[dst] as u16).to_be() as u64,
32 => (self.reg[dst] as u32).to_be() as u64,
64 => self.reg[dst].to_be(),
_ => {
throw_error!(self, EbpfError::InvalidInstruction);
}
};
},

// BPF_ALU64_STORE class
ebpf::ADD64_IMM => self.reg[dst] = self.reg[dst].wrapping_add(insn.imm as u64),
ebpf::ADD64_REG => self.reg[dst] = self.reg[dst].wrapping_add(self.reg[src]),
ebpf::SUB64_IMM => if self.executable.get_sbpf_version().swap_sub_reg_imm_operands() {
self.reg[dst] = (insn.imm as u64).wrapping_sub(self.reg[dst])
} else {
self.reg[dst] = self.reg[dst].wrapping_sub(insn.imm as u64)
},
ebpf::SUB64_REG => self.reg[dst] = self.reg[dst].wrapping_sub(self.reg[src]),
ebpf::MUL64_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.reg[dst].wrapping_mul(insn.imm as u64),
ebpf::ST_1B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u8);
},
ebpf::MUL64_REG if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.reg[dst].wrapping_mul(self.reg[src]),
ebpf::ST_1B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u8);
},
ebpf::DIV64_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] /= insn.imm as u64,
ebpf::ST_2B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u16);
},
ebpf::DIV64_REG if !self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] /= self.reg[src];
},
ebpf::ST_2B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u16);
},
ebpf::OR64_IMM => self.reg[dst] |= insn.imm as u64,
ebpf::OR64_REG => self.reg[dst] |= self.reg[src],
ebpf::AND64_IMM => self.reg[dst] &= insn.imm as u64,
ebpf::AND64_REG => self.reg[dst] &= self.reg[src],
ebpf::LSH64_IMM => self.reg[dst] = self.reg[dst].wrapping_shl(insn.imm as u32),
ebpf::LSH64_REG => self.reg[dst] = self.reg[dst].wrapping_shl(self.reg[src] as u32),
ebpf::RSH64_IMM => self.reg[dst] = self.reg[dst].wrapping_shr(insn.imm as u32),
ebpf::RSH64_REG => self.reg[dst] = self.reg[dst].wrapping_shr(self.reg[src] as u32),
ebpf::ST_4B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u32);
},
ebpf::NEG64 if !self.executable.get_sbpf_version().disable_neg() => self.reg[dst] = (self.reg[dst] as i64).wrapping_neg() as u64,
ebpf::ST_4B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u32);
},
ebpf::MOD64_IMM if !self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] %= insn.imm as u64,
ebpf::ST_8B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, u64);
},
ebpf::MOD64_REG if !self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] %= self.reg[src];
},
ebpf::ST_8B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, u64);
},
ebpf::XOR64_IMM => self.reg[dst] ^= insn.imm as u64,
ebpf::XOR64_REG => self.reg[dst] ^= self.reg[src],
ebpf::MOV64_IMM => self.reg[dst] = insn.imm as u64,
ebpf::MOV64_REG => self.reg[dst] = self.reg[src],
ebpf::ARSH64_IMM => self.reg[dst] = (self.reg[dst] as i64).wrapping_shr(insn.imm as u32) as u64,
ebpf::ARSH64_REG => self.reg[dst] = (self.reg[dst] as i64).wrapping_shr(self.reg[src] as u32) as u64,
ebpf::HOR64_IMM if self.executable.get_sbpf_version().disable_lddw() => {
self.reg[dst] |= (insn.imm as u64).wrapping_shl(32);
}

// BPF_PQR class
ebpf::LMUL32_IMM if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u32).wrapping_mul(insn.imm as u32) as u64,
ebpf::LMUL32_REG if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u32).wrapping_mul(self.reg[src] as u32) as u64,
ebpf::LMUL64_IMM if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.reg[dst].wrapping_mul(insn.imm as u64),
ebpf::LMUL64_REG if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = self.reg[dst].wrapping_mul(self.reg[src]),
ebpf::UHMUL64_IMM if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u128).wrapping_mul(insn.imm as u32 as u128).wrapping_shr(64) as u64,
ebpf::UHMUL64_REG if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as u128).wrapping_mul(self.reg[src] as u128).wrapping_shr(64) as u64,
ebpf::SHMUL64_IMM if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as i64 as i128).wrapping_mul(insn.imm as i128).wrapping_shr(64) as u64,
ebpf::SHMUL64_REG if self.executable.get_sbpf_version().enable_pqr() => self.reg[dst] = (self.reg[dst] as i64 as i128).wrapping_mul(self.reg[src] as i64 as i128).wrapping_shr(64) as u64,
ebpf::UDIV32_IMM if self.executable.get_sbpf_version().enable_pqr() => {
self.reg[dst] = (self.reg[dst] as u32 / insn.imm as u32) as u64;
}
ebpf::UDIV32_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u32);
self.reg[dst] = (self.reg[dst] as u32 / self.reg[src] as u32) as u64;
},
ebpf::UDIV64_IMM if self.executable.get_sbpf_version().enable_pqr() => {
self.reg[dst] /= insn.imm as u32 as u64;
}
ebpf::UDIV64_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] /= self.reg[src];
},
ebpf::UREM32_IMM if self.executable.get_sbpf_version().enable_pqr() => {
self.reg[dst] = (self.reg[dst] as u32 % insn.imm as u32) as u64;
}
ebpf::UREM32_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u32);
self.reg[dst] = (self.reg[dst] as u32 % self.reg[src] as u32) as u64;
},
ebpf::UREM64_IMM if self.executable.get_sbpf_version().enable_pqr() => {
self.reg[dst] %= insn.imm as u32 as u64;
}
ebpf::UREM64_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], u64);
self.reg[dst] %= self.reg[src];
},
ebpf::SDIV32_IMM if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i32);
self.reg[dst] = (self.reg[dst] as i32 / insn.imm as i32) as u32 as u64;
}
ebpf::SDIV32_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], i32);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i32);
self.reg[dst] = (self.reg[dst] as i32 / self.reg[src] as i32) as u32 as u64;
},
ebpf::SDIV64_IMM if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i64);
self.reg[dst] = (self.reg[dst] as i64 / insn.imm) as u64;
}
ebpf::SDIV64_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], i64);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i64);
self.reg[dst] = (self.reg[dst] as i64 / self.reg[src] as i64) as u64;
},
ebpf::SREM32_IMM if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i32);
self.reg[dst] = (self.reg[dst] as i32 % insn.imm as i32) as u32 as u64;
}
ebpf::SREM32_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], i32);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i32);
self.reg[dst] = (self.reg[dst] as i32 % self.reg[src] as i32) as u32 as u64;
},
ebpf::SREM64_IMM if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideOverflow; self, insn.imm, self.reg[dst], i64);
self.reg[dst] = (self.reg[dst] as i64 % insn.imm) as u64;
}
ebpf::SREM64_REG if self.executable.get_sbpf_version().enable_pqr() => {
throw_error!(DivideByZero; self, self.reg[src], i64);
throw_error!(DivideOverflow; self, self.reg[src], self.reg[dst], i64);
self.reg[dst] = (self.reg[dst] as i64 % self.reg[src] as i64) as u64;
},

// BPF_JMP32 class
ebpf::JEQ32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) == insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JEQ32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) == self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGT32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) > insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGT32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) > self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGE32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) >= insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGE32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) >= self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLT32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) < insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLT32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) < self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLE32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) <= insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLE32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) <= self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSET32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) & insn.imm as u32 != 0 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSET32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) & self.reg[src] as u32 != 0 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JNE32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) != insn.imm as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JNE32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as u32) != self.reg[src] as u32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGT32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) > insn.imm as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGT32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) > self.reg[src] as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGE32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) >= insn.imm as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGE32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) >= self.reg[src] as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLT32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) < insn.imm as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLT32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) < self.reg[src] as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLE32_IMM if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) <= insn.imm as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLE32_REG if self.executable.get_sbpf_version().enable_jmp32() => if (self.reg[dst] as i32) <= self.reg[src] as i32 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },

// BPF_JMP64 class
ebpf::JA => { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JEQ64_IMM => if self.reg[dst] == insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JEQ64_REG => if self.reg[dst] == self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGT64_IMM => if self.reg[dst] > insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGT64_REG => if self.reg[dst] > self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGE64_IMM => if self.reg[dst] >= insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JGE64_REG => if self.reg[dst] >= self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLT64_IMM => if self.reg[dst] < insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLT64_REG => if self.reg[dst] < self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLE64_IMM => if self.reg[dst] <= insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JLE64_REG => if self.reg[dst] <= self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSET64_IMM => if self.reg[dst] & insn.imm as u64 != 0 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSET64_REG => if self.reg[dst] & self.reg[src] != 0 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JNE64_IMM => if self.reg[dst] != insn.imm as u64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JNE64_REG => if self.reg[dst] != self.reg[src] { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGT64_IMM => if (self.reg[dst] as i64) > insn.imm { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGT64_REG => if (self.reg[dst] as i64) > self.reg[src] as i64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGE64_IMM => if (self.reg[dst] as i64) >= insn.imm { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSGE64_REG => if (self.reg[dst] as i64) >= self.reg[src] as i64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLT64_IMM => if (self.reg[dst] as i64) < insn.imm { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLT64_REG => if (self.reg[dst] as i64) < self.reg[src] as i64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLE64_IMM => if (self.reg[dst] as i64) <= insn.imm { next_pc = (next_pc as i64 + insn.off as i64) as u64; },
ebpf::JSLE64_REG => if (self.reg[dst] as i64) <= self.reg[src] as i64 { next_pc = (next_pc as i64 + insn.off as i64) as u64; },

ebpf::CALL_REG => {
let target_pc = if self.executable.get_sbpf_version().callx_uses_src_reg() {
self.reg[src]
} else if self.executable.get_sbpf_version().callx_uses_dst_reg() {
self.reg[dst]
} else {
self.reg[insn.imm as usize]
};
if !self.push_frame(config) {
return false;
}
check_pc!(self, next_pc, target_pc.wrapping_sub(self.program_vm_addr) / ebpf::INSN_SIZE as u64);
},

ebpf::CALL_IMM => {
let mut resolved = false;
// External syscall
if !self.executable.get_sbpf_version().static_syscalls() || insn.src == 0 {
if let Some((_, function)) = self.executable.get_loader().get_function_registry().lookup_by_key(insn.imm as u32) {
self.reg[0] = match self.dispatch_syscall(function) {
ProgramResult::Ok(value) => *value,
ProgramResult::Err(_err) => return false,
};
resolved = true;
}
}
// Internal call
if self.executable.get_sbpf_version().static_syscalls() {
let target_pc = (next_pc as i64).saturating_add(insn.imm);
if ebpf::is_pc_in_program(self.program, target_pc as usize) && insn.src == 1 {
if !self.push_frame(config) {
return false;
}
next_pc = target_pc as u64;
resolved = true;
}
} else if let Some((_, target_pc)) =
self.executable
.get_function_registry()
.lookup_by_key(insn.imm as u32) {
if !self.push_frame(config) {
return false;
}
check_pc!(self, next_pc, target_pc as u64);
resolved = true;
}
if !resolved {
throw_error!(self, EbpfError::UnsupportedInstruction);
}
}
ebpf::EXIT => {
if self.vm.call_depth == 0 {
if config.enable_instruction_meter && self.vm.due_insn_count > self.vm.previous_instruction_meter {
throw_error!(self, EbpfError::ExceededMaxInstructions);
}
self.vm.program_result = ProgramResult::Ok(self.reg[0]);
return false;
}
// Return from BPF to BPF call
self.vm.call_depth -= 1;
let frame = &self.vm.call_frames[self.vm.call_depth as usize];
self.reg[ebpf::FRAME_PTR_REG] = frame.frame_pointer;
self.reg[ebpf::FIRST_SCRATCH_REG
..ebpf::FIRST_SCRATCH_REG + ebpf::SCRATCH_REGS]
.copy_from_slice(&frame.caller_saved_registers);
check_pc!(self, next_pc, frame.target_pc);
}
_ => throw_error!(self, EbpfError::UnsupportedInstruction),