Browse Source

Merge pull request #475 from yesh0/aya-obj

aya-obj: move code for object file loading and relocation into a separate crate
Alessandro Decina 2 years ago
parent
commit
897957a
40 changed files with 2133 additions and 1481 deletions
  1. 2 2
      Cargo.toml
  2. 27 0
      aya-obj/Cargo.toml
  3. 55 0
      aya-obj/README.md
  4. 0 0
      aya-obj/include/linux_wrapper.h
  5. 88 38
      aya-obj/src/btf/btf.rs
  6. 37 13
      aya-obj/src/btf/info.rs
  7. 12 0
      aya-obj/src/btf/mod.rs
  8. 115 84
      aya-obj/src/btf/relocation.rs
  9. 45 42
      aya-obj/src/btf/types.rs
  10. 5 5
      aya-obj/src/generated/btf_internal_bindings.rs
  11. 174 174
      aya-obj/src/generated/linux_bindings_aarch64.rs
  12. 174 174
      aya-obj/src/generated/linux_bindings_armv7.rs
  13. 174 174
      aya-obj/src/generated/linux_bindings_riscv64.rs
  14. 174 174
      aya-obj/src/generated/linux_bindings_x86_64.rs
  15. 2 0
      aya-obj/src/generated/mod.rs
  16. 85 0
      aya-obj/src/lib.rs
  17. 302 0
      aya-obj/src/maps.rs
  18. 161 187
      aya-obj/src/obj.rs
  19. 55 0
      aya-obj/src/programs/cgroup_sock.rs
  20. 79 0
      aya-obj/src/programs/cgroup_sock_addr.rs
  21. 39 0
      aya-obj/src/programs/cgroup_sockopt.rs
  22. 9 0
      aya-obj/src/programs/mod.rs
  23. 110 81
      aya-obj/src/relocation.rs
  24. 12 0
      aya-obj/src/util.rs
  25. 1 0
      aya-tool/src/bindgen.rs
  26. 1 0
      aya/Cargo.toml
  27. 44 99
      aya/src/bpf.rs
  28. 2 2
      aya/src/lib.rs
  29. 8 5
      aya/src/maps/bloom_filter.rs
  30. 8 5
      aya/src/maps/hash_map/hash_map.rs
  31. 8 5
      aya/src/maps/lpm_trie.rs
  32. 2 47
      aya/src/maps/mod.rs
  33. 0 9
      aya/src/obj/btf/mod.rs
  34. 1 50
      aya/src/programs/cgroup_sock.rs
  35. 1 74
      aya/src/programs/cgroup_sock_addr.rs
  36. 2 35
      aya/src/programs/cgroup_sockopt.rs
  37. 2 0
      test/integration-test/Cargo.toml
  38. 1 0
      test/integration-test/src/tests/mod.rs
  39. 114 0
      test/integration-test/src/tests/rbpf.rs
  40. 2 2
      xtask/src/codegen/aya.rs

+ 2 - 2
Cargo.toml

@@ -1,12 +1,12 @@
 [workspace]
 members = [
-    "aya", "aya-tool", "aya-log", "aya-log-common", "aya-log-parser", "test/integration-test", "test/integration-test-macros", "xtask",
+    "aya", "aya-obj", "aya-tool", "aya-log", "aya-log-common", "aya-log-parser", "test/integration-test", "test/integration-test-macros", "xtask",
     # macros
     "aya-bpf-macros", "aya-log-ebpf-macros",
     # ebpf crates
     "bpf/aya-bpf", "bpf/aya-bpf-bindings", "bpf/aya-log-ebpf", "test/integration-ebpf"
 ]
-default-members = ["aya", "aya-tool", "aya-log", "aya-bpf-macros", "aya-log-ebpf-macros"]
+default-members = ["aya", "aya-obj", "aya-tool", "aya-log", "aya-bpf-macros", "aya-log-ebpf-macros"]
 
 [profile.dev]
 panic = "abort"

+ 27 - 0
aya-obj/Cargo.toml

@@ -0,0 +1,27 @@
+[package]
+name = "aya-obj"
+version = "0.1.0"
+description = "An eBPF object file parsing library with BTF and relocation support."
+keywords = ["ebpf", "bpf", "btf", "elf", "object"]
+license = "MIT OR Apache-2.0"
+authors = ["The Aya Contributors"]
+repository = "https://github.com/aya-rs/aya"
+readme = "README.md"
+documentation = "https://docs.rs/aya-obj"
+edition = "2021"
+
+[dependencies]
+bytes = "1"
+log = "0.4"
+object = { version = "0.30", default-features = false, features = ["read_core", "elf"] }
+hashbrown = { version = "0.13", optional = true }
+thiserror-std = { package = "thiserror", version = "1" }
+thiserror-core = { version = "1", default-features = false, features = [], optional = true }
+
+[dev-dependencies]
+matches = "0.1.8"
+rbpf = "0.1.0"
+
+[features]
+default = []
+no_std = ["hashbrown", "thiserror-core"]

+ 55 - 0
aya-obj/README.md

@@ -0,0 +1,55 @@
+# aya-obj
+
+## Status
+
+This crate includes code that started as internal API used by
+the [aya] crate. It has been split out so that it can be used by
+other projects that deal with eBPF object files. Unless you're writing
+low level eBPF plumbing tools, you should not need to use this crate
+but see the [aya] crate instead.
+
+The API as it is today has a few rough edges and is generally not as
+polished nor stable as the main [aya] crate API. As always,
+improvements welcome!
+
+[aya]: https://github.com/aya-rs/aya
+
+## Overview
+
+eBPF programs written with [libbpf] or [aya-bpf] are usually compiled
+into an ELF object file, using various sections to store information
+about the eBPF programs.
+
+`aya-obj` is a library for parsing such eBPF object files, with BTF and
+relocation support.
+
+[libbpf]: https://github.com/libbpf/libbpf
+[aya-bpf]: https://github.com/aya-rs/aya
+
+## Example
+
+This example loads a simple eBPF program and runs it with [rbpf].
+
+```rust
+use aya_obj::{generated::bpf_insn, Object};
+
+// Parse the object file
+let bytes = std::fs::read("program.o").unwrap();
+let mut object = Object::parse(&bytes).unwrap();
+// Relocate the programs
+object.relocate_calls().unwrap();
+object.relocate_maps(std::iter::empty()).unwrap();
+
+// Run with rbpf
+let instructions = &object.programs["prog_name"].function.instructions;
+let data = unsafe {
+    core::slice::from_raw_parts(
+        instructions.as_ptr() as *const u8,
+        instructions.len() * core::mem::size_of::<bpf_insn>(),
+    )
+};
+let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
+let _return = vm.execute_program().unwrap();
+```
+
+[rbpf]: https://github.com/qmonnet/rbpf

+ 0 - 0
aya/include/linux_wrapper.h → aya-obj/include/linux_wrapper.h


+ 88 - 38
aya/src/obj/btf/btf.rs → aya-obj/src/btf/btf.rs

