Skip to main content

aya_friday_obj/btf/
btf.rs

1use std::{
2    borrow::{Cow, ToOwned as _},
3    cell::OnceCell,
4    ffi::{CStr, FromBytesUntilNulError},
5    format, mem, ptr,
6};
7
8use bytes::BufMut as _;
9use log::debug;
10use object::{Endianness, SectionIndex};
11
12use crate::{
13    Object,
14    btf::{
15        Array, BtfEnum, BtfEnum64, BtfKind, BtfMember, BtfType, Const, DataSec, DataSecEntry, Enum,
16        Enum64, Enum64Fallback, Enum64VariantFallback, FuncInfo, FuncLinkage, Int, IntEncoding,
17        LineInfo, Struct, Typedef, Union, Var, VarLinkage,
18        info::{FuncSecInfo, LineSecInfo},
19        relocation::Relocation,
20    },
21    generated::{btf_ext_header, btf_header},
22    util::{HashMap, bytes_of},
23};
24
25pub(crate) const MAX_RESOLVE_DEPTH: usize = 32;
26pub(crate) const MAX_SPEC_LEN: usize = 64;
27
28/// The error type returned when `BTF` operations fail.
29#[derive(thiserror::Error, Debug)]
30pub enum BtfError {
31    /// Error parsing file
32    #[error("error parsing {path}")]
33    FileError {
34        /// file path
35        path: std::path::PathBuf,
36        /// source of the error
37        #[source]
38        error: std::io::Error,
39    },
40
41    /// Error parsing BTF header
42    #[error("error parsing BTF header")]
43    InvalidHeader,
44
45    /// invalid BTF type info segment
46    #[error("invalid BTF type info segment")]
47    InvalidTypeInfo,
48
49    /// invalid BTF relocation info segment
50    #[error("invalid BTF relocation info segment")]
51    InvalidRelocationInfo,
52
53    /// invalid BTF type kind
54    #[error("invalid BTF type kind `{kind}`")]
55    InvalidTypeKind {
56        /// type kind
57        kind: u32,
58    },
59
60    /// invalid BTF relocation kind
61    #[error("invalid BTF relocation kind `{kind}`")]
62    InvalidRelocationKind {
63        /// type kind
64        kind: u32,
65    },
66
67    /// invalid BTF string offset
68    #[error("invalid BTF string offset: {offset}")]
69    InvalidStringOffset {
70        /// offset
71        offset: usize,
72    },
73
74    /// invalid BTF info
75    #[error("invalid BTF info, offset: {offset} len: {len} section_len: {section_len}")]
76    InvalidInfo {
77        /// offset
78        offset: usize,
79        /// length
80        len: usize,
81        /// section length
82        section_len: usize,
83    },
84
85    /// invalid BTF line infos
86    #[error("invalid BTF line info, offset: {offset} len: {len} section_len: {section_len}")]
87    InvalidLineInfo {
88        /// offset
89        offset: usize,
90        /// length
91        len: usize,
92        /// section length
93        section_len: usize,
94    },
95
96    /// unknown BTF type id
97    #[error("unknown BTF type id `{type_id}`")]
98    UnknownBtfType {
99        /// type id
100        type_id: u32,
101    },
102
103    /// unexpected btf type id
104    #[error("unexpected BTF type id `{type_id}`")]
105    UnexpectedBtfType {
106        /// type id
107        type_id: u32,
108    },
109
110    /// unknown BTF type
111    #[error("unknown BTF type `{type_name}`")]
112    UnknownBtfTypeName {
113        /// type name
114        type_name: String,
115    },
116
117    /// maximum depth reached resolving BTF type
118    #[error("maximum depth reached resolving BTF type")]
119    MaximumTypeDepthReached {
120        /// type id
121        type_id: u32,
122    },
123
124    /// Loading the btf failed
125    #[error("the BPF_BTF_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
126    LoadError {
127        /// The [`std::io::Error`] returned by the `BPF_BTF_LOAD` syscall.
128        #[source]
129        io_error: std::io::Error,
130        /// The error log produced by the kernel verifier.
131        verifier_log: crate::VerifierLog,
132    },
133
134    /// offset not found for symbol
135    #[error("Offset not found for symbol `{symbol_name}`")]
136    SymbolOffsetNotFound {
137        /// name of the symbol
138        symbol_name: String,
139    },
140
141    /// btf type that is not VAR found in DATASEC
142    #[error("BTF type that is not VAR was found in DATASEC")]
143    InvalidDatasec,
144
145    /// unable to determine the size of section
146    #[error("Unable to determine the size of section `{section_name}`")]
147    UnknownSectionSize {
148        /// name of the section
149        section_name: String,
150    },
151
152    /// unable to get symbol name
153    #[error("Unable to get symbol name")]
154    InvalidSymbolName,
155
156    /// Inner map definition cannot be pinned.
157    #[error("BTF map `{name}`: inner map definition cannot be pinned")]
158    InnerMapCannotBePinned {
159        /// The name of the map with the invalid definition.
160        name: String,
161    },
162
163    /// Multi-level map-in-map is not supported.
164    #[error("BTF map `{name}`: multi-level map-in-map is not supported")]
165    MultiLevelMapInMapNotSupported {
166        /// The name of the map with the invalid definition.
167        name: String,
168    },
169
170    /// The `values` spec must be a zero-sized array.
171    #[error("BTF map `{name}`: `values` spec is not a zero-sized array")]
172    InvalidValuesSpec {
173        /// The name of the map with the invalid definition.
174        name: String,
175    },
176}
177
178/// Available BTF features
179#[derive(Default, Debug)]
180pub struct BtfFeatures {
181    btf_func: bool,
182    btf_func_global: bool,
183    btf_datasec: bool,
184    btf_datasec_zero: bool,
185    btf_float: bool,
186    btf_decl_tag: bool,
187    btf_type_tag: bool,
188    btf_enum64: bool,
189}
190
191impl BtfFeatures {
192    #[doc(hidden)]
193    #[expect(
194        clippy::fn_params_excessive_bools,
195        reason = "this interface is terrible"
196    )]
197    #[expect(clippy::too_many_arguments, reason = "this interface is terrible")]
198    pub const fn new(
199        btf_func: bool,
200        btf_func_global: bool,
201        btf_datasec: bool,
202        btf_datasec_zero: bool,
203        btf_float: bool,
204        btf_decl_tag: bool,
205        btf_type_tag: bool,
206        btf_enum64: bool,
207    ) -> Self {
208        Self {
209            btf_func,
210            btf_func_global,
211            btf_datasec,
212            btf_datasec_zero,
213            btf_float,
214            btf_decl_tag,
215            btf_type_tag,
216            btf_enum64,
217        }
218    }
219
220    /// Returns true if `BTF_TYPE_FUNC` is supported.
221    pub const fn btf_func(&self) -> bool {
222        self.btf_func
223    }
224
225    /// Returns true if `BTF_TYPE_FUNC_GLOBAL` is supported.
226    pub const fn btf_func_global(&self) -> bool {
227        self.btf_func_global
228    }
229
230    /// Returns true if `BTF_TYPE_DATASEC` is supported.
231    pub const fn btf_datasec(&self) -> bool {
232        self.btf_datasec
233    }
234
235    /// Returns true if zero-length `DATASec` entries are accepted.
236    pub const fn btf_datasec_zero(&self) -> bool {
237        self.btf_datasec_zero
238    }
239
240    /// Returns true if `BTF_FLOAT` is supported.
241    pub const fn btf_float(&self) -> bool {
242        self.btf_float
243    }
244
245    /// Returns true if `BTF_DECL_TAG` is supported.
246    pub const fn btf_decl_tag(&self) -> bool {
247        self.btf_decl_tag
248    }
249
250    /// Returns true if `BTF_TYPE_TAG` is supported.
251    pub const fn btf_type_tag(&self) -> bool {
252        self.btf_type_tag
253    }
254
255    /// Returns true if `BTF_KIND_FUNC_PROTO` is supported.
256    pub const fn btf_kind_func_proto(&self) -> bool {
257        self.btf_func && self.btf_decl_tag
258    }
259
260    /// Returns true if `BTF_KIND_ENUM64` is supported.
261    pub const fn btf_enum64(&self) -> bool {
262        self.btf_enum64
263    }
264}
265
266/// BPF Type Format metadata.
267///
268/// BTF is a kind of debug metadata that allows eBPF programs compiled against one kernel version
269/// to be loaded into different kernel versions.
270///
271/// Aya automatically loads BTF metadata if you use `Ebpf::load_file`. You
272/// only need to explicitly use this type if you want to load BTF from a non-standard
273/// location or if you are using `Ebpf::load`.
274#[derive(Clone, Debug)]
275pub struct Btf {
276    header: btf_header,
277    strings: Vec<u8>,
278    types: BtfTypes,
279    _endianness: Endianness,
280}
281
282fn add_type(header: &mut btf_header, types: &mut BtfTypes, btf_type: BtfType) -> u32 {
283    let size = btf_type.type_info_size() as u32;
284    let type_id = types.len();
285    types.push(btf_type);
286    header.type_len += size;
287    header.str_off += size;
288    type_id as u32
289}
290
291impl Btf {
292    /// Creates a new empty instance with its header initialized
293    pub fn new() -> Self {
294        Self {
295            header: btf_header {
296                magic: 0xeb9f,
297                version: 0x01,
298                flags: 0x00,
299                hdr_len: 0x18,
300                type_off: 0x00,
301                type_len: 0x00,
302                str_off: 0x00,
303                str_len: 0x00,
304            },
305            strings: vec![0],
306            types: BtfTypes::default(),
307            _endianness: Endianness::default(),
308        }
309    }
310
311    pub(crate) const fn is_empty(&self) -> bool {
312        // the first one is awlays BtfType::Unknown
313        self.types.types.len() < 2
314    }
315
316    pub(crate) fn types(&self) -> impl Iterator<Item = &BtfType> {
317        self.types.types.iter()
318    }
319
320    /// Adds a string to BTF metadata, returning an offset
321    pub fn add_string(&mut self, name: &str) -> u32 {
322        let str = name.bytes().chain(core::iter::once(0));
323        let name_offset = self.strings.len();
324        self.strings.extend(str);
325        self.header.str_len = self.strings.len() as u32;
326        name_offset as u32
327    }
328
329    /// Adds a type to BTF metadata, returning a type id
330    pub fn add_type(&mut self, btf_type: BtfType) -> u32 {
331        add_type(&mut self.header, &mut self.types, btf_type)
332    }
333
334    /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
335    pub fn from_sys_fs() -> Result<Self, BtfError> {
336        Self::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
337    }
338
339    /// Loads BTF metadata from the given `path`.
340    pub fn parse_file<P: AsRef<std::path::Path>>(
341        path: P,
342        endianness: Endianness,
343    ) -> Result<Self, BtfError> {
344        use std::{borrow::ToOwned as _, fs};
345        let path = path.as_ref();
346        Self::parse(
347            &fs::read(path).map_err(|error| BtfError::FileError {
348                path: path.to_owned(),
349                error,
350            })?,
351            endianness,
352        )
353    }
354
355    /// Parses BTF from binary data of the given endianness
356    pub fn parse(data: &[u8], endianness: Endianness) -> Result<Self, BtfError> {
357        if data.len() < size_of::<btf_header>() {
358            return Err(BtfError::InvalidHeader);
359        }
360
361        // safety: btf_header is POD so read_unaligned is safe
362        let header = unsafe { read_btf_header(data) };
363
364        let str_off = header.hdr_len as usize + header.str_off as usize;
365        let str_len = header.str_len as usize;
366        if str_off + str_len > data.len() {
367            return Err(BtfError::InvalidHeader);
368        }
369
370        let strings = data[str_off..str_off + str_len].to_vec();
371        let types = Self::read_type_info(&header, data, endianness)?;
372
373        Ok(Self {
374            header,
375            strings,
376            types,
377            _endianness: endianness,
378        })
379    }
380
381    fn read_type_info(
382        header: &btf_header,
383        data: &[u8],
384        endianness: Endianness,
385    ) -> Result<BtfTypes, BtfError> {
386        let hdr_len = header.hdr_len as usize;
387        let type_off = header.type_off as usize;
388        let type_len = header.type_len as usize;
389        let base = hdr_len + type_off;
390        if base + type_len > data.len() {
391            return Err(BtfError::InvalidTypeInfo);
392        }
393
394        let mut data = &data[base..base + type_len];
395        let mut types = BtfTypes::default();
396        while !data.is_empty() {
397            // Safety:
398            // read() reads POD values from ELF, which is sound, but the values can still contain
399            // internally inconsistent values (like out of bound offsets and such).
400            let ty = unsafe { BtfType::read(data, endianness)? };
401            data = &data[ty.type_info_size()..];
402            types.push(ty);
403        }
404        Ok(types)
405    }
406
407    pub(crate) fn string_at(&self, offset: u32) -> Result<Cow<'_, str>, BtfError> {
408        let btf_header {
409            hdr_len,
410            mut str_off,
411            str_len,
412            ..
413        } = self.header;
414        str_off += hdr_len;
415        if offset >= str_off + str_len {
416            return Err(BtfError::InvalidStringOffset {
417                offset: offset as usize,
418            });
419        }
420
421        let offset = offset as usize;
422
423        let s = CStr::from_bytes_until_nul(&self.strings[offset..])
424            .map_err(|FromBytesUntilNulError { .. }| BtfError::InvalidStringOffset { offset })?;
425
426        Ok(s.to_string_lossy())
427    }
428
429    pub(crate) fn type_by_id(&self, type_id: u32) -> Result<&BtfType, BtfError> {
430        self.types.type_by_id(type_id)
431    }
432
433    pub(crate) fn resolve_type(&self, root_type_id: u32) -> Result<u32, BtfError> {
434        self.types.resolve_type(root_type_id)
435    }
436
437    pub(crate) fn type_name(&self, ty: &BtfType) -> Result<Cow<'_, str>, BtfError> {
438        self.string_at(ty.name_offset())
439    }
440
441    pub(crate) fn err_type_name(&self, ty: &BtfType) -> Option<String> {
442        self.string_at(ty.name_offset()).ok().map(String::from)
443    }
444
445    /// Returns a type id matching the type name and [`BtfKind`].
446    pub fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result<u32, BtfError> {
447        for (type_id, ty) in self.types().enumerate() {
448            if ty.kind() != kind {
449                continue;
450            }
451            if self.type_name(ty)? == name {
452                return Ok(type_id as u32);
453            }
454        }
455
456        Err(BtfError::UnknownBtfTypeName {
457            type_name: name.to_owned(),
458        })
459    }
460
461    pub(crate) fn type_size(&self, root_type_id: u32) -> Result<usize, BtfError> {
462        let mut type_id = root_type_id;
463        let mut n_elems = 1;
464        for () in core::iter::repeat_n((), MAX_RESOLVE_DEPTH) {
465            let ty = self.types.type_by_id(type_id)?;
466            let size = match ty {
467                BtfType::Array(Array { array, .. }) => {
468                    n_elems = array.len;
469                    type_id = array.element_type;
470                    continue;
471                }
472                other => {
473                    if let Some(size) = other.size() {
474                        size
475                    } else if let Some(next) = other.btf_type() {
476                        type_id = next;
477                        continue;
478                    } else {
479                        return Err(BtfError::UnexpectedBtfType { type_id });
480                    }
481                }
482            };
483            return Ok((size * n_elems) as usize);
484        }
485
486        Err(BtfError::MaximumTypeDepthReached {
487            type_id: root_type_id,
488        })
489    }
490
491    /// Encodes the metadata as BTF format
492    pub fn to_bytes(&self) -> Vec<u8> {
493        // Safety: btf_header is POD
494        let mut buf = unsafe { bytes_of::<btf_header>(&self.header).to_vec() };
495        buf.extend(self.types.to_bytes());
496        buf.put(self.strings.as_slice());
497        buf
498    }
499
500    // This follows the same logic as libbpf's bpf_object__sanitize_btf() function.
501    // https://github.com/libbpf/libbpf/blob/05f94ddbb837f5f4b3161e341eed21be307eaa04/src/libbpf.c#L2701
502    //
503    // Fixup: The loader needs to adjust values in the BTF before it's loaded into the kernel.
504    // Sanitize: Replace an unsupported BTF type with a placeholder type.
505    //
506    // In addition to the libbpf logic, it performs some fixups to the BTF generated by bpf-linker
507    // for Aya programs. These fixups are gradually moving into bpf-linker itself.
508    pub(crate) fn fixup_and_sanitize(
509        &mut self,
510        section_infos: &HashMap<String, (SectionIndex, u64)>,
511        symbol_offsets: &HashMap<String, u64>,
512        features: &BtfFeatures,
513    ) -> Result<(), BtfError> {
514        let enum64_placeholder_id = OnceCell::new();
515        let filler_var_id = OnceCell::new();
516        let mut types = mem::take(&mut self.types);
517        for i in 0..types.types.len() {
518            let t = &mut types.types[i];
519            let kind = t.kind();
520            match t {
521                // Fixup PTR for Rust.
522                //
523                // LLVM emits names for Rust pointer types, which the kernel doesn't like.
524                // While I figure out if this needs fixing in the Kernel or LLVM, we'll
525                // do a fixup here.
526                BtfType::Ptr(ptr) => {
527                    ptr.name_offset = 0;
528                }
529                // Sanitize VAR if they are not supported.
530                BtfType::Var(v) if !features.btf_datasec => {
531                    *t = BtfType::Int(Int::new(v.name_offset, 1, IntEncoding::None, 0));
532                }
533                // Sanitize DATASEC if they are not supported.
534                BtfType::DataSec(d) if !features.btf_datasec => {
535                    debug!("{kind}: not supported. replacing with STRUCT");
536
537                    // STRUCT aren't allowed to have "." in their name, fixup this if needed.
538                    let mut name_offset = d.name_offset;
539                    let name = self.string_at(name_offset)?;
540
541                    // Handle any "." characters in struct names.
542                    // Example: ".maps"
543                    let fixed_name = name.replace('.', "_");
544                    if fixed_name != name {
545                        name_offset = self.add_string(&fixed_name);
546                    }
547
548                    let entries = mem::take(&mut d.entries);
549
550                    let members = entries
551                        .iter()
552                        .map(|e| {
553                            let mt = types.type_by_id(e.btf_type).unwrap();
554                            BtfMember {
555                                name_offset: mt.name_offset(),
556                                btf_type: e.btf_type,
557                                offset: e.offset * 8,
558                            }
559                        })
560                        .collect();
561
562                    // Must reborrow here because we borrow `types` immutably above.
563                    let t = &mut types.types[i];
564                    *t = BtfType::Struct(Struct::new(name_offset, members, entries.len() as u32));
565                }
566                // Fixup DATASEC.
567                //
568                // DATASEC sizes aren't always set by LLVM so we need to fix them
569                // here before loading the btf to the kernel.
570                BtfType::DataSec(d) if features.btf_datasec => {
571                    // Start DataSec Fixups
572                    let name = self.string_at(d.name_offset)?;
573                    let name = name.into_owned();
574
575                    // Handle any "/" characters in section names.
576                    // Example: "maps/hashmap"
577                    let fixed_name = name.replace('/', ".");
578                    if fixed_name != name {
579                        d.name_offset = self.add_string(&fixed_name);
580                    }
581
582                    // There are some cases when the compiler does indeed populate the size.
583                    if d.size > 0 {
584                        debug!("{kind} {name}: size fixup not required");
585                    } else {
586                        // We need to get the size of the section from the ELF file.
587                        // Fortunately, we cached these when parsing it initially
588                        // and we can this up by name in section_infos.
589                        let Some((_, size)) = section_infos.get(&name) else {
590                            return Err(BtfError::UnknownSectionSize { section_name: name });
591                        };
592                        debug!("{kind} {name}: fixup size to {size}");
593                        d.size = *size as u32;
594
595                        // The Vec<btf_var_secinfo> contains BTF_KIND_VAR sections
596                        // that need to have their offsets adjusted. To do this,
597                        // we need to get the offset from the ELF file.
598                        // This was also cached during initial parsing and
599                        // we can query by name in symbol_offsets.
600                        let old_size = d.type_info_size();
601                        let mut entries = mem::take(&mut d.entries);
602                        let mut section_size = d.size;
603                        let name_offset = d.name_offset;
604
605                        // Kernels before 5.12 reject zero-length DATASEC. See
606                        // https://github.com/torvalds/linux/commit/13ca51d5eb358edcb673afccb48c3440b9fda21b.
607                        if entries.is_empty() && !features.btf_datasec_zero {
608                            let filler_var_id = *filler_var_id.get_or_init(|| {
609                                let filler_type_name = self.add_string("__aya_datasec_filler_type");
610                                let filler_type_id = add_type(
611                                    &mut self.header,
612                                    &mut types,
613                                    BtfType::Int(Int::new(
614                                        filler_type_name,
615                                        1,
616                                        IntEncoding::None,
617                                        0,
618                                    )),
619                                );
620
621                                let filler_var_name = self.add_string("__aya_datasec_filler");
622                                add_type(
623                                    &mut self.header,
624                                    &mut types,
625                                    BtfType::Var(Var::new(
626                                        filler_var_name,
627                                        filler_type_id,
628                                        VarLinkage::Static,
629                                    )),
630                                )
631                            });
632                            let filler_len = section_size.max(1);
633                            debug!(
634                                "{kind} {name}: injecting filler entry for zero-length DATASEC (len={filler_len})"
635                            );
636                            entries.push(DataSecEntry {
637                                btf_type: filler_var_id,
638                                offset: 0,
639                                size: filler_len,
640                            });
641                            if section_size == 0 {
642                                section_size = filler_len;
643                            }
644                        }
645
646                        for e in &mut entries {
647                            if let BtfType::Var(var) = types.type_by_id(e.btf_type)? {
648                                let var_name = self.string_at(var.name_offset)?;
649                                if var.linkage == VarLinkage::Static {
650                                    debug!("{kind} {name}: VAR {var_name}: fixup not required");
651                                    continue;
652                                }
653
654                                let Some(offset) = symbol_offsets.get(var_name.as_ref()) else {
655                                    return Err(BtfError::SymbolOffsetNotFound {
656                                        symbol_name: var_name.into_owned(),
657                                    });
658                                };
659                                e.offset = *offset as u32;
660                                debug!("{kind} {name}: VAR {var_name}: fixup offset {offset}");
661                            } else {
662                                return Err(BtfError::InvalidDatasec);
663                            }
664                        }
665                        let fixed_section = DataSec::new(name_offset, entries, section_size);
666                        let new_size = fixed_section.type_info_size();
667                        if new_size != old_size {
668                            self.header.type_len =
669                                self.header.type_len - old_size as u32 + new_size as u32;
670                            self.header.str_off = self.header.type_len;
671                        }
672
673                        // Must reborrow here because we borrow `types` above.
674                        let t = &mut types.types[i];
675                        *t = BtfType::DataSec(fixed_section);
676                    }
677                }
678                // Fixup FUNC_PROTO.
679                BtfType::FuncProto(ty) if features.btf_func => {
680                    for (i, param) in ty.params.iter_mut().enumerate() {
681                        if param.name_offset == 0 && param.btf_type != 0 {
682                            param.name_offset = self.add_string(&format!("param{i}"));
683                        }
684                    }
685                }
686                // Sanitize FUNC_PROTO.
687                BtfType::FuncProto(ty) if !features.btf_func => {
688                    debug!("{kind}: not supported. replacing with ENUM");
689                    let members: Vec<BtfEnum> = ty
690                        .params
691                        .iter()
692                        .map(|p| BtfEnum {
693                            name_offset: p.name_offset,
694                            value: p.btf_type,
695                        })
696                        .collect();
697                    let enum_type = BtfType::Enum(Enum::new(ty.name_offset, false, members));
698                    *t = enum_type;
699                }
700                // Sanitize FUNC.
701                BtfType::Func(ty) => {
702                    let name = self.string_at(ty.name_offset)?;
703                    // Sanitize FUNC.
704                    if !features.btf_func {
705                        debug!("{kind}: not supported. replacing with TYPEDEF");
706                        *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type));
707                    } else if !features.btf_func_global
708                        || name == "memset"
709                        || name == "memcpy"
710                        || name == "memmove"
711                        || name == "memcmp"
712                    {
713                        // Sanitize BTF_FUNC_GLOBAL when not supported and ensure that
714                        // memory builtins are marked as static. Globals are type checked
715                        // and verified separately from their callers, while instead we
716                        // want tracking info (eg bound checks) to be propagated to the
717                        // memory builtins.
718                        if ty.linkage() == FuncLinkage::Global {
719                            if features.btf_func_global {
720                                debug!("changing FUNC {name} linkage to BTF_FUNC_STATIC");
721                            } else {
722                                debug!(
723                                    "{kind}: BTF_FUNC_GLOBAL not supported. replacing with BTF_FUNC_STATIC",
724                                );
725                            }
726                            ty.set_linkage(FuncLinkage::Static);
727                        }
728                    }
729                }
730                // Sanitize FLOAT.
731                BtfType::Float(ty) if !features.btf_float => {
732                    debug!("{kind}: not supported. replacing with STRUCT");
733                    *t = BtfType::Struct(Struct::new(0, vec![], ty.size));
734                }
735                // Sanitize DECL_TAG.
736                BtfType::DeclTag(ty) if !features.btf_decl_tag => {
737                    debug!("{kind}: not supported. replacing with INT");
738                    *t = BtfType::Int(Int::new(ty.name_offset, 1, IntEncoding::None, 0));
739                }
740                // Sanitize TYPE_TAG.
741                BtfType::TypeTag(ty) if !features.btf_type_tag => {
742                    debug!("{kind}: not supported. replacing with CONST");
743                    *t = BtfType::Const(Const::new(ty.btf_type));
744                }
745                // Sanitize Signed ENUMs.
746                BtfType::Enum(ty) if !features.btf_enum64 && ty.is_signed() => {
747                    debug!("{kind}: signed ENUMs not supported. Marking as unsigned");
748                    ty.set_signed(false);
749                }
750                // Sanitize ENUM64.
751                BtfType::Enum64(ty) if !features.btf_enum64 => {
752                    // Kernels before 6.0 do not support ENUM64. See
753                    // https://github.com/torvalds/linux/commit/6089fb325cf737eeb2c4d236c94697112ca860da.
754                    debug!("{kind}: not supported. replacing with UNION");
755
756                    // `ty` is borrowed from `types` and we use that borrow
757                    // below, so we must not borrow it again in the
758                    // get_or_init closure.
759                    let is_signed = ty.is_signed();
760                    let Enum64 {
761                        name_offset,
762                        size,
763                        variants,
764                        ..
765                    } = ty;
766                    let (name_offset, size, variants) = (*name_offset, *size, mem::take(variants));
767
768                    let fallback = Enum64Fallback {
769                        signed: is_signed,
770                        variants: variants
771                            .iter()
772                            .copied()
773                            .map(
774                                |BtfEnum64 {
775                                     name_offset,
776                                     value_high,
777                                     value_low,
778                                 }| Enum64VariantFallback {
779                                    name_offset,
780                                    value: (u64::from(value_high) << 32) | u64::from(value_low),
781                                },
782                            )
783                            .collect(),
784                    };
785
786                    // The rewritten UNION still needs a concrete member type. Share a single
787                    // synthetic INT placeholder between every downgraded ENUM64.
788                    let placeholder_id = enum64_placeholder_id.get_or_init(|| {
789                        let placeholder_name = self.add_string("enum64_placeholder");
790                        add_type(
791                            &mut self.header,
792                            &mut types,
793                            BtfType::Int(Int::new(placeholder_name, 1, IntEncoding::None, 0)),
794                        )
795                    });
796                    let members: Vec<BtfMember> = variants
797                        .iter()
798                        .map(|v| BtfMember {
799                            name_offset: v.name_offset,
800                            btf_type: *placeholder_id,
801                            offset: 0,
802                        })
803                        .collect();
804
805                    // Must reborrow here because we borrow `types` above.
806                    let t = &mut types.types[i];
807                    *t = BtfType::Union(Union::new(name_offset, size, members, Some(fallback)));
808                }
809                // The type does not need fixing up or sanitization.
810                _ => {}
811            }
812        }
813        self.types = types;
814        Ok(())
815    }
816}
817
818impl Default for Btf {
819    fn default() -> Self {
820        Self::new()
821    }
822}
823
824impl Object {
825    /// Fixes up and sanitizes BTF data.
826    ///
827    /// Mostly, it removes unsupported types and works around LLVM behaviours.
828    pub fn fixup_and_sanitize_btf(
829        &mut self,
830        features: &BtfFeatures,
831    ) -> Result<Option<&Btf>, BtfError> {
832        if let Some(obj_btf) = &mut self.btf {
833            if obj_btf.is_empty() {
834                return Ok(None);
835            }
836            // fixup btf
837            obj_btf.fixup_and_sanitize(
838                &self.section_infos,
839                &self.symbol_offset_by_name,
840                features,
841            )?;
842            Ok(Some(obj_btf))
843        } else {
844            Ok(None)
845        }
846    }
847}
848
849const unsafe fn read_btf_header(data: &[u8]) -> btf_header {
850    // Safety: Btf_header is POD so read_unaligned is safe
851    unsafe { ptr::read_unaligned(data.as_ptr().cast()) }
852}
853
854/// Data in the `.BTF.ext` section
855#[derive(Debug, Clone)]
856pub struct BtfExt {
857    data: Vec<u8>,
858    _endianness: Endianness,
859    relocations: Vec<(u32, Vec<Relocation>)>,
860    header: btf_ext_header,
861    func_info_rec_size: usize,
862    pub(crate) func_info: FuncInfo,
863    line_info_rec_size: usize,
864    pub(crate) line_info: LineInfo,
865    core_relo_rec_size: usize,
866}
867
868impl BtfExt {
869    pub(crate) fn parse(data: &[u8], endianness: Endianness, btf: &Btf) -> Result<Self, BtfError> {
870        #[repr(C)]
871        #[derive(Debug, Copy, Clone)]
872        struct MinimalHeader {
873            pub magic: u16,
874            pub version: u8,
875            pub flags: u8,
876            pub hdr_len: u32,
877        }
878
879        if data.len() < size_of::<MinimalHeader>() {
880            return Err(BtfError::InvalidHeader);
881        }
882
883        let header = {
884            // first find the actual size of the header by converting into the minimal valid header
885            // Safety: MinimalHeader is POD so read_unaligned is safe
886            let minimal_header = unsafe {
887                ptr::read_unaligned::<MinimalHeader>(data.as_ptr().cast::<MinimalHeader>())
888            };
889
890            let len_to_read = minimal_header.hdr_len as usize;
891
892            // prevent invalid input from causing UB
893            if data.len() < len_to_read {
894                return Err(BtfError::InvalidHeader);
895            }
896
897            // forwards compatibility: if newer headers are bigger
898            // than the pre-generated btf_ext_header we should only
899            // read up to btf_ext_header
900            let len_to_read = len_to_read.min(size_of::<btf_ext_header>());
901
902            // now create our full-fledge header; but start with it
903            // zeroed out so unavailable fields stay as zero on older
904            // BTF.ext sections
905            let mut header = mem::MaybeUninit::<btf_ext_header>::zeroed();
906            // Safety: we have checked that len_to_read is less than
907            // size_of::<btf_ext_header> and less than
908            // data.len(). Additionally, we know that the header has
909            // been initialized so it's safe to call for assume_init.
910            unsafe {
911                ptr::copy(data.as_ptr(), header.as_mut_ptr().cast::<u8>(), len_to_read);
912                header.assume_init()
913            }
914        };
915
916        let btf_ext_header {
917            hdr_len,
918            func_info_off,
919            func_info_len,
920            line_info_off,
921            line_info_len,
922            core_relo_off,
923            core_relo_len,
924            ..
925        } = header;
926
927        let rec_size = |offset, len| {
928            let offset = hdr_len as usize + offset as usize;
929            let len = len as usize;
930            // check that there's at least enough space for the `rec_size` field
931            if (len > 0 && len < 4) || offset + len > data.len() {
932                return Err(BtfError::InvalidInfo {
933                    offset,
934                    len,
935                    section_len: data.len(),
936                });
937            }
938            let read_u32 = if endianness == Endianness::Little {
939                u32::from_le_bytes
940            } else {
941                u32::from_be_bytes
942            };
943            Ok(if len > 0 {
944                read_u32(data[offset..offset + 4].try_into().unwrap()) as usize
945            } else {
946                0
947            })
948        };
949
950        let mut ext = Self {
951            header,
952            relocations: Vec::new(),
953            func_info: FuncInfo::new(),
954            line_info: LineInfo::new(),
955            func_info_rec_size: rec_size(func_info_off, func_info_len)?,
956            line_info_rec_size: rec_size(line_info_off, line_info_len)?,
957            core_relo_rec_size: rec_size(core_relo_off, core_relo_len)?,
958            data: data.to_vec(),
959            _endianness: endianness,
960        };
961
962        let func_info_rec_size = ext.func_info_rec_size;
963        ext.func_info.data.extend(
964            SecInfoIter::new(ext.func_info_data(), ext.func_info_rec_size, endianness)
965                .map(move |sec| {
966                    let name = btf
967                        .string_at(sec.name_offset)
968                        .ok()
969                        .map(String::from)
970                        .unwrap();
971                    let info = FuncSecInfo::parse(
972                        sec.name_offset,
973                        sec.num_info,
974                        func_info_rec_size,
975                        sec.data,
976                        endianness,
977                    );
978                    Ok((name, info))
979                })
980                .collect::<Result<HashMap<_, _>, _>>()?,
981        );
982
983        let line_info_rec_size = ext.line_info_rec_size;
984        ext.line_info.data.extend(
985            SecInfoIter::new(ext.line_info_data(), ext.line_info_rec_size, endianness)
986                .map(move |sec| {
987                    let name = btf
988                        .string_at(sec.name_offset)
989                        .ok()
990                        .map(String::from)
991                        .unwrap();
992                    let info = LineSecInfo::parse(
993                        sec.name_offset,
994                        sec.num_info,
995                        line_info_rec_size,
996                        sec.data,
997                        endianness,
998                    );
999                    Ok((name, info))
1000                })
1001                .collect::<Result<HashMap<_, _>, _>>()?,
1002        );
1003
1004        let rec_size = ext.core_relo_rec_size;
1005        ext.relocations.extend(
1006            SecInfoIter::new(ext.core_relo_data(), ext.core_relo_rec_size, endianness)
1007                .map(move |sec| {
1008                    let relos = sec
1009                        .data
1010                        .chunks(rec_size)
1011                        .enumerate()
1012                        .map(|(n, rec)| unsafe { Relocation::parse(rec, n) })
1013                        .collect::<Result<Vec<_>, _>>()?;
1014                    Ok((sec.name_offset, relos))
1015                })
1016                .collect::<Result<Vec<_>, _>>()?,
1017        );
1018
1019        Ok(ext)
1020    }
1021
1022    fn info_data(&self, offset: u32, len: u32) -> &[u8] {
1023        let offset = (self.header.hdr_len + offset) as usize;
1024        let data = &self.data[offset..offset + len as usize];
1025        if len > 0 {
1026            // skip `rec_size`
1027            &data[4..]
1028        } else {
1029            data
1030        }
1031    }
1032
1033    fn core_relo_data(&self) -> &[u8] {
1034        self.info_data(self.header.core_relo_off, self.header.core_relo_len)
1035    }
1036
1037    fn func_info_data(&self) -> &[u8] {
1038        self.info_data(self.header.func_info_off, self.header.func_info_len)
1039    }
1040
1041    fn line_info_data(&self) -> &[u8] {
1042        self.info_data(self.header.line_info_off, self.header.line_info_len)
1043    }
1044
1045    pub(crate) const fn relocations(&self) -> &[(u32, Vec<Relocation>)] {
1046        self.relocations.as_slice()
1047    }
1048
1049    pub(crate) const fn func_info_rec_size(&self) -> usize {
1050        self.func_info_rec_size
1051    }
1052
1053    pub(crate) const fn line_info_rec_size(&self) -> usize {
1054        self.line_info_rec_size
1055    }
1056}
1057
1058pub(crate) struct SecInfoIter<'a> {
1059    data: &'a [u8],
1060    offset: usize,
1061    rec_size: usize,
1062    endianness: Endianness,
1063}
1064
1065impl<'a> SecInfoIter<'a> {
1066    const fn new(data: &'a [u8], rec_size: usize, endianness: Endianness) -> Self {
1067        Self {
1068            data,
1069            rec_size,
1070            offset: 0,
1071            endianness,
1072        }
1073    }
1074}
1075
1076impl<'a> Iterator for SecInfoIter<'a> {
1077    type Item = SecInfo<'a>;
1078
1079    fn next(&mut self) -> Option<Self::Item> {
1080        let data = self.data;
1081        if self.offset + 8 >= data.len() {
1082            return None;
1083        }
1084
1085        let read_u32 = if self.endianness == Endianness::Little {
1086            u32::from_le_bytes
1087        } else {
1088            u32::from_be_bytes
1089        };
1090        let name_offset = read_u32(data[self.offset..self.offset + 4].try_into().unwrap());
1091        self.offset += 4;
1092        let num_info = u32::from_ne_bytes(data[self.offset..self.offset + 4].try_into().unwrap());
1093        self.offset += 4;
1094
1095        let data = &data[self.offset..self.offset + (self.rec_size * num_info as usize)];
1096        self.offset += self.rec_size * num_info as usize;
1097
1098        Some(SecInfo {
1099            name_offset,
1100            num_info,
1101            data,
1102        })
1103    }
1104}
1105
1106/// [`BtfTypes`] allows for access and manipulation of a
1107/// collection of [`BtfType`] objects.
1108#[derive(Debug, Clone)]
1109pub(crate) struct BtfTypes {
1110    pub(crate) types: Vec<BtfType>,
1111}
1112
1113impl Default for BtfTypes {
1114    fn default() -> Self {
1115        Self {
1116            types: vec![BtfType::Unknown],
1117        }
1118    }
1119}
1120
1121impl BtfTypes {
1122    pub(crate) fn to_bytes(&self) -> Vec<u8> {
1123        let mut buf = vec![];
1124        for t in self.types.iter().skip(1) {
1125            let b = t.to_bytes();
1126            buf.extend(b)
1127        }
1128        buf
1129    }
1130
1131    pub(crate) const fn len(&self) -> usize {
1132        self.types.len()
1133    }
1134
1135    pub(crate) fn push(&mut self, value: BtfType) {
1136        self.types.push(value)
1137    }
1138
1139    pub(crate) fn type_by_id(&self, type_id: u32) -> Result<&BtfType, BtfError> {
1140        self.types
1141            .get(type_id as usize)
1142            .ok_or(BtfError::UnknownBtfType { type_id })
1143    }
1144
1145    pub(crate) fn resolve_type(&self, root_type_id: u32) -> Result<u32, BtfError> {
1146        let mut type_id = root_type_id;
1147        for () in core::iter::repeat_n((), MAX_RESOLVE_DEPTH) {
1148            let ty = self.type_by_id(type_id)?;
1149
1150            match ty {
1151                BtfType::Volatile(ty) => {
1152                    type_id = ty.btf_type;
1153                }
1154                BtfType::Const(ty) => {
1155                    type_id = ty.btf_type;
1156                }
1157                BtfType::Restrict(ty) => {
1158                    type_id = ty.btf_type;
1159                }
1160                BtfType::Typedef(ty) => {
1161                    type_id = ty.btf_type;
1162                }
1163                BtfType::TypeTag(ty) => {
1164                    type_id = ty.btf_type;
1165                }
1166                _ => return Ok(type_id),
1167            }
1168        }
1169
1170        Err(BtfError::MaximumTypeDepthReached {
1171            type_id: root_type_id,
1172        })
1173    }
1174}
1175
1176#[derive(Debug)]
1177pub(crate) struct SecInfo<'a> {
1178    name_offset: u32,
1179    num_info: u32,
1180    data: &'a [u8],
1181}
1182
1183#[cfg(test)]
1184mod tests {
1185    use assert_matches::assert_matches;
1186
1187    use super::*;
1188    use crate::btf::{BtfParam, DeclTag, Float, Func, FuncProto, Ptr, TypeTag};
1189
1190    #[test]
1191    fn test_parse_header() {
1192        let header = btf_header {
1193            magic: 0xeb9f,
1194            version: 0x01,
1195            flags: 0x00,
1196            hdr_len: 0x18,
1197            type_off: 0x00,
1198            type_len: 0x2a5464,
1199            str_off: 0x2a5464,
1200            str_len: 0x1c6410,
1201        };
1202        let data = unsafe { bytes_of::<btf_header>(&header).to_vec() };
1203        let header = unsafe { read_btf_header(&data) };
1204        assert_eq!(header.magic, 0xeb9f);
1205        assert_eq!(header.version, 0x01);
1206        assert_eq!(header.flags, 0x00);
1207        assert_eq!(header.hdr_len, 0x18);
1208        assert_eq!(header.type_off, 0x00);
1209        assert_eq!(header.type_len, 0x2a5464);
1210        assert_eq!(header.str_off, 0x2a5464);
1211        assert_eq!(header.str_len, 0x1c6410);
1212    }
1213
1214    #[test]
1215    fn test_parse_btf() {
1216        // this generated BTF data is from an XDP program that simply returns XDP_PASS
1217        // compiled using clang
1218        let data: &[u8] = if cfg!(target_endian = "little") {
1219            &[
1220                0x9f, 0xeb, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01,
1221                0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1222                0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00,
1223                0x00, 0x04, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
1224                0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
1225                0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
1226                0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00,
1227                0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
1228                0x03, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00,
1229                0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1230                0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
1231                0x00, 0x0d, 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
1232                0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00,
1233                0x00, 0x01, 0x69, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, 0x00,
1234                0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
1235                0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
1236                0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x00,
1237                0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
1238                0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00,
1239                0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
1240                0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x78,
1241                0x64, 0x70, 0x5f, 0x6d, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x64, 0x61, 0x74,
1242                0x61, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x74,
1243                0x61, 0x00, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66, 0x69, 0x6e,
1244                0x64, 0x65, 0x78, 0x00, 0x72, 0x78, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69,
1245                0x6e, 0x64, 0x65, 0x78, 0x00, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66,
1246                0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x5f, 0x5f, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6e,
1247                0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x74, 0x78,
1248                0x00, 0x69, 0x6e, 0x74, 0x00, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x00,
1249                0x78, 0x64, 0x70, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65,
1250                0x2f, 0x64, 0x61, 0x76, 0x65, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x62, 0x70, 0x66, 0x64,
1251                0x2f, 0x62, 0x70, 0x66, 0x2f, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x2e,
1252                0x62, 0x70, 0x66, 0x2e, 0x63, 0x00, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
1253                0x72, 0x6e, 0x20, 0x58, 0x44, 0x50, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x3b, 0x00, 0x63,
1254                0x68, 0x61, 0x72, 0x00, 0x5f, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x53, 0x49,
1255                0x5a, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x5f, 0x00, 0x5f, 0x6c, 0x69, 0x63,
1256                0x65, 0x6e, 0x73, 0x65, 0x00, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00,
1257            ]
1258        } else {
1259            &[
1260                0xeb, 0x9f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1261                0x01, 0x0c, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00,
1262                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
1263                0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03,
1264                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
1265                0x00, 0x20, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40,
1266                0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
1267                0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3f,
1268                0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x4e, 0x08, 0x00,
1269                0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x00,
1270                0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
1271                0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x01,
1272                0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00,
1273                0x00, 0x20, 0x00, 0x00, 0x00, 0x69, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05,
1274                0x00, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
1275                0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1276                0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1277                0x00, 0xbc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20,
1278                0x00, 0x00, 0x00, 0xd0, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
1279                0x00, 0x01, 0x00, 0x00, 0x00, 0xd9, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
1280                0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x78,
1281                0x64, 0x70, 0x5f, 0x6d, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x64, 0x61, 0x74,
1282                0x61, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x74,
1283                0x61, 0x00, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66, 0x69, 0x6e,
1284                0x64, 0x65, 0x78, 0x00, 0x72, 0x78, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69,
1285                0x6e, 0x64, 0x65, 0x78, 0x00, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66,
1286                0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x5f, 0x5f, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6e,
1287                0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x74, 0x78,
1288                0x00, 0x69, 0x6e, 0x74, 0x00, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x00,
1289                0x78, 0x64, 0x70, 0x2f, 0x70, 0x61, 0x73, 0x73, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65,
1290                0x2f, 0x64, 0x61, 0x76, 0x65, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x62, 0x70, 0x66, 0x64,
1291                0x2f, 0x62, 0x70, 0x66, 0x2f, 0x78, 0x64, 0x70, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x2e,
1292                0x62, 0x70, 0x66, 0x2e, 0x63, 0x00, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
1293                0x72, 0x6e, 0x20, 0x58, 0x44, 0x50, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x3b, 0x00, 0x63,
1294                0x68, 0x61, 0x72, 0x00, 0x5f, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x53, 0x49,
1295                0x5a, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x5f, 0x00, 0x5f, 0x6c, 0x69, 0x63,
1296                0x65, 0x6e, 0x73, 0x65, 0x00, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00,
1297            ]
1298        };
1299        assert_eq!(data.len(), 517);
1300        let btf = Btf::parse(data, Endianness::default()).unwrap();
1301        let data2 = btf.to_bytes();
1302        assert_eq!(data2.len(), 517);
1303        assert_eq!(data, data2);
1304
1305        const FUNC_LEN: u32 = 0x14;
1306        const LINE_INFO_LEN: u32 = 0x1c;
1307        const CORE_RELO_LEN: u32 = 0;
1308        const DATA_LEN: u32 = (FUNC_LEN + LINE_INFO_LEN + CORE_RELO_LEN) / 4;
1309        struct TestStruct {
1310            _header: btf_ext_header,
1311            _data: [u32; DATA_LEN as usize],
1312        }
1313        let test_data = TestStruct {
1314            _header: btf_ext_header {
1315                magic: 0xeb9f,
1316                version: 1,
1317                flags: 0,
1318                hdr_len: 0x20,
1319                func_info_off: 0,
1320                func_info_len: FUNC_LEN,
1321                line_info_off: FUNC_LEN,
1322                line_info_len: LINE_INFO_LEN,
1323                core_relo_off: FUNC_LEN + LINE_INFO_LEN,
1324                core_relo_len: CORE_RELO_LEN,
1325            },
1326            _data: [
1327                0x00000008u32,
1328                0x00000072u32,
1329                0x00000001u32,
1330                0x00000000u32,
1331                0x00000007u32,
1332                0x00000010u32,
1333                0x00000072u32,
1334                0x00000001u32,
1335                0x00000000u32,
1336                0x0000007bu32,
1337                0x000000a2u32,
1338                0x00002c05u32,
1339            ],
1340        };
1341        let ext_data = unsafe { bytes_of::<TestStruct>(&test_data).to_vec() };
1342
1343        assert_eq!(ext_data.len(), 80);
1344        let _unused: BtfExt = BtfExt::parse(&ext_data, Endianness::default(), &btf).unwrap();
1345    }
1346
1347    #[test]
1348    fn parsing_older_ext_data() {
1349        const TYPE_LEN: u32 = 0;
1350        const STR_LEN: u32 = 1;
1351        struct BtfTestStruct {
1352            _header: btf_header,
1353            _data: [u8; (TYPE_LEN + STR_LEN) as usize],
1354        }
1355        let btf_test_data = BtfTestStruct {
1356            _header: btf_header {
1357                magic: 0xeb9f,
1358                version: 0x01,
1359                flags: 0x00,
1360                hdr_len: 24,
1361                type_off: 0,
1362                type_len: TYPE_LEN,
1363                str_off: TYPE_LEN,
1364                str_len: TYPE_LEN + STR_LEN,
1365            },
1366            _data: [0x00u8],
1367        };
1368        let btf_data = unsafe { bytes_of::<BtfTestStruct>(&btf_test_data).to_vec() };
1369
1370        const FUNC_INFO_LEN: u32 = 4;
1371        const LINE_INFO_LEN: u32 = 4;
1372        const CORE_RELO_LEN: u32 = 16;
1373        let ext_header = btf_ext_header {
1374            magic: 0xeb9f,
1375            version: 1,
1376            flags: 0,
1377            hdr_len: 24,
1378            func_info_off: 0,
1379            func_info_len: FUNC_INFO_LEN,
1380            line_info_off: FUNC_INFO_LEN,
1381            line_info_len: LINE_INFO_LEN,
1382            core_relo_off: FUNC_INFO_LEN + LINE_INFO_LEN,
1383            core_relo_len: CORE_RELO_LEN,
1384        };
1385        let btf_ext_data = unsafe { bytes_of::<btf_ext_header>(&ext_header).to_vec() };
1386
1387        let btf = Btf::parse(&btf_data, Endianness::default()).unwrap();
1388        let btf_ext = BtfExt::parse(&btf_ext_data, Endianness::default(), &btf).unwrap();
1389        assert_eq!(btf_ext.func_info_rec_size(), 8);
1390        assert_eq!(btf_ext.line_info_rec_size(), 16);
1391    }
1392
1393    #[test]
1394    fn test_write_btf() {
1395        let mut btf = Btf::new();
1396        let name_offset = btf.add_string("int");
1397        let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0));
1398        btf.add_type(int_type);
1399
1400        let name_offset = btf.add_string("widget");
1401        let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0));
1402        btf.add_type(int_type);
1403
1404        let btf_bytes = btf.to_bytes();
1405        let raw_btf = btf_bytes.as_slice();
1406
1407        let btf = Btf::parse(raw_btf, Endianness::default()).unwrap();
1408        assert_eq!(btf.string_at(1).unwrap(), "int");
1409        assert_eq!(btf.string_at(5).unwrap(), "widget");
1410    }
1411
1412    #[test]
1413    fn test_fixup_ptr() {
1414        let mut btf = Btf::new();
1415        let name_offset = btf.add_string("int");
1416        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1417            name_offset,
1418            4,
1419            IntEncoding::Signed,
1420            0,
1421        )));
1422
1423        let name_offset = btf.add_string("&mut int");
1424        let ptr_type_id = btf.add_type(BtfType::Ptr(Ptr::new(name_offset, int_type_id)));
1425
1426        let features = Default::default();
1427
1428        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1429            .unwrap();
1430        assert_matches!(btf.type_by_id(ptr_type_id).unwrap(), BtfType::Ptr(fixed) => {
1431            assert_eq!(fixed.name_offset, 0);
1432        });
1433        // Ensure we can convert to bytes and back again
1434        let raw = btf.to_bytes();
1435        Btf::parse(&raw, Endianness::default()).unwrap();
1436    }
1437
1438    #[test]
1439    fn test_sanitize_var() {
1440        let mut btf = Btf::new();
1441        let name_offset = btf.add_string("int");
1442        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1443            name_offset,
1444            4,
1445            IntEncoding::Signed,
1446            0,
1447        )));
1448
1449        let name_offset = btf.add_string("&mut int");
1450        let var_type_id = btf.add_type(BtfType::Var(Var::new(
1451            name_offset,
1452            int_type_id,
1453            VarLinkage::Static,
1454        )));
1455
1456        let features = BtfFeatures {
1457            btf_datasec: false,
1458            ..Default::default()
1459        };
1460
1461        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1462            .unwrap();
1463        assert_matches!(btf.type_by_id(var_type_id).unwrap(), BtfType::Int(fixed) => {
1464            assert_eq!(fixed.name_offset, name_offset);
1465        });
1466        // Ensure we can convert to bytes and back again
1467        let raw = btf.to_bytes();
1468        Btf::parse(&raw, Endianness::default()).unwrap();
1469    }
1470
1471    #[test]
1472    fn test_sanitize_datasec() {
1473        let mut btf = Btf::new();
1474        let name_offset = btf.add_string("int");
1475        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1476            name_offset,
1477            4,
1478            IntEncoding::Signed,
1479            0,
1480        )));
1481
1482        let var_name_offset = btf.add_string("foo");
1483        let var_type_id = btf.add_type(BtfType::Var(Var::new(
1484            var_name_offset,
1485            int_type_id,
1486            VarLinkage::Static,
1487        )));
1488
1489        let name_offset = btf.add_string("data");
1490        let variables = vec![DataSecEntry {
1491            btf_type: var_type_id,
1492            offset: 0,
1493            size: 4,
1494        }];
1495        let datasec_type_id =
1496            btf.add_type(BtfType::DataSec(DataSec::new(name_offset, variables, 0)));
1497
1498        let features = BtfFeatures {
1499            btf_datasec: false,
1500            ..Default::default()
1501        };
1502
1503        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1504            .unwrap();
1505        assert_matches!(btf.type_by_id(datasec_type_id).unwrap(), BtfType::Struct(fixed) => {
1506            assert_eq!(fixed.name_offset , name_offset);
1507            assert_matches!(*fixed.members, [
1508                BtfMember {
1509                    name_offset: name_offset1,
1510                    btf_type,
1511                    offset: 0,
1512                },
1513            ] => {
1514                assert_eq!(name_offset1, var_name_offset);
1515                assert_eq!(btf_type, var_type_id);
1516            })
1517        });
1518        // Ensure we can convert to bytes and back again
1519        let raw = btf.to_bytes();
1520        Btf::parse(&raw, Endianness::default()).unwrap();
1521    }
1522
1523    #[test]
1524    fn test_fixup_datasec() {
1525        let mut btf = Btf::new();
1526        let name_offset = btf.add_string("int");
1527        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1528            name_offset,
1529            4,
1530            IntEncoding::Signed,
1531            0,
1532        )));
1533
1534        let name_offset = btf.add_string("foo");
1535        let var_type_id = btf.add_type(BtfType::Var(Var::new(
1536            name_offset,
1537            int_type_id,
1538            VarLinkage::Global,
1539        )));
1540
1541        let name_offset = btf.add_string(".data/foo");
1542        let variables = vec![DataSecEntry {
1543            btf_type: var_type_id,
1544            offset: 0,
1545            size: 4,
1546        }];
1547        let datasec_type_id =
1548            btf.add_type(BtfType::DataSec(DataSec::new(name_offset, variables, 0)));
1549
1550        let features = BtfFeatures {
1551            btf_datasec: true,
1552            ..Default::default()
1553        };
1554
1555        btf.fixup_and_sanitize(
1556            &HashMap::from([(".data/foo".to_owned(), (SectionIndex(0), 32u64))]),
1557            &HashMap::from([("foo".to_owned(), 64u64)]),
1558            &features,
1559        )
1560        .unwrap();
1561
1562        assert_matches!(btf.type_by_id(datasec_type_id).unwrap(), BtfType::DataSec(fixed) => {
1563            assert_ne!(fixed.name_offset, name_offset);
1564            assert_eq!(fixed.size, 32);
1565            assert_matches!(*fixed.entries, [
1566                    DataSecEntry {
1567                        btf_type,
1568                        offset,
1569                        size,
1570                    },
1571                ] => {
1572                    assert_eq!(btf_type, var_type_id);
1573                    assert_eq!(offset, 64);
1574                    assert_eq!(size, 4);
1575                }
1576            );
1577            assert_eq!(btf.string_at(fixed.name_offset).unwrap(), ".data.foo");
1578        });
1579        // Ensure we can convert to bytes and back again
1580        let raw = btf.to_bytes();
1581        Btf::parse(&raw, Endianness::default()).unwrap();
1582    }
1583
1584    #[test]
1585    fn test_sanitize_func_and_proto() {
1586        let mut btf = Btf::new();
1587        let name_offset = btf.add_string("int");
1588        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1589            name_offset,
1590            4,
1591            IntEncoding::Signed,
1592            0,
1593        )));
1594
1595        let params = vec![
1596            BtfParam {
1597                name_offset: btf.add_string("a"),
1598                btf_type: int_type_id,
1599            },
1600            BtfParam {
1601                name_offset: btf.add_string("b"),
1602                btf_type: int_type_id,
1603            },
1604        ];
1605        let func_proto_type_id =
1606            btf.add_type(BtfType::FuncProto(FuncProto::new(params, int_type_id)));
1607        let inc = btf.add_string("inc");
1608        let func_type_id = btf.add_type(BtfType::Func(Func::new(
1609            inc,
1610            func_proto_type_id,
1611            FuncLinkage::Static,
1612        )));
1613
1614        let features = BtfFeatures {
1615            btf_func: false,
1616            ..Default::default()
1617        };
1618
1619        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1620            .unwrap();
1621        assert_matches!(btf.type_by_id(func_proto_type_id).unwrap(), BtfType::Enum(fixed) => {
1622            assert_eq!(fixed.name_offset, 0);
1623            assert_matches!(*fixed.variants, [
1624                    BtfEnum {
1625                        name_offset: name_offset1,
1626                        value: value1,
1627                    },
1628                    BtfEnum {
1629                        name_offset: name_offset2,
1630                        value: value2,
1631                    },
1632                ] => {
1633                    assert_eq!(btf.string_at(name_offset1).unwrap(), "a");
1634                    assert_eq!(value1, int_type_id);
1635                    assert_eq!(btf.string_at(name_offset2).unwrap(), "b");
1636                    assert_eq!(value2, int_type_id);
1637                }
1638            );
1639        });
1640
1641        assert_matches!(btf.type_by_id(func_type_id).unwrap(), BtfType::Typedef(fixed) => {
1642            assert_eq!(fixed.name_offset, inc);
1643            assert_eq!(fixed.btf_type, func_proto_type_id);
1644        });
1645
1646        // Ensure we can convert to bytes and back again
1647        let raw = btf.to_bytes();
1648        Btf::parse(&raw, Endianness::default()).unwrap();
1649    }
1650
1651    #[test]
1652    fn test_fixup_func_proto() {
1653        let mut btf = Btf::new();
1654        let name_offset = btf.add_string("int");
1655        let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0));
1656        let int_type_id = btf.add_type(int_type);
1657
1658        let params = vec![
1659            BtfParam {
1660                name_offset: 0,
1661                btf_type: int_type_id,
1662            },
1663            BtfParam {
1664                name_offset: 0,
1665                btf_type: int_type_id,
1666            },
1667        ];
1668        let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id));
1669        let func_proto_type_id = btf.add_type(func_proto);
1670
1671        let features = BtfFeatures {
1672            btf_func: true,
1673            ..Default::default()
1674        };
1675
1676        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1677            .unwrap();
1678
1679        assert_matches!(btf.type_by_id(func_proto_type_id).unwrap(), BtfType::FuncProto(fixed) => {
1680            assert_matches!(*fixed.params, [
1681                    BtfParam {
1682                        name_offset: name_offset1,
1683                        btf_type: btf_type1,
1684                    },
1685                    BtfParam {
1686                        name_offset: name_offset2,
1687                        btf_type: btf_type2,
1688                    },
1689                ] => {
1690                    assert_eq!(btf.string_at(name_offset1).unwrap(), "param0");
1691                    assert_eq!(btf_type1, int_type_id);
1692                    assert_eq!(btf.string_at(name_offset2).unwrap(), "param1");
1693                    assert_eq!(btf_type2, int_type_id);
1694                }
1695            );
1696        });
1697
1698        // Ensure we can convert to bytes and back again
1699        let raw = btf.to_bytes();
1700        Btf::parse(&raw, Endianness::default()).unwrap();
1701    }
1702
1703    #[test]
1704    fn test_sanitize_func_global() {
1705        let mut btf = Btf::new();
1706        let name_offset = btf.add_string("int");
1707        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1708            name_offset,
1709            4,
1710            IntEncoding::Signed,
1711            0,
1712        )));
1713
1714        let params = vec![
1715            BtfParam {
1716                name_offset: btf.add_string("a"),
1717                btf_type: int_type_id,
1718            },
1719            BtfParam {
1720                name_offset: btf.add_string("b"),
1721                btf_type: int_type_id,
1722            },
1723        ];
1724        let func_proto_type_id =
1725            btf.add_type(BtfType::FuncProto(FuncProto::new(params, int_type_id)));
1726        let inc = btf.add_string("inc");
1727        let func_type_id = btf.add_type(BtfType::Func(Func::new(
1728            inc,
1729            func_proto_type_id,
1730            FuncLinkage::Global,
1731        )));
1732
1733        let features = BtfFeatures {
1734            btf_func: true,
1735            btf_func_global: false,
1736            ..Default::default()
1737        };
1738
1739        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1740            .unwrap();
1741
1742        assert_matches!(btf.type_by_id(func_type_id).unwrap(), BtfType::Func(fixed) => {
1743            assert_eq!(fixed.linkage(), FuncLinkage::Static);
1744        });
1745
1746        // Ensure we can convert to bytes and back again
1747        let raw = btf.to_bytes();
1748        Btf::parse(&raw, Endianness::default()).unwrap();
1749    }
1750
1751    #[test]
1752    fn test_sanitize_mem_builtins() {
1753        let mut btf = Btf::new();
1754        let name_offset = btf.add_string("int");
1755        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1756            name_offset,
1757            4,
1758            IntEncoding::Signed,
1759            0,
1760        )));
1761
1762        let params = vec![
1763            BtfParam {
1764                name_offset: btf.add_string("a"),
1765                btf_type: int_type_id,
1766            },
1767            BtfParam {
1768                name_offset: btf.add_string("b"),
1769                btf_type: int_type_id,
1770            },
1771        ];
1772        let func_proto_type_id =
1773            btf.add_type(BtfType::FuncProto(FuncProto::new(params, int_type_id)));
1774
1775        let builtins = ["memset", "memcpy", "memcmp", "memmove"];
1776        for fname in builtins {
1777            let func_name_offset = btf.add_string(fname);
1778            let func_type_id = btf.add_type(BtfType::Func(Func::new(
1779                func_name_offset,
1780                func_proto_type_id,
1781                FuncLinkage::Global,
1782            )));
1783
1784            let features = BtfFeatures {
1785                btf_func: true,
1786                btf_func_global: true, // to force function name check
1787                ..Default::default()
1788            };
1789
1790            btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1791                .unwrap();
1792
1793            assert_matches!(btf.type_by_id(func_type_id).unwrap(), BtfType::Func(fixed) => {
1794                assert_eq!(fixed.linkage(), FuncLinkage::Static);
1795            });
1796
1797            // Ensure we can convert to bytes and back again
1798            let raw = btf.to_bytes();
1799            Btf::parse(&raw, Endianness::default()).unwrap();
1800        }
1801    }
1802
1803    #[test]
1804    fn test_sanitize_float() {
1805        let mut btf = Btf::new();
1806        let name_offset = btf.add_string("float");
1807        let float_type_id = btf.add_type(BtfType::Float(Float::new(name_offset, 16)));
1808
1809        let features = BtfFeatures {
1810            btf_float: false,
1811            ..Default::default()
1812        };
1813
1814        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1815            .unwrap();
1816        assert_matches!(btf.type_by_id(float_type_id).unwrap(), BtfType::Struct(fixed) => {
1817            assert_eq!(fixed.name_offset, 0);
1818            assert_eq!(fixed.size, 16);
1819        });
1820
1821        // Ensure we can convert to bytes and back again
1822        let raw = btf.to_bytes();
1823        Btf::parse(&raw, Endianness::default()).unwrap();
1824    }
1825
1826    #[test]
1827    fn test_sanitize_decl_tag() {
1828        let mut btf = Btf::new();
1829        let name_offset = btf.add_string("int");
1830        let int_type_id = btf.add_type(BtfType::Int(Int::new(
1831            name_offset,
1832            4,
1833            IntEncoding::Signed,
1834            0,
1835        )));
1836
1837        let name_offset = btf.add_string("foo");
1838        let var_type_id = btf.add_type(BtfType::Var(Var::new(
1839            name_offset,
1840            int_type_id,
1841            VarLinkage::Static,
1842        )));
1843
1844        let name_offset = btf.add_string("decl_tag");
1845        let decl_tag_type_id =
1846            btf.add_type(BtfType::DeclTag(DeclTag::new(name_offset, var_type_id, -1)));
1847
1848        let features = BtfFeatures {
1849            btf_decl_tag: false,
1850            ..Default::default()
1851        };
1852
1853        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1854            .unwrap();
1855        assert_matches!(btf.type_by_id(decl_tag_type_id).unwrap(), BtfType::Int(fixed) => {
1856            assert_eq!(fixed.name_offset, name_offset);
1857            assert_eq!(fixed.size, 1);
1858        });
1859
1860        // Ensure we can convert to bytes and back again
1861        let raw = btf.to_bytes();
1862        Btf::parse(&raw, Endianness::default()).unwrap();
1863    }
1864
1865    #[test]
1866    fn test_sanitize_type_tag() {
1867        let mut btf = Btf::new();
1868
1869        let int_type_id = btf.add_type(BtfType::Int(Int::new(0, 4, IntEncoding::Signed, 0)));
1870
1871        let name_offset = btf.add_string("int");
1872        let type_tag_type = btf.add_type(BtfType::TypeTag(TypeTag::new(name_offset, int_type_id)));
1873        btf.add_type(BtfType::Ptr(Ptr::new(0, type_tag_type)));
1874
1875        let features = BtfFeatures {
1876            btf_type_tag: false,
1877            ..Default::default()
1878        };
1879
1880        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1881            .unwrap();
1882        assert_matches!(btf.type_by_id(type_tag_type).unwrap(), BtfType::Const(fixed) => {
1883            assert_eq!(fixed.btf_type, int_type_id);
1884        });
1885
1886        // Ensure we can convert to bytes and back again
1887        let raw = btf.to_bytes();
1888        Btf::parse(&raw, Endianness::default()).unwrap();
1889    }
1890
1891    #[test]
1892    #[cfg_attr(miri, ignore = "`open` not available when isolation is enabled")]
1893    #[cfg_attr(
1894        target_endian = "big",
1895        ignore = "Not possible to emulate \"/sys/kernel/btf/vmlinux\" as big endian"
1896    )]
1897    fn test_read_btf_from_sys_fs() {
1898        let btf = Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()).unwrap();
1899        let task_struct_id = btf
1900            .id_by_type_name_kind("task_struct", BtfKind::Struct)
1901            .unwrap();
1902        // we can't assert on exact ID since this may change across kernel versions
1903        assert!(task_struct_id != 0);
1904
1905        let netif_id = btf
1906            .id_by_type_name_kind("netif_receive_skb", BtfKind::Func)
1907            .unwrap();
1908        assert!(netif_id != 0);
1909
1910        let u32_def = btf.id_by_type_name_kind("__u32", BtfKind::Typedef).unwrap();
1911        assert!(u32_def != 0);
1912
1913        let u32_base = btf.resolve_type(u32_def).unwrap();
1914        assert!(u32_base != 0);
1915
1916        let u32_ty = btf.type_by_id(u32_base).unwrap();
1917        assert_eq!(u32_ty.kind(), BtfKind::Int);
1918    }
1919
1920    #[test]
1921    fn test_sanitize_signed_enum() {
1922        let mut btf = Btf::new();
1923        let name_offset = btf.add_string("signed_enum");
1924        let name_a = btf.add_string("A");
1925        let name_b = btf.add_string("B");
1926        let name_c = btf.add_string("C");
1927        let enum64_type = Enum::new(
1928            name_offset,
1929            true,
1930            vec![
1931                BtfEnum::new(name_a, -1i32 as u32),
1932                BtfEnum::new(name_b, -2i32 as u32),
1933                BtfEnum::new(name_c, -3i32 as u32),
1934            ],
1935        );
1936        let enum_type_id = btf.add_type(BtfType::Enum(enum64_type));
1937
1938        let features = BtfFeatures {
1939            btf_enum64: false,
1940            ..Default::default()
1941        };
1942
1943        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1944            .unwrap();
1945
1946        assert_matches!(btf.type_by_id(enum_type_id).unwrap(), BtfType::Enum(fixed) => {
1947            assert!(!fixed.is_signed());
1948            assert_matches!(fixed.variants[..], [
1949                BtfEnum { name_offset: name1, value: 0xFFFF_FFFF },
1950                BtfEnum { name_offset: name2, value: 0xFFFF_FFFE },
1951                BtfEnum { name_offset: name3, value: 0xFFFF_FFFD },
1952            ] => {
1953                assert_eq!(name1, name_a);
1954                assert_eq!(name2, name_b);
1955                assert_eq!(name3, name_c);
1956            });
1957        });
1958
1959        // Ensure we can convert to bytes and back again.
1960        let raw = btf.to_bytes();
1961        Btf::parse(&raw, Endianness::default()).unwrap();
1962    }
1963
1964    #[test]
1965    fn test_sanitize_enum64() {
1966        let mut btf = Btf::new();
1967        let name_offset = btf.add_string("enum64");
1968        let name_a = btf.add_string("A");
1969        let name_b = btf.add_string("B");
1970        let name_c = btf.add_string("C");
1971        let enum64_type = Enum64::new(
1972            name_offset,
1973            false,
1974            vec![
1975                BtfEnum64::new(name_a, 1),
1976                BtfEnum64::new(name_b, 2),
1977                BtfEnum64::new(name_c, 3),
1978            ],
1979        );
1980        let enum_type_id = btf.add_type(BtfType::Enum64(enum64_type));
1981
1982        let features = BtfFeatures {
1983            btf_enum64: false,
1984            ..Default::default()
1985        };
1986
1987        btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
1988            .unwrap();
1989
1990        assert_matches!(btf.type_by_id(enum_type_id).unwrap(), BtfType::Union(fixed) => {
1991            let placeholder = btf.id_by_type_name_kind("enum64_placeholder", BtfKind::Int)
1992                .expect("enum64_placeholder type not found");
1993            assert_matches!(fixed.members[..], [
1994                BtfMember { name_offset: name_offset1, btf_type: btf_type1, offset: 0 },
1995                BtfMember { name_offset: name_offset2, btf_type: btf_type2, offset: 0 },
1996                BtfMember { name_offset: name_offset3, btf_type: btf_type3, offset: 0 },
1997            ] => {
1998                assert_eq!(name_offset1, name_a);
1999                assert_eq!(btf_type1, placeholder);
2000                assert_eq!(name_offset2, name_b);
2001                assert_eq!(btf_type2, placeholder);
2002                assert_eq!(name_offset3, name_c);
2003                assert_eq!(btf_type3, placeholder);
2004            });
2005        });
2006
2007        // Ensure we can convert to bytes and back again.
2008        let raw = btf.to_bytes();
2009        Btf::parse(&raw, Endianness::default()).unwrap();
2010    }
2011}