diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index e8da97e1..193e9528 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -15,14 +15,15 @@ use object::Endianness; use thiserror::Error; use crate::{ - generated::{btf_ext_header, btf_header}, btf::{ info::{FuncSecInfo, LineSecInfo}, relocation::Relocation, Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int, IntEncoding, LineInfo, Struct, Typedef, VarLinkage, }, + generated::{btf_ext_header, btf_header}, util::bytes_of, + Object, }; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32; @@ -157,7 +158,9 @@ 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, @@ -172,9 +175,9 @@ pub struct BtfFeatures { /// 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, @@ -184,6 +187,7 @@ pub struct Btf { } impl Btf { + /// Creates a new empty instance with its header initialized pub fn new() -> Btf { Btf { header: btf_header { @@ -206,6 +210,7 @@ impl Btf { self.types.types.iter() } + /// 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(); @@ -214,6 +219,7 @@ impl Btf { name_offset as 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(); @@ -240,6 +246,7 @@ impl Btf { ) } + /// Parses BTF from binary data of the given endianness pub fn parse(data: &[u8], endianness: Endianness) -> Result { if data.len() < mem::size_of::() { return Err(BtfError::InvalidHeader); @@ -333,6 +340,7 @@ impl Btf { self.string_at(ty.name_offset()).ok().map(String::from) } + /// Returns a type id matching the type name and [BtfKind] pub fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result { for (type_id, ty) in self.types().enumerate() { if ty.kind() != kind { @@ -379,6 +387,7 @@ impl Btf { }) } + /// Encodes the metadata as BTF format pub fn to_bytes(&self) -> Vec { // Safety: btf_header is POD let mut buf = unsafe { bytes_of::(&self.header).to_vec() }; @@ -388,7 +397,7 @@ impl Btf { buf } - pub fn fixup_and_sanitize( + pub(crate) fn fixup_and_sanitize( &mut self, section_sizes: &HashMap, symbol_offsets: &HashMap, @@ -569,11 +578,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, 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 .BTF.ext section #[derive(Debug, Clone)] pub struct BtfExt { data: Vec, diff --git a/aya-obj/src/btf/info.rs b/aya-obj/src/btf/info.rs index d265a3a9..5d1652c4 100644 --- a/aya-obj/src/btf/info.rs +++ b/aya-obj/src/btf/info.rs @@ -19,10 +19,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 struct FuncSecInfo { - pub _sec_name_offset: u32, + pub(crate) _sec_name_offset: u32, + /// The number of info entries pub num_info: u32, + /// Info entries pub func_info: Vec, } @@ -64,6 +72,7 @@ impl FuncSecInfo { } } + /// Encodes the [bpf_func_info] entries pub fn func_info_bytes(&self) -> Vec { let mut buf = vec![]; for l in &self.func_info { @@ -73,13 +82,20 @@ impl FuncSecInfo { buf } + /// 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 struct FuncInfo { + /// The [FuncSecInfo] subsections for some sections, referenced by section names pub data: HashMap, } @@ -98,12 +114,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 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, } @@ -154,6 +177,7 @@ impl LineSecInfo { } } + /// Encode the entries pub fn line_info_bytes(&self) -> Vec { let mut buf = vec![]; for l in &self.line_info { @@ -163,6 +187,7 @@ impl LineSecInfo { buf } + /// Returns the number of entries pub fn len(&self) -> usize { self.line_info.len() } diff --git a/aya-obj/src/btf/mod.rs b/aya-obj/src/btf/mod.rs index 44bf123f..a5606e5e 100644 --- a/aya-obj/src/btf/mod.rs +++ b/aya-obj/src/btf/mod.rs @@ -7,5 +7,6 @@ mod relocation; mod types; pub use btf::*; -pub(crate) use info::*; -pub(crate) use types::*; +pub use info::*; +pub use relocation::BtfRelocationError; +pub use types::*; diff --git a/aya-obj/src/btf/relocation.rs b/aya-obj/src/btf/relocation.rs index b3e89d93..641e7265 100644 --- a/aya-obj/src/btf/relocation.rs +++ b/aya-obj/src/btf/relocation.rs @@ -14,6 +14,7 @@ use crate::{ Object, Program, ProgramSection, }; +/// The error type returned by [`Object::relocate_btf`]. #[derive(Error, Debug)] #[error("error relocating `{section}`")] pub struct BtfRelocationError { @@ -24,58 +25,91 @@ pub struct BtfRelocationError { error: RelocationError, } +/// Relocation failures #[derive(Error, Debug)] enum RelocationError { + /// I/O error #[error(transparent)] IOError(#[from] 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, }, + /// Maximum nesting level reached evaluating candidate type #[error("maximum nesting level reached evaluating candidate type `{}`", err_type_name(.type_name))] - MaximumNestingLevelReached { type_name: Option }, + MaximumNestingLevelReached { + /// The type name + type_name: Option, + }, + /// Invalid access string #[error("invalid access string `{spec}` for type `{}`: {error}", err_type_name(.type_name))] InvalidAccessIndex { + /// The type name type_name: Option, + /// 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, }, @@ -86,6 +120,7 @@ enum RelocationError { ins_index: usize, }, + /// BTF error #[error("invalid BTF")] BtfError(#[from] BtfError), } @@ -136,7 +171,7 @@ impl TryFrom for RelocationKind { } #[derive(Debug, Copy, Clone)] -pub struct Relocation { +pub(crate) struct Relocation { kind: RelocationKind, ins_offset: usize, type_id: u32, @@ -164,6 +199,7 @@ impl Relocation { } impl Object { + /// Relocate 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), @@ -172,10 +208,13 @@ impl Object { let mut candidates_cache = HashMap::>::new(); for (sec_name_off, relos) in btf_ext.relocations() { - 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 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(§ion_name) { Ok(program) => program, @@ -193,10 +232,12 @@ impl Object { match relocate_btf_program(program, relos, local_btf, target_btf, &mut candidates_cache) { Ok(_) => {} - Err(error) => return Err(BtfRelocationError { - section: section_name.to_owned(), - error, - }), + Err(error) => { + return Err(BtfRelocationError { + section: section_name.to_owned(), + error, + }) + } } } diff --git a/aya-obj/src/btf/types.rs b/aya-obj/src/btf/types.rs index f50b6d07..2f5cfc1e 100644 --- a/aya-obj/src/btf/types.rs +++ b/aya-obj/src/btf/types.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::{fmt::Display, mem, ptr}; use object::Endianness; diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index d145d2aa..22ceb1cc 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -5,7 +5,7 @@ html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg" )] #![cfg_attr(docsrs, feature(doc_cfg))] -#![deny(clippy::all)] +#![deny(clippy::all, missing_docs)] #![allow(clippy::missing_safety_doc, clippy::len_without_is_empty)] pub mod btf; @@ -16,4 +16,5 @@ pub mod programs; pub mod relocation; mod util; +pub use maps::Map; pub use obj::*; diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index 94d0b590..b54f3bbb 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -1,5 +1,7 @@ //! Map struct and type bindings. +use core::mem; + use thiserror::Error; /// Invalid map type encontered @@ -52,6 +54,7 @@ impl TryFrom for crate::generated::bpf_map_type { } } +/// BTF definition of a map #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct BtfMapDef { pub(crate) map_type: u32, @@ -60,21 +63,34 @@ pub struct BtfMapDef { 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 { - #[error("unsupported pinning type")] - Unsupported, + /// Unsupported pinning type + #[error("unsupported pinning type `{pinning_type}`")] + Unsupported { + /// The unsupported pinning type + pinning_type: u32, + }, } impl TryFrom for PinningType { @@ -84,7 +100,7 @@ impl TryFrom for PinningType { match value { 0 => Ok(PinningType::None), 1 => Ok(PinningType::ByName), - _ => Err(PinningError::Unsupported), + pinning_type => Err(PinningError::Unsupported { pinning_type }), } } } @@ -95,17 +111,191 @@ impl Default for PinningType { } } +/// 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::() * 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 { + 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, + /// 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, +} diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 46e6b1bf..cf4cb7a7 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -14,196 +14,90 @@ use std::{ }; use thiserror::Error; -use crate::relocation::*; +use crate::{ + maps::{BtfMap, LegacyMap, Map, MapKind, MINIMUM_MAP_SIZE}, + relocation::*, +}; use crate::{ - maps::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}, + maps::{bpf_map_def, BtfMapDef, PinningType}, programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType}, - maps::BtfMapDef, maps::PinningType, }; use std::slice::from_raw_parts_mut; 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::() * 5; +/// The loaded object file representation #[derive(Clone)] pub struct Object { + /// The endianness pub endianness: Endianness, + /// Program license pub license: CString, + /// Kernel version pub kernel_version: KernelVersion, + /// Program BTF pub btf: Option, + /// Program BTF.ext pub btf_ext: Option, + /// Referenced maps pub maps: HashMap, + /// Programs pub programs: HashMap, + /// Functions pub functions: HashMap, - pub relocations: HashMap>, - pub symbols_by_index: HashMap, - pub section_sizes: HashMap, + pub(crate) relocations: HashMap>, + pub(crate) symbols_by_index: HashMap, + pub(crate) section_sizes: HashMap, // symbol_offset_by_name caches symbols that could be referenced from a // BTF VAR type so the offsets can be fixed up - pub symbol_offset_by_name: HashMap, - pub text_section_index: Option, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub 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 - } - } -} - -#[derive(Debug, Clone)] -pub enum Map { - Legacy(LegacyMap), - Btf(BtfMap), -} - -impl Map { - pub fn map_type(&self) -> u32 { - match self { - Map::Legacy(m) => m.def.map_type, - Map::Btf(m) => m.def.map_type, - } - } - - pub fn key_size(&self) -> u32 { - match self { - Map::Legacy(m) => m.def.key_size, - Map::Btf(m) => m.def.key_size, - } - } - - pub fn value_size(&self) -> u32 { - match self { - Map::Legacy(m) => m.def.value_size, - Map::Btf(m) => m.def.value_size, - } - } - - pub fn max_entries(&self) -> u32 { - match self { - Map::Legacy(m) => m.def.max_entries, - Map::Btf(m) => m.def.max_entries, - } - } - - 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, - } - } - - pub fn map_flags(&self) -> u32 { - match self { - Map::Legacy(m) => m.def.map_flags, - Map::Btf(m) => m.def.map_flags, - } - } - - pub fn pinning(&self) -> PinningType { - match self { - Map::Legacy(m) => m.def.pinning, - Map::Btf(m) => m.def.pinning, - } - } - - pub fn data(&self) -> &[u8] { - match self { - Map::Legacy(m) => &m.data, - Map::Btf(m) => &m.data, - } - } - - pub fn data_mut(&mut self) -> &mut Vec { - match self { - Map::Legacy(m) => m.data.as_mut(), - Map::Btf(m) => m.data.as_mut(), - } - } - - pub fn kind(&self) -> MapKind { - match self { - Map::Legacy(m) => m.kind, - Map::Btf(m) => m.kind, - } - } - - pub fn section_index(&self) -> usize { - match self { - Map::Legacy(m) => m.section_index, - Map::Btf(m) => m.section_index, - } - } - - pub 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 def: bpf_map_def, - pub section_index: usize, - pub symbol_index: usize, - pub data: Vec, - pub kind: MapKind, -} - -#[derive(Debug, Clone)] -pub struct BtfMap { - pub def: BtfMapDef, - pub section_index: usize, - pub symbol_index: usize, - pub kind: MapKind, - pub data: Vec, + pub(crate) symbol_offset_by_name: HashMap, + pub(crate) text_section_index: Option, } +/// An eBPF program #[derive(Debug, Clone)] 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 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, + /// 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, } +/// Sections containing eBPF programs #[derive(Debug, Clone)] +#[allow(missing_docs)] pub enum ProgramSection { KRetProbe { name: String, @@ -298,6 +192,7 @@ pub enum ProgramSection { } impl ProgramSection { + /// Returns the program name pub fn name(&self) -> &str { match self { ProgramSection::KRetProbe { name } => name, @@ -520,6 +415,7 @@ impl FromStr for ProgramSection { } impl Object { + /// Parses the binary data as an object file into an [Object] pub fn parse(data: &[u8]) -> Result { let obj = object::read::File::parse(data).map_err(ParseError::ElfError)?; let endianness = obj.endianness(); @@ -603,6 +499,7 @@ impl Object { } } + /// Patches map data pub fn patch_map_data(&mut self, globals: HashMap<&str, &[u8]>) -> Result<(), ParseError> { let symbols: HashMap = self .symbols_by_index @@ -942,11 +839,14 @@ impl Object { } } +/// 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), + /// Error parsing BTF object #[error("BTF error")] BtfError(#[from] BtfError), @@ -1006,6 +906,7 @@ 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, } @@ -1169,9 +1070,12 @@ fn get_map_field(btf: &Btf, type_id: u32) -> Result { Ok(arr.len) } +/// The parsed kernel version #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum KernelVersion { + /// Specified version Version(u32), + /// Any version Any, } @@ -1314,6 +1218,7 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe Ok((map_name.to_string(), map_def)) } +/// 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 { @@ -1353,6 +1258,7 @@ pub fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map { } } +/// Copies a block of eBPF instructions pub fn copy_instructions(data: &[u8]) -> Result, ParseError> { if data.len() % mem::size_of::() > 0 { return Err(ParseError::InvalidProgramCode); diff --git a/aya-obj/src/programs/cgroup_sock.rs b/aya-obj/src/programs/cgroup_sock.rs index d30d269b..59cc254e 100644 --- a/aya-obj/src/programs/cgroup_sock.rs +++ b/aya-obj/src/programs/cgroup_sock.rs @@ -3,7 +3,7 @@ use thiserror::Error; use crate::generated::bpf_attach_type; -/// Defines where to attach a [`CgroupSock`] program. +/// Defines where to attach a `CgroupSock` program. #[derive(Copy, Clone, Debug)] pub enum CgroupSockAttachType { /// Called after the IPv4 bind events. diff --git a/aya-obj/src/programs/cgroup_sock_addr.rs b/aya-obj/src/programs/cgroup_sock_addr.rs index 61cb33c5..73ee00a2 100644 --- a/aya-obj/src/programs/cgroup_sock_addr.rs +++ b/aya-obj/src/programs/cgroup_sock_addr.rs @@ -3,7 +3,7 @@ use thiserror::Error; use crate::generated::bpf_attach_type; -/// Defines where to attach a [`CgroupSockAddr`] program. +/// Defines where to attach a `CgroupSockAddr` program. #[derive(Copy, Clone, Debug)] pub enum CgroupSockAddrAttachType { /// Attach to IPv4 bind events. diff --git a/aya-obj/src/programs/cgroup_sockopt.rs b/aya-obj/src/programs/cgroup_sockopt.rs index daa4b250..f85f52f1 100644 --- a/aya-obj/src/programs/cgroup_sockopt.rs +++ b/aya-obj/src/programs/cgroup_sockopt.rs @@ -3,7 +3,7 @@ use thiserror::Error; use crate::generated::bpf_attach_type; -/// Defines where to attach a [`CgroupSockopt`] program. +/// Defines where to attach a `CgroupSockopt` program. #[derive(Copy, Clone, Debug)] pub enum CgroupSockoptAttachType { /// Attach to GetSockopt. diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index 6f56eb60..25d5c1f9 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -1,3 +1,5 @@ +//! Program relocation handling. + use std::{collections::HashMap, mem}; use log::debug; @@ -9,11 +11,13 @@ use crate::{ bpf_insn, BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE, }, - obj::{Function, Object, Program}, Map, + maps::Map, + obj::{Function, Object, Program}, }; pub(crate) const INS_SIZE: usize = mem::size_of::(); +/// The error type returned by [`Object::relocate_maps`] and [`Object::relocate_calls`] #[derive(Error, Debug)] #[error("error relocating `{function}`")] pub struct BpfRelocationError { @@ -24,34 +28,58 @@ pub struct BpfRelocationError { error: RelocationError, } +/// Relocation failures #[derive(Debug, Error)] 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, }, + /// 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, }, } #[derive(Debug, Copy, Clone)] -pub struct Relocation { +pub(crate) struct Relocation { // byte offset of the instruction to be relocated pub(crate) offset: u64, // index of the symbol to relocate to @@ -59,7 +87,7 @@ pub struct Relocation { } #[derive(Debug, Clone)] -pub struct Symbol { +pub(crate) struct Symbol { pub(crate) index: usize, pub(crate) section_index: Option, pub(crate) name: Option, @@ -70,6 +98,7 @@ pub struct Symbol { } impl Object { + /// Relocates the map references pub fn relocate_maps<'a, I: Iterator, &'a Map)>>( &mut self, maps: I, @@ -107,6 +136,7 @@ impl Object { Ok(()) } + /// Relocates function calls pub fn relocate_calls(&mut self) -> Result<(), BpfRelocationError> { for (name, program) in self.programs.iter_mut() { let linker = FunctionLinker::new( @@ -115,12 +145,10 @@ impl Object { &self.relocations, &self.symbols_by_index, ); - linker - .link(program) - .map_err(|error| BpfRelocationError{ - function: name.to_owned(), - error, - })?; + linker.link(program).map_err(|error| BpfRelocationError { + function: name.to_owned(), + error, + })?; } Ok(()) @@ -444,11 +472,7 @@ fn insn_is_call(ins: &bpf_insn) -> bool { #[cfg(test)] mod test { - use crate::{ - maps::bpf_map_def, - obj::{self, BtfMap, LegacyMap, MapKind}, - maps::BtfMapDef, - }; + use crate::maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, MapKind}; use super::*; @@ -469,7 +493,7 @@ mod test { } fn fake_legacy_map(symbol_index: usize) -> Map { - obj::Map::Legacy(LegacyMap { + Map::Legacy(LegacyMap { def: bpf_map_def { ..Default::default() }, @@ -481,7 +505,7 @@ mod test { } fn fake_btf_map(symbol_index: usize) -> Map { - obj::Map::Btf(BtfMap { + Map::Btf(BtfMap { def: BtfMapDef { ..Default::default() }, @@ -578,8 +602,10 @@ mod test { 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))]); + 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, @@ -673,8 +699,10 @@ mod test { 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))]); + 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, diff --git a/aya-obj/src/util.rs b/aya-obj/src/util.rs index 6c208ec9..4aa177bb 100644 --- a/aya-obj/src/util.rs +++ b/aya-obj/src/util.rs @@ -4,4 +4,4 @@ use core::{mem, slice}; pub(crate) unsafe fn bytes_of(val: &T) -> &[u8] { let size = mem::size_of::(); slice::from_raw_parts(slice::from_ref(val).as_ptr().cast(), size) -} \ No newline at end of file +} diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index d1988f67..3f765b9a 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -7,7 +7,10 @@ use std::{ path::{Path, PathBuf}, }; -use aya_obj::{btf::{BtfFeatures, BtfRelocationError}, relocation::BpfRelocationError}; +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,9 +62,7 @@ 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 Pod for [T; N] {} -pub use aya_obj::maps::bpf_map_def; -pub use aya_obj::maps::BtfMapDef; -pub use aya_obj::maps::PinningType; +pub use aya_obj::maps::{bpf_map_def, PinningType}; // Features implements BPF and BTF feature detection #[derive(Default, Debug)] @@ -358,14 +360,9 @@ impl<'a> BpfLoader<'a> { obj.patch_map_data(self.globals.clone())?; let btf_fd = if let Some(ref btf) = 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(§ion_data, &symbol_offsets, 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 } @@ -441,7 +438,10 @@ impl<'a> BpfLoader<'a> { maps.insert(name, map); } - obj.relocate_maps(maps.iter().map(|(s, data)| (s.as_str(), data.fd, &data.obj)))?; + obj.relocate_maps( + maps.iter() + .map(|(s, data)| (s.as_str(), data.fd, &data.obj)), + )?; obj.relocate_calls()?; let programs = obj diff --git a/aya/src/maps/bloom_filter.rs b/aya/src/maps/bloom_filter.rs index 615e64c7..16eb1aa3 100644 --- a/aya/src/maps/bloom_filter.rs +++ b/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, diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs index e7045466..a67cdead 100644 --- a/aya/src/maps/hash_map/hash_map.rs +++ b/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, diff --git a/aya/src/maps/lpm_trie.rs b/aya/src/maps/lpm_trie.rs index a7d43703..4a6cc94f 100644 --- a/aya/src/maps/lpm_trie.rs +++ b/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::>() 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, diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 783cc3db..e9d3009b 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -844,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, diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index de4c1f43..97231f17 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -9,8 +9,7 @@ use std::{ use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT, programs::{ - 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}, };