@@ -1,29 +1,29 @@
-use std::{
+use core::{ffi::CStr, mem, ptr};
+
+use alloc::{
     borrow::Cow,
-    collections::HashMap,
-    convert::TryInto,
-    ffi::{CStr, CString},
-    fs, io, mem,
-    path::{Path, PathBuf},
-    ptr,
+    ffi::CString,
+    format,
+    string::{String, ToString},
+    vec,
+    vec::Vec,
 };
-
 use bytes::BufMut;
 
 use log::debug;
 use object::Endianness;
-use thiserror::Error;
 
 use crate::{
-    generated::{btf_ext_header, btf_header},
-    obj::btf::{
+    btf::{
         info::{FuncSecInfo, LineSecInfo},
         relocation::Relocation,
         Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int,
         IntEncoding, LineInfo, Struct, Typedef, VarLinkage,
     },
-    util::bytes_of,
-    Features,
+    generated::{btf_ext_header, btf_header},
+    thiserror::{self, Error},
+    util::{bytes_of, HashMap},
+    Object,
 };
 
 pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
@@ -32,14 +32,15 @@ pub(crate) const MAX_SPEC_LEN: usize = 64;
 /// The error type returned when `BTF` operations fail.
 #[derive(Error, Debug)]
 pub enum BtfError {
+    #[cfg(not(feature = "no_std"))]
     /// Error parsing file
     #[error("error parsing {path}")]
     FileError {
         /// file path
-        path: PathBuf,
+        path: std::path::PathBuf,
         /// source of the error
         #[source]
-        error: io::Error,
+        error: std::io::Error,
     },
 
     /// Error parsing BTF header
@@ -125,12 +126,13 @@ pub enum BtfError {
         type_id: u32,
     },
 
+    #[cfg(not(feature = "no_std"))]
     /// 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.
+        /// The [`std::io::Error`] returned by the `BPF_BTF_LOAD` syscall.
         #[source]
-        io_error: io::Error,
+        io_error: std::io::Error,
         /// The error log produced by the kernel verifier.
         verifier_log: String,
     },
@@ -158,14 +160,26 @@ pub enum BtfError {
     InvalidSymbolName,
 }
 
+/// Available BTF features
+#[derive(Default, Debug)]
+#[allow(missing_docs)]
+pub struct BtfFeatures {
+    pub btf_func: bool,
+    pub btf_func_global: bool,
+    pub btf_datasec: bool,
+    pub btf_float: bool,
+    pub btf_decl_tag: bool,
+    pub btf_type_tag: bool,
+}
+
 /// Bpf Type Format metadata.
 ///
 /// BTF is a kind of debug metadata that allows eBPF programs compiled against one kernel version
 /// to be loaded into different kernel versions.
 ///
-/// Aya automatically loads BTF metadata if you use [`Bpf::load_file`](crate::Bpf::load_file). You
+/// Aya automatically loads BTF metadata if you use `Bpf::load_file`. You
 /// only need to explicitly use this type if you want to load BTF from a non-standard
-/// location or if you are using [`Bpf::load`](crate::Bpf::load).
+/// location or if you are using `Bpf::load`.
 #[derive(Clone, Debug)]
 pub struct Btf {
     header: btf_header,
@@ -175,7 +189,8 @@ pub struct Btf {
 }
 
 impl Btf {
-    pub(crate) fn new() -> Btf {
+    /// Creates a new empty instance with its header initialized
+    pub fn new() -> Btf {
         Btf {
             header: btf_header {
                 magic: 0xeb9f,
@@ -197,7 +212,8 @@ impl Btf {
         self.types.types.iter()
     }
 
-    pub(crate) fn add_string(&mut self, name: String) -> u32 {
+    /// Adds a string to BTF metadata, returning an offset
+    pub fn add_string(&mut self, name: String) -> u32 {
         let str = CString::new(name).unwrap();
         let name_offset = self.strings.len();
         self.strings.extend(str.as_c_str().to_bytes_with_nul());
@@ -205,7 +221,8 @@ impl Btf {
         name_offset as u32
     }
 
-    pub(crate) fn add_type(&mut self, btf_type: BtfType) -> u32 {
+    /// Adds a type to BTF metadata, returning a type id
+    pub fn add_type(&mut self, btf_type: BtfType) -> u32 {
         let size = btf_type.type_info_size() as u32;
         let type_id = self.types.len();
         self.types.push(btf_type);
@@ -215,12 +232,18 @@ impl Btf {
     }
 
     /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
+    #[cfg(not(feature = "no_std"))]
     pub fn from_sys_fs() -> Result<Btf, BtfError> {
         Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
     }
 
     /// Loads BTF metadata from the given `path`.
-    pub fn parse_file<P: AsRef<Path>>(path: P, endianness: Endianness) -> Result<Btf, BtfError> {
+    #[cfg(not(feature = "no_std"))]
+    pub fn parse_file<P: AsRef<std::path::Path>>(
+        path: P,
+        endianness: Endianness,
+    ) -> Result<Btf, BtfError> {
+        use std::{borrow::ToOwned, fs};
         let path = path.as_ref();
         Btf::parse(
             &fs::read(path).map_err(|error| BtfError::FileError {
@@ -231,7 +254,8 @@ impl Btf {
         )
     }
 
-    pub(crate) fn parse(data: &[u8], endianness: Endianness) -> Result<Btf, BtfError> {
+    /// Parses BTF from binary data of the given endianness
+    pub fn parse(data: &[u8], endianness: Endianness) -> Result<Btf, BtfError> {
         if data.len() < mem::size_of::<btf_header>() {
             return Err(BtfError::InvalidHeader);
         }
@@ -324,7 +348,8 @@ impl Btf {
         self.string_at(ty.name_offset()).ok().map(String::from)
     }
 
-    pub(crate) fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result<u32, BtfError> {
+    /// Returns a type id matching the type name and [BtfKind]
+    pub fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result<u32, BtfError> {
         for (type_id, ty) in self.types().enumerate() {
             if ty.kind() != kind {
                 continue;
@@ -370,7 +395,8 @@ impl Btf {
         })
     }
 
-    pub(crate) fn to_bytes(&self) -> Vec<u8> {
+    /// Encodes the metadata as BTF format
+    pub fn to_bytes(&self) -> Vec<u8> {
         // Safety: btf_header is POD
         let mut buf = unsafe { bytes_of::<btf_header>(&self.header).to_vec() };
         // Skip the first type since it's always BtfType::Unknown for type_by_id to work
@@ -383,7 +409,7 @@ impl Btf {
         &mut self,
         section_sizes: &HashMap<String, u64>,
         symbol_offsets: &HashMap<String, u64>,
-        features: &Features,
+        features: &BtfFeatures,
     ) -> Result<(), BtfError> {
         let mut types = mem::take(&mut self.types);
         for i in 0..types.types.len() {
@@ -560,11 +586,34 @@ impl Default for Btf {
     }
 }
 
+impl Object {
+    /// Fixes up and sanitizes BTF data.
+    ///
+    /// Mostly, it removes unsupported types and works around LLVM behaviours.
+    pub fn fixup_and_sanitize_btf(
+        &mut self,
+        features: &BtfFeatures,
+    ) -> Result<Option<&Btf>, BtfError> {
+        if let Some(ref mut obj_btf) = self.btf {
+            // fixup btf
+            obj_btf.fixup_and_sanitize(
+                &self.section_sizes,
+                &self.symbol_offset_by_name,
+                features,
+            )?;
+            Ok(Some(obj_btf))
+        } else {
+            Ok(None)
+        }
+    }
+}
+
 unsafe fn read_btf_header(data: &[u8]) -> btf_header {
     // safety: btf_header is POD so read_unaligned is safe
     ptr::read_unaligned(data.as_ptr() as *const btf_header)
 }
 
+/// Data in the `.BTF.ext` section
 #[derive(Debug, Clone)]
 pub struct BtfExt {
     data: Vec<u8>,
@@ -863,7 +912,7 @@ pub(crate) struct SecInfo<'a> {
 
 #[cfg(test)]
 mod tests {
-    use crate::obj::btf::{
+    use crate::btf::{
         BtfParam, DataSec, DataSecEntry, DeclTag, Float, Func, FuncProto, Ptr, TypeTag, Var,
     };
 
@@ -996,7 +1045,7 @@ mod tests {
         let name_offset = btf.add_string("&mut int".to_string());
         let ptr_type_id = btf.add_type(BtfType::Ptr(Ptr::new(name_offset, int_type_id)));
 
-        let features = Features {
+        let features = BtfFeatures {
             ..Default::default()
         };
 
@@ -1034,7 +1083,7 @@ mod tests {
             VarLinkage::Static,
         )));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_datasec: false,
             ..Default::default()
         };
@@ -1078,7 +1127,7 @@ mod tests {
         let datasec_type_id =
             btf.add_type(BtfType::DataSec(DataSec::new(name_offset, variables, 0)));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_datasec: false,
             ..Default::default()
         };
@@ -1125,7 +1174,7 @@ mod tests {
         let datasec_type_id =
             btf.add_type(BtfType::DataSec(DataSec::new(name_offset, variables, 0)));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_datasec: true,
             ..Default::default()
         };
@@ -1186,7 +1235,7 @@ mod tests {
             FuncLinkage::Static,
         )));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_func: false,
             ..Default::default()
         };
@@ -1235,7 +1284,7 @@ mod tests {
         let func_proto = BtfType::FuncProto(FuncProto::new(params, int_type_id));
         let func_proto_type_id = btf.add_type(func_proto);
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_func: true,
             ..Default::default()
         };
@@ -1284,7 +1333,7 @@ mod tests {
             FuncLinkage::Global,
         )));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_func: true,
             btf_func_global: false,
             ..Default::default()
@@ -1309,7 +1358,7 @@ mod tests {
         let name_offset = btf.add_string("float".to_string());
         let float_type_id = btf.add_type(BtfType::Float(Float::new(name_offset, 16)));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_float: false,
             ..Default::default()
         };
@@ -1349,7 +1398,7 @@ mod tests {
         let decl_tag_type_id =
             btf.add_type(BtfType::DeclTag(DeclTag::new(name_offset, var_type_id, -1)));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_decl_tag: false,
             ..Default::default()
         };
@@ -1377,7 +1426,7 @@ mod tests {
         let type_tag_type = btf.add_type(BtfType::TypeTag(TypeTag::new(name_offset, int_type_id)));
         btf.add_type(BtfType::Ptr(Ptr::new(0, type_tag_type)));
 
-        let features = Features {
+        let features = BtfFeatures {
             btf_type_tag: false,
             ..Default::default()
         };
@@ -1395,6 +1444,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg(not(feature = "no_std"))]
     #[cfg_attr(miri, ignore)]
     fn test_read_btf_from_sys_fs() {
         let btf = Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()).unwrap();

+ 37 - 13
aya/src/obj/btf/info.rs → aya-obj/src/btf/info.rs

@@ -1,12 +1,11 @@
-use std::collections::HashMap;
-
+use alloc::{string::String, vec, vec::Vec};
 use bytes::BufMut;
 use object::Endianness;
 
 use crate::{
     generated::{bpf_func_info, bpf_line_info},
-    obj::relocation::INS_SIZE,
-    util::bytes_of,
+    relocation::INS_SIZE,
+    util::{bytes_of, HashMap},
 };
 
 /* The func_info subsection layout:
@@ -19,10 +18,18 @@ use crate::{
  *   a list of bpf_func_info records for section #2
  *   ......
  */
+
+/// A collection of [bpf_func_info] collected from the `btf_ext_info_sec` struct
+/// inside the [FuncInfo] subsection.
+///
+/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
+/// for more information.
 #[derive(Debug, Clone, Default)]
-pub(crate) struct FuncSecInfo {
-    pub _sec_name_offset: u32,
+pub struct FuncSecInfo {
+    pub(crate) _sec_name_offset: u32,
+    /// The number of info entries
     pub num_info: u32,
+    /// Info entries
     pub func_info: Vec<bpf_func_info>,
 }
 
@@ -64,7 +71,8 @@ impl FuncSecInfo {
         }
     }
 
-    pub(crate) fn func_info_bytes(&self) -> Vec<u8> {
+    /// Encodes the [bpf_func_info] entries.
+    pub fn func_info_bytes(&self) -> Vec<u8> {
         let mut buf = vec![];
         for l in &self.func_info {
             // Safety: bpf_func_info is POD
@@ -73,13 +81,20 @@ impl FuncSecInfo {
         buf
     }
 
-    pub(crate) fn len(&self) -> usize {
+    /// Returns the number of [bpf_func_info] entries.
+    pub fn len(&self) -> usize {
         self.func_info.len()
     }
 }
 
+/// A collection of [FuncSecInfo] collected from the `func_info` subsection
+/// in the `.BTF.ext` section.
+///
+/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
+/// for more information.
 #[derive(Debug, Clone)]
-pub(crate) struct FuncInfo {
+pub struct FuncInfo {
+    /// The [FuncSecInfo] subsections for some sections, referenced by section names
     pub data: HashMap<String, FuncSecInfo>,
 }
 
@@ -98,12 +113,19 @@ impl FuncInfo {
     }
 }
 
+/// A collection of [bpf_line_info] collected from the `btf_ext_info_sec` struct
+/// inside the `line_info` subsection.
+///
+/// See [BPF Type Format (BTF) — The Linux Kernel documentation](https://docs.kernel.org/bpf/btf.html)
+/// for more information.
 #[derive(Debug, Clone, Default)]
-pub(crate) struct LineSecInfo {
+pub struct LineSecInfo {
     // each line info section has a header
-    pub _sec_name_offset: u32,
+    pub(crate) _sec_name_offset: u32,
+    /// The number of entries
     pub num_info: u32,
     // followed by one or more bpf_line_info structs
+    /// The [bpf_line_info] entries
     pub line_info: Vec<bpf_line_info>,
 }
 
@@ -154,7 +176,8 @@ impl LineSecInfo {
         }
     }
 
-    pub(crate) fn line_info_bytes(&self) -> Vec<u8> {
+    /// Encodes the entries.
+    pub fn line_info_bytes(&self) -> Vec<u8> {
         let mut buf = vec![];
         for l in &self.line_info {
             // Safety: bpf_func_info is POD
@@ -163,7 +186,8 @@ impl LineSecInfo {
         buf
     }
 
-    pub(crate) fn len(&self) -> usize {
+    /// Returns the number of entries.
+    pub fn len(&self) -> usize {
         self.line_info.len()
     }
 }

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

@@ -0,0 +1,12 @@
+//! BTF loading, parsing and relocation.
+
+#[allow(clippy::module_inception)]
+mod btf;
+mod info;
+mod relocation;
+mod types;
+
+pub use btf::*;
+pub use info::*;
+pub use relocation::BtfRelocationError;
+pub use types::*;

+ 115 - 84
aya/src/obj/btf/relocation.rs → aya-obj/src/btf/relocation.rs

@@ -1,74 +1,124 @@
-use std::{collections::HashMap, io, mem, ptr, str::FromStr};
-
-use thiserror::Error;
+use core::{mem, ptr, str::FromStr};
+
+use alloc::{
+    borrow::ToOwned,
+    format,
+    string::{String, ToString},
+    vec,
+    vec::Vec,
+};
 
 use crate::{
+    btf::{
+        fields_are_compatible, types_are_compatible, Array, Btf, BtfError, BtfMember, BtfType,
+        IntEncoding, Struct, Union, MAX_SPEC_LEN,
+    },
     generated::{
         bpf_core_relo, bpf_core_relo_kind::*, bpf_insn, BPF_ALU, BPF_ALU64, BPF_B, BPF_DW, BPF_H,
         BPF_K, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_W, BTF_INT_SIGNED,
     },
-    obj::{
-        btf::{
-            fields_are_compatible, types_are_compatible, Array, BtfMember, BtfType, IntEncoding,
-            Struct, Union, MAX_SPEC_LEN,
-        },
-        Btf, BtfError, Object, Program, ProgramSection,
-    },
-    BpfError,
+    thiserror::{self, Error},
+    util::HashMap,
+    Object, Program, ProgramSection,
 };
 
+/// The error type returned by [`Object::relocate_btf`].
+#[derive(Error, Debug)]
+#[error("error relocating `{section}`")]
+pub struct BtfRelocationError {
+    /// The function name
+    pub section: String,
+    #[source]
+    /// The original error
+    error: RelocationError,
+}
+
+/// Relocation failures
 #[derive(Error, Debug)]
 enum RelocationError {
+    #[cfg(not(feature = "no_std"))]
+    /// I/O error
     #[error(transparent)]
-    IOError(#[from] io::Error),
+    IOError(#[from] std::io::Error),
 
+    /// Program not found
     #[error("program not found")]
     ProgramNotFound,
 
+    /// Invalid relocation access string
     #[error("invalid relocation access string {access_str}")]
-    InvalidAccessString { access_str: String },
+    InvalidAccessString {
+        /// The access string
+        access_str: String,
+    },
 
+    /// Invalid instruction index referenced by relocation
     #[error("invalid instruction index #{index} referenced by relocation #{relocation_number}, the program contains {num_instructions} instructions")]
     InvalidInstructionIndex {
+        /// The invalid instruction index
         index: usize,
+        /// Number of instructions in the program
         num_instructions: usize,
+        /// The relocation number
         relocation_number: usize,
     },
 
+    /// Multiple candidate target types found with different memory layouts
     #[error("error relocating {type_name}, multiple candidate target types found with different memory layouts: {candidates:?}")]
     ConflictingCandidates {
+        /// The type name
         type_name: String,
+        /// The candidates
         candidates: Vec<String>,
     },
 
+    /// Maximum nesting level reached evaluating candidate type
     #[error("maximum nesting level reached evaluating candidate type `{}`", err_type_name(.type_name))]
-    MaximumNestingLevelReached { type_name: Option<String> },
+    MaximumNestingLevelReached {
+        /// The type name
+        type_name: Option<String>,
+    },
 
+    /// Invalid access string
     #[error("invalid access string `{spec}` for type `{}`: {error}", err_type_name(.type_name))]
     InvalidAccessIndex {
+        /// The type name
         type_name: Option<String>,
+        /// The access string
         spec: String,
+        /// The index
         index: usize,
+        /// The max index
         max_index: usize,
+        /// The error message
         error: String,
     },
 
+    /// Relocation not valid for type
     #[error(
         "relocation #{relocation_number} of kind `{relocation_kind}` not valid for type `{type_kind}`: {error}"
     )]
     InvalidRelocationKindForType {
+        /// The relocation number
         relocation_number: usize,
+        /// The relocation kind
         relocation_kind: String,
+        /// The type kind
         type_kind: String,
+        /// The error message
         error: String,
     },
 
+    /// Invalid instruction referenced by relocation
     #[error(
         "instruction #{index} referenced by relocation #{relocation_number} is invalid: {error}"
     )]
     InvalidInstruction {
+        /// The relocation number
         relocation_number: usize,
+        /// The instruction index
         index: usize,
+        /// The error message
         error: String,
     },
 
@@ -78,6 +128,10 @@ enum RelocationError {
         type_id: u32,
         ins_index: usize,
     },
+
+    /// BTF error
+    #[error("invalid BTF")]
+    BtfError(#[from] BtfError),
 }
 
 fn err_type_name(name: &Option<String>) -> String {
@@ -126,7 +180,7 @@ impl TryFrom<u32> for RelocationKind {
 }
 
 #[derive(Debug, Copy, Clone)]
-pub struct Relocation {
+pub(crate) struct Relocation {
     kind: RelocationKind,
     ins_offset: usize,
     type_id: u32,
@@ -154,7 +208,8 @@ impl Relocation {
 }
 
 impl Object {
-    pub fn relocate_btf(&mut self, target_btf: &Btf) -> Result<(), BpfError> {
+    /// Relocates programs inside this object file with loaded BTF info.
+    pub fn relocate_btf(&mut self, target_btf: &Btf) -> Result<(), BtfRelocationError> {
         let (local_btf, btf_ext) = match (&self.btf, &self.btf_ext) {
             (Some(btf), Some(btf_ext)) => (btf, btf_ext),
             _ => return Ok(()),
@@ -162,7 +217,13 @@ impl Object {
 
         let mut candidates_cache = HashMap::<u32, Vec<Candidate>>::new();
         for (sec_name_off, relos) in btf_ext.relocations() {
-            let section_name = local_btf.string_at(*sec_name_off)?;
+            let section_name =
+                local_btf
+                    .string_at(*sec_name_off)
+                    .map_err(|e| BtfRelocationError {
+                        section: format!("section@{sec_name_off}"),
+                        error: RelocationError::BtfError(e),
+                    })?;
 
             let program_section = match ProgramSection::from_str(&section_name) {
                 Ok(program) => program,
@@ -173,18 +234,17 @@ impl Object {
             let program = self
                 .programs
                 .get_mut(section_name)
-                .ok_or(BpfError::RelocationError {
-                    function: section_name.to_owned(),
-                    error: Box::new(RelocationError::ProgramNotFound),
+                .ok_or(BtfRelocationError {
+                    section: section_name.to_owned(),
+                    error: RelocationError::ProgramNotFound,
                 })?;
             match relocate_btf_program(program, relos, local_btf, target_btf, &mut candidates_cache)
             {
                 Ok(_) => {}
-                Err(ErrorWrapper::BtfError(e)) => return Err(e.into()),
-                Err(ErrorWrapper::RelocationError(error)) => {
-                    return Err(BpfError::RelocationError {
-                        function: section_name.to_owned(),
-                        error: Box::new(error),
+                Err(error) => {
+                    return Err(BtfRelocationError {
+                        section: section_name.to_owned(),
+                        error,
                     })
                 }
             }
@@ -200,17 +260,16 @@ fn relocate_btf_program<'target>(
     local_btf: &Btf,
     target_btf: &'target Btf,
     candidates_cache: &mut HashMap<u32, Vec<Candidate<'target>>>,
-) -> Result<(), ErrorWrapper> {
+) -> Result<(), RelocationError> {
     for rel in relos {
         let instructions = &mut program.function.instructions;
-        let ins_index = rel.ins_offset / std::mem::size_of::<bpf_insn>();
+        let ins_index = rel.ins_offset / mem::size_of::<bpf_insn>();
         if ins_index >= instructions.len() {
             return Err(RelocationError::InvalidInstructionIndex {
                 index: ins_index,
                 num_instructions: instructions.len(),
                 relocation_number: rel.number,
-            }
-            .into());
+            });
         }
 
         let local_ty = local_btf.type_by_id(rel.type_id)?;
@@ -266,8 +325,7 @@ fn relocate_btf_program<'target>(
                 return Err(RelocationError::ConflictingCandidates {
                     type_name: local_name.to_string(),
                     candidates: conflicts,
-                }
-                .into());
+                });
             }
             target_comp_rel
         } else {
@@ -317,7 +375,7 @@ fn find_candidates<'target>(
 fn match_candidate<'target>(
     local_spec: &AccessSpec,
     candidate: &'target Candidate,
-) -> Result<Option<AccessSpec<'target>>, ErrorWrapper> {
+) -> Result<Option<AccessSpec<'target>>, RelocationError> {
     let mut target_spec = AccessSpec {
         btf: candidate.btf,
         root_type_id: candidate.type_id,
@@ -419,8 +477,7 @@ fn match_candidate<'target>(
                     if target_spec.parts.len() == MAX_SPEC_LEN {
                         return Err(RelocationError::MaximumNestingLevelReached {
                             type_name: Some(candidate.name.clone()),
-                        }
-                        .into());
+                        });
                     }
 
                     target_spec.parts.push(accessor.index);
@@ -446,7 +503,7 @@ fn match_member<'target>(
     target_btf: &'target Btf,
     target_id: u32,
     target_spec: &mut AccessSpec<'target>,
-) -> Result<Option<u32>, ErrorWrapper> {
+) -> Result<Option<u32>, RelocationError> {
     let local_ty = local_btf.type_by_id(local_accessor.type_id)?;
     let local_member = match local_ty {
         // this won't panic, bounds are checked when local_spec is built in AccessSpec::new
@@ -470,8 +527,7 @@ fn match_member<'target>(
             let root_ty = target_spec.btf.type_by_id(target_spec.root_type_id)?;
             return Err(RelocationError::MaximumNestingLevelReached {
                 type_name: target_spec.btf.err_type_name(root_ty),
-            }
-            .into());
+            });
         }
 
         // this will not panic as we've already established these are fields types
@@ -532,7 +588,7 @@ impl<'a> AccessSpec<'a> {
         root_type_id: u32,
         spec: &str,
         relocation: Relocation,
-    ) -> Result<AccessSpec<'a>, ErrorWrapper> {
+    ) -> Result<AccessSpec<'a>, RelocationError> {
         let parts = spec
             .split(':')
             .map(|s| s.parse::<usize>())
@@ -552,8 +608,7 @@ impl<'a> AccessSpec<'a> {
                 if parts != [0] {
                     return Err(RelocationError::InvalidAccessString {
                         access_str: spec.to_string(),
-                    }
-                    .into());
+                    });
                 }
                 AccessSpec {
                     btf,
@@ -569,8 +624,7 @@ impl<'a> AccessSpec<'a> {
                     if parts.len() != 1 {
                         return Err(RelocationError::InvalidAccessString {
                             access_str: spec.to_string(),
-                        }
-                        .into());
+                        });
                     }
                     let index = parts[0];
                     if index >= en.variants.len() {
@@ -580,8 +634,7 @@ impl<'a> AccessSpec<'a> {
                             index,
                             max_index: en.variants.len(),
                             error: "tried to access nonexistant enum variant".to_string(),
-                        }
-                        .into());
+                        });
                     }
                     let accessors = vec![Accessor {
                         type_id,
@@ -607,8 +660,7 @@ impl<'a> AccessSpec<'a> {
                         relocation_kind: format!("{:?}", relocation.kind),
                         type_kind: format!("{:?}", ty.kind()),
                         error: "enum relocation on non-enum type".to_string(),
-                    }
-                    .into())
+                    })
                 }
             },
 
@@ -638,8 +690,7 @@ impl<'a> AccessSpec<'a> {
                                     index,
                                     max_index: members.len(),
                                     error: "out of bounds struct or union access".to_string(),
-                                }
-                                .into());
+                                });
                             }
 
                             let member = &members[index];
@@ -675,8 +726,7 @@ impl<'a> AccessSpec<'a> {
                                     index,
                                     max_index: array.len as usize,
                                     error: "array index out of bounds".to_string(),
-                                }
-                                .into());
+                                });
                             }
                             accessors.push(Accessor {
                                 type_id,
@@ -693,8 +743,7 @@ impl<'a> AccessSpec<'a> {
                                 type_kind: format!("{:?}", ty.kind()),
                                 error: "field relocation on a type that doesn't have fields"
                                     .to_string(),
-                            }
-                            .into());
+                            });
                         }
                     };
                 }
@@ -747,7 +796,7 @@ impl ComputedRelocation {
         rel: &Relocation,
         local_spec: &AccessSpec,
         target_spec: Option<&AccessSpec>,
-    ) -> Result<ComputedRelocation, ErrorWrapper> {
+    ) -> Result<ComputedRelocation, RelocationError> {
         use RelocationKind::*;
         let ret = match rel.kind {
             FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
@@ -774,10 +823,10 @@ impl ComputedRelocation {
         rel: &Relocation,
         local_btf: &Btf,
         target_btf: &Btf,
-    ) -> Result<(), ErrorWrapper> {
+    ) -> Result<(), RelocationError> {
         let instructions = &mut program.function.instructions;
         let num_instructions = instructions.len();
-        let ins_index = rel.ins_offset / std::mem::size_of::<bpf_insn>();
+        let ins_index = rel.ins_offset / mem::size_of::<bpf_insn>();
         let mut ins =
             instructions
                 .get_mut(ins_index)
@@ -799,20 +848,18 @@ impl ComputedRelocation {
                         relocation_number: rel.number,
                         index: ins_index,
                         error: format!("invalid src_reg={src_reg:x} expected {BPF_K:x}"),
-                    }
-                    .into());
+                    });
                 }
 
                 ins.imm = target_value as i32;
             }
             BPF_LDX | BPF_ST | BPF_STX => {
-                if target_value > std::i16::MAX as u32 {
+                if target_value > i16::MAX as u32 {
                     return Err(RelocationError::InvalidInstruction {
                         relocation_number: rel.number,
                         index: ins_index,
                         error: format!("value `{target_value}` overflows 16 bits offset field"),
-                    }
-                    .into());
+                    });
                 }
 
                 ins.off = target_value as i16;
@@ -837,8 +884,7 @@ impl ComputedRelocation {
                                     err_type_name(&target_btf.err_type_name(target_ty)),
                                     self.target.size,
                                 ),
-                            }
-                            .into())
+                            })
                         }
                     }
 
@@ -852,8 +898,7 @@ impl ComputedRelocation {
                                 relocation_number: rel.number,
                                 index: ins_index,
                                 error: format!("invalid target size {size}"),
-                            }
-                            .into())
+                            })
                         }
                     } as u8;
                     ins.code = ins.code & 0xE0 | size | ins.code & 0x07;
