aya/
bpf.rs

1use std::{
2    borrow::Cow,
3    collections::{HashMap, HashSet},
4    fs, io,
5    os::{
6        fd::{AsFd as _, AsRawFd as _},
7        raw::c_int,
8    },
9    path::{Path, PathBuf},
10    sync::{Arc, LazyLock},
11};
12
13use aya_obj::{
14    btf::{BtfFeatures, BtfRelocationError},
15    generated::{BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS},
16    relocation::EbpfRelocationError,
17    EbpfSectionKind, Features,
18};
19use log::{debug, warn};
20use thiserror::Error;
21
22use crate::{
23    generated::{
24        bpf_map_type::{self, *},
25        AYA_PERF_EVENT_IOC_DISABLE, AYA_PERF_EVENT_IOC_ENABLE, AYA_PERF_EVENT_IOC_SET_BPF,
26    },
27    maps::{Map, MapData, MapError},
28    obj::{
29        btf::{Btf, BtfError},
30        Object, ParseError, ProgramSection,
31    },
32    programs::{
33        BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
34        CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent,
35        ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup,
36        SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
37    },
38    sys::{
39        bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
40        is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported,
41        is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
42        is_btf_supported, is_btf_type_tag_supported, is_info_gpl_compatible_supported,
43        is_info_map_ids_supported, is_perf_link_supported, is_probe_read_kernel_supported,
44        is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs,
45    },
46    util::{bytes_of, bytes_of_slice, nr_cpus, page_size},
47};
48
49pub(crate) const BPF_OBJ_NAME_LEN: usize = 16;
50
51pub(crate) const PERF_EVENT_IOC_ENABLE: c_int = AYA_PERF_EVENT_IOC_ENABLE;
52pub(crate) const PERF_EVENT_IOC_DISABLE: c_int = AYA_PERF_EVENT_IOC_DISABLE;
53pub(crate) const PERF_EVENT_IOC_SET_BPF: c_int = AYA_PERF_EVENT_IOC_SET_BPF;
54
55/// Marker trait for types that can safely be converted to and from byte slices.
56pub unsafe trait Pod: Copy + 'static {}
57
58macro_rules! unsafe_impl_pod {
59    ($($struct_name:ident),+ $(,)?) => {
60        $(
61            unsafe impl Pod for $struct_name { }
62        )+
63    }
64}
65
66unsafe_impl_pod!(i8, u8, i16, u16, i32, u32, i64, u64, u128, i128);
67
68// It only makes sense that an array of POD types is itself POD
69unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
70
71pub use aya_obj::maps::{bpf_map_def, PinningType};
72
73pub(crate) static FEATURES: LazyLock<Features> = LazyLock::new(detect_features);
74
75fn detect_features() -> Features {
76    let btf = if is_btf_supported() {
77        Some(BtfFeatures::new(
78            is_btf_func_supported(),
79            is_btf_func_global_supported(),
80            is_btf_datasec_supported(),
81            is_btf_float_supported(),
82            is_btf_decl_tag_supported(),
83            is_btf_type_tag_supported(),
84            is_btf_enum64_supported(),
85        ))
86    } else {
87        None
88    };
89    let f = Features::new(
90        is_prog_name_supported(),
91        is_probe_read_kernel_supported(),
92        is_perf_link_supported(),
93        is_bpf_global_data_supported(),
94        is_bpf_cookie_supported(),
95        is_prog_id_supported(BPF_MAP_TYPE_CPUMAP),
96        is_prog_id_supported(BPF_MAP_TYPE_DEVMAP),
97        is_info_map_ids_supported(),
98        is_info_gpl_compatible_supported(),
99        btf,
100    );
101    debug!("BPF Feature Detection: {:#?}", f);
102    f
103}
104
105/// Returns a reference to the detected BPF features.
106pub fn features() -> &'static Features {
107    &FEATURES
108}
109
110/// Builder style API for advanced loading of eBPF programs.
111///
112/// Loading eBPF code involves a few steps, including loading maps and applying
113/// relocations. You can use `EbpfLoader` to customize some of the loading
114/// options.
115///
116/// # Examples
117///
118/// ```no_run
119/// use aya::{EbpfLoader, Btf};
120/// use std::fs;
121///
122/// let bpf = EbpfLoader::new()
123///     // load the BTF data from /sys/kernel/btf/vmlinux
124///     .btf(Btf::from_sys_fs().ok().as_ref())
125///     // load pinned maps from /sys/fs/bpf/my-program
126///     .map_pin_path("/sys/fs/bpf/my-program")
127///     // finally load the code
128///     .load_file("file.o")?;
129/// # Ok::<(), aya::EbpfError>(())
130/// ```
131#[derive(Debug)]
132pub struct EbpfLoader<'a> {
133    btf: Option<Cow<'a, Btf>>,
134    map_pin_path: Option<PathBuf>,
135    globals: HashMap<&'a str, (&'a [u8], bool)>,
136    max_entries: HashMap<&'a str, u32>,
137    extensions: HashSet<&'a str>,
138    verifier_log_level: VerifierLogLevel,
139    allow_unsupported_maps: bool,
140}
141
142/// Builder style API for advanced loading of eBPF programs.
143#[deprecated(since = "0.13.0", note = "use `EbpfLoader` instead")]
144pub type BpfLoader<'a> = EbpfLoader<'a>;
145
146bitflags::bitflags! {
147    /// Used to set the verifier log level flags in [EbpfLoader](EbpfLoader::verifier_log_level()).
148    #[derive(Clone, Copy, Debug)]
149    pub struct VerifierLogLevel: u32 {
150        /// Sets no verifier logging.
151        const DISABLE = 0;
152        /// Enables debug verifier logging.
153        const DEBUG = 1;
154        /// Enables verbose verifier logging.
155        const VERBOSE = 2 | Self::DEBUG.bits();
156        /// Enables verifier stats.
157        const STATS = 4;
158    }
159}
160
161impl Default for VerifierLogLevel {
162    fn default() -> Self {
163        Self::DEBUG | Self::STATS
164    }
165}
166
167impl<'a> EbpfLoader<'a> {
168    /// Creates a new loader instance.
169    pub fn new() -> Self {
170        Self {
171            btf: Btf::from_sys_fs().ok().map(Cow::Owned),
172            map_pin_path: None,
173            globals: HashMap::new(),
174            max_entries: HashMap::new(),
175            extensions: HashSet::new(),
176            verifier_log_level: VerifierLogLevel::default(),
177            allow_unsupported_maps: false,
178        }
179    }
180
181    /// Sets the target [BTF](Btf) info.
182    ///
183    /// The loader defaults to loading `BTF` info using [Btf::from_sys_fs].
184    /// Use this method if you want to load `BTF` from a custom location or
185    /// pass `None` to disable `BTF` relocations entirely.
186    /// # Example
187    ///
188    /// ```no_run
189    /// use aya::{EbpfLoader, Btf, Endianness};
190    ///
191    /// let bpf = EbpfLoader::new()
192    ///     // load the BTF data from a custom location
193    ///     .btf(Btf::parse_file("/custom_btf_file", Endianness::default()).ok().as_ref())
194    ///     .load_file("file.o")?;
195    ///
196    /// # Ok::<(), aya::EbpfError>(())
197    /// ```
198    pub fn btf(&mut self, btf: Option<&'a Btf>) -> &mut Self {
199        self.btf = btf.map(Cow::Borrowed);
200        self
201    }
202
203    /// Allows programs containing unsupported maps to be loaded.
204    ///
205    /// By default programs containing unsupported maps will fail to load. This
206    /// method can be used to configure the loader so that unsupported maps will
207    /// be loaded, but won't be accessible from userspace. Can be useful when
208    /// using unsupported maps that are only accessed from eBPF code and don't
209    /// require any userspace interaction.
210    ///
211    /// # Example
212    ///
213    /// ```no_run
214    /// use aya::EbpfLoader;
215    ///
216    /// let bpf = EbpfLoader::new()
217    ///     .allow_unsupported_maps()
218    ///     .load_file("file.o")?;
219    /// # Ok::<(), aya::EbpfError>(())
220    /// ```
221    ///
222    pub fn allow_unsupported_maps(&mut self) -> &mut Self {
223        self.allow_unsupported_maps = true;
224        self
225    }
226
227    /// Sets the base directory path for pinned maps.
228    ///
229    /// Pinned maps will be loaded from `path/MAP_NAME`.
230    /// The caller is responsible for ensuring the directory exists.
231    ///
232    /// # Example
233    ///
234    /// ```no_run
235    /// use aya::EbpfLoader;
236    ///
237    /// let bpf = EbpfLoader::new()
238    ///     .map_pin_path("/sys/fs/bpf/my-program")
239    ///     .load_file("file.o")?;
240    /// # Ok::<(), aya::EbpfError>(())
241    /// ```
242    ///
243    pub fn map_pin_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
244        self.map_pin_path = Some(path.as_ref().to_owned());
245        self
246    }
247
248    /// Sets the value of a global variable.
249    ///
250    /// If the `must_exist` argument is `true`, [`EbpfLoader::load`] will fail with [`ParseError::SymbolNotFound`] if the loaded object code does not contain the variable.
251    ///
252    /// From Rust eBPF, a global variable can be defined as follows:
253    ///
254    /// ```no_run
255    /// #[no_mangle]
256    /// static VERSION: i32 = 0;
257    /// ```
258    ///
259    /// Then it can be accessed using `core::ptr::read_volatile`:
260    ///
261    /// ```no_run
262    /// # #[no_mangle]
263    /// # static VERSION: i32 = 0;
264    /// # unsafe fn try_test() {
265    /// let version = core::ptr::read_volatile(&VERSION);
266    /// # }
267    /// ```
268    ///
269    /// The type of a global variable must be `Pod` (plain old data), for instance `u8`, `u32` and
270    /// all other primitive types. You may use custom types as well, but you must ensure that those
271    /// types are `#[repr(C)]` and only contain other `Pod` types.
272    ///
273    /// From C eBPF, you would annotate a global variable as `volatile const`.
274    ///
275    /// # Example
276    ///
277    /// ```no_run
278    /// use aya::EbpfLoader;
279    ///
280    /// let bpf = EbpfLoader::new()
281    ///     .set_global("VERSION", &2, true)
282    ///     .set_global("PIDS", &[1234u16, 5678], true)
283    ///     .load_file("file.o")?;
284    /// # Ok::<(), aya::EbpfError>(())
285    /// ```
286    ///
287    pub fn set_global<T: Into<GlobalData<'a>>>(
288        &mut self,
289        name: &'a str,
290        value: T,
291        must_exist: bool,
292    ) -> &mut Self {
293        self.globals.insert(name, (value.into().bytes, must_exist));
294        self
295    }
296
297    /// Set the max_entries for specified map.
298    ///
299    /// Overwrite the value of max_entries of the map that matches
300    /// the provided name before the map is created.
301    ///
302    /// # Example
303    ///
304    /// ```no_run
305    /// use aya::EbpfLoader;
306    ///
307    /// let bpf = EbpfLoader::new()
308    ///     .set_max_entries("map", 64)
309    ///     .load_file("file.o")?;
310    /// # Ok::<(), aya::EbpfError>(())
311    /// ```
312    ///
313    pub fn set_max_entries(&mut self, name: &'a str, size: u32) -> &mut Self {
314        self.max_entries.insert(name, size);
315        self
316    }
317
318    /// Treat the provided program as an [`Extension`]
319    ///
320    /// When attempting to load the program with the provided `name`
321    /// the program type is forced to be ] [`Extension`] and is not
322    /// inferred from the ELF section name.
323    ///
324    /// # Example
325    ///
326    /// ```no_run
327    /// use aya::EbpfLoader;
328    ///
329    /// let bpf = EbpfLoader::new()
330    ///     .extension("myfunc")
331    ///     .load_file("file.o")?;
332    /// # Ok::<(), aya::EbpfError>(())
333    /// ```
334    ///
335    pub fn extension(&mut self, name: &'a str) -> &mut Self {
336        self.extensions.insert(name);
337        self
338    }
339
340    /// Sets BPF verifier log level.
341    ///
342    /// # Example
343    ///
344    /// ```no_run
345    /// use aya::{EbpfLoader, VerifierLogLevel};
346    ///
347    /// let bpf = EbpfLoader::new()
348    ///     .verifier_log_level(VerifierLogLevel::VERBOSE | VerifierLogLevel::STATS)
349    ///     .load_file("file.o")?;
350    /// # Ok::<(), aya::EbpfError>(())
351    /// ```
352    ///
353    pub fn verifier_log_level(&mut self, level: VerifierLogLevel) -> &mut Self {
354        self.verifier_log_level = level;
355        self
356    }
357
358    /// Loads eBPF bytecode from a file.
359    ///
360    /// # Examples
361    ///
362    /// ```no_run
363    /// use aya::EbpfLoader;
364    ///
365    /// let bpf = EbpfLoader::new().load_file("file.o")?;
366    /// # Ok::<(), aya::EbpfError>(())
367    /// ```
368    pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Ebpf, EbpfError> {
369        let path = path.as_ref();
370        self.load(&fs::read(path).map_err(|error| EbpfError::FileError {
371            path: path.to_owned(),
372            error,
373        })?)
374    }
375
376    /// Loads eBPF bytecode from a buffer.
377    ///
378    /// # Examples
379    ///
380    /// ```no_run
381    /// use aya::EbpfLoader;
382    /// use std::fs;
383    ///
384    /// let data = fs::read("file.o").unwrap();
385    /// let bpf = EbpfLoader::new().load(&data)?;
386    /// # Ok::<(), aya::EbpfError>(())
387    /// ```
388    pub fn load(&mut self, data: &[u8]) -> Result<Ebpf, EbpfError> {
389        let Self {
390            btf,
391            map_pin_path,
392            globals,
393            max_entries,
394            extensions,
395            verifier_log_level,
396            allow_unsupported_maps,
397        } = self;
398        let mut obj = Object::parse(data)?;
399        obj.patch_map_data(globals.clone())?;
400
401        let btf_fd = if let Some(features) = &FEATURES.btf() {
402            if let Some(btf) = obj.fixup_and_sanitize_btf(features)? {
403                match load_btf(btf.to_bytes(), *verifier_log_level) {
404                    Ok(btf_fd) => Some(Arc::new(btf_fd)),
405                    // Only report an error here if the BTF is truly needed, otherwise proceed without.
406                    Err(err) => {
407                        for program in obj.programs.values() {
408                            match program.section {
409                                ProgramSection::Extension
410                                | ProgramSection::FEntry { sleepable: _ }
411                                | ProgramSection::FExit { sleepable: _ }
412                                | ProgramSection::Lsm { sleepable: _ }
413                                | ProgramSection::BtfTracePoint => {
414                                    return Err(EbpfError::BtfError(err))
415                                }
416                                ProgramSection::KRetProbe
417                                | ProgramSection::KProbe
418                                | ProgramSection::UProbe { sleepable: _ }
419                                | ProgramSection::URetProbe { sleepable: _ }
420                                | ProgramSection::TracePoint
421                                | ProgramSection::SocketFilter
422                                | ProgramSection::Xdp {
423                                    frags: _,
424                                    attach_type: _,
425                                }
426                                | ProgramSection::SkMsg
427                                | ProgramSection::SkSkbStreamParser
428                                | ProgramSection::SkSkbStreamVerdict
429                                | ProgramSection::SockOps
430                                | ProgramSection::SchedClassifier
431                                | ProgramSection::CgroupSkb
432                                | ProgramSection::CgroupSkbIngress
433                                | ProgramSection::CgroupSkbEgress
434                                | ProgramSection::CgroupSockAddr { attach_type: _ }
435                                | ProgramSection::CgroupSysctl
436                                | ProgramSection::CgroupSockopt { attach_type: _ }
437                                | ProgramSection::LircMode2
438                                | ProgramSection::PerfEvent
439                                | ProgramSection::RawTracePoint
440                                | ProgramSection::SkLookup
441                                | ProgramSection::CgroupSock { attach_type: _ }
442                                | ProgramSection::CgroupDevice => {}
443                            }
444                        }
445
446                        warn!("Object BTF couldn't be loaded in the kernel: {err}");
447
448                        None
449                    }
450                }
451            } else {
452                None
453            }
454        } else {
455            None
456        };
457
458        if let Some(btf) = &btf {
459            obj.relocate_btf(btf)?;
460        }
461        let mut maps = HashMap::new();
462        for (name, mut obj) in obj.maps.drain() {
463            if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) =
464                (FEATURES.bpf_global_data(), obj.section_kind())
465            {
466                continue;
467            }
468            let num_cpus = || {
469                Ok(nr_cpus().map_err(|(path, error)| EbpfError::FileError {
470                    path: PathBuf::from(path),
471                    error,
472                })? as u32)
473            };
474            let map_type: bpf_map_type = obj.map_type().try_into().map_err(MapError::from)?;
475            if let Some(max_entries) = max_entries_override(
476                map_type,
477                max_entries.get(name.as_str()).copied(),
478                || obj.max_entries(),
479                num_cpus,
480                || page_size() as u32,
481            )? {
482                obj.set_max_entries(max_entries)
483            }
484            match obj.map_type().try_into() {
485                Ok(BPF_MAP_TYPE_CPUMAP) => {
486                    obj.set_value_size(if FEATURES.cpumap_prog_id() { 8 } else { 4 })
487                }
488                Ok(BPF_MAP_TYPE_DEVMAP | BPF_MAP_TYPE_DEVMAP_HASH) => {
489                    obj.set_value_size(if FEATURES.devmap_prog_id() { 8 } else { 4 })
490                }
491                _ => (),
492            }
493            let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
494            let mut map = match obj.pinning() {
495                PinningType::None => MapData::create(obj, &name, btf_fd)?,
496                PinningType::ByName => {
497                    // pin maps in /sys/fs/bpf by default to align with libbpf
498                    // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
499                    let path = map_pin_path
500                        .as_deref()
501                        .unwrap_or_else(|| Path::new("/sys/fs/bpf"));
502
503                    MapData::create_pinned_by_name(path, obj, &name, btf_fd)?
504                }
505            };
506            map.finalize()?;
507            maps.insert(name, map);
508        }
509
510        let text_sections = obj
511            .functions
512            .keys()
513            .map(|(section_index, _)| *section_index)
514            .collect();
515
516        obj.relocate_maps(
517            maps.iter()
518                .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())),
519            &text_sections,
520        )?;
521        obj.relocate_calls(&text_sections)?;
522        obj.sanitize_functions(&FEATURES);
523
524        let programs = obj
525            .programs
526            .drain()
527            .map(|(name, prog_obj)| {
528                let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone();
529
530                let prog_name = if FEATURES.bpf_name() {
531                    Some(name.clone())
532                } else {
533                    None
534                };
535                let section = prog_obj.section.clone();
536                let obj = (prog_obj, function_obj);
537
538                let btf_fd = btf_fd.as_ref().map(Arc::clone);
539                let program = if extensions.contains(name.as_str()) {
540                    Program::Extension(Extension {
541                        data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
542                    })
543                } else {
544                    match &section {
545                        ProgramSection::KProbe => Program::KProbe(KProbe {
546                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
547                            kind: ProbeKind::KProbe,
548                        }),
549                        ProgramSection::KRetProbe => Program::KProbe(KProbe {
550                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
551                            kind: ProbeKind::KRetProbe,
552                        }),
553                        ProgramSection::UProbe { sleepable } => {
554                            let mut data =
555                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
556                            if *sleepable {
557                                data.flags = BPF_F_SLEEPABLE;
558                            }
559                            Program::UProbe(UProbe {
560                                data,
561                                kind: ProbeKind::UProbe,
562                            })
563                        }
564                        ProgramSection::URetProbe { sleepable } => {
565                            let mut data =
566                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
567                            if *sleepable {
568                                data.flags = BPF_F_SLEEPABLE;
569                            }
570                            Program::UProbe(UProbe {
571                                data,
572                                kind: ProbeKind::URetProbe,
573                            })
574                        }
575                        ProgramSection::TracePoint => Program::TracePoint(TracePoint {
576                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
577                        }),
578                        ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter {
579                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
580                        }),
581                        ProgramSection::Xdp {
582                            frags, attach_type, ..
583                        } => {
584                            let mut data =
585                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
586                            if *frags {
587                                data.flags = BPF_F_XDP_HAS_FRAGS;
588                            }
589                            Program::Xdp(Xdp {
590                                data,
591                                attach_type: *attach_type,
592                            })
593                        }
594                        ProgramSection::SkMsg => Program::SkMsg(SkMsg {
595                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
596                        }),
597                        ProgramSection::CgroupSysctl => Program::CgroupSysctl(CgroupSysctl {
598                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
599                        }),
600                        ProgramSection::CgroupSockopt { attach_type, .. } => {
601                            Program::CgroupSockopt(CgroupSockopt {
602                                data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
603                                attach_type: *attach_type,
604                            })
605                        }
606                        ProgramSection::SkSkbStreamParser => Program::SkSkb(SkSkb {
607                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
608                            kind: SkSkbKind::StreamParser,
609                        }),
610                        ProgramSection::SkSkbStreamVerdict => Program::SkSkb(SkSkb {
611                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
612                            kind: SkSkbKind::StreamVerdict,
613                        }),
614                        ProgramSection::SockOps => Program::SockOps(SockOps {
615                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
616                        }),
617                        ProgramSection::SchedClassifier => {
618                            Program::SchedClassifier(SchedClassifier {
619                                data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
620                            })
621                        }
622                        ProgramSection::CgroupSkb => Program::CgroupSkb(CgroupSkb {
623                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
624                            expected_attach_type: None,
625                        }),
626                        ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb {
627                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
628                            expected_attach_type: Some(CgroupSkbAttachType::Ingress),
629                        }),
630                        ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb {
631                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
632                            expected_attach_type: Some(CgroupSkbAttachType::Egress),
633                        }),
634                        ProgramSection::CgroupSockAddr { attach_type, .. } => {
635                            Program::CgroupSockAddr(CgroupSockAddr {
636                                data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
637                                attach_type: *attach_type,
638                            })
639                        }
640                        ProgramSection::LircMode2 => Program::LircMode2(LircMode2 {
641                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
642                        }),
643                        ProgramSection::PerfEvent => Program::PerfEvent(PerfEvent {
644                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
645                        }),
646                        ProgramSection::RawTracePoint => Program::RawTracePoint(RawTracePoint {
647                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
648                        }),
649                        ProgramSection::Lsm { sleepable } => {
650                            let mut data =
651                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
652                            if *sleepable {
653                                data.flags = BPF_F_SLEEPABLE;
654                            }
655                            Program::Lsm(Lsm { data })
656                        }
657                        ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint {
658                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
659                        }),
660                        ProgramSection::FEntry { sleepable } => {
661                            let mut data =
662                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
663                            if *sleepable {
664                                data.flags = BPF_F_SLEEPABLE;
665                            }
666                            Program::FEntry(FEntry { data })
667                        }
668                        ProgramSection::FExit { sleepable } => {
669                            let mut data =
670                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
671                            if *sleepable {
672                                data.flags = BPF_F_SLEEPABLE;
673                            }
674                            Program::FExit(FExit { data })
675                        }
676                        ProgramSection::Extension => Program::Extension(Extension {
677                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
678                        }),
679                        ProgramSection::SkLookup => Program::SkLookup(SkLookup {
680                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
681                        }),
682                        ProgramSection::CgroupSock { attach_type, .. } => {
683                            Program::CgroupSock(CgroupSock {
684                                data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
685                                attach_type: *attach_type,
686                            })
687                        }
688                        ProgramSection::CgroupDevice => Program::CgroupDevice(CgroupDevice {
689                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
690                        }),
691                    }
692                };
693                (name, program)
694            })
695            .collect();
696        let maps = maps
697            .drain()
698            .map(parse_map)
699            .collect::<Result<HashMap<String, Map>, EbpfError>>()?;
700
701        if !*allow_unsupported_maps {
702            maps.iter().try_for_each(|(_, x)| match x {
703                Map::Unsupported(map) => Err(EbpfError::MapError(MapError::Unsupported {
704                    map_type: map.obj().map_type(),
705                })),
706                _ => Ok(()),
707            })?;
708        };
709
710        Ok(Ebpf { maps, programs })
711    }
712}
713
714fn parse_map(data: (String, MapData)) -> Result<(String, Map), EbpfError> {
715    let (name, map) = data;
716    let map_type = bpf_map_type::try_from(map.obj().map_type()).map_err(MapError::from)?;
717    let map = match map_type {
718        BPF_MAP_TYPE_ARRAY => Map::Array(map),
719        BPF_MAP_TYPE_PERCPU_ARRAY => Map::PerCpuArray(map),
720        BPF_MAP_TYPE_PROG_ARRAY => Map::ProgramArray(map),
721        BPF_MAP_TYPE_HASH => Map::HashMap(map),
722        BPF_MAP_TYPE_LRU_HASH => Map::LruHashMap(map),
723        BPF_MAP_TYPE_PERCPU_HASH => Map::PerCpuHashMap(map),
724        BPF_MAP_TYPE_LRU_PERCPU_HASH => Map::PerCpuLruHashMap(map),
725        BPF_MAP_TYPE_PERF_EVENT_ARRAY => Map::PerfEventArray(map),
726        BPF_MAP_TYPE_RINGBUF => Map::RingBuf(map),
727        BPF_MAP_TYPE_SOCKHASH => Map::SockHash(map),
728        BPF_MAP_TYPE_SOCKMAP => Map::SockMap(map),
729        BPF_MAP_TYPE_BLOOM_FILTER => Map::BloomFilter(map),
730        BPF_MAP_TYPE_LPM_TRIE => Map::LpmTrie(map),
731        BPF_MAP_TYPE_STACK => Map::Stack(map),
732        BPF_MAP_TYPE_STACK_TRACE => Map::StackTraceMap(map),
733        BPF_MAP_TYPE_QUEUE => Map::Queue(map),
734        BPF_MAP_TYPE_CPUMAP => Map::CpuMap(map),
735        BPF_MAP_TYPE_DEVMAP => Map::DevMap(map),
736        BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map),
737        BPF_MAP_TYPE_XSKMAP => Map::XskMap(map),
738        m => {
739            warn!("The map {name} is of type {:#?} which is currently unsupported in Aya, use `allow_unsupported_maps()` to load it anyways", m);
740            Map::Unsupported(map)
741        }
742    };
743
744    Ok((name, map))
745}
746
747/// Computes the value which should be used to override the max_entries value of the map
748/// based on the user-provided override and the rules for that map type.
749fn max_entries_override(
750    map_type: bpf_map_type,
751    user_override: Option<u32>,
752    current_value: impl Fn() -> u32,
753    num_cpus: impl Fn() -> Result<u32, EbpfError>,
754    page_size: impl Fn() -> u32,
755) -> Result<Option<u32>, EbpfError> {
756    let max_entries = || user_override.unwrap_or_else(&current_value);
757    Ok(match map_type {
758        BPF_MAP_TYPE_PERF_EVENT_ARRAY if max_entries() == 0 => Some(num_cpus()?),
759        BPF_MAP_TYPE_RINGBUF => Some(adjust_to_page_size(max_entries(), page_size()))
760            .filter(|adjusted| *adjusted != max_entries())
761            .or(user_override),
762        _ => user_override,
763    })
764}
765
766// Adjusts the byte size of a RingBuf map to match a power-of-two multiple of the page size.
767//
768// This mirrors the logic used by libbpf.
769// See https://github.com/libbpf/libbpf/blob/ec6f716eda43/src/libbpf.c#L2461-L2463
770fn adjust_to_page_size(byte_size: u32, page_size: u32) -> u32 {
771    // If the byte_size is zero, return zero and let the verifier reject the map
772    // when it is loaded. This is the behavior of libbpf.
773    if byte_size == 0 {
774        return 0;
775    }
776    // TODO: Replace with primitive method when int_roundings (https://github.com/rust-lang/rust/issues/88581)
777    // is stabilized.
778    fn div_ceil(n: u32, rhs: u32) -> u32 {
779        let d = n / rhs;
780        let r = n % rhs;
781        if r > 0 && rhs > 0 {
782            d + 1
783        } else {
784            d
785        }
786    }
787    let pages_needed = div_ceil(byte_size, page_size);
788    page_size * pages_needed.next_power_of_two()
789}
790
791#[cfg(test)]
792mod tests {
793    use crate::generated::bpf_map_type::*;
794
795    const PAGE_SIZE: u32 = 4096;
796    const NUM_CPUS: u32 = 4;
797
798    #[test]
799    fn test_adjust_to_page_size() {
800        use super::adjust_to_page_size;
801        [
802            (0, 0),
803            (4096, 1),
804            (4096, 4095),
805            (4096, 4096),
806            (8192, 4097),
807            (8192, 8192),
808            (16384, 8193),
809        ]
810        .into_iter()
811        .for_each(|(exp, input)| assert_eq!(exp, adjust_to_page_size(input, PAGE_SIZE)))
812    }
813
814    #[test]
815    fn test_max_entries_override() {
816        use super::max_entries_override;
817        [
818            (BPF_MAP_TYPE_RINGBUF, Some(1), 1, Some(PAGE_SIZE)),
819            (BPF_MAP_TYPE_RINGBUF, None, 1, Some(PAGE_SIZE)),
820            (BPF_MAP_TYPE_RINGBUF, None, PAGE_SIZE, None),
821            (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 1, None),
822            (BPF_MAP_TYPE_PERF_EVENT_ARRAY, Some(42), 1, Some(42)),
823            (BPF_MAP_TYPE_PERF_EVENT_ARRAY, Some(0), 1, Some(NUM_CPUS)),
824            (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 0, Some(NUM_CPUS)),
825            (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 42, None),
826            (BPF_MAP_TYPE_ARRAY, None, 1, None),
827            (BPF_MAP_TYPE_ARRAY, Some(2), 1, Some(2)),
828        ]
829        .into_iter()
830        .for_each(|(map_type, user_override, current_value, exp)| {
831            assert_eq!(
832                exp,
833                max_entries_override(
834                    map_type,
835                    user_override,
836                    || { current_value },
837                    || Ok(NUM_CPUS),
838                    || PAGE_SIZE
839                )
840                .unwrap()
841            )
842        })
843    }
844}
845
846impl Default for EbpfLoader<'_> {
847    fn default() -> Self {
848        EbpfLoader::new()
849    }
850}
851
852/// The main entry point into the library, used to work with eBPF programs and maps.
853#[derive(Debug)]
854pub struct Ebpf {
855    maps: HashMap<String, Map>,
856    programs: HashMap<String, Program>,
857}
858
859/// The main entry point into the library, used to work with eBPF programs and maps.
860#[deprecated(since = "0.13.0", note = "use `Ebpf` instead")]
861pub type Bpf = Ebpf;
862
863impl Ebpf {
864    /// Loads eBPF bytecode from a file.
865    ///
866    /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If
867    /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from
868    /// `/sys/kernel/btf/vmlinux`.
869    ///
870    /// For more loading options, see [EbpfLoader].
871    ///
872    /// # Examples
873    ///
874    /// ```no_run
875    /// use aya::Ebpf;
876    ///
877    /// let bpf = Ebpf::load_file("file.o")?;
878    /// # Ok::<(), aya::EbpfError>(())
879    /// ```
880    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Self, EbpfError> {
881        EbpfLoader::new()
882            .btf(Btf::from_sys_fs().ok().as_ref())
883            .load_file(path)
884    }
885
886    /// Loads eBPF bytecode from a buffer.
887    ///
888    /// Parses the object code contained in `data` and initializes the
889    /// [maps](crate::maps) defined in it. If the kernel supports [BTF](Btf)
890    /// debug info, it is automatically loaded from `/sys/kernel/btf/vmlinux`.
891    ///
892    /// For more loading options, see [EbpfLoader].
893    ///
894    /// # Examples
895    ///
896    /// ```no_run
897    /// use aya::{Ebpf, Btf};
898    /// use std::fs;
899    ///
900    /// let data = fs::read("file.o").unwrap();
901    /// // load the BTF data from /sys/kernel/btf/vmlinux
902    /// let bpf = Ebpf::load(&data)?;
903    /// # Ok::<(), aya::EbpfError>(())
904    /// ```
905    pub fn load(data: &[u8]) -> Result<Self, EbpfError> {
906        EbpfLoader::new()
907            .btf(Btf::from_sys_fs().ok().as_ref())
908            .load(data)
909    }
910
911    /// Returns a reference to the map with the given name.
912    ///
913    /// The returned type is mostly opaque. In order to do anything useful with it you need to
914    /// convert it to a [typed map](crate::maps).
915    ///
916    /// For more details and examples on maps and their usage, see the [maps module
917    /// documentation][crate::maps].
918    pub fn map(&self, name: &str) -> Option<&Map> {
919        self.maps.get(name)
920    }
921
922    /// Returns a mutable reference to the map with the given name.
923    ///
924    /// The returned type is mostly opaque. In order to do anything useful with it you need to
925    /// convert it to a [typed map](crate::maps).
926    ///
927    /// For more details and examples on maps and their usage, see the [maps module
928    /// documentation][crate::maps].
929    pub fn map_mut(&mut self, name: &str) -> Option<&mut Map> {
930        self.maps.get_mut(name)
931    }
932
933    /// Takes ownership of a map with the given name.
934    ///
935    /// Use this when borrowing with [`map`](crate::Ebpf::map) or [`map_mut`](crate::Ebpf::map_mut)
936    /// is not possible (eg when using the map from an async task). The returned
937    /// map will be closed on `Drop`, therefore the caller is responsible for
938    /// managing its lifetime.
939    ///
940    /// The returned type is mostly opaque. In order to do anything useful with it you need to
941    /// convert it to a [typed map](crate::maps).
942    ///
943    /// For more details and examples on maps and their usage, see the [maps module
944    /// documentation][crate::maps].
945    pub fn take_map(&mut self, name: &str) -> Option<Map> {
946        self.maps.remove(name)
947    }
948
949    /// An iterator over all the maps.
950    ///
951    /// # Examples
952    /// ```no_run
953    /// # let mut bpf = aya::Ebpf::load(&[])?;
954    /// for (name, map) in bpf.maps() {
955    ///     println!(
956    ///         "found map `{}`",
957    ///         name,
958    ///     );
959    /// }
960    /// # Ok::<(), aya::EbpfError>(())
961    /// ```
962    pub fn maps(&self) -> impl Iterator<Item = (&str, &Map)> {
963        self.maps.iter().map(|(name, map)| (name.as_str(), map))
964    }
965
966    /// A mutable iterator over all the maps.
967    ///
968    /// # Examples
969    /// ```no_run
970    /// # use std::path::Path;
971    /// # #[derive(thiserror::Error, Debug)]
972    /// # enum Error {
973    /// #     #[error(transparent)]
974    /// #     Ebpf(#[from] aya::EbpfError),
975    /// #     #[error(transparent)]
976    /// #     Pin(#[from] aya::pin::PinError)
977    /// # }
978    /// # let mut bpf = aya::Ebpf::load(&[])?;
979    /// # let pin_path = Path::new("/tmp/pin_path");
980    /// for (_, map) in bpf.maps_mut() {
981    ///     map.pin(pin_path)?;
982    /// }
983    /// # Ok::<(), Error>(())
984    /// ```
985    pub fn maps_mut(&mut self) -> impl Iterator<Item = (&str, &mut Map)> {
986        self.maps.iter_mut().map(|(name, map)| (name.as_str(), map))
987    }
988
989    /// Returns a reference to the program with the given name.
990    ///
991    /// You can use this to inspect a program and its properties. To load and attach a program, use
992    /// [program_mut](Self::program_mut) instead.
993    ///
994    /// For more details on programs and their usage, see the [programs module
995    /// documentation](crate::programs).
996    ///
997    /// # Examples
998    ///
999    /// ```no_run
1000    /// # let bpf = aya::Ebpf::load(&[])?;
1001    /// let program = bpf.program("SSL_read").unwrap();
1002    /// println!("program SSL_read is of type {:?}", program.prog_type());
1003    /// # Ok::<(), aya::EbpfError>(())
1004    /// ```
1005    pub fn program(&self, name: &str) -> Option<&Program> {
1006        self.programs.get(name)
1007    }
1008
1009    /// Returns a mutable reference to the program with the given name.
1010    ///
1011    /// Used to get a program before loading and attaching it. For more details on programs and
1012    /// their usage, see the [programs module documentation](crate::programs).
1013    ///
1014    /// # Examples
1015    ///
1016    /// ```no_run
1017    /// # let mut bpf = aya::Ebpf::load(&[])?;
1018    /// use aya::programs::UProbe;
1019    ///
1020    /// let program: &mut UProbe = bpf.program_mut("SSL_read").unwrap().try_into()?;
1021    /// program.load()?;
1022    /// program.attach(Some("SSL_read"), 0, "libssl", None)?;
1023    /// # Ok::<(), aya::EbpfError>(())
1024    /// ```
1025    pub fn program_mut(&mut self, name: &str) -> Option<&mut Program> {
1026        self.programs.get_mut(name)
1027    }
1028
1029    /// An iterator over all the programs.
1030    ///
1031    /// # Examples
1032    /// ```no_run
1033    /// # let bpf = aya::Ebpf::load(&[])?;
1034    /// for (name, program) in bpf.programs() {
1035    ///     println!(
1036    ///         "found program `{}` of type `{:?}`",
1037    ///         name,
1038    ///         program.prog_type()
1039    ///     );
1040    /// }
1041    /// # Ok::<(), aya::EbpfError>(())
1042    /// ```
1043    pub fn programs(&self) -> impl Iterator<Item = (&str, &Program)> {
1044        self.programs.iter().map(|(s, p)| (s.as_str(), p))
1045    }
1046
1047    /// An iterator mutably referencing all of the programs.
1048    ///
1049    /// # Examples
1050    /// ```no_run
1051    /// # use std::path::Path;
1052    /// # #[derive(thiserror::Error, Debug)]
1053    /// # enum Error {
1054    /// #     #[error(transparent)]
1055    /// #     Ebpf(#[from] aya::EbpfError),
1056    /// #     #[error(transparent)]
1057    /// #     Pin(#[from] aya::pin::PinError)
1058    /// # }
1059    /// # let mut bpf = aya::Ebpf::load(&[])?;
1060    /// # let pin_path = Path::new("/tmp/pin_path");
1061    /// for (_, program) in bpf.programs_mut() {
1062    ///     program.pin(pin_path)?;
1063    /// }
1064    /// # Ok::<(), Error>(())
1065    /// ```
1066    pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
1067        self.programs.iter_mut().map(|(s, p)| (s.as_str(), p))
1068    }
1069}
1070
1071/// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`].
1072#[derive(Debug, Error)]
1073pub enum EbpfError {
1074    /// Error loading file
1075    #[error("error loading {path}")]
1076    FileError {
1077        /// The file path
1078        path: PathBuf,
1079        #[source]
1080        /// The original io::Error
1081        error: io::Error,
1082    },
1083
1084    /// Unexpected pinning type
1085    #[error("unexpected pinning type {name}")]
1086    UnexpectedPinningType {
1087        /// The value encountered
1088        name: u32,
1089    },
1090
1091    /// Error parsing BPF object
1092    #[error("error parsing BPF object: {0}")]
1093    ParseError(#[from] ParseError),
1094
1095    /// Error parsing BTF object
1096    #[error("BTF error: {0}")]
1097    BtfError(#[from] BtfError),
1098
1099    /// Error performing relocations
1100    #[error("error relocating function")]
1101    RelocationError(#[from] EbpfRelocationError),
1102
1103    /// Error performing relocations
1104    #[error("error relocating section")]
1105    BtfRelocationError(#[from] BtfRelocationError),
1106
1107    /// No BTF parsed for object
1108    #[error("no BTF parsed for object")]
1109    NoBTF,
1110
1111    #[error("map error: {0}")]
1112    /// A map error
1113    MapError(#[from] MapError),
1114
1115    #[error("program error: {0}")]
1116    /// A program error
1117    ProgramError(#[from] ProgramError),
1118}
1119
1120/// The error type returned by [`Bpf::load_file`] and [`Bpf::load`].
1121#[deprecated(since = "0.13.0", note = "use `EbpfError` instead")]
1122pub type BpfError = EbpfError;
1123
1124fn load_btf(
1125    raw_btf: Vec<u8>,
1126    verifier_log_level: VerifierLogLevel,
1127) -> Result<crate::MockableFd, BtfError> {
1128    let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
1129        bpf_load_btf(raw_btf.as_slice(), logger, verifier_log_level)
1130    });
1131    ret.map_err(|(_, io_error)| BtfError::LoadError {
1132        io_error,
1133        verifier_log,
1134    })
1135}
1136
1137/// Global data that can be exported to eBPF programs before they are loaded.
1138///
1139/// Valid global data includes `Pod` types and slices of `Pod` types. See also
1140/// [EbpfLoader::set_global].
1141pub struct GlobalData<'a> {
1142    bytes: &'a [u8],
1143}
1144
1145impl<'a, T: Pod> From<&'a [T]> for GlobalData<'a> {
1146    fn from(s: &'a [T]) -> Self {
1147        GlobalData {
1148            bytes: bytes_of_slice(s),
1149        }
1150    }
1151}
1152
1153impl<'a, T: Pod> From<&'a T> for GlobalData<'a> {
1154    fn from(v: &'a T) -> Self {
1155        GlobalData {
1156            // Safety: v is Pod
1157            bytes: unsafe { bytes_of(v) },
1158        }
1159    }
1160}