1pub 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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
51#[repr(u32)]
52#[doc(alias = "btf_kind")]
53pub enum BtfKind {
54 Void = 0,
56 Int,
58 Ptr,
60 Array,
62 Struct,
64 Union,
66 Enum,
68 Fwd,
70 Typedef,
72 Volatile,
74 Const,
76 Restrict,
78 Func,
80 FuncProto,
82 Var,
84 DataSec,
86 Float,
88 DeclTag,
90 TypeTag,
92 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#[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#[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 #[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 #[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 #[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 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 #[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 libbpf_sys::bpf_object__btf(obj)
241 };
242 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 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 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 unsafe {
288 libbpf_sys::bpf_object__close(bpf_obj)
293 };
294 x
295 }
296 }
297 }
298
299 fn name_at(&self, offset: u32) -> Option<&'btf OsStr> {
303 let name = unsafe {
304 libbpf_sys::btf__name_by_offset(self.ptr.as_ptr(), offset)
307 };
308 NonNull::new(name as *mut _)
309 .map(|p| unsafe {
310 OsStr::from_bytes(CStr::from_ptr(p.as_ptr()).to_bytes())
312 })
313 .filter(|s| !s.is_empty()) }
315
316 pub fn is_empty(&self) -> bool {
318 self.len() == 0
319 }
320
321 #[doc(alias = "btf__type_cnt")]
323 pub fn len(&self) -> usize {
324 unsafe {
325 libbpf_sys::btf__type_cnt(self.ptr.as_ptr()) as usize
327 }
328 }
329
330 #[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 #[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 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 #[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 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 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 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 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 .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 libbpf_sys::btf__free(self.ptr.as_ptr())
447 }
448 }
449 DropPolicy::ObjPtr(obj) => {
450 unsafe {
451 libbpf_sys::bpf_object__close(obj)
454 }
455 }
456 }
457 }
458}
459
460#[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 #[inline]
489 pub fn type_id(&self) -> TypeId {
490 self.type_id
491 }
492
493 #[inline]
495 #[doc(alias = "btf__name_by_offset")]
496 pub fn name(&'_ self) -> Option<&'btf OsStr> {
497 self.name
498 }
499
500 #[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 #[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 #[inline]
527 pub fn is_any_enum(&self) -> bool {
528 matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
529 }
530
531 #[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 #[inline]
539 pub fn is_composite(&self) -> bool {
540 matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
541 }
542
543 #[inline]
556 unsafe fn size_unchecked(&self) -> u32 {
557 unsafe { self.ty.__bindgen_anon_1.size }
558 }
559
560 #[inline]
575 unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
576 unsafe { self.ty.__bindgen_anon_1.type_ }.into()
577 }
578
579 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 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 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 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 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 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
700pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
709 #[inline]
711 fn size(&self) -> usize {
712 unsafe { self.size_unchecked() as usize }
713 }
714}
715
716pub unsafe trait ReferencesType<'btf>:
725 Deref<Target = BtfType<'btf>> + sealed::Sealed
726{
727 #[inline]
729 fn referenced_type_id(&self) -> TypeId {
730 unsafe { self.referenced_type_id_unchecked() }
731 }
732
733 #[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 assert_eq!(
765 discriminant(&t),
766 discriminant(&BtfKind::try_from(t as u32).unwrap())
767 );
768 }
769 }
770}