@@ -876,8 +921,7 @@ impl ComputedRelocation {
                     relocation_number: rel.number,
                     index: ins_index,
                     error: format!("invalid instruction class {class:x}"),
-                }
-                .into())
+                })
             }
         };
 
@@ -887,7 +931,7 @@ impl ComputedRelocation {
     fn compute_enum_relocation(
         rel: &Relocation,
         spec: Option<&AccessSpec>,
-    ) -> Result<ComputedRelocationValue, ErrorWrapper> {
+    ) -> Result<ComputedRelocationValue, RelocationError> {
         use RelocationKind::*;
         let value = match (rel.kind, spec) {
             (EnumVariantExists, spec) => spec.is_some() as u32,
@@ -918,7 +962,7 @@ impl ComputedRelocation {
     fn compute_field_relocation(
         rel: &Relocation,
         spec: Option<&AccessSpec>,
-    ) -> Result<ComputedRelocationValue, ErrorWrapper> {
+    ) -> Result<ComputedRelocationValue, RelocationError> {
         use RelocationKind::*;
 
         if let FieldExists = rel.kind {
@@ -963,8 +1007,7 @@ impl ComputedRelocation {
                         relocation_kind: format!("{rel_kind:?}"),
                         type_kind: format!("{:?}", ty.kind()),
                         error: "invalid relocation kind for array type".to_string(),
-                    }
-                    .into());
+                    });
                 }
             };
         }
@@ -979,8 +1022,7 @@ impl ComputedRelocation {
                     relocation_kind: format!("{:?}", rel.kind),
                     type_kind: format!("{:?}", ty.kind()),
                     error: "field relocation on a type that doesn't have fields".to_string(),
-                }
-                .into());
+                });
             }
         };
 
@@ -1055,7 +1097,7 @@ impl ComputedRelocation {
         rel: &Relocation,
         local_spec: &AccessSpec,
         target_spec: Option<&AccessSpec>,
-    ) -> Result<ComputedRelocationValue, ErrorWrapper> {
+    ) -> Result<ComputedRelocationValue, RelocationError> {
         use RelocationKind::*;
 
         let value = match (rel.kind, target_spec) {
@@ -1081,14 +1123,3 @@ impl ComputedRelocation {
         })
     }
 }
-
-// this exists only to simplify propagating errors from relocate_btf() and to associate
-// RelocationError(s) with their respective program name
-#[derive(Error, Debug)]
-enum ErrorWrapper {
-    #[error(transparent)]
-    BtfError(#[from] BtfError),
-
-    #[error(transparent)]
-    RelocationError(#[from] RelocationError),
-}

+ 45 - 42
aya/src/obj/btf/types.rs → aya-obj/src/btf/types.rs

@@ -1,11 +1,14 @@
-use std::{fmt::Display, mem, ptr};
+#![allow(missing_docs)]
 
+use core::{fmt::Display, mem, ptr};
+
+use alloc::{string::ToString, vec, vec::Vec};
 use object::Endianness;
 
-use crate::obj::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH};
+use crate::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH};
 
 #[derive(Clone, Debug)]
