Browse Source

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

Support Parsing of BTF Maps
Dave Tucker 2 years ago
parent
commit
73ee3cff70

+ 51 - 16
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<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()
-                    .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<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),

+ 6 - 6
aya/src/maps/array/array.rs

@@ -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(())

+ 6 - 6
aya/src/maps/array/per_cpu_array.rs

@@ -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(())

+ 5 - 5
aya/src/maps/array/program_array.rs

@@ -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(())

+ 15 - 6
aya/src/maps/bloom_filter.rs

@@ -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();
 

+ 27 - 7
aya/src/maps/hash_map/hash_map.rs

@@ -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();
 

+ 2 - 2
aya/src/maps/hash_map/mod.rs

@@ -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 });
     };

+ 1 - 1
aya/src/maps/hash_map/per_cpu_hash_map.rs

@@ -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

+ 19 - 7
aya/src/maps/lpm_trie.rs

@@ -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);

+ 6 - 4
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<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,
         }
     }
 

+ 1 - 1
aya/src/maps/perf/perf_event_array.rs

@@ -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,

+ 4 - 4
aya/src/maps/queue.rs

@@ -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()
     }
 }
 

+ 1 - 1
aya/src/maps/sock/sock_hash.rs

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

+ 5 - 5
aya/src/maps/sock/sock_map.rs

@@ -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(())

+ 4 - 4
aya/src/maps/stack.rs

@@ -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()
     }
 }
 

+ 3 - 3
aya/src/maps/stack_trace.rs

@@ -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 });
         }

+ 392 - 26
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<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,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<String, Symbol>,
+    ) -> Result<(), BpfError> {
+        if self.btf.is_none() {
+            return Err(BpfError::NoBTF);
+        }
+        let btf = self.btf.as_ref().unwrap();
+
+        for t in btf.types() {
+            if let BtfType::DataSec(_, sec_info) = &t {
+                let type_name = match btf.type_name(t) {
+                    Ok(Some(name)) => name,
+                    _ => continue,
+                };
+                if type_name == section.name {
+                    // each btf_var_secinfo contains a map
+                    for info in sec_info {
+                        let (map_name, def) = parse_btf_map_def(btf, info)?;
+                        let symbol_index = symbols
+                            .get(&map_name)
+                            .ok_or_else(|| {
+                                BpfError::ParseError(ParseError::SymbolNotFound {
+                                    name: map_name.to_string(),
+                                })
+                            })?
+                            .index;
+                        self.maps.insert(
+                            map_name,
+                            Map::Btf(BtfMap {
+                                def,
+                                section_index: section.index.0,
+                                symbol_index,
+                                kind: MapKind::Other,
+                                data: Vec::new(),
+                            }),
+                        );
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+
     fn parse_section(&mut self, mut section: Section) -> Result<(), BpfError> {
         let mut parts = section.name.rsplitn(2, '/').collect::<Vec<_>>();
         parts.reverse();
@@ -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() {
-            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::<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")
+        }
     }
 }

+ 268 - 3
aya/src/obj/relocation.rs

@@ -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);
+    }
+}

+ 16 - 8
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<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

+ 30 - 0
test/integration-ebpf/src/bpf/multimap-btf.bpf.c

@@ -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";

+ 32 - 2
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<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();