Browse Source

Add BPF_PROG_TYPE_EXT

This requires loading the BTF to kernel when loading all programs as
well as implementing Extension program type

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
Dave Tucker 3 năm trước cách đây
mục cha
commit
5c6131afba

+ 1 - 0
aya/Cargo.toml

@@ -22,6 +22,7 @@ futures = { version = "0.3.12", optional = true, default-features = false, featu
 tokio = { version = "1.2.0", features = ["macros", "rt", "rt-multi-thread", "net"], optional = true }
 async-std = { version = "1.9.0", optional = true }
 async-io = { version = "1.3", optional = true }
+log = "0.4"
 
 [dev-dependencies]
 matches = "0.1.8"

+ 186 - 65
aya/src/bpf.rs

@@ -1,6 +1,6 @@
 use std::{
     borrow::Cow,
-    collections::HashMap,
+    collections::{HashMap, HashSet},
     error::Error,
     ffi::CString,
     fs, io,
@@ -8,6 +8,7 @@ use std::{
     path::{Path, PathBuf},
 };
 
+use log::debug;
 use thiserror::Error;
 
 use crate::{
@@ -21,12 +22,16 @@ use crate::{
         MapKind, Object, ParseError, ProgramSection,
     },
     programs::{
-        BtfTracePoint, CgroupSkb, CgroupSkbAttachType, FEntry, FExit, KProbe, LircMode2, Lsm,
-        PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
-        SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
+        BtfTracePoint, CgroupSkb, CgroupSkbAttachType, Extension, FEntry, FExit, KProbe, LircMode2,
+        Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
+        SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
     },
-    sys::{bpf_map_freeze, bpf_map_update_elem_ptr},
-    util::{bytes_of, possible_cpus, POSSIBLE_CPUS},
+    sys::{
+        bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
+        is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
+        is_btf_supported, is_prog_name_supported,
+    },
+    util::{bytes_of, possible_cpus, VerifierLog, POSSIBLE_CPUS},
 };
 
 pub(crate) const BPF_OBJ_NAME_LEN: usize = 16;
@@ -77,6 +82,47 @@ impl Default for PinningType {
     }
 }
 
+// Features implements BPF and BTF feature detection
+#[derive(Default, Debug)]
+pub(crate) struct Features {
+    pub bpf_name: bool,
+    pub btf: bool,
+    pub btf_func: bool,
+    pub btf_func_global: bool,
+    pub btf_datasec: bool,
+    pub btf_float: bool,
+}
+
+impl Features {
+    fn probe_features(&mut self) {
+        self.bpf_name = is_prog_name_supported();
+        debug!("[FEAT PROBE] BPF program name support: {}", self.bpf_name);
+
+        self.btf = is_btf_supported();
+        debug!("[FEAT PROBE] BTF support: {}", self.btf);
+
+        if self.btf {
+            self.btf_func = is_btf_func_supported();
+            debug!("[FEAT PROBE] BTF func support: {}", self.btf_func);
+
+            self.btf_func_global = is_btf_func_global_supported();
+            debug!(
+                "[FEAT PROBE] BTF global func support: {}",
+                self.btf_func_global
+            );
+
+            self.btf_datasec = is_btf_datasec_supported();
+            debug!(
+                "[FEAT PROBE] BTF var and datasec support: {}",
+                self.btf_datasec
+            );
+
+            self.btf_float = is_btf_float_supported();
+            debug!("[FEAT PROBE] BTF float support: {}", self.btf_float);
+        }
+    }
+}
+
 /// Builder style API for advanced loading of eBPF programs.
 ///
 /// Loading eBPF code involves a few steps, including loading maps and applying
@@ -103,15 +149,21 @@ pub struct BpfLoader<'a> {
     btf: Option<Cow<'a, Btf>>,
     map_pin_path: Option<PathBuf>,
     globals: HashMap<&'a str, &'a [u8]>,
+    features: Features,
+    extensions: HashSet<&'a str>,
 }
 
 impl<'a> BpfLoader<'a> {
     /// Creates a new loader instance.
     pub fn new() -> BpfLoader<'a> {
+        let mut features = Features::default();
+        features.probe_features();
         BpfLoader {
             btf: Btf::from_sys_fs().ok().map(Cow::Owned),
             map_pin_path: None,
             globals: HashMap::new(),
+            features,
+            extensions: HashSet::new(),
         }
     }
 
@@ -187,6 +239,28 @@ impl<'a> BpfLoader<'a> {
         self
     }
 
+    /// Treat the provided program as an [`Extension`]
+    ///
+    /// When attempting to load the program with the provided `name`
+    /// the program type is forced to be ] [`Extension`] and is not
+    /// inferred from the ELF section name.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use aya::BpfLoader;
+    ///
+    /// let bpf = BpfLoader::new()
+    ///     .extension("myfunc")
+    ///     .load_file("file.o")?;
+    /// # Ok::<(), aya::BpfError>(())
+    /// ```
+    ///
+    pub fn extension(&mut self, name: &'a str) -> &mut BpfLoader<'a> {
+        self.extensions.insert(name);
+        self
+    }
+
     /// Loads eBPF bytecode from a file.
     ///
     /// # Examples
@@ -221,6 +295,39 @@ impl<'a> BpfLoader<'a> {
         let mut obj = Object::parse(data)?;
         obj.patch_map_data(self.globals.clone())?;
 
+        let btf_fd = if self.features.btf {
+            if let Some(ref mut obj_btf) = obj.btf {
+                // fixup btf
+                let section_data = obj.section_sizes.clone();
+                let symbol_offsets = obj.symbol_offset_by_name.clone();
+                obj_btf.fixup(&section_data, &symbol_offsets)?;
+                let btf = obj_btf.sanitize(&self.features)?;
+
+                // load btf to the kernel
+                let raw_btf = btf.to_bytes();
+                let mut log_buf = VerifierLog::new();
+                log_buf.grow();
+                let ret = bpf_load_btf(raw_btf.as_slice(), &mut log_buf);
+                match ret {
+                    Ok(fd) => Some(fd),
+                    Err(io_error) => {
+                        log_buf.truncate();
+                        return Err(BpfError::BtfError(BtfError::LoadError {
+                            io_error,
+                            verifier_log: log_buf
+                                .as_c_str()
+                                .map(|s| s.to_string_lossy().to_string())
+                                .unwrap_or_else(|| "[none]".to_owned()),
+                        }));
+                    }
+                }
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+
         if let Some(btf) = &self.btf {
             obj.relocate_btf(btf)?;
         }
@@ -288,76 +395,90 @@ impl<'a> BpfLoader<'a> {
             .programs
             .drain()
             .map(|(name, obj)| {
+                let prog_name = if self.features.bpf_name {
+                    Some(name.clone())
+                } else {
+                    None
+                };
                 let data = ProgramData {
+                    name: prog_name,
                     obj,
                     fd: None,
                     links: Vec::new(),
                     expected_attach_type: None,
                     attach_btf_obj_fd: None,
                     attach_btf_id: None,
+                    attach_prog_fd: None,
+                    btf_fd,
                 };
-                let program = match &data.obj.section {
-                    ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
-                        data,
-                        kind: ProbeKind::KProbe,
-                    }),
-                    ProgramSection::KRetProbe { .. } => Program::KProbe(KProbe {
-                        data,
-                        kind: ProbeKind::KRetProbe,
-                    }),
-                    ProgramSection::UProbe { .. } => Program::UProbe(UProbe {
-                        data,
-                        kind: ProbeKind::UProbe,
-                    }),
-                    ProgramSection::URetProbe { .. } => Program::UProbe(UProbe {
-                        data,
-                        kind: ProbeKind::URetProbe,
-                    }),
-                    ProgramSection::TracePoint { .. } => Program::TracePoint(TracePoint { data }),
-                    ProgramSection::SocketFilter { .. } => {
-                        Program::SocketFilter(SocketFilter { data })
-                    }
-                    ProgramSection::Xdp { .. } => Program::Xdp(Xdp { data }),
-                    ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg { data }),
-                    ProgramSection::SkSkbStreamParser { .. } => Program::SkSkb(SkSkb {
-                        data,
-                        kind: SkSkbKind::StreamParser,
-                    }),
-                    ProgramSection::SkSkbStreamVerdict { .. } => Program::SkSkb(SkSkb {
-                        data,
-                        kind: SkSkbKind::StreamVerdict,
-                    }),
-                    ProgramSection::SockOps { .. } => Program::SockOps(SockOps { data }),
-                    ProgramSection::SchedClassifier { .. } => {
-                        Program::SchedClassifier(SchedClassifier {
+                let program = if self.extensions.contains(name.as_str()) {
+                    Program::Extension(Extension { data })
+                } else {
+                    match &data.obj.section {
+                        ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
                             data,
-                            name: unsafe {
-                                CString::from_vec_unchecked(Vec::from(name.clone()))
-                                    .into_boxed_c_str()
-                            },
-                        })
-                    }
-                    ProgramSection::CgroupSkbIngress { .. } => Program::CgroupSkb(CgroupSkb {
-                        data,
-                        expected_attach_type: Some(CgroupSkbAttachType::Ingress),
-                    }),
-                    ProgramSection::CgroupSkbEgress { .. } => Program::CgroupSkb(CgroupSkb {
-                        data,
-                        expected_attach_type: Some(CgroupSkbAttachType::Egress),
-                    }),
-                    ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
-                    ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
-                    ProgramSection::RawTracePoint { .. } => {
-                        Program::RawTracePoint(RawTracePoint { data })
-                    }
-                    ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }),
-                    ProgramSection::BtfTracePoint { .. } => {
-                        Program::BtfTracePoint(BtfTracePoint { data })
+                            kind: ProbeKind::KProbe,
+                        }),
+                        ProgramSection::KRetProbe { .. } => Program::KProbe(KProbe {
+                            data,
+                            kind: ProbeKind::KRetProbe,
+                        }),
+                        ProgramSection::UProbe { .. } => Program::UProbe(UProbe {
+                            data,
+                            kind: ProbeKind::UProbe,
+                        }),
+                        ProgramSection::URetProbe { .. } => Program::UProbe(UProbe {
+                            data,
+                            kind: ProbeKind::URetProbe,
+                        }),
+                        ProgramSection::TracePoint { .. } => {
+                            Program::TracePoint(TracePoint { data })
+                        }
+                        ProgramSection::SocketFilter { .. } => {
+                            Program::SocketFilter(SocketFilter { data })
+                        }
+                        ProgramSection::Xdp { .. } => Program::Xdp(Xdp { data }),
+                        ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg { data }),
+                        ProgramSection::SkSkbStreamParser { .. } => Program::SkSkb(SkSkb {
+                            data,
+                            kind: SkSkbKind::StreamParser,
+                        }),
+                        ProgramSection::SkSkbStreamVerdict { .. } => Program::SkSkb(SkSkb {
+                            data,
+                            kind: SkSkbKind::StreamVerdict,
+                        }),
+                        ProgramSection::SockOps { .. } => Program::SockOps(SockOps { data }),
+                        ProgramSection::SchedClassifier { .. } => {
+                            Program::SchedClassifier(SchedClassifier {
+                                data,
+                                name: unsafe {
+                                    CString::from_vec_unchecked(Vec::from(name.clone()))
+                                        .into_boxed_c_str()
+                                },
+                            })
+                        }
+                        ProgramSection::CgroupSkbIngress { .. } => Program::CgroupSkb(CgroupSkb {
+                            data,
+                            expected_attach_type: Some(CgroupSkbAttachType::Ingress),
+                        }),
+                        ProgramSection::CgroupSkbEgress { .. } => Program::CgroupSkb(CgroupSkb {
+                            data,
+                            expected_attach_type: Some(CgroupSkbAttachType::Egress),
+                        }),
+                        ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
+                        ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
+                        ProgramSection::RawTracePoint { .. } => {
+                            Program::RawTracePoint(RawTracePoint { data })
+                        }
+                        ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }),
+                        ProgramSection::BtfTracePoint { .. } => {
+                            Program::BtfTracePoint(BtfTracePoint { data })
+                        }
+                        ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
+                        ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
+                        ProgramSection::Extension { .. } => Program::Extension(Extension { data }),
                     }
