1 //! Wrappers representing concrete btf types.
2 
3 use std::ffi::OsStr;
4 use std::fmt;
5 use std::fmt::Display;
6 use std::ops::Deref;
7 
8 use super::BtfKind;
9 use super::BtfType;
10 use super::HasSize;
11 use super::ReferencesType;
12 use 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.
16 macro_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.
55 macro_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 
114 macro_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 
185 macro_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)]
242 pub 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 
257 impl MemberAttr {
258     #[inline]
new(kflag: bool, offset: u32) -> Self259     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 pub enum Linkage {
280     /// Static linkage
281     Static = 0,
282     /// Global linkage
283     Global,
284     /// External linkage
285     Extern,
286     /// Unknown
287     Unknown,
288 }
289 
290 impl From<u32> for Linkage {
from(value: u32) -> Self291     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 
303 impl From<Linkage> for u32 {
from(value: Linkage) -> Self304     fn from(value: Linkage) -> Self {
305         value as u32
306     }
307 }
308 
309 impl Display for Linkage {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result310     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
325 gen_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)]
336 pub 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)]
349 pub 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 
360 impl<'btf> TryFrom<BtfType<'btf>> for Int<'btf> {
361     type Error = BtfType<'btf>;
362 
try_from(t: BtfType<'btf>) -> Result<Self, Self::Error>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 
406 impl<'btf> Deref for Int<'btf> {
407     type Target = BtfType<'btf>;
deref(&self) -> &Self::Target408     fn deref(&self) -> &Self::Target {
409         &self.source
410     }
411 }
412 
413 // SAFETY: Int has the .size field set.
414 impl super::sealed::Sealed for Int<'_> {}
415 unsafe impl<'btf> HasSize<'btf> for Int<'btf> {}
416 
417 // Ptr
418 gen_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
426 gen_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 
433 impl<'s> Array<'s> {
434     /// The type id of the stored type.
435     #[inline]
ty(&self) -> TypeId436     pub fn ty(&self) -> TypeId {
437         self.ptr.type_.into()
438     }
439 
440     /// The type of index used.
441     #[inline]
index_ty(&self) -> TypeId442     pub fn index_ty(&self) -> TypeId {
443         self.ptr.index_type.into()
444     }
445 
446     /// The capacity of the array.
447     #[inline]
capacity(&self) -> usize448     pub fn capacity(&self) -> usize {
449         self.ptr.nelems as usize
450     }
451 
452     /// The type contained in this array.
453     #[inline]
contained_type(&self) -> BtfType<'s>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
463 gen_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
487 gen_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)]
515 pub 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 
522 impl<'btf> From<Struct<'btf>> for Composite<'btf> {
from(s: Struct<'btf>) -> Self523     fn from(s: Struct<'btf>) -> Self {
524         Self {
525             source: s.source,
526             is_struct: true,
527             members: s.members,
528         }
529     }
530 }
531 
532 impl<'btf> From<Union<'btf>> for Composite<'btf> {
from(s: Union<'btf>) -> Self533     fn from(s: Union<'btf>) -> Self {
534         Self {
535             source: s.source,
536             is_struct: false,
537             members: s.members,
538         }
539     }
540 }
541 
542 impl<'btf> TryFrom<BtfType<'btf>> for Composite<'btf> {
543     type Error = BtfType<'btf>;
544 
try_from(t: BtfType<'btf>) -> Result<Self, Self::Error>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 
552 impl<'btf> TryFrom<Composite<'btf>> for Struct<'btf> {
553     type Error = Composite<'btf>;
554 
try_from(value: Composite<'btf>) -> Result<Self, Self::Error>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 
567 impl<'btf> TryFrom<Composite<'btf>> for Union<'btf> {
568     type Error = Composite<'btf>;
569 
try_from(value: Composite<'btf>) -> Result<Self, Self::Error>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 
582 // Composite
583 gen_collection_members_concrete_type! {
584     btf_member as Composite with HasSize;
585 
586     /// A member of a [Struct]
587     struct CompositeMember<'btf> {
588         /// The member's name
589         pub name: Option<&'btf OsStr>,
590         /// The member's type
591         pub ty: TypeId,
592         /// If this member is a bifield, these are it's attributes.
593         pub attr: MemberAttr
594     }
595 
596     |btf, member, kflag| CompositeMember {
597         name: btf.name_at(member.name_off),
598         ty: member.type_.into(),
599         attr: MemberAttr::new(kflag, member.offset),
600     }
601 }
602 
603 // Enum
604 gen_collection_concrete_type! {
605     /// An Enum of at most 32 bits.
606     ///
607     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum)
608     btf_enum as Enum with HasSize;
609 
610     /// A member of an [Enum]
611     struct EnumMember<'btf> {
612         /// The name of this enum variant.
613         pub name: Option<&'btf OsStr>,
614         /// The numeric value of this enum variant.
615         pub value: i32,
616     }
617 
618     |btf, member| EnumMember {
619         name: btf.name_at(member.name_off),
620         value: member.val,
621     }
622 }
623 
624 // Fwd
625 gen_fieldless_concrete_type! {
626     /// A forward declared C type.
627     ///
628     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-fwd)
629     Fwd
630 }
631 
632 impl Fwd<'_> {
633     /// The kind of C type that is forwardly declared.
kind(&self) -> FwdKind634     pub fn kind(&self) -> FwdKind {
635         if self.source.kind_flag() {
636             FwdKind::Union
637         } else {
638             FwdKind::Struct
639         }
640     }
641 }
642 
643 /// The kinds of types that can be forward declared.
644 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
645 pub enum FwdKind {
646     /// A struct.
647     Struct,
648     /// A union.
649     Union,
650 }
651 
652 // Typedef
653 gen_fieldless_concrete_type! {
654     /// A C typedef.
655     ///
656     /// References the original type.
657     ///
658     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-typedef)
659     Typedef with ReferencesType
660 }
661 
662 // Volatile
663 gen_fieldless_concrete_type! {
664     /// The volatile modifier.
665     ///
666     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-volatile)
667     Volatile with ReferencesType
668 }
669 
670 // Const
671 gen_fieldless_concrete_type! {
672     /// The const modifier.
673     ///
674     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-const)
675     Const with ReferencesType
676 }
677 
678 // Restrict
679 gen_fieldless_concrete_type! {
680     /// The restrict modifier.
681     ///
682     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-restrict)
683     Restrict with ReferencesType
684 }
685 
686 // Func
687 gen_fieldless_concrete_type! {
688     /// A function.
689     ///
690     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func)
691     Func with ReferencesType
692 }
693 
694 impl Func<'_> {
695     /// This function's linkage.
696     #[inline]
linkage(&self) -> Linkage697     pub fn linkage(&self) -> Linkage {
698         self.source.vlen().into()
699     }
700 }
701 
702 // FuncProto
703 gen_collection_concrete_type! {
704     /// A function prototype.
705     ///
706     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-func-proto)
707     btf_param as FuncProto with ReferencesType;
708 
709     /// A parameter of a [FuncProto].
710     struct FuncProtoParam<'btf> {
711         /// The parameter's name
712         pub name: Option<&'btf OsStr>,
713         /// The parameter's type
714         pub ty: TypeId,
715     }
716 
717     |btf, member| FuncProtoParam {
718         name: btf.name_at(member.name_off),
719         ty: member.type_.into()
720     }
721 }
722 
723 // Var
724 gen_concrete_type! {
725     /// A global variable.
726     ///
727     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-var)
728     btf_var as Var with ReferencesType
729 }
730 
731 impl Var<'_> {
732     /// The kind of linkage this variable has.
733     #[inline]
linkage(&self) -> Linkage734     pub fn linkage(&self) -> Linkage {
735         self.ptr.linkage.into()
736     }
737 }
738 
739 // DataSec
740 gen_collection_concrete_type! {
741     /// An ELF's data section, such as `.data`, `.bss` or `.rodata`.
742     ///
743     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-datasec)
744     btf_var_secinfo as DataSec with HasSize;
745 
746     /// Describes the btf var in a section.
747     ///
748     /// See [`DataSec`].
749     struct VarSecInfo {
750         /// The type id of the var
751         pub ty: TypeId,
752         /// The offset in the section
753         pub offset: u32,
754         /// The size of the type.
755         pub size: usize,
756     }
757 
758     |_btf, member| VarSecInfo {
759         ty: member.type_.into(),
760         offset: member.offset,
761         size: member.size as usize
762     }
763 }
764 
765 // Float
766 gen_fieldless_concrete_type! {
767     /// A floating point number.
768     ///
769     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-float)
770     Float with HasSize
771 }
772 
773 // DeclTag
774 gen_concrete_type! {
775     /// A declaration tag.
776     ///
777     /// A custom tag the programmer can attach to a symbol.
778     ///
779     /// See the [clang docs](https://clang.llvm.org/docs/AttributeReference.html#btf-decl-tag) on
780     /// it.
781     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-decl-tag)
782     btf_decl_tag as DeclTag with ReferencesType
783 }
784 
785 impl DeclTag<'_> {
786     /// The component index is present only when the tag points to a struct/union member or a
787     /// function argument.
788     /// And component_idx indicates which member or argument, this decl tag refers to.
789     #[inline]
component_index(&self) -> Option<u32>790     pub fn component_index(&self) -> Option<u32> {
791         self.ptr.component_idx.try_into().ok()
792     }
793 }
794 
795 // TypeTag
796 gen_fieldless_concrete_type! {
797     /// A type tag.
798     ///
799     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-type-tag)
800     TypeTag with ReferencesType
801 }
802 
803 // Enum64
804 gen_collection_concrete_type! {
805     /// An Enum of 64 bits.
806     ///
807     /// See also [libbpf docs](https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-enum64)
808     btf_enum64 as Enum64 with HasSize;
809 
810     /// A member of an [Enum64].
811     struct Enum64Member<'btf> {
812         /// The name of this enum variant.
813         pub name: Option<&'btf OsStr>,
814         /// The numeric value of this enum variant.
815         pub value: u64,
816     }
817 
818     |btf, member| Enum64Member {
819         name: btf.name_at(member.name_off),
820         value: {
821             let hi: u64 = member.val_hi32.into();
822             let lo: u64 = member.val_lo32.into();
823             hi << 32 | lo
824         },
825     }
826 }
827 
828 /// A macro that allows matching on the type of a [`BtfType`] as if it was an enum.
829 ///
830 /// Each pattern can be of two types.
831 ///
832 /// ```no_run
833 /// use libbpf_rs::btf::BtfType;
834 /// use libbpf_rs::btf_type_match;
835 ///
836 /// # fn do_something_with_an_int(i: libbpf_rs::btf::types::Int) -> &'static str { "" }
837 /// let ty: BtfType;
838 /// # ty = todo!();
839 /// btf_type_match!(match ty {
840 ///     BtfKind::Int(i) => do_something_with_an_int(i),
841 ///     BtfKind::Struct => "it's a struct",
842 ///     BtfKind::Union => {
843 ///         "it's a union"
844 ///     },
845 ///     _ => "default",
846 /// });
847 /// ```
848 ///
849 /// Variable Binding.
850 ///
851 /// ```compile_fail
852 ///     BtfKind::Int(i) => {
853 ///         // we can use i here and it will be an `Int`
854 ///     }
855 /// ```
856 ///
857 /// NonBinding.
858 ///
859 /// ```compile_fail
860 ///     BtfKind::Int => {
861 ///         // we don't have access to the variable, but we know the scrutinee is an Int
862 ///     }
863 /// ```
864 ///
865 /// Multiple Variants
866 /// ```compile_fail
867 ///     BtfKind::Struct | BtfKind::Union => {
868 ///         // we don't have access to the variable,
869 ///         // but we know the scrutinee is either a Struct or a Union
870 ///     }
871 /// ```
872 ///
873 /// Special case for [`Struct`] and [`Union`]: [`Composite`]
874 /// ```compile_fail
875 ///     BtfKind::Composite(c) => {
876 ///         // we can use `c` as an instance of `Composite`.
877 ///         // this branch will match if the type is either a Struct or a Union.
878 ///     }
879 /// ```
880 // $(BtfKind::$name:ident $(($var:ident))? => $action:expr $(,)?)+
881 #[macro_export]
882 macro_rules! btf_type_match {
883     // base rule
884     (
885         match $ty:ident {
886             $($pattern:tt)+
887         }
888     ) => {{
889         let ty: $crate::btf::BtfType<'_> = $ty;
890         $crate::__btf_type_match!(match ty.kind() { } $($pattern)*)
891     }};
892 }
893 
894 #[doc(hidden)]
895 #[macro_export]
896 macro_rules! __btf_type_match {
897     /*
898      * Composite special case
899      *
900      * This is similar to simple-match but it's hardcoded for composite which matches both structs
901      * and unions.
902      */
903     (
904         match $ty:ident.kind() { $($p:pat => $a:expr),* }
905         BtfKind::Composite $( ($var:ident) )? => $action:expr,
906         $($rest:tt)*
907     ) => {
908         $crate::__btf_type_match!(match $ty.kind() { $($p => $a,)* }
909             BtfKind::Composite $( ($var) )* => { $action }
910             $($rest)*
911         )
912     };
913     (
914         match $ty:ident.kind() { $($p:pat => $a:expr),* }
915         BtfKind::Composite $(($var:ident))? => $action:block
916         $($rest:tt)*
917     ) => {
918         $crate::__btf_type_match!(match $ty.kind() {
919             $($p => $a,)*
920             $crate::btf::BtfKind::Struct | $crate::btf::BtfKind::Union => {
921                 $(let $var = $crate::btf::types::Composite::try_from($ty).unwrap();)*
922                 $action
923             }
924         }
925              $($rest)*
926         )
927     };
928     // simple-match: match on simple patterns that use an expression followed by a comma
929     (
930         match $ty:ident.kind() { $($p:pat => $a:expr),* }
931         BtfKind::$name:ident $(($var:ident))? => $action:expr,
932         $($rest:tt)*
933     ) => {
934         $crate::__btf_type_match!(
935             match $ty.kind() { $($p => $a),* }
936             BtfKind::$name $(($var))? => { $action }
937             $($rest)*
938         )
939     };
940     // simple-match: match on simple patterns that use a block without a comma
941     (
942         match $ty:ident.kind() { $($p:pat => $a:expr),* }
943         BtfKind::$name:ident $(($var:ident))? => $action:block
944         $($rest:tt)*
945     ) => {
946         $crate::__btf_type_match!(match $ty.kind() {
947             $($p => $a,)*
948             $crate::btf::BtfKind::$name => {
949                 $(let $var = $crate::btf::types::$name::try_from($ty).unwrap();)*
950                 $action
951             }
952         }
953              $($rest)*
954         )
955     };
956     // or-pattern: match on one or more variants without capturing a variable and using an
957     //             expression followed by a comma.
958     (
959         match $ty:ident.kind() { $($p:pat => $a:expr),* }
960         $(BtfKind::$name:ident)|+  => $action:expr,
961         $($rest:tt)*
962     ) => {
963         $crate::__btf_type_match!(
964             match $ty.kind() { $($p => $a),* }
965             $(BtfKind::$name)|* => { $action }
966             $($rest)*
967         )
968     };
969     (
970         match $ty:ident.kind() { $($p:pat => $a:expr),* }
971         $(BtfKind::$name:ident)|+  => $action:block
972         $($rest:tt)*
973     ) => {
974         $crate::__btf_type_match!(match $ty.kind() {
975             $($p => $a,)*
976             $($crate::btf::BtfKind::$name)|* => {
977                 $action
978             }
979         }
980              $($rest)*
981         )
982     };
983     // default match case
984     //
985     // we only need the expression case here because this case is not followed by a $rest:tt like
986     // the others, which let's us use the $(,)? pattern.
987     (
988         match $ty:ident.kind() { $($p:pat => $a:expr),* }
989         _ => $action:expr $(,)?
990     ) => {
991         $crate::__btf_type_match!(match $ty.kind() {
992             $($p => $a,)*
993             _ => { $action }
994         }
995 
996         )
997     };
998     // stop case, where the code is actually generated
999     (match $ty:ident.kind() { $($p:pat => $a:expr),*  } ) => {
1000         match $ty.kind() {
1001             $($p => $a),*
1002         }
1003     }
1004 }
1005 
1006 #[cfg(test)]
1007 mod test {
1008     use super::*;
1009 
1010     // creates a dummy btftype, not it's not safe to use this type, but it is safe to match on it,
1011     // which is all we need for these tests.
1012     macro_rules! dummy_type {
1013         ($ty:ident) => {
1014             let btf = $crate::Btf {
1015                 ptr: std::ptr::NonNull::dangling(),
1016                 drop_policy: $crate::btf::DropPolicy::Nothing,
1017                 _marker: std::marker::PhantomData,
1018             };
1019             let $ty = BtfType {
1020                 type_id: $crate::btf::TypeId::from(1),
1021                 name: None,
1022                 source: &btf,
1023                 ty: &libbpf_sys::btf_type::default(),
1024             };
1025         };
1026     }
1027 
foo(_: super::Int<'_>) -> &'static str1028     fn foo(_: super::Int<'_>) -> &'static str {
1029         "int"
1030     }
1031 
1032     #[test]
full_switch_case()1033     fn full_switch_case() {
1034         dummy_type!(ty);
1035         btf_type_match!(match ty {
1036             BtfKind::Int(i) => foo(i),
1037             BtfKind::Struct => "it's a struct",
1038             BtfKind::Void => "",
1039             BtfKind::Ptr => "",
1040             BtfKind::Array => "",
1041             BtfKind::Union => "",
1042             BtfKind::Enum => "",
1043             BtfKind::Fwd => "",
1044             BtfKind::Typedef => "",
1045             BtfKind::Volatile => "",
1046             BtfKind::Const => "",
1047             BtfKind::Restrict => "",
1048             BtfKind::Func => "",
1049             BtfKind::FuncProto => "",
1050             BtfKind::Var => "",
1051             BtfKind::DataSec => "",
1052             BtfKind::Float => "",
1053             BtfKind::DeclTag => "",
1054             BtfKind::TypeTag => "",
1055             BtfKind::Enum64 => "",
1056         });
1057     }
1058 
1059     #[test]
partial_match()1060     fn partial_match() {
1061         dummy_type!(ty);
1062         btf_type_match!(match ty {
1063             BtfKind::Int => "int",
1064             _ => "default",
1065         });
1066     }
1067 
1068     #[test]
or_pattern_match()1069     fn or_pattern_match() {
1070         dummy_type!(ty);
1071         // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1072         // const | restrict branch.
1073         #[rustfmt::skip]
1074         btf_type_match!(match ty {
1075             BtfKind::Int => "int",
1076             BtfKind::Struct | BtfKind::Union => "composite",
1077             BtfKind::Typedef | BtfKind::Volatile => {
1078                 "qualifier"
1079             }
1080             BtfKind::Const | BtfKind::Restrict => {
1081                 "const or restrict"
1082             },
1083             _ => "default",
1084         });
1085     }
1086 
1087     #[test]
match_arm_with_brackets()1088     fn match_arm_with_brackets() {
1089         dummy_type!(ty);
1090         // we ask rustfmt to not format this block so that we can keep the trailing `,` in the int
1091         // branch.
1092         #[rustfmt::skip]
1093         btf_type_match!(match ty {
1094             BtfKind::Void => {
1095                 "void"
1096             }
1097             BtfKind::Int => {
1098                 "int"
1099             },
1100             BtfKind::Struct => "struct",
1101             _ => "default",
1102         });
1103     }
1104 
1105     #[test]
match_on_composite()1106     fn match_on_composite() {
1107         dummy_type!(ty);
1108         btf_type_match!(match ty {
1109             BtfKind::Composite(c) => c.is_struct,
1110             _ => false,
1111         });
1112         btf_type_match!(match ty {
1113             BtfKind::Composite(c) => {
1114                 c.is_struct
1115             }
1116             _ => false,
1117         });
1118         // we ask rustfmt to not format this block so that we can keep the trailing `,` in the
1119         // composite branch.
1120         #[rustfmt::skip]
1121         btf_type_match!(match ty {
1122             BtfKind::Composite(c) => {
1123                 c.is_struct
1124             },
1125             _ => false,
1126         });
1127     }
1128 
1129     #[test]
match_arm_with_multiple_statements()1130     fn match_arm_with_multiple_statements() {
1131         dummy_type!(ty);
1132 
1133         btf_type_match!(match ty {
1134             BtfKind::Int(i) => {
1135                 let _ = i;
1136                 "int"
1137             }
1138             _ => {
1139                 let _ = 1;
1140                 "default"
1141             }
1142         });
1143     }
1144 
1145     #[test]
non_expression_guards()1146     fn non_expression_guards() {
1147         dummy_type!(ty);
1148 
1149         btf_type_match!(match ty {
1150             BtfKind::Int => {
1151                 let _ = 1;
1152                 "int"
1153             }
1154             BtfKind::Typedef | BtfKind::Const => {
1155                 let _ = 1;
1156                 "qualifier"
1157             }
1158             _ => {
1159                 let _ = 1;
1160                 "default"
1161             }
1162         });
1163 
1164         btf_type_match!(match ty {
1165             BtfKind::Int => {
1166                 let _ = 1;
1167             }
1168             BtfKind::Typedef | BtfKind::Const => {
1169                 let _ = 1;
1170             }
1171             _ => {
1172                 let _ = 1;
1173             }
1174         });
1175     }
1176 
1177     #[test]
linkage_type()1178     fn linkage_type() {
1179         use std::mem::discriminant;
1180         use Linkage::*;
1181 
1182         for t in [Static, Global, Extern, Unknown] {
1183             // check if discriminants match after a roundtrip conversion
1184             assert_eq!(discriminant(&t), discriminant(&Linkage::from(t as u32)));
1185         }
1186     }
1187 }
1188