1use std::{
2 cmp,
3 collections::HashMap,
4 ffi::CStr,
5 io::{BufRead, Cursor, Seek, SeekFrom},
6 mem,
7 sync::Arc,
8};
9
10use memmap2::Mmap;
11
12use crate::{btf::*, cbtf, Error, Result};
13
14pub struct BtfSection(Box<dyn BtfBackend + Send + Sync>);
16
17impl BtfSection {
18 pub(super) fn from_mmap(mmap: Mmap, base: Option<Arc<BtfSection>>) -> Result<Self> {
22 Ok(Self(Box::new(MmapBtfSection::new(mmap, base)?)))
23 }
24
25 pub(super) fn from_reader<R: Seek + BufRead>(
28 reader: &mut R,
29 base: Option<Arc<BtfSection>>,
30 ) -> Result<Self> {
31 Ok(Self(Box::new(CachedBtfSection::new(reader, base)?)))
32 }
33
34 pub fn resolve_ids_by_name(&self, name: &str) -> Result<Vec<u32>> {
38 self.0.resolve_ids_by_name(name)
39 }
40
41 #[cfg(feature = "regex")]
46 pub fn resolve_ids_by_regex(&self, re: ®ex::Regex) -> Result<Vec<u32>> {
47 self.0.resolve_ids_by_regex(re)
48 }
49
50 pub fn resolve_type_by_id(&self, id: u32) -> Result<Type> {
52 self.0.resolve_type_by_id(id)
53 }
54
55 pub fn resolve_types_by_name(&self, name: &str) -> Result<Vec<Type>> {
59 let mut types = Vec::new();
60 self.resolve_ids_by_name(name)?
61 .iter()
62 .try_for_each(|id| -> Result<()> {
63 types.push(self.resolve_type_by_id(*id)?);
64 Ok(())
65 })?;
66 Ok(types)
67 }
68
69 #[cfg(feature = "regex")]
74 pub fn resolve_types_by_regex(&self, re: ®ex::Regex) -> Result<Vec<Type>> {
75 let mut types = Vec::new();
76 self.resolve_ids_by_regex(re)?
77 .iter()
78 .try_for_each(|id| -> Result<()> {
79 types.push(self.resolve_type_by_id(*id)?);
80 Ok(())
81 })?;
82 Ok(types)
83 }
84
85 pub fn type_id_range(&self) -> (u32, u32) {
88 let start = self.0.type_id_offset();
89 let end = start + self.0.types() as u32 - 1;
90 (start, end)
91 }
92
93 pub fn type_iter(&self) -> TypeIter<'_> {
95 TypeIter::new(self, None)
96 }
97
98 pub(super) fn resolve_name(&self, r#type: &dyn BtfType) -> Result<String> {
101 let offset = r#type
102 .get_name_offset()
103 .ok_or(Error::OpNotSupp("No name offset in type".to_string()))?;
104 self.resolve_name_by_offset(offset)
105 .ok_or(Error::InvalidString(offset))
106 }
107
108 fn header(&self) -> &cbtf::btf_header {
109 self.0.header()
110 }
111
112 fn types(&self) -> usize {
114 self.0.types()
115 }
116
117 fn resolve_name_by_offset(&self, offset: u32) -> Option<String> {
119 self.0.resolve_name_by_offset(offset)
120 }
121}
122
123pub(super) trait BtfBackend {
126 fn header(&self) -> &cbtf::btf_header;
128 fn type_id_offset(&self) -> u32;
130 fn types(&self) -> usize;
132 fn resolve_ids_by_name(&self, name: &str) -> Result<Vec<u32>>;
134 fn resolve_type_by_id(&self, id: u32) -> Result<Type>;
136 fn resolve_name_by_offset(&self, offset: u32) -> Option<String>;
138 #[cfg(feature = "regex")]
140 fn resolve_ids_by_regex(&self, re: ®ex::Regex) -> Result<Vec<u32>>;
141}
142
143struct CachedBtfSection {
147 header: cbtf::btf_header,
148 type_offset: u32,
150 str_cache: HashMap<u32, String>,
153 strings: HashMap<String, Vec<u32>>,
156 types: Vec<Type>,
160}
161
162impl CachedBtfSection {
163 fn new<R: Seek + BufRead>(reader: &mut R, base: Option<Arc<BtfSection>>) -> Result<Self> {
164 let (header, endianness) = cbtf::btf_header::from_reader(reader)?;
167 if header.version != 1 {
168 return Err(Error::Format(format!(
169 "Unsupported BTF version: {}",
170 header.version
171 )));
172 }
173 if header.flags != 0 {
174 return Err(Error::Format(format!(
175 "Unsupported flags {:#x}",
176 header.flags
177 )));
178 }
179 let (est_str, est_ty) = estimate(&header);
180
181 let offset = u64::checked_add(header.hdr_len as u64, header.str_off as u64)
183 .ok_or(Error::Format("Invalid strings section offset".to_string()))?;
184 reader.seek(SeekFrom::Start(offset))?;
185
186 let mut str_cache = HashMap::with_capacity(est_str);
187 let mut offset: u32 = 0;
188
189 let (mut id, start_str_off) = match base {
191 None => (1, 0),
192 Some(ref base) => (base.types() as u32, base.header().str_len),
193 };
194
195 while offset < header.str_len {
196 let mut raw = Vec::new();
197 let bytes = reader.read_until(b'\0', &mut raw)? as u32;
198
199 let s = bytes_to_str(&raw)?;
200 str_cache.insert(start_str_off + offset, String::from(s));
201
202 offset += bytes;
203 }
204
205 let offset = u64::checked_add(header.hdr_len as u64, header.type_off as u64)
207 .ok_or(Error::Format("Invalid types section offset".to_string()))?;
208 reader.seek(SeekFrom::Start(offset))?;
209
210 let mut strings: HashMap<String, Vec<u32>> = HashMap::with_capacity(est_str);
211 let mut types = Vec::with_capacity(est_ty);
212
213 if base.is_none() {
214 types.push(Type::Void);
217 }
218
219 let end_type_section = u64::checked_add(offset, header.type_len as u64)
220 .ok_or(Error::Format("Invalid types section length".to_string()))?;
221 while reader.stream_position()? < end_type_section {
222 let bt = cbtf::btf_type::from_reader(reader, &endianness)?;
223 let r#type = Type::from_reader(reader, &endianness, bt)?;
224
225 if let Some(name_off) = bt.name_offset() {
226 let name = str_cache.get(&name_off).cloned().or_else(|| {
229 base.as_ref()
230 .and_then(|base| base.resolve_name_by_offset(name_off))
231 });
232
233 match name {
234 Some(ref name) => match strings.get_mut(name) {
235 Some(entry) => entry.push(id),
236 None => _ = strings.insert(name.clone(), vec![id]),
237 },
238 None => return Err(Error::InvalidString(name_off)),
239 }
240 }
241
242 types.push(r#type);
243 id += 1;
244 }
245
246 if reader.stream_position()? != end_type_section {
248 return Err(Error::Format("Invalid type section".to_string()));
249 }
250
251 Ok(Self {
252 header,
253 type_offset: match base {
254 Some(base) => base.types() as u32,
255 None => 0,
256 },
257 str_cache,
258 strings,
259 types,
260 })
261 }
262}
263
264impl BtfBackend for CachedBtfSection {
265 fn header(&self) -> &cbtf::btf_header {
266 &self.header
267 }
268
269 fn type_id_offset(&self) -> u32 {
270 self.type_offset
271 }
272
273 fn types(&self) -> usize {
274 self.types.len()
275 }
276
277 fn resolve_ids_by_name(&self, name: &str) -> Result<Vec<u32>> {
278 Ok(self.strings.get(name).cloned().unwrap_or_default())
279 }
280
281 fn resolve_type_by_id(&self, id: u32) -> Result<Type> {
282 let local_id = match id.checked_sub(self.type_offset) {
283 Some(id) if (id as usize) < self.types() => id,
284 _ => return Err(Error::InvalidType(id)),
285 };
286
287 self.types
288 .get(local_id as usize)
289 .cloned()
290 .ok_or(Error::InvalidType(id))
291 }
292
293 fn resolve_name_by_offset(&self, offset: u32) -> Option<String> {
294 self.str_cache.get(&offset).cloned()
295 }
296
297 #[cfg(feature = "regex")]
298 fn resolve_ids_by_regex(&self, re: ®ex::Regex) -> Result<Vec<u32>> {
299 Ok(self
300 .strings
301 .iter()
302 .filter_map(|(name, ids)| match re.is_match(name) {
303 true => Some(ids.clone()),
304 false => None,
305 })
306 .flatten()
307 .collect::<Vec<_>>())
308 }
309}
310
311struct MmapBtfSection {
315 endianness: cbtf::Endianness,
316 header: cbtf::btf_header,
317 str_offset: u32,
319 type_offset: u32,
321 types: usize,
323 mmap: Mmap,
325 type_offsets: Vec<usize>,
327}
328
329impl MmapBtfSection {
330 fn new(mmap: Mmap, base: Option<Arc<BtfSection>>) -> Result<Self> {
331 let len = mmap.len();
332 let mut reader = Cursor::new(mmap);
333
334 let (header, endianness) = cbtf::btf_header::from_reader(&mut reader)?;
337 if header.version != 1 {
338 return Err(Error::Format(format!(
339 "Unsupported BTF version: {}",
340 header.version
341 )));
342 }
343 if header.flags != 0 {
344 return Err(Error::Format(format!(
345 "Unsupported flags {:#x}",
346 header.flags
347 )));
348 }
349 let (_, est_ty) = estimate(&header);
350
351 let offset = u64::checked_add(header.hdr_len as u64, header.str_off as u64)
353 .ok_or(Error::Format("Invalid strings section offset".to_string()))?;
354 let offset = u64::checked_add(offset, header.str_len as u64)
355 .ok_or(Error::Format("Invalid strings section length".to_string()))?;
356 if len < offset as usize {
357 return Err(Error::Format(
358 "String section is missing or incomplete".to_string(),
359 ));
360 }
361
362 let offset = u64::checked_add(header.hdr_len as u64, header.type_off as u64)
364 .ok_or(Error::Format("Invalid types section offset".to_string()))?;
365 reader.seek(SeekFrom::Start(offset))?;
366
367 let mut offsets = Vec::with_capacity(est_ty);
368 let mut types = 0;
369
370 let end_type_section = u64::checked_add(offset, header.type_len as u64)
371 .ok_or(Error::Format("Invalid types section length".to_string()))?;
372 while reader.stream_position()? < end_type_section {
373 offsets.push(reader.stream_position()? as usize);
374 cbtf::btf_skip_type(&mut reader, &endianness)?;
375 types += 1;
376 }
377
378 if reader.stream_position()? != end_type_section {
380 return Err(Error::Format("Invalid type section".to_string()));
381 }
382
383 let (str_offset, type_offset) = match base {
384 Some(base) => (base.header().str_len, base.types() as u32),
385 None => (0, 0),
386 };
387
388 Ok(Self {
389 endianness,
390 header,
391 str_offset,
392 type_offset,
393 types,
394 mmap: reader.into_inner(),
395 type_offsets: offsets,
396 })
397 }
398
399 fn iter_over_names<F>(&self, mut f: F) -> Result<()>
402 where
403 F: FnMut(u32, &[u8]) -> Result<()>,
404 {
405 let mmap = &self.mmap;
406
407 for (id, offset) in self.type_offsets.iter().enumerate() {
408 let bt = cbtf::btf_type::from_bytes(&mmap[*offset..], &self.endianness)?;
409 let name_off = match bt.name_offset() {
410 Some(offset) => offset,
411 None => continue,
412 };
413
414 if name_off < self.header.str_len {
415 let start = (self.header.hdr_len + self.header.str_off + name_off) as usize;
416
417 f(id as u32 + 1 + self.type_offset, &mmap[start..])?;
418 }
419 }
420
421 Ok(())
422 }
423}
424
425impl BtfBackend for MmapBtfSection {
426 fn header(&self) -> &cbtf::btf_header {
427 &self.header
428 }
429
430 fn type_id_offset(&self) -> u32 {
431 self.type_offset
432 }
433
434 fn types(&self) -> usize {
435 (if self.type_offset != 0 { 0 } else { 1 }) + self.types
437 }
438
439 fn resolve_ids_by_name(&self, name: &str) -> Result<Vec<u32>> {
440 let len = name.len();
441 let mut ids = Vec::new();
442
443 self.iter_over_names(|id, buf| {
444 if len < buf.len() && buf[len] == b'\0' && name.as_bytes() == &buf[..len] {
446 ids.push(id);
447 }
448 Ok(())
449 })?;
450
451 Ok(ids)
452 }
453
454 fn resolve_type_by_id(&self, id: u32) -> Result<Type> {
455 let local_id = match id.checked_sub(self.type_offset) {
456 Some(id) if (id as usize) < self.types() => id,
457 _ => return Err(Error::InvalidType(id)),
458 };
459
460 if id == 0 {
461 return Ok(Type::Void);
462 }
463
464 Ok(match self.type_offsets.get(local_id as usize - 1) {
465 Some(offset) => {
466 let bt = cbtf::btf_type::from_bytes(&self.mmap[*offset..], &self.endianness)?;
467 Type::from_bytes(
468 &self.mmap[(*offset + mem::size_of::<cbtf::btf_type>())..],
469 &self.endianness,
470 bt,
471 )?
472 }
473 None => return Err(Error::InvalidType(id)),
474 })
475 }
476
477 fn resolve_name_by_offset(&self, offset: u32) -> Option<String> {
478 let offset = match offset.checked_sub(self.str_offset) {
479 Some(id) if id <= self.header.str_len => id,
480 _ => return None,
481 };
482
483 let start = (self.header.hdr_len + self.header.str_off + offset) as usize;
484 bytes_to_str(&self.mmap[start..])
485 .ok()
486 .map(|s| s.to_string())
487 }
488
489 #[cfg(feature = "regex")]
490 fn resolve_ids_by_regex(&self, re: ®ex::Regex) -> Result<Vec<u32>> {
491 let mut ids = Vec::new();
492 self.iter_over_names(|id, buf| {
493 if let Ok(s) = bytes_to_str(buf) {
494 if re.is_match(s) {
495 ids.push(id);
496 }
497 }
498 Ok(())
499 })?;
500 Ok(ids)
501 }
502}
503
504fn estimate(header: &cbtf::btf_header) -> (usize, usize) {
506 let mut strings = header.str_len as usize / 15;
507 let mut types = header.type_len as usize / 22;
508
509 const MAX_SIZE: usize = 16 * 1024 * 1024;
511 strings = cmp::min(strings, MAX_SIZE / mem::size_of::<String>());
512 types = cmp::min(types, MAX_SIZE / mem::size_of::<Type>());
513
514 (strings, types)
515}
516
517fn bytes_to_str(buf: &[u8]) -> Result<&str> {
519 CStr::from_bytes_until_nul(buf)
520 .map_err(|e| Error::Format(format!("Could not parse string: {e}")))?
521 .to_str()
522 .map_err(|e| Error::Format(format!("Invalid UTF-8 string: {e}")))
523}