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..069c87b2 100644 --- a/aya-obj/Cargo.toml +++ b/aya-obj/Cargo.toml @@ -18,6 +18,7 @@ workspace = true [dependencies] bytes = { workspace = true } +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..6b5b202e 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, + 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,45 @@ fn add_type(header: &mut btf_header, types: &mut BtfTypes, btf_type: BtfType) -> type_id as u32 } +/// Maximum length of a type name accepted by the [Linux kernel][name-limit] +/// according to the [`KSYM_NAME_LEN` variable][ksym-name-len]. That variable +/// got increased in [kernel 6.1][ksym-name-len-bump], but we keep the old value +/// for backwards compatibility. +/// Ker +/// KSYM_NAME_LEN from Linux kernel intentionally set to the lowest value found +/// across versions to ensure backward 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 +737,14 @@ impl Btf { } // Sanitize FUNC. BtfType::Func(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); + } + // Adjust type and linkage according to features. let name = self.string_at(ty.name_offset)?; - // Sanitize FUNC. if !features.btf_func { debug!("{kind}: not supported. replacing with TYPEDEF"); *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); @@ -805,6 +853,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 +1474,42 @@ 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); + + assert_eq!(san.len(), 128); + assert_eq!( + san, + "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_f6a9dc7f4a53c91d" + ); + } + #[test] fn test_fixup_ptr() { let mut btf = Btf::new();