Skip to main content

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