mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 19:36:47 +00:00
* feat(kprobe): Add basic kprobe support for x86_64 * feat: add ebpf support (#912) - 实现bpf()一部分命令,包括几种基本map,相关的helper函数 - 实现部分perf相关的数据结构 - 暂时为文件实现简单mmap - 实现一个使用kprobe统计syscall 调用次数的ebpf程序 对eBPF支持程度(基本): - 简单的eBPF程序(没有指定特殊的Map) - 使用内核已经实现的Map的eBPF程序 - 可以和kprobe配合使用 - 内核Map相关的接口定义已经实现,添加新的Map较为简单 不支持的功能: - 区分不同的eBPF程序类型(Network/Cgroup)并限定可调用的helper函数集 - 与内核其它跟踪机制配合(tracepoint) - 其它helper和Map todo - [ ] 修改mmap,需要讨论,因为这个和块缓存层相关 - [x] 添加文档 - [x] 修复可能的错误 - [x] 增加rbpf版本信息 * feat: add /sys/devices/system/cpu/possible file * feat: add /sys/devices/system/cpu/online
278 lines
9.0 KiB
Rust
278 lines
9.0 KiB
Rust
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
|
// Copyright 2017 Rich Lane <lanerl@gmail.com>
|
|
|
|
//! This module translates eBPF assembly language to binary.
|
|
|
|
use alloc::{
|
|
collections::BTreeMap,
|
|
format,
|
|
string::{String, ToString},
|
|
vec,
|
|
vec::Vec,
|
|
};
|
|
|
|
use self::InstructionType::{
|
|
AluBinary, AluUnary, Call, Endian, JumpConditional, JumpUnconditional, LoadAbs, LoadImm,
|
|
LoadInd, LoadReg, NoOperand, StoreImm, StoreReg,
|
|
};
|
|
use crate::{
|
|
asm_parser::{
|
|
parse, Instruction, Operand,
|
|
Operand::{Integer, Memory, Nil, Register},
|
|
},
|
|
ebpf::{self, Insn},
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
enum InstructionType {
|
|
AluBinary,
|
|
AluUnary,
|
|
LoadImm,
|
|
LoadAbs,
|
|
LoadInd,
|
|
LoadReg,
|
|
StoreImm,
|
|
StoreReg,
|
|
JumpUnconditional,
|
|
JumpConditional,
|
|
Call,
|
|
Endian(i64),
|
|
NoOperand,
|
|
}
|
|
|
|
fn make_instruction_map() -> BTreeMap<String, (InstructionType, u8)> {
|
|
let mut result = BTreeMap::new();
|
|
|
|
let alu_binary_ops = [
|
|
("add", ebpf::BPF_ADD),
|
|
("sub", ebpf::BPF_SUB),
|
|
("mul", ebpf::BPF_MUL),
|
|
("div", ebpf::BPF_DIV),
|
|
("or", ebpf::BPF_OR),
|
|
("and", ebpf::BPF_AND),
|
|
("lsh", ebpf::BPF_LSH),
|
|
("rsh", ebpf::BPF_RSH),
|
|
("mod", ebpf::BPF_MOD),
|
|
("xor", ebpf::BPF_XOR),
|
|
("mov", ebpf::BPF_MOV),
|
|
("arsh", ebpf::BPF_ARSH),
|
|
];
|
|
|
|
let mem_sizes = [
|
|
("w", ebpf::BPF_W),
|
|
("h", ebpf::BPF_H),
|
|
("b", ebpf::BPF_B),
|
|
("dw", ebpf::BPF_DW),
|
|
];
|
|
|
|
let jump_conditions = [
|
|
("jeq", ebpf::BPF_JEQ),
|
|
("jgt", ebpf::BPF_JGT),
|
|
("jge", ebpf::BPF_JGE),
|
|
("jlt", ebpf::BPF_JLT),
|
|
("jle", ebpf::BPF_JLE),
|
|
("jset", ebpf::BPF_JSET),
|
|
("jne", ebpf::BPF_JNE),
|
|
("jsgt", ebpf::BPF_JSGT),
|
|
("jsge", ebpf::BPF_JSGE),
|
|
("jslt", ebpf::BPF_JSLT),
|
|
("jsle", ebpf::BPF_JSLE),
|
|
];
|
|
|
|
{
|
|
let mut entry = |name: &str, inst_type: InstructionType, opc: u8| {
|
|
result.insert(name.to_string(), (inst_type, opc))
|
|
};
|
|
|
|
// Miscellaneous.
|
|
entry("exit", NoOperand, ebpf::EXIT);
|
|
entry("ja", JumpUnconditional, ebpf::JA);
|
|
entry("call", Call, ebpf::CALL);
|
|
entry("lddw", LoadImm, ebpf::LD_DW_IMM);
|
|
|
|
// AluUnary.
|
|
entry("neg", AluUnary, ebpf::NEG64);
|
|
entry("neg32", AluUnary, ebpf::NEG32);
|
|
entry("neg64", AluUnary, ebpf::NEG64);
|
|
|
|
// AluBinary.
|
|
for &(name, opc) in &alu_binary_ops {
|
|
entry(name, AluBinary, ebpf::BPF_ALU64 | opc);
|
|
entry(&format!("{name}32"), AluBinary, ebpf::BPF_ALU | opc);
|
|
entry(&format!("{name}64"), AluBinary, ebpf::BPF_ALU64 | opc);
|
|
}
|
|
|
|
// LoadAbs, LoadInd, LoadReg, StoreImm, and StoreReg.
|
|
for &(suffix, size) in &mem_sizes {
|
|
entry(
|
|
&format!("ldabs{suffix}"),
|
|
LoadAbs,
|
|
ebpf::BPF_ABS | ebpf::BPF_LD | size,
|
|
);
|
|
entry(
|
|
&format!("ldind{suffix}"),
|
|
LoadInd,
|
|
ebpf::BPF_IND | ebpf::BPF_LD | size,
|
|
);
|
|
entry(
|
|
&format!("ldx{suffix}"),
|
|
LoadReg,
|
|
ebpf::BPF_MEM | ebpf::BPF_LDX | size,
|
|
);
|
|
entry(
|
|
&format!("st{suffix}"),
|
|
StoreImm,
|
|
ebpf::BPF_MEM | ebpf::BPF_ST | size,
|
|
);
|
|
entry(
|
|
&format!("stx{suffix}"),
|
|
StoreReg,
|
|
ebpf::BPF_MEM | ebpf::BPF_STX | size,
|
|
);
|
|
}
|
|
|
|
// JumpConditional.
|
|
for &(name, condition) in &jump_conditions {
|
|
entry(name, JumpConditional, ebpf::BPF_JMP | condition);
|
|
entry(
|
|
&format!("{name}32"),
|
|
JumpConditional,
|
|
ebpf::BPF_JMP32 | condition,
|
|
);
|
|
}
|
|
|
|
// Endian.
|
|
for &size in &[16, 32, 64] {
|
|
entry(&format!("be{size}"), Endian(size), ebpf::BE);
|
|
entry(&format!("le{size}"), Endian(size), ebpf::LE);
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String> {
|
|
if !(0..16).contains(&dst) {
|
|
return Err(format!("Invalid destination register {dst}"));
|
|
}
|
|
if dst < 0 || src >= 16 {
|
|
return Err(format!("Invalid source register {src}"));
|
|
}
|
|
if !(-32768..32768).contains(&off) {
|
|
return Err(format!("Invalid offset {off}"));
|
|
}
|
|
if !(-2147483648..2147483648).contains(&imm) {
|
|
return Err(format!("Invalid immediate {imm}"));
|
|
}
|
|
Ok(Insn {
|
|
opc,
|
|
dst: dst as u8,
|
|
src: src as u8,
|
|
off: off as i16,
|
|
imm: imm as i32,
|
|
})
|
|
}
|
|
|
|
// TODO Use slice patterns when available and remove this function.
|
|
fn operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String> {
|
|
match operands.len() {
|
|
0 => Ok((Nil, Nil, Nil)),
|
|
1 => Ok((operands[0], Nil, Nil)),
|
|
2 => Ok((operands[0], operands[1], Nil)),
|
|
3 => Ok((operands[0], operands[1], operands[2])),
|
|
_ => Err("Too many operands".to_string()),
|
|
}
|
|
}
|
|
|
|
fn encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result<Insn, String> {
|
|
let (a, b, c) = (operands_tuple(operands))?;
|
|
match (inst_type, a, b, c) {
|
|
(AluBinary, Register(dst), Register(src), Nil) => insn(opc | ebpf::BPF_X, dst, src, 0, 0),
|
|
(AluBinary, Register(dst), Integer(imm), Nil) => insn(opc | ebpf::BPF_K, dst, 0, 0, imm),
|
|
(AluUnary, Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, 0),
|
|
(LoadAbs, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
|
|
(LoadInd, Register(src), Integer(imm), Nil) => insn(opc, 0, src, 0, imm),
|
|
(LoadReg, Register(dst), Memory(src, off), Nil)
|
|
| (StoreReg, Memory(dst, off), Register(src), Nil) => insn(opc, dst, src, off, 0),
|
|
(StoreImm, Memory(dst, off), Integer(imm), Nil) => insn(opc, dst, 0, off, imm),
|
|
(NoOperand, Nil, Nil, Nil) => insn(opc, 0, 0, 0, 0),
|
|
(JumpUnconditional, Integer(off), Nil, Nil) => insn(opc, 0, 0, off, 0),
|
|
(JumpConditional, Register(dst), Register(src), Integer(off)) => {
|
|
insn(opc | ebpf::BPF_X, dst, src, off, 0)
|
|
}
|
|
(JumpConditional, Register(dst), Integer(imm), Integer(off)) => {
|
|
insn(opc | ebpf::BPF_K, dst, 0, off, imm)
|
|
}
|
|
(Call, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
|
|
(Endian(size), Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, size),
|
|
(LoadImm, Register(dst), Integer(imm), Nil) => insn(opc, dst, 0, 0, (imm << 32) >> 32),
|
|
_ => Err(format!("Unexpected operands: {operands:?}")),
|
|
}
|
|
}
|
|
|
|
fn assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String> {
|
|
let instruction_map = make_instruction_map();
|
|
let mut result: Vec<Insn> = vec![];
|
|
for instruction in parsed {
|
|
let name = instruction.name.as_str();
|
|
match instruction_map.get(name) {
|
|
Some(&(inst_type, opc)) => {
|
|
match encode(inst_type, opc, &instruction.operands) {
|
|
Ok(insn) => result.push(insn),
|
|
Err(msg) => return Err(format!("Failed to encode {name}: {msg}")),
|
|
}
|
|
// Special case for lddw.
|
|
if let LoadImm = inst_type {
|
|
if let Integer(imm) = instruction.operands[1] {
|
|
result.push(insn(0, 0, 0, 0, imm >> 32).unwrap());
|
|
}
|
|
}
|
|
}
|
|
None => return Err(format!("Invalid instruction {name:?}")),
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
/// Parse assembly source and translate to binary.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rbpf::assembler::assemble;
|
|
/// let prog = assemble("add64 r1, 0x605
|
|
/// mov64 r2, 0x32
|
|
/// mov64 r1, r0
|
|
/// be16 r0
|
|
/// neg64 r2
|
|
/// exit");
|
|
/// println!("{:?}", prog);
|
|
/// # assert_eq!(prog,
|
|
/// # Ok(vec![0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
|
|
/// # 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
|
|
/// # 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
/// # 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
|
/// # 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
/// # 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
|
/// ```
|
|
///
|
|
/// This will produce the following output:
|
|
///
|
|
/// ```test
|
|
/// Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
|
|
/// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
|
|
/// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
/// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
|
/// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
|
|
/// ```
|
|
pub fn assemble(src: &str) -> Result<Vec<u8>, String> {
|
|
let parsed = (parse(src))?;
|
|
let insns = (assemble_internal(&parsed))?;
|
|
let mut result: Vec<u8> = vec![];
|
|
for insn in insns {
|
|
result.extend_from_slice(&insn.to_array());
|
|
}
|
|
Ok(result)
|
|
}
|