use std::ffi::OsStr;
use std::fmt;
use std::fmt::Display;
use std::ops::Deref;
use super::BtfKind;
use super::BtfType;
use super::HasSize;
use super::ReferencesType;
use super::TypeId;
macro_rules! gen_fieldless_concrete_type {
(
$(#[$docs:meta])*
$name:ident $(with $trait:ident)?
) => {
$(#[$docs])*
#[derive(Clone, Copy, Debug)]
pub struct $name<'btf> {
source: BtfType<'btf>,
}
impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
type Error = BtfType<'btf>;
fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
if t.kind() == BtfKind::$name {
Ok($name { source: t })
} else {
Err(t)
}
}
}
impl<'btf> ::std::ops::Deref for $name<'btf> {
type Target = BtfType<'btf>;
fn deref(&self) -> &Self::Target {
&self.source
}
}
$(
impl super::sealed::Sealed for $name<'_> {}
unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
)*
};
}
macro_rules! gen_concrete_type {
(
$(#[$docs:meta])*
$libbpf_ty:ident as $name:ident $(with $trait:ident)?
) => {
$(#[$docs])*
#[derive(Clone, Copy, Debug)]
pub struct $name<'btf> {
source: BtfType<'btf>,
ptr: &'btf libbpf_sys::$libbpf_ty,
}
impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
type Error = BtfType<'btf>;
fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
if t.kind() == BtfKind::$name {
let ptr = unsafe {
(t.ty as *const libbpf_sys::btf_type).offset(1)
};
let ptr = ptr.cast::<libbpf_sys::$libbpf_ty>();
Ok($name {
source: t,
ptr: unsafe { &*ptr },
})
} else {
Err(t)
}
}
}
impl<'btf> ::std::ops::Deref for $name<'btf> {
type Target = BtfType<'btf>;
fn deref(&self) -> &Self::Target {
&self.source
}
}
$(
impl super::sealed::Sealed for $name<'_> {}
unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
)*
};
}
macro_rules! gen_collection_members_concrete_type {
(
$libbpf_ty:ident as $name:ident $(with $trait:ident)?;
$(#[$docs:meta])*
struct $member_name:ident $(<$lt:lifetime>)? {
$(
$(#[$field_docs:meta])*
pub $field:ident : $type:ty
),* $(,)?
}
|$btf:ident, $member:ident $(, $kind_flag:ident)?| $convert:expr
) => {
impl<'btf> ::std::ops::Deref for $name<'btf> {
type Target = BtfType<'btf>;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl<'btf> $name<'btf> {
#[inline]
pub fn is_empty(&self) -> bool {
self.members.is_empty()
}
#[doc = ::core::concat!("How many members this [`", ::core::stringify!($name), "`] has")]
#[inline]
pub fn len(&self) -> usize {
self.members.len()
}
#[doc = ::core::concat!("Get a [`", ::core::stringify!($member_name), "`] at a given index")]
pub fn get(&self, index: usize) -> Option<$member_name$(<$lt>)*> {
self.members.get(index).map(|m| self.c_to_rust_member(m))
}
#[doc = ::core::concat!("Returns an iterator over the [`", ::core::stringify!($member_name), "`]'s of the [`", ::core::stringify!($name), "`]")]
pub fn iter(&'btf self) -> impl ExactSizeIterator<Item = $member_name$(<$lt>)*> + 'btf {
self.members.iter().map(|m| self.c_to_rust_member(m))
}
fn c_to_rust_member(&self, member: &libbpf_sys::$libbpf_ty) -> $member_name$(<$lt>)* {
let $btf = self.source.source;
let $member = member;
$(let $kind_flag = self.source.kind_flag();)*
$convert
}
}
$(#[$docs])*
#[derive(Clone, Copy, Debug)]
pub struct $member_name $(<$lt>)? {
$(
$(#[$field_docs])*
pub $field: $type
),*
}
$(
impl $crate::btf::sealed::Sealed for $name<'_> {}
unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
)*
};
}
macro_rules! gen_collection_concrete_type {
(
$(#[$docs:meta])*
$libbpf_ty:ident as $name:ident $(with $trait:ident)?;
$($rest:tt)+
) => {
$(#[$docs])*
#[derive(Clone, Copy, Debug)]
pub struct $name<'btf> {
source: BtfType<'btf>,
members: &'btf [libbpf_sys::$libbpf_ty],
}
impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
type Error = BtfType<'btf>;
fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
if t.kind() == BtfKind::$name {
let base_ptr = unsafe {
(t.ty as *const libbpf_sys::btf_type).offset(1)
};
let members = unsafe {
std::slice::from_raw_parts(base_ptr.cast(), t.vlen() as usize)
};
Ok(Self { source: t, members })
} else {
Err(t)
}
}
}
gen_collection_members_concrete_type!{
$libbpf_ty as $name $(with $trait)?;
$($rest)*
}
};
}
#[derive(Clone, Copy, Debug)]
pub enum MemberAttr {
Normal {
offset: u32,
},
BitField {
size: u8,
offset: u32,
},
}
impl MemberAttr {
#[inline]
fn new(kflag: bool, offset: u32) -> Self {
if kflag {
let size = (offset >> 24) as u8;
if size != 0 {
Self::BitField {
size,
offset: offset & 0x00_ff_ff_ff,
}
} else {
Self::Normal { offset }
}
} else {
Self::Normal { offset }
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Linkage {
Static = 0,
Global,
Extern,
Unknown,
}
impl From<u32> for Linkage {
fn from(value: u32) -> Self {
use Linkage::*;
match value {
x if x == Static as u32 => Static,
x if x == Global as u32 => Global,
x if x == Extern as u32 => Extern,
_ => Unknown,
}
}
}
impl From<Linkage> for u32 {
fn from(value: Linkage) -> Self {
value as Self
}
}
impl Display for Linkage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Static => "static",
Self::Global => "global",
Self::Extern => "extern",
Self::Unknown => "(unknown)",
}
)
}
}
gen_fieldless_concrete_type! {
Void
}
#[derive(Clone, Copy, Debug)]
pub struct Int<'btf> {
source: BtfType<'btf>,
pub encoding: IntEncoding,
pub offset: u8,
pub bits: u8,
}
#[derive(Clone, Copy, Debug)]
pub enum IntEncoding {
None,
Signed,
Char,
Bool,
}
impl<'btf> TryFrom<BtfType<'btf>> for Int<'btf> {
type Error = BtfType<'btf>;
fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
if t.kind() == BtfKind::Int {
let int = {
let base_ptr = t.ty as *const libbpf_sys::btf_type;
let u32_ptr = unsafe {
base_ptr.offset(1).cast::<u32>()
};
unsafe {
*u32_ptr
}
};
let encoding = match (int & 0x0f_00_00_00) >> 24 {
0b1 => IntEncoding::Signed,
0b10 => IntEncoding::Char,
0b100 => IntEncoding::Bool,
_ => IntEncoding::None,
};
Ok(Self {
source: t,
encoding,
offset: ((int & 0x00_ff_00_00) >> 24) as u8,
bits: (int & 0x00_00_00_ff) as u8,
})
} else {
Err(t)
}
}
}
impl<'btf> Deref for Int<'btf> {
type Target = BtfType<'btf>;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl super::sealed::Sealed for Int<'_> {}
unsafe impl<'btf> HasSize<'btf> for Int<'btf> {}
gen_fieldless_concrete_type! {
Ptr with ReferencesType
}
gen_concrete_type! {
btf_array as Array
}
impl<'s> Array<'s> {
#[inline]
pub fn ty(&self) -> TypeId {
self.ptr.type_.into()
}
#[inline]
pub fn index_ty(&self) -> TypeId {
self.ptr.index_type.into()
}
#[inline]
pub fn capacity(&self) -> usize {
self.ptr.nelems as usize
}
#[inline]
pub fn contained_type(&self) -> BtfType<'s> {
self.source
.source
.type_by_id(self.ty())
.expect("arrays should always reference an existing type")
}
}
gen_collection_concrete_type! {
btf_member as Struct with HasSize;
struct StructMember<'btf> {
pub name: Option<&'btf OsStr>,
pub ty: TypeId,
pub attr: MemberAttr,
}
|btf, member, kflag| StructMember {
name: btf.name_at(member.name_off),
ty: member.type_.into(),
attr: MemberAttr::new(kflag, member.offset),
}
}
gen_collection_concrete_type! {
btf_member as Union with HasSize;
struct UnionMember<'btf> {
pub name: Option<&'btf OsStr>,
pub ty: TypeId,
pub attr: MemberAttr,
}
|btf, member, kflag| UnionMember {
name: btf.name_at(member.name_off),
ty: member.type_.into(),
attr: MemberAttr::new(kflag, member.offset),
}
}
#[derive(Clone, Copy, Debug)]
pub struct Composite<'btf> {
source: BtfType<'btf>,
pub is_struct: bool,
members: &'btf [libbpf_sys::btf_member],
}
impl<'btf> From<Struct<'btf>> for Composite<'btf> {
fn from(s: Struct<'btf>) -> Self {
Self {
source: s.source,
is_struct: true,
members: s.members,
}
}
}
impl<'btf> From<Union<'btf>> for Composite<'btf> {
fn from(s: Union<'btf>) -> Self {
Self {
source: s.source,
is_struct: false,
members: s.members,
}
}
}
impl<'btf> TryFrom<BtfType<'btf>> for Composite<'btf> {
type Error = BtfType<'btf>;
fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
Struct::try_from(t)
.map(Self::from)
.or_else(|_| Union::try_from(t).map(Self::from))
}
}
impl<'btf> TryFrom<Composite<'btf>> for Struct<'btf> {
type Error = Composite<'btf>;
fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
if value.is_struct {
Ok(Self {
source: value.source,
members: value.members,
})
} else {
Err(value)
}
}
}
impl<'btf> TryFrom<Composite<'btf>> for Union<'btf> {
type Error = Composite<'btf>;
fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
if !value.is_struct {
Ok(Self {
source: value.source,
members: value.members,
})
} else {
Err(value)
}
}
}
impl Composite<'_> {
pub fn is_empty_union(&self) -> bool {
!self.is_struct && self.is_empty()
}
}
gen_collection_members_concrete_type! {
btf_member as Composite with HasSize;
struct CompositeMember<'btf> {
pub name: Option<&'btf OsStr>,
pub ty: TypeId,
pub attr: MemberAttr
}
|btf, member, kflag| CompositeMember {
name: btf.name_at(member.name_off),
ty: member.type_.into(),
attr: MemberAttr::new(kflag, member.offset),
}
}
gen_collection_concrete_type! {
btf_enum as Enum with HasSize;
struct EnumMember<'btf> {
pub name: Option<&'btf OsStr>,
pub value: i64,
}
|btf, member, signed| {
EnumMember {
name: btf.name_at(member.name_off),
value: if signed {
member.val.into()
} else {
u32::from_ne_bytes(member.val.to_ne_bytes()).into()
}
}
}
}
impl Enum<'_> {
#[inline]
pub fn is_signed(&self) -> bool {
self.kind_flag()
}
}
gen_fieldless_concrete_type! {
Fwd
}
impl Fwd<'_> {
pub fn kind(&self) -> FwdKind {
if self.source.kind_flag() {
FwdKind::Union
} else {
FwdKind::Struct
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum FwdKind {
Struct,
Union,
}
gen_fieldless_concrete_type! {
Typedef with ReferencesType
}
gen_fieldless_concrete_type! {
Volatile with ReferencesType
}
gen_fieldless_concrete_type! {
Const with ReferencesType
}
gen_fieldless_concrete_type! {
Restrict with ReferencesType
}
gen_fieldless_concrete_type! {
Func with ReferencesType
}
impl Func<'_> {
#[inline]
pub fn linkage(&self) -> Linkage {
self.source.vlen().into()
}
}
gen_collection_concrete_type! {
btf_param as FuncProto with ReferencesType;
struct FuncProtoParam<'btf> {
pub name: Option<&'btf OsStr>,
pub ty: TypeId,
}
|btf, member| FuncProtoParam {
name: btf.name_at(member.name_off),
ty: member.type_.into()
}
}
gen_concrete_type! {
btf_var as Var with ReferencesType
}
impl Var<'_> {
#[inline]
pub fn linkage(&self) -> Linkage {
self.ptr.linkage.into()
}
}
gen_collection_concrete_type! {
btf_var_secinfo as DataSec with HasSize;
struct VarSecInfo {
pub ty: TypeId,
pub offset: u32,
pub size: usize,
}
|_btf, member| VarSecInfo {
ty: member.type_.into(),
offset: member.offset,
size: member.size as usize
}
}
gen_fieldless_concrete_type! {
Float with HasSize
}
gen_concrete_type! {
btf_decl_tag as DeclTag with ReferencesType
}
impl DeclTag<'_> {
#[inline]
pub fn component_index(&self) -> Option<u32> {
self.ptr.component_idx.try_into().ok()
}
}
gen_fieldless_concrete_type! {
TypeTag with ReferencesType
}
gen_collection_concrete_type! {
btf_enum64 as Enum64 with HasSize;
struct Enum64Member<'btf> {
pub name: Option<&'btf OsStr>,
pub value: i128,
}
|btf, member, signed| Enum64Member {
name: btf.name_at(member.name_off),
value: {
let hi: u64 = member.val_hi32.into();
let lo: u64 = member.val_lo32.into();
let val = (hi << 32) | lo;
if signed {
i64::from_ne_bytes(val.to_ne_bytes()).into()
} else {
val.into()
}
},
}
}
impl Enum64<'_> {
#[inline]
pub fn is_signed(&self) -> bool {
self.kind_flag()
}
}
#[macro_export]
macro_rules! btf_type_match {
(
match $ty:ident {
$($pattern:tt)+
}
) => {{
let ty: $crate::btf::BtfType<'_> = $ty;
$crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __btf_type_match {
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
BtfKind::Composite $( ($var:ident) )? => $action:expr,
$($rest:tt)*
) => {
$crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
BtfKind::Composite $( ($var) )* => { $action }
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
BtfKind::Composite $(($var:ident))? => $action:block
$($rest:tt)*
) => {
$crate::__btf_type_match!(match $ty.kind() {
$($p => $a,)*
$crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
$(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
$action
}
}
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
BtfKind::$name:ident $(($var:ident))? => $action:expr,
$($rest:tt)*
) => {
$crate::__btf_type_match!(
match $ty.kind() { $($p => $a),* }
BtfKind::$name $(($var))? => { $action }
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
BtfKind::$name:ident $(($var:ident))? => $action:block
$($rest:tt)*
) => {
$crate::__btf_type_match!(match $ty.kind() {
$($p => $a,)*
$crate::btf::BtfKind::$name => {
$(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
$action
}
}
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
$(BtfKind::$name:ident)|+ => $action:expr,
$($rest:tt)*
) => {
$crate::__btf_type_match!(
match $ty.kind() { $($p => $a),* }
$(BtfKind::$name)|* => { $action }
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
$(BtfKind::$name:ident)|+ => $action:block
$($rest:tt)*
) => {
$crate::__btf_type_match!(match $ty.kind() {
$($p => $a,)*
$($crate::btf::BtfKind::$name)|* => {
$action
}
}
$($rest)*
)
};
(
match $ty:ident.kind() { $($p:pat => $a:expr),* }
_ => $action:expr $(,)?
) => {
$crate::__btf_type_match!(match $ty.kind() {
$($p => $a,)*
_ => { $action }
}
)
};
(match $ty:ident.kind() { $($p:pat => $a:expr),* } ) => {
match $ty.kind() {
$($p => $a),*
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! dummy_type {
($ty:ident) => {
let btf = $crate::Btf {
ptr: std::ptr::NonNull::dangling(),
drop_policy: $crate::btf::DropPolicy::Nothing,
_marker: std::marker::PhantomData,
};
let $ty = BtfType {
type_id: $crate::btf::TypeId::from(1),
name: None,
source: &btf,
ty: &libbpf_sys::btf_type::default(),
};
};
}
fn foo(_: super::Int<'_>) -> &'static str {
"int"
}
#[test]
fn full_switch_case() {
dummy_type!(ty);
btf_type_match!(match ty {
BtfKind::Int(i) => foo(i),
BtfKind::Struct => "it's a struct",
BtfKind::Void => "",
BtfKind::Ptr => "",
BtfKind::Array => "",
BtfKind::Union => "",
BtfKind::Enum => "",
BtfKind::Fwd => "",
BtfKind::Typedef => "",
BtfKind::Volatile => "",
BtfKind::Const => "",
BtfKind::Restrict => "",
BtfKind::Func => "",
BtfKind::FuncProto => "",
BtfKind::Var => "",
BtfKind::DataSec => "",
BtfKind::Float => "",
BtfKind::DeclTag => "",
BtfKind::TypeTag => "",
BtfKind::Enum64 => "",
});
}
#[test]
fn partial_match() {
dummy_type!(ty);
btf_type_match!(match ty {
BtfKind::Int => "int",
_ => "default",
});
}
#[test]
fn or_pattern_match() {
dummy_type!(ty);
#[rustfmt::skip]
btf_type_match!(match ty {
BtfKind::Int => "int",
BtfKind::Struct | BtfKind::Union => "composite",
BtfKind::Typedef | BtfKind::Volatile => {
"qualifier"
}
BtfKind::Const | BtfKind::Restrict => {
"const or restrict"
},
_ => "default",
});
}
#[test]
fn match_arm_with_brackets() {
dummy_type!(ty);
#[rustfmt::skip]
btf_type_match!(match ty {
BtfKind::Void => {
"void"
}
BtfKind::Int => {
"int"
},
BtfKind::Struct => "struct",
_ => "default",
});
}
#[test]
fn match_on_composite() {
dummy_type!(ty);
btf_type_match!(match ty {
BtfKind::Composite(c) => c.is_struct,
_ => false,
});
btf_type_match!(match ty {
BtfKind::Composite(c) => {
c.is_struct
}
_ => false,
});
#[rustfmt::skip]
btf_type_match!(match ty {
BtfKind::Composite(c) => {
c.is_struct
},
_ => false,
});
}
#[test]
fn match_arm_with_multiple_statements() {
dummy_type!(ty);
btf_type_match!(match ty {
BtfKind::Int(i) => {
let _ = i;
"int"
}
_ => {
let _ = 1;
"default"
}
});
}
#[test]
fn non_expression_guards() {
dummy_type!(ty);
btf_type_match!(match ty {
BtfKind::Int => {
let _ = 1;
"int"
}
BtfKind::Typedef | BtfKind::Const => {
let _ = 1;
"qualifier"
}
_ => {
let _ = 1;
"default"
}
});
btf_type_match!(match ty {
BtfKind::Int => {
let _ = 1;
}
BtfKind::Typedef | BtfKind::Const => {
let _ = 1;
}
_ => {
let _ = 1;
}
});
}
#[test]
fn linkage_type() {
use std::mem::discriminant;
use Linkage::*;
for t in [Static, Global, Extern, Unknown] {
assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
}
}
}