#match #bytecode #instructions #bit-field #generate

opcode-macros

位字段中编码的指令宏

1 个不稳定版本

0.1.0 2023 年 1 月 6 日

#1674Rust 模式

MIT 许可证

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