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)]
52pub enum BtfKind {
53 Void = 0,
55 Int,
57 Ptr,
59 Array,
61 Struct,
63 Union,
65 Enum,
67 Fwd,
69 Typedef,
71 Volatile,
73 Const,
75 Restrict,
77 Func,
79 FuncProto,
81 Var,
83 DataSec,
85 Float,
87 DeclTag,
89 TypeTag,
91 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#[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
156pub struct Btf<'source> {
163 ptr: NonNull<libbpf_sys::btf>,
164 drop_policy: DropPolicy,
165 _marker: PhantomData<&'source ()>,
166}
167
168impl Btf<'static> {
169 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 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 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 OwnedFd::from_raw_fd(fd)
204 };
205 let mut info = libbpf_sys::bpf_prog_info::default();
206 parse_ret_i32(unsafe {
207 libbpf_sys::bpf_obj_get_info_by_fd(
208 fd.as_raw_fd(),
209 (&mut info as *mut libbpf_sys::bpf_prog_info).cast::<c_void>(),
210 &mut (size_of::<libbpf_sys::bpf_prog_info>() as u32),
211 )
212 })?;
213
214 let ptr = unsafe { libbpf_sys::btf__load_from_kernel_by_id(info.btf_id) };
215 let ptr = validate_bpf_ret(ptr).context("failed to load BTF from kernel")?;
216
217 Ok(Self {
218 ptr,
219 drop_policy: DropPolicy::SelfPtrOnly,
220 _marker: PhantomData,
221 })
222 }
223}
224
225impl<'btf> Btf<'btf> {
226 pub fn from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>> {
228 Self::from_bpf_object_raw(obj)
229 }
230
231 fn from_bpf_object_raw(obj: *const libbpf_sys::bpf_object) -> Result<Option<Self>> {
232 let ptr = unsafe {
233 libbpf_sys::bpf_object__btf(obj)
235 };
236 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 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 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 unsafe {
282 libbpf_sys::bpf_object__close(bpf_obj)
287 };
288 x
289 }
290 }
291 }
292
293 fn name_at(&self, offset: u32) -> Option<&OsStr> {
297 let name = unsafe {
298 libbpf_sys::btf__name_by_offset(self.ptr.as_ptr(), offset)
301 };
302 NonNull::new(name as *mut _)
303 .map(|p| unsafe {
304 OsStr::from_bytes(CStr::from_ptr(p.as_ptr()).to_bytes())
306 })
307 .filter(|s| !s.is_empty()) }
309
310 pub fn is_empty(&self) -> bool {
312 self.len() == 0
313 }
314
315 pub fn len(&self) -> usize {
317 unsafe {
318 libbpf_sys::btf__type_cnt(self.ptr.as_ptr()) as usize
320 }
321 }
322
323 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 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 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 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 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 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 pub fn type_by_kind<'s, K>(&'s self) -> impl Iterator<Item = K> + 's
385 where
386 K: TryFrom<BtfType<'s>>,
387 {
388 (1..self.len() as u32)
389 .map(TypeId::from)
390 .filter_map(|id| self.type_by_id(id))
391 .filter_map(|t| K::try_from(t).ok())
392 }
393}
394
395impl AsRawLibbpf for Btf<'_> {
396 type LibbpfType = libbpf_sys::btf;
397
398 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
400 self.ptr
401 }
402}
403
404impl Debug for Btf<'_> {
405 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
406 struct BtfDumper<'btf>(&'btf Btf<'btf>);
407
408 impl Debug for BtfDumper<'_> {
409 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
410 f.debug_list()
411 .entries(
412 (1..self.0.len())
413 .map(|i| TypeId::from(i as u32))
414 .map(|id| self.0.type_by_id::<BtfType<'_>>(id).unwrap()),
419 )
420 .finish()
421 }
422 }
423
424 f.debug_tuple("Btf<'_>").field(&BtfDumper(self)).finish()
425 }
426}
427
428impl Drop for Btf<'_> {
429 fn drop(&mut self) {
430 match self.drop_policy {
431 DropPolicy::Nothing => {}
432 DropPolicy::SelfPtrOnly => {
433 unsafe {
434 libbpf_sys::btf__free(self.ptr.as_ptr())
436 }
437 }
438 DropPolicy::ObjPtr(obj) => {
439 unsafe {
440 libbpf_sys::bpf_object__close(obj)
443 }
444 }
445 }
446 }
447}
448
449#[derive(Clone, Copy)]
456pub struct BtfType<'btf> {
457 type_id: TypeId,
458 name: Option<&'btf OsStr>,
459 source: &'btf Btf<'btf>,
460 ty: &'btf libbpf_sys::btf_type,
461}
462
463impl Debug for BtfType<'_> {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 f.debug_struct("BtfType")
466 .field("type_id", &self.type_id)
467 .field("name", &self.name())
468 .field("source", &self.source.as_libbpf_object())
469 .field("ty", &(self.ty as *const _))
470 .finish()
471 }
472}
473
474impl<'btf> BtfType<'btf> {
475 #[inline]
477 pub fn type_id(&self) -> TypeId {
478 self.type_id
479 }
480
481 #[inline]
483 pub fn name(&'_ self) -> Option<&'btf OsStr> {
484 self.name
485 }
486
487 #[inline]
489 pub fn kind(&self) -> BtfKind {
490 ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
491 }
492
493 #[inline]
494 fn vlen(&self) -> u32 {
495 self.ty.info & 0xffff
496 }
497
498 #[inline]
499 fn kind_flag(&self) -> bool {
500 (self.ty.info >> 31) == 1
501 }
502
503 #[inline]
505 pub fn is_mod(&self) -> bool {
506 matches!(
507 self.kind(),
508 BtfKind::Volatile | BtfKind::Const | BtfKind::Restrict | BtfKind::TypeTag
509 )
510 }
511
512 #[inline]
514 pub fn is_any_enum(&self) -> bool {
515 matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
516 }
517
518 #[inline]
520 pub fn is_core_compat(&self, other: &Self) -> bool {
521 self.kind() == other.kind() || (self.is_any_enum() && other.is_any_enum())
522 }
523
524 #[inline]
526 pub fn is_composite(&self) -> bool {
527 matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
528 }
529
530 #[inline]
543 unsafe fn size_unchecked(&self) -> u32 {
544 unsafe { self.ty.__bindgen_anon_1.size }
545 }
546
547 #[inline]
562 unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
563 unsafe { self.ty.__bindgen_anon_1.type_ }.into()
564 }
565
566 pub fn next_type(&self) -> Option<Self> {
568 match self.kind() {
569 BtfKind::Ptr
570 | BtfKind::Typedef
571 | BtfKind::Volatile
572 | BtfKind::Const
573 | BtfKind::Restrict
574 | BtfKind::Func
575 | BtfKind::FuncProto
576 | BtfKind::Var
577 | BtfKind::DeclTag
578 | BtfKind::TypeTag => {
579 let tid = unsafe {
580 self.referenced_type_id_unchecked()
582 };
583 self.source.type_by_id(tid)
584 }
585
586 BtfKind::Void
587 | BtfKind::Int
588 | BtfKind::Array
589 | BtfKind::Struct
590 | BtfKind::Union
591 | BtfKind::Enum
592 | BtfKind::Fwd
593 | BtfKind::DataSec
594 | BtfKind::Float
595 | BtfKind::Enum64 => None,
596 }
597 }
598
599 pub fn skip_mods_and_typedefs(&self) -> Self {
604 let mut ty = *self;
605 loop {
606 if ty.is_mod() || ty.kind() == BtfKind::Typedef {
607 ty = ty.next_type().unwrap();
608 } else {
609 return ty;
610 }
611 }
612 }
613
614 pub fn alignment(&self) -> Result<NonZeroUsize> {
619 let skipped = self.skip_mods_and_typedefs();
620 match skipped.kind() {
621 BtfKind::Int => {
622 let ptr_size = skipped.source.ptr_size()?;
623 let int = types::Int::try_from(skipped).unwrap();
624 Ok(Ord::min(
625 ptr_size,
626 NonZeroUsize::new(int.bits.div_ceil(8).into()).unwrap(),
627 ))
628 }
629 BtfKind::Ptr => skipped.source.ptr_size(),
630 BtfKind::Array => types::Array::try_from(skipped)
631 .unwrap()
632 .contained_type()
633 .alignment(),
634 BtfKind::Struct | BtfKind::Union => {
635 let c = Composite::try_from(skipped).unwrap();
636 let mut align = NonZeroUsize::new(1usize).unwrap();
637 for m in c.iter() {
638 align = Ord::max(
639 align,
640 skipped
641 .source
642 .type_by_id::<Self>(m.ty)
643 .unwrap()
644 .alignment()?,
645 );
646 }
647
648 Ok(align)
649 }
650 BtfKind::Enum | BtfKind::Enum64 | BtfKind::Float => {
651 Ok(Ord::min(skipped.source.ptr_size()?, unsafe {
652 NonZeroUsize::new_unchecked(skipped.size_unchecked() as usize)
655 }))
656 }
657 BtfKind::Var => {
658 let var = types::Var::try_from(skipped).unwrap();
659 var.source
660 .type_by_id::<Self>(var.referenced_type_id())
661 .unwrap()
662 .alignment()
663 }
664 BtfKind::DataSec => unsafe {
665 NonZeroUsize::new(skipped.size_unchecked() as usize)
667 }
668 .ok_or_else(|| Error::with_invalid_data("DataSec with size of 0")),
669 BtfKind::Void
670 | BtfKind::Volatile
671 | BtfKind::Const
672 | BtfKind::Restrict
673 | BtfKind::Typedef
674 | BtfKind::FuncProto
675 | BtfKind::Fwd
676 | BtfKind::Func
677 | BtfKind::DeclTag
678 | BtfKind::TypeTag => Err(Error::with_invalid_data(format!(
679 "Cannot get alignment of type with kind {:?}. TypeId is {}",
680 skipped.kind(),
681 skipped.type_id(),
682 ))),
683 }
684 }
685}
686
687pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
696 #[inline]
698 fn size(&self) -> usize {
699 unsafe { self.size_unchecked() as usize }
700 }
701}
702
703pub unsafe trait ReferencesType<'btf>:
712 Deref<Target = BtfType<'btf>> + sealed::Sealed
713{
714 #[inline]
716 fn referenced_type_id(&self) -> TypeId {
717 unsafe { self.referenced_type_id_unchecked() }
718 }
719
720 #[inline]
722 fn referenced_type(&self) -> BtfType<'btf> {
723 self.source.type_by_id(self.referenced_type_id()).unwrap()
724 }
725}
726
727mod sealed {
728 pub trait Sealed {}
729}
730
731#[cfg(test)]
732mod tests {
733 use super::*;
734
735 use std::mem::discriminant;
736
737 #[test]
738 fn from_vmlinux() {
739 assert!(Btf::from_vmlinux().is_ok());
740 }
741
742 #[test]
743 fn btf_kind() {
744 use BtfKind::*;
745
746 for t in [
747 Void, Int, Ptr, Array, Struct, Union, Enum, Fwd, Typedef, Volatile, Const, Restrict,
748 Func, FuncProto, Var, DataSec, Float, DeclTag, TypeTag, Enum64,
749 ] {
750 assert_eq!(
752 discriminant(&t),
753 discriminant(&BtfKind::try_from(t as u32).unwrap())
754 );
755 }
756 }
757}