aya_obj/btf/
info.rs

1use alloc::{string::String, vec, vec::Vec};
2
3use bytes::BufMut as _;
4use object::Endianness;
5
6use crate::{
7    generated::{bpf_func_info, bpf_line_info},
8    relocation::INS_SIZE,
9    util::{HashMap, bytes_of},
10};
11
12/* The func_info subsection layout:
13 *   record size for struct bpf_func_info in the func_info subsection
14 *   struct btf_sec_func_info for section #1
15 *   a list of bpf_func_info records for section #1
16 *     where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
17 *     but may not be identical
18 *   struct btf_sec_func_info for section #2
19 *   a list of bpf_func_info records for section #2
20 *   ......
21 */
22
23/// A collection of [bpf_func_info] collected from the `btf_ext_info_sec` struct
24/// inside the [FuncInfo] subsection.
25///
26/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
27/// for more information.
28#[derive(Debug, Clone, Default)]
29pub struct FuncSecInfo {
30    pub(crate) _sec_name_offset: u32,
31    /// The number of info entries
32    pub num_info: u32,
33    /// Info entries
34    pub func_info: Vec<bpf_func_info>,
35}
36
37impl FuncSecInfo {
38    pub(crate) fn parse(
39        sec_name_offset: u32,
40        num_info: u32,
41        rec_size: usize,
42        func_info_data: &[u8],
43        endianness: Endianness,
44    ) -> Self {
45        let func_info = func_info_data
46            .chunks(rec_size)
47            .map(|data| {
48                let read_u32 = if endianness == Endianness::Little {
49                    u32::from_le_bytes
50                } else {
51                    u32::from_be_bytes
52                };
53
54                let mut offset = 0;
55
56                // ELF instruction offsets are in bytes
57                // Kernel instruction offsets are in instructions units
58                // We can convert by dividing the length in bytes by INS_SIZE
59                let insn_off =
60                    read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
61                offset += 4;
62                let type_id = read_u32(data[offset..offset + 4].try_into().unwrap());
63
64                bpf_func_info { insn_off, type_id }
65            })
66            .collect();
67
68        Self {
69            _sec_name_offset: sec_name_offset,
70            num_info,
71            func_info,
72        }
73    }
74
75    /// Encodes the [bpf_func_info] entries.
76    pub fn func_info_bytes(&self) -> Vec<u8> {
77        let mut buf = vec![];
78        for l in &self.func_info {
79            // Safety: bpf_func_info is POD
80            buf.put(unsafe { bytes_of::<bpf_func_info>(l) })
81        }
82        buf
83    }
84
85    /// Returns the number of [bpf_func_info] entries.
86    #[expect(clippy::len_without_is_empty)]
87    pub fn len(&self) -> usize {
88        self.func_info.len()
89    }
90}
91
92/// A collection of [FuncSecInfo] collected from the `func_info` subsection
93/// in the `.BTF.ext` section.
94///
95/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
96/// for more information.
97#[derive(Debug, Clone)]
98pub struct FuncInfo {
99    /// The [FuncSecInfo] subsections for some sections, referenced by section names
100    pub data: HashMap<String, FuncSecInfo>,
101}
102
103impl FuncInfo {
104    pub(crate) fn new() -> Self {
105        Self {
106            data: HashMap::new(),
107        }
108    }
109
110    pub(crate) fn get(&self, name: &str) -> FuncSecInfo {
111        match self.data.get(name) {
112            Some(d) => d.clone(),
113            None => FuncSecInfo::default(),
114        }
115    }
116}
117
118/// A collection of [bpf_line_info] collected from the `btf_ext_info_sec` struct
119/// inside the `line_info` subsection.
120///
121/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
122/// for more information.
123#[derive(Debug, Clone, Default)]
124pub struct LineSecInfo {
125    // each line info section has a header
126    pub(crate) _sec_name_offset: u32,
127    /// The number of entries
128    pub num_info: u32,
129    // followed by one or more bpf_line_info structs
130    /// The [bpf_line_info] entries
131    pub line_info: Vec<bpf_line_info>,
132}
133
134impl LineSecInfo {
135    pub(crate) fn parse(
136        sec_name_offset: u32,
137        num_info: u32,
138        rec_size: usize,
139        func_info_data: &[u8],
140        endianness: Endianness,
141    ) -> Self {
142        let line_info = func_info_data
143            .chunks(rec_size)
144            .map(|data| {
145                let read_u32 = if endianness == Endianness::Little {
146                    u32::from_le_bytes
147                } else {
148                    u32::from_be_bytes
149                };
150
151                let mut offset = 0;
152
153                // ELF instruction offsets are in bytes
154                // Kernel instruction offsets are in instructions units
155                // We can convert by dividing the length in bytes by INS_SIZE
156                let insn_off =
157                    read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
158                offset += 4;
159                let file_name_off = read_u32(data[offset..offset + 4].try_into().unwrap());
160                offset += 4;
161                let line_off = read_u32(data[offset..offset + 4].try_into().unwrap());
162                offset += 4;
163                let line_col = read_u32(data[offset..offset + 4].try_into().unwrap());
164
165                bpf_line_info {
166                    insn_off,
167                    file_name_off,
168                    line_off,
169                    line_col,
170                }
171            })
172            .collect();
173
174        Self {
175            _sec_name_offset: sec_name_offset,
176            num_info,
177            line_info,
178        }
179    }
180
181    /// Encodes the entries.
182    pub fn line_info_bytes(&self) -> Vec<u8> {
183        let mut buf = vec![];
184        for l in &self.line_info {
185            // Safety: bpf_func_info is POD
186            buf.put(unsafe { bytes_of::<bpf_line_info>(l) })
187        }
188        buf
189    }
190
191    /// Returns the number of entries.
192    #[expect(clippy::len_without_is_empty)]
193    pub fn len(&self) -> usize {
194        self.line_info.len()
195    }
196}
197
198#[derive(Debug, Clone)]
199pub(crate) struct LineInfo {
200    pub data: HashMap<String, LineSecInfo>,
201}
202
203impl LineInfo {
204    pub(crate) fn new() -> Self {
205        Self {
206            data: HashMap::new(),
207        }
208    }
209
210    pub(crate) fn get(&self, name: &str) -> LineSecInfo {
211        match self.data.get(name) {
212            Some(d) => d.clone(),
213            None => LineSecInfo::default(),
214        }
215    }
216}