From f97622947706a8efd06546c45860cc60cfe41a13 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Fri, 10 Dec 2021 19:12:01 +0000 Subject: [PATCH] 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 --- aya/src/bpf.rs | 67 ++- aya/src/maps/array/array.rs | 12 +- aya/src/maps/array/per_cpu_array.rs | 12 +- aya/src/maps/array/program_array.rs | 10 +- aya/src/maps/bloom_filter.rs | 21 +- aya/src/maps/hash_map/hash_map.rs | 34 +- aya/src/maps/hash_map/mod.rs | 4 +- aya/src/maps/hash_map/per_cpu_hash_map.rs | 2 +- aya/src/maps/lpm_trie.rs | 26 +- aya/src/maps/mod.rs | 10 +- aya/src/maps/perf/perf_event_array.rs | 2 +- aya/src/maps/queue.rs | 8 +- aya/src/maps/sock/sock_hash.rs | 2 +- aya/src/maps/sock/sock_map.rs | 10 +- aya/src/maps/stack.rs | 8 +- aya/src/maps/stack_trace.rs | 6 +- aya/src/obj/mod.rs | 418 ++++++++++++++++-- aya/src/obj/relocation.rs | 271 +++++++++++- aya/src/sys/bpf.rs | 24 +- .../src/bpf/multimap-btf.bpf.c | 30 ++ test/integration-test/src/tests/load.rs | 34 +- 21 files changed, 894 insertions(+), 117 deletions(-) create mode 100644 test/integration-ebpf/src/bpf/multimap-btf.bpf.c diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 9ed72d3a..621b2e33 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -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 for PinningType { + type Error = PinningError; + + fn try_from(value: u32) -> Result { + 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() - .map_err(|error| BpfError::FileError { - path: PathBuf::from(POSSIBLE_CPUS), - error, - })? - .len() as u32; + 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, + ); } 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, }, + /// No BTF parsed for object + #[error("no BTF parsed for object")] + NoBTF, + #[error("map error")] /// A map error MapError(#[from] MapError), diff --git a/aya/src/maps/array/array.rs b/aya/src/maps/array/array.rs index 6b5e5fa2..557e9ce5 100644 --- a/aya/src/maps/array/array.rs +++ b/aya/src/maps/array/array.rs @@ -40,20 +40,20 @@ pub struct Array, V: Pod> { impl, V: Pod> Array { fn new(map: T) -> Result, 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::(); - 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::(); - 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, V: Pod> Array { /// /// 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, V: Pod> Array { } 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(()) diff --git a/aya/src/maps/array/per_cpu_array.rs b/aya/src/maps/array/per_cpu_array.rs index ed91dfaf..2344a708 100644 --- a/aya/src/maps/array/per_cpu_array.rs +++ b/aya/src/maps/array/per_cpu_array.rs @@ -59,20 +59,20 @@ pub struct PerCpuArray, V: Pod> { impl, V: Pod> PerCpuArray { fn new(map: T) -> Result, 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::(); - 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::(); - 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, V: Pod> PerCpuArray { /// /// 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, V: Pod> PerCpuArray { } 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(()) diff --git a/aya/src/maps/array/program_array.rs b/aya/src/maps/array/program_array.rs index ae0a28e0..127da90f 100644 --- a/aya/src/maps/array/program_array.rs +++ b/aya/src/maps/array/program_array.rs @@ -57,20 +57,20 @@ pub struct ProgramArray> { impl> ProgramArray { fn new(map: T) -> Result, 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::(); - 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::(); - 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> ProgramArray { } 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(()) diff --git a/aya/src/maps/bloom_filter.rs b/aya/src/maps/bloom_filter.rs index ae2f5faf..fd38ada3 100644 --- a/aya/src/maps/bloom_filter.rs +++ b/aya/src/maps/bloom_filter.rs @@ -41,7 +41,7 @@ pub struct BloomFilter, V: Pod> { impl, V: Pod> BloomFilter { pub(crate) fn new(map: T) -> Result, 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, V: Pod> BloomFilter { } let size = mem::size_of::(); - 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(); diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs index bb454acf..df11da03 100644 --- a/aya/src/maps/hash_map/hash_map.rs +++ b/aya/src/maps/hash_map/hash_map.rs @@ -42,7 +42,7 @@ pub struct HashMap, K, V> { impl, K: Pod, V: Pod> HashMap { pub(crate) fn new(map: T) -> Result, 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::, _>>(); @@ -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::, _>>().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(); diff --git a/aya/src/maps/hash_map/mod.rs b/aya/src/maps/hash_map/mod.rs index 349f67b3..e878e2d4 100644 --- a/aya/src/maps/hash_map/mod.rs +++ b/aya/src/maps/hash_map/mod.rs @@ -15,12 +15,12 @@ pub use per_cpu_hash_map::*; pub(crate) fn check_kv_size(map: &Map) -> Result<(), MapError> { let size = mem::size_of::(); - 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::(); - 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 }); }; diff --git a/aya/src/maps/hash_map/per_cpu_hash_map.rs b/aya/src/maps/hash_map/per_cpu_hash_map.rs index a76d1c23..32a616e4 100644 --- a/aya/src/maps/hash_map/per_cpu_hash_map.rs +++ b/aya/src/maps/hash_map/per_cpu_hash_map.rs @@ -52,7 +52,7 @@ pub struct PerCpuHashMap, K: Pod, V: Pod> { impl, K: Pod, V: Pod> PerCpuHashMap { pub(crate) fn new(map: T) -> Result, 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 diff --git a/aya/src/maps/lpm_trie.rs b/aya/src/maps/lpm_trie.rs index 473d92e4..4b7262c7 100644 --- a/aya/src/maps/lpm_trie.rs +++ b/aya/src/maps/lpm_trie.rs @@ -101,7 +101,7 @@ unsafe impl Pod for Key {} impl, K: Pod, V: Pod> LpmTrie { pub(crate) fn new(map: T) -> Result, 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, K: Pod, V: Pod> LpmTrie { }); } let size = mem::size_of::>(); - 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::(); - 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::>() 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); diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index a432a615..8e390681 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -266,6 +266,7 @@ fn maybe_warn_rlimit() { pub struct Map { pub(crate) obj: obj::Map, pub(crate) fd: Option, + pub(crate) btf_fd: Option, /// 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::try_from(self.obj.def.map_type) + bpf_map_type::try_from(self.obj.map_type()) } pub(crate) fn fd_or_err(&self) -> Result { @@ -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, } } diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs index eede6009..e6aa2e8d 100644 --- a/aya/src/maps/perf/perf_event_array.rs +++ b/aya/src/maps/perf/perf_event_array.rs @@ -164,7 +164,7 @@ pub struct PerfEventArray> { impl> PerfEventArray { pub(crate) fn new(map: T) -> Result, 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, diff --git a/aya/src/maps/queue.rs b/aya/src/maps/queue.rs index a6257807..0c26d93c 100644 --- a/aya/src/maps/queue.rs +++ b/aya/src/maps/queue.rs @@ -39,20 +39,20 @@ pub struct Queue, V: Pod> { impl, V: Pod> Queue { fn new(map: T) -> Result, 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::(); - 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, V: Pod> Queue { /// /// 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() } } diff --git a/aya/src/maps/sock/sock_hash.rs b/aya/src/maps/sock/sock_hash.rs index 59f3f54a..82ce91cd 100644 --- a/aya/src/maps/sock/sock_hash.rs +++ b/aya/src/maps/sock/sock_hash.rs @@ -69,7 +69,7 @@ pub struct SockHash, K> { impl, K: Pod> SockHash { pub(crate) fn new(map: T) -> Result, 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 { diff --git a/aya/src/maps/sock/sock_map.rs b/aya/src/maps/sock/sock_map.rs index d9624a7e..9b870069 100644 --- a/aya/src/maps/sock/sock_map.rs +++ b/aya/src/maps/sock/sock_map.rs @@ -47,20 +47,20 @@ pub struct SockMap> { impl> SockMap { fn new(map: T) -> Result, 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::(); - 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::(); - 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> SockMap { } 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(()) diff --git a/aya/src/maps/stack.rs b/aya/src/maps/stack.rs index b22b049c..8268480f 100644 --- a/aya/src/maps/stack.rs +++ b/aya/src/maps/stack.rs @@ -39,20 +39,20 @@ pub struct Stack, V: Pod> { impl, V: Pod> Stack { fn new(map: T) -> Result, 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::(); - 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, V: Pod> Stack { /// /// 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() } } diff --git a/aya/src/maps/stack_trace.rs b/aya/src/maps/stack_trace.rs index e4aeac8b..af3c64f9 100644 --- a/aya/src/maps/stack_trace.rs +++ b/aya/src/maps/stack_trace.rs @@ -73,14 +73,14 @@ pub struct StackTraceMap { impl> StackTraceMap { fn new(map: T) -> Result, 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::(); - 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> StackTraceMap { 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::() { return Err(MapError::InvalidValueSize { size, expected }); } diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index 9094986e..9375383d 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -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, } -#[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 { + 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, +} + #[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,18 +808,63 @@ 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, + ) -> Result<(), BpfError> { + if self.btf.is_none() { + return Err(BpfError::NoBTF); + } + let btf = self.btf.as_ref().unwrap(); + + for t in btf.types() { + if let BtfType::DataSec(_, sec_info) = &t { + let type_name = match btf.type_name(t) { + Ok(Some(name)) => name, + _ => continue, + }; + if type_name == section.name { + // each btf_var_secinfo contains a map + for info in sec_info { + let (map_name, def) = parse_btf_map_def(btf, info)?; + let symbol_index = symbols + .get(&map_name) + .ok_or_else(|| { + BpfError::ParseError(ParseError::SymbolNotFound { + name: map_name.to_string(), + }) + })? + .index; + self.maps.insert( + map_name, + Map::Btf(BtfMap { + def, + section_index: section.index.0, + symbol_index, + kind: MapKind::Other, + data: Vec::new(), + }), + ); + } + } + } + } + Ok(()) + } + fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> { let mut parts = section.name.rsplitn(2, '/').collect::>(); parts.reverse(); @@ -740,6 +887,22 @@ impl Object { BpfSectionKind::Text => self.parse_text_section(section)?, BpfSectionKind::Btf => self.parse_btf(§ion)?, BpfSectionKind::BtfExt => self.parse_btf_ext(§ion)?, + BpfSectionKind::BtfMaps => { + let symbols: HashMap = 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(§ion, symbols)? + } BpfSectionKind::Maps => { let symbols: Vec = 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 Result { + 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 { } 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 { @@ -1043,6 +1227,82 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result { } } +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, ParseError> { if data.len() % mem::size_of::() > 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() { - assert_eq!(&m.def, def); + 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::() 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") + } } } diff --git a/aya/src/obj/relocation.rs b/aya/src/obj/relocation.rs index af316a84..978faab8 100644 --- a/aya/src/obj/relocation.rs +++ b/aya/src/obj/relocation.rs @@ -65,12 +65,12 @@ impl Object { pub fn relocate_maps(&mut self, maps: &HashMap) -> 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::>(); 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::>(); let functions = self @@ -189,7 +189,7 @@ fn relocate_maps<'a, I: Iterator>( 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) -> 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::() 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::() 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); + } +} diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index d26fb5e2..16a5023f 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -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) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; 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 diff --git a/test/integration-ebpf/src/bpf/multimap-btf.bpf.c b/test/integration-ebpf/src/bpf/multimap-btf.bpf.c new file mode 100644 index 00000000..955a91d3 --- /dev/null +++ b/test/integration-ebpf/src/bpf/multimap-btf.bpf.c @@ -0,0 +1,30 @@ +#include +#include + +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"; diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index bacbf317..472ce62b 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -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 = Array::try_from(bpf.map_mut("map_1")?)?; + let map_2: Array = 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();