Merge pull request #140 from dave-tucker/btf-maps

Support Parsing of BTF Maps
pull/348/head
Dave Tucker 2 years ago committed by GitHub
commit 73ee3cff70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,7 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
convert::TryFrom,
error::Error, error::Error,
ffi::CString, ffi::CString,
fs, io, fs, io,
@ -73,14 +74,43 @@ pub(crate) struct bpf_map_def {
pub(crate) pinning: PinningType, 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)] #[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum PinningType { pub(crate) enum PinningType {
None = 0, None = 0,
#[allow(dead_code)] // ByName is constructed from the BPF side
ByName = 1, 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 { impl Default for PinningType {
fn default() -> Self { fn default() -> Self {
PinningType::None PinningType::None
@ -340,21 +370,23 @@ impl<'a> BpfLoader<'a> {
let mut maps = HashMap::new(); let mut maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() { for (name, mut obj) in obj.maps.drain() {
if obj.def.map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 && obj.def.max_entries == 0 if obj.map_type() == BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 && obj.max_entries() == 0 {
{ obj.set_max_entries(
obj.def.max_entries = possible_cpus() possible_cpus()
.map_err(|error| BpfError::FileError { .map_err(|error| BpfError::FileError {
path: PathBuf::from(POSSIBLE_CPUS), path: PathBuf::from(POSSIBLE_CPUS),
error, error,
})? })?
.len() as u32; .len() as u32,
);
} }
let mut map = Map { let mut map = Map {
obj, obj,
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd,
}; };
let fd = match map.obj.def.pinning { let fd = match map.obj.pinning() {
PinningType::ByName => { PinningType::ByName => {
let path = match &self.map_pin_path { let path = match &self.map_pin_path {
Some(p) => p, Some(p) => p,
@ -375,16 +407,15 @@ impl<'a> BpfLoader<'a> {
} }
PinningType::None => map.create(&name)?, PinningType::None => map.create(&name)?,
}; };
if !map.obj.data.is_empty() && map.obj.kind != MapKind::Bss { if !map.obj.data().is_empty() && map.obj.kind() != MapKind::Bss {
bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_mut_ptr(), 0).map_err( bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data_mut().as_mut_ptr(), 0)
|(code, io_error)| MapError::SyscallError { .map_err(|(code, io_error)| MapError::SyscallError {
call: "bpf_map_update_elem".to_owned(), call: "bpf_map_update_elem".to_owned(),
code, code,
io_error, io_error,
}, })?;
)?;
} }
if map.obj.kind == MapKind::Rodata { if map.obj.kind() == MapKind::Rodata {
bpf_map_freeze(fd).map_err(|(code, io_error)| MapError::SyscallError { bpf_map_freeze(fd).map_err(|(code, io_error)| MapError::SyscallError {
call: "bpf_map_freeze".to_owned(), call: "bpf_map_freeze".to_owned(),
code, code,
@ -804,6 +835,10 @@ pub enum BpfError {
error: Box<dyn Error + Send + Sync>, error: Box<dyn Error + Send + Sync>,
}, },
/// No BTF parsed for object
#[error("no BTF parsed for object")]
NoBTF,
#[error("map error")] #[error("map error")]
/// A map error /// A map error
MapError(#[from] MapError), MapError(#[from] MapError),

@ -40,20 +40,20 @@ pub struct Array<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> Array<T, V> { impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
fn new(map: T) -> Result<Array<T, V>, MapError> { fn new(map: T) -> Result<Array<T, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_ARRAY as u32 { if map_type != BPF_MAP_TYPE_ARRAY as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = mem::size_of::<u32>(); let expected = mem::size_of::<u32>();
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<V>(); let expected = mem::size_of::<V>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -69,7 +69,7 @@ impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
/// ///
/// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
pub fn len(&self) -> u32 { pub fn len(&self) -> u32 {
self.inner.obj.def.max_entries self.inner.obj.max_entries()
} }
/// Returns the value stored at the given index. /// Returns the value stored at the given index.
@ -99,8 +99,8 @@ impl<T: Deref<Target = Map>, V: Pod> Array<T, V> {
} }
fn check_bounds(&self, index: u32) -> Result<(), MapError> { fn check_bounds(&self, index: u32) -> Result<(), MapError> {
let max_entries = self.inner.obj.def.max_entries; let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.def.max_entries { if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries }) Err(MapError::OutOfBounds { index, max_entries })
} else { } else {
Ok(()) Ok(())

@ -59,20 +59,20 @@ pub struct PerCpuArray<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> { impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
fn new(map: T) -> Result<PerCpuArray<T, V>, MapError> { fn new(map: T) -> Result<PerCpuArray<T, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_PERCPU_ARRAY as u32 { if map_type != BPF_MAP_TYPE_PERCPU_ARRAY as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = mem::size_of::<u32>(); let expected = mem::size_of::<u32>();
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<V>(); let expected = mem::size_of::<V>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -88,7 +88,7 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
/// ///
/// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
pub fn len(&self) -> u32 { pub fn len(&self) -> u32 {
self.inner.obj.def.max_entries self.inner.obj.max_entries()
} }
/// Returns a slice of values - one for each CPU - stored at the given index. /// Returns a slice of values - one for each CPU - stored at the given index.
@ -118,8 +118,8 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
} }
fn check_bounds(&self, index: u32) -> Result<(), MapError> { fn check_bounds(&self, index: u32) -> Result<(), MapError> {
let max_entries = self.inner.obj.def.max_entries; let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.def.max_entries { if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries }) Err(MapError::OutOfBounds { index, max_entries })
} else { } else {
Ok(()) Ok(())

@ -57,20 +57,20 @@ pub struct ProgramArray<T: Deref<Target = Map>> {
impl<T: Deref<Target = Map>> ProgramArray<T> { impl<T: Deref<Target = Map>> ProgramArray<T> {
fn new(map: T) -> Result<ProgramArray<T>, MapError> { fn new(map: T) -> Result<ProgramArray<T>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_PROG_ARRAY as u32 { if map_type != BPF_MAP_TYPE_PROG_ARRAY as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = mem::size_of::<u32>(); let expected = mem::size_of::<u32>();
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<RawFd>(); let expected = mem::size_of::<RawFd>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -86,8 +86,8 @@ impl<T: Deref<Target = Map>> ProgramArray<T> {
} }
fn check_bounds(&self, index: u32) -> Result<(), MapError> { fn check_bounds(&self, index: u32) -> Result<(), MapError> {
let max_entries = self.inner.obj.def.max_entries; let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.def.max_entries { if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries }) Err(MapError::OutOfBounds { index, max_entries })
} else { } else {
Ok(()) Ok(())

@ -41,7 +41,7 @@ pub struct BloomFilter<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> { impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
pub(crate) fn new(map: T) -> Result<BloomFilter<T, V>, MapError> { pub(crate) fn new(map: T) -> Result<BloomFilter<T, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
// validate the map definition // validate the map definition
if map_type != BPF_MAP_TYPE_BLOOM_FILTER as u32 { if map_type != BPF_MAP_TYPE_BLOOM_FILTER as u32 {
@ -51,7 +51,7 @@ impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
} }
let size = mem::size_of::<V>(); let size = mem::size_of::<V>();
let expected = map.obj.def.value_size as usize; let expected = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
}; };
@ -140,7 +140,7 @@ mod tests {
use std::io; use std::io;
fn new_obj_map() -> obj::Map { fn new_obj_map() -> obj::Map {
obj::Map { obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_BLOOM_FILTER as u32, map_type: BPF_MAP_TYPE_BLOOM_FILTER as u32,
key_size: 4, key_size: 4,
@ -152,7 +152,7 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
} })
} }
fn sys_error(value: i32) -> SysResult { fn sys_error(value: i32) -> SysResult {
@ -165,6 +165,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
BloomFilter::<_, u16>::new(&map), BloomFilter::<_, u16>::new(&map),
@ -178,7 +179,7 @@ mod tests {
#[test] #[test]
fn test_try_from_wrong_map() { fn test_try_from_wrong_map() {
let map = Map { let map = Map {
obj: obj::Map { obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32, map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4, key_size: 4,
@ -190,9 +191,10 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
}, }),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
@ -207,6 +209,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
@ -221,6 +224,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(BloomFilter::<_, u32>::new(&mut map).is_ok()); assert!(BloomFilter::<_, u32>::new(&mut map).is_ok());
@ -232,6 +236,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(BloomFilter::<_, u32>::try_from(&map).is_ok()) assert!(BloomFilter::<_, u32>::try_from(&map).is_ok())
} }
@ -244,6 +249,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap(); let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
@ -267,6 +273,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap(); let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
@ -280,6 +287,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap(); let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
@ -302,6 +310,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap(); let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();

@ -42,7 +42,7 @@ pub struct HashMap<T: Deref<Target = Map>, K, V> {
impl<T: Deref<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> { impl<T: Deref<Target = Map>, K: Pod, V: Pod> HashMap<T, K, V> {
pub(crate) fn new(map: T) -> Result<HashMap<T, K, V>, MapError> { pub(crate) fn new(map: T) -> Result<HashMap<T, K, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
// validate the map definition // validate the map definition
if map_type != BPF_MAP_TYPE_HASH as u32 && map_type != BPF_MAP_TYPE_LRU_HASH as u32 { if map_type != BPF_MAP_TYPE_HASH as u32 && map_type != BPF_MAP_TYPE_LRU_HASH as u32 {
@ -159,7 +159,7 @@ mod tests {
use super::*; use super::*;
fn new_obj_map() -> obj::Map { fn new_obj_map() -> obj::Map {
obj::Map { obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_HASH as u32, map_type: BPF_MAP_TYPE_HASH as u32,
key_size: 4, key_size: 4,
@ -171,7 +171,7 @@ mod tests {
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
symbol_index: 0, symbol_index: 0,
} })
} }
fn sys_error(value: i32) -> SysResult { fn sys_error(value: i32) -> SysResult {
@ -184,6 +184,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
HashMap::<_, u8, u32>::new(&map), HashMap::<_, u8, u32>::new(&map),
@ -200,6 +201,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
HashMap::<_, u32, u16>::new(&map), HashMap::<_, u32, u16>::new(&map),
@ -213,7 +215,7 @@ mod tests {
#[test] #[test]
fn test_try_from_wrong_map() { fn test_try_from_wrong_map() {
let map = Map { let map = Map {
obj: obj::Map { obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32, map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4, key_size: 4,
@ -225,9 +227,10 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
}, }),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
@ -242,6 +245,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
@ -256,6 +260,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok()); assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok());
@ -267,6 +272,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
} }
@ -274,7 +280,7 @@ mod tests {
#[test] #[test]
fn test_try_from_ok_lru() { fn test_try_from_ok_lru() {
let map = Map { let map = Map {
obj: obj::Map { obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_LRU_HASH as u32, map_type: BPF_MAP_TYPE_LRU_HASH as u32,
key_size: 4, key_size: 4,
@ -286,9 +292,10 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
}, }),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
@ -302,6 +309,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -325,6 +333,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -339,6 +348,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -362,6 +372,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -375,6 +386,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -397,6 +409,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -433,6 +446,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>(); let keys = hm.keys().collect::<Result<Vec<_>, _>>();
@ -477,6 +491,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -505,6 +520,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -535,6 +551,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap(); let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
@ -568,6 +585,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -602,6 +620,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -642,6 +661,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();

@ -15,12 +15,12 @@ pub use per_cpu_hash_map::*;
pub(crate) fn check_kv_size<K, V>(map: &Map) -> Result<(), MapError> { pub(crate) fn check_kv_size<K, V>(map: &Map) -> Result<(), MapError> {
let size = mem::size_of::<K>(); let size = mem::size_of::<K>();
let expected = map.obj.def.key_size as usize; let expected = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let size = mem::size_of::<V>(); let size = mem::size_of::<V>();
let expected = map.obj.def.value_size as usize; let expected = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
}; };

@ -52,7 +52,7 @@ pub struct PerCpuHashMap<T: Deref<Target = Map>, K: Pod, V: Pod> {
impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> { impl<T: Deref<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
pub(crate) fn new(map: T) -> Result<PerCpuHashMap<T, K, V>, MapError> { pub(crate) fn new(map: T) -> Result<PerCpuHashMap<T, K, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
// validate the map definition // validate the map definition
if map_type != BPF_MAP_TYPE_PERCPU_HASH as u32 if map_type != BPF_MAP_TYPE_PERCPU_HASH as u32

@ -101,7 +101,7 @@ unsafe impl<K: Pod> Pod for Key<K> {}
impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> { impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
pub(crate) fn new(map: T) -> Result<LpmTrie<T, K, V>, MapError> { pub(crate) fn new(map: T) -> Result<LpmTrie<T, K, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
// validate the map definition // validate the map definition
if map_type != BPF_MAP_TYPE_LPM_TRIE as u32 { if map_type != BPF_MAP_TYPE_LPM_TRIE as u32 {
@ -110,12 +110,12 @@ impl<T: Deref<Target = Map>, K: Pod, V: Pod> LpmTrie<T, K, V> {
}); });
} }
let size = mem::size_of::<Key<K>>(); let size = mem::size_of::<Key<K>>();
let expected = map.obj.def.key_size as usize; let expected = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let size = mem::size_of::<V>(); let size = mem::size_of::<V>();
let expected = map.obj.def.value_size as usize; let expected = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
}; };
@ -230,7 +230,7 @@ mod tests {
use std::{io, mem, net::Ipv4Addr}; use std::{io, mem, net::Ipv4Addr};
fn new_obj_map() -> obj::Map { fn new_obj_map() -> obj::Map {
obj::Map { obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_LPM_TRIE as u32, map_type: BPF_MAP_TYPE_LPM_TRIE as u32,
key_size: mem::size_of::<Key<u32>>() as u32, key_size: mem::size_of::<Key<u32>>() as u32,
@ -242,7 +242,7 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
} })
} }
fn sys_error(value: i32) -> SysResult { fn sys_error(value: i32) -> SysResult {
@ -255,6 +255,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
LpmTrie::<_, u16, u32>::new(&map), LpmTrie::<_, u16, u32>::new(&map),
@ -271,6 +272,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
LpmTrie::<_, u32, u16>::new(&map), LpmTrie::<_, u32, u16>::new(&map),
@ -284,7 +286,7 @@ mod tests {
#[test] #[test]
fn test_try_from_wrong_map() { fn test_try_from_wrong_map() {
let map = Map { let map = Map {
obj: obj::Map { obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32, map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4, key_size: 4,
@ -296,8 +298,9 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: obj::MapKind::Other, kind: obj::MapKind::Other,
}, }),
fd: None, fd: None,
btf_fd: None,
pinned: false, pinned: false,
}; };
@ -313,6 +316,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(matches!( assert!(matches!(
@ -327,6 +331,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(LpmTrie::<_, u32, u32>::new(&mut map).is_ok()); assert!(LpmTrie::<_, u32, u32>::new(&mut map).is_ok());
@ -338,6 +343,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
assert!(LpmTrie::<_, u32, u32>::try_from(&map).is_ok()) assert!(LpmTrie::<_, u32, u32>::try_from(&map).is_ok())
} }
@ -350,6 +356,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8); let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -374,6 +381,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
@ -390,6 +398,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8); let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -414,6 +423,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8); let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -428,6 +438,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8); let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -452,6 +463,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: Some(42), fd: Some(42),
pinned: false, pinned: false,
btf_fd: None,
}; };
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap(); let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8); let ipaddr = Ipv4Addr::new(8, 8, 8, 8);

@ -266,6 +266,7 @@ fn maybe_warn_rlimit() {
pub struct Map { pub struct Map {
pub(crate) obj: obj::Map, pub(crate) obj: obj::Map,
pub(crate) fd: Option<RawFd>, pub(crate) fd: Option<RawFd>,
pub(crate) btf_fd: Option<RawFd>,
/// Indicates if this map has been pinned to bpffs /// Indicates if this map has been pinned to bpffs
pub pinned: bool, pub pinned: bool,
} }
@ -279,7 +280,7 @@ impl Map {
let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?; let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
let fd = bpf_create_map(&c_name, &self.obj.def).map_err(|(code, io_error)| { let fd = bpf_create_map(&c_name, &self.obj, self.btf_fd).map_err(|(code, io_error)| {
let k_ver = kernel_version().unwrap(); let k_ver = kernel_version().unwrap();
if k_ver < (5, 11, 0) { if k_ver < (5, 11, 0) {
maybe_warn_rlimit(); maybe_warn_rlimit();
@ -327,7 +328,7 @@ impl Map {
/// Returns the [`bpf_map_type`] of this map /// Returns the [`bpf_map_type`] of this map
pub fn map_type(&self) -> Result<bpf_map_type, MapError> { pub fn map_type(&self) -> Result<bpf_map_type, MapError> {
bpf_map_type::try_from(self.obj.def.map_type) bpf_map_type::try_from(self.obj.map_type())
} }
pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> { pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> {
@ -625,7 +626,7 @@ mod tests {
use super::*; use super::*;
fn new_obj_map() -> obj::Map { fn new_obj_map() -> obj::Map {
obj::Map { obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_HASH as u32, map_type: BPF_MAP_TYPE_HASH as u32,
key_size: 4, key_size: 4,
@ -637,7 +638,7 @@ mod tests {
symbol_index: 0, symbol_index: 0,
data: Vec::new(), data: Vec::new(),
kind: MapKind::Other, kind: MapKind::Other,
} })
} }
fn new_map() -> Map { fn new_map() -> Map {
@ -645,6 +646,7 @@ mod tests {
obj: new_obj_map(), obj: new_obj_map(),
fd: None, fd: None,
pinned: false, pinned: false,
btf_fd: None,
} }
} }

@ -164,7 +164,7 @@ pub struct PerfEventArray<T: DerefMut<Target = Map>> {
impl<T: DerefMut<Target = Map>> PerfEventArray<T> { impl<T: DerefMut<Target = Map>> PerfEventArray<T> {
pub(crate) fn new(map: T) -> Result<PerfEventArray<T>, MapError> { pub(crate) fn new(map: T) -> Result<PerfEventArray<T>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 { if map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,

@ -39,20 +39,20 @@ pub struct Queue<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> Queue<T, V> { impl<T: Deref<Target = Map>, V: Pod> Queue<T, V> {
fn new(map: T) -> Result<Queue<T, V>, MapError> { fn new(map: T) -> Result<Queue<T, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_QUEUE as u32 { if map_type != BPF_MAP_TYPE_QUEUE as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = 0; let expected = 0;
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<V>(); let expected = mem::size_of::<V>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -68,7 +68,7 @@ impl<T: Deref<Target = Map>, V: Pod> Queue<T, V> {
/// ///
/// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
pub fn capacity(&self) -> u32 { pub fn capacity(&self) -> u32 {
self.inner.obj.def.max_entries self.inner.obj.max_entries()
} }
} }

@ -69,7 +69,7 @@ pub struct SockHash<T: Deref<Target = Map>, K> {
impl<T: Deref<Target = Map>, K: Pod> SockHash<T, K> { impl<T: Deref<Target = Map>, K: Pod> SockHash<T, K> {
pub(crate) fn new(map: T) -> Result<SockHash<T, K>, MapError> { pub(crate) fn new(map: T) -> Result<SockHash<T, K>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
// validate the map definition // validate the map definition
if map_type != BPF_MAP_TYPE_SOCKHASH as u32 { if map_type != BPF_MAP_TYPE_SOCKHASH as u32 {

@ -47,20 +47,20 @@ pub struct SockMap<T: Deref<Target = Map>> {
impl<T: Deref<Target = Map>> SockMap<T> { impl<T: Deref<Target = Map>> SockMap<T> {
fn new(map: T) -> Result<SockMap<T>, MapError> { fn new(map: T) -> Result<SockMap<T>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_SOCKMAP as u32 { if map_type != BPF_MAP_TYPE_SOCKMAP as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = mem::size_of::<u32>(); let expected = mem::size_of::<u32>();
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<RawFd>(); let expected = mem::size_of::<RawFd>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -76,8 +76,8 @@ impl<T: Deref<Target = Map>> SockMap<T> {
} }
fn check_bounds(&self, index: u32) -> Result<(), MapError> { fn check_bounds(&self, index: u32) -> Result<(), MapError> {
let max_entries = self.inner.obj.def.max_entries; let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.def.max_entries { if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries }) Err(MapError::OutOfBounds { index, max_entries })
} else { } else {
Ok(()) Ok(())

@ -39,20 +39,20 @@ pub struct Stack<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> { impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> {
fn new(map: T) -> Result<Stack<T, V>, MapError> { fn new(map: T) -> Result<Stack<T, V>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_STACK as u32 { if map_type != BPF_MAP_TYPE_STACK as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = 0; let expected = 0;
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
let expected = mem::size_of::<V>(); let expected = mem::size_of::<V>();
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }
@ -68,7 +68,7 @@ impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> {
/// ///
/// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
pub fn capacity(&self) -> u32 { pub fn capacity(&self) -> u32 {
self.inner.obj.def.max_entries self.inner.obj.max_entries()
} }
} }

@ -73,14 +73,14 @@ pub struct StackTraceMap<T> {
impl<T: Deref<Target = Map>> StackTraceMap<T> { impl<T: Deref<Target = Map>> StackTraceMap<T> {
fn new(map: T) -> Result<StackTraceMap<T>, MapError> { fn new(map: T) -> Result<StackTraceMap<T>, MapError> {
let map_type = map.obj.def.map_type; let map_type = map.obj.map_type();
if map_type != BPF_MAP_TYPE_STACK_TRACE as u32 { if map_type != BPF_MAP_TYPE_STACK_TRACE as u32 {
return Err(MapError::InvalidMapType { return Err(MapError::InvalidMapType {
map_type: map_type as u32, map_type: map_type as u32,
}); });
} }
let expected = mem::size_of::<u32>(); let expected = mem::size_of::<u32>();
let size = map.obj.def.key_size as usize; let size = map.obj.key_size() as usize;
if size != expected { if size != expected {
return Err(MapError::InvalidKeySize { size, expected }); return Err(MapError::InvalidKeySize { size, expected });
} }
@ -93,7 +93,7 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
io_error, io_error,
} }
})?; })?;
let size = map.obj.def.value_size as usize; let size = map.obj.value_size() as usize;
if size > max_stack_depth * mem::size_of::<u64>() { if size > max_stack_depth * mem::size_of::<u64>() {
return Err(MapError::InvalidValueSize { size, expected }); return Err(MapError::InvalidValueSize { size, expected });
} }

@ -1,6 +1,7 @@
pub(crate) mod btf; pub(crate) mod btf;
mod relocation; mod relocation;
use log::debug;
use object::{ use object::{
read::{Object as ElfObject, ObjectSection, Section as ObjSection}, read::{Object as ElfObject, ObjectSection, Section as ObjSection},
Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind, Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind,
@ -19,14 +20,14 @@ use relocation::*;
use crate::{ use crate::{
bpf_map_def, bpf_map_def,
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY, BPF_F_RDONLY_PROG}, generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY, btf_var_secinfo, BPF_F_RDONLY_PROG},
obj::btf::{Btf, BtfError, BtfExt}, obj::btf::{Btf, BtfError, BtfExt, BtfType},
programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType}, programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType},
BpfError, BpfError, BtfMapDef, PinningType,
}; };
use std::slice::from_raw_parts_mut; use std::slice::from_raw_parts_mut;
use self::btf::{FuncSecInfo, LineSecInfo}; use self::btf::{BtfKind, FuncSecInfo, LineSecInfo};
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
/// The first five __u32 of `bpf_map_def` must be defined. /// The first five __u32 of `bpf_map_def` must be defined.
@ -51,7 +52,7 @@ pub struct Object {
pub(crate) text_section_index: Option<usize>, pub(crate) text_section_index: Option<usize>,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum MapKind { pub(crate) enum MapKind {
Bss, Bss,
Data, Data,
@ -74,7 +75,99 @@ impl From<&str> for MapKind {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Map { 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) def: bpf_map_def,
pub(crate) section_index: usize, pub(crate) section_index: usize,
pub(crate) symbol_index: usize, pub(crate) symbol_index: usize,
@ -82,6 +175,15 @@ pub struct Map {
pub(crate) kind: MapKind, 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)] #[derive(Debug, Clone)]
pub(crate) struct Program { pub(crate) struct Program {
pub(crate) license: CString, pub(crate) license: CString,
@ -519,20 +621,20 @@ impl Object {
.iter_mut() .iter_mut()
// assumption: there is only one map created per section where we're trying to // assumption: there is only one map created per section where we're trying to
// patch data. this assumption holds true for the .rodata section at least // patch data. this assumption holds true for the .rodata section at least
.find(|(_, m)| symbol.section_index == Some(m.section_index)) .find(|(_, m)| symbol.section_index == Some(m.section_index()))
.ok_or_else(|| ParseError::MapNotFound { .ok_or_else(|| ParseError::MapNotFound {
index: symbol.section_index.unwrap_or(0), index: symbol.section_index.unwrap_or(0),
})?; })?;
let start = symbol.address as usize; let start = symbol.address as usize;
let end = start + symbol.size as usize; let end = start + symbol.size as usize;
if start > end || end > map.data.len() { if start > end || end > map.data().len() {
return Err(ParseError::InvalidGlobalData { return Err(ParseError::InvalidGlobalData {
name: name.to_string(), name: name.to_string(),
sym_size: symbol.size, sym_size: symbol.size,
data_size: data.len(), data_size: data.len(),
}); });
} }
map.data.splice(start..end, data.iter().cloned()); map.data_mut().splice(start..end, data.iter().cloned());
} else { } else {
return Err(ParseError::SymbolNotFound { return Err(ParseError::SymbolNotFound {
name: name.to_owned(), name: name.to_owned(),
@ -706,18 +808,63 @@ impl Object {
let def = parse_map_def(name, data)?; let def = parse_map_def(name, data)?;
self.maps.insert( self.maps.insert(
name.to_string(), name.to_string(),
Map { Map::Legacy(LegacyMap {
section_index: section.index.0, section_index: section.index.0,
symbol_index: sym.index, symbol_index: sym.index,
def, def,
data: Vec::new(), data: Vec::new(),
kind: MapKind::Other, kind: MapKind::Other,
}, }),
); );
} }
Ok(()) Ok(())
} }
fn parse_btf_maps(
&mut self,
section: &Section,
symbols: HashMap<String, Symbol>,
) -> Result<(), BpfError> {
if self.btf.is_none() {
return Err(BpfError::NoBTF);
}
let btf = self.btf.as_ref().unwrap();
for t in btf.types() {
if let BtfType::DataSec(_, sec_info) = &t {
let type_name = match btf.type_name(t) {
Ok(Some(name)) => name,
_ => continue,
};
if type_name == section.name {
// each btf_var_secinfo contains a map
for info in sec_info {
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(),
})
})?
.index;
self.maps.insert(
map_name,
Map::Btf(BtfMap {
def,
section_index: section.index.0,
symbol_index,
kind: MapKind::Other,
data: Vec::new(),
}),
);
}
}
}
}
Ok(())
}
fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> { fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> {
let mut parts = section.name.rsplitn(2, '/').collect::<Vec<_>>(); let mut parts = section.name.rsplitn(2, '/').collect::<Vec<_>>();
parts.reverse(); parts.reverse();
@ -740,6 +887,22 @@ impl Object {
BpfSectionKind::Text => self.parse_text_section(section)?, BpfSectionKind::Text => self.parse_text_section(section)?,
BpfSectionKind::Btf => self.parse_btf(&section)?, BpfSectionKind::Btf => self.parse_btf(&section)?,
BpfSectionKind::BtfExt => self.parse_btf_ext(&section)?, BpfSectionKind::BtfExt => self.parse_btf_ext(&section)?,
BpfSectionKind::BtfMaps => {
let symbols: HashMap<String, Symbol> = self
.symbols_by_index
.values()
.filter(|s| {
if let Some(idx) = s.section_index {
idx == section.index.0 && s.name.is_some()
} else {
false
}
})
.cloned()
.map(|s| (s.name.as_ref().unwrap().to_string(), s))
.collect();
self.parse_btf_maps(&section, symbols)?
}
BpfSectionKind::Maps => { BpfSectionKind::Maps => {
let symbols: Vec<Symbol> = self let symbols: Vec<Symbol> = self
.symbols_by_index .symbols_by_index
@ -770,10 +933,7 @@ impl Object {
); );
} }
} }
BpfSectionKind::Undefined BpfSectionKind::Undefined | BpfSectionKind::License | BpfSectionKind::Version => {}
| BpfSectionKind::BtfMaps
| BpfSectionKind::License
| BpfSectionKind::Version => {}
} }
Ok(()) Ok(())
@ -977,6 +1137,30 @@ fn parse_version(data: &[u8], endianness: object::Endianness) -> Result<KernelVe
}) })
} }
// Gets an integer value from a BTF map defintion K/V pair.
// type_id should be a PTR to an ARRAY.
// the value is encoded in the array nr_elems field.
fn get_map_field(btf: &Btf, type_id: u32) -> Result<u32, BtfError> {
let pty = match &btf.type_by_id(type_id)? {
BtfType::Ptr(pty) => pty,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.kind()?.unwrap_or(BtfKind::Unknown) as u32,
})
}
};
// Safety: union
let arr = match &btf.type_by_id(unsafe { pty.__bindgen_anon_1.type_ })? {
BtfType::Array(_, arr) => arr,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.kind()?.unwrap_or(BtfKind::Unknown) as u32,
})
}
};
Ok(arr.nelems)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum KernelVersion { pub enum KernelVersion {
Version(u32), Version(u32),
@ -1014,13 +1198,13 @@ fn parse_map(section: &Section, name: &str) -> Result<Map, ParseError> {
} }
MapKind::Other => (parse_map_def(name, section.data)?, Vec::new()), MapKind::Other => (parse_map_def(name, section.data)?, Vec::new()),
}; };
Ok(Map { Ok(Map::Legacy(LegacyMap {
section_index: section.index.0, section_index: section.index.0,
symbol_index: 0, symbol_index: 0,
def, def,
data, data,
kind, kind,
}) }))
} }
fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> { fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
@ -1043,6 +1227,82 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
} }
} }
fn parse_btf_map_def(btf: &Btf, info: &btf_var_secinfo) -> Result<(String, BtfMapDef), BtfError> {
let ty = match btf.type_by_id(info.type_)? {
BtfType::Var(ty, _) => ty,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.kind()?.unwrap_or(BtfKind::Unknown) as u32,
})
}
};
let map_name = btf.string_at(ty.name_off)?;
let mut map_def = BtfMapDef::default();
// Safety: union
let root_type = btf.resolve_type(unsafe { ty.__bindgen_anon_1.type_ })?;
let members = match btf.type_by_id(root_type)? {
BtfType::Struct(_, members) => members,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.kind()?.unwrap_or(BtfKind::Unknown) as u32,
})
}
};
for m in members {
match btf.string_at(m.name_off)?.as_ref() {
"type" => {
map_def.map_type = get_map_field(btf, m.type_)?;
}
"key" => {
if let BtfType::Ptr(pty) = btf.type_by_id(m.type_)? {
// Safety: union
let t = unsafe { pty.__bindgen_anon_1.type_ };
map_def.key_size = btf.type_size(t)? as u32;
map_def.btf_key_type_id = t;
} else {
return Err(BtfError::UnexpectedBtfType { type_id: m.type_ });
}
}
"key_size" => {
map_def.key_size = get_map_field(btf, m.type_)?;
}
"value" => {
if let BtfType::Ptr(pty) = btf.type_by_id(m.type_)? {
// Safety: union
let t = unsafe { pty.__bindgen_anon_1.type_ };
map_def.value_size = btf.type_size(t)? as u32;
map_def.btf_value_type_id = t;
} else {
return Err(BtfError::UnexpectedBtfType { type_id: m.type_ });
}
}
"value_size" => {
map_def.value_size = get_map_field(btf, m.type_)?;
}
"max_entries" => {
map_def.max_entries = get_map_field(btf, m.type_)?;
}
"map_flags" => {
map_def.map_flags = get_map_field(btf, m.type_)?;
}
"pinning" => {
let pinning = get_map_field(btf, m.type_)?;
map_def.pinning = PinningType::try_from(pinning).unwrap_or_else(|_| {
debug!("{} is not a valid pin type. using PIN_NONE", pinning);
PinningType::None
});
}
other => {
debug!("skipping unknown map section: {}", other);
continue;
}
}
}
Ok((map_name.to_string(), map_def))
}
pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> { pub(crate) fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
if data.len() % mem::size_of::<bpf_insn>() > 0 { if data.len() % mem::size_of::<bpf_insn>() > 0 {
return Err(ParseError::InvalidProgramCode); return Err(ParseError::InvalidProgramCode);
@ -1187,6 +1447,7 @@ mod tests {
map_flags: 5, map_flags: 5,
id: 0, id: 0,
pinning: PinningType::None, pinning: PinningType::None,
..Default::default()
}; };
assert_eq!( assert_eq!(
@ -1205,6 +1466,7 @@ mod tests {
map_flags: 5, map_flags: 5,
id: 6, id: 6,
pinning: PinningType::ByName, pinning: PinningType::ByName,
..Default::default()
}; };
assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def); assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
@ -1220,6 +1482,7 @@ mod tests {
map_flags: 5, map_flags: 5,
id: 6, id: 6,
pinning: PinningType::ByName, pinning: PinningType::ByName,
..Default::default()
}; };
let mut buf = [0u8; 128]; let mut buf = [0u8; 128];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) }; unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) };
@ -1250,11 +1513,12 @@ mod tests {
map_flags: 5, map_flags: 5,
id: 0, id: 0,
pinning: PinningType::None, pinning: PinningType::None,
..Default::default()
}) })
), ),
"foo" "foo"
), ),
Ok(Map { Ok(Map::Legacy(LegacyMap{
section_index: 0, section_index: 0,
def: bpf_map_def { def: bpf_map_def {
map_type: 1, map_type: 1,
@ -1267,7 +1531,7 @@ mod tests {
}, },
data, data,
.. ..
}) if data.is_empty() })) if data.is_empty()
)) ))
} }
@ -1283,7 +1547,7 @@ mod tests {
), ),
".bss" ".bss"
), ),
Ok(Map { Ok(Map::Legacy(LegacyMap {
section_index: 0, section_index: 0,
symbol_index: 0, symbol_index: 0,
def: bpf_map_def { def: bpf_map_def {
@ -1297,7 +1561,7 @@ mod tests {
}, },
data, data,
kind kind
}) if data == map_data && value_size == map_data.len() as u32 && kind == MapKind::Bss })) if data == map_data && value_size == map_data.len() as u32 && kind == MapKind::Bss
)) ))
} }
@ -1393,8 +1657,12 @@ mod tests {
assert!(obj.maps.get("foo").is_some()); assert!(obj.maps.get("foo").is_some());
assert!(obj.maps.get("bar").is_some()); assert!(obj.maps.get("bar").is_some());
assert!(obj.maps.get("baz").is_some()); assert!(obj.maps.get("baz").is_some());
for m in obj.maps.values() { for map in obj.maps.values() {
assert_eq!(&m.def, def); if let Map::Legacy(m) = map {
assert_eq!(&m.def, def);
} else {
panic!("expected a BTF map")
}
} }
} }
@ -1905,7 +2173,7 @@ mod tests {
let mut obj = fake_obj(); let mut obj = fake_obj();
obj.maps.insert( obj.maps.insert(
".rodata".to_string(), ".rodata".to_string(),
Map { Map::Legacy(LegacyMap {
def: bpf_map_def { def: bpf_map_def {
map_type: BPF_MAP_TYPE_ARRAY as u32, map_type: BPF_MAP_TYPE_ARRAY as u32,
key_size: mem::size_of::<u32>() as u32, key_size: mem::size_of::<u32>() as u32,
@ -1914,12 +2182,13 @@ mod tests {
map_flags: BPF_F_RDONLY_PROG, map_flags: BPF_F_RDONLY_PROG,
id: 1, id: 1,
pinning: PinningType::None, pinning: PinningType::None,
..Default::default()
}, },
section_index: 1, section_index: 1,
symbol_index: 1, symbol_index: 1,
data: vec![0, 0, 0], data: vec![0, 0, 0],
kind: MapKind::Rodata, kind: MapKind::Rodata,
}, }),
); );
obj.symbols_by_index.insert( obj.symbols_by_index.insert(
1, 1,
@ -1939,6 +2208,103 @@ mod tests {
.unwrap(); .unwrap();
let map = obj.maps.get(".rodata").unwrap(); let map = obj.maps.get(".rodata").unwrap();
assert_eq!(test_data, map.data); assert_eq!(test_data, map.data());
}
#[test]
fn test_parse_btf_map_section() {
let mut obj = fake_obj();
fake_sym(&mut obj, 0, 0, "map_1", 0);
fake_sym(&mut obj, 0, 0, "map_2", 0);
// generated from:
// objcopy --dump-section .BTF=test.btf ./target/bpfel-unknown-none/debug/multimap-btf.bpf.o
// hexdump -v -e '7/1 "0x%02X, " 1/1 " 0x%02X,\n"' test.btf
let data: &[u8] = &[
0x9F, 0xEB, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x01,
0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xCC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x09, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0A, 0x00,
0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xC0, 0x00,
0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0D, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x20, 0x00,
0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x0D, 0x02, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x12, 0x00, 0x00, 0x00, 0xB0, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xBE, 0x01,
0x00, 0x00, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0F,
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x41, 0x52, 0x52, 0x41, 0x59,
0x5F, 0x53, 0x49, 0x5A, 0x45, 0x5F, 0x54, 0x59, 0x50, 0x45, 0x5F, 0x5F, 0x00, 0x5F,
0x5F, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6E, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x20,
0x69, 0x6E, 0x74, 0x00, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x00, 0x75, 0x6E, 0x73, 0x69,
0x67, 0x6E, 0x65, 0x64, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x20, 0x6C, 0x6F, 0x6E, 0x67,
0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x6B, 0x65, 0x79, 0x00, 0x76, 0x61, 0x6C, 0x75,
0x65, 0x00, 0x6D, 0x61, 0x78, 0x5F, 0x65, 0x6E, 0x74, 0x72, 0x69, 0x65, 0x73, 0x00,
0x6D, 0x61, 0x70, 0x5F, 0x31, 0x00, 0x6D, 0x61, 0x70, 0x5F, 0x32, 0x00, 0x63, 0x74,
0x78, 0x00, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F, 0x67, 0x00, 0x74, 0x72, 0x61,
0x63, 0x65, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x00, 0x2F, 0x76, 0x61, 0x72, 0x2F, 0x68,
0x6F, 0x6D, 0x65, 0x2F, 0x64, 0x61, 0x76, 0x65, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x61,
0x79, 0x61, 0x2D, 0x72, 0x73, 0x2F, 0x61, 0x79, 0x61, 0x2F, 0x74, 0x65, 0x73, 0x74,
0x2F, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2D, 0x65,
0x62, 0x70, 0x66, 0x2F, 0x73, 0x72, 0x63, 0x2F, 0x62, 0x70, 0x66, 0x2F, 0x6D, 0x75,
0x6C, 0x74, 0x69, 0x6D, 0x61, 0x70, 0x2D, 0x62, 0x74, 0x66, 0x2E, 0x62, 0x70, 0x66,
0x2E, 0x63, 0x00, 0x69, 0x6E, 0x74, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x70, 0x72, 0x6F,
0x67, 0x28, 0x76, 0x6F, 0x69, 0x64, 0x20, 0x2A, 0x63, 0x74, 0x78, 0x29, 0x00, 0x09,
0x5F, 0x5F, 0x75, 0x33, 0x32, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x3D, 0x20, 0x30, 0x3B,
0x00, 0x09, 0x5F, 0x5F, 0x75, 0x36, 0x34, 0x20, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79,
0x5F, 0x66, 0x6F, 0x75, 0x72, 0x20, 0x3D, 0x20, 0x32, 0x34, 0x3B, 0x00, 0x09, 0x5F,
0x5F, 0x75, 0x36, 0x34, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x79, 0x5F, 0x74, 0x77, 0x6F,
0x20, 0x3D, 0x20, 0x34, 0x32, 0x3B, 0x00, 0x20, 0x20, 0x20, 0x20, 0x62, 0x70, 0x66,
0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C,
0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F, 0x31, 0x2C, 0x20, 0x26, 0x6B, 0x65,
0x79, 0x2C, 0x20, 0x26, 0x74, 0x77, 0x65, 0x6E, 0x74, 0x79, 0x5F, 0x66, 0x6F, 0x75,
0x72, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59, 0x29, 0x3B, 0x00, 0x20,
0x20, 0x20, 0x20, 0x62, 0x70, 0x66, 0x5F, 0x6D, 0x61, 0x70, 0x5F, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x5F, 0x65, 0x6C, 0x65, 0x6D, 0x28, 0x26, 0x6D, 0x61, 0x70, 0x5F,
0x32, 0x2C, 0x20, 0x26, 0x6B, 0x65, 0x79, 0x2C, 0x20, 0x26, 0x66, 0x6F, 0x72, 0x74,
0x79, 0x5F, 0x74, 0x77, 0x6F, 0x2C, 0x20, 0x42, 0x50, 0x46, 0x5F, 0x41, 0x4E, 0x59,
0x29, 0x3B, 0x00, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x30, 0x3B, 0x00,
0x63, 0x68, 0x61, 0x72, 0x00, 0x5F, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
0x2E, 0x6D, 0x61, 0x70, 0x73, 0x00, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, 0x00,
];
let btf_section = fake_section(BpfSectionKind::Btf, ".BTF", data);
obj.parse_section(btf_section).unwrap();
let map_section = fake_section(BpfSectionKind::BtfMaps, ".maps", &[]);
obj.parse_section(map_section).unwrap();
let map = obj.maps.get("map_1").unwrap();
if let Map::Btf(m) = map {
assert_eq!(m.def.key_size, 4);
assert_eq!(m.def.value_size, 8);
assert_eq!(m.def.max_entries, 1);
} else {
panic!("expected a BTF map")
}
} }
} }

@ -65,12 +65,12 @@ impl Object {
pub fn relocate_maps(&mut self, maps: &HashMap<String, Map>) -> Result<(), BpfError> { pub fn relocate_maps(&mut self, maps: &HashMap<String, Map>) -> Result<(), BpfError> {
let maps_by_section = maps let maps_by_section = maps
.iter() .iter()
.map(|(name, map)| (map.obj.section_index, (name.as_str(), map))) .map(|(name, map)| (map.obj.section_index(), (name.as_str(), map)))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let maps_by_symbol = maps let maps_by_symbol = maps
.iter() .iter()
.map(|(name, map)| (map.obj.symbol_index, (name.as_str(), map))) .map(|(name, map)| (map.obj.symbol_index(), (name.as_str(), map)))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let functions = self let functions = self
@ -189,7 +189,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
section_index, section_index,
})?; })?;
if !map.obj.data.is_empty() { if !map.obj.data().is_empty() {
instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_VALUE as u8); 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; instructions[ins_index + 1].imm = instructions[ins_index].imm + sym.address as i32;
} else { } else {
@ -433,3 +433,268 @@ fn insn_is_call(ins: &bpf_insn) -> bool {
&& ins.dst_reg() == 0 && ins.dst_reg() == 0
&& ins.off == 0 && ins.off == 0
} }
#[cfg(test)]
mod test {
use crate::{
bpf_map_def,
obj::{self, BtfMap, LegacyMap, MapKind},
BtfMapDef,
};
use super::*;
fn fake_sym(index: usize, section_index: usize, address: u64, name: &str, size: u64) -> Symbol {
Symbol {
index,
section_index: Some(section_index),
name: Some(name.to_string()),
address,
size,
is_definition: false,
kind: SymbolKind::Data,
}
}
fn ins(bytes: &[u8]) -> bpf_insn {
unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const _) }
}
fn fake_legacy_map(fd: i32, symbol_index: usize) -> Map {
Map {
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_btf_map(fd: i32, symbol_index: usize) -> Map {
Map {
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_func(name: &str, instructions: Vec<bpf_insn>) -> Function {
Function {
address: Default::default(),
name: name.to_string(),
section_index: SectionIndex(0),
section_offset: Default::default(),
instructions,
func_info: Default::default(),
line_info: Default::default(),
func_info_rec_size: Default::default(),
line_info_rec_size: Default::default(),
}
}
#[test]
fn test_single_legacy_map_relocation() {
let mut fun = fake_func(
"test",
vec![ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
])],
);
let symbol_table = HashMap::from([(1, fake_sym(1, 0, 0, "test_map", 0))]);
let relocations = vec![Relocation {
offset: 0x0,
symbol_index: 1,
}];
let maps_by_section = HashMap::new();
let map = fake_legacy_map(1, 1);
let maps_by_symbol = HashMap::from([(1, ("test_map", &map))]);
relocate_maps(
&mut fun,
relocations.iter(),
&maps_by_section,
&maps_by_symbol,
&symbol_table,
None,
)
.unwrap();
assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[0].imm, 1);
mem::forget(map);
}
#[test]
fn test_multiple_legacy_map_relocation() {
let mut fun = fake_func(
"test",
vec![
ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
],
);
let symbol_table = HashMap::from([
(1, fake_sym(1, 0, 0, "test_map_1", 0)),
(2, fake_sym(2, 0, 0, "test_map_2", 0)),
]);
let relocations = vec![
Relocation {
offset: 0x0,
symbol_index: 1,
},
Relocation {
offset: mem::size_of::<bpf_insn>() as u64,
symbol_index: 2,
},
];
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))]);
relocate_maps(
&mut fun,
relocations.iter(),
&maps_by_section,
&maps_by_symbol,
&symbol_table,
None,
)
.unwrap();
assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[0].imm, 1);
assert_eq!(fun.instructions[1].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[1].imm, 2);
mem::forget(map_1);
mem::forget(map_2);
}
#[test]
fn test_single_btf_map_relocation() {
let mut fun = fake_func(
"test",
vec![ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
])],
);
let symbol_table = HashMap::from([(1, fake_sym(1, 0, 0, "test_map", 0))]);
let relocations = vec![Relocation {
offset: 0x0,
symbol_index: 1,
}];
let maps_by_section = HashMap::new();
let map = fake_btf_map(1, 1);
let maps_by_symbol = HashMap::from([(1, ("test_map", &map))]);
relocate_maps(
&mut fun,
relocations.iter(),
&maps_by_section,
&maps_by_symbol,
&symbol_table,
None,
)
.unwrap();
assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[0].imm, 1);
mem::forget(map);
}
#[test]
fn test_multiple_btf_map_relocation() {
let mut fun = fake_func(
"test",
vec![
ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
ins(&[
0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
]),
],
);
let symbol_table = HashMap::from([
(1, fake_sym(1, 0, 0, "test_map_1", 0)),
(2, fake_sym(2, 0, 0, "test_map_2", 0)),
]);
let relocations = vec![
Relocation {
offset: 0x0,
symbol_index: 1,
},
Relocation {
offset: mem::size_of::<bpf_insn>() as u64,
symbol_index: 2,
},
];
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))]);
relocate_maps(
&mut fun,
relocations.iter(),
&maps_by_section,
&maps_by_symbol,
&symbol_table,
None,
)
.unwrap();
assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[0].imm, 1);
assert_eq!(fun.instructions[1].src_reg(), BPF_PSEUDO_MAP_FD as u8);
assert_eq!(fun.instructions[1].imm, 2);
mem::forget(map_1);
mem::forget(map_2);
}
}

@ -17,26 +17,34 @@ use std::{
}; };
use crate::{ use crate::{
bpf_map_def,
generated::{ generated::{
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type, bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type,
}, },
maps::PerCpuValues, maps::PerCpuValues,
obj::btf::{FuncSecInfo, LineSecInfo}, obj::{
self,
btf::{FuncSecInfo, LineSecInfo},
},
sys::{kernel_version, syscall, SysResult, Syscall}, sys::{kernel_version, syscall, SysResult, Syscall},
util::VerifierLog, util::VerifierLog,
Pod, BPF_OBJ_NAME_LEN, Pod, BPF_OBJ_NAME_LEN,
}; };
pub(crate) fn bpf_create_map(name: &CStr, def: &bpf_map_def) -> SysResult { pub(crate) fn bpf_create_map(name: &CStr, def: &obj::Map, btf_fd: Option<RawFd>) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_1 }; let u = unsafe { &mut attr.__bindgen_anon_1 };
u.map_type = def.map_type; u.map_type = def.map_type();
u.key_size = def.key_size; u.key_size = def.key_size();
u.value_size = def.value_size; u.value_size = def.value_size();
u.max_entries = def.max_entries; u.max_entries = def.max_entries();
u.map_flags = def.map_flags; u.map_flags = def.map_flags();
if let obj::Map::Btf(m) = def {
u.btf_key_type_id = m.def.btf_key_type_id;
u.btf_value_type_id = m.def.btf_value_type_id;
u.btf_fd = btf_fd.unwrap() as u32;
}
// https://github.com/torvalds/linux/commit/ad5b177bd73f5107d97c36f56395c4281fb6f089 // https://github.com/torvalds/linux/commit/ad5b177bd73f5107d97c36f56395c4281fb6f089
// The map name was added as a parameter in kernel 4.15+ so we skip adding it on // The map name was added as a parameter in kernel 4.15+ so we skip adding it on

