Skip to main content

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}