Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion aya-obj/src/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,12 @@ pub struct bpf_map_def {
// optional features
/// Id
pub id: u32,
/// Pinning type
/// Pinning type. Only [`PinningType::None`] is supported for legacy maps.
pub pinning: PinningType,
/// Inner map id. Map-of-maps is not supported for legacy maps.
pub inner_id: u32,
/// Inner map index. Map-of-maps is not supported for legacy maps.
pub inner_idx: u32,
}

/// The first five __u32 of `bpf_map_def` must be defined.
Expand Down
99 changes: 91 additions & 8 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,9 @@ pub enum ParseError {
/// No BTF parsed for object
#[error("no BTF parsed for object")]
NoBTF,

#[error("map `{name}` uses unsupported legacy pinning")]
UnsupportedLegacyPinning { name: String },
}

/// Invalid bindings to the bpf type from the parsed/received value.
Expand Down Expand Up @@ -1251,16 +1254,23 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
});
}

if data.len() < size_of::<bpf_map_def>() {
let map_def = if data.len() < size_of::<bpf_map_def>() {
let mut map_def = bpf_map_def::default();
unsafe {
let map_def_ptr = from_raw_parts_mut(ptr::from_mut(&mut map_def).cast(), data.len());
map_def_ptr.copy_from_slice(data);
}
Ok(map_def)
map_def
} else {
Ok(unsafe { ptr::read_unaligned(data.as_ptr().cast()) })
unsafe { ptr::read_unaligned(data.as_ptr().cast()) }
};

if map_def.pinning != PinningType::None {
return Err(ParseError::UnsupportedLegacyPinning {
name: name.to_owned(),
});
}
Ok(map_def)
}

fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDef), BtfError> {
Expand Down Expand Up @@ -1367,6 +1377,8 @@ pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
map_flags: info.map_flags,
pinning: pinned,
id: info.id,
inner_id: 0,
inner_idx: 0,
},
section_index: 0,
symbol_index: None,
Expand Down Expand Up @@ -1566,6 +1578,8 @@ mod tests {
map_flags: 5,
id: 0,
pinning: PinningType::None,
inner_id: 0,
inner_idx: 0,
};

assert_eq!(
Expand All @@ -1574,6 +1588,25 @@ mod tests {
);
}

#[test]
fn test_parse_map_def_unsupported_pinning() {
let def = bpf_map_def {
map_type: 1,
key_size: 2,
value_size: 3,
max_entries: 4,
map_flags: 5,
id: 0,
pinning: PinningType::ByName,
inner_id: 0,
inner_idx: 0,
};
assert_matches!(
parse_map_def("foo", bytes_of(&def)),
Err(ParseError::UnsupportedLegacyPinning { .. })
);
}

#[test]
fn test_parse_map_def() {
let def = bpf_map_def {
Expand All @@ -1583,7 +1616,9 @@ mod tests {
max_entries: 4,
map_flags: 5,
id: 6,
pinning: PinningType::ByName,
pinning: PinningType::None,
inner_id: 0,
inner_idx: 0,
};

assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
Expand All @@ -1598,7 +1633,9 @@ mod tests {
max_entries: 4,
map_flags: 5,
id: 6,
pinning: PinningType::ByName,
pinning: PinningType::None,
inner_id: 0,
inner_idx: 0,
};
let mut buf = [0u8; 128];
unsafe { ptr::write_unaligned(buf.as_mut_ptr().cast(), def) }
Expand Down Expand Up @@ -1630,6 +1667,8 @@ mod tests {
map_flags: 0,
id: 0,
pinning: PinningType::None,
inner_id: 0,
inner_idx: 0,
},
data,
}) if data == map_data && value_size == map_data.len() as u32
Expand Down Expand Up @@ -1809,17 +1848,59 @@ mod tests {
}

