Support BTF Maps

This commit allows for BTF maps in the .maps ELF section to be parsed.
It reads the necessary information from the BTF section of the ELF file.
While the btf_ids of Keys and Values types are stored, they are not (yet)
used.

When creating a BTF map, we pass the btf_key_type_id and
btf_value_type_id.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/140/head
Dave Tucker 3 years ago
parent 480f6180f5
commit f976229477

@ -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,15 +808,60 @@ 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(())
}
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(()) Ok(())
} }
@ -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() {
if let Map::Legacy(m) = map {
assert_eq!(&m.def, def); 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