libbpf_rs/btf/
types.rs

1//! Wrappers representing concrete btf types.
2
3use std::ffi::OsStr;
4use std::fmt;
5use std::fmt::Display;
6use std::ops::Deref;
7
8use super::BtfKind;
9use super::BtfType;
10use super::HasSize;
11use super::ReferencesType;
12use super::TypeId;
13
14// Generate a btf type that doesn't have any fields, i.e. there is no data after the BtfType
15// pointer.
16macro_rules! gen_fieldless_concrete_type {
17    (
18        $(#[$docs:meta])*
19        $name:ident $(with $trait:ident)?
20    ) => {
21        $(#[$docs])*
22        #[derive(Clone, Copy, Debug)]
23        pub struct $name<'btf> {
24            source: BtfType<'btf>,
25        }
26
27        impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
28            type Error = BtfType<'btf>;
29
30            fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
31                if t.kind() == BtfKind::$name {
32                    Ok($name { source: t })
33                } else {
34                    Err(t)
35                }
36            }
37        }
38
39        impl<'btf> ::std::ops::Deref for $name<'btf> {
40            type Target = BtfType<'btf>;
41            fn deref(&self) -> &Self::Target {
42                &self.source
43            }
44        }
45
46        $(
47            impl super::sealed::Sealed for $name<'_> {}
48            unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
49        )*
50    };
51}
52
53// Generate a btf type that has at least one field, and as such, there is data following the
54// btf_type pointer.
55macro_rules! gen_concrete_type {
56    (
57        $(#[$docs:meta])*
58        $libbpf_ty:ident as $name:ident $(with $trait:ident)?
59    ) => {
60        $(#[$docs])*
61        #[derive(Clone, Copy, Debug)]
62        pub struct $name<'btf> {
63            source: BtfType<'btf>,
64            ptr: &'btf libbpf_sys::$libbpf_ty,
65        }
66
67        impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
68            type Error = BtfType<'btf>;
69
70            fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
71                if t.kind() == BtfKind::$name {
72                    let ptr = unsafe {
73                        // SAFETY:
74                        //
75                        // It's in bounds to access the memory following this btf_type
76                        // because we've checked the type
77                        (t.ty as *const libbpf_sys::btf_type).offset(1)
78                    };
79                    let ptr = ptr.cast::<libbpf_sys::$libbpf_ty>();
80                    Ok($name {
81                        source: t,
82                        // SAFETY:
83                        //
84                        // This pointer is aligned.
85                        //      all fields of all struct have size and
86                        //      alignment of u32, if t.ty was aligned, then this must be as well
87                        //
88                        // It's initialized
89                        //      libbpf guarantees this since we've checked the type
90                        //
91                        // The lifetime will match the lifetime of the original t.ty reference.
92                        ptr: unsafe { &*ptr },
93                    })
94                } else {
95                    Err(t)
96                }
97            }
98        }
99
100        impl<'btf> ::std::ops::Deref for $name<'btf> {
101            type Target = BtfType<'btf>;
102            fn deref(&self) -> &Self::Target {
103                &self.source
104            }
105        }
106
107        $(
108            impl super::sealed::Sealed for $name<'_> {}
109            unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
110        )*
111    };
112}
113
114macro_rules! gen_collection_members_concrete_type {
115    (
116        $libbpf_ty:ident as $name:ident $(with $trait:ident)?;
117
118        $(#[$docs:meta])*
119        struct $member_name:ident $(<$lt:lifetime>)? {
120            $(
121                $(#[$field_docs:meta])*
122                pub $field:ident : $type:ty
123            ),* $(,)?
124        }
125
126        |$btf:ident, $member:ident $(, $kind_flag:ident)?| $convert:expr
127    ) => {
128        impl<'btf> ::std::ops::Deref for $name<'btf> {
129            type Target = BtfType<'btf>;
130            fn deref(&self) -> &Self::Target {
131                &self.source
132            }
133        }
134
135        impl<'btf> $name<'btf> {
136            /// Whether this type has no members
137            #[inline]
138            pub fn is_empty(&self) -> bool {
139                self.members.is_empty()
140            }
141
142            #[doc = ::core::concat!("How many members this [`", ::core::stringify!($name), "`] has")]
143            #[inline]
144            pub fn len(&self) -> usize {
145                self.members.len()
146            }
147
148            #[doc = ::core::concat!("Get a [`", ::core::stringify!($member_name), "`] at a given index")]
149            /// # Errors
150            ///
151            /// This function returns [`None`] when the index is out of bounds.
152            pub fn get(&self, index: usize) -> Option<$member_name$(<$lt>)*> {
153                self.members.get(index).map(|m| self.c_to_rust_member(m))
154            }
155
156            #[doc = ::core::concat!("Returns an iterator over the [`", ::core::stringify!($member_name), "`]'s of the [`", ::core::stringify!($name), "`]")]
157            pub fn iter(&'btf self) -> impl ExactSizeIterator<Item = $member_name$(<$lt>)*> + 'btf {
158                self.members.iter().map(|m| self.c_to_rust_member(m))
159            }
160
161            fn c_to_rust_member(&self, member: &libbpf_sys::$libbpf_ty) -> $member_name$(<$lt>)* {
162                let $btf = self.source.source;
163                let $member = member;
164                $(let $kind_flag = self.source.kind_flag();)*
165                $convert
166            }
167        }
168
169        $(#[$docs])*
170        #[derive(Clone, Copy, Debug)]
171        pub struct $member_name $(<$lt>)? {
172            $(
173                $(#[$field_docs])*
174                pub $field: $type
175            ),*
176        }
177
178        $(
179            impl $crate::btf::sealed::Sealed for $name<'_> {}
180            unsafe impl<'btf> $trait<'btf> for $name<'btf> {}
181        )*
182    };
183}
184
185macro_rules! gen_collection_concrete_type {
186    (
187        $(#[$docs:meta])*
188        $libbpf_ty:ident as $name:ident $(with $trait:ident)?;
189
190        $($rest:tt)+
191    ) => {
192        $(#[$docs])*
193        #[derive(Clone, Copy, Debug)]
194        pub struct $name<'btf> {
195            source: BtfType<'btf>,
196            members: &'btf [libbpf_sys::$libbpf_ty],
197        }
198
199        impl<'btf> TryFrom<BtfType<'btf>> for $name<'btf> {
200            type Error = BtfType<'btf>;
201
202            fn try_from(t: BtfType<'btf>) -> ::core::result::Result<Self, Self::Error> {
203                if t.kind() == BtfKind::$name {
204                    let base_ptr = unsafe {
205                        // SAFETY:
206                        //
207                        // It's in bounds to access the memory following this btf_type
208                        // because we've checked the type
209                        (t.ty as *const libbpf_sys::btf_type).offset(1)
210                    };
211                    let members = unsafe {
212                        // SAFETY:
213                        //
214                        // This pointer is aligned.
215                        //      all fields of all struct have size and
216                        //      alignment of u32, if t.ty was aligned, then this must be as well
217                        //
218                        // It's initialized
219                        //      libbpf guarantees this since we've checked the type
220                        //
221                        // The lifetime will match the lifetime of the original t.ty reference.
222                        //
223                        // The docs specify the length of the array is stored in vlen.
224                        std::slice::from_raw_parts(base_ptr.cast(), t.vlen() as usize)
225                    };
226                    Ok(Self { source: t, members })
227                } else {
228                    Err(t)
229                }
230            }
231        }
232
233        gen_collection_members_concrete_type!{
234            $libbpf_ty as $name $(with $trait)?;
235            $($rest)*
236        }
237    };
238}
239
240/// The attributes of a member.
241#[derive(Clone, Copy, Debug)]
242pub enum MemberAttr {
243    /// Member is a normal field.
244    Normal {
245        /// The offset of this member in the struct/union.
246        offset: u32,
247    },
248    /// Member is a bitfield.
249    BitField {
250        /// The size of the bitfield.
251        size: u8,
252        /// The offset of the bitfield.
253        offset: u32,
254    },
255}
256
257impl MemberAttr {
258    #[inline]
259    fn new(kflag: bool, offset: u32) -> Self {
260        if kflag {
261            let size = (offset >> 24) as u8;
262            if size != 0 {
263                Self::BitField {
264                    size,
265                    offset: offset & 0x00_ff_ff_ff,
266                }
267            } else {
268                Self::Normal { offset }
269            }
270        } else {
271            Self::Normal { offset }
272        }
273    }
274}
275
276/// The kind of linkage a variable of function can have.
277#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
278#[repr(u32)]
279pub enum Linkage {
280    /// Static linkage
281    Static = 0,
282    /// Global linkage
283    Global,
284    /// External linkage
285    Extern,
286    /// Unknown
287    Unknown,
288}
289
290impl From<u32> for Linkage {
291    fn from(value: u32) -> Self {
292        use Linkage::*;
293
294        match value {
295            x if x == Static as u32 => Static,
296            x if x == Global as u32 => Global,
297            x if x == Extern as u32 => Extern,
298            _ => Unknown,
299        }
300    }
301}
302
303impl From<Linkage> for u32 {
304    fn from(value: Linkage) -> Self {
305        value as u32
306    }
307}
308
309impl Display for Linkage {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        write!(
312            f,
313            "{}",
314            match self {
315                Linkage::Static => "static",
316                Linkage::Global => "global",
317                Linkage::Extern => "extern",
318                Linkage::Unknown => "(unknown)",
319            }
320        )
321    }
322}
323
324// Void
325gen_fieldless_concrete_type! {
326    /// The representation of the c_void type.
327    Void
328}
329
330// Int
331
332/// An integer.
333///
334/// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int)
335#[derive(Clone, Copy, Debug)]
336pub struct Int<'btf> {
337    source: BtfType<'btf>,
338    /// The encoding of the number.
339    pub encoding: IntEncoding,
340    /// The offset in bits where the value of this integer starts. Mostly usefull for bitfields in
341    /// structs.
342    pub offset: u8,
343    /// The number of bits in the int. (For example, an u8 has 8 bits).
344    pub bits: u8,
345}
346
347/// The kinds of ways a btf [Int] can be encoded.
348#[derive(Clone, Copy, Debug)]
349pub enum IntEncoding {
350    /// No encoding.
351    None,
352    /// Signed.
353    Signed,
354    /// It's a c_char.
355    Char,
356    /// It's a bool.
357    Bool,
358}
359
360impl<'btf> TryFrom<BtfType<'btf>> for Int<'btf> {
361    type Error = BtfType<'btf>;
362
363    fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
364        if t.kind() == BtfKind::Int {
365            let int = {
366                let base_ptr = t.ty as *const libbpf_sys::btf_type;
367                let u32_ptr = unsafe {
368                    // SAFETY:
369                    //
370                    // It's in bounds to access the memory following this btf_type
371                    // because we've checked the type
372                    base_ptr.offset(1).cast::<u32>()
373                };
374                unsafe {
375                    // SAFETY:
376                    //
377                    // This pointer is aligned.
378                    //      all fields of all struct have size and
379                    //      alignment of u32, if t.ty was aligned, then this must be as well
380                    //
381                    // It's initialized
382                    //      libbpf guarantees this since we've checked the type
383                    //
384                    // The lifetime will match the lifetime of the original t.ty reference.
385                    *u32_ptr
386                }
387            };
388            let encoding = match (int & 0x0f_00_00_00) >> 24 {
389                0b1 => IntEncoding::Signed,
390                0b10 => IntEncoding::Char,
391                0b100 => IntEncoding::Bool,
392                _ => IntEncoding::None,
393            };
394            Ok(Self {
395                source: t,
396                encoding,
397                offset: ((int & 0x00_ff_00_00) >> 24) as u8,
398                bits: (int & 0x00_00_00_ff) as u8,
399            })
400        } else {
401            Err(t)
402        }
403    }
404}
405
406impl<'btf> Deref for Int<'btf> {
407    type Target = BtfType<'btf>;
408    fn deref(&self) -> &Self::Target {
409        &self.source
410    }
411}
412
413// SAFETY: Int has the .size field set.
414impl super::sealed::Sealed for Int<'_> {}
415unsafe impl<'btf> HasSize<'btf> for Int<'btf> {}
416
417// Ptr
418gen_fieldless_concrete_type! {
419    /// A pointer.
420    ///
421    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-ptr)
422    Ptr with ReferencesType
423}
424
425// Array
426gen_concrete_type! {
427    /// An array.
428    ///
429    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-array)
430    btf_array as Array
431}
432
433impl<'s> Array<'s> {
434    /// The type id of the stored type.
435    #[inline]
436    pub fn ty(&self) -> TypeId {
437        self.ptr.type_.into()
438    }
439
440    /// The type of index used.
441    #[inline]
442    pub fn index_ty(&self) -> TypeId {
443        self.ptr.index_type.into()
444    }
445
446    /// The capacity of the array.
447    #[inline]
448    pub fn capacity(&self) -> usize {
449        self.ptr.nelems as usize
450    }
451
452    /// The type contained in this array.
453    #[inline]
454    pub fn contained_type(&self) -> BtfType<'s> {
455        self.source
456            .source
457            .type_by_id(self.ty())
458            .expect("arrays should always reference an existing type")
459    }
460}
461
462// Struct
463gen_collection_concrete_type! {
464    /// A struct.
465    ///
466    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-struct)
467    btf_member as Struct with HasSize;
468
469    /// A member of a [Struct]
470    struct StructMember<'btf> {
471        /// The member's name
472        pub name: Option<&'btf OsStr>,
473        /// The member's type
474        pub ty: TypeId,
475        /// The attributes of this member.
476        pub attr: MemberAttr,
477    }
478
479    |btf, member, kflag| StructMember {
480        name: btf.name_at(member.name_off),
481        ty: member.type_.into(),
482        attr: MemberAttr::new(kflag, member.offset),
483    }
484}
485
486// Union
487gen_collection_concrete_type! {
488    /// A Union.
489    ///
490    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-union)
491    btf_member as Union with HasSize;
492
493    /// A member of an [Union]
494    struct UnionMember<'btf> {
495        /// The member's name
496        pub name: Option<&'btf OsStr>,
497        /// The member's type
498        pub ty: TypeId,
499        /// The attributes of this member.
500        pub attr: MemberAttr,
501    }
502
503    |btf, member, kflag| UnionMember {
504        name: btf.name_at(member.name_off),
505        ty: member.type_.into(),
506        attr: MemberAttr::new(kflag, member.offset),
507    }
508}
509
510/// A Composite type, which can be one of a [`Struct`] or a [`Union`].
511///
512/// Sometimes it's not useful to distinguish them, in that case, one can use this
513/// type to inspect any of them.
514#[derive(Clone, Copy, Debug)]
515pub struct Composite<'btf> {
516    source: BtfType<'btf>,
517    /// Whether this type is a struct.
518    pub is_struct: bool,
519    members: &'btf [libbpf_sys::btf_member],
520}
521
522impl<'btf> From<Struct<'btf>> for Composite<'btf> {
523    fn from(s: Struct<'btf>) -> Self {
524        Self {
525            source: s.source,
526            is_struct: true,
527            members: s.members,
528        }
529    }
530}
531
532impl<'btf> From<Union<'btf>> for Composite<'btf> {
533    fn from(s: Union<'btf>) -> Self {
534        Self {
535            source: s.source,
536            is_struct: false,
537            members: s.members,
538        }
539    }
540}
541
542impl<'btf> TryFrom<BtfType<'btf>> for Composite<'btf> {
543    type Error = BtfType<'btf>;
544
545    fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
546        Struct::try_from(t)
547            .map(Self::from)
548            .or_else(|_| Union::try_from(t).map(Self::from))
549    }
550}
551
552impl<'btf> TryFrom<Composite<'btf>> for Struct<'btf> {
553    type Error = Composite<'btf>;
554
555    fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
556        if value.is_struct {
557            Ok(Self {
558                source: value.source,
559                members: value.members,
560            })
561        } else {
562            Err(value)
563        }
564    }
565}
566
567impl<'btf> TryFrom<Composite<'btf>> for Union<'btf> {
568    type Error = Composite<'btf>;
569
570    fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
571        if !value.is_struct {
572            Ok(Self {
573                source: value.source,
574                members: value.members,
575            })
576        } else {
577            Err(value)
578        }
579    }
580}
581
582impl Composite<'_> {
583    /// Returns whether this composite type is a `union {}`.
584    pub fn is_empty_union(&self) -> bool {
585        !self.is_struct && self.is_empty()
586    }
587}
588
589// Composite
590gen_collection_members_concrete_type! {
591    btf_member as Composite with HasSize;
592
593    /// A member of a [Struct]
594    struct CompositeMember<'btf> {
595        /// The member's name
596        pub name: Option<&'btf OsStr>,
597        /// The member's type
598        pub ty: TypeId,
599        /// If this member is a bifield, these are it's attributes.
600        pub attr: MemberAttr
601    }
602
603    |btf, member, kflag| CompositeMember {
604        name: btf.name_at(member.name_off),
605        ty: member.type_.into(),
606        attr: MemberAttr::new(kflag, member.offset),
607    }
608}
609
610// Enum
611gen_collection_concrete_type! {
612    /// An Enum of at most 32 bits.
613    ///
614    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum)
615    btf_enum as Enum with HasSize;
616
617    /// A member of an [Enum]
618    struct EnumMember<'btf> {
619        /// The name of this enum variant.
620        pub name: Option<&'btf OsStr>,
621        /// The numeric value of this enum variant.
622        pub value: i32,
623    }
624
625    |btf, member| EnumMember {
626        name: btf.name_at(member.name_off),
627        value: member.val,
628    }
629}
630
631// Fwd
632gen_fieldless_concrete_type! {
633    /// A forward declared C type.
634    ///
635    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-fwd)
636    Fwd
637}
638
639impl Fwd<'_> {
640    /// The kind of C type that is forwardly declared.
641    pub fn kind(&self) -> FwdKind {
642        if self.source.kind_flag() {
643            FwdKind::Union
644        } else {
645            FwdKind::Struct
646        }
647    }
648}
649
650/// The kinds of types that can be forward declared.
651#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
652pub enum FwdKind {
653    /// A struct.
654    Struct,
655    /// A union.
656    Union,
657}
658
659// Typedef
660gen_fieldless_concrete_type! {
661    /// A C typedef.
662    ///
663    /// References the original type.
664    ///
665    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-typedef)
666    Typedef with ReferencesType
667}
668
669// Volatile
670gen_fieldless_concrete_type! {
671    /// The volatile modifier.
672    ///
673    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-volatile)
674    Volatile with ReferencesType
675}
676
677// Const
678gen_fieldless_concrete_type! {
679    /// The const modifier.
680    ///
681    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-const)
682    Const with ReferencesType
683}
684
685// Restrict
686gen_fieldless_concrete_type! {
687    /// The restrict modifier.
688    ///
689    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-restrict)
690    Restrict with ReferencesType
691}
692
693// Func
694gen_fieldless_concrete_type! {
695    /// A function.
696    ///
697    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func)
698    Func with ReferencesType
699}
700
701impl Func<'_> {
702    /// This function's linkage.
703    #[inline]
704    pub fn linkage(&self) -> Linkage {
705        self.source.vlen().into()
706    }
707}
708
709// FuncProto
710gen_collection_concrete_type! {
711    /// A function prototype.
712    ///
713    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func-proto)
714    btf_param as FuncProto with ReferencesType;
715
716    /// A parameter of a [FuncProto].
717    struct FuncProtoParam<'btf> {
718        /// The parameter's name
719        pub name: Option<&'btf OsStr>,
720        /// The parameter's type
721        pub ty: TypeId,
722    }
723
724    |btf, member| FuncProtoParam {
725        name: btf.name_at(member.name_off),
726        ty: member.type_.into()
727    }
728}
729
730// Var
731gen_concrete_type! {
732    /// A global variable.
733    ///
734    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-var)
735    btf_var as Var with ReferencesType
736}
737
738impl Var<'_> {
739    /// The kind of linkage this variable has.
740    #[inline]
741    pub fn linkage(&self) -> Linkage {
742        self.ptr.linkage.into()
743    }
744}
745
746// DataSec
747gen_collection_concrete_type! {
748    /// An ELF's data section, such as `.data`, `.bss` or `.rodata`.
749    ///
750    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-datasec)
751    btf_var_secinfo as DataSec with HasSize;
752
753    /// Describes the btf var in a section.
754    ///
755    /// See [`DataSec`].
756    struct VarSecInfo {
757        /// The type id of the var
758        pub ty: TypeId,
759        /// The offset in the section
760        pub offset: u32,
761        /// The size of the type.
762        pub size: usize,
763    }
764
765    |_btf, member| VarSecInfo {
766        ty: member.type_.into(),
767        offset: member.offset,
768        size: member.size as usize
769    }
770}
771
772// Float
773gen_fieldless_concrete_type! {
774    /// A floating point number.
775    ///
776    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-float)
777    Float with HasSize
778}
779
780// DeclTag
781gen_concrete_type! {
782    /// A declaration tag.
783    ///
784    /// A custom tag the programmer can attach to a symbol.
785    ///
786    /// See the [clang docs](https://clang.llvm.org/docs/AttributeReference.html#btf-decl-tag) on
787    /// it.
788    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-decl-tag)
789    btf_decl_tag as DeclTag with ReferencesType
790}
791
792impl DeclTag<'_> {
793    /// The component index is present only when the tag points to a struct/union member or a
794    /// function argument.
795    /// And component_idx indicates which member or argument, this decl tag refers to.
796    #[inline]
797    pub fn component_index(&self) -> Option<u32> {
798        self.ptr.component_idx.try_into().ok()
799    }
800}
801
802// TypeTag
803gen_fieldless_concrete_type! {
804    /// A type tag.
805    ///
806    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-type-tag)
807    TypeTag with ReferencesType
808}
809
810// Enum64
811gen_collection_concrete_type! {
812    /// An Enum of 64 bits.
813    ///
814    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum64)
815    btf_enum64 as Enum64 with HasSize;
816
817    /// A member of an [Enum64].
818    struct Enum64Member<'btf> {
819        /// The name of this enum variant.
820        pub name: Option<&'btf OsStr>,
821        /// The numeric value of this enum variant.
822        pub value: u64,
823    }
824
825    |btf, member| Enum64Member {
826        name: btf.name_at(member.name_off),
827        value: {
828            let hi: u64 = member.val_hi32.into();
829            let lo: u64 = member.val_lo32.into();
830            hi << 32 | lo
831        },
832    }
833}
834
835/// A macro that allows matching on the type of a [`BtfType`] as if it was an enum.
836///
837/// Each pattern can be of two types.
838///
839/// ```no_run
840/// use libbpf_rs::btf::BtfType;
841/// use libbpf_rs::btf_type_match;
842///
843/// # fn do_something_with_an_int(i: libbpf_rs::btf::types::Int) -> &'static str { "" }
844/// let ty: BtfType;
845/// # ty = todo!();
846/// btf_type_match!(match ty {
847///     BtfKind::Int(i) => do_something_with_an_int(i),
848///     BtfKind::Struct => "it's a struct",
849///     BtfKind::Union => {
850///         "it's a union"
851///     },
852///     _ => "default",
853/// });
854/// ```
855///
856/// Variable Binding.
857///
858/// ```compile_fail
859///     BtfKind::Int(i) => {
860///         // we can use i here and it will be an `Int`
861///     }
862/// ```
863///
864/// NonBinding.
865///
866/// ```compile_fail
867///     BtfKind::Int => {
868///         // we don't have access to the variable, but we know the scrutinee is an Int
869///     }
870/// ```
871///
872/// Multiple Variants
873/// ```compile_fail
874///     BtfKind::Struct | BtfKind::Union => {
875///         // we don't have access to the variable,
876///         // but we know the scrutinee is either a Struct or a Union
877///     }
878/// ```
879///
880/// Special case for [`Struct`] and [`Union`]: [`Composite`]
881/// ```compile_fail
882///     BtfKind::Composite(c) => {
883///         // we can use `c` as an instance of `Composite`.
884///         // this branch will match if the type is either a Struct or a Union.
885///     }
886/// ```
887// $(BtfKind::$name:ident $(($var:ident))? => $action:expr $(,)?)+
888#[macro_export]
889macro_rules! btf_type_match {
890    // base rule
891    (
892        match $ty:ident {
893            $($pattern:tt)+
894        }
895    ) => {{
896        let ty: $crate::btf::BtfType<'_> = $ty;
897        $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
898    }};
899}
900
901#[doc(hidden)]
902#[macro_export]
903macro_rules! __btf_type_match {
904    /*
905     * Composite special case
906     *
907     * This is similar to simple-match but it's hardcoded for composite which matches both structs
908     * and unions.
909     */
910    (
911        match $ty:ident.kind() { $($p:pat => $a:expr),* }
912        BtfKind::Composite $( ($var:ident) )? => $action:expr,
913        $($rest:tt)*
914    ) => {
915        $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
916            BtfKind::Composite $( ($var) )* => { $action }
917            $($rest)*
918        )
919    };
920    (
921        match $ty:ident.kind() { $($p:pat => $a:expr),* }
922        BtfKind::Composite $(($var:ident))? => $action:block
923        $($rest:tt)*
924    ) => {
925        $crate::__btf_type_match!(match $ty.kind() {
926            $($p => $a,)*
927            $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
928                $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
929                $action
930            }
931        }
932             $($rest)*
933        )
934    };
935    // simple-match: match on simple patterns that use an expression followed by a comma
936    (
937        match $ty:ident.kind() { $($p:pat => $a:expr),* }
938        BtfKind::$name:ident $(($var:ident))? => $action:expr,
939        $($rest:tt)*
940    ) => {
941        $crate::__btf_type_match!(
942            match $ty.kind() { $($p => $a),* }
943            BtfKind::$name $(($var))? => { $action }
944            $($rest)*
945        )
946    };
947    // simple-match: match on simple patterns that use a block without a comma
948    (
949        match $ty:ident.kind() { $($p:pat => $a:expr),* }
950        BtfKind::$name:ident $(($var:ident))? => $action:block
951        $($rest:tt)*
952    ) => {
953        $crate::__btf_type_match!(match $ty.kind() {
954            $($p => $a,)*
955            $crate::btf::BtfKind::$name => {
956                $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
957                $action
958            }
959        }
960             $($rest)*
961        )
962    };
963    // or-pattern: match on one or more variants without capturing a variable and using an
964    //             expression followed by a comma.
965    (
966        match $ty:ident.kind() { $($p:pat => $a:expr),* }
967        $(BtfKind::$name:ident)|+  => $action:expr,
968        $($rest:tt)*
969    ) => {
970        $crate::__btf_type_match!(
971            match $ty.kind() { $($p => $a),* }
972            $(BtfKind::$name)|* => { $action }
973            $($rest)*
974        )
975    };
976    (
977        match $ty:ident.kind() { $($p:pat => $a:expr),* }
978        $(BtfKind::$name:ident)|+  => $action:block
979        $($rest:tt)*
980    ) => {
981        $crate::__btf_type_match!(match $ty.kind() {
982            $($p => $a,)*
983            $($crate::btf::BtfKind::$name)|* => {
984                $action
985            }
986        }
987             $($rest)*
988        )
989    };
990    // default match case
991    //
992    // we only need the expression case here because this case is not followed by a $rest:tt like
993    // the others, which let's us use the $(,)? pattern.
994    (
995        match $ty:ident.kind() { $($p:pat => $a:expr),* }
996        _ => $action:expr $(,)?
997    ) => {
998        $crate::__btf_type_match!(match $ty.kind() {
999            $($p => $a,)*
1000            _ => { $action }
1001        }
1002
1003        )
1004    };
1005    // stop case, where the code is actually generated
1006    (match $ty:ident.kind() { $($p:pat => $a:expr),*  } ) => {
1007        match $ty.kind() {
1008            $($p => $a),*
1009        }
1010    }
1011}
1012
1013#[cfg(test)]
1014mod test {
1015    use super::*;
1016
1017    // creates a dummy btftype, not it's not safe to use this type, but it is safe to match on it,
1018    // which is all we need for these tests.
1019    macro_rules! dummy_type {
1020        ($ty:ident) => {
1021            let btf = $crate::Btf {
1022                ptr: std::ptr::NonNull::dangling(),
1023                drop_policy: $crate::btf::DropPolicy::Nothing,
1024                _marker: std::marker::PhantomData,
1025            };
1026            let $ty = BtfType {
1027                type_id: $crate::btf::TypeId::from(1),
1028                name: None,
1029                source: &btf,
1030                ty: &libbpf_sys::btf_type::default(),
1031            };
1032        };
1033    }
1034
1035    fn foo(_: super::Int<'_>) -> &'static str {
1036        "int"
1037    }
1038
1039    #[test]
1040    fn full_switch_case() {
1041        dummy_type!(ty);
1042        btf_type_match!(match ty {
1043            BtfKind::Int(i) => foo(i),
1044            BtfKind::Struct => "it's a struct",
1045            BtfKind::Void => "",
1046            BtfKind::Ptr => "",
1047            BtfKind::Array => "",
1048            BtfKind::Union => "",
1049            BtfKind::Enum => "",
1050            BtfKind::Fwd => "",
1051            BtfKind::Typedef => "",
1052            BtfKind::Volatile => "",
1053            BtfKind::Const => "",
1054            BtfKind::Restrict => "",
1055            BtfKind::Func => "",
1056            BtfKind::FuncProto => "",
1057            BtfKind::Var => "",
1058            BtfKind::DataSec => "",
1059            BtfKind::Float => "",
1060            BtfKind::DeclTag => "",
1061            BtfKind::TypeTag => "",
1062            BtfKind::Enum64 => "",
1063        });
1064    }
1065
1066    #[test]
1067    fn partial_match() {
1068        dummy_type!(ty);
1069        btf_type_match!(match ty {
1070            BtfKind::Int => "int",
1071            _ => "default",
1072        });
1073    }
1074
1075    #[test]
1076    fn or_pattern_match() {
1077        dummy_type!(ty);
1078        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1079        // const | restrict branch.
1080        #[rustfmt::skip]
1081        btf_type_match!(match ty {
1082            BtfKind::Int => "int",
1083            BtfKind::Struct | BtfKind::Union => "composite",
1084            BtfKind::Typedef | BtfKind::Volatile => {
1085                "qualifier"
1086            }
1087            BtfKind::Const | BtfKind::Restrict => {
1088                "const or restrict"
1089            },
1090            _ => "default",
1091        });
1092    }
1093
1094    #[test]
1095    fn match_arm_with_brackets() {
1096        dummy_type!(ty);
1097        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the int
1098        // branch.
1099        #[rustfmt::skip]
1100        btf_type_match!(match ty {
1101            BtfKind::Void => {
1102                "void"
1103            }
1104            BtfKind::Int => {
1105                "int"
1106            },
1107            BtfKind::Struct => "struct",
1108            _ => "default",
1109        });
1110    }
1111
1112    #[test]
1113    fn match_on_composite() {
1114        dummy_type!(ty);
1115        btf_type_match!(match ty {
1116            BtfKind::Composite(c) => c.is_struct,
1117            _ => false,
1118        });
1119        btf_type_match!(match ty {
1120            BtfKind::Composite(c) => {
1121                c.is_struct
1122            }
1123            _ => false,
1124        });
1125        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1126        // composite branch.
1127        #[rustfmt::skip]
1128        btf_type_match!(match ty {
1129            BtfKind::Composite(c) => {
1130                c.is_struct
1131            },
1132            _ => false,
1133        });
1134    }
1135
1136    #[test]
1137    fn match_arm_with_multiple_statements() {
1138        dummy_type!(ty);
1139
1140        btf_type_match!(match ty {
1141            BtfKind::Int(i) => {
1142                let _ = i;
1143                "int"
1144            }
1145            _ => {
1146                let _ = 1;
1147                "default"
1148            }
1149        });
1150    }
1151
1152    #[test]
1153    fn non_expression_guards() {
1154        dummy_type!(ty);
1155
1156        btf_type_match!(match ty {
1157            BtfKind::Int => {
1158                let _ = 1;
1159                "int"
1160            }
1161            BtfKind::Typedef | BtfKind::Const => {
1162                let _ = 1;
1163                "qualifier"
1164            }
1165            _ => {
1166                let _ = 1;
1167                "default"
1168            }
1169        });
1170
1171        btf_type_match!(match ty {
1172            BtfKind::Int => {
1173                let _ = 1;
1174            }
1175            BtfKind::Typedef | BtfKind::Const => {
1176                let _ = 1;
1177            }
1178            _ => {
1179                let _ = 1;
1180            }
1181        });
1182    }
1183
1184    #[test]
1185    fn linkage_type() {
1186        use std::mem::discriminant;
1187        use Linkage::*;
1188
1189        for t in [Static, Global, Extern, Unknown] {
1190            // check if discriminants match after a roundtrip conversion
1191            assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1192        }
1193    }
1194}