aya_obj/btf/
btf.rs

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