|
|
|
|
@ -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<u64>";
|
|
|
|
|
assert_eq!(sanitize_type_name(name), "MyStruct_3C_u64_3E_");
|
|
|
|
|
|
|
|
|
|
let name = "MyStruct<u64, u64>";
|
|
|
|
|
assert_eq!(sanitize_type_name(name), "MyStruct_3C_u64_2C__20_u64_3E_");
|
|
|
|
|
|
|
|
|
|
let name = "my_function<aya_bpf::BpfContext>";
|
|
|
|
|
assert_eq!(
|
|
|
|
|
sanitize_type_name(name),
|
|
|
|
|
"my_function_3C_aya_bpf_3A__3A_BpfContext_3E_"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let name = "my_function<aya_bpf::BpfContext, aya_log_ebpf::WriteToBuf>";
|
|
|
|
|
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<aya_bpf::this::is::a::very::long::namespace::BpfContext, aya_log_ebpf::this::is::a::very::long::namespace::WriteToBuf>";
|
|
|
|
|
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();
|
|
|
|
|
|