1 个不稳定版本
0.1.0 | 2023 年 1 月 6 日 |
---|
#1674 在 Rust 模式
41KB
849 行
opcode-macros
这个包提供了一个 opcode_match
宏,它可以生成针对位字段中编码的指令的复杂匹配语句。
格式
基本格式如下
use opcode_macros::opcode_match;
let opcode = 0u8;
mod namespace {
pub const A_1: u8 = 0;
pub const A_2: u8 = 0;
pub const B_1: u8 = 0;
pub const B_2: u8 = 0;
}
let result =
opcode_match! {
opcode as u8 in namespace,
[[A_1: a1, A_2: a2], [B_1: b1, B_2: b2]] => {
// Code
1
}
_ => {
// Code
0
}
}
;
assert_eq!(result, 1);
它将生成类似这样的内容
let opcode = 0u8;
mod namespace {
pub const A_1: u8 = 0;
pub const A_2: u8 = 0;
pub const B_1: u8 = 0;
pub const B_2: u8 = 0;
}
const A_1_B_1: u8 = namespace::A_1 | namespace::B_1;
const A_1_B_2: u8 = namespace::A_1 | namespace::B_2;
const A_2_B_1: u8 = namespace::A_2 | namespace::B_1;
const A_2_B_2: u8 = namespace::A_2 | namespace::B_2;
match opcode {
A_1_B_1 => { /* Code */ }
A_1_B_2 => { /* Code */ }
A_2_B_1 => { /* Code */ }
A_2_B_2 => { /* Code */ }
_ => { /* Code */ }
}
匹配分支头
匹配分支头类似于 [[A_1: a1, A_2: a2, [B_1: b1, B_2: b2, ]]
.
例如,在 eBPF 指令中,BPF_ALU | BPF_K | BPF_ADD
是一个用于 32 位加法的指令,而 BPF_ALU | BPF_K | BPF_SUB
是一个用于 32 位减法的指令。为了匹配这些指令,我们使用以下代码
use opcode_macros::opcode_match;
use ebpf_consts::*;
let opcode = 0x04u8;
let mut result = 0u64;
let dst = 10u64;
let imm = 10u64;
opcode_match! {
opcode as u8 in ebpf_consts,
[[BPF_ALU: _], [BPF_K: _],
[BPF_ADD: add, BPF_SUB: sub]] => {
result = dst.#"wrapping_{}"2(imm);
}
_ => {}
}
assert_eq!(result, 20);
稍后我们将讨论模板规则。
在上面的例子中,你也可以使用其他一些变体
[BPF_ADD: "加", BPF_SUB: "减"]
[BPF_ADD: [加], BPF_SUB: [减]]
[BPF_ADD: ["加"], BPF_SUB: ["减"]]
[BPF_ADD: ["加", "额外1"], BPF_SUB: ["减", "额外2"]]
如果你想用符号如 "+" 替换代码的一部分,你需要引用这些符号,如 [BPF_ADD: "+"]
.
代码模板
替换
这不是一个真实世界的例子。
use opcode_macros::opcode_match;
use ebpf_consts::*;
use core::ops::Add;
let opcode = 0u8;
opcode_match! {
opcode as u8 in ebpf_consts,
[
// Group 0
[BPF_K: ["const", 1, 2, "Constant Operation"]],
// Group 1
[BPF_ADD: ["add", 3, 4, "Addition Operation"]],
] => {
// Use the first token in group 0 as a string
assert_eq!(#0, "const");
// Use the fourth token in group 0 as a string
assert_eq!(#:3:0, "Constant Operation");
assert_eq!(#:0:1, "add");
assert_eq!(#:3:1, "Addition Operation");
// Use raw tokens
assert_eq!(#:1:1, "3");
assert_eq!(#:1:=1, 3);
// 30.add(40) == 70, where #=1 is just #:0:=1
let value = 30isize;
assert_eq!(value.#=1(40), 70);
// With in-token substitution: add -> wrapping_add
assert_eq!(value.#"wrapping_{}"1(40), 70);
}
_ => panic!(),
}
条件块
use opcode_macros::opcode_match;
use ebpf_consts::*;
let opcode = BPF_X | BPF_ALU;
opcode_match! {
opcode as u8 in ebpf_consts,
[[BPF_X: x, BPF_K: k], [BPF_ALU: "alu", BPF_ALU64: "alu64"]] => {
#?((x))
println!("In V1 branch");
assert_eq!(#0, "x"); ##
#?((k))
println!("In V2 && {} branch", #1);
assert_eq!(#0, "k"); ##
#?((!"k"))
println!("In V2 && {} branch", #1);
assert_eq!(#0, "x"); ##
println!("Common");
}
_ => panic!(),
};
语法为 #?((cond1, cond2)|(cond3|cond4)|...) CODE ##
,只有在操作码匹配 (cond1 && cond2) || (cond3 && cond4) || ...
时才注入代码。条件可以通过感叹号 !cond1
取反。
我们不允许嵌套条件。
依赖项
~1.5MB
~35K SLoC