libbpf_rs/btf/
mod.rs

1//! Parse and introspect btf information, from files or loaded objects.
2//!
3//! To find a specific type you can use one of 3 methods
4//!
5//! - [Btf::type_by_name]
6//! - [Btf::type_by_id]
7//! - [Btf::type_by_kind]
8//!
9//! All of these are generic over `K`, which is any type that can be created from a [`BtfType`],
10//! for all of these methods, not finding any type by the passed parameter or finding a type of
11//! another [`BtfKind`] will result in a [`None`] being returned (or filtered out in the case of
12//! [`Btf::type_by_kind`]). If you want to get a type independently of the kind, just make sure `K`
13//! binds to [`BtfType`].
14
15pub mod types;
16
17use std::ffi::CStr;
18use std::ffi::CString;
19use std::ffi::OsStr;
20use std::fmt;
21use std::fmt::Debug;
22use std::fmt::Display;
23use std::fmt::Formatter;
24use std::fmt::Result as FmtResult;
25use std::io;
26use std::marker::PhantomData;
27use std::mem::size_of;
28use std::num::NonZeroUsize;
29use std::ops::Deref;
30use std::os::raw::c_ulong;
31use std::os::raw::c_void;
32use std::os::unix::prelude::AsRawFd;
33use std::os::unix::prelude::FromRawFd;
34use std::os::unix::prelude::OsStrExt;
35use std::os::unix::prelude::OwnedFd;
36use std::path::Path;
37use std::ptr;
38use std::ptr::NonNull;
39
40use crate::util::parse_ret_i32;
41use crate::util::validate_bpf_ret;
42use crate::AsRawLibbpf;
43use crate::Error;
44use crate::ErrorExt as _;
45use crate::Result;
46
47use self::types::Composite;
48
49/// The various btf types.
50#[derive(Debug, PartialEq, Eq, Clone, Copy)]
51#[repr(u32)]
52pub enum BtfKind {
53    /// [Void](types::Void)
54    Void = 0,
55    /// [Int](types::Int)
56    Int,
57    /// [Ptr](types::Ptr)
58    Ptr,
59    /// [Array](types::Array)
60    Array,
61    /// [Struct](types::Struct)
62    Struct,
63    /// [Union](types::Union)
64    Union,
65    /// [Enum](types::Enum)
66    Enum,
67    /// [Fwd](types::Fwd)
68    Fwd,
69    /// [Typedef](types::Typedef)
70    Typedef,
71    /// [Volatile](types::Volatile)
72    Volatile,
73    /// [Const](types::Const)
74    Const,
75    /// [Restrict](types::Restrict)
76    Restrict,
77    /// [Func](types::Func)
78    Func,
79    /// [FuncProto](types::FuncProto)
80    FuncProto,
81    /// [Var](types::Var)
82    Var,
83    /// [DataSec](types::DataSec)
84    DataSec,
85    /// [Float](types::Float)
86    Float,
87    /// [DeclTag](types::DeclTag)
88    DeclTag,
89    /// [TypeTag](types::TypeTag)
90    TypeTag,
91    /// [Enum64](types::Enum64)
92    Enum64,
93}
94
95impl TryFrom<u32> for BtfKind {
96    type Error = u32;
97
98    fn try_from(value: u32) -> Result<Self, Self::Error> {
99        use BtfKind::*;
100
101        Ok(match value {
102            x if x == Void as u32 => Void,
103            x if x == Int as u32 => Int,
104            x if x == Ptr as u32 => Ptr,
105            x if x == Array as u32 => Array,
106            x if x == Struct as u32 => Struct,
107            x if x == Union as u32 => Union,
108            x if x == Enum as u32 => Enum,
109            x if x == Fwd as u32 => Fwd,
110            x if x == Typedef as u32 => Typedef,
111            x if x == Volatile as u32 => Volatile,
112            x if x == Const as u32 => Const,
113            x if x == Restrict as u32 => Restrict,
114            x if x == Func as u32 => Func,
115            x if x == FuncProto as u32 => FuncProto,
116            x if x == Var as u32 => Var,
117            x if x == DataSec as u32 => DataSec,
118            x if x == Float as u32 => Float,
119            x if x == DeclTag as u32 => DeclTag,
120            x if x == TypeTag as u32 => TypeTag,
121            x if x == Enum64 as u32 => Enum64,
122            v => return Err(v),
123        })
124    }
125}
126
127/// The id of a btf type.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub struct TypeId(u32);
130
131impl From<u32> for TypeId {
132    fn from(s: u32) -> Self {
133        Self(s)
134    }
135}
136
137impl From<TypeId> for u32 {
138    fn from(t: TypeId) -> Self {
139        t.0
140    }
141}
142
143impl Display for TypeId {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}", self.0)
146    }
147}
148
149#[derive(Debug)]
150enum DropPolicy {
151    Nothing,
152    SelfPtrOnly,
153    ObjPtr(*mut libbpf_sys::bpf_object),
154}
155
156/// The btf information of a bpf object.
157///
158/// The lifetime bound protects against this object outliving its source. This can happen when it
159/// was derived from an [`Object`](super::Object), which owns the data this structs points too. When
160/// instead the [`Btf::from_path`] method is used, the lifetime will be `'static` since it doesn't
161/// borrow from anything.
162pub struct Btf<'source> {
163    ptr: NonNull<libbpf_sys::btf>,
164    drop_policy: DropPolicy,
165    _marker: PhantomData<&'source ()>,
166}
167
168impl Btf<'static> {
169    /// Load the btf information from specified path.
170    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
171        fn inner(path: &Path) -> Result<Btf<'static>> {
172            let path = CString::new(path.as_os_str().as_bytes()).map_err(|_| {
173                Error::with_invalid_data(format!("invalid path {path:?}, has null bytes"))
174            })?;
175            let ptr = unsafe { libbpf_sys::btf__parse(path.as_ptr(), ptr::null_mut()) };
176            let ptr = validate_bpf_ret(ptr).context("failed to parse BTF information")?;
177            Ok(Btf {
178                ptr,
179                drop_policy: DropPolicy::SelfPtrOnly,
180                _marker: PhantomData,
181            })
182        }
183        inner(path.as_ref())
184    }
185
186    /// Load the vmlinux btf information from few well-known locations.
187    pub fn from_vmlinux() -> Result<Self> {
188        let ptr = unsafe { libbpf_sys::btf__load_vmlinux_btf() };
189        let ptr = validate_bpf_ret(ptr).context("failed to load BTF from vmlinux")?;
190
191        Ok(Btf {
192            ptr,
193            drop_policy: DropPolicy::SelfPtrOnly,
194            _marker: PhantomData,
195        })
196    }
197
198    /// Load the btf information of an bpf object from a program id.
199    pub fn from_prog_id(id: u32) -> Result<Self> {
200        let fd = parse_ret_i32(unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) })?;
201        let fd = unsafe {
202            // SAFETY: parse_ret_i32 will check that this fd is above -1
203            OwnedFd::from_raw_fd(fd)
204        };
205        let mut info = libbpf_sys::bpf_prog_info::default();
206        parse_ret_i32(unsafe {
207            libbpf_sys::bpf_obj_get_info_by_fd(
208                fd.as_raw_fd(),
209                (&mut info as *mut libbpf_sys::bpf_prog_info).cast::<c_void>(),
210                &mut (size_of::<libbpf_sys::bpf_prog_info>() as u32),
211            )
212        })?;
213
214        let ptr = unsafe { libbpf_sys::btf__load_from_kernel_by_id(info.btf_id) };
215        let ptr = validate_bpf_ret(ptr).context("failed to load BTF from kernel")?;
216
217        Ok(Self {
218            ptr,
219            drop_policy: DropPolicy::SelfPtrOnly,
220            _marker: PhantomData,
221        })
222    }
223}
224
225impl<'btf> Btf<'btf> {
226    /// Create a new `Btf` instance from the given [`libbpf_sys::bpf_object`].
227    pub fn from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>> {
228        Self::from_bpf_object_raw(obj)
229    }
230
231    fn from_bpf_object_raw(obj: *const libbpf_sys::bpf_object) -> Result<Option<Self>> {
232        let ptr = unsafe {
233            // SAFETY: the obj pointer is valid since it's behind a reference.
234            libbpf_sys::bpf_object__btf(obj)
235        };
236        // Contrary to general `libbpf` contract, `bpf_object__btf` may
237        // return `NULL` without setting `errno`.
238        if ptr.is_null() {
239            return Ok(None)
240        }
241        let ptr = validate_bpf_ret(ptr).context("failed to create BTF from BPF object")?;
242        let slf = Self {
243            ptr,
244            drop_policy: DropPolicy::Nothing,
245            _marker: PhantomData,
246        };
247        Ok(Some(slf))
248    }
249
250    /// From raw bytes coming from an object file.
251    pub fn from_raw(name: &'btf str, object_file: &'btf [u8]) -> Result<Option<Self>> {
252        let cname = CString::new(name)
253            .map_err(|_| Error::with_invalid_data(format!("invalid path {name:?}, has null bytes")))
254            .unwrap();
255
256        let obj_opts = libbpf_sys::bpf_object_open_opts {
257            sz: size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
258            object_name: cname.as_ptr(),
259            ..Default::default()
260        };
261
262        let ptr = unsafe {
263            libbpf_sys::bpf_object__open_mem(
264                object_file.as_ptr() as *const c_void,
265                object_file.len() as c_ulong,
266                &obj_opts,
267            )
268        };
269
270        let mut bpf_obj = validate_bpf_ret(ptr).context("failed to open BPF object from memory")?;
271        // SAFETY: The pointer has been validated.
272        let bpf_obj = unsafe { bpf_obj.as_mut() };
273        match Self::from_bpf_object_raw(bpf_obj) {
274            Ok(Some(this)) => Ok(Some(Self {
275                drop_policy: DropPolicy::ObjPtr(bpf_obj),
276                ..this
277            })),
278            x => {
279                // SAFETY: The obj pointer is valid because we checked
280                //         its validity.
281                unsafe {
282                    // We free it here, otherwise it will be a memory
283                    // leak as this codepath (Ok(None) | Err(e)) does
284                    // not reference it anymore and as such it can be
285                    // dropped.
286                    libbpf_sys::bpf_object__close(bpf_obj)
287                };
288                x
289            }
290        }
291    }
292
293    /// Gets a string at a given offset.
294    ///
295    /// Returns [`None`] when the offset is out of bounds or if the name is empty.
296    fn name_at(&self, offset: u32) -> Option<&OsStr> {
297        let name = unsafe {
298            // SAFETY:
299            // Assuming that btf is a valid pointer, this is always okay to call.
300            libbpf_sys::btf__name_by_offset(self.ptr.as_ptr(), offset)
301        };
302        NonNull::new(name as *mut _)
303            .map(|p| unsafe {
304                // SAFETY: a non-null pointer coming from libbpf is always valid
305                OsStr::from_bytes(CStr::from_ptr(p.as_ptr()).to_bytes())
306            })
307            .filter(|s| !s.is_empty()) // treat empty strings as none
308    }
309
310    /// Whether this btf instance has no types.
311    pub fn is_empty(&self) -> bool {
312        self.len() == 0
313    }
314
315    /// The number of [BtfType]s in this object.
316    pub fn len(&self) -> usize {
317        unsafe {
318            // SAFETY: the btf pointer is valid.
319            libbpf_sys::btf__type_cnt(self.ptr.as_ptr()) as usize
320        }
321    }
322
323    /// The btf pointer size.
324    pub fn ptr_size(&self) -> Result<NonZeroUsize> {
325        let sz = unsafe { libbpf_sys::btf__pointer_size(self.ptr.as_ptr()) as usize };
326        NonZeroUsize::new(sz).ok_or_else(|| {
327            Error::with_io_error(io::ErrorKind::Other, "could not determine pointer size")
328        })
329    }
330
331    /// Find a btf type by name
332    ///
333    /// # Panics
334    /// If `name` has null bytes.
335    pub fn type_by_name<'s, K>(&'s self, name: &str) -> Option<K>
336    where
337        K: TryFrom<BtfType<'s>>,
338    {
339        let c_string = CString::new(name)
340            .map_err(|_| Error::with_invalid_data(format!("{name:?} contains null bytes")))
341            .unwrap();
342        let ty = unsafe {
343            // SAFETY: the btf pointer is valid and the c_string pointer was created from safe code
344            // therefore it's also valid.
345            libbpf_sys::btf__find_by_name(self.ptr.as_ptr(), c_string.as_ptr())
346        };
347        if ty < 0 {
348            None
349        } else {
350            self.type_by_id(TypeId(ty as _))
351        }
352    }
353
354    /// Find a type by it's [TypeId].
355    pub fn type_by_id<'s, K>(&'s self, type_id: TypeId) -> Option<K>
356    where
357        K: TryFrom<BtfType<'s>>,
358    {
359        let btf_type = unsafe {
360            // SAFETY: the btf pointer is valid.
361            libbpf_sys::btf__type_by_id(self.ptr.as_ptr(), type_id.0)
362        };
363
364        let btf_type = NonNull::new(btf_type as *mut libbpf_sys::btf_type)?;
365
366        let ty = unsafe {
367            // SAFETY: if it is non-null then it points to a valid type.
368            btf_type.as_ref()
369        };
370
371        let name = self.name_at(ty.name_off);
372
373        BtfType {
374            type_id,
375            name,
376            source: self,
377            ty,
378        }
379        .try_into()
380        .ok()
381    }
382
383    /// Find all types of a specific type kind.
384    pub fn type_by_kind<'s, K>(&'s self) -> impl Iterator<Item = K> + 's
385    where
386        K: TryFrom<BtfType<'s>>,
387    {
388        (1..self.len() as u32)
389            .map(TypeId::from)
390            .filter_map(|id| self.type_by_id(id))
391            .filter_map(|t| K::try_from(t).ok())
392    }
393}
394
395impl AsRawLibbpf for Btf<'_> {
396    type LibbpfType = libbpf_sys::btf;
397
398    /// Retrieve the underlying [`libbpf_sys::btf`] object.
399    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
400        self.ptr
401    }
402}
403
404impl Debug for Btf<'_> {
405    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
406        struct BtfDumper<'btf>(&'btf Btf<'btf>);
407
408        impl Debug for BtfDumper<'_> {
409            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
410                f.debug_list()
411                    .entries(
412                        (1..self.0.len())
413                            .map(|i| TypeId::from(i as u32))
414                            // SANITY: A type with this ID should always exist
415                            //         given that BTF IDs are fully populated up
416                            //         to `len`. Conversion to `BtfType` is
417                            //         always infallible.
418                            .map(|id| self.0.type_by_id::<BtfType<'_>>(id).unwrap()),
419                    )
420                    .finish()
421            }
422        }
423
424        f.debug_tuple("Btf<'_>").field(&BtfDumper(self)).finish()
425    }
426}
427
428impl Drop for Btf<'_> {
429    fn drop(&mut self) {
430        match self.drop_policy {
431            DropPolicy::Nothing => {}
432            DropPolicy::SelfPtrOnly => {
433                unsafe {
434                    // SAFETY: the btf pointer is valid.
435                    libbpf_sys::btf__free(self.ptr.as_ptr())
436                }
437            }
438            DropPolicy::ObjPtr(obj) => {
439                unsafe {
440                    // SAFETY: the bpf obj pointer is valid.
441                    // closing the obj automatically frees the associated btf object.
442                    libbpf_sys::bpf_object__close(obj)
443                }
444            }
445        }
446    }
447}
448
449/// An undiscriminated btf type
450///
451/// The [`btf_type_match`](crate::btf_type_match) can be used to match on the variants of this type
452/// as if it was a rust enum.
453///
454/// You can also use the [`TryFrom`] trait to convert to any of the possible [`types`].
455#[derive(Clone, Copy)]
456pub struct BtfType<'btf> {
457    type_id: TypeId,
458    name: Option<&'btf OsStr>,
459    source: &'btf Btf<'btf>,
460    ///  the __bindgen_anon_1 field is a union defined as
461    ///  ```no_run
462    ///  union btf_type__bindgen_ty_1 {
463    ///      size_: u32,
464    ///      type_: u32,
465    ///  }
466    ///  ```
467    ty: &'btf libbpf_sys::btf_type,
468}
469
470impl Debug for BtfType<'_> {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        f.debug_struct("BtfType")
473            .field("type_id", &self.type_id)
474            .field("name", &self.name())
475            .field("source", &self.source.as_libbpf_object())
476            .field("ty", &(self.ty as *const _))
477            .finish()
478    }
479}
480
481impl<'btf> BtfType<'btf> {
482    /// This type's type id.
483    #[inline]
484    pub fn type_id(&self) -> TypeId {
485        self.type_id
486    }
487
488    /// This type's name.
489    #[inline]
490    pub fn name(&'_ self) -> Option<&'btf OsStr> {
491        self.name
492    }
493
494    /// This type's kind.
495    #[inline]
496    pub fn kind(&self) -> BtfKind {
497        ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
498    }
499
500    #[inline]
501    fn vlen(&self) -> u32 {
502        self.ty.info & 0xffff
503    }
504
505    #[inline]
506    fn kind_flag(&self) -> bool {
507        (self.ty.info >> 31) == 1
508    }
509
510    /// Whether this represent's a modifier.
511    #[inline]
512    pub fn is_mod(&self) -> bool {
513        matches!(
514            self.kind(),
515            BtfKind::Volatile | BtfKind::Const | BtfKind::Restrict | BtfKind::TypeTag
516        )
517    }
518
519    /// Whether this represents any kind of enum.
520    #[inline]
521    pub fn is_any_enum(&self) -> bool {
522        matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
523    }
524
525    /// Whether this btf type is core compatible to `other`.
526    #[inline]
527    pub fn is_core_compat(&self, other: &Self) -> bool {
528        self.kind() == other.kind() || (self.is_any_enum() && other.is_any_enum())
529    }
530
531    /// Whether this type represents a composite type (struct/union).
532    #[inline]
533    pub fn is_composite(&self) -> bool {
534        matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
535    }
536
537    /// The size of the described type.
538    ///
539    /// # Safety
540    ///
541    /// This function can only be called when the [`Self::kind`] returns one of:
542    ///   - [`BtfKind::Int`],
543    ///   - [`BtfKind::Float`],
544    ///   - [`BtfKind::Enum`],
545    ///   - [`BtfKind::Struct`],
546    ///   - [`BtfKind::Union`],
547    ///   - [`BtfKind::DataSec`],
548    ///   - [`BtfKind::Enum64`],
549    #[inline]
550    unsafe fn size_unchecked(&self) -> u32 {
551        unsafe { self.ty.__bindgen_anon_1.size }
552    }
553
554    /// The [`TypeId`] of the referenced type.
555    ///
556    /// # Safety
557    /// This function can only be called when the [`Self::kind`] returns one of:
558    ///     - [`BtfKind::Ptr`],
559    ///     - [`BtfKind::Typedef`],
560    ///     - [`BtfKind::Volatile`],
561    ///     - [`BtfKind::Const`],
562    ///     - [`BtfKind::Restrict`],
563    ///     - [`BtfKind::Func`],
564    ///     - [`BtfKind::FuncProto`],
565    ///     - [`BtfKind::Var`],
566    ///     - [`BtfKind::DeclTag`],
567    ///     - [`BtfKind::TypeTag`],
568    #[inline]
569    unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
570        unsafe { self.ty.__bindgen_anon_1.type_ }.into()
571    }
572
573    /// If this type implements [`ReferencesType`], returns the type it references.
574    pub fn next_type(&self) -> Option<Self> {
575        match self.kind() {
576            BtfKind::Ptr
577            | BtfKind::Typedef
578            | BtfKind::Volatile
579            | BtfKind::Const
580            | BtfKind::Restrict
581            | BtfKind::Func
582            | BtfKind::FuncProto
583            | BtfKind::Var
584            | BtfKind::DeclTag
585            | BtfKind::TypeTag => {
586                let tid = unsafe {
587                    // SAFETY: we checked the kind
588                    self.referenced_type_id_unchecked()
589                };
590                self.source.type_by_id(tid)
591            }
592
593            BtfKind::Void
594            | BtfKind::Int
595            | BtfKind::Array
596            | BtfKind::Struct
597            | BtfKind::Union
598            | BtfKind::Enum
599            | BtfKind::Fwd
600            | BtfKind::DataSec
601            | BtfKind::Float
602            | BtfKind::Enum64 => None,
603        }
604    }
605
606    /// Given a type, follows the refering type ids until it finds a type that isn't a modifier or
607    /// a [`BtfKind::Typedef`].
608    ///
609    /// See [is_mod](Self::is_mod).
610    pub fn skip_mods_and_typedefs(&self) -> Self {
611        let mut ty = *self;
612        loop {
613            if ty.is_mod() || ty.kind() == BtfKind::Typedef {
614                ty = ty.next_type().unwrap();
615            } else {
616                return ty;
617            }
618        }
619    }
620
621    /// Returns the alignment of this type, if this type points to some modifier or typedef, those
622    /// will be skipped until the underlying type (with an alignment) is found.
623    ///
624    /// See [skip_mods_and_typedefs](Self::skip_mods_and_typedefs).
625    pub fn alignment(&self) -> Result<NonZeroUsize> {
626        let skipped = self.skip_mods_and_typedefs();
627        match skipped.kind() {
628            BtfKind::Int => {
629                let ptr_size = skipped.source.ptr_size()?;
630                let int = types::Int::try_from(skipped).unwrap();
631                Ok(Ord::min(
632                    ptr_size,
633                    NonZeroUsize::new(((int.bits + 7) / 8).into()).unwrap(),
634                ))
635            }
636            BtfKind::Ptr => skipped.source.ptr_size(),
637            BtfKind::Array => types::Array::try_from(skipped)
638                .unwrap()
639                .contained_type()
640                .alignment(),
641            BtfKind::Struct | BtfKind::Union => {
642                let c = Composite::try_from(skipped).unwrap();
643                let mut align = NonZeroUsize::new(1usize).unwrap();
644                for m in c.iter() {
645                    align = Ord::max(
646                        align,
647                        skipped
648                            .source
649                            .type_by_id::<Self>(m.ty)
650                            .unwrap()
651                            .alignment()?,
652                    );
653                }
654
655                Ok(align)
656            }
657            BtfKind::Enum | BtfKind::Enum64 | BtfKind::Float => {
658                Ok(Ord::min(skipped.source.ptr_size()?, unsafe {
659                    // SAFETY: We checked the type.
660                    // Unwrap: Enums in C have always size >= 1
661                    NonZeroUsize::new_unchecked(skipped.size_unchecked() as usize)
662                }))
663            }
664            BtfKind::Var => {
665                let var = types::Var::try_from(skipped).unwrap();
666                var.source
667                    .type_by_id::<Self>(var.referenced_type_id())
668                    .unwrap()
669                    .alignment()
670            }
671            BtfKind::DataSec => unsafe {
672                // SAFETY: We checked the type.
673                NonZeroUsize::new(skipped.size_unchecked() as usize)
674            }
675            .ok_or_else(|| Error::with_invalid_data("DataSec with size of 0")),
676            BtfKind::Void
677            | BtfKind::Volatile
678            | BtfKind::Const
679            | BtfKind::Restrict
680            | BtfKind::Typedef
681            | BtfKind::FuncProto
682            | BtfKind::Fwd
683            | BtfKind::Func
684            | BtfKind::DeclTag
685            | BtfKind::TypeTag => Err(Error::with_invalid_data(format!(
686                "Cannot get alignment of type with kind {:?}. TypeId is {}",
687                skipped.kind(),
688                skipped.type_id(),
689            ))),
690        }
691    }
692}
693
694/// Some btf types have a size field, describing their size.
695///
696/// # Safety
697///
698/// It's only safe to implement this for types where the underlying btf_type has a .size set.
699///
700/// See the [docs](https://www.kernel.org/doc/html/latest/bpf/btf.html) for a reference of which
701/// [`BtfKind`] can implement this trait.
702pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
703    /// The size of the described type.
704    #[inline]
705    fn size(&self) -> usize {
706        unsafe { self.size_unchecked() as usize }
707    }
708}
709
710/// Some btf types refer to other types by their type id.
711///
712/// # Safety
713///
714/// It's only safe to implement this for types where the underlying btf_type has a .type set.
715///
716/// See the [docs](https://www.kernel.org/doc/html/latest/bpf/btf.html) for a reference of which
717/// [`BtfKind`] can implement this trait.
718pub unsafe trait ReferencesType<'btf>:
719    Deref<Target = BtfType<'btf>> + sealed::Sealed
720{
721    /// The referenced type's id.
722    #[inline]
723    fn referenced_type_id(&self) -> TypeId {
724        unsafe { self.referenced_type_id_unchecked() }
725    }
726
727    /// The referenced type.
728    #[inline]
729    fn referenced_type(&self) -> BtfType<'btf> {
730        self.source.type_by_id(self.referenced_type_id()).unwrap()
731    }
732}
733
734mod sealed {
735    pub trait Sealed {}
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741
742    use std::mem::discriminant;
743
744    #[test]
745    fn from_vmlinux() {
746        assert!(Btf::from_vmlinux().is_ok());
747    }
748
749    #[test]
750    fn btf_kind() {
751        use BtfKind::*;
752
753        for t in [
754            Void, Int, Ptr, Array, Struct, Union, Enum, Fwd, Typedef, Volatile, Const, Restrict,
755            Func, FuncProto, Var, DataSec, Float, DeclTag, TypeTag, Enum64,
756        ] {
757            // check if discriminants match after a roundtrip conversion
758            assert_eq!(
759                discriminant(&t),
760                discriminant(&BtfKind::try_from(t as u32).unwrap())
761            );
762        }
763    }
764}