Skip to main content

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