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