#[test]
fn test_parse_section_multiple_maps() {
fn test_parse_section_multiple_maps_v1_legacy() {
let mut obj = fake_obj();
fake_sym(&mut obj, 0, 0, "foo", 28);
fake_sym(&mut obj, 0, 28, "bar", 28);
fake_sym(&mut obj, 0, 60, "baz", 28);
Comment on lines +1853 to +1855
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_parse_section_multiple_maps_v1_legacy uses several magic numbers (28, 60) for legacy map-def sizes/offsets. Since the intent is to validate the legacy 28-byte format, consider introducing a named constant (e.g., legacy map-def size and padding) and computing the offsets from it to make the test intent clearer and reduce the chance of accidental inconsistency.

Copilot uses AI. Check for mistakes.
let def = &bpf_map_def {
map_type: 1,
key_size: 2,
value_size: 3,
max_entries: 4,
map_flags: 5,
..Default::default()
};
let map_data = bytes_of(def)[..28].to_vec();
let mut buf = vec![];
buf.extend(&map_data);
buf.extend(&map_data);
// throw in some padding
buf.extend([0, 0, 0, 0]);
buf.extend(&map_data);
assert_matches!(
obj.parse_section(fake_section(
EbpfSectionKind::Maps,
"maps",
buf.as_slice(),
None
)),
Ok(())
);
assert!(obj.maps.contains_key("foo"));
assert!(obj.maps.contains_key("bar"));
assert!(obj.maps.contains_key("baz"));
for map in obj.maps.values() {
assert_matches!(map, Map::Legacy(m) => {
assert_eq!(&m.def, def);
})
}
}

#[test]
fn test_parse_section_multiple_maps_v2_legacy_tc() {
let mut obj = fake_obj();
fake_sym(&mut obj, 0, 0, "foo", size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 28, "bar", size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 60, "baz", size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 36, "bar", size_of::<bpf_map_def>() as u64);
fake_sym(&mut obj, 0, 76, "baz", size_of::<bpf_map_def>() as u64);
Comment on lines 1893 to +1895
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In test_parse_section_multiple_maps_v2_legacy_tc, the symbol addresses are hard-coded to 36 and 76 while symbol sizes use size_of::<bpf_map_def>(). If bpf_map_def ever changes again, these offsets can become inconsistent with the struct size and make the test fail for the wrong reason. Consider deriving the offsets from size_of::<bpf_map_def>() (and the padding length) to keep the test self-consistent.

Copilot uses AI. Check for mistakes.
let def = &bpf_map_def {
map_type: 1,
key_size: 2,
value_size: 3,
max_entries: 4,
map_flags: 5,
inner_id: 6,
inner_idx: 7,
..Default::default()
};
let map_data = bytes_of(def).to_vec();
Expand Down Expand Up @@ -2628,6 +2709,8 @@ mod tests {
map_flags: BPF_F_RDONLY_PROG,
id: 1,
pinning: PinningType::None,
inner_id: 0,
inner_idx: 0,
},
section_index: 1,
section_kind: EbpfSectionKind::Rodata,
Expand Down
48 changes: 48 additions & 0 deletions test/integration-test/bpf/tc_legacy_map.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
// clang-format on

struct bpf_elf_map {
__u32 type;
__u32 size_key;
__u32 size_value;
__u32 max_elem;
__u32 flags;
__u32 id;
__u32 pinning;
__u32 inner_id;
__u32 inner_idx;
};

struct bpf_elf_map SEC("maps") tc_map_1 = {
.type = BPF_MAP_TYPE_ARRAY,
.size_key = sizeof(__u32),
.size_value = sizeof(__u32),
.max_elem = 1,
.id = 1,
.inner_id = 1,
.inner_idx = 1,
};

struct bpf_elf_map SEC("maps") tc_map_2 = {
.type = BPF_MAP_TYPE_ARRAY,
.size_key = sizeof(__u32),
.size_value = sizeof(__u32),
.max_elem = 2,
.id = 2,
.inner_id = 2,
.inner_idx = 2,
};

SEC("classifier")
int tc_pass(void *ctx) {
__u32 key = 0;
__u32 val = 42;

bpf_map_update_elem(&tc_map_1, &key, &val, BPF_ANY);
bpf_map_update_elem(&tc_map_2, &key, &val, BPF_ANY);
return 0;
}

char _license[] SEC("license") = "GPL";
1 change: 1 addition & 0 deletions test/integration-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fn main() -> Result<()> {
("struct_flavors_reloc.bpf.c", true),
("text_64_64_reloc.c", false),
("variables_reloc.bpf.c", false),
("tc_legacy_map.bpf.c", false),
];
const C_BPF_HEADERS: &[&str] = &["reloc.h", "struct_with_scalars.h"];

Expand Down
1 change: 1 addition & 0 deletions test/integration-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bpf_file!(
ITER_TASK => "iter.bpf.o",
MAIN => "main.bpf.o",
MULTIMAP_BTF => "multimap-btf.bpf.o",
TC_LEGACY_MAP => "tc_legacy_map.bpf.o",
RINGBUF_BTF => "ringbuf-btf.bpf.o",

ENUM_SIGNED_32_RELOC_BPF => "enum_signed_32_reloc.bpf.o",
Expand Down
11 changes: 11 additions & 0 deletions test/integration-test/src/tests/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,3 +675,14 @@ fn pin_lifecycle_uprobe() {
// Make sure the function isn't optimized out.
uprobe_function();
}

#[test_log::test]
fn legacy_36_byte_maps() {
let mut bpf = Ebpf::load(crate::TC_LEGACY_MAP).unwrap();

let map_1: Array<_, u32> = bpf.take_map("tc_map_1").unwrap().try_into().unwrap();
let map_2: Array<_, u32> = bpf.take_map("tc_map_2").unwrap().try_into().unwrap();

assert_eq!(map_1.len(), 1);
assert_eq!(map_2.len(), 2);
}
Comment on lines +679 to +688
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is being tested here? reading the change description and this test name i am not understanding what i should be checking here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

each map has a distinct max_elem (1 and 2) to verify both are parsed correctly from the 36-byte struct. the parser is backwards compatible, so it also handles the 28-byte variant without needing separate test cases

6 changes: 6 additions & 0 deletions xtask/public-api/aya-obj.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8625,6 +8625,8 @@ impl<T> core::convert::From<T> for aya_obj::maps::LegacyMap
pub fn aya_obj::maps::LegacyMap::from(t: T) -> T
#[repr(C)] pub struct aya_obj::maps::bpf_map_def
pub aya_obj::maps::bpf_map_def::id: u32
pub aya_obj::maps::bpf_map_def::inner_id: u32
pub aya_obj::maps::bpf_map_def::inner_idx: u32
pub aya_obj::maps::bpf_map_def::key_size: u32
pub aya_obj::maps::bpf_map_def::map_flags: u32
pub aya_obj::maps::bpf_map_def::map_type: u32
Expand Down Expand Up @@ -8762,6 +8764,8 @@ pub aya_obj::obj::ParseError::SymbolTableConflict::section_index: usize
pub aya_obj::obj::ParseError::UnknownSymbol
pub aya_obj::obj::ParseError::UnknownSymbol::address: u64
pub aya_obj::obj::ParseError::UnknownSymbol::section_index: usize
pub aya_obj::obj::ParseError::UnsupportedLegacyPinning
pub aya_obj::obj::ParseError::UnsupportedLegacyPinning::name: alloc::string::String
pub aya_obj::obj::ParseError::UnsupportedRelocationTarget
impl core::convert::From<aya_obj::btf::BtfError> for aya_obj::ParseError
pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self
Expand Down Expand Up @@ -9644,6 +9648,8 @@ pub aya_obj::ParseError::SymbolTableConflict::section_index: usize
pub aya_obj::ParseError::UnknownSymbol
pub aya_obj::ParseError::UnknownSymbol::address: u64
pub aya_obj::ParseError::UnknownSymbol::section_index: usize
pub aya_obj::ParseError::UnsupportedLegacyPinning
pub aya_obj::ParseError::UnsupportedLegacyPinning::name: alloc::string::String
pub aya_obj::ParseError::UnsupportedRelocationTarget
impl core::convert::From<aya_obj::btf::BtfError> for aya_obj::ParseError
pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self
Expand Down