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,
468}
469
470impl Debug for BtfType<'_> {
471 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
481impl<'btf> BtfType<'btf> {
482 #[inline]
484 pub fn type_id(&self) -> TypeId {
485 self.type_id
486 }
487
488 #[inline]
490 pub fn name(&'_ self) -> Option<&'btf OsStr> {
491 self.name
492 }
493
494 #[inline]
496 pub fn kind(&self) -> BtfKind {
497 ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
498 }
499
500 #[inline]
501 fn vlen(&self) -> u32 {
502 self.ty.info & 0xffff
503 }
504
505 #[inline]
506 fn kind_flag(&self) -> bool {
507 (self.ty.info >> 31) == 1
508 }
509
510 #[inline]
512 pub fn is_mod(&self) -> bool {
513 matches!(
514 self.kind(),
515 BtfKind::Volatile | BtfKind::Const | BtfKind::Restrict | BtfKind::TypeTag
516 )
517 }
518
519 #[inline]
521 pub fn is_any_enum(&self) -> bool {
522 matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
523 }
524
525 #[inline]
527 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 #[inline]
533 pub fn is_composite(&self) -> bool {
534 matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
535 }
536
537 #[inline]
550 unsafe fn size_unchecked(&self) -> u32 {
551 unsafe { self.ty.__bindgen_anon_1.size }
552 }
553
554 #[inline]
569 unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
570 unsafe { self.ty.__bindgen_anon_1.type_ }.into()
571 }
572
573 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 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 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 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 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 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
694pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
703 #[inline]
705 fn size(&self) -> usize {
706 unsafe { self.size_unchecked() as usize }
707 }
708}
709
710pub unsafe trait ReferencesType<'btf>:
719 Deref<Target = BtfType<'btf>> + sealed::Sealed
720{
721 #[inline]
723 fn referenced_type_id(&self) -> TypeId {
724 unsafe { self.referenced_type_id_unchecked() }
725 }
726
727 #[inline]
729 fn referenced_type(&self) -> BtfType<'btf> {
730 self.source.type_by_id(self.referenced_type_id()).unwrap()
731 }
732}
733
734mod sealed {
735 pub trait Sealed {}
736}
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741
742 use std::mem::discriminant;
743
744 #[test]
745 fn from_vmlinux() {
746 assert!(Btf::from_vmlinux().is_ok());
747 }
748
749 #[test]
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 assert_eq!(
759 discriminant(&t),
760 discriminant(&BtfKind::try_from(t as u32).unwrap())
761 );
762 }
763 }
764}