Skip to main content

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}