sbpf 的 interpreter.rs 解读(已完结)
本文为阅读 sbpf 的 interpreter.rs(Rust 语言) 的代码理解笔记。
sbpf 的 interpreter.rs 解读
编译器属性
1 | #![allow(clippy::arithmetic_side_effects)] |
这是一个 crate 级属性(注意 #! 表示应用于整个 crate),用于:
- 禁用 clippy 的算术副作用检查:clippy 通常警告可能溢出或异常的算术操作
- 在特定场景下需要:eBPF 解释器中很多算术操作使用
wrapping_*方法,明确允许溢出行为 - 出于性能考虑:避免额外的检查开销
版权声明、许可证声明
1 | // Derived from uBPF <https://github.com/iovisor/ubpf> |
模块文档注释
1 | //! Interpreter for eBPF programs. |
注意: //! 代表模块级文档注释
描述当前文件的主要功能:eBPF程序的解释器
模块导入:解释器执行所需的所有核心组件。
1 | use crate::{ |
ebpf- eBPF指令集定义和常量(操作码、结构体)elf::Executable- 可执行文件加载和程序信息error::{EbpfError, ProgramResult}- 错误类型和执行结果program::BuiltinFunction- 系统调用函数定义vm::{Config, ContextObject, EbpfVm}- 虚拟机配置、执行环境和状态管理
安全的内存访问包装器
1 | /// Virtual memory operation helper. |
该宏使用格式:
load 指令:
1
translate_memory_access!($self, load, $vm_addr, $type)
参数 类型 说明 $selfInterpreter实例解释器状态 load字面量 固定关键字,表示加载操作 $vm_addru64虚拟内存地址 $typeRust类型 数据类型: 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)
参数 类型 说明 $selfInterpreter实例解释器状态 store字面量 固定关键字,表示存储操作 $value表达式 要存储的值 $vm_addru64虚拟内存地址 $typeRust类型 数据类型: 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 | macro_rules! throw_error { |
除法溢出的含义为:如 i32::MIN ÷ (-1) 会导致溢出,因为 i32::MAX 比 -i32::MAX 小 1 。
程序计数器验证宏
1 | macro_rules! check_pc { |
检查函数 ebpf::is_pc_in_program(program, target_pc) 的功能:
- 检查
target_pc是否指向合法的指令位置 - 确保不会跳转到:
- 程序开始之前
- 程序结束之后
- 指令中间(如LD_DW的第二部分)
枚举定义:定义调试器执行模式的两种状态
1 | /// State of the interpreter during a debugging session // 这是枚举的文档注释 |
这个枚举用于控制调试器的执行模式。
其中 #[cfg(feature = "debugger")] 的含义:只有当启用 ‘debugger’ 编译特性时,下面的代码才会被编译和包含在最终程序中。
eBPF解释器的核心状态结构体定义
1 | /// State of an interpreter |
这段代码定义了 eBPF 解释器的核心状态结构体,其中除了寄存器公开,其余的各字段均为“仅当前 crate 内可见”
首先是结构体定义:
'a生命周期:解释器本身持有的引用的生命周期'b生命周期:通过 EbpfVm 传递的更深层引用的生命周期
(为什么需要两个生命周期?因为'b>='a,下面的 vm 字段持有对EbpfVm的可变引用('a),而 EbpfVm 内部又持有对上下文对象的引用(生命周期'b),这两个引用需要独立管理以确保内存安全。)C : ContextObject:泛型参数,必须是实现了 ContextObject trait 的类型(代表执行上下文)。下面是该特征的定义:1
2
3
4
5
6pub trait ContextObject {
// Required methods
fn trace(&mut self, state: [u64; 12]);
fn consume(&mut self, amount: u64);
fn get_remaining(&self) -> u64;
}方法详解
- trace(&mut self, state: [u64; 12])
调用时机:启用追踪时,每条指令执行后调用
参数:state 是 12 个寄存器的当前值(r0-r11)
用途:记录执行轨迹,用于调试和分析 - consume(&mut self, amount: u64)
调用时机:每条指令执行时调用
参数:amount 是要消耗的指令数(通常为 1)
用途:从指令计量器中扣除已执行的指令数 - get_remaining(&self) -> u64
调用时机:检查是否还有剩余指令配额时
返回值:剩余允许执行的指令数
用途:防止程序执行无限循环或超出资源限制
因为 Rust 语言的特征就相当于接口,所以这个定义要求这个特征就是为了让 eBPF 程序在不同地方都能跑,比如 Solana 区块链、普通 Linux 等,只要实现这个接口就行。
- trace(&mut self, state: [u64; 12])
然后是字段部分
虚拟机引用
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 | Interpreter |
Interpreter 的方法
1 | 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 | /// Creates a new interpreter state |
这是构造函数,创建新的解释器实例
函数签名:
1
2
3
4
5pub fn new(
vm: &'a mut EbpfVm<'b, C>,
executable: &'a Executable<C>,
registers: [u64; 12],
) -> Selfvm: &'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
14let (program_vm_addr, program) = executable.get_text_bytes();
Self {
vm, // 保存虚拟机引用
executable, // 保存可执行文件引用
program, // 保存程序字节码
program_vm_addr, // 保存程序虚拟地址
reg: registers, // 设置初始寄存器值
// 条件编译:仅在启用 debugger 特性时包含
debug_state: DebugState::Continue, // 默认继续模式
breakpoints: Vec::new(), // 空断点列表
}第一行的
get_text_bytes()是Executable结构体的一个方法,用于获取 eBPF 程序的代码段(text section)信息。该函数返回一个元组(u64, &[u8]),u64为程序代码段在虚拟内存中的起始地址,&[u8]为程序代码的字节码切片。后面的内容都是返回值
Self的内容,在新建一个Interpreter时,除了program_vm_addr和program两个值,其他值都来源于函数参数或者默认值。
get_dbg_pc()
1 | /// Translate between the virtual machines' pc value and the pc value used by the debugger |
这个函数仅启用 debugger 特性时编译,返回调试器使用的 PC 地址。转换的地址是绝对地址或文件偏移,GDB 等调试器只能通过这个进行设置断点等调试操作。
函数体仅一个返回值,即计算公式:reg[11] + offset ,其中 reg[11] 就是前面提到的程序计数器(PC),offset 就是代码段在整个文件或内存中的起始偏移地址,由 ELF 文件格式决定。
push_frame()
1 | fn push_frame(&mut self, config: &Config) -> bool { |
该函数用于处理函数调用时的栈帧压入操作。
函数签名
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
31let 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_frames是EbpfVm结构体中用于存储函数调用栈的向量(Vec<CallFrame>)。根据
CallFrame的定义,每个帧保存了:- 调用者保存的寄存器 (
caller_saved_registers) - 调用者的帧指针 (
frame_pointer) - 返回地址 (
target_pc)
call_depth是EbpfVm结构体中记录当前函数调用深度的字段(u64)。call_depth既是深度,也是下一个空闲帧的索引(但是u64需要转换为usize才可以当数组索引,所以有as usize)。这行代码完成了以下效果:
- 用当前调用深度
call_depth作为索引 - 从预分配的
call_frames数组中取出一个帧 - 获取这个帧的可变引用,准备写入数据(所以是
mut) - 这个帧将保存当前函数的上下文(见接下来的保存过程),为即将调用的新函数做准备
- 调用者保存的寄存器 (
保存过程:
1
2
3
4
5frame.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;这段代码保存了以下内容:
caller_saved_registers:调用者的临时寄存器(scratch registers,通过栈帧保存这些值,确保函数返回后能恢复原状。切片的上下限分别意思为:第一个临时寄存器的索引..第一个临时寄存器的索引 + 总共有几个临时寄存器)frame_pointer:当前栈帧指针(r10)target_pc:返回地址(当前 PC + 1),函数执行完后要跳回的位置
自增及检查过程:
1
2
3
4self.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
17if 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
7if 新版本(自动调整) {
// 计算每个栈帧要占多少空间
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 | fn sign_extension(&self, value: i32) -> 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 | /// Advances the interpreter state by one instruction |
该函数实现执行一条指令的操作,分为检查和执行两部分
属性:
1
2// 告诉 Rust 代码格式化工具(rustfmt)跳过这段代码,不要自动重新格式化。
// 强制编译器总是内联这个函数,即把函数体直接插入到调用处,而不是生成函数调用。函数介绍(除指令匹配细节)
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
26pub 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
4if 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
3if 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
17let 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 | fn dispatch_syscall(&mut self, function: BuiltinFunction<C>) -> &ProgramResult { |
这个函数负责调用系统功能(如打印、读写内存等):
- 暂停指令计数:进入系统调用前,先保存已执行的指令数
- 传递参数:把 eBPF 程序的
r0-r5寄存器值传给系统函数 - 执行系统函数:调用真正的系统功能
- 重置计数:系统调用完成后,清零指令计数(准备重新开始)
- 返回结果:把系统函数的执行结果返回给 eBPF 程序
简单说:eBPF 程序请求操作系统帮忙做事时,通过这个函数中转。
附录:eBPF 指令集以及指令匹配代码
加载指令
| 指令 | 类别 | 作用 | 代码实现 | 注意事项 |
|---|---|---|---|---|
LD_DW_IMM |
64位加载 | 加载64位立即数 到寄存器 |
ebpf::augment_lddw_unchecked()self.reg[dst] = insn.imm as u64self.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))elseself.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_extensionself.reg[src] as i32 as i64 as u64elseself.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])elseself.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 | ebpf::LD_DW_IMM if !self.executable.get_sbpf_version().disable_lddw() => { |