@ -0,0 +1,30 @@
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} map_1 SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} map_2 SEC(".maps");
SEC("tracepoint")
int bpf_prog(void *ctx)
{
__u32 key = 0;
__u64 twenty_four = 24;
__u64 forty_two = 42;
bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY);
bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY);
return 0;
}
char _license[] SEC("license") = "GPL";

@ -1,8 +1,13 @@
use std::{convert::TryInto, process::Command}; use std::{
convert::{TryFrom, TryInto},
process::Command,
thread, time,
};
use aya::{ use aya::{
include_bytes_aligned, include_bytes_aligned,
programs::{Xdp, XdpFlags}, maps::{Array, MapRefMut},
programs::{TracePoint, Xdp, XdpFlags},
Bpf, Bpf,
}; };
@ -34,6 +39,31 @@ fn multiple_maps() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[integration_test]
fn multiple_btf_maps() -> anyhow::Result<()> {
let bytes =
include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/multimap-btf.bpf.o");
let mut bpf = Bpf::load(bytes)?;
let map_1: Array<MapRefMut, u64> = Array::try_from(bpf.map_mut("map_1")?)?;
let map_2: Array<MapRefMut, u64> = Array::try_from(bpf.map_mut("map_2")?)?;
let prog: &mut TracePoint = bpf.program_mut("tracepoint").unwrap().try_into().unwrap();
prog.load().unwrap();
prog.attach("sched", "sched_switch").unwrap();
thread::sleep(time::Duration::from_secs(3));
let key = 0;
let val_1 = map_1.get(&key, 0)?;
let val_2 = map_2.get(&key, 0)?;
assert_eq!(val_1, 24);
assert_eq!(val_2, 42);
Ok(())
}
fn is_loaded() -> bool { fn is_loaded() -> bool {
let output = Command::new("bpftool").args(&["prog"]).output().unwrap(); let output = Command::new("bpftool").args(&["prog"]).output().unwrap();
let stdout = String::from_utf8(output.stdout).unwrap(); let stdout = String::from_utf8(output.stdout).unwrap();

Loading…
Cancel
Save