diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ff28b164..1a5ae77c 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -501,12 +501,16 @@ impl Btf { symbol_offsets: &HashMap, features: &BtfFeatures, ) -> Result<(), BtfError> { + debug!("fixup_and_sanitize: starting with {} BTF types", self.types.types.len()); 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]; let kind = t.kind(); + if i < 10 || kind == BtfKind::Func { + debug!("Processing type {}: kind={:?}", i, kind); + } match t { // Fixup PTR for Rust. // @@ -562,6 +566,18 @@ impl Btf { let name = self.string_at(d.name_offset)?; let name = name.into_owned(); + // .ksyms describes forward declarations of kfunc signatures, as well as + // references to kernel symbols. + // .kconfig describes kernel configuration variables (e.g., CONFIG_HZ). + // Both are extern-backing datasecs with nothing to fix up - all sizes + // and offsets are 0, and there's no corresponding ELF section. + // See: + // https://github.com/libbpf/libbpf/blob/05f94ddbb837f5f4b3161e341eed21be307eaa04/src/libbpf.c#L2916 + if name == ".ksyms" || name == ".kconfig" { + debug!("{kind} {name}: skipping fixups for extern-backing datasec"); + continue; + } + // Handle any "/" characters in section names. // Example: "maps/hashmap" let fixed_name = name.replace('/', "."); @@ -696,10 +712,21 @@ impl Btf { // Sanitize FUNC. BtfType::Func(ty) => { let name = self.string_at(ty.name_offset)?; + let linkage = ty.linkage(); + debug!("{kind} {name}: linkage={:?}, info=0x{:x}", linkage, ty.info()); + // Sanitize FUNC. if !features.btf_func { debug!("{kind}: not supported. replacing with TYPEDEF"); *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); + } else if linkage == FuncLinkage::Unknown { + // Unknown linkage is invalid and will be rejected by the kernel. + // This should never happen with valid BTF from clang/LLVM. + debug!("{kind} {name}: WARNING - Unknown linkage detected! This will cause kernel rejection."); + } else if linkage == FuncLinkage::Extern { + // Extern functions (kfuncs) should pass through unchanged, like libbpf does. + // See: https://github.com/libbpf/libbpf/blob/05f94ddbb837f5f4b3161e341eed21be307eaa04/src/libbpf.c#L2892 + debug!("{kind} {name}: extern linkage - passing through unchanged"); } else if !features.btf_func_global || name == "memset" || name == "memcpy" @@ -829,17 +856,22 @@ impl Object { features: &BtfFeatures, ) -> Result, BtfError> { if let Some(obj_btf) = &mut self.btf { + debug!("Object has BTF with {} types", obj_btf.types.types.len()); if obj_btf.is_empty() { + debug!("BTF is empty, skipping fixup"); return Ok(None); } // fixup btf + debug!("Starting BTF fixup_and_sanitize"); obj_btf.fixup_and_sanitize( &self.section_infos, &self.symbol_offset_by_name, features, )?; + debug!("BTF fixup_and_sanitize completed successfully"); Ok(Some(obj_btf)) } else { + debug!("Object has no BTF section"); Ok(None) } } @@ -1589,6 +1621,80 @@ mod tests { Btf::parse(&raw, Endianness::default()).unwrap(); } + #[test] + fn test_skip_extern_datasec() { + // Test that .ksyms and .kconfig sections are skipped during fixup + let mut btf = Btf::new(); + let name_offset = btf.add_string("int"); + let int_type_id = btf.add_type(BtfType::Int(Int::new( + name_offset, + 4, + IntEncoding::Signed, + 0, + ))); + + // Create a .ksyms datasec + let ksyms_var_name = btf.add_string("kfunc_helper"); + let ksyms_var_type_id = btf.add_type(BtfType::Var(Var::new( + ksyms_var_name, + int_type_id, + VarLinkage::Extern, + ))); + let ksyms_name_offset = btf.add_string(".ksyms"); + let ksyms_datasec_id = btf.add_type(BtfType::DataSec(DataSec::new( + ksyms_name_offset, + vec![DataSecEntry { + btf_type: ksyms_var_type_id, + offset: 0, + size: 0, + }], + 0, + ))); + + // Create a .kconfig datasec + let kconfig_var_name = btf.add_string("CONFIG_HZ"); + let kconfig_var_type_id = btf.add_type(BtfType::Var(Var::new( + kconfig_var_name, + int_type_id, + VarLinkage::Extern, + ))); + let kconfig_name_offset = btf.add_string(".kconfig"); + let kconfig_datasec_id = btf.add_type(BtfType::DataSec(DataSec::new( + kconfig_name_offset, + vec![DataSecEntry { + btf_type: kconfig_var_type_id, + offset: 0, + size: 0, + }], + 0, + ))); + + let features = BtfFeatures { + btf_datasec: true, + ..Default::default() + }; + + // Pass empty section_infos - .ksyms and .kconfig should not require them + btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features) + .unwrap(); + + // Verify that .ksyms datasec was not modified (still has size 0) + assert_matches!(btf.type_by_id(ksyms_datasec_id).unwrap(), BtfType::DataSec(fixed) => { + assert_eq!(btf.string_at(fixed.name_offset).unwrap(), ".ksyms"); + assert_eq!(fixed.size, 0); + }); + + // Verify that .kconfig datasec was not modified (still has size 0) + assert_matches!(btf.type_by_id(kconfig_datasec_id).unwrap(), BtfType::DataSec(fixed) => { + assert_eq!(btf.string_at(fixed.name_offset).unwrap(), ".kconfig"); + assert_eq!(fixed.size, 0); + }); + + // Ensure we can convert to bytes and back again + let raw = btf.to_bytes(); + Btf::parse(&raw, Endianness::default()).unwrap(); + } + #[test] fn test_sanitize_func_and_proto() { let mut btf = Btf::new(); @@ -1808,6 +1914,58 @@ mod tests { } } + #[test] + fn test_extern_func_preserved() { + // Test that extern FUNC entries (kfuncs) are preserved unchanged + let mut btf = Btf::new(); + let name_offset = btf.add_string("int"); + let int_type_id = btf.add_type(BtfType::Int(Int::new( + name_offset, + 4, + IntEncoding::Signed, + 0, + ))); + + let params = vec![BtfParam { + name_offset: btf.add_string("skb"), + btf_type: int_type_id, + }]; + let func_proto_type_id = + btf.add_type(BtfType::FuncProto(FuncProto::new(params, int_type_id))); + + // Create an extern FUNC (kfunc) + let kfunc_name = btf.add_string("bpf_sha256"); + let kfunc_type_id = btf.add_type(BtfType::Func(Func::new( + kfunc_name, + func_proto_type_id, + FuncLinkage::Extern, + ))); + + let features = BtfFeatures { + btf_func: true, + btf_func_global: true, + ..Default::default() + }; + + btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features) + .unwrap(); + + // Verify that extern linkage is preserved + assert_matches!(btf.type_by_id(kfunc_type_id).unwrap(), BtfType::Func(fixed) => { + assert_eq!(fixed.linkage(), FuncLinkage::Extern); + assert_eq!(btf.string_at(fixed.name_offset).unwrap(), "bpf_sha256"); + }); + + // Ensure we can convert to bytes and back again + let raw = btf.to_bytes(); + let reparsed = Btf::parse(&raw, Endianness::default()).unwrap(); + + // Verify the linkage is still extern after round-trip + assert_matches!(reparsed.type_by_id(kfunc_type_id).unwrap(), BtfType::Func(fixed) => { + assert_eq!(fixed.linkage(), FuncLinkage::Extern); + }); + } + #[test] fn test_sanitize_float() { let mut btf = Btf::new(); diff --git a/aya-obj/src/btf/types.rs b/aya-obj/src/btf/types.rs index 13226bc0..364df63f 100644 --- a/aya-obj/src/btf/types.rs +++ b/aya-obj/src/btf/types.rs @@ -276,6 +276,10 @@ impl Func { pub(crate) fn set_linkage(&mut self, linkage: FuncLinkage) { self.info = (self.info & 0xFFFF0000) | (linkage as u32) & 0xFFFF; } + + pub(crate) fn info(&self) -> u32 { + self.info + } } #[repr(C)] diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index c3d0d107..442e2d92 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -441,6 +441,7 @@ impl FromStr for ProgramSection { impl Object { /// Parses the binary data as an object file into an [Object] pub fn parse(data: &[u8]) -> Result { + log::debug!("Object::parse called with {} bytes", data.len()); let obj = object::read::File::parse(data).map_err(ParseError::ElfError)?; let endianness = obj.endianness(); @@ -492,10 +493,14 @@ impl Object { // as they're required to prepare function and line information // when parsing program sections if let Some(s) = obj.section_by_name(".BTF") { + log::debug!("Found .BTF section in object file"); bpf_obj.parse_section(Section::try_from(&s)?)?; if let Some(s) = obj.section_by_name(".BTF.ext") { + log::debug!("Found .BTF.ext section in object file"); bpf_obj.parse_section(Section::try_from(&s)?)?; } + } else { + log::debug!("No .BTF section found in object file"); } for s in obj.sections() {