-                    ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
-                    ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
                 };
-
                 (name, program)
             })
             .collect();

+ 515 - 10
aya/src/obj/btf/btf.rs

@@ -1,7 +1,8 @@
 use std::{
     borrow::Cow,
+    collections::HashMap,
     convert::TryInto,
-    ffi::{c_void, CStr},
+    ffi::{c_void, CStr, CString},
     fs, io, mem,
     path::{Path, PathBuf},
     ptr,
@@ -9,13 +10,22 @@ use std::{
 
 use bytes::BufMut;
 
+use log::debug;
 use object::Endianness;
 use thiserror::Error;
 
 use crate::{
-    generated::{btf_ext_header, btf_header},
+    generated::{
+        btf_enum, btf_ext_header, btf_func_linkage, btf_header, btf_member, btf_var_secinfo,
+    },
     obj::btf::{relocation::Relocation, BtfKind, BtfType},
     util::bytes_of,
+    Features,
+};
+
+use super::{
+    info::{FuncSecInfo, LineSecInfo},
+    type_vlen, FuncInfo, LineInfo,
 };
 
 pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
@@ -74,6 +84,28 @@ pub enum BtfError {
 
     #[error("maximum depth reached resolving BTF type")]
     MaximumTypeDepthReached { type_id: u32 },
+
+    /// Loading the btf failed
+    #[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")]
+    LoadError {
+        /// The [`io::Error`] returned by the `BPF_BTF_LOAD` syscall.
+        #[source]
+        io_error: io::Error,
+        /// The error log produced by the kernel verifier.
+        verifier_log: String,
+    },
+
+    #[error("Offset not found for symbol `{symbol_name}`")]
+    SymbolOffsetNotFound { symbol_name: String },
+
+    #[error("BTF type that is not VAR was found in DATASEC")]
+    InvalidDatasec,
+
+    #[error("Unable to determine the size of section `{section_name}`")]
+    UnknownSectionSize { section_name: String },
+
+    #[error("Unable to get symbol name")]
+    InvalidSymbolName,
 }
 
 /// Bpf Type Format metadata.
@@ -93,6 +125,40 @@ pub struct Btf {
 }
 
 impl Btf {
+    pub(crate) fn new() -> Btf {
+        Btf {
+            header: btf_header {
+                magic: 0xeb9f,
+                version: 0x01,
+                flags: 0x00,
+                hdr_len: 0x18,
+                type_off: 0x00,
+                type_len: 0x00,
+                str_off: 0x00,
+                str_len: 0x00,
+            },
+            strings: vec![0],
+            types: vec![],
+            _endianness: Endianness::default(),
+        }
+    }
+
+    pub(crate) fn add_string(&mut self, name: String) -> u32 {
+        let str = CString::new(name).unwrap();
+        let name_off = self.strings.len();
+        self.strings.extend(str.as_c_str().to_bytes_with_nul());
+        self.header.str_len = self.strings.len() as u32;
+        name_off as u32
+    }
+
+    pub(crate) fn add_type(&mut self, type_: BtfType) -> u32 {
+        let size = type_.type_info_size() as u32;
+        self.types.push(type_);
+        self.header.type_len += size;
+        self.header.str_off += size;
+        self.types.len() as u32
+    }
+
     /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
     pub fn from_sys_fs() -> Result<Btf, BtfError> {
         Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
@@ -305,6 +371,194 @@ impl Btf {
         buf.put(self.strings.as_slice());
         buf
     }
+
+    pub(crate) fn fixup(
+        &mut self,
+        section_sizes: &HashMap<String, u64>,
+        symbol_offsets: &HashMap<String, u64>,
+    ) -> Result<(), BtfError> {
+        // FIXME: there is probably a more elegant way of doing operation in place
+        // for now, and to keep on the good side of the borrow checker, we'll create
+        // a new Vec and populate it as we go
+        let mut types = vec![];
+        for t in &self.types {
+            let kind = t.kind()?.unwrap_or_default();
+            // datasec sizes aren't set by llvm
+            // we need to fix them here before loading the btf to the kernel
+            match t {
+                BtfType::DataSec(mut ty, data) => {
+                    // Start DataSec Fixups
+                    let sec_name = self.type_name(t)?.ok_or(BtfError::InvalidTypeInfo)?;
+                    let name = sec_name.to_string();
+                    // There are cases when the compiler does indeed populate the
+                    // size. If we hit this case, push to the types vector and
+                    // continue
+                    if unsafe { ty.__bindgen_anon_1.size > 0 } {
+                        debug!("{} {}: fixup not required", kind, name);
+                        types.push(BtfType::DataSec(ty, data.clone()));
+                        continue;
+                    }
+
+                    // We need to get the size of the section from the ELF file
+                    // Fortunately, we cached these when parsing it initially
+                    // and we can this up by name in section_sizes
+                    if let Some(size) = section_sizes.get(&name) {
+                        debug!("{} {}: fixup size to {}", kind, name, size);
+                        ty.__bindgen_anon_1.size = *size as u32;
+                    } else {
+                        return Err(BtfError::UnknownSectionSize { section_name: name });
+                    }
+
+                    // The Vec<btf_var_secinfo> contains BTF_KIND_VAR sections
+                    // that need to have their offsets adjusted. To do this,
+                    // we need to get the offset from the ELF file.
+                    // This was also cached during initial parsing and
+                    // we can query by name in symbol_offsets
+                    let mut adjusted_data: Vec<btf_var_secinfo> = vec![];
+                    for d in data {
+                        let var_type = self.type_by_id(d.type_)?;
+                        let var_kind = var_type.kind()?.unwrap();
+                        if let BtfType::Var(vty, var) = var_type {
+                            let var_name = self.string_at(vty.name_off)?.to_string();
+                            if var.linkage == btf_func_linkage::BTF_FUNC_STATIC as u32 {
+                                debug!(
+                                    "{} {}: {} {}: fixup not required",
+                                    kind, name, var_kind, var_name
+                                );
+                                adjusted_data.push(*d);
+                                continue;
+                            }
+
+                            let offset = symbol_offsets.get(&var_name).ok_or(
+                                BtfError::SymbolOffsetNotFound {
+                                    symbol_name: var_name.clone(),
+                                },
+                            )?;
+                            adjusted_data.push(btf_var_secinfo {
+                                type_: d.type_,
+                                offset: *offset as u32,
+                                size: d.size,
+                            });
+                            debug!(
+                                "{} {}: {} {}: fixup offset {}",
+                                kind, name, var_kind, var_name, offset
+                            );
+                        } else {
+                            return Err(BtfError::InvalidDatasec);
+                        }
+                    }
+                    types.push(BtfType::DataSec(ty, adjusted_data))
+                }
+                // The type does not need fixing up
+                // Push it to the new types vec unmodified
+                ty => types.push(ty.clone()),
+            }
+        }
+        self.types = types;
+        Ok(())
+    }
+
+    pub(crate) fn sanitize(&self, features: &Features) -> Result<Btf, BtfError> {
+        let mut btf = Btf::new();
+
+        btf.strings = self.strings.to_vec();
+        btf.header.str_len = btf.strings.len() as u32;
+
+        // Skip the first type as it's only there
+        // to make type_by_id work
+        for t in &self.types[1..] {
+            let kind = t.kind()?.unwrap_or_default();
+            match t {
+                BtfType::Var(ty, vars) => {
+                    if !features.btf_datasec {
+                        debug!("{}: not supported. replacing with INT", kind);
+                        let int_type = BtfType::new_int(ty.name_off, 1, 0, 0);
+                        btf.add_type(int_type);
+                    } else {
+                        btf.add_type(BtfType::Var(*ty, *vars));
+                    }
+                }
+                BtfType::DataSec(ty, data) => {
+                    if !features.btf_datasec {
+                        debug!("{}: not supported. replacing with STRUCT", kind);
+                        let members: Vec<btf_member> = data
+                            .iter()
+                            .map(|p| -> btf_member {
+                                let mt = self.type_by_id(p.type_).unwrap();
+                                btf_member {
+                                    name_off: mt.btf_type().unwrap().name_off,
+                                    type_: p.type_,
+                                    offset: p.offset * 8,
+                                }
+                            })
+                            .collect();
+                        let struct_type = BtfType::new_struct(ty.name_off, members, 0);
+                        btf.add_type(struct_type);
+                    } else {
+                        btf.add_type(BtfType::DataSec(*ty, data.to_vec()));
+                    }
+                }
+                BtfType::FuncProto(ty, vars) => {
+                    if !features.btf_func {
+                        debug!("{}: not supported. replacing with ENUM", kind);
+                        let members: Vec<btf_enum> = vars
+                            .iter()
+                            .map(|p| -> btf_enum {
+                                btf_enum {
+                                    name_off: p.name_off,
+                                    val: p.type_ as i32,
+                                }
+                            })
+                            .collect();
+                        let enum_type = BtfType::new_enum(ty.name_off, members);
+                        btf.add_type(enum_type);
+                    } else {
+                        btf.add_type(BtfType::FuncProto(*ty, vars.to_vec()));
+                    }
+                }
+                BtfType::Func(mut ty) => {
+                    if !features.btf_func {
+                        debug!("{}: not supported. replacing with TYPEDEF", kind);
+                        let typedef_type =
+                            BtfType::new_typedef(ty.name_off, unsafe { ty.__bindgen_anon_1.type_ });
+                        btf.add_type(typedef_type);
+                    } else if type_vlen(&ty) == btf_func_linkage::BTF_FUNC_GLOBAL as usize
+                        && !features.btf_func_global
+                    {
+                        debug!(
+                            "{}: BTF_FUNC_GLOBAL not supported. replacing with BTF_FUNC_STATIC",
+                            kind
+                        );
+                        ty.info |= (btf_func_linkage::BTF_FUNC_STATIC as u32) & 0xFFFF;
+                        btf.add_type(BtfType::Func(ty));
+                    } else {
+                        btf.add_type(BtfType::Func(ty));
+                    }
+                }
+                BtfType::Float(ty) => {
+                    if !features.btf_float {
+                        debug!("{}: not supported. replacing with STRUCT", kind);
+                        let struct_ty =
+                            BtfType::new_struct(0, vec![], unsafe { ty.__bindgen_anon_1.size });
+                        btf.add_type(struct_ty);
+                    } else {
+                        btf.add_type(BtfType::Float(*ty));
+                    }
+                }
+                // The type does not need sanitizing
+                ty => {
+                    btf.add_type(ty.clone());
+                }
+            }
+        }
+        Ok(btf)
+    }
+}
+
+impl Default for Btf {
+    fn default() -> Self {
+        Self::new()
+    }
 }
 
 unsafe fn read_btf_header(data: &[u8]) -> btf_header {
@@ -318,13 +572,19 @@ pub struct BtfExt {
     _endianness: Endianness,
     relocations: Vec<(u32, Vec<Relocation>)>,
     header: btf_ext_header,
-    _func_info_rec_size: usize,
-    _line_info_rec_size: usize,
+    func_info_rec_size: usize,
+    pub(crate) func_info: FuncInfo,
+    line_info_rec_size: usize,
+    pub(crate) line_info: LineInfo,
     core_relo_rec_size: usize,
 }
 
 impl BtfExt {
-    pub(crate) fn parse(data: &[u8], endianness: Endianness) -> Result<BtfExt, BtfError> {
+    pub(crate) fn parse(
+        data: &[u8],
+        endianness: Endianness,
+        btf: &Btf,
+    ) -> Result<BtfExt, BtfError> {
         // Safety: btf_ext_header is POD so read_unaligned is safe
         let header = unsafe {
             ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header)
@@ -366,13 +626,57 @@ impl BtfExt {
         let mut ext = BtfExt {
             header,
             relocations: Vec::new(),
-            _func_info_rec_size: rec_size(func_info_off, func_info_len)?,
-            _line_info_rec_size: rec_size(line_info_off, line_info_len)?,
+            func_info: FuncInfo::new(),
+            line_info: LineInfo::new(),
+            func_info_rec_size: rec_size(func_info_off, func_info_len)?,
+            line_info_rec_size: rec_size(line_info_off, line_info_len)?,
             core_relo_rec_size: rec_size(core_relo_off, core_relo_len)?,
             data: data.to_vec(),
             _endianness: endianness,
         };
 
+        let func_info_rec_size = ext.func_info_rec_size;
+        ext.func_info.data.extend(
+            SecInfoIter::new(ext.func_info_data(), ext.func_info_rec_size, endianness)
+                .map(move |sec| {
+                    let name = btf
+                        .string_at(sec.sec_name_off)
+                        .ok()
+                        .map(String::from)
+                        .unwrap();
+                    let info = FuncSecInfo::parse(
+                        sec.sec_name_off,
+                        sec.num_info,
+                        func_info_rec_size,
+                        sec.data,
+                        endianness,
+                    );
+                    Ok((name, info))
+                })
+                .collect::<Result<HashMap<_, _>, _>>()?,
+        );
+
+        let line_info_rec_size = ext.line_info_rec_size;
+        ext.line_info.data.extend(
+            SecInfoIter::new(ext.line_info_data(), ext.line_info_rec_size, endianness)
+                .map(move |sec| {
+                    let name = btf
+                        .string_at(sec.sec_name_off)
+                        .ok()
+                        .map(String::from)
+                        .unwrap();
+                    let info = LineSecInfo::parse(
+                        sec.sec_name_off,
+                        sec.num_info,
+                        line_info_rec_size,
+                        sec.data,
+                        endianness,
+                    );
+                    Ok((name, info))
+                })
+                .collect::<Result<HashMap<_, _>, _>>()?,
+        );
+
         let rec_size = ext.core_relo_rec_size;
         ext.relocations.extend(
             SecInfoIter::new(ext.core_relo_data(), ext.core_relo_rec_size, endianness)
@@ -406,9 +710,25 @@ impl BtfExt {
         self.info_data(self.header.core_relo_off, self.header.core_relo_len)
     }
 
+    fn func_info_data(&self) -> &[u8] {
+        self.info_data(self.header.func_info_off, self.header.func_info_len)
+    }
+
+    fn line_info_data(&self) -> &[u8] {
+        self.info_data(self.header.line_info_off, self.header.line_info_len)
+    }
+
     pub(crate) fn relocations(&self) -> impl Iterator<Item = &(u32, Vec<Relocation>)> {
         self.relocations.iter()
     }
+
+    pub(crate) fn func_info_rec_size(&self) -> usize {
+        self.func_info_rec_size
+    }
+
+    pub(crate) fn line_info_rec_size(&self) -> usize {
+        self.line_info_rec_size
+    }
 }
 
 pub(crate) struct SecInfoIter<'a> {
@@ -453,7 +773,7 @@ impl<'a> Iterator for SecInfoIter<'a> {
 
         Some(SecInfo {
             sec_name_off,
-            _num_info: num_info,
+            num_info,
             data,
         })
     }
@@ -462,12 +782,14 @@ impl<'a> Iterator for SecInfoIter<'a> {
 #[derive(Debug)]
 pub(crate) struct SecInfo<'a> {
     sec_name_off: u32,
-    _num_info: u32,
+    num_info: u32,
     data: &'a [u8],
 }
 
 #[cfg(test)]
 mod tests {
+    use crate::generated::{btf_param, BTF_INT_SIGNED, BTF_VAR_STATIC};
+
     use super::*;
 
     #[test]
@@ -530,12 +852,195 @@ mod tests {
             0x5a, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x5f, 0x00, 0x5f, 0x6c, 0x69, 0x63,
             0x65, 0x6e, 0x73, 0x65, 0x00, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00,
         ];
+        assert_eq!(data.len(), 517);
         let got = Btf::parse(data, Endianness::default());
         match got {
             Ok(_) => {}
             Err(e) => panic!("{}", e),
         }
-        let data2 = got.unwrap().to_bytes();
+        let btf = got.unwrap();
+        let data2 = btf.to_bytes();
+        assert_eq!(data2.len(), 517);
         assert_eq!(data, data2);
+
+        let ext_data: &[u8] = &[
+            0x9f, 0xeb, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
+            0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+            0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00,
+            0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x05, 0x2c, 0x00, 0x00,
+        ];
+
+        assert_eq!(ext_data.len(), 80);
+        let got = BtfExt::parse(ext_data, Endianness::default(), &btf);
+        if let Err(e) = got {
+            panic!("{}", e)
+        }
+    }
+
+    #[test]
+    fn test_write_btf() {
+        let mut btf = Btf::new();
+        let name_offset = btf.add_string("int".to_string());
+        let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+        btf.add_type(int_type);
+
+        let name_offset = btf.add_string("widget".to_string());
+        let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+        btf.add_type(int_type);
+
+        let btf_bytes = btf.to_bytes();
+        let raw_btf = btf_bytes.as_slice();
+
+        let parsed = Btf::parse(raw_btf, Endianness::default());
+        match parsed {
+            Ok(btf) => {
+                assert_eq!(btf.string_at(1).unwrap(), "int");
+                assert_eq!(btf.string_at(5).unwrap(), "widget");
+            }
+            Err(e) => {
+                panic!("{}", e)
+            }
+        }
+    }
+
+    #[test]
+    fn test_sanitize_btf() {
+        let mut btf = Btf::new();
+        let name_offset = btf.add_string("int".to_string());
+        let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+        let int_type_id = btf.add_type(int_type);
+
+        let name_offset = btf.add_string("foo".to_string());
+        let var_type = BtfType::new_var(name_offset, int_type_id, BTF_VAR_STATIC);
+        let var_type_id = btf.add_type(var_type);
+
+        let name_offset = btf.add_string(".data".to_string());
+        let variables = vec![btf_var_secinfo {
+            type_: var_type_id,
+            offset: 0,
+            size: 4,
+        }];
+        let datasec_type = BtfType::new_datasec(name_offset, variables, 4);
+        btf.add_type(datasec_type);
+
+        let name_offset = btf.add_string("float".to_string());
+        let float_type = BtfType::new_float(name_offset, 16);
+        btf.add_type(float_type);
+
+        let a_name = btf.add_string("a".to_string());
+        let b_name = btf.add_string("b".to_string());
+        let params = vec![
+            btf_param {
+                name_off: a_name,
+                type_: int_type_id,
+            },
+            btf_param {
+                name_off: b_name,
+                type_: int_type_id,
+            },
+        ];
+        let func_proto = BtfType::new_func_proto(params, int_type_id);
+        let func_proto_type_id = btf.add_type(func_proto);
+
+        let add = btf.add_string("static".to_string());
+        let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_STATIC);
+        btf.add_type(func);
+
+        let c_name = btf.add_string("c".to_string());
+        let d_name = btf.add_string("d".to_string());
+        let params = vec![
+            btf_param {
+                name_off: c_name,
+                type_: int_type_id,
+            },
+            btf_param {
+                name_off: d_name,
+                type_: int_type_id,
+            },
+        ];
+        let func_proto = BtfType::new_func_proto(params, int_type_id);
+        let func_proto_type_id = btf.add_type(func_proto);
+
+        let add = btf.add_string("global".to_string());
+        let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_GLOBAL);
+        btf.add_type(func);
+
+        let cases = HashMap::from([
+            (
+                "noop",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: true,
+                    btf_func_global: true,
+                    btf_datasec: true,
+                    btf_float: true,
+                },
+            ),
+            (
+                "no datasec",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: true,
+                    btf_func_global: true,
+                    btf_datasec: false,
+                    btf_float: true,
+                },
+            ),
+            (
+                "no float",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: true,
+                    btf_func_global: true,
+                    btf_datasec: true,
+                    btf_float: false,
+                },
+            ),
+            (
+                "no func",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: false,
+                    btf_func_global: true,
+                    btf_datasec: true,
+                    btf_float: true,
+                },
+            ),
+            (
+                "no global func",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: true,
+                    btf_func_global: false,
+                    btf_datasec: true,
+                    btf_float: true,
+                },
+            ),
+            (
+                "all off",
+                Features {
+                    bpf_name: true,
+                    btf: true,
+                    btf_func: false,
+                    btf_func_global: false,
+                    btf_datasec: false,
+                    btf_float: false,
+                },
+            ),
+        ]);
+
+        for (name, features) in cases {
+            println!("[CASE] Sanitize {}", name);
+            let new_btf = btf.sanitize(&features).unwrap();
+            let raw_new_btf = new_btf.to_bytes();
+            Btf::parse(&raw_new_btf, Endianness::default()).unwrap();
+        }
     }
 }

