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: i64,
623    }
624
625    |btf, member, signed| {
626        EnumMember {
627            name: btf.name_at(member.name_off),
628            value: if signed {
629                member.val.into()
630            } else {
631                u32::from_ne_bytes(member.val.to_ne_bytes()).into()
632            }
633        }
634    }
635}
636
637impl Enum<'_> {
638    /// Check whether the enum is signed or not.
639    #[inline]
640    pub fn is_signed(&self) -> bool {
641        self.kind_flag()
642    }
643}
644
645
646// Fwd
647gen_fieldless_concrete_type! {
648    /// A forward declared C type.
649    ///
650    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-fwd)
651    Fwd
652}
653
654impl Fwd<'_> {
655    /// The kind of C type that is forwardly declared.
656    pub fn kind(&self) -> FwdKind {
657        if self.source.kind_flag() {
658            FwdKind::Union
659        } else {
660            FwdKind::Struct
661        }
662    }
663}
664
665/// The kinds of types that can be forward declared.
666#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
667pub enum FwdKind {
668    /// A struct.
669    Struct,
670    /// A union.
671    Union,
672}
673
674// Typedef
675gen_fieldless_concrete_type! {
676    /// A C typedef.
677    ///
678    /// References the original type.
679    ///
680    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-typedef)
681    Typedef with ReferencesType
682}
683
684// Volatile
685gen_fieldless_concrete_type! {
686    /// The volatile modifier.
687    ///
688    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-volatile)
689    Volatile with ReferencesType
690}
691
692// Const
693gen_fieldless_concrete_type! {
694    /// The const modifier.
695    ///
696    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-const)
697    Const with ReferencesType
698}
699
700// Restrict
701gen_fieldless_concrete_type! {
702    /// The restrict modifier.
703    ///
704    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-restrict)
705    Restrict with ReferencesType
706}
707
708// Func
709gen_fieldless_concrete_type! {
710    /// A function.
711    ///
712    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func)
713    Func with ReferencesType
714}
715
716impl Func<'_> {
717    /// This function's linkage.
718    #[inline]
719    pub fn linkage(&self) -> Linkage {
720        self.source.vlen().into()
721    }
722}
723
724// FuncProto
725gen_collection_concrete_type! {
726    /// A function prototype.
727    ///
728    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func-proto)
729    btf_param as FuncProto with ReferencesType;
730
731    /// A parameter of a [FuncProto].
732    struct FuncProtoParam<'btf> {
733        /// The parameter's name
734        pub name: Option<&'btf OsStr>,
735        /// The parameter's type
736        pub ty: TypeId,
737    }
738
739    |btf, member| FuncProtoParam {
740        name: btf.name_at(member.name_off),
741        ty: member.type_.into()
742    }
743}
744
745// Var
746gen_concrete_type! {
747    /// A global variable.
748    ///
749    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-var)
750    btf_var as Var with ReferencesType
751}
752
753impl Var<'_> {
754    /// The kind of linkage this variable has.
755    #[inline]
756    pub fn linkage(&self) -> Linkage {
757        self.ptr.linkage.into()
758    }
759}
760
761// DataSec
762gen_collection_concrete_type! {
763    /// An ELF's data section, such as `.data`, `.bss` or `.rodata`.
764    ///
765    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-datasec)
766    btf_var_secinfo as DataSec with HasSize;
767
768    /// Describes the btf var in a section.
769    ///
770    /// See [`DataSec`].
771    struct VarSecInfo {
772        /// The type id of the var
773        pub ty: TypeId,
774        /// The offset in the section
775        pub offset: u32,
776        /// The size of the type.
777        pub size: usize,
778    }
779
780    |_btf, member| VarSecInfo {
781        ty: member.type_.into(),
782        offset: member.offset,
783        size: member.size as usize
784    }
785}
786
787// Float
788gen_fieldless_concrete_type! {
789    /// A floating point number.
790    ///
791    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-float)
792    Float with HasSize
793}
794
795// DeclTag
796gen_concrete_type! {
797    /// A declaration tag.
798    ///
799    /// A custom tag the programmer can attach to a symbol.
800    ///
801    /// See the [clang docs](https://clang.llvm.org/docs/AttributeReference.html#btf-decl-tag) on
802    /// it.
803    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-decl-tag)
804    btf_decl_tag as DeclTag with ReferencesType
805}
806
807impl DeclTag<'_> {
808    /// The component index is present only when the tag points to a struct/union member or a
809    /// function argument.
810    /// And `component_idx` indicates which member or argument, this decl tag refers to.
811    #[inline]
812    pub fn component_index(&self) -> Option<u32> {
813        self.ptr.component_idx.try_into().ok()
814    }
815}
816
817// TypeTag
818gen_fieldless_concrete_type! {
819    /// A type tag.
820    ///
821    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-type-tag)
822    TypeTag with ReferencesType
823}
824
825// Enum64
826gen_collection_concrete_type! {
827    /// An Enum of 64 bits.
828    ///
829    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum64)
830    btf_enum64 as Enum64 with HasSize;
831
832    /// A member of an [Enum64].
833    struct Enum64Member<'btf> {
834        /// The name of this enum variant.
835        pub name: Option<&'btf OsStr>,
836        /// The numeric value of this enum variant.
837        pub value: i128,
838    }
839
840    |btf, member, signed| Enum64Member {
841        name: btf.name_at(member.name_off),
842        value: {
843            let hi: u64 = member.val_hi32.into();
844            let lo: u64 = member.val_lo32.into();
845            let val = (hi << 32) | lo;
846            if signed {
847                i64::from_ne_bytes(val.to_ne_bytes()).into()
848            } else {
849                val.into()
850            }
851        },
852    }
853}
854
855impl Enum64<'_> {
856    /// Check whether the enum is signed or not.
857    #[inline]
858    pub fn is_signed(&self) -> bool {
859        self.kind_flag()
860    }
861}
862
863
864/// A macro that allows matching on the type of a [`BtfType`] as if it was an enum.
865///
866/// Each pattern can be of two types.
867///
868/// ```no_run
869/// use libbpf_rs::btf::BtfType;
870/// use libbpf_rs::btf_type_match;
871///
872/// # fn do_something_with_an_int(i: libbpf_rs::btf::types::Int) -> &'static str { "" }
873/// let ty: BtfType;
874/// # ty = todo!();
875/// btf_type_match!(match ty {
876///     BtfKind::Int(i) => do_something_with_an_int(i),
877///     BtfKind::Struct => "it's a struct",
878///     BtfKind::Union => {
879///         "it's a union"
880///     },
881///     _ => "default",
882/// });
883/// ```
884///
885/// Variable Binding.
886///
887/// ```compile_fail
888///     BtfKind::Int(i) => {
889///         // we can use i here and it will be an `Int`
890///     }
891/// ```
892///
893/// Non-binding.
894///
895/// ```compile_fail
896///     BtfKind::Int => {
897///         // we don't have access to the variable, but we know the scrutinee is an Int
898///     }
899/// ```
900///
901/// Multiple Variants
902/// ```compile_fail
903///     BtfKind::Struct | BtfKind::Union => {
904///         // we don't have access to the variable,
905///         // but we know the scrutinee is either a Struct or a Union
906///     }
907/// ```
908///
909/// Special case for [`Struct`] and [`Union`]: [`Composite`]
910/// ```compile_fail
911///     BtfKind::Composite(c) => {
912///         // we can use `c` as an instance of `Composite`.
913///         // this branch will match if the type is either a Struct or a Union.
914///     }
915/// ```
916// $(BtfKind::$name:ident $(($var:ident))? => $action:expr $(,)?)+
917#[macro_export]
918macro_rules! btf_type_match {
919    // base rule
920    (
921        match $ty:ident {
922            $($pattern:tt)+
923        }
924    ) => {{
925        let ty: $crate::btf::BtfType<'_> = $ty;
926        $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
927    }};
928}
929
930#[doc(hidden)]
931#[macro_export]
932macro_rules! __btf_type_match {
933    /*
934     * Composite special case
935     *
936     * This is similar to simple-match but it's hardcoded for composite which matches both structs
937     * and unions.
938     */
939    (
940        match $ty:ident.kind() { $($p:pat => $a:expr),* }
941        BtfKind::Composite $( ($var:ident) )? => $action:expr,
942        $($rest:tt)*
943    ) => {
944        $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
945            BtfKind::Composite $( ($var) )* => { $action }
946            $($rest)*
947        )
948    };
949    (
950        match $ty:ident.kind() { $($p:pat => $a:expr),* }
951        BtfKind::Composite $(($var:ident))? => $action:block
952        $($rest:tt)*
953    ) => {
954        $crate::__btf_type_match!(match $ty.kind() {
955            $($p => $a,)*
956            $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
957                $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
958                $action
959            }
960        }
961             $($rest)*
962        )
963    };
964    // simple-match: match on simple patterns that use an expression followed by a comma
965    (
966        match $ty:ident.kind() { $($p:pat => $a:expr),* }
967        BtfKind::$name:ident $(($var:ident))? => $action:expr,
968        $($rest:tt)*
969    ) => {
970        $crate::__btf_type_match!(
971            match $ty.kind() { $($p => $a),* }
972            BtfKind::$name $(($var))? => { $action }
973            $($rest)*
974        )
975    };
976    // simple-match: match on simple patterns that use a block without a comma
977    (
978        match $ty:ident.kind() { $($p:pat => $a:expr),* }
979        BtfKind::$name:ident $(($var:ident))? => $action:block
980        $($rest:tt)*
981    ) => {
982        $crate::__btf_type_match!(match $ty.kind() {
983            $($p => $a,)*
984            $crate::btf::BtfKind::$name => {
985                $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
986                $action
987            }
988        }
989             $($rest)*
990        )
991    };
992    // or-pattern: match on one or more variants without capturing a variable and using an
993    //             expression followed by a comma.
994    (
995        match $ty:ident.kind() { $($p:pat => $a:expr),* }
996        $(BtfKind::$name:ident)|+  => $action:expr,
997        $($rest:tt)*
998    ) => {
999        $crate::__btf_type_match!(
1000            match $ty.kind() { $($p => $a),* }
1001            $(BtfKind::$name)|* => { $action }
1002            $($rest)*
1003        )
1004    };
1005    (
1006        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1007        $(BtfKind::$name:ident)|+  => $action:block
1008        $($rest:tt)*
1009    ) => {
1010        $crate::__btf_type_match!(match $ty.kind() {
1011            $($p => $a,)*
1012            $($crate::btf::BtfKind::$name)|* => {
1013                $action
1014            }
1015        }
1016             $($rest)*
1017        )
1018    };
1019    // default match case
1020    //
1021    // we only need the expression case here because this case is not followed by a $rest:tt like
1022    // the others, which let's us use the $(,)? pattern.
1023    (
1024        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1025        _ => $action:expr $(,)?
1026    ) => {
1027        $crate::__btf_type_match!(match $ty.kind() {
1028            $($p => $a,)*
1029            _ => { $action }
1030        }
1031
1032        )
1033    };
1034    // stop case, where the code is actually generated
1035    (match $ty:ident.kind() { $($p:pat => $a:expr),*  } ) => {
1036        match $ty.kind() {
1037            $($p => $a),*
1038        }
1039    }
1040}
1041
1042#[cfg(test)]
1043mod test {
1044    use super::*;
1045
1046    // creates a dummy btftype, not it's not safe to use this type, but it is safe to match on it,
1047    // which is all we need for these tests.
1048    macro_rules! dummy_type {
1049        ($ty:ident) => {
1050            let btf = $crate::Btf {
1051                ptr: std::ptr::NonNull::dangling(),
1052                drop_policy: $crate::btf::DropPolicy::Nothing,
1053                _marker: std::marker::PhantomData,
1054            };
1055            let $ty = BtfType {
1056                type_id: $crate::btf::TypeId::from(1),
1057                name: None,
1058                source: &btf,
1059                ty: &libbpf_sys::btf_type::default(),
1060            };
1061        };
1062    }
1063
1064    fn foo(_: super::Int<'_>) -> &'static str {
1065        "int"
1066    }
1067
1068    #[test]
1069    fn full_switch_case() {
1070        dummy_type!(ty);
1071        btf_type_match!(match ty {
1072            BtfKind::Int(i) => foo(i),
1073            BtfKind::Struct => "it's a struct",
1074            BtfKind::Void => "",
1075            BtfKind::Ptr => "",
1076            BtfKind::Array => "",
1077            BtfKind::Union => "",
1078            BtfKind::Enum => "",
1079            BtfKind::Fwd => "",
1080            BtfKind::Typedef => "",
1081            BtfKind::Volatile => "",
1082            BtfKind::Const => "",
1083            BtfKind::Restrict => "",
1084            BtfKind::Func => "",
1085            BtfKind::FuncProto => "",
1086            BtfKind::Var => "",
1087            BtfKind::DataSec => "",
1088            BtfKind::Float => "",
1089            BtfKind::DeclTag => "",
1090            BtfKind::TypeTag => "",
1091            BtfKind::Enum64 => "",
1092        });
1093    }
1094
1095    #[test]
1096    fn partial_match() {
1097        dummy_type!(ty);
1098        btf_type_match!(match ty {
1099            BtfKind::Int => "int",
1100            _ => "default",
1101        });
1102    }
1103
1104    #[test]
1105    fn or_pattern_match() {
1106        dummy_type!(ty);
1107        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1108        // const | restrict branch.
1109        #[rustfmt::skip]
1110        btf_type_match!(match ty {
1111            BtfKind::Int => "int",
1112            BtfKind::Struct | BtfKind::Union => "composite",
1113            BtfKind::Typedef | BtfKind::Volatile => {
1114                "qualifier"
1115            }
1116            BtfKind::Const | BtfKind::Restrict => {
1117                "const or restrict"
1118            },
1119            _ => "default",
1120        });
1121    }
1122
1123    #[test]
1124    fn match_arm_with_brackets() {
1125        dummy_type!(ty);
1126        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the int
1127        // branch.
1128        #[rustfmt::skip]
1129        btf_type_match!(match ty {
1130            BtfKind::Void => {
1131                "void"
1132            }
1133            BtfKind::Int => {
1134                "int"
1135            },
1136            BtfKind::Struct => "struct",
1137            _ => "default",
1138        });
1139    }
1140
1141    #[test]
1142    fn match_on_composite() {
1143        dummy_type!(ty);
1144        btf_type_match!(match ty {
1145            BtfKind::Composite(c) => c.is_struct,
1146            _ => false,
1147        });
1148        btf_type_match!(match ty {
1149            BtfKind::Composite(c) => {
1150                c.is_struct
1151            }
1152            _ => false,
1153        });
1154        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1155        // composite branch.
1156        #[rustfmt::skip]
1157        btf_type_match!(match ty {
1158            BtfKind::Composite(c) => {
1159                c.is_struct
1160            },
1161            _ => false,
1162        });
1163    }
1164
1165    #[test]
1166    fn match_arm_with_multiple_statements() {
1167        dummy_type!(ty);
1168
1169        btf_type_match!(match ty {
1170            BtfKind::Int(i) => {
1171                let _ = i;
1172                "int"
1173            }
1174            _ => {
1175                let _ = 1;
1176                "default"
1177            }
1178        });
1179    }
1180
1181    #[test]
1182    fn non_expression_guards() {
1183        dummy_type!(ty);
1184
1185        btf_type_match!(match ty {
1186            BtfKind::Int => {
1187                let _ = 1;
1188                "int"
1189            }
1190            BtfKind::Typedef | BtfKind::Const => {
1191                let _ = 1;
1192                "qualifier"
1193            }
1194            _ => {
1195                let _ = 1;
1196                "default"
1197            }
1198        });
1199
1200        btf_type_match!(match ty {
1201            BtfKind::Int => {
1202                let _ = 1;
1203            }
1204            BtfKind::Typedef | BtfKind::Const => {
1205                let _ = 1;
1206            }
1207            _ => {
1208                let _ = 1;
1209            }
1210        });
1211    }
1212
1213    #[test]
1214    fn linkage_type() {
1215        use std::mem::discriminant;
1216        use Linkage::*;
1217
1218        for t in [Static, Global, Extern, Unknown] {
1219            // check if discriminants match after a roundtrip conversion
1220            assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1221        }
1222    }
1223}