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