#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BpfOpcode {
ADD,
SUB,
MUL,
DIV,
SDIV,
OR,
AND,
LSH,
RSH,
NEG,
MOD,
SMOD,
XOR,
MOV,
ARSH,
ADD32,
SUB32,
MUL32,
DIV32,
SDIV32,
OR32,
AND32,
LSH32,
RSH32,
NEG32,
MOD32,
SMOD32,
XOR32,
MOV32,
ARSH32,
END_LE16,
END_LE32,
END_LE64,
END_BE16,
END_BE32,
END_BE64,
LD_DW,
LDX_B,
LDX_H,
LDX_W,
LDX_DW,
ST_DW,
STX_DW,
ST_B,
ST_H,
ST_W,
ST_DW_IMM,
STX_B,
STX_H,
STX_W,
STX_DW_IMM,
JA,
JEQ,
JGT,
JGE,
JLT,
JLE,
JSET,
JNE,
JSGT,
JSGE,
JSLT,
JSLE,
CALL,
EXIT,
JEQ32,
JGT32,
JGE32,
JLT32,
JLE32,
JSET32,
JNE32,
JSGT32,
JSGE32,
JSLT32,
JSLE32,
XADD,
XADD32,
FETCH_ADD,
FETCH_AND,
FETCH_OR,
FETCH_XOR,
}
pub mod class {
pub const LD: u8 = 0x00;
pub const LDX: u8 = 0x01;
pub const ST: u8 = 0x02;
pub const STX: u8 = 0x03;
pub const ALU: u8 = 0x04;
pub const JMP: u8 = 0x05;
pub const JMP32: u8 = 0x06;
pub const ALU64: u8 = 0x07;
}
pub mod source {
pub const K: u8 = 0x00; pub const X: u8 = 0x08; }
pub mod alu_op {
pub const ADD: u8 = 0x00;
pub const SUB: u8 = 0x10;
pub const MUL: u8 = 0x20;
pub const DIV: u8 = 0x30;
pub const OR: u8 = 0x40;
pub const AND: u8 = 0x50;
pub const LSH: u8 = 0x60;
pub const RSH: u8 = 0x70;
pub const NEG: u8 = 0x80;
pub const MOD: u8 = 0x90;
pub const XOR: u8 = 0xA0;
pub const MOV: u8 = 0xB0;
pub const ARSH: u8 = 0xC0;
pub const END: u8 = 0xD0;
}
pub mod jump_op {
pub const JA: u8 = 0x00;
pub const JEQ: u8 = 0x10;
pub const JGT: u8 = 0x20;
pub const JGE: u8 = 0x30;
pub const JSET: u8 = 0x40;
pub const JNE: u8 = 0x50;
pub const JSGT: u8 = 0x60;
pub const JSGE: u8 = 0x70;
pub const CALL: u8 = 0x80;
pub const EXIT: u8 = 0x90;
pub const JLT: u8 = 0xA0;
pub const JLE: u8 = 0xB0;
pub const JSLT: u8 = 0xC0;
pub const JSLE: u8 = 0xD0;
}
pub struct BpfInstrInfo;
impl BpfInstrInfo {
pub fn get_mnemonic(opcode: &BpfOpcode) -> &'static str {
match opcode {
BpfOpcode::ADD => "add",
BpfOpcode::SUB => "sub",
BpfOpcode::MUL => "mul",
BpfOpcode::DIV => "div",
BpfOpcode::SDIV => "sdiv",
BpfOpcode::OR => "or",
BpfOpcode::AND => "and",
BpfOpcode::LSH => "lsh",
BpfOpcode::RSH => "rsh",
BpfOpcode::NEG => "neg",
BpfOpcode::MOD => "mod",
BpfOpcode::SMOD => "smod",
BpfOpcode::XOR => "xor",
BpfOpcode::MOV => "mov",
BpfOpcode::ARSH => "arsh",
BpfOpcode::ADD32 => "add32",
BpfOpcode::SUB32 => "sub32",
BpfOpcode::MUL32 => "mul32",
BpfOpcode::DIV32 => "div32",
BpfOpcode::SDIV32 => "sdiv32",
BpfOpcode::OR32 => "or32",
BpfOpcode::AND32 => "and32",
BpfOpcode::LSH32 => "lsh32",
BpfOpcode::RSH32 => "rsh32",
BpfOpcode::NEG32 => "neg32",
BpfOpcode::MOD32 => "mod32",
BpfOpcode::SMOD32 => "smod32",
BpfOpcode::XOR32 => "xor32",
BpfOpcode::MOV32 => "mov32",
BpfOpcode::ARSH32 => "arsh32",
BpfOpcode::END_LE16 => "end_le16",
BpfOpcode::END_LE32 => "end_le32",
BpfOpcode::END_LE64 => "end_le64",
BpfOpcode::END_BE16 => "end_be16",
BpfOpcode::END_BE32 => "end_be32",
BpfOpcode::END_BE64 => "end_be64",
BpfOpcode::LD_DW => "lddw",
BpfOpcode::LDX_B => "ldxb",
BpfOpcode::LDX_H => "ldxh",
BpfOpcode::LDX_W => "ldxw",
BpfOpcode::LDX_DW => "ldxdw",
BpfOpcode::ST_DW => "stdw",
BpfOpcode::STX_DW => "stxdw",
BpfOpcode::ST_B => "stb",
BpfOpcode::ST_H => "sth",
BpfOpcode::ST_W => "stw",
BpfOpcode::ST_DW_IMM => "stdw",
BpfOpcode::STX_B => "stxb",
BpfOpcode::STX_H => "stxh",
BpfOpcode::STX_W => "stxw",
BpfOpcode::STX_DW_IMM => "stxdw",
BpfOpcode::JA => "ja",
BpfOpcode::JEQ => "jeq",
BpfOpcode::JGT => "jgt",
BpfOpcode::JGE => "jge",
BpfOpcode::JLT => "jlt",
BpfOpcode::JLE => "jle",
BpfOpcode::JSET => "jset",
BpfOpcode::JNE => "jne",
BpfOpcode::JSGT => "jsgt",
BpfOpcode::JSGE => "jsge",
BpfOpcode::JSLT => "jslt",
BpfOpcode::JSLE => "jsle",
BpfOpcode::CALL => "call",
BpfOpcode::EXIT => "exit",
BpfOpcode::JEQ32 => "jeq32",
BpfOpcode::JGT32 => "jgt32",
BpfOpcode::JGE32 => "jge32",
BpfOpcode::JLT32 => "jlt32",
BpfOpcode::JLE32 => "jle32",
BpfOpcode::JSET32 => "jset32",
BpfOpcode::JNE32 => "jne32",
BpfOpcode::JSGT32 => "jsgt32",
BpfOpcode::JSGE32 => "jsge32",
BpfOpcode::JSLT32 => "jslt32",
BpfOpcode::JSLE32 => "jsle32",
BpfOpcode::XADD => "xadd",
BpfOpcode::XADD32 => "xadd32",
BpfOpcode::FETCH_ADD => "fetch_add",
BpfOpcode::FETCH_AND => "fetch_and",
BpfOpcode::FETCH_OR => "fetch_or",
BpfOpcode::FETCH_XOR => "fetch_xor",
}
}
pub fn is_branch(opcode: &BpfOpcode) -> bool {
matches!(
opcode,
BpfOpcode::JA
| BpfOpcode::JEQ
| BpfOpcode::JGT
| BpfOpcode::JGE
| BpfOpcode::JLT
| BpfOpcode::JLE
| BpfOpcode::JSET
| BpfOpcode::JNE
| BpfOpcode::JSGT
| BpfOpcode::JSGE
| BpfOpcode::JSLT
| BpfOpcode::JSLE
| BpfOpcode::JEQ32
| BpfOpcode::JGT32
| BpfOpcode::JGE32
| BpfOpcode::JLT32
| BpfOpcode::JLE32
| BpfOpcode::JSET32
| BpfOpcode::JNE32
| BpfOpcode::JSGT32
| BpfOpcode::JSGE32
| BpfOpcode::JSLT32
| BpfOpcode::JSLE32
)
}
pub fn is_terminator(opcode: &BpfOpcode) -> bool {
matches!(opcode, BpfOpcode::EXIT | BpfOpcode::JA)
}
pub fn is_unconditional_jump(opcode: &BpfOpcode) -> bool {
matches!(opcode, BpfOpcode::JA)
}
pub fn uses_src_reg(opcode: &BpfOpcode) -> bool {
!matches!(
opcode,
BpfOpcode::JA
| BpfOpcode::CALL
| BpfOpcode::EXIT
| BpfOpcode::NEG
| BpfOpcode::NEG32
| BpfOpcode::END_LE16
| BpfOpcode::END_LE32
| BpfOpcode::END_LE64
| BpfOpcode::END_BE16
| BpfOpcode::END_BE32
| BpfOpcode::END_BE64
| BpfOpcode::LD_DW
)
}
pub fn has_immediate(opcode: &BpfOpcode) -> bool {
!matches!(opcode, BpfOpcode::EXIT | BpfOpcode::NEG | BpfOpcode::NEG32)
}
pub fn get_opcode_byte(opcode: &BpfOpcode) -> u8 {
match opcode {
BpfOpcode::ADD => class::ALU64 | source::X | alu_op::ADD,
BpfOpcode::SUB => class::ALU64 | source::X | alu_op::SUB,
BpfOpcode::MUL => class::ALU64 | source::X | alu_op::MUL,
BpfOpcode::DIV => class::ALU64 | source::X | alu_op::DIV,
BpfOpcode::SDIV => class::ALU64 | source::X | alu_op::DIV,
BpfOpcode::OR => class::ALU64 | source::X | alu_op::OR,
BpfOpcode::AND => class::ALU64 | source::X | alu_op::AND,
BpfOpcode::LSH => class::ALU64 | source::X | alu_op::LSH,
BpfOpcode::RSH => class::ALU64 | source::X | alu_op::RSH,
BpfOpcode::NEG => class::ALU64 | alu_op::NEG,
BpfOpcode::MOD => class::ALU64 | source::X | alu_op::MOD,
BpfOpcode::SMOD => class::ALU64 | source::X | alu_op::MOD,
BpfOpcode::XOR => class::ALU64 | source::X | alu_op::XOR,
BpfOpcode::MOV => class::ALU64 | source::X | alu_op::MOV,
BpfOpcode::ARSH => class::ALU64 | source::X | alu_op::ARSH,
BpfOpcode::ADD32 => class::ALU | alu_op::ADD,
BpfOpcode::SUB32 => class::ALU | alu_op::SUB,
BpfOpcode::MUL32 => class::ALU | alu_op::MUL,
BpfOpcode::DIV32 => class::ALU | alu_op::DIV,
BpfOpcode::SDIV32 => class::ALU | alu_op::DIV,
BpfOpcode::OR32 => class::ALU | alu_op::OR,
BpfOpcode::AND32 => class::ALU | alu_op::AND,
BpfOpcode::LSH32 => class::ALU | alu_op::LSH,
BpfOpcode::RSH32 => class::ALU | alu_op::RSH,
BpfOpcode::NEG32 => class::ALU | alu_op::NEG,
BpfOpcode::MOD32 => class::ALU | alu_op::MOD,
BpfOpcode::SMOD32 => class::ALU | alu_op::MOD,
BpfOpcode::XOR32 => class::ALU | alu_op::XOR,
BpfOpcode::MOV32 => class::ALU | alu_op::MOV,
BpfOpcode::ARSH32 => class::ALU | alu_op::ARSH,
BpfOpcode::END_LE16 => class::ALU | alu_op::END,
BpfOpcode::END_LE32 => class::ALU | alu_op::END,
BpfOpcode::END_LE64 => class::ALU | alu_op::END,
BpfOpcode::END_BE16 => class::ALU | alu_op::END,
BpfOpcode::END_BE32 => class::ALU | alu_op::END,
BpfOpcode::END_BE64 => class::ALU | alu_op::END,
BpfOpcode::LD_DW => class::LD | 0x18,
BpfOpcode::LDX_B => class::LDX | 0x10,
BpfOpcode::LDX_H => class::LDX | 0x08,
BpfOpcode::LDX_W => class::LDX | 0x00,
BpfOpcode::LDX_DW => class::LDX | 0x18,
BpfOpcode::ST_DW => class::ST | 0x18,
BpfOpcode::ST_DW_IMM => class::ST | 0x00,
BpfOpcode::ST_B => class::STX | 0x10,
BpfOpcode::ST_H => class::STX | 0x08,
BpfOpcode::ST_W => class::STX | 0x00,
BpfOpcode::STX_B => class::STX | 0x10,
BpfOpcode::STX_H => class::STX | 0x08,
BpfOpcode::STX_W => class::STX | 0x00,
BpfOpcode::STX_DW => class::STX | 0x18,
BpfOpcode::STX_DW_IMM => class::STX | 0x18,
BpfOpcode::JA => class::JMP | jump_op::JA,
BpfOpcode::JEQ => class::JMP | jump_op::JEQ,
BpfOpcode::JGT => class::JMP | jump_op::JGT,
BpfOpcode::JGE => class::JMP | jump_op::JGE,
BpfOpcode::JLT => class::JMP | jump_op::JLT,
BpfOpcode::JLE => class::JMP | jump_op::JLE,
BpfOpcode::JSET => class::JMP | jump_op::JSET,
BpfOpcode::JNE => class::JMP | jump_op::JNE,
BpfOpcode::JSGT => class::JMP | jump_op::JSGT,
BpfOpcode::JSGE => class::JMP | jump_op::JSGE,
BpfOpcode::JSLT => class::JMP | jump_op::JSLT,
BpfOpcode::JSLE => class::JMP | jump_op::JSLE,
BpfOpcode::CALL => class::JMP | jump_op::CALL,
BpfOpcode::EXIT => class::JMP | jump_op::EXIT,
BpfOpcode::JEQ32 => class::JMP32 | jump_op::JEQ,
BpfOpcode::JGT32 => class::JMP32 | jump_op::JGT,
BpfOpcode::JGE32 => class::JMP32 | jump_op::JGE,
BpfOpcode::JLT32 => class::JMP32 | jump_op::JLT,
BpfOpcode::JLE32 => class::JMP32 | jump_op::JLE,
BpfOpcode::JSET32 => class::JMP32 | jump_op::JSET,
BpfOpcode::JNE32 => class::JMP32 | jump_op::JNE,
BpfOpcode::JSGT32 => class::JMP32 | jump_op::JSGT,
BpfOpcode::JSGE32 => class::JMP32 | jump_op::JSGE,
BpfOpcode::JSLT32 => class::JMP32 | jump_op::JSLT,
BpfOpcode::JSLE32 => class::JMP32 | jump_op::JSLE,
BpfOpcode::XADD => class::STX | 0xC0,
BpfOpcode::XADD32 => class::STX | 0xC0,
BpfOpcode::FETCH_ADD => class::STX | 0xC0,
BpfOpcode::FETCH_AND => class::STX | 0xD0,
BpfOpcode::FETCH_OR => class::STX | 0xE0,
BpfOpcode::FETCH_XOR => class::STX | 0xF0,
}
}
pub fn instr_size(opcode: &BpfOpcode) -> u32 {
match opcode {
BpfOpcode::LD_DW => 2, _ => 1, }
}
pub fn is_alu32(opcode: &BpfOpcode) -> bool {
matches!(
opcode,
BpfOpcode::ADD32
| BpfOpcode::SUB32
| BpfOpcode::MUL32
| BpfOpcode::DIV32
| BpfOpcode::SDIV32
| BpfOpcode::OR32
| BpfOpcode::AND32
| BpfOpcode::LSH32
| BpfOpcode::RSH32
| BpfOpcode::NEG32
| BpfOpcode::MOD32
| BpfOpcode::SMOD32
| BpfOpcode::XOR32
| BpfOpcode::MOV32
| BpfOpcode::ARSH32
)
}
pub fn is_atomic(opcode: &BpfOpcode) -> bool {
matches!(
opcode,
BpfOpcode::XADD
| BpfOpcode::XADD32
| BpfOpcode::FETCH_ADD
| BpfOpcode::FETCH_AND
| BpfOpcode::FETCH_OR
| BpfOpcode::FETCH_XOR
)
}
}
pub const BPF_REG_0: u8 = 0; pub const BPF_REG_1: u8 = 1; pub const BPF_REG_2: u8 = 2; pub const BPF_REG_3: u8 = 3; pub const BPF_REG_4: u8 = 4; pub const BPF_REG_5: u8 = 5; pub const BPF_REG_6: u8 = 6; pub const BPF_REG_7: u8 = 7; pub const BPF_REG_8: u8 = 8; pub const BPF_REG_9: u8 = 9; pub const BPF_REG_10: u8 = 10;
pub const BPF_NUM_REGS: usize = 11;
pub fn reg_name(reg: u8) -> &'static str {
match reg {
0 => "r0",
1 => "r1",
2 => "r2",
3 => "r3",
4 => "r4",
5 => "r5",
6 => "r6",
7 => "r7",
8 => "r8",
9 => "r9",
10 => "r10",
_ => "???",
}
}
pub mod atomic_op {
pub const ADD: u8 = 0x01;
pub const OR: u8 = 0x40;
pub const AND: u8 = 0x50;
pub const XOR: u8 = 0xA0;
pub const XCHG: u8 = 0xE0 | 0x01;
pub const CMPXCHG: u8 = 0xF0 | 0x01;
pub const FETCH: u8 = 0x01;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BpfAtomicOp {
Add(bool),
Or(bool),
And(bool),
Xor(bool),
Xchg(bool),
Cmpxchg,
}
impl BpfAtomicOp {
pub fn from_imm(imm: i32) -> Option<Self> {
let op = (imm & 0xFF) as u8;
let fetch = (imm & 0x100) != 0;
match op {
atomic_op::ADD => Some(BpfAtomicOp::Add(fetch)),
atomic_op::OR => Some(BpfAtomicOp::Or(fetch)),
atomic_op::AND => Some(BpfAtomicOp::And(fetch)),
atomic_op::XOR => Some(BpfAtomicOp::Xor(fetch)),
0xE1 => Some(BpfAtomicOp::Xchg(fetch)),
0xF1 => Some(BpfAtomicOp::Cmpxchg),
_ => None,
}
}
pub fn mnemonic(&self) -> &'static str {
match self {
BpfAtomicOp::Add(true) => "fetch_add",
BpfAtomicOp::Add(false) => "add",
BpfAtomicOp::Or(true) => "fetch_or",
BpfAtomicOp::Or(false) => "or",
BpfAtomicOp::And(true) => "fetch_and",
BpfAtomicOp::And(false) => "and",
BpfAtomicOp::Xor(true) => "fetch_xor",
BpfAtomicOp::Xor(false) => "xor",
BpfAtomicOp::Xchg(true) => "xchg",
BpfAtomicOp::Xchg(false) => "xchg",
BpfAtomicOp::Cmpxchg => "cmpxchg",
}
}
}
pub struct BpfInstructionEncoding {
pub opcode: u8,
pub dst_reg: u8,
pub src_reg: u8,
pub offset: i16,
pub imm: i32,
}
impl BpfInstructionEncoding {
pub fn new(opcode: u8, dst: u8, src: u8, off: i16, imm: i32) -> Self {
BpfInstructionEncoding {
opcode,
dst_reg: dst,
src_reg: src,
offset: off,
imm,
}
}
pub fn to_bytes(&self) -> [u8; 8] {
let mut bytes = [0u8; 8];
bytes[0] = self.opcode;
bytes[1] = (self.src_reg << 4) | (self.dst_reg & 0xF);
bytes[2] = (self.offset as u16 & 0xFF) as u8;
bytes[3] = ((self.offset as u16 >> 8) & 0xFF) as u8;
bytes[4..8].copy_from_slice(&self.imm.to_le_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8; 8]) -> Self {
let opcode = bytes[0];
let dst_reg = bytes[1] & 0xF;
let src_reg = (bytes[1] >> 4) & 0xF;
let offset = i16::from_le_bytes([bytes[2], bytes[3]]);
let imm = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
BpfInstructionEncoding {
opcode,
dst_reg,
src_reg,
offset,
imm,
}
}
pub fn class(&self) -> u8 {
self.opcode & 0x07
}
pub fn source_mode(&self) -> u8 {
self.opcode & 0x08
}
pub fn alu_jmp_op(&self) -> u8 {
self.opcode & 0xF0
}
}
pub fn decode_opcode(opcode_byte: u8, imm: i32) -> Option<BpfOpcode> {
let class = opcode_byte & 0x07;
let src = opcode_byte & 0x08;
let op = opcode_byte & 0xF0;
match class {
class::LD => match opcode_byte {
0x18 => Some(BpfOpcode::LD_DW),
_ => None,
},
class::LDX => match opcode_byte {
0x61 => Some(BpfOpcode::LDX_W),
0x69 => Some(BpfOpcode::LDX_H),
0x71 => Some(BpfOpcode::LDX_B),
0x79 => Some(BpfOpcode::LDX_DW),
_ => None,
},
class::ST => match opcode_byte {
0x18 => Some(BpfOpcode::ST_DW_IMM),
_ => None,
},
class::STX => match opcode_byte {
0x63 => Some(BpfOpcode::STX_W),
0x6B => Some(BpfOpcode::STX_H),
0x73 => Some(BpfOpcode::STX_B),
0x7B => Some(BpfOpcode::STX_DW_IMM),
0xC3 => decode_atomic_opcode(imm),
_ => None,
},
class::ALU => match opcode_byte {
0x04 => Some(BpfOpcode::ADD32),
0x0C if src == source::K => Some(BpfOpcode::ADD32),
0x14 => Some(BpfOpcode::SUB32),
0x24 => Some(BpfOpcode::MUL32),
0x34 => Some(BpfOpcode::DIV32),
0x3C => Some(BpfOpcode::SDIV32),
0x44 => Some(BpfOpcode::OR32),
0x54 => Some(BpfOpcode::AND32),
0x64 => Some(BpfOpcode::LSH32),
0x74 => Some(BpfOpcode::RSH32),
0x84 => Some(BpfOpcode::NEG32),
0x94 => Some(BpfOpcode::MOD32),
0x9C => Some(BpfOpcode::SMOD32),
0xA4 => Some(BpfOpcode::XOR32),
0xB4 => Some(BpfOpcode::MOV32),
0xC4 => Some(BpfOpcode::ARSH32),
0xD4 => decode_endian_opcode(imm, false),
_ => None,
},
class::JMP => match op {
jump_op::JA => Some(BpfOpcode::JA),
jump_op::JEQ => Some(BpfOpcode::JEQ),
jump_op::JGT => Some(BpfOpcode::JGT),
jump_op::JGE => Some(BpfOpcode::JGE),
jump_op::JLT => Some(BpfOpcode::JLT),
jump_op::JLE => Some(BpfOpcode::JLE),
jump_op::JSET => Some(BpfOpcode::JSET),
jump_op::JNE => Some(BpfOpcode::JNE),
jump_op::JSGT => Some(BpfOpcode::JSGT),
jump_op::JSGE => Some(BpfOpcode::JSGE),
jump_op::JSLT => Some(BpfOpcode::JSLT),
jump_op::JSLE => Some(BpfOpcode::JSLE),
jump_op::CALL => Some(BpfOpcode::CALL),
jump_op::EXIT => Some(BpfOpcode::EXIT),
_ => None,
},
class::JMP32 => match op {
jump_op::JEQ => Some(BpfOpcode::JEQ32),
jump_op::JGT => Some(BpfOpcode::JGT32),
jump_op::JGE => Some(BpfOpcode::JGE32),
jump_op::JLT => Some(BpfOpcode::JLT32),
jump_op::JLE => Some(BpfOpcode::JLE32),
jump_op::JSET => Some(BpfOpcode::JSET32),
jump_op::JNE => Some(BpfOpcode::JNE32),
jump_op::JSGT => Some(BpfOpcode::JSGT32),
jump_op::JSGE => Some(BpfOpcode::JSGE32),
jump_op::JSLT => Some(BpfOpcode::JSLT32),
jump_op::JSLE => Some(BpfOpcode::JSLE32),
_ => None,
},
class::ALU64 => match opcode_byte {
0x07 => Some(BpfOpcode::ADD),
0x0F if src == source::K => Some(BpfOpcode::ADD),
0x17 => Some(BpfOpcode::SUB),
0x27 => Some(BpfOpcode::MUL),
0x37 => Some(BpfOpcode::DIV),
0x3F => Some(BpfOpcode::SDIV),
0x47 => Some(BpfOpcode::OR),
0x57 => Some(BpfOpcode::AND),
0x67 => Some(BpfOpcode::LSH),
0x77 => Some(BpfOpcode::RSH),
0x87 => Some(BpfOpcode::NEG),
0x97 => Some(BpfOpcode::MOD),
0x9F => Some(BpfOpcode::SMOD),
0xA7 => Some(BpfOpcode::XOR),
0xB7 => Some(BpfOpcode::MOV),
0xC7 => Some(BpfOpcode::ARSH),
0xD7 => decode_endian_opcode(imm, true),
_ => None,
},
_ => None,
}
}
fn decode_atomic_opcode(imm: i32) -> Option<BpfOpcode> {
let op = (imm & 0xFF) as u8;
match op {
atomic_op::ADD if (imm & 0x100) != 0 => Some(BpfOpcode::FETCH_ADD),
atomic_op::AND if (imm & 0x100) != 0 => Some(BpfOpcode::FETCH_AND),
atomic_op::OR if (imm & 0x100) != 0 => Some(BpfOpcode::FETCH_OR),
atomic_op::XOR if (imm & 0x100) != 0 => Some(BpfOpcode::FETCH_XOR),
0xC3 if (imm & 0x100) == 0 => Some(BpfOpcode::XADD),
_ => None,
}
}
fn decode_endian_opcode(imm: i32, is64: bool) -> Option<BpfOpcode> {
match (imm, is64) {
(16, false) => Some(BpfOpcode::END_LE16),
(32, false) => Some(BpfOpcode::END_LE32),
(64, false) => Some(BpfOpcode::END_LE64),
(16, true) => Some(BpfOpcode::END_LE16),
(32, true) => Some(BpfOpcode::END_LE32),
(64, true) => Some(BpfOpcode::END_LE64),
_ => None,
}
}
pub mod bpf_limits {
pub const MAX_INSNS: u32 = 1_000_000;
pub const MAX_STACK: u32 = 512;
pub const MAX_JUMPS: u32 = 8192;
pub const MAX_CALL_DEPTH: u32 = 8;
pub const MAX_MAP_NAME: usize = 16;
pub const MAX_MAPS: u32 = 64;
}
pub fn validate_instructions(insns: &[BpfOpcode]) -> Result<(), String> {
if insns.is_empty() {
return Err("empty instruction sequence".to_string());
}
if insns.len() > bpf_limits::MAX_INSNS as usize {
return Err(format!(
"too many instructions: {} > {}",
insns.len(),
bpf_limits::MAX_INSNS
));
}
let last = insns.last().unwrap();
if !matches!(last, BpfOpcode::EXIT | BpfOpcode::JA) {
return Err("program does not end with EXIT or JA".to_string());
}
Ok(())
}
pub struct BpfBuilder {
pub instructions: Vec<BpfOpcode>,
}
impl BpfBuilder {
pub fn new() -> Self {
BpfBuilder {
instructions: Vec::new(),
}
}
pub fn alu64(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn alu32(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn jmp64(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn jmp32(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn load(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn store(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn atomic(&mut self, op: BpfOpcode) -> &mut Self {
self.instructions.push(op);
self
}
pub fn exit(&mut self) -> &mut Self {
self.instructions.push(BpfOpcode::EXIT);
self
}
pub fn build(self) -> Vec<BpfOpcode> {
self.instructions
}
pub fn validate(&self) -> Result<(), String> {
validate_instructions(&self.instructions)
}
}
impl Default for BpfBuilder {
fn default() -> Self {
Self::new()
}
}
pub mod call_conv {
use super::*;
pub const RETURN_REG: u8 = BPF_REG_0;
pub const ARG1_REG: u8 = BPF_REG_1;
pub const ARG2_REG: u8 = BPF_REG_2;
pub const ARG3_REG: u8 = BPF_REG_3;
pub const ARG4_REG: u8 = BPF_REG_4;
pub const ARG5_REG: u8 = BPF_REG_5;
pub const CALLEE_SAVED: &[u8] = &[
BPF_REG_6,
BPF_REG_7,
BPF_REG_8,
BPF_REG_9,
];
pub const FRAME_PTR: u8 = BPF_REG_10;
pub const CTX_REG: u8 = BPF_REG_1;
pub fn arg_regs() -> &'static [u8] {
&[ARG1_REG, ARG2_REG, ARG3_REG, ARG4_REG, ARG5_REG]
}
pub fn is_callee_saved(reg: u8) -> bool {
CALLEE_SAVED.contains(®)
}
pub fn is_caller_saved(reg: u8) -> bool {
matches!(reg, 0 | 1 | 2 | 3 | 4 | 5)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_mnemonic_add() {
assert_eq!(BpfInstrInfo::get_mnemonic(&BpfOpcode::ADD), "add");
}
#[test]
fn test_get_mnemonic_exit() {
assert_eq!(BpfInstrInfo::get_mnemonic(&BpfOpcode::EXIT), "exit");
}
#[test]
fn test_get_mnemonic_lddw() {
assert_eq!(BpfInstrInfo::get_mnemonic(&BpfOpcode::LD_DW), "lddw");
}
#[test]
fn test_is_branch_true() {
assert!(BpfInstrInfo::is_branch(&BpfOpcode::JEQ));
assert!(BpfInstrInfo::is_branch(&BpfOpcode::JNE));
assert!(BpfInstrInfo::is_branch(&BpfOpcode::JGT32));
assert!(BpfInstrInfo::is_branch(&BpfOpcode::JA));
}
#[test]
fn test_is_branch_false() {
assert!(!BpfInstrInfo::is_branch(&BpfOpcode::ADD));
assert!(!BpfInstrInfo::is_branch(&BpfOpcode::EXIT));
assert!(!BpfInstrInfo::is_branch(&BpfOpcode::LD_DW));
}
#[test]
fn test_is_terminator_exit() {
assert!(BpfInstrInfo::is_terminator(&BpfOpcode::EXIT));
}
#[test]
fn test_is_terminator_ja() {
assert!(BpfInstrInfo::is_terminator(&BpfOpcode::JA));
}
#[test]
fn test_is_terminator_conditional() {
assert!(!BpfInstrInfo::is_terminator(&BpfOpcode::JEQ));
}
#[test]
fn test_uses_src_reg_alu() {
assert!(BpfInstrInfo::uses_src_reg(&BpfOpcode::ADD));
assert!(BpfInstrInfo::uses_src_reg(&BpfOpcode::MUL));
}
#[test]
fn test_uses_src_reg_neg() {
assert!(!BpfInstrInfo::uses_src_reg(&BpfOpcode::NEG));
}
#[test]
fn test_uses_src_reg_call() {
assert!(!BpfInstrInfo::uses_src_reg(&BpfOpcode::CALL));
}
#[test]
fn test_instr_size_lddw() {
assert_eq!(BpfInstrInfo::instr_size(&BpfOpcode::LD_DW), 2);
}
#[test]
fn test_instr_size_normal() {
assert_eq!(BpfInstrInfo::instr_size(&BpfOpcode::ADD), 1);
assert_eq!(BpfInstrInfo::instr_size(&BpfOpcode::EXIT), 1);
}
#[test]
fn test_is_alu32() {
assert!(BpfInstrInfo::is_alu32(&BpfOpcode::ADD32));
assert!(BpfInstrInfo::is_alu32(&BpfOpcode::MOV32));
assert!(!BpfInstrInfo::is_alu32(&BpfOpcode::ADD));
assert!(!BpfInstrInfo::is_alu32(&BpfOpcode::JEQ));
}
#[test]
fn test_is_atomic() {
assert!(BpfInstrInfo::is_atomic(&BpfOpcode::XADD));
assert!(BpfInstrInfo::is_atomic(&BpfOpcode::FETCH_ADD));
assert!(!BpfInstrInfo::is_atomic(&BpfOpcode::ADD));
}
#[test]
fn test_reg_name() {
assert_eq!(reg_name(0), "r0");
assert_eq!(reg_name(10), "r10");
assert_eq!(reg_name(255), "???");
}
#[test]
fn test_opcode_byte_values() {
let add = BpfInstrInfo::get_opcode_byte(&BpfOpcode::ADD);
assert_eq!(add, 0x0F);
let exit = BpfInstrInfo::get_opcode_byte(&BpfOpcode::EXIT);
assert_eq!(exit, 0x95);
let lddw = BpfInstrInfo::get_opcode_byte(&BpfOpcode::LD_DW);
assert_eq!(lddw, 0x18); }
#[test]
fn test_is_unconditional_jump() {
assert!(BpfInstrInfo::is_unconditional_jump(&BpfOpcode::JA));
assert!(!BpfInstrInfo::is_unconditional_jump(&BpfOpcode::JEQ));
}
#[test]
fn test_has_immediate() {
assert!(BpfInstrInfo::has_immediate(&BpfOpcode::ADD));
assert!(BpfInstrInfo::has_immediate(&BpfOpcode::JA));
assert!(!BpfInstrInfo::has_immediate(&BpfOpcode::EXIT));
}
#[test]
fn test_all_mnemonics_unique() {
let mut mnemonics: Vec<&str> = Vec::new();
let opcodes = [
BpfOpcode::ADD,
BpfOpcode::SUB,
BpfOpcode::MUL,
BpfOpcode::DIV,
BpfOpcode::OR,
BpfOpcode::AND,
BpfOpcode::LSH,
BpfOpcode::RSH,
BpfOpcode::NEG,
BpfOpcode::MOD,
BpfOpcode::XOR,
BpfOpcode::MOV,
BpfOpcode::ARSH,
BpfOpcode::EXIT,
BpfOpcode::CALL,
BpfOpcode::JA,
BpfOpcode::JEQ,
BpfOpcode::JNE,
BpfOpcode::FETCH_ADD,
BpfOpcode::XADD,
BpfOpcode::END_LE16,
BpfOpcode::LD_DW,
];
for op in &opcodes {
mnemonics.push(BpfInstrInfo::get_mnemonic(op));
}
mnemonics.sort_unstable();
mnemonics.dedup();
assert_eq!(mnemonics.len(), opcodes.len());
}
}