kbpf_basic/preprocessor.rs
1use alloc::{vec, vec::Vec};
2
3use rbpf::ebpf::{self, to_insn_vec};
4
5use crate::{
6 KernelAuxiliaryOps, Result,
7 linux_bpf::{BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE},
8};
9
10/// eBPF preprocessor for relocating map file descriptors in eBPF instructions.
11pub struct EBPFPreProcessor {
12 new_insn: Vec<u8>,
13 raw_file_ptr: Vec<usize>,
14}
15
16impl EBPFPreProcessor {
17 /// Preprocess the instructions to relocate the map file descriptors.
18 pub fn preprocess<F: KernelAuxiliaryOps>(mut instructions: Vec<u8>) -> Result<Self> {
19 let mut fmt_insn = to_insn_vec(&instructions);
20 let mut index = 0;
21 let mut raw_file_ptr = vec![];
22 loop {
23 if index >= fmt_insn.len() {
24 break;
25 }
26 let mut insn = fmt_insn[index].clone();
27 if insn.opc == ebpf::LD_DW_IMM {
28 // relocate the instruction
29 let mut next_insn = fmt_insn[index + 1].clone();
30 // the imm is the map_fd because user lib has already done the relocation
31 let map_fd = insn.imm as usize;
32 let src_reg = insn.src;
33 // See https://www.kernel.org/doc/html/latest/bpf/standardization/instruction-set.html#id23
34 let ptr = match src_reg as u32 {
35 BPF_PSEUDO_MAP_VALUE => {
36 // dst = map_val(map_by_fd(imm)) + next_imm
37 // map_val(map) gets the address of the first value in a given map
38 let value_ptr = F::get_unified_map_from_fd(map_fd as u32, |unified_map| {
39 unified_map.map().map_values_ptr_range()
40 })?;
41 let offset = next_insn.imm as usize;
42 log::trace!(
43 "Relocate for BPF_PSEUDO_MAP_VALUE, instruction index: {}, map_fd: {}, ptr: {:#x}, offset: {}",
44 index,
45 map_fd,
46 value_ptr.start,
47 offset
48 );
49 Some(value_ptr.start + offset)
50 }
51 BPF_PSEUDO_MAP_FD => {
52 let map_ptr = F::get_unified_map_ptr_from_fd(map_fd as u32)? as usize;
53 log::trace!(
54 "Relocate for BPF_PSEUDO_MAP_FD, instruction index: {}, map_fd: {}, ptr: {:#x}",
55 index,
56 map_fd,
57 map_ptr
58 );
59 raw_file_ptr.push(map_ptr);
60 Some(map_ptr)
61 }
62 ty => {
63 log::error!(
64 "relocation for ty: {} not implemented, instruction index: {}",
65 ty,
66 index
67 );
68 None
69 }
70 };
71 if let Some(ptr) = ptr {
72 // The current ins store the map_data_ptr low 32 bits,
73 // the next ins store the map_data_ptr high 32 bits
74 insn.imm = ptr as i32;
75 next_insn.imm = (ptr >> 32) as i32;
76 fmt_insn[index] = insn;
77 fmt_insn[index + 1] = next_insn;
78 index += 2;
79 } else {
80 index += 1;
81 }
82 } else {
83 index += 1;
84 }
85 }
86 let mut idx = 0;
87 for ins in fmt_insn {
88 let bytes = ins.to_array();
89 instructions[idx..idx + 8].copy_from_slice(&bytes);
90 idx += 8;
91 }
92 Ok(Self {
93 new_insn: instructions,
94 raw_file_ptr,
95 })
96 }
97
98 /// Get the new instructions after preprocessing.
99 pub fn get_new_insn(&self) -> &Vec<u8> {
100 self.new_insn.as_ref()
101 }
102
103 /// Get the raw file pointer after preprocessing.
104 /// The raw file pointer is a list of pointers to the maps that are used in the program.
105 /// The pointers are used to access the maps in the program.
106 pub fn get_raw_file_ptr(&self) -> &Vec<usize> {
107 self.raw_file_ptr.as_ref()
108 }
109}