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 
15 pub mod types;
16 
17 use std::ffi::CStr;
18 use std::ffi::CString;
19 use std::ffi::OsStr;
20 use std::fmt;
21 use std::fmt::Debug;
22 use std::fmt::Display;
23 use std::fmt::Formatter;
24 use std::fmt::Result as FmtResult;
25 use std::io;
26 use std::marker::PhantomData;
27 use std::mem::size_of;
28 use std::num::NonZeroUsize;
29 use std::ops::Deref;
30 use std::os::raw::c_ulong;
31 use std::os::raw::c_void;
32 use std::os::unix::prelude::AsRawFd;
33 use std::os::unix::prelude::FromRawFd;
34 use std::os::unix::prelude::OsStrExt;
35 use std::os::unix::prelude::OwnedFd;
36 use std::path::Path;
37 use std::ptr;
38 use std::ptr::NonNull;
39 
40 use crate::util::parse_ret_i32;
41 use crate::util::validate_bpf_ret;
42 use crate::AsRawLibbpf;
43 use crate::Error;
44 use crate::ErrorExt as _;
45 use crate::Result;
46 
47 use self::types::Composite;
48 
49 /// The various btf types.
50 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
51 #[repr(u32)]
52 pub 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 
95 impl TryFrom<u32> for BtfKind {
96     type Error = u32;
97 
try_from(value: u32) -> Result<Self, Self::Error>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)]
129 pub struct TypeId(u32);
130 
131 impl From<u32> for TypeId {
from(s: u32) -> Self132     fn from(s: u32) -> Self {
133         Self(s)
134     }
135 }
136 
137 impl From<TypeId> for u32 {
from(t: TypeId) -> Self138     fn from(t: TypeId) -> Self {
139         t.0
140     }
141 }
142 
143 impl Display for TypeId {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result144     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145         write!(f, "{}", self.0)
146     }
147 }
148 
149 #[derive(Debug)]
150 enum 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.
162 pub struct Btf<'source> {
163     ptr: NonNull<libbpf_sys::btf>,
164     drop_policy: DropPolicy,
165     _marker: PhantomData<&'source ()>,
166 }
167 
168 impl Btf<'static> {
169     /// Load the btf information from specified path.
from_path<P: AsRef<Path>>(path: P) -> Result<Self>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.
from_vmlinux() -> Result<Self>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.
from_prog_id(id: u32) -> Result<Self>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 
225 impl<'btf> Btf<'btf> {
226     /// Create a new `Btf` instance from the given [`libbpf_sys::bpf_object`].
from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>>227     pub fn from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>> {
228         Self::from_bpf_object_raw(obj)
229     }
230 
from_bpf_object_raw(obj: *const libbpf_sys::bpf_object) -> Result<Option<Self>>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.
from_raw(name: &'btf str, object_file: &'btf [u8]) -> Result<Option<Self>>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.
name_at(&self, offset: u32) -> Option<&OsStr>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.
is_empty(&self) -> bool311     pub fn is_empty(&self) -> bool {
312         self.len() == 0
313     }
314 
315     /// The number of [BtfType]s in this object.
len(&self) -> usize316     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.
ptr_size(&self) -> Result<NonZeroUsize>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.
type_by_name<'s, K>(&'s self, name: &str) -> Option<K> where K: TryFrom<BtfType<'s>>,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].
type_by_id<'s, K>(&'s self, type_id: TypeId) -> Option<K> where K: TryFrom<BtfType<'s>>,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.
type_by_kind<'s, K>(&'s self) -> impl Iterator<Item = K> + 's where K: TryFrom<BtfType<'s>>,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 
395 impl AsRawLibbpf for Btf<'_> {
396     type LibbpfType = libbpf_sys::btf;
397 
398     /// Retrieve the underlying [`libbpf_sys::btf`] object.
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>399     fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
400         self.ptr
401     }
402 }
403 
404 impl Debug for Btf<'_> {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult405     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 
428 impl Drop for Btf<'_> {
drop(&mut self)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)]
456 pub 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 
470 impl Debug for BtfType<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result471     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 
481 impl<'btf> BtfType<'btf> {
482     /// This type's type id.
483     #[inline]
type_id(&self) -> TypeId484     pub fn type_id(&self) -> TypeId {
485         self.type_id
486     }
487 
488     /// This type's name.
489     #[inline]
name(&'_ self) -> Option<&'btf OsStr>490     pub fn name(&'_ self) -> Option<&'btf OsStr> {
491         self.name
492     }
493 
494     /// This type's kind.
495     #[inline]
kind(&self) -> BtfKind496     pub fn kind(&self) -> BtfKind {
497         ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
498     }
499 
500     #[inline]
vlen(&self) -> u32501     fn vlen(&self) -> u32 {
502         self.ty.info & 0xffff
503     }
504 
505     #[inline]
kind_flag(&self) -> bool506     fn kind_flag(&self) -> bool {
507         (self.ty.info >> 31) == 1
508     }
509 
510     /// Whether this represent's a modifier.
511     #[inline]
is_mod(&self) -> bool512     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]
is_any_enum(&self) -> bool521     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]
is_core_compat(&self, other: &Self) -> bool527     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]
is_composite(&self) -> bool533     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]
size_unchecked(&self) -> u32550     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]
referenced_type_id_unchecked(&self) -> TypeId569     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.
next_type(&self) -> Option<Self>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).
skip_mods_and_typedefs(&self) -> Self610     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).
alignment(&self) -> Result<NonZeroUsize>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.
702 pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
703     /// The size of the described type.
704     #[inline]
size(&self) -> usize705     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.
718 pub unsafe trait ReferencesType<'btf>:
719     Deref<Target = BtfType<'btf>> + sealed::Sealed
720 {
721     /// The referenced type's id.
722     #[inline]
referenced_type_id(&self) -> TypeId723     fn referenced_type_id(&self) -> TypeId {
724         unsafe { self.referenced_type_id_unchecked() }
725     }
726 
727     /// The referenced type.
728     #[inline]
referenced_type(&self) -> BtfType<'btf>729     fn referenced_type(&self) -> BtfType<'btf> {
730         self.source.type_by_id(self.referenced_type_id()).unwrap()
731     }
732 }
733 
734 mod sealed {
735     pub trait Sealed {}
736 }
737 
738 #[cfg(test)]
739 mod tests {
740     use super::*;
741 
742     use std::mem::discriminant;
743 
744     #[test]
from_vmlinux()745     fn from_vmlinux() {
746         assert!(Btf::from_vmlinux().is_ok());
747     }
748 
749     #[test]
btf_kind()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 }
765