-pub(crate) enum BtfType {
+pub enum BtfType {
     Unknown,
     Fwd(Fwd),
     Const(Const),
@@ -29,7 +32,7 @@ pub(crate) enum BtfType {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Fwd {
+pub struct Fwd {
     pub(crate) name_offset: u32,
     info: u32,
     _unused: u32,
@@ -51,7 +54,7 @@ impl Fwd {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Const {
+pub struct Const {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -82,7 +85,7 @@ impl Const {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Volatile {
+pub struct Volatile {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -103,7 +106,7 @@ impl Volatile {
 }
 
 #[derive(Clone, Debug)]
-pub(crate) struct Restrict {
+pub struct Restrict {
     pub(crate) name_offset: u32,
     _info: u32,
     pub(crate) btf_type: u32,
@@ -125,7 +128,7 @@ impl Restrict {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Ptr {
+pub struct Ptr {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -144,7 +147,7 @@ impl Ptr {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, btf_type: u32) -> Self {
+    pub fn new(name_offset: u32, btf_type: u32) -> Self {
         let info = (BtfKind::Ptr as u32) << 24;
         Ptr {
             name_offset,
@@ -156,7 +159,7 @@ impl Ptr {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Typedef {
+pub struct Typedef {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -187,7 +190,7 @@ impl Typedef {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Float {
+pub struct Float {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -205,7 +208,7 @@ impl Float {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, size: u32) -> Self {
+    pub fn new(name_offset: u32, size: u32) -> Self {
         let info = (BtfKind::Float as u32) << 24;
         Float {
             name_offset,
@@ -217,7 +220,7 @@ impl Float {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Func {
+pub struct Func {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -225,7 +228,7 @@ pub(crate) struct Func {
 
 #[repr(u32)]
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum FuncLinkage {
+pub enum FuncLinkage {
     Static = 0,
     Global = 1,
     Extern = 2,
@@ -255,7 +258,7 @@ impl Func {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, proto: u32, linkage: FuncLinkage) -> Self {
+    pub fn new(name_offset: u32, proto: u32, linkage: FuncLinkage) -> Self {
         let mut info = (BtfKind::Func as u32) << 24;
         info |= (linkage as u32) & 0xFFFF;
         Func {
@@ -276,7 +279,7 @@ impl Func {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct TypeTag {
+pub struct TypeTag {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -295,7 +298,7 @@ impl TypeTag {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, btf_type: u32) -> Self {
+    pub fn new(name_offset: u32, btf_type: u32) -> Self {
         let info = (BtfKind::TypeTag as u32) << 24;
         TypeTag {
             name_offset,
@@ -307,7 +310,7 @@ impl TypeTag {
 
 #[repr(u32)]
 #[derive(Clone, Debug, Eq, PartialEq)]
-pub(crate) enum IntEncoding {
+pub enum IntEncoding {
     None,
     Signed = 1,
     Char = 2,
@@ -329,7 +332,7 @@ impl From<u32> for IntEncoding {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Int {
+pub struct Int {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -353,7 +356,7 @@ impl Int {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, size: u32, encoding: IntEncoding, offset: u32) -> Self {
+    pub fn new(name_offset: u32, size: u32, encoding: IntEncoding, offset: u32) -> Self {
         let info = (BtfKind::Int as u32) << 24;
         let mut data = 0u32;
         data |= (encoding as u32 & 0x0f) << 24;
@@ -391,7 +394,7 @@ pub(crate) struct BtfEnum {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Enum {
+pub struct Enum {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -441,7 +444,7 @@ pub(crate) struct BtfMember {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Struct {
+pub struct Struct {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -502,7 +505,7 @@ impl Struct {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Union {
+pub struct Union {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -559,7 +562,7 @@ pub(crate) struct BtfArray {
 }
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Array {
+pub struct Array {
     pub(crate) name_offset: u32,
     info: u32,
     _unused: u32,
@@ -602,14 +605,14 @@ impl Array {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct BtfParam {
-    pub(crate) name_offset: u32,
-    pub(crate) btf_type: u32,
+pub struct BtfParam {
+    pub name_offset: u32,
+    pub btf_type: u32,
 }
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct FuncProto {
+pub struct FuncProto {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) return_type: u32,
@@ -637,7 +640,7 @@ impl FuncProto {
         mem::size_of::<Fwd>() + mem::size_of::<BtfParam>() * self.params.len()
     }
 
-    pub(crate) fn new(params: Vec<BtfParam>, return_type: u32) -> Self {
+    pub fn new(params: Vec<BtfParam>, return_type: u32) -> Self {
         let mut info = (BtfKind::FuncProto as u32) << 24;
         info |= (params.len() as u32) & 0xFFFF;
         FuncProto {
@@ -651,7 +654,7 @@ impl FuncProto {
 
 #[repr(u32)]
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum VarLinkage {
+pub enum VarLinkage {
     Static,
     Global,
     Extern,
@@ -671,7 +674,7 @@ impl From<u32> for VarLinkage {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct Var {
+pub struct Var {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -696,7 +699,7 @@ impl Var {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, btf_type: u32, linkage: VarLinkage) -> Self {
+    pub fn new(name_offset: u32, btf_type: u32, linkage: VarLinkage) -> Self {
         let info = (BtfKind::Var as u32) << 24;
         Var {
             name_offset,
@@ -709,15 +712,15 @@ impl Var {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct DataSecEntry {
-    pub(crate) btf_type: u32,
-    pub(crate) offset: u32,
-    pub(crate) size: u32,
+pub struct DataSecEntry {
+    pub btf_type: u32,
+    pub offset: u32,
+    pub size: u32,
 }
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct DataSec {
+pub struct DataSec {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) size: u32,
@@ -746,7 +749,7 @@ impl DataSec {
         mem::size_of::<Fwd>() + mem::size_of::<DataSecEntry>() * self.entries.len()
     }
 
-    pub(crate) fn new(name_offset: u32, entries: Vec<DataSecEntry>, size: u32) -> Self {
+    pub fn new(name_offset: u32, entries: Vec<DataSecEntry>, size: u32) -> Self {
         let mut info = (BtfKind::DataSec as u32) << 24;
         info |= (entries.len() as u32) & 0xFFFF;
         DataSec {
@@ -760,7 +763,7 @@ impl DataSec {
 
 #[repr(C)]
 #[derive(Clone, Debug)]
-pub(crate) struct DeclTag {
+pub struct DeclTag {
     pub(crate) name_offset: u32,
     info: u32,
     pub(crate) btf_type: u32,
@@ -785,7 +788,7 @@ impl DeclTag {
         mem::size_of::<Self>()
     }
 
-    pub(crate) fn new(name_offset: u32, btf_type: u32, component_index: i32) -> Self {
+    pub fn new(name_offset: u32, btf_type: u32, component_index: i32) -> Self {
         let info = (BtfKind::DeclTag as u32) << 24;
         DeclTag {
             name_offset,
@@ -798,7 +801,7 @@ impl DeclTag {
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 #[repr(u32)]
-pub(crate) enum BtfKind {
+pub enum BtfKind {
     Unknown = 0,
     Int = 1,
     Ptr = 2,
@@ -851,7 +854,7 @@ impl TryFrom<u32> for BtfKind {
 }
 
 impl Display for BtfKind {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
             BtfKind::Unknown => write!(f, "[UNKNOWN]"),
             BtfKind::Int => write!(f, "[INT]"),

+ 5 - 5
aya/src/generated/btf_internal_bindings.rs → aya-obj/src/generated/btf_internal_bindings.rs

@@ -1,10 +1,10 @@
-/* automatically generated by rust-bindgen 0.60.1 */
+/* automatically generated by rust-bindgen 0.63.0 */
 
-pub type __u8 = ::std::os::raw::c_uchar;
-pub type __u16 = ::std::os::raw::c_ushort;
-pub type __u32 = ::std::os::raw::c_uint;
+pub type __u8 = ::core::ffi::c_uchar;
+pub type __u16 = ::core::ffi::c_ushort;
+pub type __u32 = ::core::ffi::c_uint;
 pub mod bpf_core_relo_kind {
-    pub type Type = ::std::os::raw::c_uint;
+    pub type Type = ::core::ffi::c_uint;
     pub const BPF_CORE_FIELD_BYTE_OFFSET: Type = 0;
     pub const BPF_CORE_FIELD_BYTE_SIZE: Type = 1;
     pub const BPF_CORE_FIELD_EXISTS: Type = 2;

File diff suppressed because it is too large
+ 174 - 174
aya-obj/src/generated/linux_bindings_aarch64.rs


File diff suppressed because it is too large
+ 174 - 174
aya-obj/src/generated/linux_bindings_armv7.rs


File diff suppressed because it is too large
+ 174 - 174
aya-obj/src/generated/linux_bindings_riscv64.rs


File diff suppressed because it is too large
+ 174 - 174
aya-obj/src/generated/linux_bindings_x86_64.rs


+ 2 - 0
aya/src/generated/mod.rs → aya-obj/src/generated/mod.rs

@@ -1,3 +1,5 @@
+//! eBPF bindings generated by rust-bindgen
+
 #![allow(
     dead_code,
     non_camel_case_types,

+ 85 - 0
aya-obj/src/lib.rs

@@ -0,0 +1,85 @@
+//! An eBPF object file parsing library with BTF and relocation support.
+//!
+//! # Status
+//!
+//! This crate includes code that started as internal API used by
+//! the [aya] crate. It has been split out so that it can be used by
+//! other projects that deal with eBPF object files. Unless you're writing
+//! low level eBPF plumbing tools, you should not need to use this crate
+//! but see the [aya] crate instead.
+//!
+//! The API as it is today has a few rough edges and is generally not as
+//! polished nor stable as the main [aya] crate API. As always,
+//! improvements welcome!
+//!
+//! [aya]: https://github.com/aya-rs/aya
+//!
+//! # Overview
+//!
+//! eBPF programs written with [libbpf] or [aya-bpf] are usually compiled
+//! into an ELF object file, using various sections to store information
+//! about the eBPF programs.
+//!
+//! `aya-obj` is a library for parsing such eBPF object files, with BTF and
+//! relocation support.
+//!
+//! [libbpf]: https://github.com/libbpf/libbpf
+//! [aya-bpf]: https://github.com/aya-rs/aya
+//!
+//! # Example
+//!
+//! This example loads a simple eBPF program and runs it with [rbpf].
+//!
+//! ```no_run
+//! use aya_obj::{generated::bpf_insn, Object};
+//!
+//! // Parse the object file
+//! let bytes = std::fs::read("program.o").unwrap();
+//! let mut object = Object::parse(&bytes).unwrap();
+//! // Relocate the programs
+//! object.relocate_calls().unwrap();
+//! object.relocate_maps(std::iter::empty()).unwrap();
+//!
+//! // Run with rbpf
+//! let instructions = &object.programs["prog_name"].function.instructions;
+//! let data = unsafe {
+//!     core::slice::from_raw_parts(
+//!         instructions.as_ptr() as *const u8,
+//!         instructions.len() * core::mem::size_of::<bpf_insn>(),
+//!     )
+//! };
+//! let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
+//! let _return = vm.execute_program().unwrap();
+//! ```
+//!
+//! [rbpf]: https://github.com/qmonnet/rbpf
+
+#![no_std]
+#![doc(
+    html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg",
+    html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg"
+)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![deny(clippy::all, missing_docs)]
+#![allow(clippy::missing_safety_doc, clippy::len_without_is_empty)]
+#![cfg_attr(feature = "no_std", feature(error_in_core))]
+
+#[cfg(feature = "no_std")]
+pub(crate) use thiserror_core as thiserror;
+#[cfg(not(feature = "no_std"))]
+pub(crate) use thiserror_std as thiserror;
+
+extern crate alloc;
+#[cfg(not(feature = "no_std"))]
+extern crate std;
+
+pub mod btf;
+pub mod generated;
+pub mod maps;
+pub mod obj;
+pub mod programs;
+pub mod relocation;
+mod util;
+
+pub use maps::Map;
+pub use obj::*;

+ 302 - 0
aya-obj/src/maps.rs

@@ -0,0 +1,302 @@
+//! Map struct and type bindings.
+
+use core::mem;
+
+use crate::thiserror::{self, Error};
+use alloc::vec::Vec;
+
+/// Invalid map type encontered
+pub struct InvalidMapTypeError {
+    /// The map type
+    pub map_type: u32,
+}
+
+impl TryFrom<u32> for crate::generated::bpf_map_type {
+    type Error = InvalidMapTypeError;
+
+    fn try_from(map_type: u32) -> Result<Self, Self::Error> {
+        use crate::generated::bpf_map_type::*;
+        Ok(match map_type {
+            x if x == BPF_MAP_TYPE_UNSPEC as u32 => BPF_MAP_TYPE_UNSPEC,
+            x if x == BPF_MAP_TYPE_HASH as u32 => BPF_MAP_TYPE_HASH,
+            x if x == BPF_MAP_TYPE_ARRAY as u32 => BPF_MAP_TYPE_ARRAY,
+            x if x == BPF_MAP_TYPE_PROG_ARRAY as u32 => BPF_MAP_TYPE_PROG_ARRAY,
+            x if x == BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 => BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+            x if x == BPF_MAP_TYPE_PERCPU_HASH as u32 => BPF_MAP_TYPE_PERCPU_HASH,
+            x if x == BPF_MAP_TYPE_PERCPU_ARRAY as u32 => BPF_MAP_TYPE_PERCPU_ARRAY,
+            x if x == BPF_MAP_TYPE_STACK_TRACE as u32 => BPF_MAP_TYPE_STACK_TRACE,
+            x if x == BPF_MAP_TYPE_CGROUP_ARRAY as u32 => BPF_MAP_TYPE_CGROUP_ARRAY,
+            x if x == BPF_MAP_TYPE_LRU_HASH as u32 => BPF_MAP_TYPE_LRU_HASH,
+            x if x == BPF_MAP_TYPE_LRU_PERCPU_HASH as u32 => BPF_MAP_TYPE_LRU_PERCPU_HASH,
+            x if x == BPF_MAP_TYPE_LPM_TRIE as u32 => BPF_MAP_TYPE_LPM_TRIE,
+            x if x == BPF_MAP_TYPE_BLOOM_FILTER as u32 => BPF_MAP_TYPE_BLOOM_FILTER,
+            x if x == BPF_MAP_TYPE_ARRAY_OF_MAPS as u32 => BPF_MAP_TYPE_ARRAY_OF_MAPS,
+            x if x == BPF_MAP_TYPE_HASH_OF_MAPS as u32 => BPF_MAP_TYPE_HASH_OF_MAPS,
+            x if x == BPF_MAP_TYPE_DEVMAP as u32 => BPF_MAP_TYPE_DEVMAP,
+            x if x == BPF_MAP_TYPE_SOCKMAP as u32 => BPF_MAP_TYPE_SOCKMAP,
+            x if x == BPF_MAP_TYPE_CPUMAP as u32 => BPF_MAP_TYPE_CPUMAP,
+            x if x == BPF_MAP_TYPE_XSKMAP as u32 => BPF_MAP_TYPE_XSKMAP,
+            x if x == BPF_MAP_TYPE_SOCKHASH as u32 => BPF_MAP_TYPE_SOCKHASH,
+            x if x == BPF_MAP_TYPE_CGROUP_STORAGE as u32 => BPF_MAP_TYPE_CGROUP_STORAGE,
+            x if x == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY as u32 => BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
+            x if x == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE as u32 => {
+                BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
+            }
+            x if x == BPF_MAP_TYPE_QUEUE as u32 => BPF_MAP_TYPE_QUEUE,
+            x if x == BPF_MAP_TYPE_STACK as u32 => BPF_MAP_TYPE_STACK,
+            x if x == BPF_MAP_TYPE_SK_STORAGE as u32 => BPF_MAP_TYPE_SK_STORAGE,
+            x if x == BPF_MAP_TYPE_DEVMAP_HASH as u32 => BPF_MAP_TYPE_DEVMAP_HASH,
+            x if x == BPF_MAP_TYPE_STRUCT_OPS as u32 => BPF_MAP_TYPE_STRUCT_OPS,
+            x if x == BPF_MAP_TYPE_RINGBUF as u32 => BPF_MAP_TYPE_RINGBUF,
+            x if x == BPF_MAP_TYPE_INODE_STORAGE as u32 => BPF_MAP_TYPE_INODE_STORAGE,
+            x if x == BPF_MAP_TYPE_TASK_STORAGE as u32 => BPF_MAP_TYPE_TASK_STORAGE,
+            _ => return Err(InvalidMapTypeError { map_type }),
+        })
+    }
+}
+
+/// BTF definition of a map
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub struct BtfMapDef {
+    pub(crate) map_type: u32,
+    pub(crate) key_size: u32,
+    pub(crate) value_size: u32,
+    pub(crate) max_entries: u32,
+    pub(crate) map_flags: u32,
+    pub(crate) pinning: PinningType,
+    /// BTF type id of the map key
+    pub btf_key_type_id: u32,
+    /// BTF type id of the map value
+    pub btf_value_type_id: u32,
+}
+
+/// The pinning type
+///
+/// Upon pinning a map, a file representation is created for the map,
+/// so that the map can be alive and retrievable across sessions.
+#[repr(u32)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum PinningType {
+    /// No pinning
+    None = 0,
+    /// Pin by the name
+    ByName = 1,
+}
+
+/// The error type returned when failing to parse a [PinningType]
+#[derive(Debug, Error)]
+pub enum PinningError {
+    /// Unsupported pinning type
+    #[error("unsupported pinning type `{pinning_type}`")]
+    Unsupported {
+        /// The unsupported pinning type
+        pinning_type: u32,
+    },
+}
+
+impl TryFrom<u32> for PinningType {
+    type Error = PinningError;
+
+    fn try_from(value: u32) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(PinningType::None),
+            1 => Ok(PinningType::ByName),
+            pinning_type => Err(PinningError::Unsupported { pinning_type }),
+        }
+    }
+}
+
+impl Default for PinningType {
+    fn default() -> Self {
+        PinningType::None
+    }
+}
+
+/// Map definition in legacy BPF map declaration style
+#[allow(non_camel_case_types)]
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub struct bpf_map_def {
+    // minimum features required by old BPF programs
+    /// The map type
+    pub map_type: u32,
+    /// The key_size
+    pub key_size: u32,
+    /// The value size
+    pub value_size: u32,
+    /// Max entry number
+    pub max_entries: u32,
+    /// Map flags
+    pub map_flags: u32,
+    // optional features
+    /// Id
+    pub id: u32,
+    /// Pinning type
+    pub pinning: PinningType,
+}
+
+/// The first five __u32 of `bpf_map_def` must be defined.
+pub(crate) const MINIMUM_MAP_SIZE: usize = mem::size_of::<u32>() * 5;
+
+/// Kinds of maps
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum MapKind {
+    /// A map holding `.bss` section data
+    Bss,
+    /// A map holding `.data` section data
+    Data,
+    /// A map holding `.rodata` section data
+    Rodata,
+    /// Other maps
+    Other,
+}
+
+impl From<&str> for MapKind {
+    fn from(s: &str) -> Self {
+        if s == ".bss" {
+            MapKind::Bss
+        } else if s.starts_with(".data") {
+            MapKind::Data
+        } else if s.starts_with(".rodata") {
+            MapKind::Rodata
+        } else {
+            MapKind::Other
+        }
+    }
+}
+
+/// Map data defined in `maps` or `.maps` sections
+#[derive(Debug, Clone)]
+pub enum Map {
+    /// A map defined in the `maps` section
+    Legacy(LegacyMap),
+    /// A map defined in the `.maps` section
+    Btf(BtfMap),
+}
+
+impl Map {
+    /// Returns the map type
+    pub fn map_type(&self) -> u32 {
+        match self {
+            Map::Legacy(m) => m.def.map_type,
+            Map::Btf(m) => m.def.map_type,
+        }
+    }
+
+    /// Returns the key size in bytes
+    pub fn key_size(&self) -> u32 {
+        match self {
+            Map::Legacy(m) => m.def.key_size,
+            Map::Btf(m) => m.def.key_size,
+        }
+    }
+
+    /// Returns the value size in bytes
+    pub fn value_size(&self) -> u32 {
+        match self {
+            Map::Legacy(m) => m.def.value_size,
+            Map::Btf(m) => m.def.value_size,
+        }
+    }
+
+    /// Returns the max entry number
+    pub fn max_entries(&self) -> u32 {
+        match self {
+            Map::Legacy(m) => m.def.max_entries,
+            Map::Btf(m) => m.def.max_entries,
+        }
+    }
+
+    /// Sets the max entry number
+    pub fn set_max_entries(&mut self, v: u32) {
+        match self {
+            Map::Legacy(m) => m.def.max_entries = v,
+            Map::Btf(m) => m.def.max_entries = v,
+        }
+    }
+
+    /// Returns the map flags
+    pub fn map_flags(&self) -> u32 {
+        match self {
+            Map::Legacy(m) => m.def.map_flags,
+            Map::Btf(m) => m.def.map_flags,
+        }
+    }
+
+    /// Returns the pinning type of the map
+    pub fn pinning(&self) -> PinningType {
+        match self {
+            Map::Legacy(m) => m.def.pinning,
+            Map::Btf(m) => m.def.pinning,
+        }
+    }
+
+    /// Returns the map data
+    pub fn data(&self) -> &[u8] {
+        match self {
+            Map::Legacy(m) => &m.data,
+            Map::Btf(m) => &m.data,
+        }
+    }
+
+    /// Returns the map data as mutable
+    pub fn data_mut(&mut self) -> &mut Vec<u8> {
+        match self {
+            Map::Legacy(m) => m.data.as_mut(),
+            Map::Btf(m) => m.data.as_mut(),
+        }
+    }
+
+    /// Returns the map kind
+    pub fn kind(&self) -> MapKind {
+        match self {
+            Map::Legacy(m) => m.kind,
+            Map::Btf(m) => m.kind,
+        }
+    }
+
+    /// Returns the section index
+    pub fn section_index(&self) -> usize {
+        match self {
+            Map::Legacy(m) => m.section_index,
+            Map::Btf(m) => m.section_index,
+        }
+    }
+
+    /// Returns the symbol index
+    pub fn symbol_index(&self) -> usize {
+        match self {
+            Map::Legacy(m) => m.symbol_index,
+            Map::Btf(m) => m.symbol_index,
+        }
+    }
+}
+
+/// A map declared with legacy BPF map declaration style, most likely from a `maps` section.
+///
+/// See [Drop support for legacy BPF map declaration syntax - Libbpf: the road to v1.0](https://github.com/libbpf/libbpf/wiki/Libbpf:-the-road-to-v1.0#drop-support-for-legacy-bpf-map-declaration-syntax)
+/// for more info.
+#[derive(Debug, Clone)]
+pub struct LegacyMap {
+    /// The definition of the map
+    pub def: bpf_map_def,
+    /// The section index
+    pub section_index: usize,
+    /// The symbol index
+    pub symbol_index: usize,
+    /// The map data
+    pub data: Vec<u8>,
+    /// The map kind
+    pub kind: MapKind,
+}
+
+/// A BTF-defined map, most likely from a `.maps` section.
+#[derive(Debug, Clone)]
+pub struct BtfMap {
+    /// The definition of the map
+    pub def: BtfMapDef,
+    pub(crate) section_index: usize,
+    pub(crate) symbol_index: usize,
+    pub(crate) kind: MapKind,
+    pub(crate) data: Vec<u8>,
+}

+ 161 - 187
aya/src/obj/mod.rs → aya-obj/src/obj.rs

@@ -1,47 +1,58 @@
-pub(crate) mod btf;
-mod relocation;
+//! Object file loading, parsing, and relocation.
 
+use alloc::{
+    borrow::ToOwned,
+    ffi::CString,
+    string::{String, ToString},
+    vec::Vec,
+};
+use core::{ffi::CStr, mem, ptr, str::FromStr};
 use log::debug;
 use object::{
     read::{Object as ElfObject, ObjectSection, Section as ObjSection},
     Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind,
     SymbolKind,
 };
-use std::{
-    collections::HashMap,
-    ffi::{CStr, CString},
-    mem, ptr,
-    str::FromStr,
-};
-use thiserror::Error;
 
-use relocation::*;
+use crate::{
+    maps::{BtfMap, LegacyMap, Map, MapKind, MINIMUM_MAP_SIZE},
+    relocation::*,
+    thiserror::{self, Error},
+    util::HashMap,
+};
 
 use crate::{
-    bpf_map_def,
+    btf::{Btf, BtfError, BtfExt, BtfType},
     generated::{bpf_insn, bpf_map_info, bpf_map_type::BPF_MAP_TYPE_ARRAY, BPF_F_RDONLY_PROG},
-    obj::btf::{Btf, BtfError, BtfExt, BtfType},
+    maps::{bpf_map_def, BtfMapDef, PinningType},
     programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType},
-    BpfError, BtfMapDef, PinningType,
 };
-use std::slice::from_raw_parts_mut;
+use core::slice::from_raw_parts_mut;
 
-use self::btf::{Array, DataSecEntry, FuncSecInfo, LineSecInfo};
+use crate::btf::{Array, DataSecEntry, 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;
 
+/// The loaded object file representation
 #[derive(Clone)]
 pub struct Object {
-    pub(crate) endianness: Endianness,
+    /// The endianness
+    pub endianness: Endianness,
+    /// Program license
     pub license: CString,
+    /// Kernel version
     pub kernel_version: KernelVersion,
+    /// Program BTF
     pub btf: Option<Btf>,
+    /// Program BTF.ext
     pub btf_ext: Option<BtfExt>,
-    pub(crate) maps: HashMap<String, Map>,
-    pub(crate) programs: HashMap<String, Program>,
-    pub(crate) functions: HashMap<u64, Function>,
+    /// Referenced maps
+    pub maps: HashMap<String, Map>,
+    /// A hash map of programs, using the program names parsed
+    /// in [ProgramSection]s as keys.
+    pub programs: HashMap<String, Program>,
+    /// Functions
+    pub 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>,
@@ -51,160 +62,107 @@ pub struct Object {
     pub(crate) text_section_index: Option<usize>,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub(crate) enum MapKind {
-    Bss,
-    Data,
-    Rodata,
-    Other,
-}
-
-impl From<&str> for MapKind {
-    fn from(s: &str) -> Self {
-        if s == ".bss" {
-            MapKind::Bss
-        } else if s.starts_with(".data") {
-            MapKind::Data
-        } else if s.starts_with(".rodata") {
-            MapKind::Rodata
-        } else {
-            MapKind::Other
-        }
-    }
-}
-
+/// An eBPF program
 #[derive(Debug, Clone)]
-pub enum Map {
-    Legacy(LegacyMap),
-    Btf(BtfMap),
-}
-
-impl Map {
-    pub(crate) fn map_type(&self) -> u32 {
-        match self {
-            Map::Legacy(m) => m.def.map_type,
-            Map::Btf(m) => m.def.map_type,
-        }
-    }
-
-    pub(crate) fn key_size(&self) -> u32 {
-        match self {
-            Map::Legacy(m) => m.def.key_size,
-            Map::Btf(m) => m.def.key_size,
-        }
-    }
-
-    pub(crate) fn value_size(&self) -> u32 {
-        match self {
-            Map::Legacy(m) => m.def.value_size,
-            Map::Btf(m) => m.def.value_size,
-        }
-    }
-
-    pub(crate) fn max_entries(&self) -> u32 {
-        match self {
-            Map::Legacy(m) => m.def.max_entries,
-            Map::Btf(m) => m.def.max_entries,
-        }
-    }
-
-    pub(crate) fn set_max_entries(&mut self, v: u32) {
-        match self {
-            Map::Legacy(m) => m.def.max_entries = v,
-            Map::Btf(m) => m.def.max_entries = v,
-        }
-    }
-
-    pub(crate) fn map_flags(&self) -> u32 {
-        match self {
-            Map::Legacy(m) => m.def.map_flags,
-            Map::Btf(m) => m.def.map_flags,
-        }
-    }
-
-    pub(crate) fn pinning(&self) -> PinningType {
-        match self {
-            Map::Legacy(m) => m.def.pinning,
-            Map::Btf(m) => m.def.pinning,
-        }
-    }
-
-    pub(crate) fn data(&self) -> &[u8] {
-        match self {
-            Map::Legacy(m) => &m.data,
-            Map::Btf(m) => &m.data,
-        }
-    }
-
-    pub(crate) fn data_mut(&mut self) -> &mut Vec<u8> {
-        match self {
-            Map::Legacy(m) => m.data.as_mut(),
-            Map::Btf(m) => m.data.as_mut(),
-        }
-    }
-
-    pub(crate) fn kind(&self) -> MapKind {
-        match self {
-            Map::Legacy(m) => m.kind,
-            Map::Btf(m) => m.kind,
-        }
-    }
-
-    pub(crate) fn section_index(&self) -> usize {
-        match self {
-            Map::Legacy(m) => m.section_index,
-            Map::Btf(m) => m.section_index,
-        }
-    }
-
-    pub(crate) fn symbol_index(&self) -> usize {
-        match self {
-            Map::Legacy(m) => m.symbol_index,
-            Map::Btf(m) => m.symbol_index,
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct LegacyMap {
-    pub(crate) def: bpf_map_def,
-    pub(crate) section_index: usize,
-    pub(crate) symbol_index: usize,
-    pub(crate) data: Vec<u8>,
-    pub(crate) kind: MapKind,
-}
-
-#[derive(Debug, Clone)]
-pub struct BtfMap {
-    pub(crate) def: BtfMapDef,
-    pub(crate) section_index: usize,
-    pub(crate) symbol_index: usize,
-    pub(crate) kind: MapKind,
-    pub(crate) data: Vec<u8>,
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct Program {
-    pub(crate) license: CString,
-    pub(crate) kernel_version: KernelVersion,
-    pub(crate) section: ProgramSection,
-    pub(crate) function: Function,
+pub struct Program {
+    /// The license
+    pub license: CString,
+    /// The kernel version
+    pub kernel_version: KernelVersion,
+    /// The section containing the program
+    pub section: ProgramSection,
+    /// The function
+    pub function: Function,
 }
 
+/// An eBPF function
 #[derive(Debug, Clone)]
-pub(crate) struct Function {
-    pub(crate) address: u64,
-    pub(crate) name: String,
-    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,
+pub struct Function {
+    /// The address
+    pub address: u64,
+    /// The function name
+    pub name: String,
+    /// The section index
+    pub section_index: SectionIndex,
+    /// The section offset
+    pub section_offset: usize,
+    /// The eBPF byte code instructions
+    pub instructions: Vec<bpf_insn>,
+    /// The function info
+    pub func_info: FuncSecInfo,
+    /// The line info
+    pub line_info: LineSecInfo,
+    /// Function info record size
+    pub func_info_rec_size: usize,
+    /// Line info record size
+    pub line_info_rec_size: usize,
 }
 
+/// Section types containing eBPF programs
+///
+/// # Section Name Parsing
+///
+/// Section types are parsed from the section name strings.
+///
+/// In order for Aya to treat a section as a [ProgramSection],
+/// there are a few requirements:
+/// - The section must be an executable code section.
+/// - The section name must conform to [Program Types and ELF Sections].
+///
+/// [Program Types and ELF Sections]: https://docs.kernel.org/bpf/libbpf/program_types.html
+///
+/// ## Program Name
+///
+/// Each section name is parsed into a section type and a program name.
+///
+/// Generally speaking,
+/// - if the section name does not contain any slashes,
+///   then the program name is just that section name;
+/// - if there are some slashes, the name is `section_name.rsplitn(2, '/')[0]`,
+/// - except for tracepoint programs, for which the name is
+///   `section_name.splitn(2, '/')[1]`.
+///
+/// ```rust
+/// use aya_obj::ProgramSection;
+/// use std::str::FromStr;
+///
+/// assert_eq!(
+///     ProgramSection::from_str("kprobe/do_unlinkat")
+///             .unwrap().name(),
+///     "do_unlinkat",
+/// );
+/// assert_eq!(
+///     ProgramSection::from_str("tracepoint/syscalls/sys_enter_openat")
+///             .unwrap().name(),
+///     "syscalls/sys_enter_openat",
+/// );
+/// ```
+///
+/// The program name will be used in [Object] as references to each program.
+///
+/// # Unsupported Sections
+///
+/// Currently, the following section names are not supported yet:
+/// - `flow_dissector`: `BPF_PROG_TYPE_FLOW_DISSECTOR`
+/// - `ksyscall+` or `kretsyscall+`
+/// - `uprobe.s+` or `uretprobe.s+`
+/// - `usdt+`
+/// - `kprobe.multi+` or `kretprobe.multi+`: `BPF_TRACE_KPROBE_MULTI`
+/// - `lsm_cgroup+` or `lsm.s+`
+/// - `lwt_in`, `lwt_out`, `lwt_seg6local`, `lwt_xmit`
+/// - `raw_tp.w+`, `raw_tracepoint.w+`
+/// - `action`
+/// - `sk_reuseport/migrate`, `sk_reuseport`
+/// - `syscall`
+/// - `struct_ops+`
+/// - `fmod_ret+`, `fmod_ret.s+`
+/// - `fentry.s+`, `fexit.s+`
+/// - `iter+`, `iter.s+`
+/// - `xdp.frags/cpumap`, `xdp/cpumap`
+/// - `xdp.frags/devmap`, `xdp/devmap`
+/// - `xdp.frags`
 #[derive(Debug, Clone)]
+#[allow(missing_docs)]
 pub enum ProgramSection {
     KRetProbe {
         name: String,
@@ -299,7 +257,8 @@ pub enum ProgramSection {
 }
 
 impl ProgramSection {
-    fn name(&self) -> &str {
+    /// Returns the program name
+    pub fn name(&self) -> &str {
         match self {
             ProgramSection::KRetProbe { name } => name,
             ProgramSection::KProbe { name } => name,
@@ -521,7 +480,8 @@ impl FromStr for ProgramSection {
 }
 
 impl Object {
-    pub(crate) fn parse(data: &[u8]) -> Result<Object, BpfError> {
+    /// Parses the binary data as an object file into an [Object]
+    pub fn parse(data: &[u8]) -> Result<Object, ParseError> {
         let obj = object::read::File::parse(data).map_err(ParseError::ElfError)?;
         let endianness = obj.endianness();
 
@@ -604,6 +564,7 @@ impl Object {
         }
     }
 
+    /// Patches map data
     pub fn patch_map_data(&mut self, globals: HashMap<&str, &[u8]>) -> Result<(), ParseError> {
         let symbols: HashMap<String, &Symbol> = self
             .symbols_by_index
@@ -829,9 +790,9 @@ impl Object {
         &mut self,
         section: &Section,
         symbols: HashMap<String, Symbol>,
-    ) -> Result<(), BpfError> {
+    ) -> Result<(), ParseError> {
         if self.btf.is_none() {
-            return Err(BpfError::NoBTF);
+            return Err(ParseError::NoBTF);
         }
         let btf = self.btf.as_ref().unwrap();
 
@@ -847,10 +808,8 @@ impl Object {
                         let (map_name, def) = parse_btf_map_def(btf, info)?;
                         let symbol_index = symbols
                             .get(&map_name)
-                            .ok_or_else(|| {
-                                BpfError::ParseError(ParseError::SymbolNotFound {
-                                    name: map_name.to_string(),
-                                })
+                            .ok_or_else(|| ParseError::SymbolNotFound {
+                                name: map_name.to_string(),
                             })?
                             .index;
                         self.maps.insert(
@@ -870,7 +829,7 @@ impl Object {
         Ok(())
     }
 
-    fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> {
+    fn parse_section(&mut self, mut section: Section) -> Result<(), ParseError> {
         let mut parts = section.name.rsplitn(2, '/').collect::<Vec<_>>();
         parts.reverse();
 
@@ -945,10 +904,16 @@ impl Object {
     }
 }
 
-#[derive(Debug, Clone, Error)]
+/// Errors caught during parsing the object file
+#[derive(Debug, Error)]
+#[allow(missing_docs)]
 pub enum ParseError {
     #[error("error parsing ELF data")]
-    ElfError(#[from] object::read::Error),
+    ElfError(object::read::Error),
+
+    /// Error parsing BTF object
+    #[error("BTF error")]
+    BtfError(#[from] BtfError),
 
     #[error("invalid license `{data:?}`: missing NULL terminator")]
     MissingLicenseNullTerminator { data: Vec<u8> },
@@ -962,8 +927,7 @@ pub enum ParseError {
     #[error("error parsing section with index {index}")]
     SectionError {
         index: usize,
-        #[source]
-        source: object::read::Error,
+        error: object::read::Error,
     },
 
     #[error("unsupported relocation target")]
@@ -1005,6 +969,10 @@ pub enum ParseError {
 
     #[error("no symbols found for the maps included in the maps section")]
     NoSymbolsInMapSection {},
+
+    /// No BTF parsed for object
+    #[error("no BTF parsed for object")]
+    NoBTF,
 }
 
 #[derive(Debug)]
@@ -1064,9 +1032,9 @@ impl<'data, 'file, 'a> TryFrom<&'a ObjSection<'data, 'file>> for Section<'a> {
 
     fn try_from(section: &'a ObjSection) -> Result<Section<'a>, ParseError> {
         let index = section.index();
-        let map_err = |source| ParseError::SectionError {
+        let map_err = |error| ParseError::SectionError {
             index: index.0,
-            source,
+            error,
         };
         let name = section.name().map_err(map_err)?;
         let kind = match BpfSectionKind::from_name(name) {
@@ -1166,9 +1134,12 @@ fn get_map_field(btf: &Btf, type_id: u32) -> Result<u32, BtfError> {
     Ok(arr.len)
 }
 
+/// The parsed kernel version
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum KernelVersion {
+    /// Specified version
     Version(u32),
+    /// Any version
     Any,
 }
 
@@ -1311,7 +1282,8 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe
     Ok((map_name.to_string(), map_def))
 }
 
-pub(crate) fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
+/// Parses a [bpf_map_info] into a [Map].
+pub fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
     if info.btf_key_type_id != 0 {
         Map::Btf(BtfMap {
             def: BtfMapDef {
@@ -1350,7 +1322,8 @@ pub(crate) fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
     }
 }
 
-pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
+/// Copies a block of eBPF instructions
+pub fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
     if data.len() % mem::size_of::<bpf_insn>() > 0 {
         return Err(ParseError::InvalidProgramCode);
     }
@@ -1363,11 +1336,12 @@ pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError
 
 #[cfg(test)]
 mod tests {
+    use alloc::vec;
     use matches::assert_matches;
     use object::Endianness;
 
     use super::*;
-    use crate::PinningType;
+    use crate::maps::PinningType;
 
     fn fake_section<'a>(kind: BpfSectionKind, name: &'a str, data: &'a [u8]) -> Section<'a> {
         Section {
@@ -1416,7 +1390,7 @@ mod tests {
     fn test_parse_generic_error() {
         assert!(matches!(
             Object::parse(&b"foo"[..]),
-            Err(BpfError::ParseError(ParseError::ElfError(_)))
+            Err(ParseError::ElfError(_))
         ))
     }
 

+ 55 - 0
aya-obj/src/programs/cgroup_sock.rs

@@ -0,0 +1,55 @@
+//! Cgroup socket programs.
+use alloc::{borrow::ToOwned, string::String};
+
+use crate::{
+    generated::bpf_attach_type,
+    thiserror::{self, Error},
+};
+
+/// Defines where to attach a `CgroupSock` program.
+#[derive(Copy, Clone, Debug)]
+pub enum CgroupSockAttachType {
+    /// Called after the IPv4 bind events.
+    PostBind4,
+    /// Called after the IPv6 bind events.
+    PostBind6,
+    /// Attach to IPv4 connect events.
+    SockCreate,
+    /// Attach to IPv6 connect events.
+    SockRelease,
+}
+
+impl Default for CgroupSockAttachType {
+    // The kernel checks for a 0 attach_type and sets it to sock_create
+    // We may as well do that here also
+    fn default() -> Self {
+        CgroupSockAttachType::SockCreate
+    }
+}
+
+impl From<CgroupSockAttachType> for bpf_attach_type {
+    fn from(s: CgroupSockAttachType) -> bpf_attach_type {
+        match s {
+            CgroupSockAttachType::PostBind4 => bpf_attach_type::BPF_CGROUP_INET4_POST_BIND,
+            CgroupSockAttachType::PostBind6 => bpf_attach_type::BPF_CGROUP_INET6_POST_BIND,
+            CgroupSockAttachType::SockCreate => bpf_attach_type::BPF_CGROUP_INET_SOCK_CREATE,
+            CgroupSockAttachType::SockRelease => bpf_attach_type::BPF_CGROUP_INET_SOCK_RELEASE,
+        }
+    }
+}
+
+#[derive(Debug, Error)]
+#[error("{0} is not a valid attach type for a CGROUP_SOCK program")]
+pub(crate) struct InvalidAttachType(String);
+
+impl CgroupSockAttachType {
+    pub(crate) fn try_from(value: &str) -> Result<CgroupSockAttachType, InvalidAttachType> {
+        match value {
+            "post_bind4" => Ok(CgroupSockAttachType::PostBind4),
+            "post_bind6" => Ok(CgroupSockAttachType::PostBind6),
+            "sock_create" => Ok(CgroupSockAttachType::SockCreate),
+            "sock_release" => Ok(CgroupSockAttachType::SockRelease),
+            _ => Err(InvalidAttachType(value.to_owned())),
+        }
+    }
+}

+ 79 - 0
aya-obj/src/programs/cgroup_sock_addr.rs

@@ -0,0 +1,79 @@
+//! Cgroup socket address programs.
+use alloc::{borrow::ToOwned, string::String};
+
+use crate::{
+    generated::bpf_attach_type,
+    thiserror::{self, Error},
+};
+
+/// Defines where to attach a `CgroupSockAddr` program.
+#[derive(Copy, Clone, Debug)]
+pub enum CgroupSockAddrAttachType {
+    /// Attach to IPv4 bind events.
+    Bind4,
+    /// Attach to IPv6 bind events.
+    Bind6,
+    /// Attach to IPv4 connect events.
+    Connect4,
+    /// Attach to IPv6 connect events.
+    Connect6,
+    /// Attach to IPv4 getpeername events.
+    GetPeerName4,
+    /// Attach to IPv6 getpeername events.
+    GetPeerName6,
+    /// Attach to IPv4 getsockname events.
+    GetSockName4,
+    /// Attach to IPv6 getsockname events.
+    GetSockName6,
+    /// Attach to IPv4 udp_sendmsg events.
+    UDPSendMsg4,
+    /// Attach to IPv6 udp_sendmsg events.
+    UDPSendMsg6,
+    /// Attach to IPv4 udp_recvmsg events.
+    UDPRecvMsg4,
+    /// Attach to IPv6 udp_recvmsg events.
+    UDPRecvMsg6,
+}
+
+impl From<CgroupSockAddrAttachType> for bpf_attach_type {
+    fn from(s: CgroupSockAddrAttachType) -> bpf_attach_type {
+        match s {
+            CgroupSockAddrAttachType::Bind4 => bpf_attach_type::BPF_CGROUP_INET4_BIND,
+            CgroupSockAddrAttachType::Bind6 => bpf_attach_type::BPF_CGROUP_INET6_BIND,
+            CgroupSockAddrAttachType::Connect4 => bpf_attach_type::BPF_CGROUP_INET4_CONNECT,
+            CgroupSockAddrAttachType::Connect6 => bpf_attach_type::BPF_CGROUP_INET6_CONNECT,
+            CgroupSockAddrAttachType::GetPeerName4 => bpf_attach_type::BPF_CGROUP_INET4_GETPEERNAME,
+            CgroupSockAddrAttachType::GetPeerName6 => bpf_attach_type::BPF_CGROUP_INET6_GETPEERNAME,
+            CgroupSockAddrAttachType::GetSockName4 => bpf_attach_type::BPF_CGROUP_INET4_GETSOCKNAME,
+            CgroupSockAddrAttachType::GetSockName6 => bpf_attach_type::BPF_CGROUP_INET6_GETSOCKNAME,
+            CgroupSockAddrAttachType::UDPSendMsg4 => bpf_attach_type::BPF_CGROUP_UDP4_SENDMSG,
+            CgroupSockAddrAttachType::UDPSendMsg6 => bpf_attach_type::BPF_CGROUP_UDP6_SENDMSG,
+            CgroupSockAddrAttachType::UDPRecvMsg4 => bpf_attach_type::BPF_CGROUP_UDP4_RECVMSG,
+            CgroupSockAddrAttachType::UDPRecvMsg6 => bpf_attach_type::BPF_CGROUP_UDP6_RECVMSG,
+        }
+    }
+}
+
+#[derive(Debug, Error)]
+#[error("{0} is not a valid attach type for a CGROUP_SOCK_ADDR program")]
+pub(crate) struct InvalidAttachType(String);
+
+impl CgroupSockAddrAttachType {
+    pub(crate) fn try_from(value: &str) -> Result<CgroupSockAddrAttachType, InvalidAttachType> {
+        match value {
+            "bind4" => Ok(CgroupSockAddrAttachType::Bind4),
+            "bind6" => Ok(CgroupSockAddrAttachType::Bind6),
+            "connect4" => Ok(CgroupSockAddrAttachType::Connect4),
+            "connect6" => Ok(CgroupSockAddrAttachType::Connect6),
+            "getpeername4" => Ok(CgroupSockAddrAttachType::GetPeerName4),
+            "getpeername6" => Ok(CgroupSockAddrAttachType::GetPeerName6),
+            "getsockname4" => Ok(CgroupSockAddrAttachType::GetSockName4),
+            "getsockname6" => Ok(CgroupSockAddrAttachType::GetSockName6),
+            "sendmsg4" => Ok(CgroupSockAddrAttachType::UDPSendMsg4),
+            "sendmsg6" => Ok(CgroupSockAddrAttachType::UDPSendMsg6),
+            "recvmsg4" => Ok(CgroupSockAddrAttachType::UDPRecvMsg4),
+            "recvmsg6" => Ok(CgroupSockAddrAttachType::UDPRecvMsg6),
+            _ => Err(InvalidAttachType(value.to_owned())),
+        }
+    }
+}

+ 39 - 0
aya-obj/src/programs/cgroup_sockopt.rs

@@ -0,0 +1,39 @@
+//! Cgroup socket option programs.
+use alloc::{borrow::ToOwned, string::String};
+
+use crate::{
+    generated::bpf_attach_type,
+    thiserror::{self, Error},
+};
+
+/// Defines where to attach a `CgroupSockopt` program.
+#[derive(Copy, Clone, Debug)]
+pub enum CgroupSockoptAttachType {
+    /// Attach to GetSockopt.
+    Get,
+    /// Attach to SetSockopt.
+    Set,
+}
+
+impl From<CgroupSockoptAttachType> for bpf_attach_type {
+    fn from(s: CgroupSockoptAttachType) -> bpf_attach_type {
+        match s {
+            CgroupSockoptAttachType::Get => bpf_attach_type::BPF_CGROUP_GETSOCKOPT,
+            CgroupSockoptAttachType::Set => bpf_attach_type::BPF_CGROUP_SETSOCKOPT,
+        }
+    }
+}
+
+#[derive(Debug, Error)]
+#[error("{0} is not a valid attach type for a CGROUP_SOCKOPT program")]
+pub(crate) struct InvalidAttachType(String);
+
+impl CgroupSockoptAttachType {
+    pub(crate) fn try_from(value: &str) -> Result<CgroupSockoptAttachType, InvalidAttachType> {
+        match value {
+            "getsockopt" => Ok(CgroupSockoptAttachType::Get),
+            "setsockopt" => Ok(CgroupSockoptAttachType::Set),
+            _ => Err(InvalidAttachType(value.to_owned())),
+        }
+    }
+}

+ 9 - 0
aya-obj/src/programs/mod.rs

@@ -0,0 +1,9 @@
+//! Program struct and type bindings.
+
+pub mod cgroup_sock;
+pub mod cgroup_sock_addr;
+pub mod cgroup_sockopt;
+
+pub use cgroup_sock::CgroupSockAttachType;
+pub use cgroup_sock_addr::CgroupSockAddrAttachType;
+pub use cgroup_sockopt::CgroupSockoptAttachType;

+ 110 - 81
aya/src/obj/relocation.rs → aya-obj/src/relocation.rs

@@ -1,43 +1,81 @@
-use std::{collections::HashMap, mem};
+//! Program relocation handling.
 
+use core::mem;
+
+use alloc::{borrow::ToOwned, string::String};
 use log::debug;
 use object::{SectionIndex, SymbolKind};
-use thiserror::Error;
 
 use crate::{
     generated::{
         bpf_insn, BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD,
         BPF_PSEUDO_MAP_VALUE,
     },
-    maps::MapData,
+    maps::Map,
     obj::{Function, Object, Program},
-    BpfError,
+    thiserror::{self, Error},
+    util::HashMap,
 };
 
 pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
 
+/// The error type returned by [`Object::relocate_maps`] and [`Object::relocate_calls`]
+#[derive(Error, Debug)]
+#[error("error relocating `{function}`")]
+pub struct BpfRelocationError {
+    /// The function name
+    function: String,
+    #[source]
+    /// The original error
+    error: RelocationError,
+}
+
+/// Relocation failures
 #[derive(Debug, Error)]
-enum RelocationError {
+pub enum RelocationError {
+    /// Unknown symbol
     #[error("unknown symbol, index `{index}`")]
-    UnknownSymbol { index: usize },
+    UnknownSymbol {
+        /// The symbol index
+        index: usize,
+    },
 
+    /// Section not found
     #[error("section `{section_index}` not found, referenced by symbol `{}` #{symbol_index}",
             .symbol_name.clone().unwrap_or_default())]
     SectionNotFound {
+        /// The section index
         section_index: usize,
+        /// The symbol index
         symbol_index: usize,
+        /// The symbol name
         symbol_name: Option<String>,
     },
 
+    /// Unknown function
     #[error("function {address:#x} not found while relocating `{caller_name}`")]
-    UnknownFunction { address: u64, caller_name: String },
+    UnknownFunction {
+        /// The function address
+        address: u64,
+        /// The caller name
+        caller_name: String,
+    },
 
+    /// Referenced map not created yet
     #[error("the map `{name}` at section `{section_index}` has not been created")]
-    MapNotCreated { section_index: usize, name: String },
+    MapNotCreated {
+        /// The section index
+        section_index: usize,
+        /// The map name
+        name: String,
+    },
 
+    /// Invalid relocation offset
     #[error("invalid offset `{offset}` applying relocation #{relocation_number}")]
     InvalidRelocationOffset {
+        /// The relocation offset
         offset: u64,
+        /// The relocation number
         relocation_number: usize,
     },
 }
@@ -62,16 +100,17 @@ pub(crate) struct Symbol {
 }
 
 impl Object {
-    pub fn relocate_maps(&mut self, maps: &HashMap<String, MapData>) -> Result<(), BpfError> {
-        let maps_by_section = maps
-            .iter()
-            .map(|(name, map)| (map.obj.section_index(), (name.as_str(), map)))
-            .collect::<HashMap<_, _>>();
-
-        let maps_by_symbol = maps
-            .iter()
-            .map(|(name, map)| (map.obj.symbol_index(), (name.as_str(), map)))
-            .collect::<HashMap<_, _>>();
+    /// Relocates the map references
+    pub fn relocate_maps<'a, I: Iterator<Item = (&'a str, Option<i32>, &'a Map)>>(
+        &mut self,
+        maps: I,
+    ) -> Result<(), BpfRelocationError> {
+        let mut maps_by_section = HashMap::new();
+        let mut maps_by_symbol = HashMap::new();
+        for (name, fd, map) in maps {
+            maps_by_section.insert(map.section_index(), (name, fd, map));
+            maps_by_symbol.insert(map.symbol_index(), (name, fd, map));
+        }
 
         let functions = self
             .programs
@@ -89,9 +128,9 @@ impl Object {
                     &self.symbols_by_index,
                     self.text_section_index,
                 )
-                .map_err(|error| BpfError::RelocationError {
+                .map_err(|error| BpfRelocationError {
                     function: function.name.clone(),
-                    error: Box::new(error),
+                    error,
                 })?;
             }
         }
@@ -99,7 +138,8 @@ impl Object {
         Ok(())
     }
 
-    pub fn relocate_calls(&mut self) -> Result<(), BpfError> {
+    /// Relocates function calls
+    pub fn relocate_calls(&mut self) -> Result<(), BpfRelocationError> {
         for (name, program) in self.programs.iter_mut() {
             let linker = FunctionLinker::new(
                 self.text_section_index,
@@ -107,12 +147,10 @@ impl Object {
                 &self.relocations,
                 &self.symbols_by_index,
             );
-            linker
-                .link(program)
-                .map_err(|error| BpfError::RelocationError {
-                    function: name.clone(),
-                    error: Box::new(error),
-                })?;
+            linker.link(program).map_err(|error| BpfRelocationError {
+                function: name.to_owned(),
+                error,
+            })?;
         }
 
         Ok(())
@@ -122,8 +160,8 @@ impl Object {
 fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
     fun: &mut Function,
     relocations: I,
-    maps_by_section: &HashMap<usize, (&str, &MapData)>,
-    maps_by_symbol: &HashMap<usize, (&str, &MapData)>,
+    maps_by_section: &HashMap<usize, (&str, Option<i32>, &Map)>,
+    maps_by_symbol: &HashMap<usize, (&str, Option<i32>, &Map)>,
     symbol_table: &HashMap<usize, Symbol>,
     text_section_index: Option<usize>,
 ) -> Result<(), RelocationError> {
@@ -166,7 +204,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
             continue;
         }
 
-        let (name, map) = if maps_by_symbol.contains_key(&rel.symbol_index) {
+        let (name, fd, map) = if maps_by_symbol.contains_key(&rel.symbol_index) {
             maps_by_symbol
                 .get(&rel.symbol_index)
                 .ok_or(RelocationError::SectionNotFound {
@@ -184,12 +222,12 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
                 })?
         };
 
-        let map_fd = map.fd.ok_or_else(|| RelocationError::MapNotCreated {
+        let map_fd = fd.ok_or_else(|| RelocationError::MapNotCreated {
             name: (*name).into(),
             section_index,
         })?;
 
-        if !map.obj.data().is_empty() {
+        if !map.data().is_empty() {
             instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_VALUE as u8);
             instructions[ins_index + 1].imm = instructions[ins_index].imm + sym.address as i32;
         } else {
@@ -436,12 +474,9 @@ fn insn_is_call(ins: &bpf_insn) -> bool {
 
 #[cfg(test)]
 mod test {
-    use crate::{
-        bpf_map_def,
-        maps::MapData,
-        obj::{self, BtfMap, LegacyMap, MapKind},
-        BtfMapDef,
-    };
+    use alloc::{string::ToString, vec, vec::Vec};
+
+    use crate::maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, MapKind};
 
     use super::*;
 
@@ -458,41 +493,31 @@ mod test {
     }
 
     fn ins(bytes: &[u8]) -> bpf_insn {
-        unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const _) }
+        unsafe { core::ptr::read_unaligned(bytes.as_ptr() as *const _) }
     }
 
-    fn fake_legacy_map(fd: i32, symbol_index: usize) -> MapData {
-        MapData {
-            obj: obj::Map::Legacy(LegacyMap {
-                def: bpf_map_def {
-                    ..Default::default()
-                },
-                section_index: 0,
-                symbol_index,
-                data: Vec::new(),
-                kind: MapKind::Other,
-            }),
-            fd: Some(fd),
-            btf_fd: None,
-            pinned: false,
-        }
+    fn fake_legacy_map(symbol_index: usize) -> Map {
+        Map::Legacy(LegacyMap {
+            def: bpf_map_def {
+                ..Default::default()
+            },
+            section_index: 0,
+            symbol_index,
+            data: Vec::new(),
+            kind: MapKind::Other,
+        })
     }
 
-    fn fake_btf_map(fd: i32, symbol_index: usize) -> MapData {
-        MapData {
-            obj: obj::Map::Btf(BtfMap {
-                def: BtfMapDef {
-                    ..Default::default()
-                },
-                section_index: 0,
-                symbol_index,
-                data: Vec::new(),
-                kind: MapKind::Other,
-            }),
-            fd: Some(fd),
-            btf_fd: None,
-            pinned: false,
-        }
+    fn fake_btf_map(symbol_index: usize) -> Map {
+        Map::Btf(BtfMap {
+            def: BtfMapDef {
+                ..Default::default()
+            },
+            section_index: 0,
+            symbol_index,
+            data: Vec::new(),
+            kind: MapKind::Other,
+        })
     }
 
     fn fake_func(name: &str, instructions: Vec<bpf_insn>) -> Function {
@@ -527,8 +552,8 @@ mod test {
         }];
         let maps_by_section = HashMap::new();
 
-        let map = fake_legacy_map(1, 1);
-        let maps_by_symbol = HashMap::from([(1, ("test_map", &map))]);
+        let map = fake_legacy_map(1);
+        let maps_by_symbol = HashMap::from([(1, ("test_map", Some(1), &map))]);
 
         relocate_maps(
             &mut fun,
@@ -579,10 +604,12 @@ mod test {
         ];
         let maps_by_section = HashMap::new();
 
-        let map_1 = fake_legacy_map(1, 1);
-        let map_2 = fake_legacy_map(2, 2);
-        let maps_by_symbol =
-            HashMap::from([(1, ("test_map_1", &map_1)), (2, ("test_map_2", &map_2))]);
+        let map_1 = fake_legacy_map(1);
+        let map_2 = fake_legacy_map(2);
+        let maps_by_symbol = HashMap::from([
+            (1, ("test_map_1", Some(1), &map_1)),
+            (2, ("test_map_2", Some(2), &map_2)),
+        ]);
 
         relocate_maps(
             &mut fun,
@@ -622,8 +649,8 @@ mod test {
         }];
         let maps_by_section = HashMap::new();
 
-        let map = fake_btf_map(1, 1);
-        let maps_by_symbol = HashMap::from([(1, ("test_map", &map))]);
+        let map = fake_btf_map(1);
+        let maps_by_symbol = HashMap::from([(1, ("test_map", Some(1), &map))]);
 
         relocate_maps(
             &mut fun,
@@ -674,10 +701,12 @@ mod test {
         ];
         let maps_by_section = HashMap::new();
 
-        let map_1 = fake_btf_map(1, 1);
-        let map_2 = fake_btf_map(2, 2);
-        let maps_by_symbol =
-            HashMap::from([(1, ("test_map_1", &map_1)), (2, ("test_map_2", &map_2))]);
+        let map_1 = fake_btf_map(1);
+        let map_2 = fake_btf_map(2);
+        let maps_by_symbol = HashMap::from([
+            (1, ("test_map_1", Some(1), &map_1)),
+            (2, ("test_map_2", Some(2), &map_2)),
+        ]);
 
         relocate_maps(
             &mut fun,

+ 12 - 0
aya-obj/src/util.rs

@@ -0,0 +1,12 @@
+use core::{mem, slice};
+
+#[cfg(feature = "no_std")]
+pub(crate) use hashbrown::HashMap;
+#[cfg(not(feature = "no_std"))]
+pub(crate) use std::collections::HashMap;
+
+/// bytes_of converts a <T> to a byte slice
+pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
+    let size = mem::size_of::<T>();
+    slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size)
+}

+ 1 - 0
aya-tool/src/bindgen.rs

@@ -2,6 +2,7 @@ use bindgen::{self, Builder, EnumVariation};
 
 pub fn user_builder() -> Builder {
     bindgen::builder()
+        .use_core()
         .layout_tests(false)
         .generate_comments(false)
         .prepend_enum_name(false)

+ 1 - 0
aya/Cargo.toml

@@ -12,6 +12,7 @@ edition = "2021"
 
 [dependencies]
 libc = { version = "0.2.105" }
+aya-obj = { path = "../aya-obj", version = "0.1.0" }
 thiserror = "1"
 object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] }
 bitflags = "1.2.1"

+ 44 - 99
aya/src/bpf.rs

@@ -1,13 +1,16 @@
 use std::{
     borrow::Cow,
     collections::{HashMap, HashSet},
-    error::Error,
     ffi::CString,
     fs, io,
     os::{raw::c_int, unix::io::RawFd},
     path::{Path, PathBuf},
 };
 
+use aya_obj::{
+    btf::{BtfFeatures, BtfRelocationError},
+    relocation::BpfRelocationError,
+};
 use log::debug;
 use thiserror::Error;
 
@@ -19,7 +22,8 @@ use crate::{
     maps::{Map, MapData, MapError},
     obj::{
         btf::{Btf, BtfError},
-        MapKind, Object, ParseError, ProgramSection,
+        maps::MapKind,
+        Object, ParseError, ProgramSection,
     },
     programs::{
         BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
@@ -58,75 +62,13 @@ unsafe_impl_pod!(i8, u8, i16, u16, i32, u32, i64, u64, u128, i128);
 // It only makes sense that an array of POD types is itself POD
 unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
 
-#[allow(non_camel_case_types)]
-#[repr(C)]
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub(crate) struct bpf_map_def {
-    // minimum features required by old BPF programs
-    pub(crate) map_type: u32,
-    pub(crate) key_size: u32,
-    pub(crate) value_size: u32,
-    pub(crate) max_entries: u32,
-    pub(crate) map_flags: u32,
-    // optional features
-    pub(crate) id: u32,
-    pub(crate) pinning: PinningType,
-}
-
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub(crate) struct BtfMapDef {
-    pub(crate) map_type: u32,
-    pub(crate) key_size: u32,
-    pub(crate) value_size: u32,
-    pub(crate) max_entries: u32,
-    pub(crate) map_flags: u32,
-    pub(crate) pinning: PinningType,
-    pub(crate) btf_key_type_id: u32,
-    pub(crate) btf_value_type_id: u32,
-}
-
-#[repr(u32)]
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum PinningType {
-    None = 0,
-    ByName = 1,
-}
-
-#[derive(Debug, Error)]
-pub(crate) enum PinningError {
-    #[error("unsupported pinning type")]
-    Unsupported,
-}
-
-impl TryFrom<u32> for PinningType {
-    type Error = PinningError;
-
-    fn try_from(value: u32) -> Result<Self, Self::Error> {
-        match value {
-            0 => Ok(PinningType::None),
-            1 => Ok(PinningType::ByName),
-            _ => Err(PinningError::Unsupported),
-        }
-    }
-}
-
-impl Default for PinningType {
-    fn default() -> Self {
-        PinningType::None
-    }
-}
+pub use aya_obj::maps::{bpf_map_def, 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,
-    pub btf_decl_tag: bool,
-    pub btf_type_tag: bool,
+    pub btf: Option<BtfFeatures>,
 }
 
 impl Features {
@@ -134,33 +76,37 @@ impl Features {
         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);
+        self.btf = if is_btf_supported() {
+            Some(BtfFeatures::default())
+        } else {
+            None
+        };
+        debug!("[FEAT PROBE] BTF support: {}", self.btf.is_some());
 
-        if self.btf {
-            self.btf_func = is_btf_func_supported();
-            debug!("[FEAT PROBE] BTF func support: {}", self.btf_func);
+        if let Some(ref mut btf) = self.btf {
+            btf.btf_func = is_btf_func_supported();
+            debug!("[FEAT PROBE] BTF func support: {}", btf.btf_func);
 
-            self.btf_func_global = is_btf_func_global_supported();
+            btf.btf_func_global = is_btf_func_global_supported();
             debug!(
                 "[FEAT PROBE] BTF global func support: {}",
-                self.btf_func_global
+                btf.btf_func_global
             );
 
-            self.btf_datasec = is_btf_datasec_supported();
+            btf.btf_datasec = is_btf_datasec_supported();
             debug!(
                 "[FEAT PROBE] BTF var and datasec support: {}",
-                self.btf_datasec
+                btf.btf_datasec
             );
 
-            self.btf_float = is_btf_float_supported();
-            debug!("[FEAT PROBE] BTF float support: {}", self.btf_float);
+            btf.btf_float = is_btf_float_supported();
+            debug!("[FEAT PROBE] BTF float support: {}", btf.btf_float);
 
-            self.btf_decl_tag = is_btf_decl_tag_supported();
-            debug!("[FEAT PROBE] BTF decl_tag support: {}", self.btf_decl_tag);
+            btf.btf_decl_tag = is_btf_decl_tag_supported();
+            debug!("[FEAT PROBE] BTF decl_tag support: {}", btf.btf_decl_tag);
 
-            self.btf_type_tag = is_btf_type_tag_supported();
-            debug!("[FEAT PROBE] BTF type_tag support: {}", self.btf_type_tag);
+            btf.btf_type_tag = is_btf_type_tag_supported();
+            debug!("[FEAT PROBE] BTF type_tag support: {}", btf.btf_type_tag);
         }
     }
 }
@@ -413,15 +359,10 @@ 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_and_sanitize(&section_data, &symbol_offsets, &self.features)?;
+        let btf_fd = if let Some(ref btf) = self.features.btf {
+            if let Some(btf) = obj.fixup_and_sanitize_btf(btf)? {
                 // load btf to the kernel
-                let raw_btf = obj_btf.to_bytes();
-                Some(load_btf(raw_btf)?)
+                Some(load_btf(btf.to_bytes())?)
             } else {
                 None
             }
@@ -497,7 +438,10 @@ impl<'a> BpfLoader<'a> {
             maps.insert(name, map);
         }
 
-        obj.relocate_maps(&maps)?;
+        obj.relocate_maps(
+            maps.iter()
+                .map(|(s, data)| (s.as_str(), data.fd, &data.obj)),
+        )?;
         obj.relocate_calls()?;
 
         let programs = obj
@@ -655,7 +599,10 @@ impl<'a> BpfLoader<'a> {
 fn parse_map(data: (String, MapData)) -> Result<(String, Map), BpfError> {
     let name = data.0;
     let map = data.1;
-    let map_type = bpf_map_type::try_from(map.obj.map_type())?;
+    let map_type =
+        bpf_map_type::try_from(map.obj.map_type()).map_err(|e| MapError::InvalidMapType {
+            map_type: e.map_type,
+        })?;
     let map = match map_type {
         BPF_MAP_TYPE_ARRAY => Ok(Map::Array(map)),
         BPF_MAP_TYPE_PERCPU_ARRAY => Ok(Map::PerCpuArray(map)),
@@ -918,14 +865,12 @@ pub enum BpfError {
     BtfError(#[from] BtfError),
 
     /// Error performing relocations
-    #[error("error relocating `{function}`")]
-    RelocationError {
-        /// The function name
-        function: String,
-        #[source]
-        /// The original error
-        error: Box<dyn Error + Send + Sync>,
-    },
+    #[error("error relocating function")]
+    RelocationError(#[from] BpfRelocationError),
+
+    /// Error performing relocations
+    #[error("error relocating section")]
+    BtfRelocationError(#[from] BtfRelocationError),
 
     /// No BTF parsed for object
     #[error("no BTF parsed for object")]

+ 2 - 2
aya/src/lib.rs

@@ -46,9 +46,9 @@ extern crate lazy_static;
 extern crate bitflags;
 
 mod bpf;
-mod generated;
+use aya_obj::generated;
 pub mod maps;
-mod obj;
+use aya_obj as obj;
 pub mod pin;
 pub mod programs;
 mod sys;

+ 8 - 5
aya/src/maps/bloom_filter.rs

@@ -84,14 +84,17 @@ mod tests {
             bpf_map_type::{BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
         },
         maps::{Map, MapData},
-        obj,
+        obj::{
+            self,
+            maps::{LegacyMap, MapKind},
+        },
         sys::{override_syscall, SysResult, Syscall},
     };
     use libc::{EFAULT, ENOENT};
     use std::io;
 
     fn new_obj_map() -> obj::Map {
-        obj::Map::Legacy(obj::LegacyMap {
+        obj::Map::Legacy(LegacyMap {
             def: bpf_map_def {
                 map_type: BPF_MAP_TYPE_BLOOM_FILTER as u32,
                 key_size: 4,
@@ -102,7 +105,7 @@ mod tests {
             section_index: 0,
             symbol_index: 0,
             data: Vec::new(),
-            kind: obj::MapKind::Other,
+            kind: MapKind::Other,
         })
     }
 
@@ -130,7 +133,7 @@ mod tests {
     #[test]
     fn test_try_from_wrong_map() {
         let map_data = MapData {
-            obj: obj::Map::Legacy(obj::LegacyMap {
+            obj: obj::Map::Legacy(LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
                     key_size: 4,
@@ -141,7 +144,7 @@ mod tests {
                 section_index: 0,
                 symbol_index: 0,
                 data: Vec::new(),
-                kind: obj::MapKind::Other,
+                kind: MapKind::Other,
             }),
             fd: None,
             pinned: false,

+ 8 - 5
aya/src/maps/hash_map/hash_map.rs

@@ -117,14 +117,17 @@ mod tests {
             bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH},
         },
         maps::{Map, MapData},
-        obj,
+        obj::{
+            self,
+            maps::{LegacyMap, MapKind},
+        },
         sys::{override_syscall, SysResult, Syscall},
     };
 
     use super::*;
 
     fn new_obj_map() -> obj::Map {
-        obj::Map::Legacy(obj::LegacyMap {
+        obj::Map::Legacy(LegacyMap {
             def: bpf_map_def {
                 map_type: BPF_MAP_TYPE_HASH as u32,
                 key_size: 4,
@@ -134,7 +137,7 @@ mod tests {
             },
             section_index: 0,
             data: Vec::new(),
-            kind: obj::MapKind::Other,
+            kind: MapKind::Other,
             symbol_index: 0,
         })
     }
@@ -255,7 +258,7 @@ mod tests {
     #[test]
     fn test_try_from_ok_lru() {
         let map_data = MapData {
-            obj: obj::Map::Legacy(obj::LegacyMap {
+            obj: obj::Map::Legacy(LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_LRU_HASH as u32,
                     key_size: 4,
@@ -266,7 +269,7 @@ mod tests {
                 section_index: 0,
                 symbol_index: 0,
                 data: Vec::new(),
-                kind: obj::MapKind::Other,
+                kind: MapKind::Other,
             }),
             fd: Some(42),
             pinned: false,

+ 8 - 5
aya/src/maps/lpm_trie.rs

@@ -247,14 +247,17 @@ mod tests {
             bpf_map_type::{BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
         },
         maps::{Map, MapData},
-        obj,
+        obj::{
+            self,
+            maps::{LegacyMap, MapKind},
+        },
         sys::{override_syscall, SysResult, Syscall},
     };
     use libc::{EFAULT, ENOENT};
     use std::{io, mem, net::Ipv4Addr};
 
     fn new_obj_map() -> obj::Map {
-        obj::Map::Legacy(obj::LegacyMap {
+        obj::Map::Legacy(LegacyMap {
             def: bpf_map_def {
                 map_type: BPF_MAP_TYPE_LPM_TRIE as u32,
                 key_size: mem::size_of::<Key<u32>>() as u32,
@@ -265,7 +268,7 @@ mod tests {
             section_index: 0,
             symbol_index: 0,
             data: Vec::new(),
-            kind: obj::MapKind::Other,
+            kind: MapKind::Other,
         })
     }
 
@@ -310,7 +313,7 @@ mod tests {
     #[test]
     fn test_try_from_wrong_map() {
         let map_data = MapData {
-            obj: obj::Map::Legacy(obj::LegacyMap {
+            obj: obj::Map::Legacy(LegacyMap {
                 def: bpf_map_def {
                     map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
                     key_size: 4,
@@ -321,7 +324,7 @@ mod tests {
                 section_index: 0,
                 symbol_index: 0,
                 data: Vec::new(),
-                kind: obj::MapKind::Other,
+                kind: MapKind::Other,
             }),
             fd: None,
             btf_fd: None,

+ 2 - 47
aya/src/maps/mod.rs

@@ -53,7 +53,6 @@ use log::warn;
 use thiserror::Error;
 
 use crate::{
-    generated::bpf_map_type,
     obj::{self, parse_map_info},
     pin::PinError,
     sys::{
@@ -739,50 +738,6 @@ impl<K: Pod, V, I: IterableMap<K, V>> Iterator for MapIter<'_, K, V, I> {
     }
 }
 
-impl TryFrom<u32> for bpf_map_type {
-    type Error = MapError;
-
-    fn try_from(map_type: u32) -> Result<Self, Self::Error> {
-        use bpf_map_type::*;
-        Ok(match map_type {
-            x if x == BPF_MAP_TYPE_UNSPEC as u32 => BPF_MAP_TYPE_UNSPEC,
-            x if x == BPF_MAP_TYPE_HASH as u32 => BPF_MAP_TYPE_HASH,
-            x if x == BPF_MAP_TYPE_ARRAY as u32 => BPF_MAP_TYPE_ARRAY,
-            x if x == BPF_MAP_TYPE_PROG_ARRAY as u32 => BPF_MAP_TYPE_PROG_ARRAY,
-            x if x == BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 => BPF_MAP_TYPE_PERF_EVENT_ARRAY,
-            x if x == BPF_MAP_TYPE_PERCPU_HASH as u32 => BPF_MAP_TYPE_PERCPU_HASH,
-            x if x == BPF_MAP_TYPE_PERCPU_ARRAY as u32 => BPF_MAP_TYPE_PERCPU_ARRAY,
-            x if x == BPF_MAP_TYPE_STACK_TRACE as u32 => BPF_MAP_TYPE_STACK_TRACE,
-            x if x == BPF_MAP_TYPE_CGROUP_ARRAY as u32 => BPF_MAP_TYPE_CGROUP_ARRAY,
-            x if x == BPF_MAP_TYPE_LRU_HASH as u32 => BPF_MAP_TYPE_LRU_HASH,
-            x if x == BPF_MAP_TYPE_LRU_PERCPU_HASH as u32 => BPF_MAP_TYPE_LRU_PERCPU_HASH,
-            x if x == BPF_MAP_TYPE_LPM_TRIE as u32 => BPF_MAP_TYPE_LPM_TRIE,
-            x if x == BPF_MAP_TYPE_BLOOM_FILTER as u32 => BPF_MAP_TYPE_BLOOM_FILTER,
-            x if x == BPF_MAP_TYPE_ARRAY_OF_MAPS as u32 => BPF_MAP_TYPE_ARRAY_OF_MAPS,
-            x if x == BPF_MAP_TYPE_HASH_OF_MAPS as u32 => BPF_MAP_TYPE_HASH_OF_MAPS,
-            x if x == BPF_MAP_TYPE_DEVMAP as u32 => BPF_MAP_TYPE_DEVMAP,
-            x if x == BPF_MAP_TYPE_SOCKMAP as u32 => BPF_MAP_TYPE_SOCKMAP,
-            x if x == BPF_MAP_TYPE_CPUMAP as u32 => BPF_MAP_TYPE_CPUMAP,
-            x if x == BPF_MAP_TYPE_XSKMAP as u32 => BPF_MAP_TYPE_XSKMAP,
-            x if x == BPF_MAP_TYPE_SOCKHASH as u32 => BPF_MAP_TYPE_SOCKHASH,
-            x if x == BPF_MAP_TYPE_CGROUP_STORAGE as u32 => BPF_MAP_TYPE_CGROUP_STORAGE,
-            x if x == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY as u32 => BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
-            x if x == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE as u32 => {
-                BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
-            }
-            x if x == BPF_MAP_TYPE_QUEUE as u32 => BPF_MAP_TYPE_QUEUE,
-            x if x == BPF_MAP_TYPE_STACK as u32 => BPF_MAP_TYPE_STACK,
-            x if x == BPF_MAP_TYPE_SK_STORAGE as u32 => BPF_MAP_TYPE_SK_STORAGE,
-            x if x == BPF_MAP_TYPE_DEVMAP_HASH as u32 => BPF_MAP_TYPE_DEVMAP_HASH,
-            x if x == BPF_MAP_TYPE_STRUCT_OPS as u32 => BPF_MAP_TYPE_STRUCT_OPS,
-            x if x == BPF_MAP_TYPE_RINGBUF as u32 => BPF_MAP_TYPE_RINGBUF,
-            x if x == BPF_MAP_TYPE_INODE_STORAGE as u32 => BPF_MAP_TYPE_INODE_STORAGE,
-            x if x == BPF_MAP_TYPE_TASK_STORAGE as u32 => BPF_MAP_TYPE_TASK_STORAGE,
-            _ => return Err(MapError::InvalidMapType { map_type }),
-        })
-    }
-}
-
 pub(crate) struct PerCpuKernelMem {
     bytes: Vec<u8>,
 }
@@ -889,14 +844,14 @@ mod tests {
         bpf_map_def,
         generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_HASH},
         maps::MapData,
-        obj::MapKind,
+        obj::maps::{LegacyMap, MapKind},
         sys::{override_syscall, Syscall},
     };
 
     use super::*;
 
     fn new_obj_map() -> obj::Map {
-        obj::Map::Legacy(obj::LegacyMap {
+        obj::Map::Legacy(LegacyMap {
             def: bpf_map_def {
                 map_type: BPF_MAP_TYPE_HASH as u32,
                 key_size: 4,

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

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

+ 1 - 50
aya/src/programs/cgroup_sock.rs

@@ -1,7 +1,6 @@
 //! Cgroup socket programs.
-use thiserror::Error;
+pub use aya_obj::programs::CgroupSockAttachType;
 
-use crate::generated::bpf_attach_type;
 use std::{
     hash::Hash,
     os::unix::prelude::{AsRawFd, RawFd},
@@ -154,51 +153,3 @@ define_link_wrapper!(
     CgroupSockLinkInner,
     CgroupSockLinkIdInner
 );
-
-/// Defines where to attach a [`CgroupSock`] program.
-#[derive(Copy, Clone, Debug)]
-pub enum CgroupSockAttachType {
-    /// Called after the IPv4 bind events.
-    PostBind4,
-    /// Called after the IPv6 bind events.
-    PostBind6,
-    /// Attach to IPv4 connect events.
-    SockCreate,
-    /// Attach to IPv6 connect events.
-    SockRelease,
-}
-
-impl Default for CgroupSockAttachType {
-    // The kernel checks for a 0 attach_type and sets it to sock_create
-    // We may as well do that here also
-    fn default() -> Self {
-        CgroupSockAttachType::SockCreate
-    }
-}
-
-impl From<CgroupSockAttachType> for bpf_attach_type {
-    fn from(s: CgroupSockAttachType) -> bpf_attach_type {
-        match s {
-            CgroupSockAttachType::PostBind4 => bpf_attach_type::BPF_CGROUP_INET4_POST_BIND,
-            CgroupSockAttachType::PostBind6 => bpf_attach_type::BPF_CGROUP_INET6_POST_BIND,
-            CgroupSockAttachType::SockCreate => bpf_attach_type::BPF_CGROUP_INET_SOCK_CREATE,
-            CgroupSockAttachType::SockRelease => bpf_attach_type::BPF_CGROUP_INET_SOCK_RELEASE,
-        }
-    }
-}
-
-#[derive(Debug, Error)]
-#[error("{0} is not a valid attach type for a CGROUP_SOCK program")]
-pub(crate) struct InvalidAttachType(String);
-
-impl CgroupSockAttachType {
-    pub(crate) fn try_from(value: &str) -> Result<CgroupSockAttachType, InvalidAttachType> {
-        match value {
-            "post_bind4" => Ok(CgroupSockAttachType::PostBind4),
-            "post_bind6" => Ok(CgroupSockAttachType::PostBind6),
-            "sock_create" => Ok(CgroupSockAttachType::SockCreate),
-            "sock_release" => Ok(CgroupSockAttachType::SockRelease),
-            _ => Err(InvalidAttachType(value.to_owned())),
-        }
-    }
-}

+ 1 - 74
aya/src/programs/cgroup_sock_addr.rs

@@ -1,7 +1,6 @@
 //! Cgroup socket address programs.
-use thiserror::Error;
+pub use aya_obj::programs::CgroupSockAddrAttachType;
 
-use crate::generated::bpf_attach_type;
 use std::{
     hash::Hash,
     os::unix::prelude::{AsRawFd, RawFd},
@@ -160,75 +159,3 @@ define_link_wrapper!(
     CgroupSockAddrLinkInner,
     CgroupSockAddrLinkIdInner
 );
-
-/// Defines where to attach a [`CgroupSockAddr`] program.
-#[derive(Copy, Clone, Debug)]
-pub enum CgroupSockAddrAttachType {
-    /// Attach to IPv4 bind events.
-    Bind4,
-    /// Attach to IPv6 bind events.
-    Bind6,
-    /// Attach to IPv4 connect events.
-    Connect4,
-    /// Attach to IPv6 connect events.
-    Connect6,
-    /// Attach to IPv4 getpeername events.
-    GetPeerName4,
-    /// Attach to IPv6 getpeername events.
-    GetPeerName6,
-    /// Attach to IPv4 getsockname events.
-    GetSockName4,
-    /// Attach to IPv6 getsockname events.
-    GetSockName6,
-    /// Attach to IPv4 udp_sendmsg events.
-    UDPSendMsg4,
-    /// Attach to IPv6 udp_sendmsg events.
-    UDPSendMsg6,
-    /// Attach to IPv4 udp_recvmsg events.
-    UDPRecvMsg4,
-    /// Attach to IPv6 udp_recvmsg events.
-    UDPRecvMsg6,
-}
-
-impl From<CgroupSockAddrAttachType> for bpf_attach_type {
-    fn from(s: CgroupSockAddrAttachType) -> bpf_attach_type {
-        match s {
-            CgroupSockAddrAttachType::Bind4 => bpf_attach_type::BPF_CGROUP_INET4_BIND,
-            CgroupSockAddrAttachType::Bind6 => bpf_attach_type::BPF_CGROUP_INET6_BIND,
-            CgroupSockAddrAttachType::Connect4 => bpf_attach_type::BPF_CGROUP_INET4_CONNECT,
-            CgroupSockAddrAttachType::Connect6 => bpf_attach_type::BPF_CGROUP_INET6_CONNECT,
-            CgroupSockAddrAttachType::GetPeerName4 => bpf_attach_type::BPF_CGROUP_INET4_GETPEERNAME,
-            CgroupSockAddrAttachType::GetPeerName6 => bpf_attach_type::BPF_CGROUP_INET6_GETPEERNAME,
-            CgroupSockAddrAttachType::GetSockName4 => bpf_attach_type::BPF_CGROUP_INET4_GETSOCKNAME,
-            CgroupSockAddrAttachType::GetSockName6 => bpf_attach_type::BPF_CGROUP_INET6_GETSOCKNAME,
-            CgroupSockAddrAttachType::UDPSendMsg4 => bpf_attach_type::BPF_CGROUP_UDP4_SENDMSG,
-            CgroupSockAddrAttachType::UDPSendMsg6 => bpf_attach_type::BPF_CGROUP_UDP6_SENDMSG,
-            CgroupSockAddrAttachType::UDPRecvMsg4 => bpf_attach_type::BPF_CGROUP_UDP4_RECVMSG,
-            CgroupSockAddrAttachType::UDPRecvMsg6 => bpf_attach_type::BPF_CGROUP_UDP6_RECVMSG,
-        }
-    }
-}
-
-#[derive(Debug, Error)]
-#[error("{0} is not a valid attach type for a CGROUP_SOCK_ADDR program")]
-pub(crate) struct InvalidAttachType(String);
-
-impl CgroupSockAddrAttachType {
-    pub(crate) fn try_from(value: &str) -> Result<CgroupSockAddrAttachType, InvalidAttachType> {
-        match value {
-            "bind4" => Ok(CgroupSockAddrAttachType::Bind4),
-            "bind6" => Ok(CgroupSockAddrAttachType::Bind6),
-            "connect4" => Ok(CgroupSockAddrAttachType::Connect4),
-            "connect6" => Ok(CgroupSockAddrAttachType::Connect6),
-            "getpeername4" => Ok(CgroupSockAddrAttachType::GetPeerName4),
-            "getpeername6" => Ok(CgroupSockAddrAttachType::GetPeerName6),
-            "getsockname4" => Ok(CgroupSockAddrAttachType::GetSockName4),
-            "getsockname6" => Ok(CgroupSockAddrAttachType::GetSockName6),
-            "sendmsg4" => Ok(CgroupSockAddrAttachType::UDPSendMsg4),
-            "sendmsg6" => Ok(CgroupSockAddrAttachType::UDPSendMsg6),
-            "recvmsg4" => Ok(CgroupSockAddrAttachType::UDPRecvMsg4),
-            "recvmsg6" => Ok(CgroupSockAddrAttachType::UDPRecvMsg6),
-            _ => Err(InvalidAttachType(value.to_owned())),
-        }
-    }
-}

+ 2 - 35
aya/src/programs/cgroup_sockopt.rs

@@ -1,5 +1,5 @@
 //! Cgroup socket option programs.
-use thiserror::Error;
+pub use aya_obj::programs::CgroupSockoptAttachType;
 
 use std::{
     hash::Hash,
@@ -9,8 +9,7 @@ use std::{
 use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT,
     programs::{
-        bpf_attach_type, define_link_wrapper, load_program, FdLink, Link, ProgAttachLink,
-        ProgramData, ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -155,35 +154,3 @@ define_link_wrapper!(
     CgroupSockoptLinkInner,
     CgroupSockoptLinkIdInner
 );
-
-/// Defines where to attach a [`CgroupSockopt`] program.
-#[derive(Copy, Clone, Debug)]
-pub enum CgroupSockoptAttachType {
-    /// Attach to GetSockopt.
-    Get,
-    /// Attach to SetSockopt.
-    Set,
-}
-
-impl From<CgroupSockoptAttachType> for bpf_attach_type {
-    fn from(s: CgroupSockoptAttachType) -> bpf_attach_type {
-        match s {
-            CgroupSockoptAttachType::Get => bpf_attach_type::BPF_CGROUP_GETSOCKOPT,
-            CgroupSockoptAttachType::Set => bpf_attach_type::BPF_CGROUP_SETSOCKOPT,
-        }
-    }
-}
-
-#[derive(Debug, Error)]
-#[error("{0} is not a valid attach type for a CGROUP_SOCKOPT program")]
-pub(crate) struct InvalidAttachType(String);
-
-impl CgroupSockoptAttachType {
-    pub(crate) fn try_from(value: &str) -> Result<CgroupSockoptAttachType, InvalidAttachType> {
-        match value {
-            "getsockopt" => Ok(CgroupSockoptAttachType::Get),
-            "setsockopt" => Ok(CgroupSockoptAttachType::Set),
-            _ => Err(InvalidAttachType(value.to_owned())),
-        }
-    }
-}

+ 2 - 0
test/integration-test/Cargo.toml

@@ -7,6 +7,7 @@ publish = false
 [dependencies]
 anyhow = "1"
 aya = { path = "../../aya" }
+aya-obj = { path = "../../aya-obj" }
 clap = { version = "4", features = ["derive"] }
 env_logger = "0.10"
 inventory = "0.2"
@@ -15,4 +16,5 @@ lazy_static = "1"
 libc = { version = "0.2.105" }
 log = "0.4"
 object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] }
+rbpf = "0.1.0"
 regex = "1"

+ 1 - 0
test/integration-test/src/tests/mod.rs

@@ -6,6 +6,7 @@ use std::{ffi::CStr, mem};
 
 pub mod elf;
 pub mod load;
+pub mod rbpf;
 pub mod smoke;
 
 pub use integration_test_macros::integration_test;

+ 114 - 0
test/integration-test/src/tests/rbpf.rs

@@ -0,0 +1,114 @@
+use core::{mem::size_of, ptr::null_mut, slice::from_raw_parts};
+use std::collections::HashMap;
+
+use aya::include_bytes_aligned;
+use aya_obj::{generated::bpf_insn, Object, ProgramSection};
+
+use super::{integration_test, IntegrationTest};
+
+#[integration_test]
+fn run_with_rbpf() {
+    let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/pass");
+    let object = Object::parse(bytes).unwrap();
+
+    assert_eq!(object.programs.len(), 1);
+    assert!(matches!(
+        object.programs["pass"].section,
+        ProgramSection::Xdp { .. }
+    ));
+    assert_eq!(object.programs["pass"].section.name(), "pass");
+
+    let instructions = &object.programs["pass"].function.instructions;
+    let data = unsafe {
+        from_raw_parts(
+            instructions.as_ptr() as *const u8,
+            instructions.len() * size_of::<bpf_insn>(),
+        )
+    };
+    // Use rbpf interpreter instead of JIT compiler to ensure platform compatibility.
+    let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
+    const XDP_PASS: u64 = 2;
+    assert_eq!(vm.execute_program().unwrap(), XDP_PASS);
+}
+
+static mut MULTIMAP_MAPS: [*mut Vec<u64>; 2] = [null_mut(), null_mut()];
+
+#[integration_test]
+fn use_map_with_rbpf() {
+    let bytes =
+        include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/multimap-btf.bpf.o");
+    let mut object = Object::parse(bytes).unwrap();
+
+    assert_eq!(object.programs.len(), 1);
+    assert!(matches!(
+        object.programs["tracepoint"].section,
+        ProgramSection::TracePoint { .. }
+    ));
+    assert_eq!(object.programs["tracepoint"].section.name(), "tracepoint");
+
+    // Initialize maps:
+    // - fd: 0xCAFE00 or 0xCAFE01 (the 0xCAFE00 part is used to distinguish fds from indices),
+    // - Note that rbpf does not convert fds into real pointers,
+    //   so we keeps the pointers to our maps in MULTIMAP_MAPS, to be used in helpers.
+    let mut maps = HashMap::new();
+    let mut map_instances = vec![vec![0u64], vec![0u64]];
+    for (name, map) in object.maps.iter() {
+        assert_eq!(map.key_size(), size_of::<u32>() as u32);
+        assert_eq!(map.value_size(), size_of::<u64>() as u32);
+        assert_eq!(
+            map.map_type(),
+            aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY as u32
+        );
+
+        let map_id = if name == "map_1" { 0 } else { 1 };
+        let fd = map_id as i32 | 0xCAFE00;
+        maps.insert(name.to_owned(), (fd, map.clone()));
+
+        unsafe {
+            MULTIMAP_MAPS[map_id] = &mut map_instances[map_id] as *mut _;
+        }
+    }
+
+    object
+        .relocate_maps(
+            maps.iter()
+                .map(|(s, (fd, map))| (s.as_ref() as &str, Some(*fd), map)),
+        )
+        .expect("Relocation failed");
+    // Actually there is no local function call involved.
+    object.relocate_calls().unwrap();
+
+    // Executes the program
+    assert_eq!(object.programs.len(), 1);
+    let instructions = &object.programs["tracepoint"].function.instructions;
+    let data = unsafe {
+        from_raw_parts(
+            instructions.as_ptr() as *const u8,
+            instructions.len() * size_of::<bpf_insn>(),
+        )
+    };
+    let mut vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
+    vm.register_helper(2, bpf_map_update_elem_multimap)
+        .expect("Helper failed");
+    assert_eq!(vm.execute_program().unwrap(), 0);
+
+    assert_eq!(map_instances[0][0], 24);
+    assert_eq!(map_instances[1][0], 42);
+
+    unsafe {
+        MULTIMAP_MAPS[0] = null_mut();
+        MULTIMAP_MAPS[1] = null_mut();
+    }
+}
+
+fn bpf_map_update_elem_multimap(map: u64, key: u64, value: u64, _: u64, _: u64) -> u64 {
+    assert!(map == 0xCAFE00 || map == 0xCAFE01);
+    let key = *unsafe { (key as usize as *const u32).as_ref().unwrap() };
+    let value = *unsafe { (value as usize as *const u64).as_ref().unwrap() };
+    assert_eq!(key, 0);
+    unsafe {
+        let map_instance = MULTIMAP_MAPS[map as usize & 0xFF].as_mut().unwrap();
+        map_instance[0] = value;
+    }
+    0
+}

+ 2 - 2
xtask/src/codegen/aya.rs

@@ -11,7 +11,7 @@ pub fn codegen(opts: &Options) -> Result<(), anyhow::Error> {
 }
 
 fn codegen_internal_btf_bindings(opts: &Options) -> Result<(), anyhow::Error> {
-    let dir = PathBuf::from("aya");
+    let dir = PathBuf::from("aya-obj");
     let generated = dir.join("src/generated");
     let mut bindgen = bindgen::user_builder()
         .clang_arg(format!(
@@ -154,7 +154,7 @@ fn codegen_bindings(opts: &Options) -> Result<(), anyhow::Error> {
         "BPF_RINGBUF_.*",
     ];
 
-    let dir = PathBuf::from("aya");
+    let dir = PathBuf::from("aya-obj");
     let generated = dir.join("src/generated");
 
     let builder = || {

Some files were not shown because too many files changed in this diff