btfparse/btf/
type_information.rs

1/*
2  Copyright (c) 2024-present, Alessandro Gario
3  All rights reserved.
4
5  This source code is licensed in accordance with the terms specified in
6  the LICENSE file found in the root directory of this source tree.
7*/
8
9use crate::{
10    btf::{
11        Array, Const, DataSec, DeclTag, Enum, Enum64, Error as BTFError, ErrorKind as BTFErrorKind,
12        FileHeader, Float, Func, FuncProto, Fwd, Header, Int, Kind, Offset, Ptr, Readable,
13        Restrict, Result as BTFResult, Struct, TypeTag, Typedef, Union, Var, Volatile,
14    },
15    generate_constructor_dispatcher,
16    utils::Reader,
17};
18
19use std::{collections::BTreeMap, ops::Add};
20
21#[cfg(feature = "caching")]
22use std::{collections::HashMap, sync::RwLock};
23
24/// An enum representing a BTF type
25#[derive(Debug, Clone)]
26pub enum TypeVariant {
27    /// The void type
28    Void,
29
30    /// An integer type
31    Int(Int),
32
33    /// A typedef type
34    Typedef(Typedef),
35
36    /// A 32-bit enum type
37    Enum(Enum),
38
39    /// A pointer type
40    Ptr(Ptr),
41
42    /// A const type
43    Const(Const),
44
45    /// A volatile type
46    Volatile(Volatile),
47
48    /// An array type
49    Array(Array),
50
51    /// A function prototype
52    FuncProto(FuncProto),
53
54    /// A struct type
55    Struct(Struct),
56
57    /// A union type
58    Union(Union),
59
60    /// A forward declaration type
61    Fwd(Fwd),
62
63    /// A variable declaration
64    Var(Var),
65
66    /// A 64-bit enum type
67    Enum64(Enum64),
68
69    /// A function declaration
70    Func(Func),
71
72    /// A float type
73    Float(Float),
74
75    /// A restrict type
76    Restrict(Restrict),
77
78    /// A data section decl
79    DataSec(DataSec),
80
81    /// A type tag
82    TypeTag(TypeTag),
83
84    /// A decl tag
85    DeclTag(DeclTag),
86}
87
88/// Returns the name of the given type
89fn get_type_enum_value_name(type_var: &TypeVariant) -> Option<String> {
90    match type_var {
91        TypeVariant::Void => Some("void".to_string()),
92        TypeVariant::Int(int) => int.name().clone(),
93        TypeVariant::Typedef(typedef) => typedef.name().clone(),
94        TypeVariant::Enum(r#enum) => r#enum.name().clone(),
95        TypeVariant::Struct(r#struct) => r#struct.name().clone(),
96        TypeVariant::Union(r#union) => r#union.name().clone(),
97        TypeVariant::Fwd(fwd) => fwd.name().clone(),
98        TypeVariant::Var(var) => var.name().clone(),
99        TypeVariant::Enum64(enum64) => enum64.name().clone(),
100        TypeVariant::Func(func) => func.name().clone(),
101        TypeVariant::Float(float) => float.name().clone(),
102        TypeVariant::DataSec(data_sec) => data_sec.name().clone(),
103        TypeVariant::TypeTag(type_tag) => type_tag.name().clone(),
104        TypeVariant::DeclTag(decl_tag) => decl_tag.name().clone(),
105
106        TypeVariant::Ptr(_)
107        | TypeVariant::Const(_)
108        | TypeVariant::Volatile(_)
109        | TypeVariant::Array(_)
110        | TypeVariant::FuncProto(_)
111        | TypeVariant::Restrict(_) => None,
112    }
113}
114
115/// A component of a type path
116#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
117pub enum TypePathComponent<'a> {
118    /// An index into an array
119    Index(usize),
120
121    /// A name of a struct (or union) field
122    Name(&'a str),
123}
124
125/// Tracks the internal state of the type path parser
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127enum TypePathParserState {
128    /// The initial (empty) state
129    Start,
130
131    /// Inside a field name
132    InsideName,
133
134    /// Inside an index
135    InsideIndex,
136
137    /// After an index (expecting '.' or '[')
138    AfterIndex,
139
140    /// Expecting the first character of a name (after '.')
141    ExpectingName,
142
143    /// Parser has encountered an error
144    Error,
145
146    /// Parser is done
147    Done,
148}
149
150/// An iterator over the components of a type path string
151#[derive(Debug, Clone)]
152struct TypePathComponentIter<'a> {
153    path: &'a str,
154    position: usize,
155    state: TypePathParserState,
156}
157
158impl<'a> TypePathComponentIter<'a> {
159    fn new(path: &'a str) -> Self {
160        Self {
161            path,
162            position: 0,
163            state: TypePathParserState::Start,
164        }
165    }
166}
167
168impl<'a> Iterator for TypePathComponentIter<'a> {
169    type Item = BTFResult<TypePathComponent<'a>>;
170
171    fn next(&mut self) -> Option<Self::Item> {
172        if self.state == TypePathParserState::Done || self.state == TypePathParserState::Error {
173            return None;
174        }
175
176        let bytes = self.path.as_bytes();
177
178        // Handle empty path or end of input in certain states
179        if self.position >= bytes.len() {
180            self.state = TypePathParserState::Done;
181            return None;
182        }
183
184        match self.state {
185            TypePathParserState::Start => {
186                let c = bytes[self.position] as char;
187                if c == '[' {
188                    self.position += 1;
189                    self.state = TypePathParserState::InsideIndex;
190                    self.parse_index()
191                } else if c.is_alphabetic() || c == '_' {
192                    self.state = TypePathParserState::InsideName;
193                    self.parse_name()
194                } else {
195                    self.state = TypePathParserState::Error;
196                    Some(Err(BTFError::new(
197                        BTFErrorKind::InvalidTypePath,
198                        &format!("Invalid character at index {}", self.position),
199                    )))
200                }
201            }
202
203            TypePathParserState::InsideName => self.parse_name(),
204
205            TypePathParserState::InsideIndex => self.parse_index(),
206
207            TypePathParserState::AfterIndex => {
208                let c = bytes[self.position] as char;
209                if c == '[' {
210                    self.position += 1;
211                    self.state = TypePathParserState::InsideIndex;
212                    self.parse_index()
213                } else if c == '.' {
214                    self.position += 1;
215                    self.state = TypePathParserState::ExpectingName;
216                    self.next()
217                } else {
218                    self.state = TypePathParserState::Error;
219                    Some(Err(BTFError::new(
220                        BTFErrorKind::InvalidTypePath,
221                        &format!("Invalid character at index {}", self.position),
222                    )))
223                }
224            }
225
226            TypePathParserState::ExpectingName => {
227                if self.position >= bytes.len() {
228                    self.state = TypePathParserState::Error;
229                    return Some(Err(BTFError::new(
230                        BTFErrorKind::InvalidTypePath,
231                        "Expected name after '.'",
232                    )));
233                }
234                let c = bytes[self.position] as char;
235                if c.is_alphabetic() || c == '_' {
236                    self.state = TypePathParserState::InsideName;
237                    self.parse_name()
238                } else {
239                    self.state = TypePathParserState::Error;
240                    Some(Err(BTFError::new(
241                        BTFErrorKind::InvalidTypePath,
242                        &format!("Invalid character at index {}", self.position),
243                    )))
244                }
245            }
246
247            TypePathParserState::Done | TypePathParserState::Error => None,
248        }
249    }
250}
251
252impl<'a> TypePathComponentIter<'a> {
253    fn parse_name(&mut self) -> Option<BTFResult<TypePathComponent<'a>>> {
254        let start = self.position;
255        let bytes = self.path.as_bytes();
256
257        while self.position < bytes.len() {
258            let c = bytes[self.position] as char;
259            if c.is_alphanumeric() || c == '_' {
260                self.position += 1;
261            } else if c == '[' || c == '.' {
262                break;
263            } else {
264                self.state = TypePathParserState::Error;
265                return Some(Err(BTFError::new(
266                    BTFErrorKind::InvalidTypePath,
267                    &format!("Invalid character at index {}", self.position),
268                )));
269            }
270        }
271
272        let name = &self.path[start..self.position];
273
274        // Determine next state
275        if self.position >= bytes.len() {
276            self.state = TypePathParserState::Done;
277        } else {
278            let c = bytes[self.position] as char;
279            if c == '[' {
280                self.position += 1;
281                self.state = TypePathParserState::InsideIndex;
282            } else if c == '.' {
283                self.position += 1;
284                self.state = TypePathParserState::ExpectingName;
285            }
286        }
287
288        Some(Ok(TypePathComponent::Name(name)))
289    }
290
291    fn parse_index(&mut self) -> Option<BTFResult<TypePathComponent<'a>>> {
292        let start = self.position;
293        let bytes = self.path.as_bytes();
294
295        while self.position < bytes.len() {
296            let c = bytes[self.position] as char;
297            if c.is_numeric() {
298                self.position += 1;
299            } else if c == ']' {
300                break;
301            } else {
302                self.state = TypePathParserState::Error;
303                return Some(Err(BTFError::new(
304                    BTFErrorKind::InvalidTypePath,
305                    &format!("Invalid character at index {}", self.position),
306                )));
307            }
308        }
309
310        if self.position >= bytes.len() || bytes[self.position] as char != ']' {
311            self.state = TypePathParserState::Error;
312            return Some(Err(BTFError::new(
313                BTFErrorKind::InvalidTypePath,
314                "Unclosed index bracket",
315            )));
316        }
317
318        let index_str = &self.path[start..self.position];
319        if index_str.is_empty() {
320            self.state = TypePathParserState::Error;
321            return Some(Err(BTFError::new(
322                BTFErrorKind::InvalidTypePath,
323                "Empty index",
324            )));
325        }
326
327        let index = match index_str.parse::<usize>() {
328            Ok(i) => i,
329            Err(error) => {
330                self.state = TypePathParserState::Error;
331                return Some(Err(BTFError::new(
332                    BTFErrorKind::InvalidTypePath,
333                    &format!("Invalid index value: {error:?}"),
334                )));
335            }
336        };
337
338        // Skip the ']'
339        self.position += 1;
340        self.state = TypePathParserState::AfterIndex;
341
342        Some(Ok(TypePathComponent::Index(index)))
343    }
344}
345
346/// Type information acquired from the BTF data
347pub struct TypeInformation {
348    /// Maps a type id to the type object
349    id_to_type_map: BTreeMap<u32, TypeVariant>,
350
351    /// Maps a type name to a type id
352    name_to_id_map: BTreeMap<String, u32>,
353
354    /// Maps a type id to a type name
355    id_to_name_map: BTreeMap<u32, String>,
356
357    /// Cache for offset_of results to avoid redundant path parsing and type traversal
358    #[cfg(feature = "caching")]
359    offset_cache: RwLock<HashMap<(u32, String), (u32, Offset)>>,
360}
361
362// Generate the parse_type functions for each type
363generate_constructor_dispatcher!(
364    Int, Typedef, Enum, Ptr, Const, Volatile, Array, FuncProto, Struct, Union, Fwd, Var, Enum64,
365    Func, Float, Restrict, DataSec, TypeTag, DeclTag
366);
367
368/// Lightweight error type for offset_of_helper that avoids string formatting.
369/// Only formatted into a full BTFError when the error escapes to the caller.
370#[derive(Debug)]
371enum OffsetError<'a> {
372    InvalidTypeId,
373    VoidDereference,
374    IndexOutOfBounds {
375        index: usize,
376        array_size: u32,
377    },
378    ArrayOffsetOverflow,
379    PtrNotIndexable,
380    TypeNotIndexable,
381    NotStructOrUnion,
382    MemberNotFound {
383        name: &'a str,
384    },
385    /// Wraps an existing BTFError (e.g., from offset.add() or iterator)
386    Btf(BTFError),
387}
388
389impl From<BTFError> for OffsetError<'_> {
390    fn from(e: BTFError) -> Self {
391        OffsetError::Btf(e)
392    }
393}
394
395impl From<OffsetError<'_>> for BTFError {
396    fn from(e: OffsetError<'_>) -> Self {
397        match e {
398            OffsetError::InvalidTypeId => {
399                BTFError::new(BTFErrorKind::InvalidTypeID, "Invalid type id")
400            }
401            OffsetError::VoidDereference => BTFError::new(
402                BTFErrorKind::InvalidTypePath,
403                "The void type can't be dereferenced with a path",
404            ),
405            OffsetError::IndexOutOfBounds { index, array_size } => BTFError::new(
406                BTFErrorKind::InvalidTypePath,
407                &format!("Index {index} is out of bounds for array of size {array_size}"),
408            ),
409            OffsetError::ArrayOffsetOverflow => BTFError::new(
410                BTFErrorKind::InvalidTypePath,
411                "Array element offset overflow",
412            ),
413            OffsetError::PtrNotIndexable => BTFError::new(
414                BTFErrorKind::InvalidTypePath,
415                "Type is a ptr, and dereferencing it would require a read operation",
416            ),
417            OffsetError::TypeNotIndexable => {
418                BTFError::new(BTFErrorKind::InvalidTypePath, "Type is not indexable")
419            }
420            OffsetError::NotStructOrUnion => BTFError::new(
421                BTFErrorKind::InvalidTypePath,
422                "Type is not a struct or union",
423            ),
424            OffsetError::MemberNotFound { name } => BTFError::new(
425                BTFErrorKind::InvalidTypePath,
426                &format!("Member '{}' not found", name),
427            ),
428            OffsetError::Btf(e) => e,
429        }
430    }
431}
432
433impl TypeInformation {
434    /// Creates a new `TypeInformation` object
435    pub fn new(readable: &dyn Readable) -> BTFResult<Self> {
436        let mut reader = Reader::new(readable);
437
438        let file_header = FileHeader::new(&mut reader)?;
439        let type_section_start = (file_header
440            .hdr_len()
441            .checked_add(file_header.type_off())
442            .ok_or_else(|| {
443                BTFError::new(
444                    BTFErrorKind::InvalidTypeSectionOffset,
445                    "Type section start offset overflow",
446                )
447            })?) as usize;
448
449        let type_section_end = type_section_start
450            .checked_add(file_header.type_len() as usize)
451            .ok_or_else(|| {
452                BTFError::new(
453                    BTFErrorKind::InvalidTypeSectionOffset,
454                    "Type section end offset overflow",
455                )
456            })?;
457
458        reader.set_offset(type_section_start);
459
460        let mut tid_generator: u32 = 1;
461
462        let mut id_to_type_map = BTreeMap::<u32, TypeVariant>::new();
463        let mut name_to_id_map = BTreeMap::<String, u32>::new();
464        let mut id_to_name_map = BTreeMap::<u32, String>::new();
465
466        while reader.offset() < type_section_end {
467            let type_header = Header::new(&mut reader, &file_header)?;
468            let btf_type = parse_type(type_header.kind(), &mut reader, &file_header, type_header)?;
469
470            let tid = tid_generator;
471            tid_generator += 1;
472
473            if let Some(name) = get_type_enum_value_name(&btf_type) {
474                name_to_id_map.insert(name.to_string(), tid);
475                id_to_name_map.insert(tid, name.to_string());
476            }
477
478            id_to_type_map.insert(tid, btf_type);
479        }
480
481        Ok(Self {
482            id_to_type_map,
483            name_to_id_map,
484            id_to_name_map,
485            #[cfg(feature = "caching")]
486            offset_cache: RwLock::new(HashMap::new()),
487        })
488    }
489
490    /// Returns the entire type map
491    pub fn get(&self) -> &BTreeMap<u32, TypeVariant> {
492        &self.id_to_type_map
493    }
494
495    /// Returns the type id for the given type name
496    pub fn id_of(&self, type_name: &str) -> Option<u32> {
497        if type_name == "void" {
498            return Some(0);
499        }
500
501        self.name_to_id_map.get(type_name).copied()
502    }
503
504    /// Returns the type object for the given type id
505    pub fn from_id(&self, tid: u32) -> Option<TypeVariant> {
506        if tid == 0 {
507            return Some(TypeVariant::Void);
508        }
509
510        self.id_to_type_map.get(&tid).cloned()
511    }
512
513    /// Returns the name of the given type id
514    pub fn name_of(&self, tid: u32) -> Option<String> {
515        if tid == 0 {
516            return Some("void".to_string());
517        }
518
519        self.id_to_name_map.get(&tid).cloned()
520    }
521
522    /// Returns the pointee type id
523    pub fn pointee_tid(&self, tid: u32) -> BTFResult<u32> {
524        match self.from_id(tid) {
525            None => Err(BTFError::new(
526                BTFErrorKind::InvalidTypeID,
527                "Invalid type id",
528            )),
529
530            Some(type_variant) => match type_variant {
531                TypeVariant::Typedef(typedef) => self.pointee_tid(*typedef.tid()),
532                TypeVariant::Const(cnst) => self.pointee_tid(*cnst.tid()),
533                TypeVariant::Volatile(volatile) => self.pointee_tid(*volatile.tid()),
534                TypeVariant::Restrict(restrict) => self.pointee_tid(*restrict.tid()),
535
536                TypeVariant::Ptr(ptr) => Ok(*ptr.tid()),
537
538                _ => Err(BTFError::new(
539                    BTFErrorKind::InvalidTypeID,
540                    "Type is not a pointer",
541                )),
542            },
543        }
544    }
545
546    /// Returns the size of the given type id
547    pub fn size_of(&self, tid: u32) -> BTFResult<usize> {
548        let type_variant = self.from_id(tid).ok_or(BTFError::new(
549            BTFErrorKind::InvalidTypeID,
550            "Invalid type id",
551        ))?;
552
553        match type_variant {
554            TypeVariant::Ptr(_) => {
555                let list_head_tid = self.id_of("list_head").ok_or(BTFError::new(
556                    BTFErrorKind::InvalidTypeID,
557                    "The `struct list_head` type, used to extract the pointer size, was not found",
558                ))?;
559
560                let list_head_type_var = self.from_id(list_head_tid).ok_or(
561                    BTFError::new(BTFErrorKind::InvalidTypeID, "The extracted `struct list_head` type ID, used to extract the pointer size, was invalid"),
562                )?;
563
564                let list_head_type_size = match list_head_type_var {
565                    TypeVariant::Struct(str) => Ok(*str.size()),
566
567                    _ => Err(BTFError::new(
568                        BTFErrorKind::InvalidTypeID,
569                        "The extracted `struct list_head` type ID, used to extract the pointer size, is not a struct type",
570                    )),
571                }?;
572
573                Ok(list_head_type_size / 2)
574            }
575
576            TypeVariant::Array(array) => {
577                let tid = *array.element_tid();
578                let element_size = self.size_of(tid)?;
579                let element_count = *array.element_count() as usize;
580
581                Ok(element_size * element_count)
582            }
583
584            TypeVariant::Float(float) => Ok(*float.size()),
585            TypeVariant::Int(int) => Ok(*int.size()),
586            TypeVariant::Enum(enm) => Ok(*enm.size()),
587            TypeVariant::Enum64(enm) => Ok(*enm.size()),
588            TypeVariant::Struct(str) => Ok(*str.size()),
589            TypeVariant::Union(union) => Ok(*union.size()),
590            TypeVariant::DataSec(data_sec) => Ok(*data_sec.size()),
591
592            TypeVariant::Var(var) => self.size_of(*var.tid()),
593            TypeVariant::Typedef(typedef) => self.size_of(*typedef.tid()),
594            TypeVariant::Const(cnst) => self.size_of(*cnst.tid()),
595            TypeVariant::Volatile(volatile) => self.size_of(*volatile.tid()),
596            TypeVariant::Restrict(restrict) => self.size_of(*restrict.tid()),
597            TypeVariant::TypeTag(type_tag) => self.size_of(*type_tag.tid()),
598
599            _ => Err(BTFError::new(
600                BTFErrorKind::NotSized,
601                &format!("Type {type_variant:?} has no size"),
602            )),
603        }
604    }
605
606    /// Returns a tuple containing the next type id and the current offset
607    pub fn offset_of(&self, tid: u32, path: &str) -> BTFResult<(u32, Offset)> {
608        #[cfg(feature = "caching")]
609        {
610            // Check cache first (gracefully handle poisoned lock by computing fresh)
611            let cache_key = (tid, path.to_string());
612            if let Ok(cache) = self.offset_cache.read()
613                && let Some(&cached) = cache.get(&cache_key)
614            {
615                return Ok(cached);
616            }
617
618            // Cache miss - compute the result
619            let result = self.offset_of_uncached(tid, path)?;
620
621            // Store in cache (ignore if lock is poisoned)
622            if let Ok(mut cache) = self.offset_cache.write() {
623                cache.insert(cache_key, result);
624            }
625
626            Ok(result)
627        }
628
629        #[cfg(not(feature = "caching"))]
630        self.offset_of_uncached(tid, path)
631    }
632
633    /// Internal uncached implementation of offset_of
634    fn offset_of_uncached(&self, tid: u32, path: &str) -> BTFResult<(u32, Offset)> {
635        let mut path_iter = TypePathComponentIter::new(path);
636        self.offset_of_impl(Offset::ByteOffset(0), tid, &mut path_iter)
637            .map_err(Into::into)
638    }
639
640    /// Internal helper method for `TypeInformation::offset_of`
641    ///
642    /// Returns a lightweight `OffsetError` that avoids string formatting.
643    /// Errors are only formatted when converted to `BTFError` at the public API boundary.
644    fn offset_of_impl<'a>(
645        &self,
646        mut offset: Offset,
647        mut tid: u32,
648        path: &mut TypePathComponentIter<'a>,
649    ) -> Result<(u32, Offset), OffsetError<'a>> {
650        loop {
651            // Save iterator position before consuming (for anonymous member probing)
652            let path_for_anon = path.clone();
653
654            // Get next component
655            let component = match path.next() {
656                None => return Ok((tid, offset)),
657                Some(result) => result?,
658            };
659
660            // Resolve through type indirections (Fwd, Typedef, Const, Volatile, Restrict)
661            let type_var = loop {
662                let type_var = self.from_id(tid).ok_or(OffsetError::InvalidTypeId)?;
663
664                match &type_var {
665                    TypeVariant::Fwd(fwd) => {
666                        tid = *fwd.tid();
667                    }
668                    TypeVariant::Typedef(typedef) => {
669                        tid = *typedef.tid();
670                    }
671                    TypeVariant::Const(cnst) => {
672                        tid = *cnst.tid();
673                    }
674                    TypeVariant::Volatile(volatile) => {
675                        tid = *volatile.tid();
676                    }
677                    TypeVariant::Restrict(restrict) => {
678                        tid = *restrict.tid();
679                    }
680                    _ => break type_var,
681                }
682            };
683
684            // Check for void
685            if matches!(type_var, TypeVariant::Void) {
686                return Err(OffsetError::VoidDereference);
687            }
688
689            match component {
690                TypePathComponent::Index(index) => match &type_var {
691                    TypeVariant::Array(array) => {
692                        let element_count = *array.element_count() as usize;
693                        if index >= element_count {
694                            return Err(OffsetError::IndexOutOfBounds {
695                                index,
696                                array_size: *array.element_count(),
697                            });
698                        }
699
700                        let element_tid = *array.element_tid();
701                        let element_type_size = self.size_of(element_tid)?;
702
703                        let index_size = (index as u64)
704                            .checked_mul(element_type_size as u64)
705                            .and_then(|v| u32::try_from(v).ok())
706                            .ok_or(OffsetError::ArrayOffsetOverflow)?;
707
708                        offset = offset.add(index_size)?;
709                        tid = element_tid;
710                    }
711
712                    TypeVariant::Ptr(_) => {
713                        return Err(OffsetError::PtrNotIndexable);
714                    }
715
716                    _ => {
717                        return Err(OffsetError::TypeNotIndexable);
718                    }
719                },
720
721                TypePathComponent::Name(name) => {
722                    let member_list: &[_] = match &type_var {
723                        TypeVariant::Struct(s) => s.member_list(),
724                        TypeVariant::Union(u) => u.member_list(),
725                        _ => {
726                            return Err(OffsetError::NotStructOrUnion);
727                        }
728                    };
729
730                    // Try anonymous members first (using path_for_anon which includes current component)
731                    for member in member_list.iter().filter(|m| m.name().is_none()) {
732                        let mut anon_path = path_for_anon.clone();
733                        match self.offset_of_impl(
734                            member.offset().add(offset)?,
735                            member.tid(),
736                            &mut anon_path,
737                        ) {
738                            Ok((result_tid, result_offset)) => {
739                                return Ok((result_tid, result_offset));
740                            }
741                            Err(_) => continue,
742                        }
743                    }
744
745                    // Try named members
746                    match member_list
747                        .iter()
748                        .find(|member| member.name().as_deref() == Some(name))
749                    {
750                        Some(member) => {
751                            tid = member.tid();
752                            offset = offset.add(member.offset())?;
753                        }
754                        None => {
755                            return Err(OffsetError::MemberNotFound { name });
756                        }
757                    }
758                }
759            }
760        }
761    }
762}
763
764#[cfg(test)]
765mod tests {
766    use std::vec;
767
768    use super::*;
769
770    use crate::btf::{
771        LinkageType,
772        data_sec::Variable as DataSecVariable,
773        r#enum::{Integer32Value as IntegerValue32, NamedValue32},
774        enum64::{Integer64Value as IntegerValue64, NamedValue64},
775        struct_union::Member as StructMember,
776    };
777    use crate::utils::ReadableBuffer;
778    #[cfg(feature = "caching")]
779    use std::{collections::HashMap, sync::RwLock};
780
781    /// Helper to collect iterator results into a Vec for testing
782    fn collect_path_components(path: &str) -> BTFResult<Vec<TypePathComponent<'_>>> {
783        TypePathComponentIter::new(path).collect()
784    }
785
786    #[test]
787    fn test_path_component_iter() {
788        let type_path = collect_path_components("").unwrap();
789        assert!(type_path.is_empty());
790
791        let type_path = collect_path_components("[1]").unwrap();
792        assert_eq!(type_path.len(), 1);
793        assert_eq!(type_path[0], TypePathComponent::Index(1));
794
795        let type_path = collect_path_components("[1][2]").unwrap();
796        assert_eq!(type_path.len(), 2);
797        assert_eq!(type_path[0], TypePathComponent::Index(1));
798        assert_eq!(type_path[1], TypePathComponent::Index(2));
799
800        let type_path = collect_path_components("test").unwrap();
801        assert_eq!(type_path.len(), 1);
802        assert_eq!(type_path[0], TypePathComponent::Name("test"));
803
804        let type_path = collect_path_components("array[10]").unwrap();
805        assert_eq!(type_path.len(), 2);
806        assert_eq!(type_path[0], TypePathComponent::Name("array"));
807        assert_eq!(type_path[1], TypePathComponent::Index(10));
808
809        let type_path = collect_path_components("array[10].array2[11]").unwrap();
810        assert_eq!(type_path.len(), 4);
811        assert_eq!(type_path[0], TypePathComponent::Name("array"));
812        assert_eq!(type_path[1], TypePathComponent::Index(10));
813        assert_eq!(type_path[2], TypePathComponent::Name("array2"));
814        assert_eq!(type_path[3], TypePathComponent::Index(11));
815
816        // Test underscore in names
817        let type_path = collect_path_components("_test").unwrap();
818        assert_eq!(type_path.len(), 1);
819        assert_eq!(type_path[0], TypePathComponent::Name("_test"));
820
821        let type_path = collect_path_components("test_field").unwrap();
822        assert_eq!(type_path.len(), 1);
823        assert_eq!(type_path[0], TypePathComponent::Name("test_field"));
824
825        assert!(collect_path_components(".value").is_err());
826        assert!(collect_path_components(".[10]").is_err());
827        assert!(collect_path_components("[value").is_err());
828        assert!(collect_path_components("]value").is_err());
829        assert!(collect_path_components("1").is_err());
830        assert!(collect_path_components("array[10]value").is_err());
831        assert!(collect_path_components("array[]").is_err());
832        assert!(collect_path_components("[]").is_err());
833    }
834
835    fn get_test_type_info() -> TypeInformation {
836        let mut type_info = TypeInformation {
837            id_to_type_map: BTreeMap::<u32, TypeVariant>::new(),
838            name_to_id_map: BTreeMap::<String, u32>::new(),
839            id_to_name_map: BTreeMap::<u32, String>::new(),
840            #[cfg(feature = "caching")]
841            offset_cache: RwLock::new(HashMap::new()),
842        };
843
844        // tid:1 BTF_KIND_INT
845        type_info.id_to_type_map.insert(
846            1,
847            TypeVariant::Int(Int::create(
848                Header::create(Kind::Int, 1, 0, false, 4),
849                Some(String::from("unsigned int")),
850                4,
851                false,
852                false,
853                false,
854                0,
855                32,
856            )),
857        );
858
859        type_info
860            .name_to_id_map
861            .insert(String::from("unsigned int"), 1);
862
863        type_info
864            .id_to_name_map
865            .insert(1, String::from("unsigned int"));
866
867        // tid:2 BTF_KIND_PTR. Make this reference the named struct type we define
868        // later on
869        type_info.id_to_type_map.insert(
870            2,
871            TypeVariant::Ptr(Ptr::create(Header::create(Kind::Ptr, 0, 0, false, 6), 6)),
872        );
873
874        // tid:3 BTF_KIND_ARRAY
875        type_info.id_to_type_map.insert(
876            3,
877            TypeVariant::Array(Array::create(
878                Header::create(Kind::Array, 0, 0, false, 0),
879                1,
880                1,
881                10,
882            )),
883        );
884
885        //
886        // BTF_KIND_STRUCT
887        //
888
889        // tid:4 Anonymous struct type
890        type_info.id_to_type_map.insert(
891            4,
892            TypeVariant::Struct(Struct::create(
893                Header::create(Kind::Struct, 0, 2, false, 8),
894                None,
895                8,
896                vec![
897                    StructMember::create(
898                        1,
899                        Some(String::from("anon_struct_value1")),
900                        1,
901                        Offset::ByteOffset(0),
902                    ),
903                    StructMember::create(
904                        1,
905                        Some(String::from("anon_struct_value2")),
906                        1,
907                        Offset::ByteOffset(32),
908                    ),
909                ],
910            )),
911        );
912
913        // tid:5 Anonymous union type
914        type_info.id_to_type_map.insert(
915            5,
916            TypeVariant::Union(Union::create(
917                Header::create(Kind::Union, 0, 2, true, 8),
918                None,
919                8,
920                vec![
921                    StructMember::create(
922                        1,
923                        Some(String::from("anon_union_value1")),
924                        1,
925                        Offset::ByteOffset(0),
926                    ),
927                    StructMember::create(
928                        1,
929                        Some(String::from("anon_union_value2")),
930                        2,
931                        Offset::ByteOffset(0),
932                    ),
933                ],
934            )),
935        );
936
937        // tid:6 Named struct type
938        type_info.id_to_type_map.insert(
939            6,
940            TypeVariant::Struct(Struct::create(
941                Header::create(Kind::Struct, 1, 4, false, 28),
942                Some(String::from("Struct")),
943                28,
944                vec![
945                    StructMember::create(0, None, 4, Offset::ByteOffset(0)),
946                    StructMember::create(0, None, 5, Offset::ByteOffset(64)),
947                    StructMember::create(
948                        1,
949                        Some(String::from("int_value")),
950                        1,
951                        Offset::ByteOffset(128),
952                    ),
953                    StructMember::create(
954                        1,
955                        Some(String::from("ptr_value")),
956                        2,
957                        Offset::ByteOffset(160),
958                    ),
959                ],
960            )),
961        );
962
963        type_info.name_to_id_map.insert(String::from("Struct"), 6);
964        type_info.id_to_name_map.insert(6, String::from("Struct"));
965
966        // tid:7 list_head struct, used internally to determine the size of a pointer
967        type_info.id_to_type_map.insert(
968            7,
969            TypeVariant::Struct(Struct::create(
970                Header::create(Kind::Struct, 1, 2, false, 16),
971                Some(String::from("list_head")),
972                16,
973                vec![
974                    StructMember::create(1, Some(String::from("next")), 2, Offset::ByteOffset(0)),
975                    StructMember::create(1, Some(String::from("prev")), 2, Offset::ByteOffset(64)),
976                ],
977            )),
978        );
979
980        type_info
981            .name_to_id_map
982            .insert(String::from("list_head"), 7);
983
984        type_info
985            .id_to_name_map
986            .insert(7, String::from("list_head"));
987
988        // tid:8 BTF_KIND_ENUM
989        type_info.id_to_type_map.insert(
990            8,
991            TypeVariant::Enum(Enum::create(
992                Header::create(Kind::Enum, 1, 2, false, 4),
993                Some(String::from("Enum32")),
994                4,
995                vec![NamedValue32 {
996                    name: String::from("Enum32Value1"),
997                    value: IntegerValue32::Unsigned(0),
998                }],
999                false,
1000            )),
1001        );
1002
1003        type_info.name_to_id_map.insert(String::from("Enum32"), 8);
1004        type_info.id_to_name_map.insert(8, String::from("Enum32"));
1005
1006        // tid:9 BTF_KIND_ENUM64
1007        type_info.id_to_type_map.insert(
1008            9,
1009            TypeVariant::Enum64(Enum64::create(
1010                Header::create(Kind::Enum64, 1, 2, false, 8),
1011                Some(String::from("Enum64")),
1012                8,
1013                vec![NamedValue64 {
1014                    name: String::from("Enum64Value1"),
1015                    value: IntegerValue64::Unsigned(0),
1016                }],
1017                false,
1018            )),
1019        );
1020
1021        type_info.name_to_id_map.insert(String::from("Enum64"), 9);
1022        type_info.id_to_name_map.insert(9, String::from("Enum64"));
1023
1024        // tid:10 BTF_KIND_FWD
1025        type_info.id_to_type_map.insert(
1026            10,
1027            TypeVariant::Fwd(Fwd::create(
1028                Header::create(Kind::Fwd, 1, 0, false, 6),
1029                Some(String::from("Fwd")),
1030                6,
1031            )),
1032        );
1033
1034        type_info
1035            .name_to_id_map
1036            .insert(String::from("StructForwardDecl"), 10);
1037        type_info
1038            .id_to_name_map
1039            .insert(10, String::from("StructForwardDecl"));
1040
1041        // tid:11 BTF_KIND_TYPEDEF
1042        type_info.id_to_type_map.insert(
1043            11,
1044            TypeVariant::Typedef(Typedef::create(
1045                Header::create(Kind::Typedef, 1, 2, false, 6),
1046                6,
1047                Some(String::from("StructAlias")),
1048            )),
1049        );
1050
1051        type_info
1052            .name_to_id_map
1053            .insert(String::from("StructAlias"), 11);
1054
1055        type_info
1056            .id_to_name_map
1057            .insert(11, String::from("StructAlias"));
1058
1059        // tid:12 BTF_KIND_VOLATILE
1060        type_info.id_to_type_map.insert(
1061            12,
1062            TypeVariant::Volatile(Volatile::create(
1063                Header::create(Kind::Volatile, 0, 0, false, 6),
1064                6,
1065            )),
1066        );
1067
1068        // tid:13 BTF_KIND_CONST
1069        type_info.id_to_type_map.insert(
1070            13,
1071            TypeVariant::Const(Const::create(
1072                Header::create(Kind::Const, 0, 0, false, 6),
1073                6,
1074            )),
1075        );
1076
1077        // tid:14 BTF_KIND_RESTRICT
1078        type_info.id_to_type_map.insert(
1079            14,
1080            TypeVariant::Restrict(Restrict::create(
1081                Header::create(Kind::Restrict, 0, 0, false, 6),
1082                6,
1083            )),
1084        );
1085
1086        // tid:15 BTF_KIND_FUNC
1087        type_info.id_to_type_map.insert(
1088            15,
1089            TypeVariant::Func(Func::create(
1090                Header::create(Kind::Func, 1, 0, false, 16),
1091                Some(String::from("func")),
1092                16,
1093            )),
1094        );
1095
1096        type_info.name_to_id_map.insert(String::from("func"), 15);
1097        type_info.id_to_name_map.insert(15, String::from("func"));
1098
1099        // tid:16 BTF_KIND_FUNC_PROTO
1100        type_info.id_to_type_map.insert(
1101            16,
1102            TypeVariant::FuncProto(FuncProto::create(
1103                Header::create(Kind::FuncProto, 0, 0, false, 1),
1104                1,
1105                vec![],
1106            )),
1107        );
1108
1109        // tid:17 BTF_KIND_VAR
1110        type_info.id_to_type_map.insert(
1111            17,
1112            TypeVariant::Var(Var::create(
1113                Header::create(Kind::Var, 1, 0, false, 1),
1114                Some(String::from("var")),
1115                1,
1116                0,
1117                LinkageType::Global,
1118            )),
1119        );
1120
1121        type_info.name_to_id_map.insert(String::from("var"), 17);
1122        type_info.id_to_name_map.insert(17, String::from("var"));
1123
1124        // tid:18 BTF_KIND_DATASEC
1125        type_info.id_to_type_map.insert(
1126            18,
1127            TypeVariant::DataSec(DataSec::create(
1128                Header::create(Kind::DataSec, 1, 3, false, 12),
1129                Some(String::from(".data")),
1130                12,
1131                vec![
1132                    DataSecVariable {
1133                        var_decl_id: 17,
1134                        offset: 0,
1135                        var_size: 4,
1136                    },
1137                    DataSecVariable {
1138                        var_decl_id: 17,
1139                        offset: 4,
1140                        var_size: 4,
1141                    },
1142                    DataSecVariable {
1143                        var_decl_id: 17,
1144                        offset: 8,
1145                        var_size: 4,
1146                    },
1147                ],
1148            )),
1149        );
1150
1151        type_info.name_to_id_map.insert(String::from(".data"), 18);
1152        type_info.id_to_name_map.insert(18, String::from(".data"));
1153
1154        // tid:19 BTF_KIND_FLOAT
1155        type_info.id_to_type_map.insert(
1156            19,
1157            TypeVariant::Float(Float::create(
1158                Header::create(Kind::Float, 1, 0, false, 8),
1159                Some(String::from("double")),
1160                8,
1161            )),
1162        );
1163
1164        type_info.name_to_id_map.insert(String::from("double"), 19);
1165        type_info.id_to_name_map.insert(19, String::from("double"));
1166
1167        // tid:20 BTF_KIND_DECL_TAG
1168        type_info.id_to_type_map.insert(
1169            20,
1170            TypeVariant::DeclTag(DeclTag::create(
1171                Header::create(Kind::DeclTag, 1, 0, false, 6),
1172                Some(String::from("decl_tag")),
1173                6,
1174                0,
1175            )),
1176        );
1177
1178        type_info
1179            .name_to_id_map
1180            .insert(String::from("decl_tag"), 20);
1181
1182        type_info
1183            .id_to_name_map
1184            .insert(20, String::from("decl_tag"));
1185
1186        // tid:21 BTF_KIND_TYPE_TAG
1187        type_info.id_to_type_map.insert(
1188            21,
1189            TypeVariant::TypeTag(TypeTag::create(
1190                Header::create(Kind::TypeTag, 1, 0, false, 11),
1191                Some(String::from("type_tag")),
1192                11,
1193            )),
1194        );
1195
1196        type_info
1197            .name_to_id_map
1198            .insert(String::from("type_tag"), 21);
1199
1200        type_info
1201            .id_to_name_map
1202            .insert(21, String::from("type_tag"));
1203
1204        // Additional nested structs/unions scenario:
1205        //
1206        // struct qstr {
1207        //   union {
1208        //     struct {
1209        //       int hash;
1210        //       int len;
1211        //     }
1212        //
1213        //     unsigned long int hash_len;
1214        //   }
1215        //
1216        //   unsigned long int name;
1217        // }
1218        //
1219        // struct dentry {
1220        //   int test1;
1221        //   struct qstr d_name;
1222        //   int test2;
1223        // }
1224
1225        type_info.id_to_type_map.insert(
1226            100,
1227            TypeVariant::Int(Int::create(
1228                Header::create(Kind::Int, 1, 0, false, 8),
1229                Some(String::from("unsigned long int")),
1230                8,
1231                false,
1232                false,
1233                false,
1234                0,
1235                64,
1236            )),
1237        );
1238
1239        type_info.id_to_type_map.insert(
1240            101,
1241            TypeVariant::Struct(Struct::create(
1242                Header::create(Kind::Struct, 0, 2, false, 8),
1243                None,
1244                8,
1245                vec![
1246                    StructMember::create(1, Some(String::from("hash")), 1, Offset::ByteOffset(0)),
1247                    StructMember::create(1, Some(String::from("len")), 1, Offset::ByteOffset(4)),
1248                ],
1249            )),
1250        );
1251
1252        type_info.id_to_type_map.insert(
1253            102,
1254            TypeVariant::Union(Union::create(
1255                Header::create(Kind::Union, 0, 2, false, 8),
1256                None,
1257                8,
1258                vec![
1259                    StructMember::create(0, None, 101, Offset::ByteOffset(0)),
1260                    StructMember::create(
1261                        1,
1262                        Some(String::from("hash_len")),
1263                        100,
1264                        Offset::ByteOffset(0),
1265                    ),
1266                ],
1267            )),
1268        );
1269
1270        type_info.id_to_type_map.insert(
1271            103,
1272            TypeVariant::Struct(Struct::create(
1273                Header::create(Kind::Struct, 1, 2, false, 8),
1274                Some(String::from("qstr")),
1275                8,
1276                vec![
1277                    StructMember::create(0, None, 102, Offset::ByteOffset(0)),
1278                    StructMember::create(1, Some(String::from("name")), 100, Offset::ByteOffset(8)),
1279                ],
1280            )),
1281        );
1282
1283        type_info.id_to_type_map.insert(
1284            104,
1285            TypeVariant::Struct(Struct::create(
1286                Header::create(Kind::Struct, 1, 3, false, 16),
1287                Some(String::from("dentry")),
1288                8,
1289                vec![
1290                    StructMember::create(1, Some(String::from("test1")), 1, Offset::ByteOffset(0)),
1291                    StructMember::create(
1292                        1,
1293                        Some(String::from("d_name")),
1294                        103,
1295                        Offset::ByteOffset(32),
1296                    ),
1297                    StructMember::create(
1298                        1,
1299                        Some(String::from("test2")),
1300                        1,
1301                        Offset::ByteOffset(256),
1302                    ),
1303                ],
1304            )),
1305        );
1306
1307        type_info
1308    }
1309
1310    #[test]
1311    fn test_size_of() {
1312        let type_info = get_test_type_info();
1313
1314        // The void type has no size
1315        assert_eq!(type_info.name_of(0).unwrap(), "void");
1316        assert!(type_info.size_of(0).unwrap_err().kind() == BTFErrorKind::NotSized);
1317
1318        // The int type has a size of 4
1319        assert_eq!(type_info.size_of(1).unwrap(), 4);
1320
1321        // The ptr size is half the size of the `list_head` struct
1322        assert_eq!(type_info.size_of(2).unwrap(), 8);
1323
1324        // The array has 10 u32 values
1325        assert_eq!(type_info.size_of(3).unwrap(), 40);
1326
1327        // The anonymous struct is 8 bytes
1328        assert_eq!(type_info.size_of(4).unwrap(), 8);
1329
1330        // The anonymous union is 8 bytes
1331        assert_eq!(type_info.size_of(5).unwrap(), 8);
1332
1333        // The named struct is 28 bytes
1334        assert_eq!(type_info.size_of(6).unwrap(), 28);
1335
1336        // The internal `list_head` struct is 16 bytes. This is used to determine the size of a ptr
1337        assert_eq!(type_info.size_of(7).unwrap(), 16);
1338
1339        // The enum is 4 bytes
1340        assert_eq!(type_info.size_of(8).unwrap(), 4);
1341
1342        // The enum64 is 8 bytes
1343        assert_eq!(type_info.size_of(9).unwrap(), 8);
1344
1345        // A forward declaration of an undefined type can't be sized
1346        assert_eq!(type_info.name_of(10).unwrap(), "StructForwardDecl");
1347        assert!(type_info.size_of(10).unwrap_err().kind() == BTFErrorKind::NotSized);
1348
1349        // The typedef references the named struct, which is 28 bytes
1350        assert_eq!(type_info.size_of(11).unwrap(), 28);
1351
1352        // The volatile references the named struct `Struct`, which is 28 bytes
1353        assert_eq!(type_info.size_of(12).unwrap(), 28);
1354
1355        // The const references the named struct `Struct`, which is 28 bytes
1356        assert_eq!(type_info.size_of(13).unwrap(), 28);
1357
1358        // The restrict references the named struct `Struct`, which is 28 bytes
1359        assert_eq!(type_info.size_of(14).unwrap(), 28);
1360
1361        // The BTF_KIND_FUNC has no size because it is not a type
1362        assert_eq!(type_info.name_of(15).unwrap(), "func");
1363        assert!(type_info.size_of(15).unwrap_err().kind() == BTFErrorKind::NotSized);
1364
1365        // The BTF_KIND_FUNC_PROTO has no size
1366        assert!(type_info.size_of(16).unwrap_err().kind() == BTFErrorKind::NotSized);
1367
1368        // The BTF_KIND_VAR variable has the size of its type (32-bit unsigned int)
1369        assert_eq!(type_info.size_of(17).unwrap(), 4);
1370
1371        // The BTF_KIND_DATASEC variable has the size of its types (3x 32-bit unsigned int)
1372        assert_eq!(type_info.size_of(18).unwrap(), 12);
1373
1374        // The BTF_KIND_FLOAT has a size of 8 bytes
1375        assert_eq!(type_info.size_of(19).unwrap(), 8);
1376
1377        // The BTF_KIND_DECL_TAG has no size
1378        assert!(type_info.size_of(20).unwrap_err().kind() == BTFErrorKind::NotSized);
1379
1380        // The BTF_KIND_TYPE_TAG has size of the type it is applied to (named struct)
1381        assert_eq!(type_info.size_of(21).unwrap(), 28);
1382    }
1383
1384    #[test]
1385    fn pointee_tid() {
1386        let type_info = get_test_type_info();
1387        assert_eq!(type_info.pointee_tid(2).unwrap(), 6);
1388    }
1389
1390    #[test]
1391    fn test_offset_of() {
1392        let type_info = get_test_type_info();
1393
1394        // void, int, ptr, enum, enum64, fwd, float
1395        let no_deref_tid_list1 = [0, 1, 2, 8, 9, 10, 19];
1396
1397        // func, func_proto, var, datasec, decl_tag, type tag
1398        let no_deref_tid_list2 = [15, 16, 17, 18, 20, 21];
1399
1400        for tid in no_deref_tid_list1.iter().chain(no_deref_tid_list2.iter()) {
1401            assert_eq!(
1402                type_info.offset_of(*tid, "test").unwrap_err().kind(),
1403                BTFErrorKind::InvalidTypePath
1404            );
1405
1406            assert_eq!(
1407                type_info.offset_of(*tid, "[0]").unwrap_err().kind(),
1408                BTFErrorKind::InvalidTypePath
1409            );
1410        }
1411
1412        // Test invalid indexes
1413        assert_eq!(
1414            type_info
1415                .offset_of(3, "[10000000000000000000000000000]")
1416                .unwrap_err()
1417                .kind(),
1418            BTFErrorKind::InvalidTypePath
1419        );
1420
1421        assert_eq!(
1422            type_info.offset_of(3, "[test]").unwrap_err().kind(),
1423            BTFErrorKind::InvalidTypePath
1424        );
1425
1426        assert_eq!(
1427            type_info.offset_of(3, "test").unwrap_err().kind(),
1428            BTFErrorKind::InvalidTypePath
1429        );
1430
1431        // Test valid indexes
1432        assert_eq!(
1433            type_info.offset_of(3, "[0]").unwrap(),
1434            (1, Offset::ByteOffset(0))
1435        );
1436
1437        assert_eq!(
1438            type_info.offset_of(3, "[1]").unwrap(),
1439            (1, Offset::ByteOffset(4))
1440        );
1441
1442        assert_eq!(
1443            type_info.offset_of(3, "[2]").unwrap(),
1444            (1, Offset::ByteOffset(8))
1445        );
1446
1447        assert_eq!(
1448            type_info.offset_of(3, "[3]").unwrap(),
1449            (1, Offset::ByteOffset(12))
1450        );
1451
1452        assert_eq!(
1453            type_info.offset_of(3, "[4]").unwrap(),
1454            (1, Offset::ByteOffset(16))
1455        );
1456
1457        assert_eq!(
1458            type_info.offset_of(3, "[5]").unwrap(),
1459            (1, Offset::ByteOffset(20))
1460        );
1461
1462        assert_eq!(
1463            type_info.offset_of(3, "[6]").unwrap(),
1464            (1, Offset::ByteOffset(24))
1465        );
1466
1467        assert_eq!(
1468            type_info.offset_of(3, "[7]").unwrap(),
1469            (1, Offset::ByteOffset(28))
1470        );
1471
1472        assert_eq!(
1473            type_info.offset_of(3, "[8]").unwrap(),
1474            (1, Offset::ByteOffset(32))
1475        );
1476
1477        assert_eq!(
1478            type_info.offset_of(3, "[9]").unwrap(),
1479            (1, Offset::ByteOffset(36))
1480        );
1481
1482        assert_eq!(
1483            type_info.offset_of(3, "[10]").unwrap_err().kind(),
1484            BTFErrorKind::InvalidTypePath
1485        );
1486
1487        // Named struct and the typedef/const/volatile/restrict that reference it
1488        for struct_tid in [6, 11, 12, 13, 14] {
1489            let int_value_offset = type_info.offset_of(struct_tid, "int_value").unwrap();
1490            assert_eq!(int_value_offset, (1, Offset::ByteOffset(16 * 8)));
1491
1492            let ptr_value_offset = type_info.offset_of(struct_tid, "ptr_value").unwrap();
1493
1494            assert_eq!(ptr_value_offset, (2, Offset::ByteOffset(20 * 8)));
1495
1496            let anon_struct_value1_offset = type_info
1497                .offset_of(struct_tid, "anon_struct_value1")
1498                .unwrap();
1499
1500            assert_eq!(anon_struct_value1_offset, (1, Offset::ByteOffset(0)));
1501
1502            let anon_struct_value2_offset = type_info
1503                .offset_of(struct_tid, "anon_struct_value2")
1504                .unwrap();
1505
1506            assert_eq!(anon_struct_value2_offset, (1, Offset::ByteOffset(4 * 8)));
1507
1508            let anon_union_value1_offset = type_info
1509                .offset_of(struct_tid, "anon_union_value1")
1510                .unwrap();
1511
1512            assert_eq!(anon_union_value1_offset, (1, Offset::ByteOffset(8 * 8)));
1513
1514            let anon_union_value2_offset = type_info
1515                .offset_of(struct_tid, "anon_union_value2")
1516                .unwrap();
1517
1518            assert_eq!(anon_union_value2_offset, (2, Offset::ByteOffset(8 * 8)));
1519        }
1520
1521        assert_eq!(
1522            type_info.offset_of(104, "test1").unwrap(),
1523            (1, Offset::ByteOffset(0))
1524        );
1525
1526        assert_eq!(
1527            type_info.offset_of(104, "d_name").unwrap(),
1528            (103, Offset::ByteOffset(32))
1529        );
1530
1531        assert_eq!(
1532            type_info.offset_of(104, "test2").unwrap(),
1533            (1, Offset::ByteOffset(256))
1534        );
1535
1536        assert_eq!(
1537            type_info.offset_of(104, "d_name.hash").unwrap(),
1538            (1, Offset::ByteOffset(32))
1539        );
1540
1541        assert_eq!(
1542            type_info.offset_of(104, "d_name.len").unwrap(),
1543            (1, Offset::ByteOffset(36))
1544        );
1545
1546        assert_eq!(
1547            type_info.offset_of(104, "d_name.hash_len").unwrap(),
1548            (100, Offset::ByteOffset(32))
1549        );
1550
1551        assert_eq!(
1552            type_info.offset_of(104, "d_name.name").unwrap(),
1553            (100, Offset::ByteOffset(40))
1554        );
1555    }
1556
1557    #[test]
1558    fn test_type_section_offset_overflow() {
1559        // Test overflow when calculating type section start (hdr_len + type_off)
1560        let readable_buffer = ReadableBuffer::new(&[
1561            //
1562            // BTF header
1563            //
1564            0x9F, 0xEB, // magic
1565            0x01, // version
1566            0x00, // flags
1567            0xFF, 0xFF, 0xFF, 0x7F, // hdr_len
1568            0xFF, 0xFF, 0xFF, 0x7F, // type_off
1569            0x01, 0x00, 0x00, 0x00, // type_len
1570            0x00, 0x00, 0x00, 0x00, // str_off
1571            0x01, 0x00, 0x00, 0x00, // str_len
1572        ]);
1573
1574        let result = TypeInformation::new(&readable_buffer);
1575        assert!(result.is_err());
1576        if let Err(err) = result {
1577            assert_eq!(err.kind(), BTFErrorKind::InvalidTypeSectionOffset);
1578        }
1579    }
1580
1581    #[test]
1582    fn test_type_section_end_overflow() {
1583        // Test overflow when calculating type section end (start + type_len)
1584        let readable_buffer = ReadableBuffer::new(&[
1585            //
1586            // BTF header
1587            //
1588            0x9F, 0xEB, // magic
1589            0x01, // version
1590            0x00, // flags
1591            0x18, 0x00, 0x00, 0x00, // hdr_len
1592            0x00, 0x00, 0x00, 0xFF, // type_off
1593            0xFF, 0xFF, 0xFF, 0x01, // type_len
1594            0x00, 0x00, 0x00, 0x00, // str_off
1595            0x01, 0x00, 0x00, 0x00, // str_len
1596        ]);
1597
1598        let result = TypeInformation::new(&readable_buffer);
1599        assert!(result.is_err());
1600        if let Err(err) = result {
1601            assert_eq!(err.kind(), BTFErrorKind::InvalidTypeSectionOffset);
1602        }
1603    }
1604
1605    #[test]
1606    fn test_array_element_offset_overflow() {
1607        // Create an array with very large element size that will overflow
1608        // when calculating offset at high indexes
1609        let mut type_info = get_test_type_info();
1610
1611        // tid:200 - Large int type (size close to u32::MAX / small divisor)
1612        type_info.id_to_type_map.insert(
1613            200,
1614            TypeVariant::Int(Int::create(
1615                Header::create(Kind::Int, 1, 0, false, 0x10000000),
1616                Some(String::from("huge_int")),
1617                0x10000000,
1618                false,
1619                false,
1620                false,
1621                0,
1622                32,
1623            )),
1624        );
1625
1626        // tid:201 - Array of 100 huge_ints
1627        type_info.id_to_type_map.insert(
1628            201,
1629            TypeVariant::Array(Array::create(
1630                Header::create(Kind::Array, 0, 0, false, 0),
1631                200, // element type
1632                200, // index type
1633                100, // element count
1634            )),
1635        );
1636
1637        let result = type_info.offset_of(201, "[20]");
1638        assert!(result.is_err());
1639        assert_eq!(result.unwrap_err().kind(), BTFErrorKind::InvalidTypePath);
1640
1641        let result = type_info.offset_of(201, "[0]");
1642        assert!(result.is_ok());
1643    }
1644}