From 70919e35975676385f93da30d6a504977f228629 Mon Sep 17 00:00:00 2001 From: Michal R Date: Wed, 5 Nov 2025 18:06:13 +0100 Subject: [PATCH 1/2] aya-ebpf: Remove `AyaBtfMapMarker` It was never necessary in the first place. There is no place in the kernel that enforces BTF map structs themselves to be anonymous. Only pointer types (that are members of BTF maps) have to be anonymous, but that's currently handled by LLVM[0]. BTF maps work just fine without it. [0] https://github.com/llvm/llvm-project/pull/163174 --- ebpf/aya-ebpf/src/btf_maps/mod.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/ebpf/aya-ebpf/src/btf_maps/mod.rs b/ebpf/aya-ebpf/src/btf_maps/mod.rs index a65a4a7e..086c49f1 100644 --- a/ebpf/aya-ebpf/src/btf_maps/mod.rs +++ b/ebpf/aya-ebpf/src/btf_maps/mod.rs @@ -1,22 +1,9 @@ -use core::marker::PhantomData; - pub mod array; pub mod sk_storage; pub use array::Array; pub use sk_storage::SkStorage; -/// A marker used to remove names of annotated types in LLVM debug info and -/// therefore also in BTF. -#[repr(transparent)] -pub(crate) struct AyaBtfMapMarker(PhantomData<()>); - -impl AyaBtfMapMarker { - pub(crate) const fn new() -> Self { - Self(PhantomData) - } -} - #[macro_export] macro_rules! btf_map_def { ($name:ident, $t:ident) => { @@ -30,9 +17,6 @@ macro_rules! btf_map_def { value: *const V, max_entries: *const [i32; M], map_flags: *const [i32; F], - - // Anonymize the struct. - _anon: $crate::btf_maps::AyaBtfMapMarker, } #[expect( @@ -47,7 +31,6 @@ macro_rules! btf_map_def { value: ::core::ptr::null(), max_entries: ::core::ptr::null(), map_flags: ::core::ptr::null(), - _anon: $crate::btf_maps::AyaBtfMapMarker::new(), } } } From 817da9d01e0e10c31c4ec560b3d2dd6251038327 Mon Sep 17 00:00:00 2001 From: Michal R Date: Wed, 5 Nov 2025 10:36:42 +0100 Subject: [PATCH 2/2] aya-obj: Sanitize type names Rust emits characters like `<` and `>` in name types with generics, that are not accepted by the Linux kernel. In theory, the correct place to fix that would be the Linux kernel, but given that we have to support old kernels and that such change would be controversial, it's better to fix it up on our side, at least for now. We used to rely on bpf-linker to fix that up, but given that we want to support binutils at some point, we need to do it in Aya during the load. --- Cargo.toml | 1 + aya-obj/Cargo.toml | 4 ++ aya-obj/src/btf/btf.rs | 109 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 73f71433..08f9b069 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ dialoguer = { version = "0.12", default-features = false } diff = { version = "0.1.13", default-features = false } env_logger = { version = "0.11", default-features = false } epoll = { version = "4.3.3", default-features = false } +foldhash = { version = "0.2", default-features = false } futures = { version = "0.3.28", default-features = false } glob = { version = "0.3.0", default-features = false } hashbrown = { version = "0.16.0", default-features = false } diff --git a/aya-obj/Cargo.toml b/aya-obj/Cargo.toml index c3056fe5..2252e1c2 100644 --- a/aya-obj/Cargo.toml +++ b/aya-obj/Cargo.toml @@ -18,6 +18,10 @@ workspace = true [dependencies] bytes = { workspace = true } +# hashbrown uses foldhash for its `DefaultHasher` implementation. However, +# hashbrown doesn't expose any API allowing to create hashers with fixed seed, +# so we need to use `foldhash::FixedState` directly. +foldhash = { workspace = true } hashbrown = { workspace = true, features = ["default-hasher", "equivalent"] } log = { workspace = true } object = { workspace = true, features = ["elf", "read_core"] } diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ff28b164..e3be394a 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -8,10 +8,13 @@ use alloc::{ use core::{ cell::OnceCell, ffi::{CStr, FromBytesUntilNulError}, + fmt::Write as _, + hash::{BuildHasher as _, Hasher as _}, mem, ptr, }; use bytes::BufMut as _; +use foldhash::fast::FixedState; use log::debug; use object::{Endianness, SectionIndex}; @@ -275,6 +278,42 @@ fn add_type(header: &mut btf_header, types: &mut BtfTypes, btf_type: BtfType) -> type_id as u32 } +/// [The maximum length of a symbol accepted by the Linux kernel][name-limit] +/// according to the [`KSYM_NAME_LEN` constant][ksym-name-len]. That limit got +/// increased in [kernel 6.1][ksym-name-len-bump], but we keep the old value +/// for backwards compatibility. +/// +/// [name-limit]: https://github.com/torvalds/linux/blob/v4.20/kernel/bpf/btf.c#L442-L449 +/// [ksym-name-len]: https://github.com/torvalds/linux/blob/v4.20/include/linux/kallsyms.h#L17 +/// [ksym-name-len-bump]: https://github.com/torvalds/linux/commit/b8a94bfb33952bb17fbc65f8903d242a721c533d +const MAX_NAME_LEN: usize = 128; + +// Sanitize Rust type names to be valid C type names. Kernel does not accept +// other characters like `<`, `>` that Rust emits. +fn sanitize_type_name(name: &str) -> String { + let mut sanitized = String::with_capacity(name.len()); + for byte in name.bytes() { + // Characters which are valid in C type names (alphanumeric and `_`). + if matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_') { + sanitized.push(byte as char); + } else { + write!(&mut sanitized, "_{:X}_", byte).unwrap(); + } + } + + if sanitized.len() > MAX_NAME_LEN { + let mut hasher = FixedState::default().build_hasher(); + hasher.write(sanitized.as_bytes()); + let hash = hasher.finish(); + // leave space for underscore + let trim = MAX_NAME_LEN - 2 * size_of_val(&hash) - 1; + sanitized.truncate(trim); + write!(&mut sanitized, "_{:x}", hash).unwrap(); + } + + sanitized +} + impl Btf { /// Creates a new empty instance with its header initialized pub fn new() -> Self { @@ -695,8 +734,16 @@ impl Btf { } // Sanitize FUNC. BtfType::Func(ty) => { + // Sanitize the name. let name = self.string_at(ty.name_offset)?; - // Sanitize FUNC. + let fixed_name = sanitize_type_name(&name); + let name = if fixed_name != name { + ty.name_offset = self.add_string(&fixed_name); + fixed_name.into() + } else { + name + }; + // Adjust type and linkage according to features. if !features.btf_func { debug!("{kind}: not supported. replacing with TYPEDEF"); *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); @@ -805,6 +852,15 @@ impl Btf { *t = BtfType::Union(Union::new(name_offset, size, members, Some(fallback))); } } + // Sanitize STRUCT. + BtfType::Struct(ty) => { + // Sanitize the name. + let name = self.string_at(ty.name_offset)?; + let fixed_name = sanitize_type_name(&name); + if fixed_name != name { + ty.name_offset = self.add_string(&fixed_name); + } + } // The type does not need fixing up or sanitization. _ => {} } @@ -1417,6 +1473,57 @@ mod tests { assert_eq!(btf.string_at(5).unwrap(), "widget"); } + #[test] + fn test_sanitize_type_name() { + let name = "MyStruct"; + assert_eq!(sanitize_type_name(name), "MyStruct_3C_u64_3E_"); + + let name = "MyStruct"; + assert_eq!(sanitize_type_name(name), "MyStruct_3C_u64_2C__20_u64_3E_"); + + let name = "my_function"; + assert_eq!( + sanitize_type_name(name), + "my_function_3C_aya_bpf_3A__3A_BpfContext_3E_" + ); + + let name = "my_function"; + assert_eq!( + sanitize_type_name(name), + "my_function_3C_aya_bpf_3A__3A_BpfContext_2C__20_aya_log_ebpf_3A__3A_WriteToBuf_3E_" + ); + + let name = "PerfEventArray<[u8; 32]>"; + assert_eq!( + sanitize_type_name(name), + "PerfEventArray_3C__5B_u8_3B__20_32_5D__3E_" + ); + + let name = "my_function"; + let san = sanitize_type_name(name); + + #[cfg(any( + target_arch = "aarch64", + target_arch = "loongarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "x86_64" + ))] + let expected_hash = "f6a9dc7f4a53c91d"; + #[cfg(target_arch = "arm")] + let expected_hash = "e203c1e6145df4dd"; + #[cfg(target_arch = "s390x")] + let expected_hash = "6609fba3a8753dce"; + + assert_eq!(san.len(), 128); + assert_eq!( + san, + format!( + "my_function_3C_aya_bpf_3A__3A_this_3A__3A_is_3A__3A_a_3A__3A_very_3A__3A_long_3A__3A_namespace_3A__3A_BpfContex_{expected_hash}" + ) + ); + } + #[test] fn test_fixup_ptr() { let mut btf = Btf::new();