Skip to main content

aya_obj/
obj.rs

1//! Object file loading, parsing, and relocation.
2
3use std::{
4    borrow::ToOwned as _,
5    collections::BTreeMap,
6    ffi::{CStr, CString, FromBytesWithNulError},
7    mem, ptr,
8    slice::from_raw_parts_mut,
9    str::FromStr,
10    string::ToString as _,
11};
12
13use log::debug;
14use object::{
15    Endianness, ObjectSymbol as _, ObjectSymbolTable as _, RelocationTarget, SectionIndex,
16    SectionKind, SymbolKind,
17    read::{Object as _, ObjectSection as _, Section as ObjSection},
18};
19
20use crate::{
21    btf::{
22        Array, Btf, BtfError, BtfExt, BtfFeatures, BtfType, DataSecEntry, FuncSecInfo, LineSecInfo,
23        Struct,
24    },
25    generated::{
26        BPF_CALL, BPF_F_RDONLY_PROG, BPF_JMP, BPF_K, bpf_func_id, bpf_insn, bpf_map_info,
27        bpf_map_type::BPF_MAP_TYPE_ARRAY,
28    },
29    maps::{BtfMap, BtfMapDef, LegacyMap, MINIMUM_MAP_SIZE, Map, PinningType, bpf_map_def},
30    programs::{
31        CgroupSkbAttachType, CgroupSockAddrAttachType, CgroupSockAttachType,
32        CgroupSockoptAttachType, SkReuseportAttachType, SkSkbKind, XdpAttachType,
33    },
34    relocation::{INS_SIZE, Relocation, Symbol},
35    util::HashMap,
36};
37
38const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
39
40/// Features implements BPF and BTF feature detection
41#[derive(Default, Debug)]
42pub struct Features {
43    bpf_name: bool,
44    bpf_probe_read_kernel: bool,
45    bpf_perf_link: bool,
46    bpf_global_data: bool,
47    bpf_cookie: bool,
48    cpumap_prog_id: bool,
49    devmap_prog_id: bool,
50    btf: Option<BtfFeatures>,
51}
52
53impl Features {
54    #[doc(hidden)]
55    #[expect(
56        clippy::fn_params_excessive_bools,
57        reason = "this interface is terrible"
58    )]
59    #[expect(clippy::too_many_arguments, reason = "this interface is terrible")]
60    pub const fn new(
61        bpf_name: bool,
62        bpf_probe_read_kernel: bool,
63        bpf_perf_link: bool,
64        bpf_global_data: bool,
65        bpf_cookie: bool,
66        cpumap_prog_id: bool,
67        devmap_prog_id: bool,
68        btf: Option<BtfFeatures>,
69    ) -> Self {
70        Self {
71            bpf_name,
72            bpf_probe_read_kernel,
73            bpf_perf_link,
74            bpf_global_data,
75            bpf_cookie,
76            cpumap_prog_id,
77            devmap_prog_id,
78            btf,
79        }
80    }
81
82    /// Returns whether BPF program names and map names are supported.
83    ///
84    /// Although the feature probe performs the check for program name, we can use this to also
85    /// detect if map name is supported since they were both introduced in the same commit.
86    pub const fn bpf_name(&self) -> bool {
87        self.bpf_name
88    }
89
90    /// Returns whether the `bpf_probe_read_kernel` helper is supported.
91    pub const fn bpf_probe_read_kernel(&self) -> bool {
92        self.bpf_probe_read_kernel
93    }
94
95    /// Returns whether `bpf_links` are supported for Kprobes/Uprobes/Tracepoints.
96    pub const fn bpf_perf_link(&self) -> bool {
97        self.bpf_perf_link
98    }
99
100    /// Returns whether BPF program global data is supported.
101    pub const fn bpf_global_data(&self) -> bool {
102        self.bpf_global_data
103    }
104
105    /// Returns whether BPF program cookie is supported.
106    pub const fn bpf_cookie(&self) -> bool {
107        self.bpf_cookie
108    }
109
110    /// Returns whether XDP CPU Maps support chained program IDs.
111    pub const fn cpumap_prog_id(&self) -> bool {
112        self.cpumap_prog_id
113    }
114
115    /// Returns whether XDP Device Maps support chained program IDs.
116    pub const fn devmap_prog_id(&self) -> bool {
117        self.devmap_prog_id
118    }
119
120    /// If BTF is supported, returns which BTF features are supported.
121    pub const fn btf(&self) -> Option<&BtfFeatures> {
122        self.btf.as_ref()
123    }
124}
125
126/// The loaded object file representation
127#[derive(Clone, Debug)]
128pub struct Object {
129    /// The endianness
130    pub endianness: Endianness,
131    /// Program license
132    pub license: CString,
133    /// Kernel version
134    pub kernel_version: Option<u32>,
135    /// Program BTF
136    pub btf: Option<Btf>,
137    /// Program BTF.ext
138    pub btf_ext: Option<BtfExt>,
139    /// Referenced maps
140    pub maps: HashMap<String, Map>,
141    /// A hash map of programs, using the program names parsed
142    /// in [`ProgramSection`]s as keys.
143    pub programs: HashMap<String, Program>,
144    /// Functions
145    pub functions: BTreeMap<(usize, u64), Function>,
146    pub(crate) relocations: HashMap<SectionIndex, HashMap<u64, Relocation>>,
147    pub(crate) symbol_table: HashMap<usize, Symbol>,
148    pub(crate) symbols_by_section: HashMap<SectionIndex, Vec<usize>>,
149    pub(crate) section_infos: HashMap<String, (SectionIndex, u64)>,
150    // symbol_offset_by_name caches symbols that could be referenced from a
151    // BTF VAR type so the offsets can be fixed up
152    pub(crate) symbol_offset_by_name: HashMap<String, u64>,
153}
154
155/// An eBPF program
156#[derive(Debug, Clone)]
157pub struct Program {
158    /// The license
159    pub license: CString,
160    /// The kernel version
161    pub kernel_version: Option<u32>,
162    /// The section containing the program
163    pub section: ProgramSection,
164    /// The section index of the program
165    pub section_index: usize,
166    /// The address of the program
167    pub address: u64,
168}
169
170impl Program {
171    /// The key used by [`Object::functions`]
172    pub const fn function_key(&self) -> (usize, u64) {
173        (self.section_index, self.address)
174    }
175}
176
177/// An eBPF function
178#[derive(Debug, Clone)]
179pub struct Function {
180    /// The address
181    pub address: u64,
182    /// The function name
183    pub name: String,
184    /// The section index
185    pub section_index: SectionIndex,
186    /// The section offset
187    pub section_offset: usize,
188    /// The eBPF byte code instructions
189    pub instructions: Vec<bpf_insn>,
190    /// The function info
191    pub func_info: FuncSecInfo,
192    /// The line info
193    pub line_info: LineSecInfo,
194    /// Function info record size
195    pub func_info_rec_size: usize,
196    /// Line info record size
197    pub line_info_rec_size: usize,
198}
199
200/// Section types containing eBPF programs
201///
202/// # Section Name Parsing
203///
204/// Section types are parsed from the section name strings.
205///
206/// In order for Aya to treat a section as a [`ProgramSection`],
207/// there are a few requirements:
208/// - The section must be an executable code section.
209/// - The section name must conform to [Program Types and ELF Sections].
210///
211/// [Program Types and ELF Sections]: https://docs.kernel.org/bpf/libbpf/program_types.html
212///
213/// # Unsupported Sections
214///
215/// Currently, the following section names are not supported yet:
216/// - `flow_dissector`: `BPF_PROG_TYPE_FLOW_DISSECTOR`
217/// - `ksyscall+` or `kretsyscall+`
218/// - `usdt+`
219/// - `kprobe.multi+` or `kretprobe.multi+`: `BPF_TRACE_KPROBE_MULTI`
220/// - `lsm_cgroup+`
221/// - `lwt_in`, `lwt_out`, `lwt_seg6local`, `lwt_xmit`
222/// - `raw_tp.w+`, `raw_tracepoint.w+`
223/// - `action`
224/// - `syscall`
225/// - `struct_ops+`
226/// - `fmod_ret+`, `fmod_ret.s+`
227/// - `iter+`, `iter.s+`
228#[derive(Debug, Clone)]
229#[expect(missing_docs, reason = "TODO")]
230pub enum ProgramSection {
231    KRetProbe,
232    KProbe,
233    UProbe {
234        sleepable: bool,
235    },
236    URetProbe {
237        sleepable: bool,
238    },
239    TracePoint,
240    SocketFilter,
241    Xdp {
242        frags: bool,
243        attach_type: XdpAttachType,
244    },
245    SkMsg,
246    SkSkbStream {
247        kind: SkSkbKind,
248    },
249    SockOps,
250    SchedClassifier,
251    CgroupSkb {
252        attach_type: Option<CgroupSkbAttachType>,
253    },
254    CgroupSockAddr {
255        attach_type: CgroupSockAddrAttachType,
256    },
257    CgroupSysctl,
258    CgroupSockopt {
259        attach_type: CgroupSockoptAttachType,
260    },
261    LircMode2,
262    PerfEvent,
263    RawTracePoint,
264    Lsm {
265        sleepable: bool,
266    },
267    LsmCgroup,
268    BtfTracePoint,
269    FEntry {
270        sleepable: bool,
271    },
272    FExit {
273        sleepable: bool,
274    },
275    FlowDissector,
276    Extension,
277    SkLookup,
278    SkReuseport {
279        attach_type: SkReuseportAttachType,
280    },
281    CgroupSock {
282        attach_type: CgroupSockAttachType,
283    },
284    CgroupDevice,
285    Iter {
286        sleepable: bool,
287    },
288}
289
290impl FromStr for ProgramSection {
291    type Err = ParseError;
292
293    fn from_str(section: &str) -> Result<Self, ParseError> {
294        // parse the common case, eg "xdp/program_name" or
295        // "sk_skb/stream_verdict/program_name"
296        let mut pieces = section.split('/');
297        let mut next = || {
298            pieces
299                .next()
300                .ok_or_else(|| ParseError::InvalidProgramSection {
301                    section: section.to_owned(),
302                })
303        };
304        let kind = next()?;
305
306        Ok(match kind {
307            "kprobe" => Self::KProbe,
308            "kretprobe" => Self::KRetProbe,
309            "uprobe" => Self::UProbe { sleepable: false },
310            "uprobe.s" => Self::UProbe { sleepable: true },
311            "uretprobe" => Self::URetProbe { sleepable: false },
312            "uretprobe.s" => Self::URetProbe { sleepable: true },
313            "xdp" | "xdp.frags" => Self::Xdp {
314                frags: kind == "xdp.frags",
315                attach_type: match pieces.next() {
316                    None => XdpAttachType::Interface,
317                    Some("cpumap") => XdpAttachType::CpuMap,
318                    Some("devmap") => XdpAttachType::DevMap,
319                    Some(_) => {
320                        return Err(ParseError::InvalidProgramSection {
321                            section: section.to_owned(),
322                        });
323                    }
324                },
325            },
326            "tp_btf" => Self::BtfTracePoint,
327            "tracepoint" | "tp" => Self::TracePoint,
328            "socket" => Self::SocketFilter,
329            "sk_msg" => Self::SkMsg,
330            "sk_skb" => {
331                let name = next()?;
332                let kind = match name {
333                    "stream_parser" => SkSkbKind::StreamParser,
334                    "stream_verdict" => SkSkbKind::StreamVerdict,
335                    _ => {
336                        return Err(ParseError::InvalidProgramSection {
337                            section: section.to_owned(),
338                        });
339                    }
340                };
341                Self::SkSkbStream { kind }
342            }
343            "sockops" => Self::SockOps,
344            "classifier" => Self::SchedClassifier,
345            "cgroup_skb" => {
346                let name = next()?;
347                let attach_type = Some(match name {
348                    "ingress" => CgroupSkbAttachType::Ingress,
349                    "egress" => CgroupSkbAttachType::Egress,
350                    _ => {
351                        return Err(ParseError::InvalidProgramSection {
352                            section: section.to_owned(),
353                        });
354                    }
355                });
356                Self::CgroupSkb { attach_type }
357            }
358            "cgroup" => {
359                let name = next()?;
360                match name {
361                    "skb" => Self::CgroupSkb { attach_type: None },
362                    "sysctl" => Self::CgroupSysctl,
363                    "dev" => Self::CgroupDevice,
364                    "getsockopt" => Self::CgroupSockopt {
365                        attach_type: CgroupSockoptAttachType::Get,
366                    },
367                    "setsockopt" => Self::CgroupSockopt {
368                        attach_type: CgroupSockoptAttachType::Set,
369                    },
370                    "sock" => Self::CgroupSock {
371                        attach_type: CgroupSockAttachType::default(),
372                    },
373                    "post_bind4" => Self::CgroupSock {
374                        attach_type: CgroupSockAttachType::PostBind4,
375                    },
376                    "post_bind6" => Self::CgroupSock {
377                        attach_type: CgroupSockAttachType::PostBind6,
378                    },
379                    "sock_create" => Self::CgroupSock {
380                        attach_type: CgroupSockAttachType::SockCreate,
381                    },
382                    "sock_release" => Self::CgroupSock {
383                        attach_type: CgroupSockAttachType::SockRelease,
384                    },
385                    "bind4" => Self::CgroupSockAddr {
386                        attach_type: CgroupSockAddrAttachType::Bind4,
387                    },
388                    "bind6" => Self::CgroupSockAddr {
389                        attach_type: CgroupSockAddrAttachType::Bind6,
390                    },
391                    "connect4" => Self::CgroupSockAddr {
392                        attach_type: CgroupSockAddrAttachType::Connect4,
393                    },
394                    "connect6" => Self::CgroupSockAddr {
395                        attach_type: CgroupSockAddrAttachType::Connect6,
396                    },
397                    "getpeername4" => Self::CgroupSockAddr {
398                        attach_type: CgroupSockAddrAttachType::GetPeerName4,
399                    },
400                    "getpeername6" => Self::CgroupSockAddr {
401                        attach_type: CgroupSockAddrAttachType::GetPeerName6,
402                    },
403                    "getsockname4" => Self::CgroupSockAddr {
404                        attach_type: CgroupSockAddrAttachType::GetSockName4,
405                    },
406                    "getsockname6" => Self::CgroupSockAddr {
407                        attach_type: CgroupSockAddrAttachType::GetSockName6,
408                    },
409                    "sendmsg4" => Self::CgroupSockAddr {
410                        attach_type: CgroupSockAddrAttachType::UDPSendMsg4,
411                    },
412                    "sendmsg6" => Self::CgroupSockAddr {
413                        attach_type: CgroupSockAddrAttachType::UDPSendMsg6,
414                    },
415                    "recvmsg4" => Self::CgroupSockAddr {
416                        attach_type: CgroupSockAddrAttachType::UDPRecvMsg4,
417                    },
418                    "recvmsg6" => Self::CgroupSockAddr {
419                        attach_type: CgroupSockAddrAttachType::UDPRecvMsg6,
420                    },
421                    _ => {
422                        return Err(ParseError::InvalidProgramSection {
423                            section: section.to_owned(),
424                        });
425                    }
426                }
427            }
428            "lirc_mode2" => Self::LircMode2,
429            "perf_event" => Self::PerfEvent,
430            "raw_tp" | "raw_tracepoint" => Self::RawTracePoint,
431            "lsm" => Self::Lsm { sleepable: false },
432            "lsm.s" => Self::Lsm { sleepable: true },
433            "lsm_cgroup" => Self::LsmCgroup,
434            "fentry" => Self::FEntry { sleepable: false },
435            "fentry.s" => Self::FEntry { sleepable: true },
436            "fexit" => Self::FExit { sleepable: false },
437            "fexit.s" => Self::FExit { sleepable: true },
438            "flow_dissector" => Self::FlowDissector,
439            "freplace" => Self::Extension,
440            "sk_lookup" => Self::SkLookup,
441            "sk_reuseport" => {
442                let attach_type = if let Some(attach_type) = pieces.next() {
443                    if attach_type == "migrate" {
444                        SkReuseportAttachType::SelectOrMigrate
445                    } else {
446                        return Err(ParseError::InvalidProgramSection {
447                            section: section.to_owned(),
448                        });
449                    }
450                } else {
451                    SkReuseportAttachType::Select
452                };
453                Self::SkReuseport { attach_type }
454            }
455            "iter" => Self::Iter { sleepable: false },
456            "iter.s" => Self::Iter { sleepable: true },
457            _ => {
458                return Err(ParseError::InvalidProgramSection {
459                    section: section.to_owned(),
460                });
461            }
462        })
463    }
464}
465
466impl Object {
467    /// Parses the binary data as an object file into an [Object]
468    pub fn parse(data: &[u8]) -> Result<Self, ParseError> {
469        let obj = object::read::File::parse(data).map_err(ParseError::ElfError)?;
470        let endianness = obj.endianness();
471
472        let license = if let Some(section) = obj.section_by_name("license") {
473            parse_license(Section::try_from(&section)?.data)?
474        } else {
475            CString::new("GPL").unwrap()
476        };
477
478        let kernel_version = if let Some(section) = obj.section_by_name("version") {
479            parse_version(Section::try_from(&section)?.data, endianness)?
480        } else {
481            None
482        };
483
484        let mut bpf_obj = Self::new(endianness, license, kernel_version);
485
486        if let Some(symbol_table) = obj.symbol_table() {
487            for symbol in symbol_table.symbols() {
488                let name = symbol
489                    .name()
490                    .ok()
491                    .map(String::from)
492                    .ok_or(BtfError::InvalidSymbolName)?;
493                let sym = Symbol {
494                    index: symbol.index().0,
495                    name: Some(name.clone()),
496                    section_index: symbol.section().index().map(|i| i.0),
497                    address: symbol.address(),
498                    size: symbol.size(),
499                    is_definition: symbol.is_definition(),
500                    kind: symbol.kind(),
501                };
502                bpf_obj.symbol_table.insert(symbol.index().0, sym);
503                if let Some(section_idx) = symbol.section().index() {
504                    bpf_obj
505                        .symbols_by_section
506                        .entry(section_idx)
507                        .or_default()
508                        .push(symbol.index().0);
509                }
510                if symbol.is_global() || symbol.kind() == SymbolKind::Data {
511                    bpf_obj.symbol_offset_by_name.insert(name, symbol.address());
512                }
513            }
514        }
515
516        // .BTF and .BTF.ext sections must be parsed first
517        // as they're required to prepare function and line information
518        // when parsing program sections
519        if let Some(s) = obj.section_by_name(".BTF") {
520            bpf_obj.parse_section(Section::try_from(&s)?)?;
521            if let Some(s) = obj.section_by_name(".BTF.ext") {
522                bpf_obj.parse_section(Section::try_from(&s)?)?;
523            }
524        }
525
526        for s in obj.sections() {
527            if let Ok(name) = s.name() {
528                if name == ".BTF" || name == ".BTF.ext" {
529                    continue;
530                }
531            }
532
533            bpf_obj.parse_section(Section::try_from(&s)?)?;
534        }
535
536        Ok(bpf_obj)
537    }
538
539    fn new(endianness: Endianness, license: CString, kernel_version: Option<u32>) -> Self {
540        Self {
541            endianness,
542            license,
543            kernel_version,
544            btf: None,
545            btf_ext: None,
546            maps: HashMap::new(),
547            programs: HashMap::new(),
548            functions: BTreeMap::new(),
549            relocations: HashMap::new(),
550            symbol_table: HashMap::new(),
551            symbols_by_section: HashMap::new(),
552            section_infos: HashMap::new(),
553            symbol_offset_by_name: HashMap::new(),
554        }
555    }
556
557    /// Returns true if this object contains CO-RE relocations.
558    pub fn has_btf_relocations(&self) -> bool {
559        self.btf_ext
560            .as_ref()
561            .is_some_and(|ext| !ext.relocations().is_empty())
562    }
563
564    /// Patches map data
565    pub fn patch_map_data(
566        &mut self,
567        globals: HashMap<&str, (&[u8], bool)>,
568    ) -> Result<(), ParseError> {
569        let symbols: HashMap<String, &Symbol> = self
570            .symbol_table
571            .iter()
572            .filter(|(_, s)| s.name.is_some())
573            .map(|(_, s)| (s.name.as_ref().unwrap().clone(), s))
574            .collect();
575
576        for (name, (data, must_exist)) in globals {
577            if let Some(symbol) = symbols.get(name) {
578                if data.len() as u64 != symbol.size {
579                    return Err(ParseError::InvalidGlobalData {
580                        name: name.to_string(),
581                        sym_size: symbol.size,
582                        data_size: data.len(),
583                    });
584                }
585                let (_, map) = self
586                    .maps
587                    .iter_mut()
588                    // assumption: there is only one map created per section where we're trying to
589                    // patch data. this assumption holds true for the .rodata section at least
590                    .find(|(_, m)| symbol.section_index == Some(m.section_index()))
591                    .ok_or_else(|| ParseError::MapNotFound {
592                        index: symbol.section_index.unwrap_or(0),
593                    })?;
594                let start = symbol.address as usize;
595                let end = start + symbol.size as usize;
596                if start > end || end > map.data().len() {
597                    return Err(ParseError::InvalidGlobalData {
598                        name: name.to_string(),
599                        sym_size: symbol.size,
600                        data_size: data.len(),
601                    });
602                }
603                map.data_mut().splice(start..end, data.iter().copied());
604            } else if must_exist {
605                return Err(ParseError::SymbolNotFound {
606                    name: name.to_owned(),
607                });
608            }
609        }
610        Ok(())
611    }
612
613    fn parse_btf(&mut self, section: &Section<'_>) -> Result<(), BtfError> {
614        self.btf = Some(Btf::parse(section.data, self.endianness)?);
615
616        Ok(())
617    }
618
619    fn parse_btf_ext(&mut self, section: &Section<'_>) -> Result<(), BtfError> {
620        self.btf_ext = Some(BtfExt::parse(
621            section.data,
622            self.endianness,
623            self.btf.as_ref().unwrap(),
624        )?);
625        Ok(())
626    }
627
628    fn parse_programs(&mut self, section: &Section<'_>) -> Result<(), ParseError> {
629        let program_section = ProgramSection::from_str(section.name)?;
630        let syms = self.symbols_by_section.get(&section.index).ok_or_else(|| {
631            ParseError::NoSymbolsForSection {
632                section_name: section.name.to_string(),
633            }
634        })?;
635        for symbol_index in syms {
636            let symbol = self
637                .symbol_table
638                .get(symbol_index)
639                .expect("all symbols in symbols_by_section are also in symbol_table");
640
641            // Here we get both ::Label (LBB*) and ::Text symbols, and we only want the latter.
642            let name = match (symbol.name.as_ref(), symbol.kind) {
643                (Some(name), SymbolKind::Text) if !name.is_empty() => name,
644                _ => continue,
645            };
646
647            let (p, f) =
648                self.parse_program(section, program_section.clone(), name.clone(), symbol)?;
649            let key = p.function_key();
650            self.programs.insert(f.name.clone(), p);
651            self.functions.insert(key, f);
652        }
653        Ok(())
654    }
655
656    fn parse_program(
657        &self,
658        section: &Section<'_>,
659        program_section: ProgramSection,
660        name: String,
661        symbol: &Symbol,
662    ) -> Result<(Program, Function), ParseError> {
663        let offset = symbol.address as usize - section.address as usize;
664        let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
665            get_func_and_line_info(self.btf_ext.as_ref(), symbol, section, offset, true);
666
667        let start = symbol.address as usize;
668        let end = (symbol.address + symbol.size) as usize;
669
670        let function = Function {
671            name,
672            address: symbol.address,
673            section_index: section.index,
674            section_offset: start,
675            instructions: copy_instructions(&section.data[start..end])?,
676            func_info,
677            line_info,
678            func_info_rec_size,
679            line_info_rec_size,
680        };
681
682        Ok((
683            Program {
684                license: self.license.clone(),
685                kernel_version: self.kernel_version,
686                section: program_section,
687                section_index: section.index.0,
688                address: symbol.address,
689            },
690            function,
691        ))
692    }
693
694    fn parse_text_section(&mut self, section: Section<'_>) -> Result<(), ParseError> {
695        let mut symbols_by_address = HashMap::new();
696
697        for sym in self.symbol_table.values() {
698            if sym.is_definition
699                && sym.kind == SymbolKind::Text
700                && sym.section_index == Some(section.index.0)
701            {
702                if symbols_by_address.contains_key(&sym.address) {
703                    return Err(ParseError::SymbolTableConflict {
704                        section_index: section.index.0,
705                        address: sym.address,
706                    });
707                }
708                symbols_by_address.insert(sym.address, sym);
709            }
710        }
711
712        let mut offset = 0;
713        while offset < section.data.len() {
714            let address = section.address + offset as u64;
715            let sym = symbols_by_address
716                .get(&address)
717                .ok_or(ParseError::UnknownSymbol {
718                    section_index: section.index.0,
719                    address,
720                })?;
721            if sym.size == 0 {
722                return Err(ParseError::InvalidSymbol {
723                    index: sym.index,
724                    name: sym.name.clone(),
725                });
726            }
727
728            let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
729                get_func_and_line_info(self.btf_ext.as_ref(), sym, &section, offset, false);
730
731            self.functions.insert(
732                (section.index.0, sym.address),
733                Function {
734                    address,
735                    name: sym.name.clone().unwrap(),
736                    section_index: section.index,
737                    section_offset: offset,
738                    instructions: copy_instructions(
739                        &section.data[offset..offset + sym.size as usize],
740                    )?,
741                    func_info,
742                    line_info,
743                    func_info_rec_size,
744                    line_info_rec_size,
745                },
746            );
747
748            offset += sym.size as usize;
749        }
750
751        if !section.relocations.is_empty() {
752            self.relocations.insert(
753                section.index,
754                section
755                    .relocations
756                    .into_iter()
757                    .map(|rel| (rel.offset, rel))
758                    .collect(),
759            );
760        }
761
762        Ok(())
763    }
764
765    fn parse_btf_maps(&mut self, section: &Section<'_>) -> Result<(), ParseError> {
766        if self.btf.is_none() {
767            return Err(ParseError::NoBTF);
768        }
769        let btf = self.btf.as_ref().unwrap();
770        let maps: HashMap<&String, usize> = self
771            .symbols_by_section
772            .get(&section.index)
773            .ok_or_else(|| ParseError::NoSymbolsForSection {
774                section_name: section.name.to_owned(),
775            })?
776            .iter()
777            .filter_map(|s| {
778                let symbol = &self.symbol_table[s];
779                symbol.name.as_ref().map(|name| (name, symbol.index))
780            })
781            .collect();
782
783        for t in btf.types() {
784            if let BtfType::DataSec(datasec) = &t {
785                let type_name = btf.type_name(t)?;
786                if type_name == section.name {
787                    // each btf_var_secinfo contains a map
788                    for info in &datasec.entries {
789                        let (map_name, def, inner_def) = parse_btf_map_def(btf, info)?;
790                        let symbol_index =
791                            maps.get(&map_name)
792                                .ok_or_else(|| ParseError::SymbolNotFound {
793                                    name: map_name.clone(),
794                                })?;
795                        self.maps.insert(
796                            map_name,
797                            Map::Btf(BtfMap {
798                                def,
799                                inner_def,
800                                section_index: section.index.0,
801                                symbol_index: *symbol_index,
802                                data: Vec::new(),
803                            }),
804                        );
805                    }
806                }
807            }
808        }
809        Ok(())
810    }
811
812    // Parses multiple map definition contained in a single `maps` section (which is
813    // different from `.maps` which is used for BTF). We can tell where each map is
814    // based on the symbol table.
815    fn parse_maps_section<'a, I: Iterator<Item = &'a usize>>(
816        &self,
817        maps: &mut HashMap<String, Map>,
818        section: &Section<'_>,
819        symbols: I,
820    ) -> Result<(), ParseError> {
821        let mut have_symbols = false;
822        // each symbol in the section is a separate map
823        for i in symbols {
824            let sym = self
825                .symbol_table
826                .get(i)
827                .ok_or_else(|| ParseError::SymbolNotFound {
828                    name: i.to_string(),
829                })?;
830            let start = sym.address as usize;
831            let end = start + sym.size as usize;
832            let data = &section.data[start..end];
833            let name = sym
834                .name
835                .as_ref()
836                .ok_or(ParseError::MapSymbolNameNotFound { i: *i })?;
837            let def = parse_map_def(name, data)?;
838            maps.insert(
839                name.clone(),
840                Map::Legacy(LegacyMap {
841                    section_index: section.index.0,
842                    section_kind: section.kind,
843                    symbol_index: Some(sym.index),
844                    def,
845                    inner_def: None,
846                    data: Vec::new(),
847                }),
848            );
849            have_symbols = true;
850        }
851        if !have_symbols {
852            return Err(ParseError::NoSymbolsForSection {
853                section_name: section.name.to_owned(),
854            });
855        }
856
857        Ok(())
858    }
859
860    fn parse_section(&mut self, section: Section<'_>) -> Result<(), ParseError> {
861        self.section_infos
862            .insert(section.name.to_owned(), (section.index, section.size));
863        match section.kind {
864            EbpfSectionKind::Data | EbpfSectionKind::Rodata | EbpfSectionKind::Bss => {
865                self.maps
866                    .insert(section.name.to_string(), parse_data_map_section(&section));
867            }
868            EbpfSectionKind::Text => self.parse_text_section(section)?,
869            EbpfSectionKind::Btf => self.parse_btf(&section)?,
870            EbpfSectionKind::BtfExt => self.parse_btf_ext(&section)?,
871            EbpfSectionKind::BtfMaps => self.parse_btf_maps(&section)?,
872            EbpfSectionKind::Maps => {
873                // take out self.maps so we can borrow the iterator below
874                // without cloning or collecting
875                let mut maps = mem::take(&mut self.maps);
876
877                // extract the symbols for the .maps section, we'll need them
878                // during parsing
879                let symbols = self
880                    .symbols_by_section
881                    .get(&section.index)
882                    .ok_or_else(|| ParseError::NoSymbolsForSection {
883                        section_name: section.name.to_owned(),
884                    })?
885                    .iter();
886
887                let res = self.parse_maps_section(&mut maps, &section, symbols);
888
889                // put the maps back
890                self.maps = maps;
891
892                res?
893            }
894            EbpfSectionKind::Program => {
895                self.parse_programs(&section)?;
896                if !section.relocations.is_empty() {
897                    self.relocations.insert(
898                        section.index,
899                        section
900                            .relocations
901                            .into_iter()
902                            .map(|rel| (rel.offset, rel))
903                            .collect(),
904                    );
905                }
906            }
907            EbpfSectionKind::Undefined | EbpfSectionKind::License | EbpfSectionKind::Version => {}
908        }
909
910        Ok(())
911    }
912
913    /// Sanitize BPF functions.
914    pub fn sanitize_functions(&mut self, features: &Features) {
915        for function in self.functions.values_mut() {
916            function.sanitize(features);
917        }
918    }
919}
920
921fn insn_is_helper_call(ins: bpf_insn) -> bool {
922    let klass = u32::from(ins.code & 0x07);
923    let op = u32::from(ins.code & 0xF0);
924    let src = u32::from(ins.code & 0x08);
925
926    klass == BPF_JMP && op == BPF_CALL && src == BPF_K && ins.src_reg() == 0 && ins.dst_reg() == 0
927}
928
929impl Function {
930    fn sanitize(&mut self, features: &Features) {
931        for inst in &mut self.instructions {
932            if !insn_is_helper_call(*inst) {
933                continue;
934            }
935
936            if !features.bpf_probe_read_kernel {
937                const BPF_FUNC_PROBE_READ_KERNEL: i32 =
938                    bpf_func_id::BPF_FUNC_probe_read_kernel as i32;
939                const BPF_FUNC_PROBE_READ_USER: i32 = bpf_func_id::BPF_FUNC_probe_read_user as i32;
940                const BPF_FUNC_PROBE_READ: i32 = bpf_func_id::BPF_FUNC_probe_read as i32;
941                const BPF_FUNC_PROBE_READ_KERNEL_STR: i32 =
942                    bpf_func_id::BPF_FUNC_probe_read_kernel_str as i32;
943                const BPF_FUNC_PROBE_READ_USER_STR: i32 =
944                    bpf_func_id::BPF_FUNC_probe_read_user_str as i32;
945                const BPF_FUNC_PROBE_READ_STR: i32 = bpf_func_id::BPF_FUNC_probe_read_str as i32;
946
947                match inst.imm {
948                    BPF_FUNC_PROBE_READ_KERNEL | BPF_FUNC_PROBE_READ_USER => {
949                        inst.imm = BPF_FUNC_PROBE_READ;
950                    }
951                    BPF_FUNC_PROBE_READ_KERNEL_STR | BPF_FUNC_PROBE_READ_USER_STR => {
952                        inst.imm = BPF_FUNC_PROBE_READ_STR;
953                    }
954                    _ => {}
955                }
956            }
957        }
958    }
959}
960
961/// Errors caught during parsing the object file
962#[derive(Debug, thiserror::Error)]
963#[expect(missing_docs, reason = "TODO")]
964pub enum ParseError {
965    #[error("error parsing ELF data")]
966    ElfError(object::read::Error),
967
968    /// Error parsing BTF object
969    #[error("BTF error")]
970    BtfError(#[from] BtfError),
971
972    #[error("invalid license `{data:?}`: missing NULL terminator")]
973    MissingLicenseNullTerminator { data: Vec<u8> },
974
975    #[error("invalid license `{data:?}`")]
976    InvalidLicense { data: Vec<u8> },
977
978    #[error("invalid kernel version `{data:?}`")]
979    InvalidKernelVersion { data: Vec<u8> },
980
981    #[error("error parsing section with index {index}")]
982    SectionError {
983        index: usize,
984        error: object::read::Error,
985    },
986
987    #[error("unsupported relocation target")]
988    UnsupportedRelocationTarget,
989
990    #[error("invalid program section `{section}`")]
991    InvalidProgramSection { section: String },
992
993    #[error("invalid program code")]
994    InvalidProgramCode,
995
996    #[error("error parsing map `{name}`")]
997    InvalidMapDefinition { name: String },
998
999    #[error("two or more symbols in section `{section_index}` have the same address {address:#X}")]
1000    SymbolTableConflict { section_index: usize, address: u64 },
1001
1002    #[error("unknown symbol in section `{section_index}` at address {address:#X}")]
1003    UnknownSymbol { section_index: usize, address: u64 },
1004
1005    #[error("invalid symbol, index `{index}` name: {}", .name.as_deref().unwrap_or("[unknown]"))]
1006    InvalidSymbol { index: usize, name: Option<String> },
1007
1008    #[error("symbol {name} has size `{sym_size}`, but provided data is of size `{data_size}`")]
1009    InvalidGlobalData {
1010        name: String,
1011        sym_size: u64,
1012        data_size: usize,
1013    },
1014
1015    #[error("symbol with name {name} not found in the symbols table")]
1016    SymbolNotFound { name: String },
1017
1018    #[error("map for section with index {index} not found")]
1019    MapNotFound { index: usize },
1020
1021    #[error("the map number {i} in the `maps` section doesn't have a symbol name")]
1022    MapSymbolNameNotFound { i: usize },
1023
1024    #[error("no symbols found in the {section_name} section")]
1025    NoSymbolsForSection { section_name: String },
1026
1027    /// No BTF parsed for object
1028    #[error("no BTF parsed for object")]
1029    NoBTF,
1030}
1031
1032/// Invalid bindings to the bpf type from the parsed/received value.
1033pub struct InvalidTypeBinding<T> {
1034    /// The value parsed/received.
1035    pub value: T,
1036}
1037
1038/// The kind of an ELF section.
1039#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1040pub enum EbpfSectionKind {
1041    /// Undefined
1042    Undefined,
1043    /// `maps`
1044    Maps,
1045    /// `.maps`
1046    BtfMaps,
1047    /// A program section
1048    Program,
1049    /// `.data`
1050    Data,
1051    /// `.rodata`
1052    Rodata,
1053    /// `.bss`
1054    Bss,
1055    /// `.text`
1056    Text,
1057    /// `.BTF`
1058    Btf,
1059    /// `.BTF.ext`
1060    BtfExt,
1061    /// `license`
1062    License,
1063    /// `version`
1064    Version,
1065}
1066
1067impl EbpfSectionKind {
1068    fn from_name(name: &str) -> Self {
1069        if name.starts_with("license") {
1070            Self::License
1071        } else if name.starts_with("version") {
1072            Self::Version
1073        } else if name.starts_with("maps") {
1074            Self::Maps
1075        } else if name.starts_with(".maps") {
1076            Self::BtfMaps
1077        } else if name.starts_with(".text") {
1078            Self::Text
1079        } else if name.starts_with(".bss") {
1080            Self::Bss
1081        } else if name.starts_with(".data") {
1082            Self::Data
1083        } else if name.starts_with(".rodata") {
1084            Self::Rodata
1085        } else if name == ".BTF" {
1086            Self::Btf
1087        } else if name == ".BTF.ext" {
1088            Self::BtfExt
1089        } else {
1090            Self::Undefined
1091        }
1092    }
1093}
1094
1095#[derive(Debug)]
1096struct Section<'a> {
1097    index: SectionIndex,
1098    kind: EbpfSectionKind,
1099    address: u64,
1100    name: &'a str,
1101    data: &'a [u8],
1102    size: u64,
1103    relocations: Vec<Relocation>,
1104}
1105
1106impl<'a> TryFrom<&'a ObjSection<'_, '_>> for Section<'a> {
1107    type Error = ParseError;
1108
1109    fn try_from(section: &'a ObjSection<'_, '_>) -> Result<Self, ParseError> {
1110        let index = section.index();
1111        let map_err = |error| ParseError::SectionError {
1112            index: index.0,
1113            error,
1114        };
1115        let name = section.name().map_err(map_err)?;
1116        let kind = match EbpfSectionKind::from_name(name) {
1117            EbpfSectionKind::Undefined => {
1118                if section.kind() == SectionKind::Text && section.size() > 0 {
1119                    EbpfSectionKind::Program
1120                } else {
1121                    EbpfSectionKind::Undefined
1122                }
1123            }
1124            k => k,
1125        };
1126        Ok(Section {
1127            index,
1128            kind,
1129            address: section.address(),
1130            name,
1131            data: section.data().map_err(map_err)?,
1132            size: section.size(),
1133            relocations: section
1134                .relocations()
1135                .map(|(offset, r)| {
1136                    Ok(Relocation {
1137                        symbol_index: match r.target() {
1138                            RelocationTarget::Symbol(index) => index.0,
1139                            _ => return Err(ParseError::UnsupportedRelocationTarget),
1140                        },
1141                        offset,
1142                        size: r.size(),
1143                    })
1144                })
1145                .collect::<Result<Vec<_>, _>>()?,
1146        })
1147    }
1148}
1149
1150fn parse_license(data: &[u8]) -> Result<CString, ParseError> {
1151    if data.len() < 2 {
1152        return Err(ParseError::InvalidLicense {
1153            data: data.to_vec(),
1154        });
1155    }
1156    CStr::from_bytes_with_nul(data)
1157        .map_err(|err| match err {
1158            FromBytesWithNulError::NotNulTerminated => ParseError::MissingLicenseNullTerminator {
1159                data: data.to_vec(),
1160            },
1161            FromBytesWithNulError::InteriorNul { position: _ } => ParseError::InvalidLicense {
1162                data: data.to_vec(),
1163            },
1164        })
1165        .map(ToOwned::to_owned)
1166}
1167
1168fn parse_version(data: &[u8], endianness: Endianness) -> Result<Option<u32>, ParseError> {
1169    let data = match data.len() {
1170        4 => data.try_into().unwrap(),
1171        _ => {
1172            return Err(ParseError::InvalidKernelVersion {
1173                data: data.to_vec(),
1174            });
1175        }
1176    };
1177
1178    #[expect(
1179        clippy::big_endian_bytes,
1180        clippy::little_endian_bytes,
1181        reason = "that's the point"
1182    )]
1183    let v = match endianness {
1184        Endianness::Big => u32::from_be_bytes(data),
1185        Endianness::Little => u32::from_le_bytes(data),
1186    };
1187
1188    Ok(if v == KERNEL_VERSION_ANY {
1189        None
1190    } else {
1191        Some(v)
1192    })
1193}
1194
1195// Gets an integer value from a BTF map definition K/V pair.
1196// type_id should be a PTR to an ARRAY.
1197// the value is encoded in the array nr_elems field.
1198fn get_map_field(btf: &Btf, type_id: u32) -> Result<u32, BtfError> {
1199    let pty = match &btf.type_by_id(type_id)? {
1200        BtfType::Ptr(pty) => pty,
1201        other => {
1202            return Err(BtfError::UnexpectedBtfType {
1203                type_id: other.btf_type().unwrap_or(0),
1204            });
1205        }
1206    };
1207    let arr = match &btf.type_by_id(pty.btf_type)? {
1208        BtfType::Array(Array { array, .. }) => array,
1209        other => {
1210            return Err(BtfError::UnexpectedBtfType {
1211                type_id: other.btf_type().unwrap_or(0),
1212            });
1213        }
1214    };
1215    Ok(arr.len)
1216}
1217
1218// Parse '.bss' '.data' and '.rodata' sections. These sections are arrays of
1219// bytes and are relocated based on their section index.
1220fn parse_data_map_section(section: &Section<'_>) -> Map {
1221    let (def, data) = match section.kind {
1222        EbpfSectionKind::Data | EbpfSectionKind::Rodata => {
1223            let def = bpf_map_def {
1224                map_type: BPF_MAP_TYPE_ARRAY as u32,
1225                key_size: size_of::<u32>() as u32,
1226                // We need to use section.size here since
1227                // .bss will always have data.len() == 0
1228                value_size: section.size as u32,
1229                max_entries: 1,
1230                map_flags: if section.kind == EbpfSectionKind::Rodata {
1231                    BPF_F_RDONLY_PROG
1232                } else {
1233                    0
1234                },
1235                ..Default::default()
1236            };
1237            (def, section.data.to_vec())
1238        }
1239        EbpfSectionKind::Bss => {
1240            let def = bpf_map_def {
1241                map_type: BPF_MAP_TYPE_ARRAY as u32,
1242                key_size: size_of::<u32>() as u32,
1243                value_size: section.size as u32,
1244                max_entries: 1,
1245                map_flags: 0,
1246                ..Default::default()
1247            };
1248            (def, vec![0; section.size as usize])
1249        }
1250        #[expect(clippy::unreachable, reason = "unexpected section kind")]
1251        kind => {
1252            unreachable!("unexpected section kind: {kind:?}")
1253        }
1254    };
1255    Map::Legacy(LegacyMap {
1256        section_index: section.index.0,
1257        section_kind: section.kind,
1258        // Data maps don't require symbols to be relocated
1259        symbol_index: None,
1260        def,
1261        inner_def: None,
1262        data,
1263    })
1264}
1265
1266fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
1267    if data.len() < MINIMUM_MAP_SIZE {
1268        return Err(ParseError::InvalidMapDefinition {
1269            name: name.to_owned(),
1270        });
1271    }
1272
1273    if data.len() < size_of::<bpf_map_def>() {
1274        let mut map_def = bpf_map_def::default();
1275        unsafe {
1276            let map_def_ptr = from_raw_parts_mut(ptr::from_mut(&mut map_def).cast(), data.len());
1277            map_def_ptr.copy_from_slice(data);
1278        }
1279        Ok(map_def)
1280    } else {
1281        Ok(unsafe { ptr::read_unaligned(data.as_ptr().cast()) })
1282    }
1283}
1284
1285fn parse_btf_map_def(
1286    btf: &Btf,
1287    info: &DataSecEntry,
1288) -> Result<(String, BtfMapDef, Option<BtfMapDef>), BtfError> {
1289    let ty = match btf.type_by_id(info.btf_type)? {
1290        BtfType::Var(var) => var,
1291        other => {
1292            return Err(BtfError::UnexpectedBtfType {
1293                type_id: other.btf_type().unwrap_or(0),
1294            });
1295        }
1296    };
1297    let map_name = btf.string_at(ty.name_offset)?;
1298
1299    let root_type = btf.resolve_type(ty.btf_type)?;
1300    let s = match btf.type_by_id(root_type)? {
1301        BtfType::Struct(s) => s,
1302        other => {
1303            return Err(BtfError::UnexpectedBtfType {
1304                type_id: other.btf_type().unwrap_or(0),
1305            });
1306        }
1307    };
1308
1309    let (map_def, inner_def) = parse_btf_map_struct(btf, s, &map_name, false)?;
1310    Ok((map_name.to_string(), map_def, inner_def))
1311}
1312
1313/// Parses BTF struct members into a map definition.
1314///
1315/// When `is_inner` is true, rejects `values` and `pinning` fields, matching
1316/// libbpf behavior for inner map definitions.
1317fn parse_btf_map_struct(
1318    btf: &Btf,
1319    s: &Struct,
1320    map_name: &str,
1321    is_inner: bool,
1322) -> Result<(BtfMapDef, Option<BtfMapDef>), BtfError> {
1323    let mut map_def = BtfMapDef::default();
1324    let mut inner_map_def = None;
1325
1326    for m in &s.members {
1327        match btf.string_at(m.name_offset)?.as_ref() {
1328            "type" => {
1329                map_def.map_type = get_map_field(btf, m.btf_type)?;
1330            }
1331            "key" => {
1332                if let BtfType::Ptr(pty) = btf.type_by_id(m.btf_type)? {
1333                    let t = pty.btf_type;
1334                    map_def.key_size = btf.type_size(t)? as u32;
1335                    map_def.btf_key_type_id = t;
1336                } else {
1337                    return Err(BtfError::UnexpectedBtfType {
1338                        type_id: m.btf_type,
1339                    });
1340                }
1341            }
1342            "key_size" => {
1343                map_def.key_size = get_map_field(btf, m.btf_type)?;
1344            }
1345            "value" => {
1346                if let BtfType::Ptr(pty) = btf.type_by_id(m.btf_type)? {
1347                    let t = pty.btf_type;
1348                    map_def.value_size = btf.type_size(t)? as u32;
1349                    map_def.btf_value_type_id = t;
1350                } else {
1351                    return Err(BtfError::UnexpectedBtfType {
1352                        type_id: m.btf_type,
1353                    });
1354                }
1355            }
1356            "value_size" => {
1357                map_def.value_size = get_map_field(btf, m.btf_type)?;
1358            }
1359            "max_entries" => {
1360                map_def.max_entries = get_map_field(btf, m.btf_type)?;
1361            }
1362            "map_flags" => {
1363                map_def.map_flags = get_map_field(btf, m.btf_type)?;
1364            }
1365            "map_extra" => {
1366                map_def.map_extra = get_map_field(btf, m.btf_type)?.into();
1367            }
1368            "pinning" => {
1369                if is_inner {
1370                    return Err(BtfError::InnerMapCannotBePinned {
1371                        name: map_name.to_owned(),
1372                    });
1373                }
1374                let pinning = get_map_field(btf, m.btf_type)?;
1375                map_def.pinning = PinningType::try_from(pinning).unwrap_or_else(|_| {
1376                    debug!("{pinning} is not a valid pin type. using PIN_NONE");
1377                    PinningType::None
1378                });
1379            }
1380            "values" => {
1381                if is_inner {
1382                    return Err(BtfError::MultiLevelMapInMapNotSupported {
1383                        name: map_name.to_owned(),
1384                    });
1385                }
1386                // The inner map type is encoded as a zero-length array of pointers
1387                // to the inner struct, matching libbpf's parse_btf_map_def().
1388                let arr = match btf.type_by_id(m.btf_type)? {
1389                    BtfType::Array(Array { array, .. }) => array,
1390                    other => {
1391                        return Err(BtfError::UnexpectedBtfType {
1392                            type_id: other.btf_type().unwrap_or(0),
1393                        });
1394                    }
1395                };
1396                if arr.len != 0 {
1397                    return Err(BtfError::InvalidValuesSpec {
1398                        name: map_name.to_owned(),
1399                    });
1400                }
1401                let elem_type_id = btf.resolve_type(arr.element_type)?;
1402                let ptr = match btf.type_by_id(elem_type_id)? {
1403                    BtfType::Ptr(ptr) => ptr,
1404                    other => {
1405                        return Err(BtfError::UnexpectedBtfType {
1406                            type_id: other.btf_type().unwrap_or(0),
1407                        });
1408                    }
1409                };
1410                let inner_struct_id = btf.resolve_type(ptr.btf_type)?;
1411                let inner_s = match btf.type_by_id(inner_struct_id)? {
1412                    BtfType::Struct(s) => s,
1413                    other => {
1414                        return Err(BtfError::UnexpectedBtfType {
1415                            type_id: other.btf_type().unwrap_or(0),
1416                        });
1417                    }
1418                };
1419                let (inner_def, _) = parse_btf_map_struct(btf, inner_s, map_name, true)?;
1420                inner_map_def = Some(inner_def);
1421                // Map-of-maps value is always an fd (u32).
1422                map_def.value_size = size_of::<u32>() as u32;
1423            }
1424            other => {
1425                debug!("skipping unknown map section: {other}");
1426            }
1427        }
1428    }
1429    Ok((map_def, inner_map_def))
1430}
1431
1432/// Parses a [`bpf_map_info`] into a [`Map`].
1433pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
1434    // Some BTF maps are keyless, for example bloom filters, and may only set
1435    // `btf_value_type_id`. Checking the value type avoids treating those maps
1436    // as legacy maps.
1437    if info.btf_value_type_id != 0 {
1438        Map::Btf(BtfMap {
1439            def: BtfMapDef {
1440                map_type: info.type_,
1441                key_size: info.key_size,
1442                value_size: info.value_size,
1443                max_entries: info.max_entries,
1444                map_flags: info.map_flags,
1445                map_extra: info.map_extra,
1446                pinning: pinned,
1447                btf_key_type_id: info.btf_key_type_id,
1448                btf_value_type_id: info.btf_value_type_id,
1449            },
1450            inner_def: None,
1451            section_index: 0,
1452            symbol_index: 0,
1453            data: Vec::new(),
1454        })
1455    } else {
1456        Map::Legacy(LegacyMap {
1457            def: bpf_map_def {
1458                map_type: info.type_,
1459                key_size: info.key_size,
1460                value_size: info.value_size,
1461                max_entries: info.max_entries,
1462                map_flags: info.map_flags,
1463                pinning: pinned,
1464                id: info.id,
1465            },
1466            inner_def: None,
1467            section_index: 0,
1468            symbol_index: None,
1469            section_kind: EbpfSectionKind::Undefined,
1470            data: Vec::new(),
1471        })
1472    }
1473}
1474
1475/// Copies a block of eBPF instructions
1476pub fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
1477    if !data.len().is_multiple_of(size_of::<bpf_insn>()) {
1478        return Err(ParseError::InvalidProgramCode);
1479    }
1480    let instructions = data
1481        .chunks_exact(size_of::<bpf_insn>())
1482        .map(|d| unsafe { ptr::read_unaligned(d.as_ptr().cast()) })
1483        .collect::<Vec<_>>();
1484    Ok(instructions)
1485}
1486
1487fn get_func_and_line_info(
1488    btf_ext: Option<&BtfExt>,
1489    symbol: &Symbol,
1490    section: &Section<'_>,
1491    offset: usize,
1492    rewrite_insn_off: bool,
1493) -> (FuncSecInfo, LineSecInfo, usize, usize) {
1494    btf_ext
1495        .map(|btf_ext| {
1496            let instruction_offset = (offset / INS_SIZE) as u32;
1497            let symbol_size_instructions = (symbol.size as usize / INS_SIZE) as u32;
1498
1499            let mut func_info = btf_ext.func_info.get(section.name);
1500            func_info.func_info.retain_mut(|f| {
1501                let retain = f.insn_off == instruction_offset;
1502                if retain && rewrite_insn_off {
1503                    f.insn_off = 0;
1504                }
1505                retain
1506            });
1507
1508            let mut line_info = btf_ext.line_info.get(section.name);
1509            line_info
1510                .line_info
1511                .retain_mut(|l| match l.insn_off.checked_sub(instruction_offset) {
1512                    None => false,
1513                    Some(insn_off) => {
1514                        let retain = insn_off < symbol_size_instructions;
1515                        if retain && rewrite_insn_off {
1516                            l.insn_off = insn_off
1517                        }
1518                        retain
1519                    }
1520                });
1521            (
1522                func_info,
1523                line_info,
1524                btf_ext.func_info_rec_size(),
1525                btf_ext.line_info_rec_size(),
1526            )
1527        })
1528        .unwrap_or_default()
1529}
1530
1531#[cfg(test)]
1532mod tests {
1533    use assert_matches::assert_matches;
1534
1535    use super::*;
1536    use crate::generated::{bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER, btf_ext_header};
1537
1538    const FAKE_INS_LEN: u64 = 8;
1539
1540    fn fake_section<'a>(
1541        kind: EbpfSectionKind,
1542        name: &'a str,
1543        data: &'a [u8],
1544        index: Option<usize>,
1545    ) -> Section<'a> {
1546        let idx = index.unwrap_or(0);
1547        Section {
1548            index: SectionIndex(idx),
1549            kind,
1550            address: 0,
1551            name,
1552            data,
1553            size: data.len() as u64,
1554            relocations: Vec::new(),
1555        }
1556    }
1557
1558    fn fake_ins() -> bpf_insn {
1559        bpf_insn {
1560            code: 0,
1561            _bitfield_align_1: [],
1562            _bitfield_1: bpf_insn::new_bitfield_1(0, 0),
1563            off: 0,
1564            imm: 0,
1565        }
1566    }
1567
1568    fn fake_sym(obj: &mut Object, section_index: usize, address: u64, name: &str, size: u64) {
1569        let idx = obj.symbol_table.len();
1570        obj.symbol_table.insert(
1571            idx + 1,
1572            Symbol {
1573                index: idx + 1,
1574                section_index: Some(section_index),
1575                name: Some(name.to_string()),
1576                address,
1577                size,
1578                is_definition: false,
1579                kind: SymbolKind::Text,
1580            },
1581        );
1582        obj.symbols_by_section
1583            .entry(SectionIndex(section_index))
1584            .or_default()
1585            .push(idx + 1);
1586    }
1587
1588    fn bytes_of<T>(val: &T) -> &[u8] {
1589        // Safety: This is for testing only
1590        unsafe { crate::util::bytes_of(val) }
1591    }
1592
1593    #[test]
1594    fn test_parse_map_info_keyless_btf_map() {
1595        let mut info = unsafe { mem::zeroed::<bpf_map_info>() };
1596        info.type_ = BPF_MAP_TYPE_BLOOM_FILTER as u32;
1597        info.value_size = 4;
1598        info.max_entries = 64;
1599        info.map_extra = 3;
1600        info.btf_value_type_id = 7;
1601
1602        let map = parse_map_info(info, PinningType::None);
1603        assert_matches!(map, Map::Btf(m) => {
1604            assert_eq!(m.def.map_type, BPF_MAP_TYPE_BLOOM_FILTER as u32);
1605            assert_eq!(m.def.key_size, 0);
1606            assert_eq!(m.def.value_size, 4);
1607            assert_eq!(m.def.max_entries, 64);
1608            assert_eq!(m.def.map_extra, 3);
1609            assert_eq!(m.def.btf_key_type_id, 0);
1610            assert_eq!(m.def.btf_value_type_id, 7);
1611        });
1612    }
1613
1614    #[test]
1615    fn test_parse_generic_error() {
1616        assert_matches!(Object::parse(&b"foo"[..]), Err(ParseError::ElfError(_)))
1617    }
1618
1619    #[test]
1620    fn test_parse_license() {
1621        assert_matches!(parse_license(b""), Err(ParseError::InvalidLicense { .. }));
1622
1623        assert_matches!(parse_license(b"\0"), Err(ParseError::InvalidLicense { .. }));
1624
1625        assert_matches!(
1626            parse_license(b"GPL"),
1627            Err(ParseError::MissingLicenseNullTerminator { .. })
1628        );
1629
1630        assert_eq!(parse_license(b"GPL\0").unwrap().to_str().unwrap(), "GPL");
1631    }
1632
1633    #[expect(
1634        clippy::big_endian_bytes,
1635        clippy::little_endian_bytes,
1636        reason = "that's the point"
1637    )]
1638    #[test]
1639    fn test_parse_version() {
1640        assert_matches!(
1641            parse_version(b"", Endianness::Little),
1642            Err(ParseError::InvalidKernelVersion { .. })
1643        );
1644
1645        assert_matches!(
1646            parse_version(b"123", Endianness::Little),
1647            Err(ParseError::InvalidKernelVersion { .. })
1648        );
1649
1650        assert_matches!(
1651            parse_version(&0xFFFF_FFFEu32.to_le_bytes(), Endianness::Little),
1652            Ok(None)
1653        );
1654
1655        assert_matches!(
1656            parse_version(&0xFFFF_FFFEu32.to_be_bytes(), Endianness::Big),
1657            Ok(None)
1658        );
1659
1660        assert_matches!(
1661            parse_version(&1234u32.to_le_bytes(), Endianness::Little),
1662            Ok(Some(1234))
1663        );
1664    }
1665
1666    #[test]
1667    fn test_parse_map_def_error() {
1668        assert_matches!(
1669            parse_map_def("foo", &[]),
1670            Err(ParseError::InvalidMapDefinition { .. })
1671        );
1672    }
1673
1674    #[test]
1675    fn test_parse_map_short() {
1676        let def = bpf_map_def {
1677            map_type: 1,
1678            key_size: 2,
1679            value_size: 3,
1680            max_entries: 4,
1681            map_flags: 5,
1682            id: 0,
1683            pinning: PinningType::None,
1684        };
1685
1686        assert_eq!(
1687            parse_map_def("foo", &bytes_of(&def)[..MINIMUM_MAP_SIZE]).unwrap(),
1688            def
1689        );
1690    }
1691
1692    #[test]
1693    fn test_parse_map_def() {
1694        let def = bpf_map_def {
1695            map_type: 1,
1696            key_size: 2,
1697            value_size: 3,
1698            max_entries: 4,
1699            map_flags: 5,
1700            id: 6,
1701            pinning: PinningType::ByName,
1702        };
1703
1704        assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
1705    }
1706
1707    #[test]
1708    fn test_parse_map_def_with_padding() {
1709        let def = bpf_map_def {
1710            map_type: 1,
1711            key_size: 2,
1712            value_size: 3,
1713            max_entries: 4,
1714            map_flags: 5,
1715            id: 6,
1716            pinning: PinningType::ByName,
1717        };
1718        let mut buf = [0u8; 128];
1719        unsafe { ptr::write_unaligned(buf.as_mut_ptr().cast(), def) }
1720
1721        assert_eq!(parse_map_def("foo", &buf).unwrap(), def);
1722    }
1723
1724    #[test]
1725    fn test_parse_map_data() {
1726        let map_data = b"map data";
1727        assert_matches!(
1728            parse_data_map_section(
1729                &fake_section(
1730                    EbpfSectionKind::Data,
1731                    ".bss",
1732                    map_data,
1733                    None,
1734                ),
1735            ),
1736            Map::Legacy(LegacyMap {
1737                section_index: 0,
1738                section_kind: EbpfSectionKind::Data,
1739                symbol_index: None,
1740                def: bpf_map_def {
1741                    map_type: _map_type,
1742                    key_size: 4,
1743                    value_size,
1744                    max_entries: 1,
1745                    map_flags: 0,
1746                    id: 0,
1747                    pinning: PinningType::None,
1748                },
1749                data,
1750                ..
1751            }) if data == map_data && value_size == map_data.len() as u32
1752        )
1753    }
1754
1755    fn fake_obj() -> Object {
1756        Object::new(Endianness::Little, CString::new("GPL").unwrap(), None)
1757    }
1758
1759    #[test]
1760    fn sanitizes_empty_btf_files_to_none() {
1761        let mut obj = fake_obj();
1762
1763        let btf = Btf::new();
1764        let btf_bytes = btf.to_bytes();
1765        obj.parse_section(fake_section(EbpfSectionKind::Btf, ".BTF", &btf_bytes, None))
1766            .unwrap();
1767
1768        const FUNC_INFO_LEN: u32 = 4;
1769        const LINE_INFO_LEN: u32 = 4;
1770        const CORE_RELO_LEN: u32 = 16;
1771        let ext_header = btf_ext_header {
1772            magic: 0xeb9f,
1773            version: 1,
1774            flags: 0,
1775            hdr_len: 24,
1776            func_info_off: 0,
1777            func_info_len: FUNC_INFO_LEN,
1778            line_info_off: FUNC_INFO_LEN,
1779            line_info_len: LINE_INFO_LEN,
1780            core_relo_off: FUNC_INFO_LEN + LINE_INFO_LEN,
1781            core_relo_len: CORE_RELO_LEN,
1782        };
1783        let btf_ext_bytes = bytes_of::<btf_ext_header>(&ext_header).to_vec();
1784        obj.parse_section(fake_section(
1785            EbpfSectionKind::BtfExt,
1786            ".BTF.ext",
1787            &btf_ext_bytes,
1788            None,
1789        ))
1790        .unwrap();
1791
1792        let btf = obj.fixup_and_sanitize_btf(&BtfFeatures::default()).unwrap();
1793        assert!(btf.is_none());
1794    }
1795
1796    #[test]
1797    fn test_parse_program_error() {
1798        let mut obj = fake_obj();
1799        fake_sym(&mut obj, 0, 0, "foo", 1);
1800        assert_matches!(
1801            obj.parse_programs(&fake_section(
1802                EbpfSectionKind::Program,
1803                "kprobe/foo",
1804                &42u32.to_ne_bytes(),
1805                None,
1806            )),
1807            Err(ParseError::InvalidProgramCode)
1808        );
1809    }
1810
1811    #[test]
1812    fn test_parse_program() {
1813        let mut obj = fake_obj();
1814        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
1815
1816        obj.parse_programs(&fake_section(
1817            EbpfSectionKind::Program,
1818            "kprobe/foo",
1819            bytes_of(&fake_ins()),
1820            None,
1821        ))
1822        .unwrap();
1823
1824        let prog_foo = &obj.programs["foo"];
1825
1826        assert_matches!(prog_foo, Program {
1827            license,
1828            kernel_version: None,
1829            section: ProgramSection::KProbe,
1830            ..
1831        } => assert_eq!(license.to_str().unwrap(), "GPL"));
1832
1833        assert_matches!(
1834            obj.functions.get(&prog_foo.function_key()),
1835            Some(Function {
1836                name,
1837                address: 0,
1838                section_index: SectionIndex(0),
1839                section_offset: 0,
1840                instructions,
1841            ..}) if name == "foo" && instructions.len() == 1
1842        )
1843    }
1844
1845    #[test]
1846    fn test_parse_section_map() {
1847        let mut obj = fake_obj();
1848        fake_sym(&mut obj, 0, 0, "foo", size_of::<bpf_map_def>() as u64);
1849        assert_matches!(
1850            obj.parse_section(fake_section(
1851                EbpfSectionKind::Maps,
1852                "maps/foo",
1853                bytes_of(&bpf_map_def {
1854                    map_type: 1,
1855                    key_size: 2,
1856                    value_size: 3,
1857                    max_entries: 4,
1858                    map_flags: 5,
1859                    ..Default::default()
1860                }),
1861                None,
1862            )),
1863            Ok(())
1864        );
1865        assert!(obj.maps.contains_key("foo"));
1866    }
1867
1868    #[test]
1869    fn test_parse_multiple_program_in_same_section() {
1870        let mut obj = fake_obj();
1871        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
1872        fake_sym(&mut obj, 0, FAKE_INS_LEN, "bar", FAKE_INS_LEN);
1873
1874        let insns = [fake_ins(), fake_ins()];
1875        let data = bytes_of(&insns);
1876
1877        obj.parse_programs(&fake_section(
1878            EbpfSectionKind::Program,
1879            "kprobe",
1880            data,
1881            None,
1882        ))
1883        .unwrap();
1884
1885        let prog_foo = &obj.programs["foo"];
1886        let function_foo = &obj.functions[&prog_foo.function_key()];
1887        let prog_bar = &obj.programs["bar"];
1888        let function_bar = &obj.functions[&prog_bar.function_key()];
1889
1890        assert_matches!(prog_foo, Program {
1891            license,
1892            kernel_version: None,
1893            section: ProgramSection::KProbe,
1894            ..
1895        } => assert_eq!(license.to_str().unwrap(), "GPL"));
1896        assert_matches!(
1897            function_foo,
1898            Function {
1899                name,
1900                address: 0,
1901                section_index: SectionIndex(0),
1902                section_offset: 0,
1903                instructions,
1904                ..
1905            }  if name == "foo" && instructions.len() == 1
1906        );
1907
1908        assert_matches!(prog_bar, Program {
1909            license,
1910            kernel_version: None,
1911            section: ProgramSection::KProbe ,
1912            ..
1913        } => assert_eq!(license.to_str().unwrap(), "GPL"));
1914        assert_matches!(
1915            function_bar,
1916            Function {
1917                name,
1918                address: 8,
1919                section_index: SectionIndex(0),
1920                section_offset: 8,
1921                instructions,
1922                ..
1923            }  if name == "bar" && instructions.len() == 1
1924        );
1925    }
1926
1927    #[test]
1928    fn test_parse_section_multiple_maps() {
1929        let mut obj = fake_obj();
1930        fake_sym(&mut obj, 0, 0, "foo", size_of::<bpf_map_def>() as u64);
1931        fake_sym(&mut obj, 0, 28, "bar", size_of::<bpf_map_def>() as u64);
1932        fake_sym(&mut obj, 0, 60, "baz", size_of::<bpf_map_def>() as u64);
1933        let def = &bpf_map_def {
1934            map_type: 1,
1935            key_size: 2,
1936            value_size: 3,
1937            max_entries: 4,
1938            map_flags: 5,
1939            ..Default::default()
1940        };
1941        let map_data = bytes_of(def).to_vec();
1942        let mut buf = vec![];
1943        buf.extend(&map_data);
1944        buf.extend(&map_data);
1945        // throw in some padding
1946        buf.extend([0, 0, 0, 0]);
1947        buf.extend(&map_data);
1948        assert_matches!(
1949            obj.parse_section(fake_section(
1950                EbpfSectionKind::Maps,
1951                "maps",
1952                buf.as_slice(),
1953                None
1954            )),
1955            Ok(())
1956        );
1957        assert!(obj.maps.contains_key("foo"));
1958        assert!(obj.maps.contains_key("bar"));
1959        assert!(obj.maps.contains_key("baz"));
1960        for map in obj.maps.values() {
1961            assert_matches!(map, Map::Legacy(m) => {
1962                assert_eq!(&m.def, def);
1963            })
1964        }
1965    }
1966
1967    #[test]
1968    fn test_parse_section_data() {
1969        let mut obj = fake_obj();
1970        assert_matches!(
1971            obj.parse_section(fake_section(
1972                EbpfSectionKind::Data,
1973                ".bss",
1974                b"map data",
1975                None
1976            )),
1977            Ok(())
1978        );
1979        assert!(obj.maps.contains_key(".bss"));
1980
1981        assert_matches!(
1982            obj.parse_section(fake_section(
1983                EbpfSectionKind::Data,
1984                ".rodata",
1985                b"map data",
1986                None
1987            )),
1988            Ok(())
1989        );
1990        assert!(obj.maps.contains_key(".rodata"));
1991
1992        assert_matches!(
1993            obj.parse_section(fake_section(
1994                EbpfSectionKind::Data,
1995                ".rodata.boo",
1996                b"map data",
1997                None
1998            )),
1999            Ok(())
2000        );
2001        assert!(obj.maps.contains_key(".rodata.boo"));
2002
2003        assert_matches!(
2004            obj.parse_section(fake_section(
2005                EbpfSectionKind::Data,
2006                ".data",
2007                b"map data",
2008                None
2009            )),
2010            Ok(())
2011        );
2012        assert!(obj.maps.contains_key(".data"));
2013
2014        assert_matches!(
2015            obj.parse_section(fake_section(
2016                EbpfSectionKind::Data,
2017                ".data.boo",
2018                b"map data",
2019                None
2020            )),
2021            Ok(())
2022        );
2023        assert!(obj.maps.contains_key(".data.boo"));
2024    }
2025
2026    #[test]
2027    fn test_parse_section_kprobe() {
2028        let mut obj = fake_obj();
2029        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2030
2031        assert_matches!(
2032            obj.parse_section(fake_section(
2033                EbpfSectionKind::Program,
2034                "kprobe/foo",
2035                bytes_of(&fake_ins()),
2036                None
2037            )),
2038            Ok(())
2039        );
2040        assert_matches!(
2041            obj.programs.get("foo"),
2042            Some(Program {
2043                section: ProgramSection::KProbe,
2044                ..
2045            })
2046        );
2047    }
2048
2049    #[test]
2050    fn test_parse_section_uprobe() {
2051        let mut obj = fake_obj();
2052        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2053
2054        assert_matches!(
2055            obj.parse_section(fake_section(
2056                EbpfSectionKind::Program,
2057                "uprobe/foo",
2058                bytes_of(&fake_ins()),
2059                None
2060            )),
2061            Ok(())
2062        );
2063        assert_matches!(
2064            obj.programs.get("foo"),
2065            Some(Program {
2066                section: ProgramSection::UProbe { .. },
2067                ..
2068            })
2069        );
2070    }
2071
2072    #[test]
2073    fn test_parse_section_uprobe_sleepable() {
2074        let mut obj = fake_obj();
2075        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2076
2077        assert_matches!(
2078            obj.parse_section(fake_section(
2079                EbpfSectionKind::Program,
2080                "uprobe.s/foo",
2081                bytes_of(&fake_ins()),
2082                None
2083            )),
2084            Ok(())
2085        );
2086        assert_matches!(
2087            obj.programs.get("foo"),
2088            Some(Program {
2089                section: ProgramSection::UProbe {
2090                    sleepable: true,
2091                    ..
2092                },
2093                ..
2094            })
2095        );
2096    }
2097
2098    #[test]
2099    fn test_parse_section_uretprobe() {
2100        let mut obj = fake_obj();
2101        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2102
2103        assert_matches!(
2104            obj.parse_section(fake_section(
2105                EbpfSectionKind::Program,
2106                "uretprobe/foo",
2107                bytes_of(&fake_ins()),
2108                None
2109            )),
2110            Ok(())
2111        );
2112        assert_matches!(
2113            obj.programs.get("foo"),
2114            Some(Program {
2115                section: ProgramSection::URetProbe { .. },
2116                ..
2117            })
2118        );
2119    }
2120
2121    #[test]
2122    fn test_parse_section_uretprobe_sleepable() {
2123        let mut obj = fake_obj();
2124        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2125
2126        assert_matches!(
2127            obj.parse_section(fake_section(
2128                EbpfSectionKind::Program,
2129                "uretprobe.s/foo",
2130                bytes_of(&fake_ins()),
2131                None
2132            )),
2133            Ok(())
2134        );
2135        assert_matches!(
2136            obj.programs.get("foo"),
2137            Some(Program {
2138                section: ProgramSection::URetProbe {
2139                    sleepable: true,
2140                    ..
2141                },
2142                ..
2143            })
2144        );
2145    }
2146
2147    #[test]
2148    fn test_parse_section_trace_point() {
2149        let mut obj = fake_obj();
2150        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2151        fake_sym(&mut obj, 1, 0, "bar", FAKE_INS_LEN);
2152
2153        assert_matches!(
2154            obj.parse_section(fake_section(
2155                EbpfSectionKind::Program,
2156                "tracepoint/foo",
2157                bytes_of(&fake_ins()),
2158                None
2159            )),
2160            Ok(())
2161        );
2162        assert_matches!(
2163            obj.programs.get("foo"),
2164            Some(Program {
2165                section: ProgramSection::TracePoint,
2166                ..
2167            })
2168        );
2169
2170        assert_matches!(
2171            obj.parse_section(fake_section(
2172                EbpfSectionKind::Program,
2173                "tp/foo/bar",
2174                bytes_of(&fake_ins()),
2175                Some(1),
2176            )),
2177            Ok(())
2178        );
2179        assert_matches!(
2180            obj.programs.get("bar"),
2181            Some(Program {
2182                section: ProgramSection::TracePoint,
2183                ..
2184            })
2185        );
2186    }
2187
2188    #[test]
2189    fn test_parse_section_socket_filter() {
2190        let mut obj = fake_obj();
2191        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2192
2193        assert_matches!(
2194            obj.parse_section(fake_section(
2195                EbpfSectionKind::Program,
2196                "socket/foo",
2197                bytes_of(&fake_ins()),
2198                None
2199            )),
2200            Ok(())
2201        );
2202        assert_matches!(
2203            obj.programs.get("foo"),
2204            Some(Program {
2205                section: ProgramSection::SocketFilter,
2206                ..
2207            })
2208        );
2209    }
2210
2211    #[test]
2212    fn test_parse_section_xdp() {
2213        let mut obj = fake_obj();
2214        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2215
2216        assert_matches!(
2217            obj.parse_section(fake_section(
2218                EbpfSectionKind::Program,
2219                "xdp",
2220                bytes_of(&fake_ins()),
2221                None
2222            )),
2223            Ok(())
2224        );
2225        assert_matches!(
2226            obj.programs.get("foo"),
2227            Some(Program {
2228                section: ProgramSection::Xdp { frags: false, .. },
2229                ..
2230            })
2231        );
2232    }
2233
2234    #[test]
2235    fn test_parse_section_xdp_frags() {
2236        let mut obj = fake_obj();
2237        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2238
2239        assert_matches!(
2240            obj.parse_section(fake_section(
2241                EbpfSectionKind::Program,
2242                "xdp.frags",
2243                bytes_of(&fake_ins()),
2244                None
2245            )),
2246            Ok(())
2247        );
2248        assert_matches!(
2249            obj.programs.get("foo"),
2250            Some(Program {
2251                section: ProgramSection::Xdp { frags: true, .. },
2252                ..
2253            })
2254        );
2255    }
2256
2257    #[test]
2258    fn test_parse_section_sk_reuseport() {
2259        let mut obj = fake_obj();
2260        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2261
2262        assert_matches!(
2263            obj.parse_section(fake_section(
2264                EbpfSectionKind::Program,
2265                "sk_reuseport",
2266                bytes_of(&fake_ins()),
2267                None
2268            )),
2269            Ok(())
2270        );
2271        assert_matches!(
2272            obj.programs.get("foo"),
2273            Some(Program {
2274                section: ProgramSection::SkReuseport {
2275                    attach_type: SkReuseportAttachType::Select,
2276                },
2277                ..
2278            })
2279        );
2280    }
2281
2282    #[test]
2283    fn test_parse_section_sk_reuseport_migrate() {
2284        let mut obj = fake_obj();
2285        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2286
2287        assert_matches!(
2288            obj.parse_section(fake_section(
2289                EbpfSectionKind::Program,
2290                "sk_reuseport/migrate",
2291                bytes_of(&fake_ins()),
2292                None
2293            )),
2294            Ok(())
2295        );
2296        assert_matches!(
2297            obj.programs.get("foo"),
2298            Some(Program {
2299                section: ProgramSection::SkReuseport {
2300                    attach_type: SkReuseportAttachType::SelectOrMigrate,
2301                },
2302                ..
2303            })
2304        );
2305    }
2306
2307    #[test]
2308    fn test_parse_section_raw_tp() {
2309        let mut obj = fake_obj();
2310        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2311        fake_sym(&mut obj, 1, 0, "bar", FAKE_INS_LEN);
2312
2313        assert_matches!(
2314            obj.parse_section(fake_section(
2315                EbpfSectionKind::Program,
2316                "raw_tp/foo",
2317                bytes_of(&fake_ins()),
2318                None
2319            )),
2320            Ok(())
2321        );
2322        assert_matches!(
2323            obj.programs.get("foo"),
2324            Some(Program {
2325                section: ProgramSection::RawTracePoint,
2326                ..
2327            })
2328        );
2329
2330        assert_matches!(
2331            obj.parse_section(fake_section(
2332                EbpfSectionKind::Program,
2333                "raw_tracepoint/bar",
2334                bytes_of(&fake_ins()),
2335                Some(1)
2336            )),
2337            Ok(())
2338        );
2339        assert_matches!(
2340            obj.programs.get("bar"),
2341            Some(Program {
2342                section: ProgramSection::RawTracePoint,
2343                ..
2344            })
2345        );
2346    }
2347
2348    #[test]
2349    fn test_parse_section_lsm() {
2350        let mut obj = fake_obj();
2351        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2352
2353        assert_matches!(
2354            obj.parse_section(fake_section(
2355                EbpfSectionKind::Program,
2356                "lsm/foo",
2357                bytes_of(&fake_ins()),
2358                None
2359            )),
2360            Ok(())
2361        );
2362        assert_matches!(
2363            obj.programs.get("foo"),
2364            Some(Program {
2365                section: ProgramSection::Lsm { sleepable: false },
2366                ..
2367            })
2368        );
2369    }
2370
2371    #[test]
2372    fn test_parse_section_lsm_sleepable() {
2373        let mut obj = fake_obj();
2374        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2375
2376        assert_matches!(
2377            obj.parse_section(fake_section(
2378                EbpfSectionKind::Program,
2379                "lsm.s/foo",
2380                bytes_of(&fake_ins()),
2381                None
2382            )),
2383            Ok(())
2384        );
2385        assert_matches!(
2386            obj.programs.get("foo"),
2387            Some(Program {
2388                section: ProgramSection::Lsm {
2389                    sleepable: true,
2390                    ..
2391                },
2392                ..
2393            })
2394        );
2395    }
2396
2397    #[test]
2398    fn test_parse_section_lsm_cgroup() {
2399        let mut obj = fake_obj();
2400        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2401
2402        assert_matches!(
2403            obj.parse_section(fake_section(
2404                EbpfSectionKind::Program,
2405                "lsm_cgroup/foo",
2406                bytes_of(&fake_ins()),
2407                None
2408            )),
2409            Ok(())
2410        );
2411        assert_matches!(
2412            obj.programs.get("foo"),
2413            Some(Program {
2414                section: ProgramSection::LsmCgroup,
2415                ..
2416            })
2417        );
2418    }
2419
2420    #[test]
2421    fn test_parse_section_btf_tracepoint() {
2422        let mut obj = fake_obj();
2423        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2424
2425        assert_matches!(
2426            obj.parse_section(fake_section(
2427                EbpfSectionKind::Program,
2428                "tp_btf/foo",
2429                bytes_of(&fake_ins()),
2430                None
2431            )),
2432            Ok(())
2433        );
2434        assert_matches!(
2435            obj.programs.get("foo"),
2436            Some(Program {
2437                section: ProgramSection::BtfTracePoint,
2438                ..
2439            })
2440        );
2441    }
2442
2443    #[test]
2444    fn test_parse_section_skskb_unnamed() {
2445        let mut obj = fake_obj();
2446        fake_sym(&mut obj, 0, 0, "stream_parser", FAKE_INS_LEN);
2447
2448        assert_matches!(
2449            obj.parse_section(fake_section(
2450                EbpfSectionKind::Program,
2451                "sk_skb/stream_parser",
2452                bytes_of(&fake_ins()),
2453                None
2454            )),
2455            Ok(())
2456        );
2457        assert_matches!(
2458            obj.programs.get("stream_parser"),
2459            Some(Program {
2460                section: ProgramSection::SkSkbStream {
2461                    kind: SkSkbKind::StreamParser
2462                },
2463                ..
2464            })
2465        );
2466    }
2467
2468    #[test]
2469    fn test_parse_section_skskb_named() {
2470        let mut obj = fake_obj();
2471        fake_sym(&mut obj, 0, 0, "my_parser", FAKE_INS_LEN);
2472
2473        assert_matches!(
2474            obj.parse_section(fake_section(
2475                EbpfSectionKind::Program,
2476                "sk_skb/stream_parser/my_parser",
2477                bytes_of(&fake_ins()),
2478                None
2479            )),
2480            Ok(())
2481        );
2482        assert_matches!(
2483            obj.programs.get("my_parser"),
2484            Some(Program {
2485                section: ProgramSection::SkSkbStream {
2486                    kind: SkSkbKind::StreamParser
2487                },
2488                ..
2489            })
2490        );
2491    }
2492
2493    #[test]
2494    fn test_parse_section_fentry() {
2495        let mut obj = fake_obj();
2496        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2497
2498        assert_matches!(
2499            obj.parse_section(fake_section(
2500                EbpfSectionKind::Program,
2501                "fentry/foo",
2502                bytes_of(&fake_ins()),
2503                None
2504            )),
2505            Ok(())
2506        );
2507        assert_matches!(
2508            obj.programs.get("foo"),
2509            Some(Program {
2510                section: ProgramSection::FEntry { .. },
2511                ..
2512            })
2513        );
2514    }
2515
2516    #[test]
2517    fn test_parse_section_fentry_sleepable() {
2518        let mut obj = fake_obj();
2519        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2520
2521        assert_matches!(
2522            obj.parse_section(fake_section(
2523                EbpfSectionKind::Program,
2524                "fentry.s/foo",
2525                bytes_of(&fake_ins()),
2526                None
2527            )),
2528            Ok(())
2529        );
2530        assert_matches!(
2531            obj.programs.get("foo"),
2532            Some(Program {
2533                section: ProgramSection::FEntry {
2534                    sleepable: true,
2535                    ..
2536                },
2537                ..
2538            })
2539        );
2540    }
2541
2542    #[test]
2543    fn test_parse_section_fexit() {
2544        let mut obj = fake_obj();
2545        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2546
2547        assert_matches!(
2548            obj.parse_section(fake_section(
2549                EbpfSectionKind::Program,
2550                "fexit/foo",
2551                bytes_of(&fake_ins()),
2552                None
2553            )),
2554            Ok(())
2555        );
2556        assert_matches!(
2557            obj.programs.get("foo"),
2558            Some(Program {
2559                section: ProgramSection::FExit { .. },
2560                ..
2561            })
2562        );
2563    }
2564
2565    #[test]
2566    fn test_parse_section_fexit_sleepable() {
2567        let mut obj = fake_obj();
2568        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2569
2570        assert_matches!(
2571            obj.parse_section(fake_section(
2572                EbpfSectionKind::Program,
2573                "fexit.s/foo",
2574                bytes_of(&fake_ins()),
2575                None
2576            )),
2577            Ok(())
2578        );
2579        assert_matches!(
2580            obj.programs.get("foo"),
2581            Some(Program {
2582                section: ProgramSection::FExit {
2583                    sleepable: true,
2584                    ..
2585                },
2586                ..
2587            })
2588        );
2589    }
2590
2591    #[test]
2592    fn test_parse_section_cgroup_skb_ingress_unnamed() {
2593        let mut obj = fake_obj();
2594        fake_sym(&mut obj, 0, 0, "ingress", FAKE_INS_LEN);
2595
2596        assert_matches!(
2597            obj.parse_section(fake_section(
2598                EbpfSectionKind::Program,
2599                "cgroup_skb/ingress",
2600                bytes_of(&fake_ins()),
2601                None
2602            )),
2603            Ok(())
2604        );
2605        assert_matches!(
2606            obj.programs.get("ingress"),
2607            Some(Program {
2608                section: ProgramSection::CgroupSkb {
2609                    attach_type: Some(CgroupSkbAttachType::Ingress),
2610                },
2611                ..
2612            })
2613        );
2614    }
2615
2616    #[test]
2617    fn test_parse_section_cgroup_skb_ingress_named() {
2618        let mut obj = fake_obj();
2619        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2620
2621        assert_matches!(
2622            obj.parse_section(fake_section(
2623                EbpfSectionKind::Program,
2624                "cgroup_skb/ingress/foo",
2625                bytes_of(&fake_ins()),
2626                None
2627            )),
2628            Ok(())
2629        );
2630        assert_matches!(
2631            obj.programs.get("foo"),
2632            Some(Program {
2633                section: ProgramSection::CgroupSkb {
2634                    attach_type: Some(CgroupSkbAttachType::Ingress),
2635                },
2636                ..
2637            })
2638        );
2639    }
2640
2641    #[test]
2642    fn test_parse_section_cgroup_skb_no_direction_unnamed() {
2643        let mut obj = fake_obj();
2644        fake_sym(&mut obj, 0, 0, "skb", FAKE_INS_LEN);
2645
2646        assert_matches!(
2647            obj.parse_section(fake_section(
2648                EbpfSectionKind::Program,
2649                "cgroup/skb",
2650                bytes_of(&fake_ins()),
2651                None
2652            )),
2653            Ok(())
2654        );
2655        assert_matches!(
2656            obj.programs.get("skb"),
2657            Some(Program {
2658                section: ProgramSection::CgroupSkb { attach_type: None },
2659                ..
2660            })
2661        );
2662    }
2663
2664    #[test]
2665    fn test_parse_section_cgroup_skb_no_direction_named() {
2666        let mut obj = fake_obj();
2667        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2668
2669        assert_matches!(
2670            obj.parse_section(fake_section(
2671                EbpfSectionKind::Program,
2672                "cgroup/skb/foo",
2673                bytes_of(&fake_ins()),
2674                None
2675            )),
2676            Ok(())
2677        );
2678        assert_matches!(
2679            obj.programs.get("foo"),
2680            Some(Program {
2681                section: ProgramSection::CgroupSkb { attach_type: None },
2682                ..
2683            })
2684        );
2685    }
2686
2687    #[test]
2688    fn test_parse_section_sock_addr_named() {
2689        let mut obj = fake_obj();
2690        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2691
2692        assert_matches!(
2693            obj.parse_section(fake_section(
2694                EbpfSectionKind::Program,
2695                "cgroup/connect4/foo",
2696                bytes_of(&fake_ins()),
2697                None
2698            )),
2699            Ok(())
2700        );
2701        assert_matches!(
2702            obj.programs.get("foo"),
2703            Some(Program {
2704                section: ProgramSection::CgroupSockAddr {
2705                    attach_type: CgroupSockAddrAttachType::Connect4,
2706                    ..
2707                },
2708                ..
2709            })
2710        );
2711    }
2712
2713    #[test]
2714    fn test_parse_section_sock_addr_unnamed() {
2715        let mut obj = fake_obj();
2716        fake_sym(&mut obj, 0, 0, "connect4", FAKE_INS_LEN);
2717
2718        assert_matches!(
2719            obj.parse_section(fake_section(
2720                EbpfSectionKind::Program,
2721                "cgroup/connect4",
2722                bytes_of(&fake_ins()),
2723                None
2724            )),
2725            Ok(())
2726        );
2727        assert_matches!(
2728            obj.programs.get("connect4"),
2729            Some(Program {
2730                section: ProgramSection::CgroupSockAddr {
2731                    attach_type: CgroupSockAddrAttachType::Connect4,
2732                    ..
2733                },
2734                ..
2735            })
2736        );
2737    }
2738
2739    #[test]
2740    fn test_parse_section_sockopt_named() {
2741        let mut obj = fake_obj();
2742        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2743
2744        assert_matches!(
2745            obj.parse_section(fake_section(
2746                EbpfSectionKind::Program,
2747                "cgroup/getsockopt/foo",
2748                bytes_of(&fake_ins()),
2749                None
2750            )),
2751            Ok(())
2752        );
2753        assert_matches!(
2754            obj.programs.get("foo"),
2755            Some(Program {
2756                section: ProgramSection::CgroupSockopt {
2757                    attach_type: CgroupSockoptAttachType::Get,
2758                    ..
2759                },
2760                ..
2761            })
2762        );
2763    }
2764
2765    #[test]
2766    fn test_parse_section_sockopt_unnamed() {
2767        let mut obj = fake_obj();
2768        fake_sym(&mut obj, 0, 0, "getsockopt", FAKE_INS_LEN);
2769
2770        assert_matches!(
2771            obj.parse_section(fake_section(
2772                EbpfSectionKind::Program,
2773                "cgroup/getsockopt",
2774                bytes_of(&fake_ins()),
2775                None
2776            )),
2777            Ok(())
2778        );
2779        assert_matches!(
2780            obj.programs.get("getsockopt"),
2781            Some(Program {
2782                section: ProgramSection::CgroupSockopt {
2783                    attach_type: CgroupSockoptAttachType::Get,
2784                    ..
2785                },
2786                ..
2787            })
2788        );
2789    }
2790
2791    #[test]
2792    fn test_patch_map_data() {
2793        let mut obj = fake_obj();
2794        obj.maps.insert(
2795            ".rodata".to_owned(),
2796            Map::Legacy(LegacyMap {
2797                def: bpf_map_def {
2798                    map_type: BPF_MAP_TYPE_ARRAY as u32,
2799                    key_size: size_of::<u32>() as u32,
2800                    value_size: 3,
2801                    max_entries: 1,
2802                    map_flags: BPF_F_RDONLY_PROG,
2803                    id: 1,
2804                    pinning: PinningType::None,
2805                },
2806                inner_def: None,
2807                section_index: 1,
2808                section_kind: EbpfSectionKind::Rodata,
2809                symbol_index: Some(1),
2810                data: vec![0, 0, 0],
2811            }),
2812        );
2813        obj.symbol_table.insert(
2814            1,
2815            Symbol {
2816                index: 1,
2817                section_index: Some(1),
2818                name: Some("my_config".to_owned()),
2819                address: 0,
2820                size: 3,
2821                is_definition: true,
2822                kind: SymbolKind::Data,
2823            },
2824        );
2825
2826        let test_data: &[u8] = &[1, 2, 3];
2827        obj.patch_map_data(HashMap::from([
2828            ("my_config", (test_data, true)),
2829            ("optional_variable", (test_data, false)),
2830        ]))
2831        .unwrap();
2832
2833        let map = &obj.maps[".rodata"];
2834        assert_eq!(test_data, map.data());
2835    }
2836
2837    #[test]
2838    fn test_parse_btf_map_section() {
2839        let mut obj = fake_obj();
2840        fake_sym(&mut obj, 0, 0, "map_1", 0);
2841        fake_sym(&mut obj, 0, 0, "map_2", 0);
2842        // generated from:
2843        // objcopy --dump-section .BTF=test.btf ./target/bpfel-unknown-none/debug/multimap-btf.bpf.o
2844        // hexdump -v  -e '7/1 "0x%02X, " 1/1  " 0x%02X,\n"' test.btf
2845        let data: &[u8] = if cfg!(target_endian = "little") {
2846            &[
2847                0x9F, 0xEB, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x01,
2848                0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2849                0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2850                0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
2851                0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00,
2852                0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
2853                0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2854                0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
2855                0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
2856                0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
2857                0x09, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0A, 0x00,
2858                0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00,
2859                0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0C, 0x00,
2860                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
2861                0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2862                0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
2863                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x05, 0x00,
2864                0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
2865                0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xC0, 0x00,
2866                0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0D, 0x00, 0x00, 0x00,
2867                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x20, 0x00,
2868                0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2869                0x4A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00,
2870                0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
2871                0x0B, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
2872                0x00, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2873                0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
2874                0x00, 0x0D, 0x02, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
2875                0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x12, 0x00, 0x00, 0x00, 0xB0, 0x01,
2876                0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01,
2877                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
2878                0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00,
2879                0x00, 0x00, 0x00, 0x0E, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xBE, 0x01,
2880                0x00, 0x00, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
2881                0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
2882                0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0F,
2883                0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
2884                0x00, 0x00, 0x00, 0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x41, 0x52, 0x52, 0x41, 0x59,
2885                0x5F, 0x53, 0x49, 0x5A, 0x45, 0x5F, 0x54, 0x59, 0x50, 0x45, 0x5F, 0x5F, 0x00, 0x5F,
2886                0x5F, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6E, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x20,
2887                0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x00, 0x75, 0x6E, 0x73, 0x69,
2888                0x67, 0x6E, 0x65, 0x64, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x6E, 0x67,
2889                0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x6B, 0x65, 0x79, 0x00, 0x76, 0x61, 0x6C, 0x75,
2890                0x65, 0x00, 0x6D, 0x61, 0x78, 0x5F, 0x65, 0x6E, 0x74, 0x72, 0x69, 0x65, 0x73, 0x00,
2891                0x6D, 0x61, 0x70, 0x5F, 0x31, 0x00, 0x6D, 0x61, 0x70, 0x5F, 0x32, 0x00, 0x63, 0x74,
2892                0x78, 0x00, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F, 0x67, 0x00, 0x74, 0x72, 0x61,
2893                0x63, 0x65, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x00, 0x2F, 0x76, 0x61, 0x72, 0x2F, 0x68,
2894                0x6F, 0x6D, 0x65, 0x2F, 0x64, 0x61, 0x76, 0x65, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x61,
2895                0x79, 0x61, 0x2D, 0x72, 0x73, 0x2F, 0x61, 0x79, 0x61, 0x2F, 0x74, 0x65, 0x73, 0x74,
2896                0x2F, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2D, 0x65,
2897                0x62, 0x70, 0x66, 0x2F, 0x73, 0x72, 0x63, 0x2F, 0x62, 0x70, 0x66, 0x2F, 0x6D, 0x75,
2898                0x6C, 0x74, 0x69, 0x6D, 0x61, 0x70, 0x2D, 0x62, 0x74, 0x66, 0x2E, 0x62, 0x70, 0x66,
2899                0x2E, 0x63, 0x00, 0x69, 0x6E, 0x74, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F,
2900                0x67, 0x28, 0x76, 0x6F, 0x69, 0x64, 0x20, 0x2A, 0x63, 0x74, 0x78, 0x29, 0x00, 0x09,
2901                0x5F, 0x5F, 0x75, 0x33, 0x32, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x3D, 0x20, 0x30, 0x3B,
2902                0x00, 0x09, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x20, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79,
2903                0x5F, 0x66, 0x6F, 0x75, 0x72, 0x20, 0x3D, 0x20, 0x32, 0x34, 0x3B, 0x00, 0x09, 0x5F,
2904                0x5F, 0x75, 0x36, 0x34, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x79, 0x5F, 0x74, 0x77, 0x6F,
2905                0x20, 0x3D, 0x20, 0x34, 0x32, 0x3B, 0x00, 0x20, 0x20, 0x20, 0x20, 0x62, 0x70, 0x66,
2906                0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C,
2907                0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F, 0x31, 0x2C, 0x20, 0x26, 0x6B, 0x65,
2908                0x79, 0x2C, 0x20, 0x26, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79, 0x5F, 0x66, 0x6F, 0x75,
2909                0x72, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59, 0x29, 0x3B, 0x00, 0x20,
2910                0x20, 0x20, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64,
2911                0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C, 0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F,
2912                0x32, 0x2C, 0x20, 0x26, 0x6B, 0x65, 0x79, 0x2C, 0x20, 0x26, 0x66, 0x6F, 0x72, 0x74,
2913                0x79, 0x5F, 0x74, 0x77, 0x6F, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59,
2914                0x29, 0x3B, 0x00, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x30, 0x3B, 0x00,
2915                0x63, 0x68, 0x61, 0x72, 0x00, 0x5F, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
2916                0x2E, 0x6D, 0x61, 0x70, 0x73, 0x00, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
2917            ]
2918        } else {
2919            &[
2920                0xEB, 0x9F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2921                0x01, 0xF0, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x01, 0xCC, 0x00, 0x00, 0x00, 0x00,
2922                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
2923                0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
2924                0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
2925                0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00,
2926                0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
2927                0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x08, 0x00, 0x00, 0x00,
2928                0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2929                0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
2930                0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
2931                0x00, 0x0A, 0x00, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
2932                0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
2933                0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2934                0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
2935                0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x45,
2936                0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00,
2937                0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x08,
2938                0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00,
2939                0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D,
2940                0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00,
2941                0x00, 0x20, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
2942                0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
2943                0x00, 0x4E, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x54,
2944                0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x66, 0x0E, 0x00,
2945                0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
2946                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00,
2947                0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x11,
2948                0x00, 0x00, 0x00, 0x70, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
2949                0x01, 0xB0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x08,
2950                0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2951                0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0xB5,
2952                0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
2953                0x01, 0xBE, 0x0F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E,
2954                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
2955                0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0xC4, 0x0F, 0x00, 0x00, 0x01,
2956                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2957                0x00, 0x04, 0x00, 0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x41, 0x52, 0x52, 0x41, 0x59,
2958                0x5F, 0x53, 0x49, 0x5A, 0x45, 0x5F, 0x54, 0x59, 0x50, 0x45, 0x5F, 0x5F, 0x00, 0x5F,
2959                0x5F, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6E, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x20,
2960                0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x00, 0x75, 0x6E, 0x73, 0x69,
2961                0x67, 0x6E, 0x65, 0x64, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x6E, 0x67,
2962                0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x6B, 0x65, 0x79, 0x00, 0x76, 0x61, 0x6C, 0x75,
2963                0x65, 0x00, 0x6D, 0x61, 0x78, 0x5F, 0x65, 0x6E, 0x74, 0x72, 0x69, 0x65, 0x73, 0x00,
2964                0x6D, 0x61, 0x70, 0x5F, 0x31, 0x00, 0x6D, 0x61, 0x70, 0x5F, 0x32, 0x00, 0x63, 0x74,
2965                0x78, 0x00, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F, 0x67, 0x00, 0x74, 0x72, 0x61,
2966                0x63, 0x65, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x00, 0x2F, 0x76, 0x61, 0x72, 0x2F, 0x68,
2967                0x6F, 0x6D, 0x65, 0x2F, 0x64, 0x61, 0x76, 0x65, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x61,
2968                0x79, 0x61, 0x2D, 0x72, 0x73, 0x2F, 0x61, 0x79, 0x61, 0x2F, 0x74, 0x65, 0x73, 0x74,
2969                0x2F, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2D, 0x65,
2970                0x62, 0x70, 0x66, 0x2F, 0x73, 0x72, 0x63, 0x2F, 0x62, 0x70, 0x66, 0x2F, 0x6D, 0x75,
2971                0x6C, 0x74, 0x69, 0x6D, 0x61, 0x70, 0x2D, 0x62, 0x74, 0x66, 0x2E, 0x62, 0x70, 0x66,
2972                0x2E, 0x63, 0x00, 0x69, 0x6E, 0x74, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F,
2973                0x67, 0x28, 0x76, 0x6F, 0x69, 0x64, 0x20, 0x2A, 0x63, 0x74, 0x78, 0x29, 0x00, 0x09,
2974                0x5F, 0x5F, 0x75, 0x33, 0x32, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x3D, 0x20, 0x30, 0x3B,
2975                0x00, 0x09, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x20, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79,
2976                0x5F, 0x66, 0x6F, 0x75, 0x72, 0x20, 0x3D, 0x20, 0x32, 0x34, 0x3B, 0x00, 0x09, 0x5F,
2977                0x5F, 0x75, 0x36, 0x34, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x79, 0x5F, 0x74, 0x77, 0x6F,
2978                0x20, 0x3D, 0x20, 0x34, 0x32, 0x3B, 0x00, 0x20, 0x20, 0x20, 0x20, 0x62, 0x70, 0x66,
2979                0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C,
2980                0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F, 0x31, 0x2C, 0x20, 0x26, 0x6B, 0x65,
2981                0x79, 0x2C, 0x20, 0x26, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79, 0x5F, 0x66, 0x6F, 0x75,
2982                0x72, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59, 0x29, 0x3B, 0x00, 0x20,
2983                0x20, 0x20, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64,
2984                0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C, 0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F,
2985                0x32, 0x2C, 0x20, 0x26, 0x6B, 0x65, 0x79, 0x2C, 0x20, 0x26, 0x66, 0x6F, 0x72, 0x74,
2986                0x79, 0x5F, 0x74, 0x77, 0x6F, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59,
2987                0x29, 0x3B, 0x00, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x30, 0x3B, 0x00,
2988                0x63, 0x68, 0x61, 0x72, 0x00, 0x5F, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
2989                0x2E, 0x6D, 0x61, 0x70, 0x73, 0x00, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
2990            ]
2991        };
2992
2993        let btf_section = fake_section(EbpfSectionKind::Btf, ".BTF", data, None);
2994        obj.parse_section(btf_section).unwrap();
2995
2996        let map_section = fake_section(EbpfSectionKind::BtfMaps, ".maps", &[], None);
2997        obj.parse_section(map_section).unwrap();
2998
2999        let map = &obj.maps["map_1"];
3000        assert_matches!(map, Map::Btf(m) => {
3001            assert_eq!(m.def.key_size, 4);
3002            assert_eq!(m.def.value_size, 8);
3003            assert_eq!(m.def.max_entries, 1);
3004        });
3005    }
3006}