libbpf_rs_lightswitch/btf/
mod.rs1pub 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::create_bpf_entity_checked;
41use crate::util::create_bpf_entity_checked_opt;
42use crate::util::parse_ret_i32;
43use crate::AsRawLibbpf;
44use crate::Error;
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 = create_bpf_entity_checked(|| unsafe {
176 libbpf_sys::btf__parse(path.as_ptr(), ptr::null_mut())
177 })?;
178 Ok(Btf {
179 ptr,
180 drop_policy: DropPolicy::SelfPtrOnly,
181 _marker: PhantomData,
182 })
183 }
184 inner(path.as_ref())
185 }
186
187 pub fn from_vmlinux() -> Result<Self> {
189 let ptr = create_bpf_entity_checked(|| unsafe { libbpf_sys::btf__load_vmlinux_btf() })?;
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 = create_bpf_entity_checked(|| unsafe {
215 libbpf_sys::btf__load_from_kernel_by_id(info.btf_id)
216 })?;
217
218 Ok(Self {
219 ptr,
220 drop_policy: DropPolicy::SelfPtrOnly,
221 _marker: PhantomData,
222 })
223 }
224}
225
226impl<'btf> Btf<'btf> {
227 pub fn from_bpf_object(obj: &'btf libbpf_sys::bpf_object) -> Result<Option<Self>> {
229 Self::from_bpf_object_raw(obj)
230 }
231
232 fn from_bpf_object_raw(obj: *const libbpf_sys::bpf_object) -> Result<Option<Self>> {
233 create_bpf_entity_checked_opt(|| unsafe {
234 libbpf_sys::bpf_object__btf(obj)
236 })
237 .map(|opt| {
238 opt.map(|ptr| Self {
239 ptr,
240 drop_policy: DropPolicy::Nothing,
241 _marker: PhantomData,
242 })
243 })
244 }
245
246 pub fn from_raw(name: &'btf str, object_file: &'btf [u8]) -> Result<Option<Self>> {
248 let cname = CString::new(name)
249 .map_err(|_| Error::with_invalid_data(format!("invalid path {name:?}, has null bytes")))
250 .unwrap();
251
252 let obj_opts = libbpf_sys::bpf_object_open_opts {
253 sz: size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
254 object_name: cname.as_ptr(),
255 ..Default::default()
256 };
257
258 let mut bpf_obj = create_bpf_entity_checked(|| unsafe {
259 libbpf_sys::bpf_object__open_mem(
260 object_file.as_ptr() as *const c_void,
261 object_file.len() as c_ulong,
262 &obj_opts,
263 )
264 })?;
265
266 let bpf_obj = unsafe { bpf_obj.as_mut() };
267 match Self::from_bpf_object_raw(bpf_obj) {
268 Ok(Some(this)) => Ok(Some(Self {
269 drop_policy: DropPolicy::ObjPtr(bpf_obj),
270 ..this
271 })),
272 x => {
273 unsafe {
274 libbpf_sys::bpf_object__close(bpf_obj)
281 };
282 x
283 }
284 }
285 }
286
287 fn name_at(&self, offset: u32) -> Option<&OsStr> {
291 let name = unsafe {
292 libbpf_sys::btf__name_by_offset(self.ptr.as_ptr(), offset)
295 };
296 NonNull::new(name as *mut _)
297 .map(|p| unsafe {
298 OsStr::from_bytes(CStr::from_ptr(p.as_ptr()).to_bytes())
300 })
301 .filter(|s| !s.is_empty()) }
303
304 pub fn is_empty(&self) -> bool {
306 self.len() == 0
307 }
308
309 pub fn len(&self) -> usize {
311 unsafe {
312 libbpf_sys::btf__type_cnt(self.ptr.as_ptr()) as usize
314 }
315 }
316
317 pub fn ptr_size(&self) -> Result<NonZeroUsize> {
319 let sz = unsafe { libbpf_sys::btf__pointer_size(self.ptr.as_ptr()) as usize };
320 NonZeroUsize::new(sz).ok_or_else(|| {
321 Error::with_io_error(io::ErrorKind::Other, "could not determine pointer size")
322 })
323 }
324
325 pub fn type_by_name<'s, K>(&'s self, name: &str) -> Option<K>
330 where
331 K: TryFrom<BtfType<'s>>,
332 {
333 let c_string = CString::new(name)
334 .map_err(|_| Error::with_invalid_data(format!("{name:?} contains null bytes")))
335 .unwrap();
336 let ty = unsafe {
337 libbpf_sys::btf__find_by_name(self.ptr.as_ptr(), c_string.as_ptr())
340 };
341 if ty < 0 {
342 None
343 } else {
344 self.type_by_id(TypeId(ty as _))
345 }
346 }
347
348 pub fn type_by_id<'s, K>(&'s self, type_id: TypeId) -> Option<K>
350 where
351 K: TryFrom<BtfType<'s>>,
352 {
353 let btf_type = unsafe {
354 libbpf_sys::btf__type_by_id(self.ptr.as_ptr(), type_id.0)
356 };
357
358 let btf_type = NonNull::new(btf_type as *mut libbpf_sys::btf_type)?;
359
360 let ty = unsafe {
361 btf_type.as_ref()
363 };
364
365 let name = self.name_at(ty.name_off);
366
367 BtfType {
368 type_id,
369 name,
370 source: self,
371 ty,
372 }
373 .try_into()
374 .ok()
375 }
376
377 pub fn type_by_kind<'s, K>(&'s self) -> impl Iterator<Item = K> + 's
379 where
380 K: TryFrom<BtfType<'s>>,
381 {
382 (1..self.len() as u32)
383 .map(TypeId::from)
384 .filter_map(|id| self.type_by_id(id))
385 .filter_map(|t| K::try_from(t).ok())
386 }
387}
388
389impl AsRawLibbpf for Btf<'_> {
390 type LibbpfType = libbpf_sys::btf;
391
392 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
394 self.ptr
395 }
396}
397
398impl Debug for Btf<'_> {
399 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
400 struct BtfDumper<'btf>(&'btf Btf<'btf>);
401
402 impl Debug for BtfDumper<'_> {
403 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
404 f.debug_list()
405 .entries(
406 (0..self.0.len())
407 .map(|i| TypeId::from(i as u32))
408 .map(|id| self.0.type_by_id::<BtfType<'_>>(id).unwrap()),
413 )
414 .finish()
415 }
416 }
417
418 f.debug_tuple("Btf<'_>").field(&BtfDumper(self)).finish()
419 }
420}
421
422impl Drop for Btf<'_> {
423 fn drop(&mut self) {
424 match self.drop_policy {
425 DropPolicy::Nothing => {}
426 DropPolicy::SelfPtrOnly => {
427 unsafe {
428 libbpf_sys::btf__free(self.ptr.as_ptr())
430 }
431 }
432 DropPolicy::ObjPtr(obj) => {
433 unsafe {
434 libbpf_sys::bpf_object__close(obj)
437 }
438 }
439 }
440 }
441}
442
443#[derive(Clone, Copy)]
450pub struct BtfType<'btf> {
451 type_id: TypeId,
452 name: Option<&'btf OsStr>,
453 source: &'btf Btf<'btf>,
454 ty: &'btf libbpf_sys::btf_type,
463}
464
465impl Debug for BtfType<'_> {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 f.debug_struct("BtfType")
468 .field("type_id", &self.type_id)
469 .field("name", &self.name())
470 .field("source", &self.source.as_libbpf_object())
471 .field("ty", &(self.ty as *const _))
472 .finish()
473 }
474}
475
476impl<'btf> BtfType<'btf> {
477 #[inline]
479 pub fn type_id(&self) -> TypeId {
480 self.type_id
481 }
482
483 #[inline]
485 pub fn name(&'_ self) -> Option<&'btf OsStr> {
486 self.name
487 }
488
489 #[inline]
491 pub fn kind(&self) -> BtfKind {
492 ((self.ty.info >> 24) & 0x1f).try_into().unwrap()
493 }
494
495 #[inline]
496 fn vlen(&self) -> u32 {
497 self.ty.info & 0xffff
498 }
499
500 #[inline]
501 fn kind_flag(&self) -> bool {
502 (self.ty.info >> 31) == 1
503 }
504
505 #[inline]
507 pub fn is_mod(&self) -> bool {
508 matches!(
509 self.kind(),
510 BtfKind::Volatile | BtfKind::Const | BtfKind::Restrict | BtfKind::TypeTag
511 )
512 }
513
514 #[inline]
516 pub fn is_any_enum(&self) -> bool {
517 matches!(self.kind(), BtfKind::Enum | BtfKind::Enum64)
518 }
519
520 #[inline]
522 pub fn is_core_compat(&self, other: &Self) -> bool {
523 self.kind() == other.kind() || (self.is_any_enum() && other.is_any_enum())
524 }
525
526 #[inline]
528 pub fn is_composite(&self) -> bool {
529 matches!(self.kind(), BtfKind::Struct | BtfKind::Union)
530 }
531
532 #[inline]
545 unsafe fn size_unchecked(&self) -> u32 {
546 unsafe { self.ty.__bindgen_anon_1.size }
547 }
548
549 #[inline]
564 unsafe fn referenced_type_id_unchecked(&self) -> TypeId {
565 unsafe { self.ty.__bindgen_anon_1.type_ }.into()
566 }
567
568 pub fn next_type(&self) -> Option<Self> {
570 match self.kind() {
571 BtfKind::Ptr
572 | BtfKind::Typedef
573 | BtfKind::Volatile
574 | BtfKind::Const
575 | BtfKind::Restrict
576 | BtfKind::Func
577 | BtfKind::FuncProto
578 | BtfKind::Var
579 | BtfKind::DeclTag
580 | BtfKind::TypeTag => {
581 let tid = unsafe {
582 self.referenced_type_id_unchecked()
584 };
585 self.source.type_by_id(tid)
586 }
587
588 BtfKind::Void
589 | BtfKind::Int
590 | BtfKind::Array
591 | BtfKind::Struct
592 | BtfKind::Union
593 | BtfKind::Enum
594 | BtfKind::Fwd
595 | BtfKind::DataSec
596 | BtfKind::Float
597 | BtfKind::Enum64 => None,
598 }
599 }
600
601 pub fn skip_mods_and_typedefs(&self) -> Self {
606 let mut ty = *self;
607 loop {
608 if ty.is_mod() || ty.kind() == BtfKind::Typedef {
609 ty = ty.next_type().unwrap();
610 } else {
611 return ty;
612 }
613 }
614 }
615
616 pub fn alignment(&self) -> Result<NonZeroUsize> {
621 let skipped = self.skip_mods_and_typedefs();
622 match skipped.kind() {
623 BtfKind::Int => {
624 let ptr_size = skipped.source.ptr_size()?;
625 let int = types::Int::try_from(skipped).unwrap();
626 Ok(Ord::min(
627 ptr_size,
628 NonZeroUsize::new(((int.bits + 7) / 8).into()).unwrap(),
629 ))
630 }
631 BtfKind::Ptr => skipped.source.ptr_size(),
632 BtfKind::Array => types::Array::try_from(skipped)
633 .unwrap()
634 .contained_type()
635 .alignment(),
636 BtfKind::Struct | BtfKind::Union => {
637 let c = Composite::try_from(skipped).unwrap();
638 let mut align = NonZeroUsize::new(1usize).unwrap();
639 for m in c.iter() {
640 align = Ord::max(
641 align,
642 skipped
643 .source
644 .type_by_id::<Self>(m.ty)
645 .unwrap()
646 .alignment()?,
647 );
648 }
649
650 Ok(align)
651 }
652 BtfKind::Enum | BtfKind::Enum64 | BtfKind::Float => {
653 Ok(Ord::min(skipped.source.ptr_size()?, unsafe {
654 NonZeroUsize::new_unchecked(skipped.size_unchecked() as usize)
657 }))
658 }
659 BtfKind::Var => {
660 let var = types::Var::try_from(skipped).unwrap();
661 var.source
662 .type_by_id::<Self>(var.referenced_type_id())
663 .unwrap()
664 .alignment()
665 }
666 BtfKind::DataSec => unsafe {
667 NonZeroUsize::new(skipped.size_unchecked() as usize)
669 }
670 .ok_or_else(|| Error::with_invalid_data("DataSec with size of 0")),
671 BtfKind::Void
672 | BtfKind::Volatile
673 | BtfKind::Const
674 | BtfKind::Restrict
675 | BtfKind::Typedef
676 | BtfKind::FuncProto
677 | BtfKind::Fwd
678 | BtfKind::Func
679 | BtfKind::DeclTag
680 | BtfKind::TypeTag => Err(Error::with_invalid_data(format!(
681 "Cannot get alignment of type with kind {:?}. TypeId is {}",
682 skipped.kind(),
683 skipped.type_id(),
684 ))),
685 }
686 }
687}
688
689pub unsafe trait HasSize<'btf>: Deref<Target = BtfType<'btf>> + sealed::Sealed {
698 #[inline]
700 fn size(&self) -> usize {
701 unsafe { self.size_unchecked() as usize }
702 }
703}
704
705pub unsafe trait ReferencesType<'btf>:
714 Deref<Target = BtfType<'btf>> + sealed::Sealed
715{
716 #[inline]
718 fn referenced_type_id(&self) -> TypeId {
719 unsafe { self.referenced_type_id_unchecked() }
720 }
721
722 #[inline]
724 fn referenced_type(&self) -> BtfType<'btf> {
725 self.source.type_by_id(self.referenced_type_id()).unwrap()
726 }
727}
728
729mod sealed {
730 pub trait Sealed {}
731}
732
733#[cfg(test)]
734mod tests {
735 use super::*;
736
737 use std::mem::discriminant;
738
739 #[test]
740 fn from_vmlinux() {
741 assert!(Btf::from_vmlinux().is_ok());
742 }
743
744 #[test]
745 fn btf_kind() {
746 use BtfKind::*;
747
748 for t in [
749 Void, Int, Ptr, Array, Struct, Union, Enum, Fwd, Typedef, Volatile, Const, Restrict,
750 Func, FuncProto, Var, DataSec, Float, DeclTag, TypeTag, Enum64,
751 ] {
752 assert_eq!(
754 discriminant(&t),
755 discriminant(&BtfKind::try_from(t as u32).unwrap())
756 );
757 }
758 }
759}