+ 189 - 0
aya/src/obj/btf/info.rs

@@ -0,0 +1,189 @@
+use std::{collections::HashMap, convert::TryInto};
+
+use bytes::BufMut;
+use object::Endianness;
+
+use crate::{
+    generated::{bpf_func_info, bpf_line_info},
+    obj::relocation::INS_SIZE,
+    util::bytes_of,
+};
+
+/* The func_info subsection layout:
+ *   record size for struct bpf_func_info in the func_info subsection
+ *   struct btf_sec_func_info for section #1
+ *   a list of bpf_func_info records for section #1
+ *     where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
+ *     but may not be identical
+ *   struct btf_sec_func_info for section #2
+ *   a list of bpf_func_info records for section #2
+ *   ......
+ */
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FuncSecInfo {
+    pub _sec_name_offset: u32,
+    pub num_info: u32,
+    pub func_info: Vec<bpf_func_info>,
+}
+
+impl FuncSecInfo {
+    pub(crate) fn parse(
+        sec_name_offset: u32,
+        num_info: u32,
+        rec_size: usize,
+        func_info_data: &[u8],
+        endianness: Endianness,
+    ) -> FuncSecInfo {
+        let func_info = func_info_data
+            .chunks(rec_size)
+            .map(|data| {
+                let read_u32 = if endianness == Endianness::Little {
+                    u32::from_le_bytes
+                } else {
+                    u32::from_be_bytes
+                };
+
+                let mut offset = 0;
+
+                // ELF instruction offsets are in bytes
+                // Kernel instruction offsets are in instructions units
+                // We can convert by dividing the length in bytes by INS_SIZE
+                let insn_off =
+                    read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
+                offset += 4;
+                let type_id = read_u32(data[offset..offset + 4].try_into().unwrap());
+
+                bpf_func_info { insn_off, type_id }
+            })
+            .collect();
+
+        FuncSecInfo {
+            _sec_name_offset: sec_name_offset,
+            num_info,
+            func_info,
+        }
+    }
+
+    pub(crate) fn func_info_bytes(&self) -> Vec<u8> {
+        let mut buf = vec![];
+        for l in &self.func_info {
+            // Safety: bpf_func_info is POD
+            buf.put(unsafe { bytes_of::<bpf_func_info>(l) })
+        }
+        buf
+    }
+
+    pub(crate) fn len(&self) -> usize {
+        self.func_info.len()
+    }
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct FuncInfo {
+    pub data: HashMap<String, FuncSecInfo>,
+}
+
+impl FuncInfo {
+    pub(crate) fn new() -> FuncInfo {
+        FuncInfo {
+            data: HashMap::new(),
+        }
+    }
+
+    pub(crate) fn get(&self, name: &str) -> FuncSecInfo {
+        match self.data.get(name) {
+            Some(d) => d.clone(),
+            None => FuncSecInfo::default(),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Default)]
+pub(crate) struct LineSecInfo {
+    // each line info section has a header
+    pub _sec_name_offset: u32,
+    pub num_info: u32,
+    // followed by one or more bpf_line_info structs
+    pub line_info: Vec<bpf_line_info>,
+}
+
+impl LineSecInfo {
+    pub(crate) fn parse(
+        sec_name_offset: u32,
+        num_info: u32,
+        rec_size: usize,
+        func_info_data: &[u8],
+        endianness: Endianness,
+    ) -> LineSecInfo {
+        let line_info = func_info_data
+            .chunks(rec_size)
+            .map(|data| {
+                let read_u32 = if endianness == Endianness::Little {
+                    u32::from_le_bytes
+                } else {
+                    u32::from_be_bytes
+                };
+
+                let mut offset = 0;
+
+                // ELF instruction offsets are in bytes
+                // Kernel instruction offsets are in instructions units
+                // We can convert by dividing the length in bytes by INS_SIZE
+                let insn_off =
+                    read_u32(data[offset..offset + 4].try_into().unwrap()) / INS_SIZE as u32;
+                offset += 4;
+                let file_name_off = read_u32(data[offset..offset + 4].try_into().unwrap());
+                offset += 4;
+                let line_off = read_u32(data[offset..offset + 4].try_into().unwrap());
+                offset += 4;
+                let line_col = read_u32(data[offset..offset + 4].try_into().unwrap());
+
+                bpf_line_info {
+                    insn_off,
+                    file_name_off,
+                    line_off,
+                    line_col,
+                }
+            })
+            .collect();
+
+        LineSecInfo {
+            _sec_name_offset: sec_name_offset,
+            num_info,
+            line_info,
+        }
+    }
+
+    pub(crate) fn line_info_bytes(&self) -> Vec<u8> {
+        let mut buf = vec![];
+        for l in &self.line_info {
+            // Safety: bpf_func_info is POD
+            buf.put(unsafe { bytes_of::<bpf_line_info>(l) })
+        }
+        buf
+    }
+
+    pub(crate) fn len(&self) -> usize {
+        self.line_info.len()
+    }
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct LineInfo {
+    pub data: HashMap<String, LineSecInfo>,
+}
+
+impl LineInfo {
+    pub(crate) fn new() -> LineInfo {
+        LineInfo {
+            data: HashMap::new(),
+        }
+    }
+
+    pub(crate) fn get(&self, name: &str) -> LineSecInfo {
+        match self.data.get(name) {
+            Some(d) => d.clone(),
+            None => LineSecInfo::default(),
+        }
+    }
+}

+ 2 - 0
aya/src/obj/btf/mod.rs

@@ -1,8 +1,10 @@
 #[allow(clippy::module_inception)]
 mod btf;
+mod info;
 mod relocation;
 mod types;
 
 pub use btf::*;
+pub(crate) use info::*;
 pub use relocation::RelocationError;
 pub(crate) use types::*;

+ 203 - 13
aya/src/obj/btf/types.rs

@@ -1,5 +1,6 @@
 use std::{
     convert::{TryFrom, TryInto},
+    fmt::Display,
     mem, ptr,
 };
 
@@ -7,11 +8,11 @@ use object::Endianness;
 
 use crate::{
     generated::{
-        btf_array, btf_enum, btf_member, btf_param, btf_type, btf_type__bindgen_ty_1, btf_var,
-        btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST, BTF_KIND_DATASEC, BTF_KIND_ENUM,
-        BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO, BTF_KIND_FWD, BTF_KIND_INT,
-        BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT, BTF_KIND_TYPEDEF, BTF_KIND_UNION,
-        BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE,
+        btf_array, btf_enum, btf_func_linkage, btf_member, btf_param, btf_type,
+        btf_type__bindgen_ty_1, btf_var, btf_var_secinfo, BTF_KIND_ARRAY, BTF_KIND_CONST,
+        BTF_KIND_DATASEC, BTF_KIND_ENUM, BTF_KIND_FLOAT, BTF_KIND_FUNC, BTF_KIND_FUNC_PROTO,
+        BTF_KIND_FWD, BTF_KIND_INT, BTF_KIND_PTR, BTF_KIND_RESTRICT, BTF_KIND_STRUCT,
+        BTF_KIND_TYPEDEF, BTF_KIND_UNION, BTF_KIND_UNKN, BTF_KIND_VAR, BTF_KIND_VOLATILE,
     },
     obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH},
 };
@@ -87,6 +88,36 @@ impl TryFrom<u32> for BtfKind {
     }
 }
 
+impl Display for BtfKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            BtfKind::Unknown => write!(f, "[UNKNOWN]"),
+            BtfKind::Int => write!(f, "[INT]"),
+            BtfKind::Float => write!(f, "[FLOAT]"),
+            BtfKind::Ptr => write!(f, "[PTR]"),
+            BtfKind::Array => write!(f, "[ARRAY]"),
+            BtfKind::Struct => write!(f, "[STRUCT]"),
+            BtfKind::Union => write!(f, "[UNION]"),
+            BtfKind::Enum => write!(f, "[ENUM]"),
+            BtfKind::Fwd => write!(f, "[FWD]"),
+            BtfKind::Typedef => write!(f, "[TYPEDEF]"),
+            BtfKind::Volatile => write!(f, "[VOLATILE]"),
+            BtfKind::Const => write!(f, "[CONST]"),
+            BtfKind::Restrict => write!(f, "[RESTRICT]"),
+            BtfKind::Func => write!(f, "[FUNC]"),
+            BtfKind::FuncProto => write!(f, "[FUNC_PROTO]"),
+            BtfKind::Var => write!(f, "[VAR]"),
+            BtfKind::DataSec => write!(f, "[DATASEC]"),
+        }
+    }
+}
+
+impl Default for BtfKind {
+    fn default() -> Self {
+        BtfKind::Unknown
+    }
+}
+
 unsafe fn read<T>(data: &[u8]) -> Result<T, BtfError> {
     if mem::size_of::<T>() > data.len() {
         return Err(BtfError::InvalidTypeInfo);
@@ -170,7 +201,7 @@ impl BtfType {
             }
             BtfType::Enum(btf_type, enums) => {
                 let mut buf = bytes_of::<btf_type>(btf_type).to_vec();
-                for en in enums { 
+                for en in enums {
                     buf.append(&mut bytes_of::<btf_enum>(en).to_vec());
                 }
                 buf
@@ -267,13 +298,109 @@ impl BtfType {
     pub(crate) fn is_composite(&self) -> bool {
         matches!(self, BtfType::Struct(_, _) | BtfType::Union(_, _))
     }
+
+    pub(crate) fn new_int(name_off: u32, size: u32, encoding: u32, offset: u32) -> BtfType {
+        let info = (BTF_KIND_INT) << 24;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.size = size;
+
+        let mut data = 0u32;
+        data |= (encoding & 0x0f) << 24;
+        data |= (offset & 0xff) << 16;
+        data |= (size * 8) & 0xff;
+        BtfType::Int(btf_type, data)
+    }
+
+    pub(crate) fn new_func(name_off: u32, proto: u32, linkage: btf_func_linkage) -> BtfType {
+        let mut info = (BTF_KIND_FUNC) << 24;
+        info |= (linkage as u32) & 0xFFFF;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.type_ = proto;
+        BtfType::Func(btf_type)
+    }
+
+    pub(crate) fn new_func_proto(params: Vec<btf_param>, return_type: u32) -> BtfType {
+        let mut info = (BTF_KIND_FUNC_PROTO) << 24;
+        info |= (params.len() as u32) & 0xFFFF;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = 0;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.type_ = return_type;
+        BtfType::FuncProto(btf_type, params)
+    }
+
+    pub(crate) fn new_var(name_off: u32, type_: u32, linkage: u32) -> BtfType {
+        let info = (BTF_KIND_VAR) << 24;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.type_ = type_;
+        let var = btf_var { linkage };
+        BtfType::Var(btf_type, var)
+    }
+
+    pub(crate) fn new_datasec(
+        name_off: u32,
+        variables: Vec<btf_var_secinfo>,
+        size: u32,
+    ) -> BtfType {
+        let mut info = (BTF_KIND_DATASEC) << 24;
+        info |= (variables.len() as u32) & 0xFFFF;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.size = size;
+        BtfType::DataSec(btf_type, variables)
+    }
+
+    pub(crate) fn new_float(name_off: u32, size: u32) -> BtfType {
+        let info = (BTF_KIND_FLOAT) << 24;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.size = size;
+        BtfType::Float(btf_type)
+    }
+
+    pub(crate) fn new_struct(name_off: u32, members: Vec<btf_member>, size: u32) -> BtfType {
+        let mut info = (BTF_KIND_STRUCT) << 24;
+        info |= (members.len() as u32) & 0xFFFF;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.size = size;
+        BtfType::Struct(btf_type, members)
+    }
+
+    pub(crate) fn new_enum(name_off: u32, members: Vec<btf_enum>) -> BtfType {
+        let mut info = (BTF_KIND_ENUM) << 24;
+        info |= (members.len() as u32) & 0xFFFF;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.size = 4;
+        BtfType::Enum(btf_type, members)
+    }
+
+    pub(crate) fn new_typedef(name_off: u32, type_: u32) -> BtfType {
+        let info = (BTF_KIND_TYPEDEF) << 24;
+        let mut btf_type = unsafe { std::mem::zeroed::<btf_type>() };
+        btf_type.name_off = name_off;
+        btf_type.info = info;
+        btf_type.__bindgen_anon_1.type_ = type_;
+        BtfType::Typedef(btf_type)
+    }
 }
 
 fn type_kind(ty: &btf_type) -> Result<BtfKind, BtfError> {
     ((ty.info >> 24) & 0x1F).try_into()
 }
 
-fn type_vlen(ty: &btf_type) -> usize {
+pub(crate) fn type_vlen(ty: &btf_type) -> usize {
     (ty.info & 0xFFFF) as usize
 }
 
@@ -458,6 +585,8 @@ impl std::fmt::Debug for btf_type__bindgen_ty_1 {
 
 #[cfg(test)]
 mod tests {
+    use crate::generated::BTF_INT_SIGNED;
+
     use super::*;
 
     #[test]
@@ -471,13 +600,44 @@ mod tests {
         match got {
             Ok(BtfType::Int(ty, nr_bits)) => {
                 assert_eq!(ty.name_off, 1);
+                assert_eq!(unsafe { ty.__bindgen_anon_1.size }, 8);
                 assert_eq!(nr_bits, 64);
             }
             Ok(t) => panic!("expected int type, got {:#?}", t),
             Err(_) => panic!("unexpected error"),
         }
         let data2 = got.unwrap().to_bytes();
-        assert_eq!(data, data2.as_slice())
+        assert_eq!(data, data2.as_slice());
+    }
+
+    #[test]
+    fn test_write_btf_long_unsigned_int() {
+        let data: &[u8] = &[
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00,
+            0x00, 0x00,
+        ];
+        let int = BtfType::new_int(1, 8, 0, 0);
+        assert_eq!(int.to_bytes(), data);
+    }
+
+    #[test]
+    fn test_write_btf_uchar() {
+        let data: &[u8] = &[
+            0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
+            0x00, 0x00,
+        ];
+        let int = BtfType::new_int(0x13, 1, 0, 0);
+        assert_eq!(int.to_bytes(), data);
+    }
+
+    #[test]
+    fn test_write_btf_signed_short_int() {
+        let data: &[u8] = &[
+            0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00,
+            0x00, 0x01,
+        ];
+        let int = BtfType::new_int(0x4a, 2, BTF_INT_SIGNED, 0);
+        assert_eq!(int.to_bytes(), data);
     }
 
     #[test]
@@ -698,14 +858,20 @@ mod tests {
     #[test]
     fn test_read_btf_type_func_datasec() {
         let endianness = Endianness::default();
-        // NOTE: There was no data in /sys/kernell/btf/vmlinux for this type
         let data: &[u8] = &[
-            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xd9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
         ];
         let got = unsafe { BtfType::read(data, endianness) };
-        match got {
-            Ok(BtfType::DataSec(_, _)) => {}
+        match &got {
+            Ok(BtfType::DataSec(ty, info)) => {
+                assert_eq!(0, unsafe { ty.__bindgen_anon_1.size } as usize);
+                assert_eq!(1, type_vlen(ty) as usize);
+                assert_eq!(1, info.len());
+                assert_eq!(11, info[0].type_);
+                assert_eq!(0, info[0].offset);
+                assert_eq!(4, info[0].size);
+            }
             Ok(t) => panic!("expected datasec type, got {:#?}", t),
             Err(_) => panic!("unexpected error"),
         }
@@ -728,4 +894,28 @@ mod tests {
         let data2 = got.unwrap().to_bytes();
         assert_eq!(data, data2.as_slice())
     }
+
+    #[test]
+    fn test_write_btf_func_proto() {
+        let params = vec![
+            btf_param {
+                name_off: 1,
+                type_: 1,
+            },
+            btf_param {
+                name_off: 3,
+                type_: 1,
+            },
+        ];
+        let func_proto = BtfType::new_func_proto(params, 2);
+        let data = func_proto.to_bytes();
+        let got = unsafe { BtfType::read(&data, Endianness::default()) };
+        match got {
+            Ok(BtfType::FuncProto(btf_type, _params)) => {
+                assert_eq!(type_vlen(&btf_type), 2);
+            }
+            Ok(t) => panic!("expected func proto type, got {:#?}", t),
+            Err(_) => panic!("unexpected error"),
+        }
+    }
 }

+ 110 - 9
aya/src/obj/mod.rs

@@ -25,6 +25,8 @@ use crate::{
 };
 use std::slice::from_raw_parts_mut;
 
+use self::btf::{FuncSecInfo, LineSecInfo};
+
 const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
 /// The first five __u32 of `bpf_map_def` must be defined.
 const MINIMUM_MAP_SIZE: usize = mem::size_of::<u32>() * 5;
@@ -41,6 +43,10 @@ pub struct Object {
     pub(crate) functions: HashMap<u64, Function>,
     pub(crate) relocations: HashMap<SectionIndex, HashMap<u64, Relocation>>,
     pub(crate) symbols_by_index: HashMap<usize, Symbol>,
+    pub(crate) section_sizes: HashMap<String, u64>,
+    // symbol_offset_by_name caches symbols that could be referenced from a
+    // BTF VAR type so the offsets can be fixed up
+    pub(crate) symbol_offset_by_name: HashMap<String, u64>,
 }
 
 #[derive(Debug, Clone, PartialEq)]
@@ -88,6 +94,10 @@ pub(crate) struct Function {
     pub(crate) section_index: SectionIndex,
     pub(crate) section_offset: usize,
     pub(crate) instructions: Vec<bpf_insn>,
+    pub(crate) func_info: FuncSecInfo,
+    pub(crate) line_info: LineSecInfo,
+    pub(crate) func_info_rec_size: usize,
+    pub(crate) line_info_rec_size: usize,
 }
 
 #[derive(Debug, Clone)]
@@ -113,6 +123,7 @@ pub enum ProgramSection {
     BtfTracePoint { name: String },
     FEntry { name: String },
     FExit { name: String },
+    Extension { name: String },
 }
 
 impl ProgramSection {
@@ -139,6 +150,7 @@ impl ProgramSection {
             ProgramSection::BtfTracePoint { name } => name,
             ProgramSection::FEntry { name } => name,
             ProgramSection::FExit { name } => name,
+            ProgramSection::Extension { name } => name,
         }
     }
 }
@@ -194,6 +206,7 @@ impl FromStr for ProgramSection {
             "lsm" => Lsm { name },
             "fentry" => FEntry { name },
             "fexit" => FExit { name },
+            "freplace" => Extension { name },
             _ => {
                 return Err(ParseError::InvalidProgramSection {
                     section: section.to_owned(),
@@ -224,9 +237,14 @@ impl Object {
 
         if let Some(symbol_table) = obj.symbol_table() {
             for symbol in symbol_table.symbols() {
+                let name = symbol
+                    .name()
+                    .ok()
+                    .map(String::from)
+                    .ok_or(BtfError::InvalidSymbolName)?;
                 let sym = Symbol {
                     index: symbol.index().0,
-                    name: symbol.name().ok().map(String::from),
+                    name: Some(name.clone()),
                     section_index: symbol.section().index(),
                     address: symbol.address(),
                     size: symbol.size(),
@@ -236,10 +254,30 @@ impl Object {
                 bpf_obj
                     .symbols_by_index
                     .insert(symbol.index().0, sym.clone());
+
+                if symbol.is_global() || symbol.kind() == SymbolKind::Data {
+                    bpf_obj.symbol_offset_by_name.insert(name, symbol.address());
+                }
+            }
+        }
+
+        // .BTF and .BTF.ext sections must be parsed first
+        // as they're required to prepare function and line information
+        // when parsing program sections
+        if let Some(s) = obj.section_by_name(".BTF") {
+            bpf_obj.parse_section(Section::try_from(&s)?)?;
+            if let Some(s) = obj.section_by_name(".BTF.ext") {
+                bpf_obj.parse_section(Section::try_from(&s)?)?;
             }
         }
 
         for s in obj.sections() {
+            if let Ok(name) = s.name() {
+                if name == ".BTF" || name == ".BTF.ext" {
+                    continue;
+                }
+            }
+
             bpf_obj.parse_section(Section::try_from(&s)?)?;
         }
 
@@ -258,6 +296,8 @@ impl Object {
             functions: HashMap::new(),
             relocations: HashMap::new(),
             symbols_by_index: HashMap::new(),
+            section_sizes: HashMap::new(),
+            symbol_offset_by_name: HashMap::new(),
         }
     }
 
@@ -313,13 +353,32 @@ impl Object {
     }
 
     fn parse_btf_ext(&mut self, section: &Section) -> Result<(), BtfError> {
-        self.btf_ext = Some(BtfExt::parse(section.data, self.endianness)?);
+        self.btf_ext = Some(BtfExt::parse(
+            section.data,
+            self.endianness,
+            self.btf.as_ref().unwrap(),
+        )?);
         Ok(())
     }
 
     fn parse_program(&self, section: &Section) -> Result<Program, ParseError> {
         let prog_sec = ProgramSection::from_str(section.name)?;
         let name = prog_sec.name().to_owned();
+
+        let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
+            if let Some(btf_ext) = &self.btf_ext {
+                let func_info = btf_ext.func_info.get(section.name);
+                let line_info = btf_ext.line_info.get(section.name);
+                (
+                    func_info,
+                    line_info,
+                    btf_ext.func_info_rec_size(),
+                    btf_ext.line_info_rec_size(),
+                )
+            } else {
+                (FuncSecInfo::default(), LineSecInfo::default(), 0, 0)
+            };
+
         Ok(Program {
             license: self.license.clone(),
             kernel_version: self.kernel_version,
@@ -330,6 +389,10 @@ impl Object {
                 section_index: section.index,
                 section_offset: 0,
                 instructions: copy_instructions(section.data)?,
+                func_info,
+                line_info,
+                func_info_rec_size,
+                line_info_rec_size,
             },
         })
     }
@@ -365,6 +428,38 @@ impl Object {
                 });
             }
 
+            let (func_info, line_info, func_info_rec_size, line_info_rec_size) =
+                if let Some(btf_ext) = &self.btf_ext {
+                    let bytes_offset = offset as u32 / INS_SIZE as u32;
+                    let section_size_bytes = sym.size as u32 / INS_SIZE as u32;
+
+                    let mut func_info = btf_ext.func_info.get(section.name);
+                    func_info.func_info = func_info
+                        .func_info
+                        .into_iter()
+                        .filter(|f| f.insn_off == bytes_offset)
+                        .collect();
+
+                    let mut line_info = btf_ext.line_info.get(section.name);
+                    line_info.line_info = line_info
+                        .line_info
+                        .into_iter()
+                        .filter(|l| {
+                            l.insn_off >= bytes_offset
+                                && l.insn_off < (bytes_offset + section_size_bytes) as u32
+                        })
+                        .collect();
+
+                    (
+                        func_info,
+                        line_info,
+                        btf_ext.func_info_rec_size(),
+                        btf_ext.line_info_rec_size(),
+                    )
+                } else {
+                    (FuncSecInfo::default(), LineSecInfo::default(), 0, 0)
+                };
+
             self.functions.insert(
                 sym.address,
                 Function {
@@ -375,6 +470,10 @@ impl Object {
                     instructions: copy_instructions(
                         &section.data[offset..offset + sym.size as usize],
                     )?,
+                    func_info,
+                    line_info,
+                    func_info_rec_size,
+                    line_info_rec_size,
                 },
             );
 
@@ -407,7 +506,8 @@ impl Object {
         {
             parts.push(parts[0]);
         }
-
+        self.section_sizes
+            .insert(section.name.to_owned(), section.size);
         match section.kind {
             BpfSectionKind::Data => {
                 self.maps
@@ -436,8 +536,10 @@ impl Object {
                     );
                 }
             }
-
-            _ => {}
+            BpfSectionKind::Undefined
+            | BpfSectionKind::BtfMaps
+            | BpfSectionKind::License
+            | BpfSectionKind::Version => {}
         }
 
         Ok(())
@@ -700,7 +802,7 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
     }
 }
 
-fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
+pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
     if data.len() % mem::size_of::<bpf_insn>() > 0 {
         return Err(ParseError::InvalidProgramCode);
     }
@@ -978,9 +1080,8 @@ mod tests {
                     address: 0,
                     section_index: SectionIndex(0),
                     section_offset: 0,
-                    instructions
-                }
-            }) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
+                    instructions,
+                    ..} }) if license.to_string_lossy() == "GPL" && name == "foo" && instructions.len() == 1
         );
     }
 

+ 33 - 1
aya/src/obj/relocation.rs

@@ -13,7 +13,7 @@ use crate::{
     BpfError,
 };
 
-const INS_SIZE: usize = mem::size_of::<bpf_insn>();
+pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
 
 #[derive(Debug, Error)]
 enum RelocationError {
@@ -227,6 +227,11 @@ impl<'a> FunctionLinker<'a> {
         // at `start_ins`. We'll use `start_ins` to do pc-relative calls.
         let start_ins = program.instructions.len();
         program.instructions.extend(&fun.instructions);
+
+        // link func and line info into the main program
+        // the offset needs to be adjusted
+        self.link_func_and_line_info(program, fun, start_ins)?;
+
         self.linked_functions.insert(fun.address, start_ins);
 
         // relocate `fun`, recursively linking in all the callees
@@ -296,6 +301,33 @@ impl<'a> FunctionLinker<'a> {
 
         Ok(())
     }
+
+    fn link_func_and_line_info(
+        &mut self,
+        program: &mut Function,
+        fun: &Function,
+        start: usize,
+    ) -> Result<(), RelocationError> {
+        let off_adj = start - (fun.section_offset as usize / INS_SIZE);
+        let func_info = &fun.func_info.func_info;
+        let func_info = func_info.iter().map(|f| {
+            let mut new = *f;
+            new.insn_off = f.insn_off + off_adj as u32;
+            new
+        });
+        program.func_info.func_info.extend(func_info);
+        program.func_info.num_info = program.func_info.func_info.len() as u32;
+
+        let line_info = &fun.line_info.line_info;
+        let line_info = line_info.iter().map(|l| {
+            let mut new = *l;
+            new.insn_off = l.insn_off + off_adj as u32;
+            new
+        });
+        program.line_info.line_info.extend(line_info);
+        program.line_info.num_info = program.func_info.func_info.len() as u32;
+        Ok(())
+    }
 }
 
 fn is_call(ins: &bpf_insn) -> bool {

+ 6 - 7
aya/src/programs/cgroup_skb.rs

@@ -88,13 +88,12 @@ impl CgroupSkb {
         };
         let k_ver = kernel_version().unwrap();
         if k_ver >= (5, 7, 0) {
-            let link_fd =
-                bpf_link_create(prog_fd, cgroup_fd, attach_type, 0).map_err(|(_, io_error)| {
-                    ProgramError::SyscallError {
-                        call: "bpf_link_create".to_owned(),
-                        io_error,
-                    }
-                })? as RawFd;
+            let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err(
+                |(_, io_error)| ProgramError::SyscallError {
+                    call: "bpf_link_create".to_owned(),
+                    io_error,
+                },
+            )? as RawFd;
             Ok(self.data.link(FdLink { fd: Some(link_fd) }))
         } else {
             bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| {

+ 140 - 0
aya/src/programs/extension.rs

@@ -0,0 +1,140 @@
+use std::os::unix::prelude::{AsRawFd, RawFd};
+use thiserror::Error;
+
+use object::Endianness;
+
+use crate::{
+    generated::{bpf_attach_type::BPF_CGROUP_INET_INGRESS, bpf_prog_type::BPF_PROG_TYPE_EXT},
+    obj::btf::BtfKind,
+    programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError},
+    sys::{self, bpf_link_create},
+    Btf,
+};
+
+/// The type returned when loading or attaching an [`Extension`] fails
+#[derive(Debug, Error)]
+pub enum ExtensionError {
+    #[error("target BPF program does not have BTF loaded to the kernel")]
+    NoBTF,
+}
+
+/// A program used to extend existing BPF programs
+///
+/// [`Extension`] programs can be loaded to replace a global
+/// function in a program that has already been loaded.
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 5.9
+///
+/// # Examples
+///
+/// ```no_run
+/// use aya::{BpfLoader, programs::{Xdp, XdpFlags, Extension, ProgramFd}};
+/// use std::convert::TryInto;
+///
+/// let mut bpf = BpfLoader::new().extension("extension").load_file("app.o")?;
+/// let prog: &mut Xdp = bpf.program_mut("main").unwrap().try_into()?;
+/// prog.load()?;
+/// prog.attach("eth0", XdpFlags::default())?;
+///
+/// let prog_fd = prog.fd().unwrap();
+/// let ext: &mut Extension = bpf.program_mut("extension").unwrap().try_into()?;
+/// ext.load(prog_fd, "function_to_replace")?;
+/// ext.attach()?;
+/// Ok::<(), aya::BpfError>(())
+/// ```
+#[derive(Debug)]
+#[doc(alias = "BPF_PROG_TYPE_EXT")]
+pub struct Extension {
+    pub(crate) data: ProgramData,
+}
+
+impl Extension {
+    /// Loads the extension inside the kernel.
+    ///
+    /// Prepares the code included in the extension to replace the code of the function
+    /// `func_name` within the eBPF program represented by the `program` file descriptor.
+    /// This requires that both the [`Extension`] and `program` have had their BTF
+    /// loaded into the kernel as the verifier must check that the function signatures
+    /// match.
+    ///
+    /// The extension code will be loaded but inactive until it's attached.
+    /// There are no restrictions on what functions may be replaced, so you could replace
+    /// the main entry point of your program with an extension.
+    ///
+    /// See also [`Program::load`](crate::programs::Program::load).
+    pub fn load<T: AsRawFd>(&mut self, program: T, func_name: &str) -> Result<(), ProgramError> {
+        let target_prog_fd = program.as_raw_fd();
+
+        let info = sys::bpf_obj_get_info_by_fd(target_prog_fd).map_err(|io_error| {
+            ProgramError::SyscallError {
+                call: "bpf_obj_get_info_by_fd".to_owned(),
+                io_error,
+            }
+        })?;
+
+        if info.btf_id == 0 {
+            return Err(ProgramError::ExtensionError(ExtensionError::NoBTF));
+        }
+
+        let btf_fd = sys::bpf_btf_get_fd_by_id(info.btf_id).map_err(|io_error| {
+            ProgramError::SyscallError {
+                call: "bpf_btf_get_fd_by_id".to_owned(),
+                io_error,
+            }
+        })?;
+
+        let mut buf = vec![0u8; 4096];
+        let btf_info = match sys::btf_obj_get_info_by_fd(btf_fd, &mut buf) {
+            Ok(info) => {
+                if info.btf_size > buf.len() as u32 {
+                    buf.resize(info.btf_size as usize, 0u8);
+                    let btf_info =
+                        sys::btf_obj_get_info_by_fd(btf_fd, &mut buf).map_err(|io_error| {
+                            ProgramError::SyscallError {
+                                call: "bpf_obj_get_info_by_fd".to_owned(),
+                                io_error,
+                            }
+                        })?;
+                    Ok(btf_info)
+                } else {
+                    Ok(info)
+                }
+            }
+            Err(io_error) => Err(ProgramError::SyscallError {
+                call: "bpf_obj_get_info_by_fd".to_owned(),
+                io_error,
+            }),
+        }?;
+
+        let btf = Btf::parse(&buf[0..btf_info.btf_size as usize], Endianness::default())
+            .map_err(ProgramError::Btf)?;
+
+        let btf_id = btf
+            .id_by_type_name_kind(func_name, BtfKind::Func)
+            .map_err(ProgramError::Btf)?;
+
+        self.data.attach_btf_obj_fd = Some(btf_fd as u32);
+        self.data.attach_prog_fd = Some(target_prog_fd);
+        self.data.attach_btf_id = Some(btf_id);
+        load_program(BPF_PROG_TYPE_EXT, &mut self.data)
+    }
+
+    /// Attaches the extension
+    ///
+    /// Attaches the extension effectively replacing the original target function.
+    /// Detaching the returned link restores the original function.
+    pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
+        let prog_fd = self.data.fd_or_err()?;
+        let target_fd = self.data.attach_prog_fd.ok_or(ProgramError::NotLoaded)?;
+        let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
+        // the attach type must be set as 0, which is bpf_attach_type::BPF_CGROUP_INET_INGRESS
+        let link_fd = bpf_link_create(prog_fd, target_fd, BPF_CGROUP_INET_INGRESS, Some(btf_id), 0)
+            .map_err(|(_, io_error)| ProgramError::SyscallError {
+                call: "bpf_link_create".to_owned(),
+                io_error,
+            })? as RawFd;
+        Ok(self.data.link(FdLink { fd: Some(link_fd) }))
+    }
+}

+ 63 - 59
aya/src/programs/mod.rs

@@ -37,6 +37,7 @@
 //! [`Bpf::program_mut`]: crate::Bpf::program_mut
 //! [`maps`]: crate::maps
 mod cgroup_skb;
+mod extension;
 mod fentry;
 mod fexit;
 mod kprobe;
@@ -60,9 +61,8 @@ mod xdp;
 use libc::{close, dup, ENOSPC};
 use std::{
     cell::RefCell,
-    cmp,
     convert::TryFrom,
-    ffi::{CStr, CString},
+    ffi::CString,
     io,
     os::unix::io::{AsRawFd, RawFd},
     path::Path,
@@ -71,6 +71,7 @@ use std::{
 use thiserror::Error;
 
 pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
+pub use extension::{Extension, ExtensionError};
 pub use fentry::FEntry;
 pub use fexit::FExit;
 pub use kprobe::{KProbe, KProbeError};
@@ -95,6 +96,7 @@ use crate::{
     maps::MapError,
     obj::{self, btf::BtfError, Function, KernelVersion},
     sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs},
+    util::VerifierLog,
 };
 
 /// Error type returned when working with programs.
@@ -175,9 +177,21 @@ pub enum ProgramError {
     #[error(transparent)]
     TcError(#[from] TcError),
 
+    /// An error occurred while working with an [`Extension`] program.
+    #[error(transparent)]
+    ExtensionError(#[from] ExtensionError),
+
     /// An error occurred while working with BTF.
     #[error(transparent)]
     Btf(#[from] BtfError),
+
+    /// The program is not attached.
+    #[error("the program name `{name}` is invalid")]
+    InvalidName { name: String },
+
+    /// The program is too long.
+    #[error("the program name `{name}` it longer than 16 characters")]
+    NameTooLong { name: String },
 }
 
 pub trait ProgramFd {
@@ -204,6 +218,7 @@ pub enum Program {
     BtfTracePoint(BtfTracePoint),
     FEntry(FEntry),
     FExit(FExit),
+    Extension(Extension),
 }
 
 impl Program {
@@ -242,6 +257,7 @@ impl Program {
             Program::BtfTracePoint(_) => BPF_PROG_TYPE_TRACING,
             Program::FEntry(_) => BPF_PROG_TYPE_TRACING,
             Program::FExit(_) => BPF_PROG_TYPE_TRACING,
+            Program::Extension(_) => BPF_PROG_TYPE_EXT,
         }
     }
 
@@ -269,6 +285,7 @@ impl Program {
             Program::BtfTracePoint(p) => &p.data,
             Program::FEntry(p) => &p.data,
             Program::FExit(p) => &p.data,
+            Program::Extension(p) => &p.data,
         }
     }
 
@@ -291,18 +308,22 @@ impl Program {
             Program::BtfTracePoint(p) => &mut p.data,
             Program::FEntry(p) => &mut p.data,
             Program::FExit(p) => &mut p.data,
+            Program::Extension(p) => &mut p.data,
         }
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub(crate) struct ProgramData {
+    pub(crate) name: Option<String>,
     pub(crate) obj: obj::Program,
     pub(crate) fd: Option<RawFd>,
     pub(crate) links: Vec<Rc<RefCell<dyn Link>>>,
     pub(crate) expected_attach_type: Option<bpf_attach_type>,
     pub(crate) attach_btf_obj_fd: Option<u32>,
     pub(crate) attach_btf_id: Option<u32>,
+    pub(crate) attach_prog_fd: Option<RawFd>,
+    pub(crate) btf_fd: Option<RawFd>,
 }
 
 impl ProgramData {
@@ -334,67 +355,21 @@ impl ProgramData {
     }
 }
 
-const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
-const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
-
-pub(crate) struct VerifierLog {
-    buf: Vec<u8>,
-}
-
-impl VerifierLog {
-    fn new() -> VerifierLog {
-        VerifierLog { buf: Vec::new() }
-    }
-
-    pub(crate) fn buf(&mut self) -> &mut Vec<u8> {
-        &mut self.buf
-    }
-
-    fn grow(&mut self) {
-        let len = cmp::max(
-            MIN_LOG_BUF_SIZE,
-            cmp::min(MAX_LOG_BUF_SIZE, self.buf.capacity() * 10),
-        );
-        self.buf.resize(len, 0);
-        self.reset();
-    }
-
-    fn reset(&mut self) {
-        if !self.buf.is_empty() {
-            self.buf[0] = 0;
-        }
-    }
-
-    fn truncate(&mut self) {
-        if self.buf.is_empty() {
-            return;
-        }
-
-        let pos = self
-            .buf
-            .iter()
-            .position(|b| *b == 0)
-            .unwrap_or(self.buf.len() - 1);
-        self.buf[pos] = 0;
-        self.buf.truncate(pos + 1);
-    }
-
-    pub fn as_c_str(&self) -> Option<&CStr> {
-        if self.buf.is_empty() {
-            None
-        } else {
-            Some(CStr::from_bytes_with_nul(&self.buf).unwrap())
-        }
-    }
-}
-
 fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(), ProgramError> {
     let ProgramData { obj, fd, .. } = data;
     if fd.is_some() {
         return Err(ProgramError::AlreadyLoaded);
     }
     let crate::obj::Program {
-        function: Function { instructions, .. },
+        function:
+            Function {
+                instructions,
+                func_info,
+                line_info,
+                func_info_rec_size,
+                line_info_rec_size,
+                ..
+            },
         license,
         kernel_version,
         ..
@@ -411,16 +386,37 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(),
     let mut log_buf = VerifierLog::new();
     let mut retries = 0;
     let mut ret;
+
+    let prog_name = if let Some(name) = &data.name {
+        let name = name.clone();
+        let prog_name = CString::new(name.clone())
+            .map_err(|_| ProgramError::InvalidName { name: name.clone() })?;
+
+        if prog_name.to_bytes().len() > 16 {
+            return Err(ProgramError::NameTooLong { name });
+        }
+        Some(prog_name)
+    } else {
+        None
+    };
+
     loop {
         let attr = BpfLoadProgramAttrs {
+            name: prog_name.clone(),
             ty: prog_type,
             insns: instructions,
             license,
             kernel_version: target_kernel_version,
             expected_attach_type: data.expected_attach_type,
+            prog_btf_fd: data.btf_fd,
             attach_btf_obj_fd: data.attach_btf_obj_fd,
             attach_btf_id: data.attach_btf_id,
+            attach_prog_fd: data.attach_prog_fd,
             log: &mut log_buf,
+            func_info_rec_size: *func_info_rec_size,
+            func_info: func_info.clone(),
+            line_info_rec_size: *line_info_rec_size,
+            line_info: line_info.clone(),
         };
         ret = bpf_load_program(attr);
         match &ret {
@@ -491,7 +487,7 @@ pub(crate) fn query<T: AsRawFd>(
     }
 }
 
-/// Detach an attached program.
+/// Detach an attached program
 pub trait Link: std::fmt::Debug {
     fn detach(&mut self) -> Result<(), ProgramError>;
 }
@@ -599,6 +595,12 @@ macro_rules! impl_program_fd {
                     self.data.fd
                 }
             }
+
+            impl ProgramFd for &mut $struct_name {
+                fn fd(&self) -> Option<RawFd> {
+                    self.data.fd
+                }
+            }
         )+
     }
 }
@@ -620,6 +622,7 @@ impl_program_fd!(
     BtfTracePoint,
     FEntry,
     FExit,
+    Extension,
 );
 
 macro_rules! impl_try_from_program {
@@ -668,6 +671,7 @@ impl_try_from_program!(
     BtfTracePoint,
     FEntry,
     FExit,
+    Extension,
 );
 
 /// Provides information about a loaded program, like name, id and statistics

+ 6 - 4
aya/src/programs/xdp.rs

@@ -5,8 +5,10 @@ use thiserror::Error;
 
 use crate::{
     generated::{
-        bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP, XDP_FLAGS_DRV_MODE,
-        XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST,
+        bpf_attach_type::{self, BPF_XDP},
+        bpf_prog_type::BPF_PROG_TYPE_XDP,
+        XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE,
+        XDP_FLAGS_UPDATE_IF_NOEXIST,
     },
     programs::{load_program, FdLink, Link, LinkRef, ProgramData, ProgramError},
     sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd},
@@ -72,6 +74,7 @@ impl Xdp {
     ///
     /// See also [`Program::load`](crate::programs::Program::load).
     pub fn load(&mut self) -> Result<(), ProgramError> {
+        self.data.expected_attach_type = Some(bpf_attach_type::BPF_XDP);
         load_program(BPF_PROG_TYPE_XDP, &mut self.data)
     }
 
@@ -88,7 +91,6 @@ impl Xdp {
     /// kernels.
     pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
-
         let c_interface = CString::new(interface).unwrap();
         let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
         if if_index == 0 {
@@ -99,7 +101,7 @@ impl Xdp {
 
         let k_ver = kernel_version().unwrap();
         if k_ver >= (5, 9, 0) {
-            let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, flags.bits).map_err(
+            let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, None, flags.bits).map_err(
                 |(_, io_error)| ProgramError::SyscallError {
                     call: "bpf_link_create".to_owned(),
                     io_error,

+ 303 - 7
aya/src/sys/bpf.rs

@@ -1,20 +1,28 @@
+use crate::{
+    generated::{btf_func_linkage, btf_param, btf_var_secinfo, BTF_INT_SIGNED, BTF_VAR_STATIC},
+    obj::{btf::BtfType, copy_instructions},
+    Btf,
+};
+use libc::{c_char, c_long, close, ENOENT};
+
 use std::{
-    cmp,
-    ffi::CStr,
+    cmp::{self, min},
+    ffi::{CStr, CString},
     io,
     mem::{self, MaybeUninit},
     os::unix::io::RawFd,
     slice,
 };
 
-use libc::{c_long, ENOENT};
-
 use crate::{
     bpf_map_def,
-    generated::{bpf_attach_type, bpf_attr, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type},
+    generated::{
+        bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type,
+    },
     maps::PerCpuValues,
-    programs::VerifierLog,
+    obj::btf::{FuncSecInfo, LineSecInfo},
     sys::{kernel_version, SysResult},
+    util::VerifierLog,
     Pod, BPF_OBJ_NAME_LEN,
 };
 
@@ -60,20 +68,38 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult {
 }
 
 pub(crate) struct BpfLoadProgramAttrs<'a> {
+    pub(crate) name: Option<CString>,
     pub(crate) ty: bpf_prog_type,
     pub(crate) insns: &'a [bpf_insn],
     pub(crate) license: &'a CStr,
     pub(crate) kernel_version: u32,
     pub(crate) expected_attach_type: Option<bpf_attach_type>,
+    pub(crate) prog_btf_fd: Option<RawFd>,
     pub(crate) attach_btf_obj_fd: Option<u32>,
     pub(crate) attach_btf_id: Option<u32>,
+    pub(crate) attach_prog_fd: Option<RawFd>,
     pub(crate) log: &'a mut VerifierLog,
+    pub(crate) func_info_rec_size: usize,
+    pub(crate) func_info: FuncSecInfo,
+    pub(crate) line_info_rec_size: usize,
+    pub(crate) line_info: LineSecInfo,
 }
 
 pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
     let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
 
     let u = unsafe { &mut attr.__bindgen_anon_3 };
+
+    if let Some(prog_name) = aya_attr.name {
+        let mut name: [c_char; 16] = [0; 16];
+        let name_bytes = prog_name.to_bytes();
+        let len = min(name.len(), name_bytes.len());
+        name[..len].copy_from_slice(unsafe {
+            slice::from_raw_parts(name_bytes.as_ptr() as *const c_char, len)
+        });
+        u.prog_name = name;
+    }
+
     u.prog_type = aya_attr.ty as u32;
     if let Some(v) = aya_attr.expected_attach_type {
         u.expected_attach_type = v as u32;
@@ -82,6 +108,22 @@ pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
     u.insn_cnt = aya_attr.insns.len() as u32;
     u.license = aya_attr.license.as_ptr() as u64;
     u.kern_version = aya_attr.kernel_version;
+
+    if let Some(btf_fd) = aya_attr.prog_btf_fd {
+        let line_info_buf = aya_attr.line_info.line_info_bytes();
+        let func_info_buf = aya_attr.func_info.func_info_bytes();
+        u.prog_btf_fd = btf_fd as u32;
+        if aya_attr.line_info_rec_size > 0 {
+            u.line_info = line_info_buf.as_ptr() as *const _ as u64;
+            u.line_info_cnt = aya_attr.line_info.len() as u32;
+            u.line_info_rec_size = aya_attr.line_info_rec_size as u32;
+        }
+        if aya_attr.func_info_rec_size > 0 {
+            u.func_info = func_info_buf.as_ptr() as *const _ as u64;
+            u.func_info_cnt = aya_attr.func_info.len() as u32;
+            u.func_info_rec_size = aya_attr.func_info_rec_size as u32;
+        }
+    }
     let log_buf = aya_attr.log.buf();
     if log_buf.capacity() > 0 {
         u.log_level = 7;
@@ -91,6 +133,10 @@ pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
     if let Some(v) = aya_attr.attach_btf_obj_fd {
         u.__bindgen_anon_1.attach_btf_obj_fd = v;
     }
+    if let Some(v) = aya_attr.attach_prog_fd {
+        u.__bindgen_anon_1.attach_prog_fd = v as u32;
+    }
+
     if let Some(v) = aya_attr.attach_btf_id {
         u.attach_btf_id = v;
     }
@@ -266,6 +312,7 @@ pub(crate) fn bpf_link_create(
     prog_fd: RawFd,
     target_fd: RawFd,
     attach_type: bpf_attach_type,
+    btf_id: Option<u32>,
     flags: u32,
 ) -> SysResult {
     let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
@@ -274,6 +321,9 @@ pub(crate) fn bpf_link_create(
     attr.link_create.__bindgen_anon_1.target_fd = target_fd as u32;
     attr.link_create.attach_type = attach_type as u32;
     attr.link_create.flags = flags;
+    if let Some(btf_id) = btf_id {
+        attr.link_create.__bindgen_anon_2.target_btf_id = btf_id;
+    }
 
     sys_bpf(bpf_cmd::BPF_LINK_CREATE, &attr)
 }
@@ -359,6 +409,25 @@ pub(crate) fn bpf_obj_get_info_by_fd(prog_fd: RawFd) -> Result<bpf_prog_info, io
     }
 }
 
+pub(crate) fn btf_obj_get_info_by_fd(
+    prog_fd: RawFd,
+    buf: &mut [u8],
+) -> Result<bpf_btf_info, io::Error> {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let mut info = unsafe { mem::zeroed::<bpf_btf_info>() };
+    let buf_size = buf.len() as u32;
+    info.btf = buf.as_ptr() as u64;
+    info.btf_size = buf_size;
+    attr.info.bpf_fd = prog_fd as u32;
+    attr.info.info = &info as *const bpf_btf_info as u64;
+    attr.info.info_len = mem::size_of::<bpf_btf_info>() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &attr) {
+        Ok(_) => Ok(info),
+        Err((_, err)) => Err(err),
+    }
+}
+
 pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> SysResult {
     let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
 
@@ -371,6 +440,233 @@ pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> Sy
     sys_bpf(bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, &attr)
 }
 
-fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
+pub(crate) fn bpf_load_btf(raw_btf: &[u8], log: &mut VerifierLog) -> Result<RawFd, io::Error> {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = raw_btf.as_ptr() as *const _ as u64;
+    u.btf_size = mem::size_of_val(raw_btf) as u32;
+    let log_buf = log.buf();
+    if log_buf.capacity() > 0 {
+        u.btf_log_level = 1;
+        u.btf_log_buf = log_buf.as_mut_ptr() as u64;
+        u.btf_log_size = log_buf.capacity() as u32;
+    }
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => Ok(v as RawFd),
+        Err((_, err)) => Err(err),
+    }
+}
+
+pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<RawFd, io::Error> {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    attr.__bindgen_anon_6.__bindgen_anon_1.btf_id = id;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_GET_FD_BY_ID, &attr) {
+        Ok(v) => Ok(v as RawFd),
+        Err((_, err)) => Err(err),
+    }
+}
+
+pub(crate) fn is_prog_name_supported() -> bool {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_3 };
+    let mut name: [c_char; 16] = [0; 16];
+    let cstring = CString::new("aya_name_check").unwrap();
+    let name_bytes = cstring.to_bytes();
+    let len = min(name.len(), name_bytes.len());
+    name[..len].copy_from_slice(unsafe {
+        slice::from_raw_parts(name_bytes.as_ptr() as *const c_char, len)
+    });
+    u.prog_name = name;
+
+    let prog: &[u8] = &[
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
+    ];
+
+    let gpl = b"GPL\0";
+    u.license = gpl.as_ptr() as u64;
+
+    let insns = copy_instructions(prog).unwrap();
+    u.insn_cnt = insns.len() as u32;
+    u.insns = insns.as_ptr() as u64;
+    u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;
+
+    match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub(crate) fn is_btf_supported() -> bool {
+    let mut btf = Btf::new();
+    let name_offset = btf.add_string("int".to_string());
+    let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+    btf.add_type(int_type);
+    let btf_bytes = btf.to_bytes();
+
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = btf_bytes.as_ptr() as u64;
+    u.btf_size = btf_bytes.len() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub(crate) fn is_btf_func_supported() -> bool {
+    let mut btf = Btf::new();
+    let name_offset = btf.add_string("int".to_string());
+    let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+    let int_type_id = btf.add_type(int_type);
+
+    let a_name = btf.add_string("a".to_string());
+    let b_name = btf.add_string("b".to_string());
+    let params = vec![
+        btf_param {
+            name_off: a_name,
+            type_: int_type_id,
+        },
+        btf_param {
+            name_off: b_name,
+            type_: int_type_id,
+        },
+    ];
+    let func_proto = BtfType::new_func_proto(params, int_type_id);
+    let func_proto_type_id = btf.add_type(func_proto);
+
+    let add = btf.add_string("inc".to_string());
+    let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_STATIC);
+    btf.add_type(func);
+
+    let btf_bytes = btf.to_bytes();
+
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = btf_bytes.as_ptr() as u64;
+    u.btf_size = btf_bytes.len() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub(crate) fn is_btf_func_global_supported() -> bool {
+    let mut btf = Btf::new();
+    let name_offset = btf.add_string("int".to_string());
+    let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+    let int_type_id = btf.add_type(int_type);
+
+    let a_name = btf.add_string("a".to_string());
+    let b_name = btf.add_string("b".to_string());
+    let params = vec![
+        btf_param {
+            name_off: a_name,
+            type_: int_type_id,
+        },
+        btf_param {
+            name_off: b_name,
+            type_: int_type_id,
+        },
+    ];
+    let func_proto = BtfType::new_func_proto(params, int_type_id);
+    let func_proto_type_id = btf.add_type(func_proto);
+
+    let add = btf.add_string("inc".to_string());
+    let func = BtfType::new_func(add, func_proto_type_id, btf_func_linkage::BTF_FUNC_GLOBAL);
+    btf.add_type(func);
+
+    let btf_bytes = btf.to_bytes();
+
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = btf_bytes.as_ptr() as u64;
+    u.btf_size = btf_bytes.len() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub(crate) fn is_btf_datasec_supported() -> bool {
+    let mut btf = Btf::new();
+    let name_offset = btf.add_string("int".to_string());
+    let int_type = BtfType::new_int(name_offset, 4, BTF_INT_SIGNED, 0);
+    let int_type_id = btf.add_type(int_type);
+
+    let name_offset = btf.add_string("foo".to_string());
+    let var_type = BtfType::new_var(name_offset, int_type_id, BTF_VAR_STATIC);
+    let var_type_id = btf.add_type(var_type);
+
+    let name_offset = btf.add_string(".data".to_string());
+    let variables = vec![btf_var_secinfo {
+        type_: var_type_id,
+        offset: 0,
+        size: 4,
+    }];
+    let datasec_type = BtfType::new_datasec(name_offset, variables, 4);
+    btf.add_type(datasec_type);
+
+    let btf_bytes = btf.to_bytes();
+
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = btf_bytes.as_ptr() as u64;
+    u.btf_size = btf_bytes.len() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub(crate) fn is_btf_float_supported() -> bool {
+    let mut btf = Btf::new();
+    let name_offset = btf.add_string("float".to_string());
+    let float_type = BtfType::new_float(name_offset, 16);
+    btf.add_type(float_type);
+
+    let btf_bytes = btf.to_bytes();
+
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+    let u = unsafe { &mut attr.__bindgen_anon_7 };
+    u.btf = btf_bytes.as_ptr() as u64;
+    u.btf_size = btf_bytes.len() as u32;
+
+    match sys_bpf(bpf_cmd::BPF_BTF_LOAD, &attr) {
+        Ok(v) => {
+            let fd = v as RawFd;
+            unsafe { close(fd) };
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+pub fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
     syscall(Syscall::Bpf { cmd, attr })
 }

+ 56 - 1
aya/src/util.rs

@@ -1,7 +1,8 @@
 //! Utility functions.
 use std::{
+    cmp,
     collections::BTreeMap,
-    ffi::CString,
+    ffi::{CStr, CString},
     fs::{self, File},
     io::{self, BufReader},
     mem, slice,
@@ -155,6 +156,60 @@ pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
     slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size)
 }
 
+const MIN_LOG_BUF_SIZE: usize = 1024 * 10;
+const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
+
+pub(crate) struct VerifierLog {
+    buf: Vec<u8>,
+}
+
+impl VerifierLog {
+    pub(crate) fn new() -> VerifierLog {
+        VerifierLog { buf: Vec::new() }
+    }
+
+    pub(crate) fn buf(&mut self) -> &mut Vec<u8> {
+        &mut self.buf
+    }
+
+    pub(crate) fn grow(&mut self) {
+        let len = cmp::max(
+            MIN_LOG_BUF_SIZE,
+            cmp::min(MAX_LOG_BUF_SIZE, self.buf.capacity() * 10),
+        );
+        self.buf.resize(len, 0);
+        self.reset();
+    }
+
+    pub(crate) fn reset(&mut self) {
+        if !self.buf.is_empty() {
+            self.buf[0] = 0;
+        }
+    }
+
+    pub(crate) fn truncate(&mut self) {
+        if self.buf.is_empty() {
+            return;
+        }
+
+        let pos = self
+            .buf
+            .iter()
+            .position(|b| *b == 0)
+            .unwrap_or(self.buf.len() - 1);
+        self.buf[pos] = 0;
+        self.buf.truncate(pos + 1);
+    }
+
+    pub(crate) fn as_c_str(&self) -> Option<&CStr> {
+        if self.buf.is_empty() {
+            None
+        } else {
+            Some(CStr::from_bytes_with_nul(&self.buf).unwrap())
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;