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

@ -59,20 +59,20 @@ pub struct PerCpuArray<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as 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 {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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.
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.
@ -118,8 +118,8 @@ impl<T: Deref<Target = Map>, V: Pod> PerCpuArray<T, V> {
}
fn check_bounds(&self, index: u32) -> Result<(), MapError> {
let max_entries = self.inner.obj.def.max_entries;
if index >= self.inner.obj.def.max_entries {
let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries })
} else {
Ok(())

@ -57,20 +57,20 @@ pub struct ProgramArray<T: Deref<Target = Map>> {
impl<T: Deref<Target = Map>> ProgramArray<T> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as 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 {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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> {
let max_entries = self.inner.obj.def.max_entries;
if index >= self.inner.obj.def.max_entries {
let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries })
} else {
Ok(())

@ -41,7 +41,7 @@ pub struct BloomFilter<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
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
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 expected = map.obj.def.value_size as usize;
let expected = map.obj.value_size() as usize;
if size != expected {
return Err(MapError::InvalidValueSize { size, expected });
};
@ -140,7 +140,7 @@ mod tests {
use std::io;
fn new_obj_map() -> obj::Map {
obj::Map {
obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_BLOOM_FILTER as u32,
key_size: 4,
@ -152,7 +152,7 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
}
})
}
fn sys_error(value: i32) -> SysResult {
@ -165,6 +165,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
BloomFilter::<_, u16>::new(&map),
@ -178,7 +179,7 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map = Map {
obj: obj::Map {
obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
@ -190,9 +191,10 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
},
}),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
@ -207,6 +209,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
@ -221,6 +224,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(BloomFilter::<_, u32>::new(&mut map).is_ok());
@ -232,6 +236,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(BloomFilter::<_, u32>::try_from(&map).is_ok())
}
@ -244,6 +249,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
@ -267,6 +273,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
@ -280,6 +287,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
@ -302,6 +310,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
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> {
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
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::*;
fn new_obj_map() -> obj::Map {
obj::Map {
obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_HASH as u32,
key_size: 4,
@ -171,7 +171,7 @@ mod tests {
data: Vec::new(),
kind: obj::MapKind::Other,
symbol_index: 0,
}
})
}
fn sys_error(value: i32) -> SysResult {
@ -184,6 +184,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
HashMap::<_, u8, u32>::new(&map),
@ -200,6 +201,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
HashMap::<_, u32, u16>::new(&map),
@ -213,7 +215,7 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map = Map {
obj: obj::Map {
obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
@ -225,9 +227,10 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
},
}),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
@ -242,6 +245,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
@ -256,6 +260,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok());
@ -267,6 +272,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
}
@ -274,7 +280,7 @@ mod tests {
#[test]
fn test_try_from_ok_lru() {
let map = Map {
obj: obj::Map {
obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_LRU_HASH as u32,
key_size: 4,
@ -286,9 +292,10 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
},
}),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
@ -302,6 +309,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -325,6 +333,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -339,6 +348,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -362,6 +372,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -375,6 +386,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -397,6 +409,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -433,6 +446,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>();
@ -477,6 +491,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -505,6 +520,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -535,6 +551,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
@ -568,6 +585,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -602,6 +620,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -642,6 +661,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
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> {
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 {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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> {
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
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> {
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
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 expected = map.obj.def.key_size as usize;
let expected = map.obj.key_size() as usize;
if size != expected {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
return Err(MapError::InvalidValueSize { size, expected });
};
@ -230,7 +230,7 @@ mod tests {
use std::{io, mem, net::Ipv4Addr};
fn new_obj_map() -> obj::Map {
obj::Map {
obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_LPM_TRIE as u32,
key_size: mem::size_of::<Key<u32>>() as u32,
@ -242,7 +242,7 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
}
})
}
fn sys_error(value: i32) -> SysResult {
@ -255,6 +255,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
LpmTrie::<_, u16, u32>::new(&map),
@ -271,6 +272,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
LpmTrie::<_, u32, u16>::new(&map),
@ -284,7 +286,7 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map = Map {
obj: obj::Map {
obj: obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
@ -296,8 +298,9 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: obj::MapKind::Other,
},
}),
fd: None,
btf_fd: None,
pinned: false,
};
@ -313,6 +316,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert!(matches!(
@ -327,6 +331,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(LpmTrie::<_, u32, u32>::new(&mut map).is_ok());
@ -338,6 +343,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(LpmTrie::<_, u32, u32>::try_from(&map).is_ok())
}
@ -350,6 +356,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -374,6 +381,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
@ -390,6 +398,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -414,6 +423,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -428,6 +438,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
@ -452,6 +463,7 @@ mod tests {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);

@ -266,6 +266,7 @@ fn maybe_warn_rlimit() {
pub struct Map {
pub(crate) obj: obj::Map,
pub(crate) fd: Option<RawFd>,
pub(crate) btf_fd: Option<RawFd>,
/// Indicates if this map has been pinned to bpffs
pub pinned: bool,
}
@ -279,7 +280,7 @@ impl Map {
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();
if k_ver < (5, 11, 0) {
maybe_warn_rlimit();
@ -327,7 +328,7 @@ impl Map {
/// Returns the [`bpf_map_type`] of this map
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> {
@ -625,7 +626,7 @@ mod tests {
use super::*;
fn new_obj_map() -> obj::Map {
obj::Map {
obj::Map::Legacy(obj::LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_HASH as u32,
key_size: 4,
@ -637,7 +638,7 @@ mod tests {
symbol_index: 0,
data: Vec::new(),
kind: MapKind::Other,
}
})
}
fn new_map() -> Map {
@ -645,6 +646,7 @@ mod tests {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
}
}

@ -164,7 +164,7 @@ pub struct PerfEventArray<T: DerefMut<Target = Map>> {
impl<T: DerefMut<Target = Map>> PerfEventArray<T> {
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 {
return Err(MapError::InvalidMapType {
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> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as u32,
});
}
let expected = 0;
let size = map.obj.def.key_size as usize;
let size = map.obj.key_size() as usize;
if size != expected {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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.
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> {
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
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> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as 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 {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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> {
let max_entries = self.inner.obj.def.max_entries;
if index >= self.inner.obj.def.max_entries {
let max_entries = self.inner.obj.max_entries();
if index >= self.inner.obj.max_entries() {
Err(MapError::OutOfBounds { index, max_entries })
} else {
Ok(())

@ -39,20 +39,20 @@ pub struct Stack<T: Deref<Target = Map>, V: Pod> {
impl<T: Deref<Target = Map>, V: Pod> Stack<T, V> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as u32,
});
}
let expected = 0;
let size = map.obj.def.key_size as usize;
let size = map.obj.key_size() as usize;
if size != expected {
return Err(MapError::InvalidKeySize { size, expected });
}
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 {
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.
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> {
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 {
return Err(MapError::InvalidMapType {
map_type: map_type as 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 {
return Err(MapError::InvalidKeySize { size, expected });
}
@ -93,7 +93,7 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
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>() {
return Err(MapError::InvalidValueSize { size, expected });
}

@ -1,6 +1,7 @@
pub(crate) mod btf;
mod relocation;
use log::debug;
use object::{
read::{Object as ElfObject, ObjectSection, Section as ObjSection},
Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind,
@ -19,14 +20,14 @@ use relocation::*;
use crate::{
bpf_map_def,
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY, BPF_F_RDONLY_PROG},
obj::btf::{Btf, BtfError, BtfExt},
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY, btf_var_secinfo, BPF_F_RDONLY_PROG},
obj::btf::{Btf, BtfError, BtfExt, BtfType},
programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType},
BpfError,
BpfError, BtfMapDef, PinningType,
};
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;
/// 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>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum MapKind {
Bss,
Data,
@ -74,7 +75,99 @@ impl From<&str> for MapKind {
}
#[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) section_index: usize,
pub(crate) symbol_index: usize,
@ -82,6 +175,15 @@ pub struct Map {
pub(crate) kind: MapKind,
}
#[derive(Debug, Clone)]
pub struct BtfMap {
pub(crate) def: BtfMapDef,
pub(crate) section_index: usize,
pub(crate) symbol_index: usize,
pub(crate) kind: MapKind,
pub(crate) data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub(crate) struct Program {
pub(crate) license: CString,
@ -519,20 +621,20 @@ impl Object {
.iter_mut()
// 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
.find(|(_, m)| symbol.section_index == Some(m.section_index))
.find(|(_, m)| symbol.section_index == Some(m.section_index()))
.ok_or_else(|| ParseError::MapNotFound {
index: symbol.section_index.unwrap_or(0),
})?;
let start = symbol.address 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 {
name: name.to_string(),
sym_size: symbol.size,
data_size: data.len(),
});
}
map.data.splice(start..end, data.iter().cloned());
map.data_mut().splice(start..end, data.iter().cloned());
} else {
return Err(ParseError::SymbolNotFound {
name: name.to_owned(),
@ -706,15 +808,60 @@ impl Object {
let def = parse_map_def(name, data)?;
self.maps.insert(
name.to_string(),
Map {
Map::Legacy(LegacyMap {
section_index: section.index.0,
symbol_index: sym.index,
def,
data: Vec::new(),
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(())
}
@ -740,6 +887,22 @@ impl Object {
BpfSectionKind::Text => self.parse_text_section(section)?,
BpfSectionKind::Btf => self.parse_btf(&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 => {
let symbols: Vec<Symbol> = self
.symbols_by_index
@ -770,10 +933,7 @@ impl Object {
);
}
}
BpfSectionKind::Undefined
| BpfSectionKind::BtfMaps
| BpfSectionKind::License
| BpfSectionKind::Version => {}
BpfSectionKind::Undefined | BpfSectionKind::License | BpfSectionKind::Version => {}
}
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)]
pub enum KernelVersion {
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()),
};
Ok(Map {
Ok(Map::Legacy(LegacyMap {
section_index: section.index.0,
symbol_index: 0,
def,
data,
kind,
})
}))
}
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> {
if data.len() % mem::size_of::<bpf_insn>() > 0 {
return Err(ParseError::InvalidProgramCode);
@ -1187,6 +1447,7 @@ mod tests {
map_flags: 5,
id: 0,
pinning: PinningType::None,
..Default::default()
};
assert_eq!(
@ -1205,6 +1466,7 @@ mod tests {
map_flags: 5,
id: 6,
pinning: PinningType::ByName,
..Default::default()
};
assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
@ -1220,6 +1482,7 @@ mod tests {
map_flags: 5,
id: 6,
pinning: PinningType::ByName,
..Default::default()
};
let mut buf = [0u8; 128];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) };
@ -1250,11 +1513,12 @@ mod tests {
map_flags: 5,
id: 0,
pinning: PinningType::None,
..Default::default()
})
),
"foo"
),
Ok(Map {
Ok(Map::Legacy(LegacyMap{
section_index: 0,
def: bpf_map_def {
map_type: 1,
@ -1267,7 +1531,7 @@ mod tests {
},
data,
..
}) if data.is_empty()
})) if data.is_empty()
))
}
@ -1283,7 +1547,7 @@ mod tests {
),
".bss"
),
Ok(Map {
Ok(Map::Legacy(LegacyMap {
section_index: 0,
symbol_index: 0,
def: bpf_map_def {
@ -1297,7 +1561,7 @@ mod tests {
},
data,
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("bar").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);
} else {
panic!("expected a BTF map")
}
}
}
@ -1905,7 +2173,7 @@ mod tests {
let mut obj = fake_obj();
obj.maps.insert(
".rodata".to_string(),
Map {
Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_ARRAY as u32,
key_size: mem::size_of::<u32>() as u32,
@ -1914,12 +2182,13 @@ mod tests {
map_flags: BPF_F_RDONLY_PROG,
id: 1,
pinning: PinningType::None,
..Default::default()
},
section_index: 1,
symbol_index: 1,
data: vec![0, 0, 0],
kind: MapKind::Rodata,
},
}),
);
obj.symbols_by_index.insert(
1,
@ -1939,6 +2208,103 @@ mod tests {
.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> {
let maps_by_section = maps
.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<_, _>>();
let maps_by_symbol = maps
.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<_, _>>();
let functions = self
@ -189,7 +189,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
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 + 1].imm = instructions[ins_index].imm + sym.address as i32;
} else {
@ -433,3 +433,268 @@ fn insn_is_call(ins: &bpf_insn) -> bool {
&& ins.dst_reg() == 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::{
bpf_map_def,
generated::{
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_prog_info, bpf_prog_type,
},
maps::PerCpuValues,
obj::btf::{FuncSecInfo, LineSecInfo},
obj::{
self,
btf::{FuncSecInfo, LineSecInfo},
},
sys::{kernel_version, syscall, SysResult, Syscall},
util::VerifierLog,
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 u = unsafe { &mut attr.__bindgen_anon_1 };
u.map_type = def.map_type;
u.key_size = def.key_size;
u.value_size = def.value_size;
u.max_entries = def.max_entries;
u.map_flags = def.map_flags;
u.map_type = def.map_type();
u.key_size = def.key_size();
u.value_size = def.value_size();
u.max_entries = def.max_entries();
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
// 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::{
include_bytes_aligned,
programs::{Xdp, XdpFlags},
maps::{Array, MapRefMut},
programs::{TracePoint, Xdp, XdpFlags},
Bpf,
};
@ -34,6 +39,31 @@ fn multiple_maps() -> anyhow::Result<()> {
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 {
let output = Command::new("bpftool").args(&["prog"]).output().unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();

Loading…
Cancel
Save