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