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::{
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