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 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}