kbpf_basic/preprocessor.rs
1//! eBPF preprocessor for relocating map file descriptors in eBPF instructions.
2//!
3//! This module defines the `EbpfPreProcessor` struct and the `EbpfInst` trait.
4//!
5//! The `EbpfPreProcessor` struct is used to preprocess eBPF instructions to relocate map file descriptors, and the `EbpfInst` trait defines the interface for eBPF instructions that can be processed by the preprocessor.
6//!
7//! The preprocessor works by translating the raw eBPF instructions into a more structured format, identifying instructions that reference map file descriptors, and replacing those references with the actual pointers to the maps in kernel space. This allows the eBPF program to access the maps correctly when it is loaded into the kernel. The preprocessor also keeps track of the raw file pointers for the maps that are used in the program, which can be used for debugging or other purposes.
8//!
9use alloc::{vec, vec::Vec};
10
11use crate::{
12 BpfResult as Result, KernelAuxiliaryOps,
13 linux_bpf::{BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE},
14};
15
16/// eBPF preprocessor for relocating map file descriptors in eBPF instructions.
17pub struct EbpfPreProcessor {
18 new_insn: Vec<u8>,
19 raw_file_ptr: Vec<usize>,
20}
21
22/// Trait for eBPF instructions that can be processed by the preprocessor.
23pub trait EbpfInst: Clone {
24 /// Get the opcode of the instruction.
25 fn opc(&self) -> u8;
26 /// Get the destination register of the instruction.
27 fn imm(&self) -> i32;
28 /// Get the source register of the instruction.
29 fn src(&self) -> u8;
30 /// set the immediate value of the instruction.
31 fn set_imm(&mut self, imm: i32);
32 /// Convert the instruction to a byte array.
33 fn to_array(&self) -> [u8; 8];
34}
35
36const LD_DW_IMM: u8 = 0x18;
37
38impl EbpfPreProcessor {
39 /// Preprocess the instructions to relocate the map file descriptors.
40 pub fn preprocess<F: KernelAuxiliaryOps>(mut instructions: Vec<u8>) -> Result<Self> {
41 let mut fmt_insn = F::translate_instruction(instructions.clone())?;
42 let mut index = 0;
43 let mut raw_file_ptr = vec![];
44 loop {
45 if index >= fmt_insn.len() {
46 break;
47 }
48 let mut insn = fmt_insn[index].clone();
49 if insn.opc() == LD_DW_IMM {
50 // relocate the instruction
51 let mut next_insn = fmt_insn[index + 1].clone();
52 // the imm is the map_fd because user lib has already done the relocation
53 let map_fd = insn.imm() as usize;
54 let src_reg = insn.src();
55 // See https://www.kernel.org/doc/html/latest/bpf/standardization/instruction-set.html#id23
56 let ptr = match src_reg as u32 {
57 BPF_PSEUDO_MAP_VALUE => {
58 // dst = map_val(map_by_fd(imm)) + next_imm
59 // map_val(map) gets the address of the first value in a given map
60 let value_ptr = F::get_unified_map_from_fd(map_fd as u32, |unified_map| {
61 unified_map.map().map_values_ptr_range()
62 })?;
63 let offset = next_insn.imm() as usize;
64 log::trace!(
65 "Relocate for BPF_PSEUDO_MAP_VALUE, instruction index: {}, map_fd: {}, ptr: {:#x}, offset: {}",
66 index,
67 map_fd,
68 value_ptr.start,
69 offset
70 );
71 Some(value_ptr.start + offset)
72 }
73 BPF_PSEUDO_MAP_FD => {
74 let map_ptr = F::get_unified_map_ptr_from_fd(map_fd as u32)? as usize;
75 log::trace!(
76 "Relocate for BPF_PSEUDO_MAP_FD, instruction index: {}, map_fd: {}, ptr: {:#x}",
77 index,
78 map_fd,
79 map_ptr
80 );
81 raw_file_ptr.push(map_ptr);
82 Some(map_ptr)
83 }
84 ty => {
85 log::error!(
86 "relocation for ty: {} not implemented, instruction index: {}",
87 ty,
88 index
89 );
90 None
91 }
92 };
93 if let Some(ptr) = ptr {
94 // The current ins store the map_data_ptr low 32 bits,
95 // the next ins store the map_data_ptr high 32 bits
96 insn.set_imm(ptr as i32);
97 next_insn.set_imm((ptr >> 32) as i32);
98 fmt_insn[index] = insn;
99 fmt_insn[index + 1] = next_insn;
100 index += 2;
101 } else {
102 index += 1;
103 }
104 } else {
105 index += 1;
106 }
107 }
108 let mut idx = 0;
109 for ins in fmt_insn {
110 let bytes = ins.to_array();
111 instructions[idx..idx + 8].copy_from_slice(&bytes);
112 idx += 8;
113 }
114 Ok(Self {
115 new_insn: instructions,
116 raw_file_ptr,
117 })
118 }
119
120 /// Get the new instructions after preprocessing.
121 pub fn get_new_insn(&self) -> &Vec<u8> {
122 self.new_insn.as_ref()
123 }
124
125 /// Get the raw file pointer after preprocessing.
126 /// The raw file pointer is a list of pointers to the maps that are used in the program.
127 /// The pointers are used to access the maps in the program.
128 pub fn get_raw_file_ptr(&self) -> &Vec<usize> {
129 self.raw_file_ptr.as_ref()
130 }
131}