Skip to main content

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)]
279#[doc(alias = "btf_func_linkage")]
280pub enum Linkage {
281    /// Static linkage
282    Static = 0,
283    /// Global linkage
284    Global,
285    /// External linkage
286    Extern,
287    /// Unknown
288    Unknown,
289}
290
291impl From<u32> for Linkage {
292    fn from(value: u32) -> Self {
293        use Linkage::*;
294
295        match value {
296            x if x == Static as u32 => Static,
297            x if x == Global as u32 => Global,
298            x if x == Extern as u32 => Extern,
299            _ => Unknown,
300        }
301    }
302}
303
304impl From<Linkage> for u32 {
305    fn from(value: Linkage) -> Self {
306        value as Self
307    }
308}
309
310impl Display for Linkage {
311    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312        write!(
313            f,
314            "{}",
315            match self {
316                Self::Static => "static",
317                Self::Global => "global",
318                Self::Extern => "extern",
319                Self::Unknown => "(unknown)",
320            }
321        )
322    }
323}
324
325// Void
326gen_fieldless_concrete_type! {
327    /// The representation of the [`c_void`][std::ffi::c_void] type.
328    #[doc(alias = "BTF_KIND_UNKN")]
329    Void
330}
331
332// Int
333
334/// An integer.
335///
336/// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int)
337#[derive(Clone, Copy, Debug)]
338#[doc(alias = "BTF_KIND_INT")]
339pub struct Int<'btf> {
340    source: BtfType<'btf>,
341    /// The encoding of the number.
342    pub encoding: IntEncoding,
343    /// The offset in bits where the value of this integer starts. Mostly usefull for bitfields in
344    /// structs.
345    pub offset: u8,
346    /// The number of bits in the int. (For example, an u8 has 8 bits).
347    pub bits: u8,
348}
349
350/// The kinds of ways a btf [Int] can be encoded.
351#[derive(Clone, Copy, Debug)]
352pub enum IntEncoding {
353    /// No encoding.
354    None,
355    /// Signed.
356    Signed,
357    /// It's a `c_char`.
358    Char,
359    /// It's a bool.
360    Bool,
361}
362
363impl<'btf> TryFrom<BtfType<'btf>> for Int<'btf> {
364    type Error = BtfType<'btf>;
365
366    fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
367        if t.kind() == BtfKind::Int {
368            let int = {
369                let base_ptr = t.ty as *const libbpf_sys::btf_type;
370                let u32_ptr = unsafe {
371                    // SAFETY:
372                    //
373                    // It's in bounds to access the memory following this btf_type
374                    // because we've checked the type
375                    base_ptr.offset(1).cast::<u32>()
376                };
377                unsafe {
378                    // SAFETY:
379                    //
380                    // This pointer is aligned.
381                    //      all fields of all struct have size and
382                    //      alignment of u32, if t.ty was aligned, then this must be as well
383                    //
384                    // It's initialized
385                    //      libbpf guarantees this since we've checked the type
386                    //
387                    // The lifetime will match the lifetime of the original t.ty reference.
388                    *u32_ptr
389                }
390            };
391            let encoding = match (int & 0x0f_00_00_00) >> 24 {
392                0b1 => IntEncoding::Signed,
393                0b10 => IntEncoding::Char,
394                0b100 => IntEncoding::Bool,
395                _ => IntEncoding::None,
396            };
397            Ok(Self {
398                source: t,
399                encoding,
400                offset: ((int & 0x00_ff_00_00) >> 24) as u8,
401                bits: (int & 0x00_00_00_ff) as u8,
402            })
403        } else {
404            Err(t)
405        }
406    }
407}
408
409impl<'btf> Deref for Int<'btf> {
410    type Target = BtfType<'btf>;
411    fn deref(&self) -> &Self::Target {
412        &self.source
413    }
414}
415
416// SAFETY: Int has the .size field set.
417impl super::sealed::Sealed for Int<'_> {}
418unsafe impl<'btf> HasSize<'btf> for Int<'btf> {}
419
420// Ptr
421gen_fieldless_concrete_type! {
422    /// A pointer.
423    ///
424    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-ptr)
425    #[doc(alias = "BTF_KIND_PTR")]
426    Ptr with ReferencesType
427}
428
429// Array
430gen_concrete_type! {
431    /// An array.
432    ///
433    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-array)
434    #[doc(alias = "BTF_KIND_ARRAY")]
435    #[doc(alias = "btf_array")]
436    btf_array as Array
437}
438
439impl<'s> Array<'s> {
440    /// The type id of the stored type.
441    #[inline]
442    pub fn ty(&self) -> TypeId {
443        self.ptr.type_.into()
444    }
445
446    /// The type of index used.
447    #[inline]
448    pub fn index_ty(&self) -> TypeId {
449        self.ptr.index_type.into()
450    }
451
452    /// The capacity of the array.
453    #[inline]
454    pub fn capacity(&self) -> usize {
455        self.ptr.nelems as usize
456    }
457
458    /// The type contained in this array.
459    #[inline]
460    pub fn contained_type(&self) -> BtfType<'s> {
461        self.source
462            .source
463            .type_by_id(self.ty())
464            .expect("arrays should always reference an existing type")
465    }
466}
467
468// Struct
469gen_collection_concrete_type! {
470    /// A struct.
471    ///
472    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-struct)
473    #[doc(alias = "BTF_KIND_STRUCT")]
474    btf_member as Struct with HasSize;
475
476    /// A member of a [Struct]
477    #[doc(alias = "btf_member")]
478    struct StructMember<'btf> {
479        /// The member's name
480        pub name: Option<&'btf OsStr>,
481        /// The member's type
482        pub ty: TypeId,
483        /// The attributes of this member.
484        pub attr: MemberAttr,
485    }
486
487    |btf, member, kflag| StructMember {
488        name: btf.name_at(member.name_off),
489        ty: member.type_.into(),
490        attr: MemberAttr::new(kflag, member.offset),
491    }
492}
493
494// Union
495gen_collection_concrete_type! {
496    /// A Union.
497    ///
498    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-union)
499    #[doc(alias = "BTF_KIND_UNION")]
500    btf_member as Union with HasSize;
501
502    /// A member of an [Union]
503    #[doc(alias = "btf_member")]
504    struct UnionMember<'btf> {
505        /// The member's name
506        pub name: Option<&'btf OsStr>,
507        /// The member's type
508        pub ty: TypeId,
509        /// The attributes of this member.
510        pub attr: MemberAttr,
511    }
512
513    |btf, member, kflag| UnionMember {
514        name: btf.name_at(member.name_off),
515        ty: member.type_.into(),
516        attr: MemberAttr::new(kflag, member.offset),
517    }
518}
519
520/// A Composite type, which can be one of a [`Struct`] or a [`Union`].
521///
522/// Sometimes it's not useful to distinguish them, in that case, one can use this
523/// type to inspect any of them.
524#[derive(Clone, Copy, Debug)]
525pub struct Composite<'btf> {
526    source: BtfType<'btf>,
527    /// Whether this type is a struct.
528    pub is_struct: bool,
529    members: &'btf [libbpf_sys::btf_member],
530}
531
532impl<'btf> From<Struct<'btf>> for Composite<'btf> {
533    fn from(s: Struct<'btf>) -> Self {
534        Self {
535            source: s.source,
536            is_struct: true,
537            members: s.members,
538        }
539    }
540}
541
542impl<'btf> From<Union<'btf>> for Composite<'btf> {
543    fn from(s: Union<'btf>) -> Self {
544        Self {
545            source: s.source,
546            is_struct: false,
547            members: s.members,
548        }
549    }
550}
551
552impl<'btf> TryFrom<BtfType<'btf>> for Composite<'btf> {
553    type Error = BtfType<'btf>;
554
555    fn try_from(t: BtfType<'btf>) -> Result<Self, Self::Error> {
556        Struct::try_from(t)
557            .map(Self::from)
558            .or_else(|_| Union::try_from(t).map(Self::from))
559    }
560}
561
562impl<'btf> TryFrom<Composite<'btf>> for Struct<'btf> {
563    type Error = Composite<'btf>;
564
565    fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
566        if value.is_struct {
567            Ok(Self {
568                source: value.source,
569                members: value.members,
570            })
571        } else {
572            Err(value)
573        }
574    }
575}
576
577impl<'btf> TryFrom<Composite<'btf>> for Union<'btf> {
578    type Error = Composite<'btf>;
579
580    fn try_from(value: Composite<'btf>) -> Result<Self, Self::Error> {
581        if !value.is_struct {
582            Ok(Self {
583                source: value.source,
584                members: value.members,
585            })
586        } else {
587            Err(value)
588        }
589    }
590}
591
592impl Composite<'_> {
593    /// Returns whether this composite type is a `union {}`.
594    pub fn is_empty_union(&self) -> bool {
595        !self.is_struct && self.is_empty()
596    }
597}
598
599// Composite
600gen_collection_members_concrete_type! {
601    btf_member as Composite with HasSize;
602
603    /// A member of a [Struct]
604    struct CompositeMember<'btf> {
605        /// The member's name
606        pub name: Option<&'btf OsStr>,
607        /// The member's type
608        pub ty: TypeId,
609        /// If this member is a bifield, these are it's attributes.
610        pub attr: MemberAttr
611    }
612
613    |btf, member, kflag| CompositeMember {
614        name: btf.name_at(member.name_off),
615        ty: member.type_.into(),
616        attr: MemberAttr::new(kflag, member.offset),
617    }
618}
619
620// Enum
621gen_collection_concrete_type! {
622    /// An Enum of at most 32 bits.
623    ///
624    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum)
625    #[doc(alias = "BTF_KIND_ENUM")]
626    btf_enum as Enum with HasSize;
627
628    /// A member of an [Enum]
629    #[doc(alias = "btf_enum")]
630    struct EnumMember<'btf> {
631        /// The name of this enum variant.
632        pub name: Option<&'btf OsStr>,
633        /// The numeric value of this enum variant.
634        pub value: i64,
635    }
636
637    |btf, member, signed| {
638        EnumMember {
639            name: btf.name_at(member.name_off),
640            value: if signed {
641                member.val.into()
642            } else {
643                u32::from_ne_bytes(member.val.to_ne_bytes()).into()
644            }
645        }
646    }
647}
648
649impl Enum<'_> {
650    /// Check whether the enum is signed or not.
651    #[inline]
652    pub fn is_signed(&self) -> bool {
653        self.kind_flag()
654    }
655}
656
657
658// Fwd
659gen_fieldless_concrete_type! {
660    /// A forward declared C type.
661    ///
662    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-fwd)
663    #[doc(alias = "BTF_KIND_FWD")]
664    Fwd
665}
666
667impl Fwd<'_> {
668    /// The kind of C type that is forwardly declared.
669    pub fn kind(&self) -> FwdKind {
670        if self.source.kind_flag() {
671            FwdKind::Union
672        } else {
673            FwdKind::Struct
674        }
675    }
676}
677
678/// The kinds of types that can be forward declared.
679#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
680pub enum FwdKind {
681    /// A struct.
682    Struct,
683    /// A union.
684    Union,
685}
686
687// Typedef
688gen_fieldless_concrete_type! {
689    /// A C typedef.
690    ///
691    /// References the original type.
692    ///
693    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-typedef)
694    #[doc(alias = "BTF_KIND_TYPEDEF")]
695    Typedef with ReferencesType
696}
697
698// Volatile
699gen_fieldless_concrete_type! {
700    /// The volatile modifier.
701    ///
702    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-volatile)
703    #[doc(alias = "BTF_KIND_VOLATILE")]
704    Volatile with ReferencesType
705}
706
707// Const
708gen_fieldless_concrete_type! {
709    /// The const modifier.
710    ///
711    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-const)
712    #[doc(alias = "BTF_KIND_CONST")]
713    Const with ReferencesType
714}
715
716// Restrict
717gen_fieldless_concrete_type! {
718    /// The restrict modifier.
719    ///
720    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-restrict)
721    #[doc(alias = "BTF_KIND_RESTRICT")]
722    Restrict with ReferencesType
723}
724
725// Func
726gen_fieldless_concrete_type! {
727    /// A function.
728    ///
729    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func)
730    #[doc(alias = "BTF_KIND_FUNC")]
731    Func with ReferencesType
732}
733
734impl Func<'_> {
735    /// This function's linkage.
736    #[inline]
737    pub fn linkage(&self) -> Linkage {
738        self.source.vlen().into()
739    }
740}
741
742// FuncProto
743gen_collection_concrete_type! {
744    /// A function prototype.
745    ///
746    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func-proto)
747    #[doc(alias = "BTF_KIND_FUNC_PROTO")]
748    btf_param as FuncProto with ReferencesType;
749
750    /// A parameter of a [`FuncProto`].
751    #[doc(alias = "btf_param")]
752    struct FuncProtoParam<'btf> {
753        /// The parameter's name
754        pub name: Option<&'btf OsStr>,
755        /// The parameter's type
756        pub ty: TypeId,
757    }
758
759    |btf, member| FuncProtoParam {
760        name: btf.name_at(member.name_off),
761        ty: member.type_.into()
762    }
763}
764
765// Var
766gen_concrete_type! {
767    /// A global variable.
768    ///
769    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-var)
770    #[doc(alias = "BTF_KIND_VAR")]
771    #[doc(alias = "btf_var")]
772    btf_var as Var with ReferencesType
773}
774
775impl Var<'_> {
776    /// The kind of linkage this variable has.
777    #[inline]
778    pub fn linkage(&self) -> Linkage {
779        self.ptr.linkage.into()
780    }
781}
782
783// DataSec
784gen_collection_concrete_type! {
785    /// An ELF's data section, such as `.data`, `.bss` or `.rodata`.
786    ///
787    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-datasec)
788    #[doc(alias = "BTF_KIND_DATASEC")]
789    btf_var_secinfo as DataSec with HasSize;
790
791    /// Describes the btf var in a section.
792    ///
793    /// See [`DataSec`].
794    #[doc(alias = "btf_var_secinfo")]
795    struct VarSecInfo {
796        /// The type id of the var
797        pub ty: TypeId,
798        /// The offset in the section
799        pub offset: u32,
800        /// The size of the type.
801        pub size: usize,
802    }
803
804    |_btf, member| VarSecInfo {
805        ty: member.type_.into(),
806        offset: member.offset,
807        size: member.size as usize
808    }
809}
810
811// Float
812gen_fieldless_concrete_type! {
813    /// A floating point number.
814    ///
815    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-float)
816    #[doc(alias = "BTF_KIND_FLOAT")]
817    Float with HasSize
818}
819
820// DeclTag
821gen_concrete_type! {
822    /// A declaration tag.
823    ///
824    /// A custom tag the programmer can attach to a symbol.
825    ///
826    /// See the [clang docs](https://clang.llvm.org/docs/AttributeReference.html#btf-decl-tag) on
827    /// it.
828    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-decl-tag)
829    #[doc(alias = "BTF_KIND_DECL_TAG")]
830    #[doc(alias = "btf_decl_tag")]
831    btf_decl_tag as DeclTag with ReferencesType
832}
833
834impl DeclTag<'_> {
835    /// The component index is present only when the tag points to a struct/union member or a
836    /// function argument.
837    /// And `component_idx` indicates which member or argument, this decl tag refers to.
838    #[inline]
839    pub fn component_index(&self) -> Option<u32> {
840        self.ptr.component_idx.try_into().ok()
841    }
842}
843
844// TypeTag
845gen_fieldless_concrete_type! {
846    /// A type tag.
847    ///
848    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-type-tag)
849    #[doc(alias = "BTF_KIND_TYPE_TAG")]
850    TypeTag with ReferencesType
851}
852
853// Enum64
854gen_collection_concrete_type! {
855    /// An Enum of 64 bits.
856    ///
857    /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum64)
858    #[doc(alias = "BTF_KIND_ENUM64")]
859    btf_enum64 as Enum64 with HasSize;
860
861    /// A member of an [Enum64].
862    #[doc(alias = "btf_enum64")]
863    struct Enum64Member<'btf> {
864        /// The name of this enum variant.
865        pub name: Option<&'btf OsStr>,
866        /// The numeric value of this enum variant.
867        pub value: i128,
868    }
869
870    |btf, member, signed| Enum64Member {
871        name: btf.name_at(member.name_off),
872        value: {
873            let hi: u64 = member.val_hi32.into();
874            let lo: u64 = member.val_lo32.into();
875            let val = (hi << 32) | lo;
876            if signed {
877                i64::from_ne_bytes(val.to_ne_bytes()).into()
878            } else {
879                val.into()
880            }
881        },
882    }
883}
884
885impl Enum64<'_> {
886    /// Check whether the enum is signed or not.
887    #[inline]
888    pub fn is_signed(&self) -> bool {
889        self.kind_flag()
890    }
891}
892
893
894/// A macro that allows matching on the type of a [`BtfType`] as if it was an enum.
895///
896/// Each pattern can be of two types.
897///
898/// ```no_run
899/// use libbpf_rs::btf::BtfType;
900/// use libbpf_rs::btf_type_match;
901///
902/// # fn do_something_with_an_int(i: libbpf_rs::btf::types::Int) -> &'static str { "" }
903/// let ty: BtfType;
904/// # ty = todo!();
905/// btf_type_match!(match ty {
906///     BtfKind::Int(i) => do_something_with_an_int(i),
907///     BtfKind::Struct => "it's a struct",
908///     BtfKind::Union => {
909///         "it's a union"
910///     },
911///     _ => "default",
912/// });
913/// ```
914///
915/// Variable Binding.
916///
917/// ```compile_fail
918///     BtfKind::Int(i) => {
919///         // we can use i here and it will be an `Int`
920///     }
921/// ```
922///
923/// Non-binding.
924///
925/// ```compile_fail
926///     BtfKind::Int => {
927///         // we don't have access to the variable, but we know the scrutinee is an Int
928///     }
929/// ```
930///
931/// Multiple Variants
932/// ```compile_fail
933///     BtfKind::Struct | BtfKind::Union => {
934///         // we don't have access to the variable,
935///         // but we know the scrutinee is either a Struct or a Union
936///     }
937/// ```
938///
939/// Special case for [`Struct`] and [`Union`]: [`Composite`]
940/// ```compile_fail
941///     BtfKind::Composite(c) => {
942///         // we can use `c` as an instance of `Composite`.
943///         // this branch will match if the type is either a Struct or a Union.
944///     }
945/// ```
946// $(BtfKind::$name:ident $(($var:ident))? => $action:expr $(,)?)+
947#[macro_export]
948macro_rules! btf_type_match {
949    // base rule
950    (
951        match $ty:ident {
952            $($pattern:tt)+
953        }
954    ) => {{
955        let ty: $crate::btf::BtfType<'_> = $ty;
956        $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
957    }};
958}
959
960#[doc(hidden)]
961#[macro_export]
962macro_rules! __btf_type_match {
963    /*
964     * Composite special case
965     *
966     * This is similar to simple-match but it's hardcoded for composite which matches both structs
967     * and unions.
968     */
969    (
970        match $ty:ident.kind() { $($p:pat => $a:expr),* }
971        BtfKind::Composite $( ($var:ident) )? => $action:expr,
972        $($rest:tt)*
973    ) => {
974        $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
975            BtfKind::Composite $( ($var) )* => { $action }
976            $($rest)*
977        )
978    };
979    (
980        match $ty:ident.kind() { $($p:pat => $a:expr),* }
981        BtfKind::Composite $(($var:ident))? => $action:block
982        $($rest:tt)*
983    ) => {
984        $crate::__btf_type_match!(match $ty.kind() {
985            $($p => $a,)*
986            $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
987                $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
988                $action
989            }
990        }
991             $($rest)*
992        )
993    };
994    // simple-match: match on simple patterns that use an expression followed by a comma
995    (
996        match $ty:ident.kind() { $($p:pat => $a:expr),* }
997        BtfKind::$name:ident $(($var:ident))? => $action:expr,
998        $($rest:tt)*
999    ) => {
1000        $crate::__btf_type_match!(
1001            match $ty.kind() { $($p => $a),* }
1002            BtfKind::$name $(($var))? => { $action }
1003            $($rest)*
1004        )
1005    };
1006    // simple-match: match on simple patterns that use a block without a comma
1007    (
1008        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1009        BtfKind::$name:ident $(($var:ident))? => $action:block
1010        $($rest:tt)*
1011    ) => {
1012        $crate::__btf_type_match!(match $ty.kind() {
1013            $($p => $a,)*
1014            $crate::btf::BtfKind::$name => {
1015                $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
1016                $action
1017            }
1018        }
1019             $($rest)*
1020        )
1021    };
1022    // or-pattern: match on one or more variants without capturing a variable and using an
1023    //             expression followed by a comma.
1024    (
1025        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1026        $(BtfKind::$name:ident)|+  => $action:expr,
1027        $($rest:tt)*
1028    ) => {
1029        $crate::__btf_type_match!(
1030            match $ty.kind() { $($p => $a),* }
1031            $(BtfKind::$name)|* => { $action }
1032            $($rest)*
1033        )
1034    };
1035    (
1036        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1037        $(BtfKind::$name:ident)|+  => $action:block
1038        $($rest:tt)*
1039    ) => {
1040        $crate::__btf_type_match!(match $ty.kind() {
1041            $($p => $a,)*
1042            $($crate::btf::BtfKind::$name)|* => {
1043                $action
1044            }
1045        }
1046             $($rest)*
1047        )
1048    };
1049    // default match case
1050    //
1051    // we only need the expression case here because this case is not followed by a $rest:tt like
1052    // the others, which let's us use the $(,)? pattern.
1053    (
1054        match $ty:ident.kind() { $($p:pat => $a:expr),* }
1055        _ => $action:expr $(,)?
1056    ) => {
1057        $crate::__btf_type_match!(match $ty.kind() {
1058            $($p => $a,)*
1059            _ => { $action }
1060        }
1061
1062        )
1063    };
1064    // stop case, where the code is actually generated
1065    (match $ty:ident.kind() { $($p:pat => $a:expr),*  } ) => {
1066        match $ty.kind() {
1067            $($p => $a),*
1068        }
1069    }
1070}
1071
1072#[cfg(test)]
1073mod test {
1074    use super::*;
1075
1076    // creates a dummy btftype, not it's not safe to use this type, but it is safe to match on it,
1077    // which is all we need for these tests.
1078    macro_rules! dummy_type {
1079        ($ty:ident) => {
1080            let btf = $crate::Btf {
1081                ptr: std::ptr::NonNull::dangling(),
1082                drop_policy: $crate::btf::DropPolicy::Nothing,
1083                _marker: std::marker::PhantomData,
1084            };
1085            let $ty = BtfType {
1086                type_id: $crate::btf::TypeId::from(1),
1087                name: None,
1088                source: &btf,
1089                ty: &libbpf_sys::btf_type::default(),
1090            };
1091        };
1092    }
1093
1094    fn foo(_: super::Int<'_>) -> &'static str {
1095        "int"
1096    }
1097
1098    #[test]
1099    fn full_switch_case() {
1100        dummy_type!(ty);
1101        btf_type_match!(match ty {
1102            BtfKind::Int(i) => foo(i),
1103            BtfKind::Struct => "it's a struct",
1104            BtfKind::Void => "",
1105            BtfKind::Ptr => "",
1106            BtfKind::Array => "",
1107            BtfKind::Union => "",
1108            BtfKind::Enum => "",
1109            BtfKind::Fwd => "",
1110            BtfKind::Typedef => "",
1111            BtfKind::Volatile => "",
1112            BtfKind::Const => "",
1113            BtfKind::Restrict => "",
1114            BtfKind::Func => "",
1115            BtfKind::FuncProto => "",
1116            BtfKind::Var => "",
1117            BtfKind::DataSec => "",
1118            BtfKind::Float => "",
1119            BtfKind::DeclTag => "",
1120            BtfKind::TypeTag => "",
1121            BtfKind::Enum64 => "",
1122        });
1123    }
1124
1125    #[test]
1126    fn partial_match() {
1127        dummy_type!(ty);
1128        btf_type_match!(match ty {
1129            BtfKind::Int => "int",
1130            _ => "default",
1131        });
1132    }
1133
1134    #[test]
1135    fn or_pattern_match() {
1136        dummy_type!(ty);
1137        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1138        // const | restrict branch.
1139        #[rustfmt::skip]
1140        btf_type_match!(match ty {
1141            BtfKind::Int => "int",
1142            BtfKind::Struct | BtfKind::Union => "composite",
1143            BtfKind::Typedef | BtfKind::Volatile => {
1144                "qualifier"
1145            }
1146            BtfKind::Const | BtfKind::Restrict => {
1147                "const or restrict"
1148            },
1149            _ => "default",
1150        });
1151    }
1152
1153    #[test]
1154    fn match_arm_with_brackets() {
1155        dummy_type!(ty);
1156        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the int
1157        // branch.
1158        #[rustfmt::skip]
1159        btf_type_match!(match ty {
1160            BtfKind::Void => {
1161                "void"
1162            }
1163            BtfKind::Int => {
1164                "int"
1165            },
1166            BtfKind::Struct => "struct",
1167            _ => "default",
1168        });
1169    }
1170
1171    #[test]
1172    fn match_on_composite() {
1173        dummy_type!(ty);
1174        btf_type_match!(match ty {
1175            BtfKind::Composite(c) => c.is_struct,
1176            _ => false,
1177        });
1178        btf_type_match!(match ty {
1179            BtfKind::Composite(c) => {
1180                c.is_struct
1181            }
1182            _ => false,
1183        });
1184        // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1185        // composite branch.
1186        #[rustfmt::skip]
1187        btf_type_match!(match ty {
1188            BtfKind::Composite(c) => {
1189                c.is_struct
1190            },
1191            _ => false,
1192        });
1193    }
1194
1195    #[test]
1196    fn match_arm_with_multiple_statements() {
1197        dummy_type!(ty);
1198
1199        btf_type_match!(match ty {
1200            BtfKind::Int(i) => {
1201                let _ = i;
1202                "int"
1203            }
1204            _ => {
1205                let _ = 1;
1206                "default"
1207            }
1208        });
1209    }
1210
1211    #[test]
1212    fn non_expression_guards() {
1213        dummy_type!(ty);
1214
1215        btf_type_match!(match ty {
1216            BtfKind::Int => {
1217                let _ = 1;
1218                "int"
1219            }
1220            BtfKind::Typedef | BtfKind::Const => {
1221                let _ = 1;
1222                "qualifier"
1223            }
1224            _ => {
1225                let _ = 1;
1226                "default"
1227            }
1228        });
1229
1230        btf_type_match!(match ty {
1231            BtfKind::Int => {
1232                let _ = 1;
1233            }
1234            BtfKind::Typedef | BtfKind::Const => {
1235                let _ = 1;
1236            }
1237            _ => {
1238                let _ = 1;
1239            }
1240        });
1241    }
1242
1243    #[test]
1244    fn linkage_type() {
1245        use std::mem::discriminant;
1246        use Linkage::*;
1247
1248        for t in [Static, Global, Extern, Unknown] {
1249            // check if discriminants match after a roundtrip conversion
1250            assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1251        }
1252    }
1253}