Merge pull request #475 from yesh0/aya-obj

aya-obj: move code for object file loading and relocation into a separate crate
pull/402/head
Alessandro Decina 2 years ago committed by GitHub
commit 897957ac84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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"

@ -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"]

@ -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

@ -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();

@ -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()
}
}

@ -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::*;

@ -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),
}

@ -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]"),

@ -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;

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

@ -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::*;

@ -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>,
}

@ -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(_))
))
}

@ -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())),
}
}
}

@ -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())),
}
}
}

@ -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())),
}
}
}

@ -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;

@ -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,

@ -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)
}

@ -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)

@ -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"

@ -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")]

@ -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;

@ -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,

@ -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,

@ -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,

@ -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,

@ -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,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,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())),
}
}
}

@ -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())),
}
}
}

@ -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"

@ -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;

@ -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
}

@ -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 = || {

Loading…
Cancel
Save