diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ec455376..d69eb3d4 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; use core::{ + cell::OnceCell, ffi::{CStr, FromBytesUntilNulError}, mem, ptr, }; @@ -17,8 +18,8 @@ use object::{Endianness, SectionIndex}; use crate::{ Object, btf::{ - Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int, - IntEncoding, LineInfo, Struct, Typedef, Union, VarLinkage, + Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, DataSec, DataSecEntry, Enum, Enum64, + FuncInfo, FuncLinkage, Int, IntEncoding, LineInfo, Struct, Typedef, Union, Var, VarLinkage, info::{FuncSecInfo, LineSecInfo}, relocation::Relocation, }, @@ -170,6 +171,7 @@ pub struct BtfFeatures { btf_func: bool, btf_func_global: bool, btf_datasec: bool, + btf_datasec_zero: bool, btf_float: bool, btf_decl_tag: bool, btf_type_tag: bool, @@ -178,10 +180,12 @@ pub struct BtfFeatures { impl BtfFeatures { #[doc(hidden)] + #[expect(clippy::too_many_arguments, reason = "this interface is terrible")] pub fn new( btf_func: bool, btf_func_global: bool, btf_datasec: bool, + btf_datasec_zero: bool, btf_float: bool, btf_decl_tag: bool, btf_type_tag: bool, @@ -191,6 +195,7 @@ impl BtfFeatures { btf_func, btf_func_global, btf_datasec, + btf_datasec_zero, btf_float, btf_decl_tag, btf_type_tag, @@ -213,6 +218,11 @@ impl BtfFeatures { self.btf_datasec } + /// Returns true if zero-length DATASec entries are accepted. + pub fn btf_datasec_zero(&self) -> bool { + self.btf_datasec_zero + } + /// Returns true if the BTF_FLOAT is supported. pub fn btf_float(&self) -> bool { self.btf_float @@ -255,6 +265,15 @@ pub struct Btf { _endianness: Endianness, } +fn add_type(header: &mut btf_header, types: &mut BtfTypes, btf_type: BtfType) -> u32 { + let size = btf_type.type_info_size() as u32; + let type_id = types.len(); + types.push(btf_type); + header.type_len += size; + header.str_off += size; + type_id as u32 +} + impl Btf { /// Creates a new empty instance with its header initialized pub fn new() -> Self { @@ -295,12 +314,7 @@ impl Btf { /// Adds a type to BTF metadata, returning a type id pub fn add_type(&mut self, btf_type: BtfType) -> u32 { - let size = btf_type.type_info_size() as u32; - let type_id = self.types.len(); - self.types.push(btf_type); - self.header.type_len += size; - self.header.str_off += size; - type_id as u32 + add_type(&mut self.header, &mut self.types, btf_type) } /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`. @@ -486,19 +500,8 @@ impl Btf { symbol_offsets: &HashMap, features: &BtfFeatures, ) -> Result<(), BtfError> { - // ENUM64 placeholder type needs to be added before we take ownership of - // self.types to ensure that the offsets in the BtfHeader are correct. - let placeholder_name = self.add_string("enum64_placeholder"); - let enum64_placeholder_id = (!features.btf_enum64 - && self.types().any(|t| t.kind() == BtfKind::Enum64)) - .then(|| { - self.add_type(BtfType::Int(Int::new( - placeholder_name, - 1, - IntEncoding::None, - 0, - ))) - }); + let enum64_placeholder_id = OnceCell::new(); + let filler_var_id = OnceCell::new(); let mut types = mem::take(&mut self.types); for i in 0..types.types.len() { let t = &mut types.types[i]; @@ -586,8 +589,51 @@ impl Btf { // we need to get the offset from the ELF file. // This was also cached during initial parsing and // we can query by name in symbol_offsets. + let old_size = d.type_info_size(); let mut entries = mem::take(&mut d.entries); - let mut fixed_section = d.clone(); + let mut section_size = d.size; + let name_offset = d.name_offset; + + // Kernels before 5.12 reject zero-length DATASEC. See + // https://github.com/torvalds/linux/commit/13ca51d5eb358edcb673afccb48c3440b9fda21b. + if entries.is_empty() && !features.btf_datasec_zero { + let filler_var_id = *filler_var_id.get_or_init(|| { + let filler_type_name = self.add_string("__aya_datasec_filler_type"); + let filler_type_id = add_type( + &mut self.header, + &mut types, + BtfType::Int(Int::new( + filler_type_name, + 1, + IntEncoding::None, + 0, + )), + ); + + let filler_var_name = self.add_string("__aya_datasec_filler"); + add_type( + &mut self.header, + &mut types, + BtfType::Var(Var::new( + filler_var_name, + filler_type_id, + VarLinkage::Static, + )), + ) + }); + let filler_len = section_size.max(1); + debug!( + "{kind} {name}: injecting filler entry for zero-length DATASEC (len={filler_len})" + ); + entries.push(DataSecEntry { + btf_type: filler_var_id, + offset: 0, + size: filler_len, + }); + if section_size == 0 { + section_size = filler_len; + } + } for e in entries.iter_mut() { if let BtfType::Var(var) = types.type_by_id(e.btf_type)? { @@ -611,9 +657,15 @@ impl Btf { return Err(BtfError::InvalidDatasec); } } - fixed_section.entries = entries; + let fixed_section = DataSec::new(name_offset, entries, section_size); + let new_size = fixed_section.type_info_size(); + if new_size != old_size { + self.header.type_len = + self.header.type_len - old_size as u32 + new_size as u32; + self.header.str_off = self.header.type_len; + } - // Must reborrow here because we borrow `types` immutably above. + // Must reborrow here because we borrow `types` above. let t = &mut types.types[i]; *t = BtfType::DataSec(fixed_section); } @@ -691,20 +743,44 @@ impl Btf { ty.set_signed(false); } // Sanitize ENUM64. - BtfType::Enum64(ty) if !features.btf_enum64 => { - debug!("{kind}: not supported. replacing with UNION"); - let placeholder_id = - enum64_placeholder_id.expect("enum64_placeholder_id must be set"); - let members: Vec = ty - .variants - .iter() - .map(|v| BtfMember { - name_offset: v.name_offset, - btf_type: placeholder_id, - offset: 0, - }) - .collect(); - *t = BtfType::Union(Union::new(ty.name_offset, members.len() as u32, members)); + BtfType::Enum64(ty) => { + // Kernels before 6.0 do not support ENUM64. See + // https://github.com/torvalds/linux/commit/6089fb325cf737eeb2c4d236c94697112ca860da. + if !features.btf_enum64 { + debug!("{kind}: not supported. replacing with UNION"); + + // `ty` is borrowed from `types` and we use that borrow + // below, so we must not borrow it again in the + // get_or_init closure. + let Enum64 { + name_offset, + size, + variants, + .. + } = ty; + let (name_offset, size, variants) = + (*name_offset, *size, mem::take(variants)); + let placeholder_id = enum64_placeholder_id.get_or_init(|| { + let placeholder_name = self.add_string("enum64_placeholder"); + add_type( + &mut self.header, + &mut types, + BtfType::Int(Int::new(placeholder_name, 1, IntEncoding::None, 0)), + ) + }); + let members: Vec = variants + .iter() + .map(|v| BtfMember { + name_offset: v.name_offset, + btf_type: *placeholder_id, + offset: 0, + }) + .collect(); + + // Must reborrow here because we borrow `types` above. + let t = &mut types.types[i]; + *t = BtfType::Union(Union::new(name_offset, size, members)); + } } // The type does not need fixing up or sanitization. _ => {} diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index a2868f81..fd2afc67 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -30,9 +30,9 @@ use crate::{ }, sys::{ bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported, - is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported, - is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported, - is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, + is_btf_datasec_supported, is_btf_datasec_zero_supported, is_btf_decl_tag_supported, + is_btf_enum64_supported, is_btf_float_supported, is_btf_func_global_supported, + is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, is_probe_read_kernel_supported, is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs, }, @@ -66,6 +66,7 @@ fn detect_features() -> Features { is_btf_func_supported(), is_btf_func_global_supported(), is_btf_datasec_supported(), + is_btf_datasec_zero_supported(), is_btf_float_supported(), is_btf_decl_tag_supported(), is_btf_type_tag_supported(), diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index c4fa65b1..546de262 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -1103,6 +1103,15 @@ pub(crate) fn is_btf_datasec_supported() -> bool { bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() } +pub(crate) fn is_btf_datasec_zero_supported() -> bool { + let mut btf = Btf::new(); + let name_offset = btf.add_string(".empty"); + let datasec_type = BtfType::DataSec(DataSec::new(name_offset, Vec::new(), 0)); + btf.add_type(datasec_type); + + bpf_load_btf(btf.to_bytes().as_slice(), &mut [], Default::default()).is_ok() +} + pub(crate) fn is_btf_enum64_supported() -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("enum64"); diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index b40c040d..a85e251f 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -506,6 +506,7 @@ pub fn aya_obj::btf::BtfExt::from(t: T) -> T pub struct aya_obj::btf::BtfFeatures impl aya_obj::btf::BtfFeatures pub fn aya_obj::btf::BtfFeatures::btf_datasec(&self) -> bool +pub fn aya_obj::btf::BtfFeatures::btf_datasec_zero(&self) -> bool pub fn aya_obj::btf::BtfFeatures::btf_decl_tag(&self) -> bool pub fn aya_obj::btf::BtfFeatures::btf_enum64(&self) -> bool pub fn aya_obj::btf::BtfFeatures::btf_float(&self) -> bool