Add a check to skip .ksyms/.kconfig from BTF section fixups

Also add test.
grantseltzer 7 days ago
parent 0780eef640
commit 31b0881819

@ -562,6 +562,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 +708,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"
@ -1589,6 +1612,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 +1905,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();

@ -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)]

Loading…
Cancel
Save