libbpf_rs_lightswitch/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::create_bpf_entity_checked;
41use crate::util::create_bpf_entity_checked_opt;
42use crate::util::parse_ret_i32;
43use crate::AsRawLibbpf;
44use crate::Error;
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 = create_bpf_entity_checked(|| unsafe {
176                libbpf_sys::btf__parse(path.as_ptr(), ptr::null_mut())
177            })?;
178            Ok(Btf {
179                ptr,
180                drop_policy: DropPolicy::SelfPtrOnly,
181                _marker: PhantomData,
182            })
183        }
184        inner(path.as_ref())
185    }
186
187    /// Load the vmlinux btf information from few well-known locations.
188    pub fn from_vmlinux() -> Result<Self> {
189        let ptr = create_bpf_entity_checked(|| unsafe { libbpf_sys::btf__load_vmlinux_btf() })?;
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 = create_bpf_entity_checked(|| unsafe {
215            libbpf_sys::btf__load_from_kernel_by_id(info.btf_id)
216        })?;
217
218        Ok(Self {
219            ptr,
220            drop_policy: DropPolicy::SelfPtrOnly,
221            _marker: PhantomData,
222        })
223    }
224}
225
226impl<'btf> Btf<'btf> {
227    /// Create a new `Btf` instance from the given [`libbpf_sys::bpf_object`].
228    pub fn from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>> {
229        Self::from_bpf_object_raw(obj)
230    }
231
232    fn from_bpf_object_raw(obj: *const libbpf_sys::bpf_object) -> Result<Option<Self>> {
233        create_bpf_entity_checked_opt(|| unsafe {
234            // SAFETY: the obj pointer is valid since it's behind a reference.
235            libbpf_sys::bpf_object__btf(obj)
236        })
237        .map(|opt| {
238            opt.map(|ptr| Self {
239                ptr,
240                drop_policy: DropPolicy::Nothing,
241                _marker: PhantomData,
242            })
243        })
244    }
245
246    /// From raw bytes coming from an object file.
247    pub fn from_raw(name: &'btf str, object_file: &'btf [u8]) -> Result<Option<Self>> {
248        let cname = CString::new(name)
249            .map_err(|_| Error::with_invalid_data(format!("invalid path {name:?}, has null bytes")))
250            .unwrap();
251
252        let obj_opts = libbpf_sys::bpf_object_open_opts {
253            sz: size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
254            object_name: cname.as_ptr(),
255            ..Default::default()
256        };
257
258        let mut bpf_obj = create_bpf_entity_checked(|| unsafe {
259            libbpf_sys::bpf_object__open_mem(
260                object_file.as_ptr() as *const c_void,
261                object_file.len() as c_ulong,
262                &obj_opts,
263            )
264        })?;
265
266        let bpf_obj = unsafe { bpf_obj.as_mut() };
267        match Self::from_bpf_object_raw(bpf_obj) {
268            Ok(Some(this)) => Ok(Some(Self {
269                drop_policy: DropPolicy::ObjPtr(bpf_obj),
270                ..this
271            })),
272            x => {
273                unsafe {
274                    // SAFETY:
275                    // The obj pointer is valid, create_bpf_entity_checked has checked it.
276                    //
277                    // We free it here, otherwise it will be a memory leak as this codepath
278                    // (Ok(None) | Err(e)) does not reference it anymore and as such it can be
279                    // dropped.
280                    libbpf_sys::bpf_object__close(bpf_obj)
281                };
282                x
283            }
284        }
285    }
286
287    /// Gets a string at a given offset.
288    ///
289    /// Returns [`None`] when the offset is out of bounds or if the name is empty.
290    fn name_at(&self, offset: u32) -> Option<&OsStr> {
291        let name = unsafe {
292            // SAFETY:
293            // Assuming that btf is a valid pointer, this is always okay to call.
294            libbpf_sys::btf__name_by_offset(self.ptr.as_ptr(), offset)
295        };
296        NonNull::new(name as *mut _)
297            .map(|p| unsafe {
298                // SAFETY: a non-null pointer coming from libbpf is always valid
299                OsStr::from_bytes(CStr::from_ptr(p.as_ptr()).to_bytes())
300            })
301            .filter(|s| !s.is_empty()) // treat empty strings as none
302    }
303
304    /// Whether this btf instance has no types.
305    pub fn is_empty(&self) -> bool {
306        self.len() == 0
307    }
308
309    /// The number of [BtfType]s in this object.
310    pub fn len(&self) -> usize {
311        unsafe {
312            // SAFETY: the btf pointer is valid.
313            libbpf_sys::btf__type_cnt(self.ptr.as_ptr()) as usize
314        }
315    }
316
317    /// The btf pointer size.
318    pub fn ptr_size(&self) -> Result<NonZeroUsize> {
319        let sz = unsafe { libbpf_sys::btf__pointer_size(self.ptr.as_ptr()) as usize };
320        NonZeroUsize::new(sz).ok_or_else(|| {
321            Error::with_io_error(io::ErrorKind::Other, "could not determine pointer size")
322        })
323    }
324
325    /// Find a btf type by name
326    ///
327    /// # Panics
328    /// If `name` has null bytes.
329    pub fn type_by_name<'s, K>(&'s self, name: &str) -> Option<K>
330    where
331        K: TryFrom<BtfType<'s>>,
332    {
333        let c_string = CString::new(name)
334            .map_err(|_| Error::with_invalid_data(format!("{name:?} contains null bytes")))
335            .unwrap();
336        let ty = unsafe {
337            // SAFETY: the btf pointer is valid and the c_string pointer was created from safe code
338            // therefore it's also valid.
339            libbpf_sys::btf__find_by_name(self.ptr.as_ptr(), c_string.as_ptr())
340        };
341        if ty < 0 {
342            None
343        } else {
344            self.type_by_id(TypeId(ty as _))
345        }
346    }
347
348    /// Find a type by it's [TypeId].
349    pub fn type_by_id<'s, K>(&'s self, type_id: TypeId) -> Option<K>
350    where
351        K: TryFrom<BtfType<'s>>,
352    {
353        let btf_type = unsafe {
354            // SAFETY: the btf pointer is valid.
355            libbpf_sys::btf__type_by_id(self.ptr.as_ptr(), type_id.0)
356        };
357
358        let btf_type = NonNull::new(btf_type as *mut libbpf_sys::btf_type)?;
359
360        let ty = unsafe {
361            // SAFETY: if it is non-null then it points to a valid type.
362            btf_type.as_ref()
363        };
364
365        let name = self.name_at(ty.name_off);
366
367        BtfType {
368            type_id,
369            name,
370            source: self,
371            ty,
372        }
373        .try_into()
374        .ok()
375    }
376
377    /// Find all types of a specific type kind.
378    pub fn type_by_kind<'s, K>(&'s self) -> impl Iterator<Item = K> + 's
379    where
380        K: TryFrom<BtfType<'s>>,
381    {
382        (1..self.len() as u32)
383            .map(TypeId::from)
384            .filter_map(|id| self.type_by_id(id))
385            .filter_map(|t| K::try_from(t).ok())
386    }
387}
388
389impl AsRawLibbpf for Btf<'_> {
390    type LibbpfType = libbpf_sys::btf;
391
392    /// Retrieve the underlying [`libbpf_sys::btf`] object.
393    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
394        self.ptr
395    }
396}
397
398impl Debug for Btf<'_> {
399    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
400        struct BtfDumper<'btf>(&'btf Btf<'btf>);
401
402        impl Debug for BtfDumper<'_> {
403            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
404                f.debug_list()
405                    .entries(
406                        (0..self.0.len())
407                            .map(|i| TypeId::from(i as u32))
408                            // SANITY: A type with this ID should always exist
409                            //         given that BTF IDs are fully populated up
410                            //         to `len`. Conversion to `BtfType` is
411                            //         always infallible.
412                            .map(|id| self.0.type_by_id::<BtfType<'_>>(id).unwrap()),
413                    )
414                    .finish()
415            }
416        }
417
418        f.debug_tuple("Btf<'_>").field(&BtfDumper(self)).finish()
419    }
420}
421
422impl Drop for Btf<'_> {
423    fn drop(&mut self) {
424        match self.drop_policy {
425            DropPolicy::Nothing => {}
426            DropPolicy::SelfPtrOnly => {
427                unsafe {
428                    // SAFETY: the btf pointer is valid.
429                    libbpf_sys::btf__free(self.ptr.as_ptr())
430                }
431            }
432            DropPolicy::ObjPtr(obj) => {
433                unsafe {
434                    // SAFETY: the bpf obj pointer is valid.
435                    // closing the obj automatically frees the associated btf object.
436                    libbpf_sys::bpf_object__close(obj)
437                }
438            }
439        }
440    }
441}
442
443/// An undiscriminated btf type
444///
445/// The [`btf_type_match`](crate::btf_type_match) can be used to match on the variants of this type
446/// as if it was a rust enum.
447///
448/// You can also use the [`TryFrom`] trait to convert to any of the possible [`types`].
449#[derive(Clone, Copy)]
450pub struct BtfType<'btf> {
451    type_id: TypeId,
452    name: Option<&'btf OsStr>,
453    source: &'btf Btf<'btf>,
454    ///  the __bindgen_anon_1 field is a union defined as
455    ///  ```no_run
456    ///  union btf_type__bindgen_ty_1 {
457    ///      size_: u32,
458    ///      type_: u32,
459    ///  }
460    ///  ```
461    ///
462    ty: &'btf libbpf_sys::btf_type,
463}
464
465impl Debug for BtfType<'_> {
466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        f.debug_struct("BtfType")
468            .field("type_id", &self.type_id)
469            .field("name", &self.name())
470            .field("source", &self.source.as_libbpf_object())
471            .field("ty", &(self.ty as *const _))
472            .finish()
473    }
474}
475
476impl<'btf> BtfType<'btf> {
477    /// This type's type id.
478    #[inline]
479    pub fn type_id(&self) -> TypeId {
480        self.type_id
481    }
482
483    /// This type's name.
484    #[inline]
485    pub fn name(&'_ self) -> Option<&'btf OsStr> {
486        self.name
487    }
488
489    /// This type's kind.
490    #[inline]
491    pub fn kind(&self) -> BtfKind {
492        ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
493    }
494
495    #[inline]
496    fn vlen(&self) -> u32 {
497        self.ty.info & 0xffff
498    }
499
500    #[inline]
501    fn kind_flag(&self) -> bool {
502        (self.ty.info >> 31) == 1
503    }
504
505    /// Whether this represent's a modifier.
506    #[inline]
507    pub fn is_mod(&self) -> bool {
508        matches!(
509            self.kind(),
510            BtfKind::Volatile | BtfKind::Const | BtfKind::Restrict | BtfKind::TypeTag
511        )
512    }
513
514    /// Whether this represents any kind of enum.
515    #[inline]
516    pub fn is_any_enum(&self) -> bool {
517        matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
518    }
519
520    /// Whether this btf type is core compatible to `other`.
521    #[inline]
522    pub fn is_core_compat(&self, other: &Self) -> bool {
523        self.kind() == other.kind() || (self.is_any_enum() && other.is_any_enum())
524    }
525
526    /// Whether this type represents a composite type (struct/union).
527    #[inline]
528    pub fn is_composite(&self) -> bool {
529        matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
530    }
531
532    /// The size of the described type.
533    ///
534    /// # Safety
535    ///
536    /// This function can only be called when the [`Self::kind`] returns one of:
537    ///   - [`BtfKind::Int`],
538    ///   - [`BtfKind::Float`],
539    ///   - [`BtfKind::Enum`],
540    ///   - [`BtfKind::Struct`],
541    ///   - [`BtfKind::Union`],
542    ///   - [`BtfKind::DataSec`],
543    ///   - [`BtfKind::Enum64`],
544    #[inline]
545    unsafe fn size_unchecked(&self) -> u32 {
546        unsafe { self.ty.__bindgen_anon_1.size }
547    }
548
549    /// The [`TypeId`] of the referenced type.
550    ///
551    /// # Safety
552    /// This function can only be called when the [`Self::kind`] returns one of:
553    ///     - [`BtfKind::Ptr`],
554    ///     - [`BtfKind::Typedef`],
555    ///     - [`BtfKind::Volatile`],
556    ///     - [`BtfKind::Const`],
557    ///     - [`BtfKind::Restrict`],
558    ///     - [`BtfKind::Func`],
559    ///     - [`BtfKind::FuncProto`],
560    ///     - [`BtfKind::Var`],
561    ///     - [`BtfKind::DeclTag`],
562    ///     - [`BtfKind::TypeTag`],
563    #[inline]
564    unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
565        unsafe { self.ty.__bindgen_anon_1.type_ }.into()
566    }
567
568    /// If this type implements [`ReferencesType`], returns the type it references.
569    pub fn next_type(&self) -> Option<Self> {
570        match self.kind() {
571            BtfKind::Ptr
572            | BtfKind::Typedef
573            | BtfKind::Volatile
574            | BtfKind::Const
575            | BtfKind::Restrict
576            | BtfKind::Func
577            | BtfKind::FuncProto
578            | BtfKind::Var
579            | BtfKind::DeclTag
580            | BtfKind::TypeTag => {
581                let tid = unsafe {
582                    // SAFETY: we checked the kind
583                    self.referenced_type_id_unchecked()
584                };
585                self.source.type_by_id(tid)
586            }
587
588            BtfKind::Void
589            | BtfKind::Int
590            | BtfKind::Array
591            | BtfKind::Struct
592            | BtfKind::Union
593            | BtfKind::Enum
594            | BtfKind::Fwd
595            | BtfKind::DataSec
596            | BtfKind::Float
597            | BtfKind::Enum64 => None,
598        }
599    }
600
601    /// Given a type, follows the refering type ids until it finds a type that isn't a modifier or
602    /// a [`BtfKind::Typedef`].
603    ///
604    /// See [is_mod](Self::is_mod).
605    pub fn skip_mods_and_typedefs(&self) -> Self {
606        let mut ty = *self;
607        loop {
608            if ty.is_mod() || ty.kind() == BtfKind::Typedef {
609                ty = ty.next_type().unwrap();
610            } else {
611                return ty;
612            }
613        }
614    }
615
616    /// Returns the alignment of this type, if this type points to some modifier or typedef, those
617    /// will be skipped until the underlying type (with an alignment) is found.
618    ///
619    /// See [skip_mods_and_typedefs](Self::skip_mods_and_typedefs).
620    pub fn alignment(&self) -> Result<NonZeroUsize> {
621        let skipped = self.skip_mods_and_typedefs();
622        match skipped.kind() {
623            BtfKind::Int => {
624                let ptr_size = skipped.source.ptr_size()?;
625                let int = types::Int::try_from(skipped).unwrap();
626                Ok(Ord::min(
627                    ptr_size,
628                    NonZeroUsize::new(((int.bits + 7) / 8).into()).unwrap(),
629                ))
630            }
631            BtfKind::Ptr => skipped.source.ptr_size(),
632            BtfKind::Array => types::Array::try_from(skipped)
633                .unwrap()
634                .contained_type()
635                .alignment(),
636            BtfKind::Struct | BtfKind::Union => {
637                let c = Composite::try_from(skipped).unwrap();
638                let mut align = NonZeroUsize::new(1usize).unwrap();
639                for m in c.iter() {
640                    align = Ord::max(
641                        align,
642                        skipped
643                            .source
644                            .type_by_id::<Self>(m.ty)
645                            .unwrap()
646                            .alignment()?,
647                    );
648                }
649
650                Ok(align)
651            }
652            BtfKind::Enum | BtfKind::Enum64 | BtfKind::Float => {
653                Ok(Ord::min(skipped.source.ptr_size()?, unsafe {
654                    // SAFETY: We checked the type.
655                    // Unwrap: Enums in C have always size >= 1
656                    NonZeroUsize::new_unchecked(skipped.size_unchecked() as usize)
657                }))
658            }
659            BtfKind::Var => {
660                let var = types::Var::try_from(skipped).unwrap();
661                var.source
662                    .type_by_id::<Self>(var.referenced_type_id())
663                    .unwrap()
664                    .alignment()
665            }
666            BtfKind::DataSec => unsafe {
667                // SAFETY: We checked the type.
668                NonZeroUsize::new(skipped.size_unchecked() as usize)
669            }
670            .ok_or_else(|| Error::with_invalid_data("DataSec with size of 0")),
671            BtfKind::Void
672            | BtfKind::Volatile
673            | BtfKind::Const
674            | BtfKind::Restrict
675            | BtfKind::Typedef
676            | BtfKind::FuncProto
677            | BtfKind::Fwd
678            | BtfKind::Func
679            | BtfKind::DeclTag
680            | BtfKind::TypeTag => Err(Error::with_invalid_data(format!(
681                "Cannot get alignment of type with kind {:?}. TypeId is {}",
682                skipped.kind(),
683                skipped.type_id(),
684            ))),
685        }
686    }
687}
688
689/// Some btf types have a size field, describing their size.
690///
691/// # Safety
692///
693/// It's only safe to implement this for types where the underlying btf_type has a .size set.
694///
695/// See the [docs](https://www.kernel.org/doc/html/latest/bpf/btf.html) for a reference of which
696/// [`BtfKind`] can implement this trait.
697pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
698    /// The size of the described type.
699    #[inline]
700    fn size(&self) -> usize {
701        unsafe { self.size_unchecked() as usize }
702    }
703}
704
705/// Some btf types refer to other types by their type id.
706///
707/// # Safety
708///
709/// It's only safe to implement this for types where the underlying btf_type has a .type set.
710///
711/// See the [docs](https://www.kernel.org/doc/html/latest/bpf/btf.html) for a reference of which
712/// [`BtfKind`] can implement this trait.
713pub unsafe trait ReferencesType<'btf>:
714    Deref<Target = BtfType<'btf>> + sealed::Sealed
715{
716    /// The referenced type's id.
717    #[inline]
718    fn referenced_type_id(&self) -> TypeId {
719        unsafe { self.referenced_type_id_unchecked() }
720    }
721
722    /// The referenced type.
723    #[inline]
724    fn referenced_type(&self) -> BtfType<'btf> {
725        self.source.type_by_id(self.referenced_type_id()).unwrap()
726    }
727}
728
729mod sealed {
730    pub trait Sealed {}
731}
732
733#[cfg(test)]
734mod tests {
735    use super::*;
736
737    use std::mem::discriminant;
738
739    #[test]
740    fn from_vmlinux() {
741        assert!(Btf::from_vmlinux().is_ok());
742    }
743
744    #[test]
745    fn btf_kind() {
746        use BtfKind::*;
747
748        for t in [
749            Void, Int, Ptr, Array, Struct, Union, Enum, Fwd, Typedef, Volatile, Const, Restrict,
750            Func, FuncProto, Var, DataSec, Float, DeclTag, TypeTag, Enum64,
751        ] {
752            // check if discriminants match after a roundtrip conversion
753            assert_eq!(
754                discriminant(&t),
755                discriminant(&BtfKind::try_from(t as u32).unwrap())
756            );
757        }
758    }
759}