From bb1753e62c301adcf19230e77faabf8375b85609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 10 Dec 2024 15:36:48 -0800 Subject: [PATCH] libbpf-cargo: Fix skeleton generation for enum64 types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our skeleton generation fails in the presence of enum64 types in the BTF. The reason for that is that we haven't implemented the necessary type generation bits for enum64. Add the necessary code generation logic. Signed-off-by: Daniel Müller --- libbpf-cargo/CHANGELOG.md | 5 ++ libbpf-cargo/src/gen/btf.rs | 95 +++++++++++++++++++++++++++++++++++-- libbpf-cargo/src/test.rs | 88 ++++++++++++++++++++++++++++++++++ libbpf-rs/src/btf/types.rs | 9 ++++ 4 files changed, 194 insertions(+), 3 deletions(-) diff --git a/libbpf-cargo/CHANGELOG.md b/libbpf-cargo/CHANGELOG.md index 3766ef4e..352da7b5 100644 --- a/libbpf-cargo/CHANGELOG.md +++ b/libbpf-cargo/CHANGELOG.md @@ -1,3 +1,8 @@ +Unreleased +---------- +- Fixed skeleton generation when `enum64` types are present + + 0.25.0-beta.0 ------------- - Represent C enums with custom types and const fields diff --git a/libbpf-cargo/src/gen/btf.rs b/libbpf-cargo/src/gen/btf.rs index b8b76d45..3f3bcbab 100644 --- a/libbpf-cargo/src/gen/btf.rs +++ b/libbpf-cargo/src/gen/btf.rs @@ -31,6 +31,93 @@ use super::InternalMapType; const ANON_PREFIX: &str = "__anon_"; + +#[derive(Clone, Debug)] +pub(crate) enum Either { + A(A), + B(B), +} + +impl Iterator for Either +where + A: Iterator, + B: Iterator, +{ + type Item = T; + + fn next(&mut self) -> Option { + match self { + Self::A(a) => a.next(), + Self::B(b) => b.next(), + } + } +} + +impl ExactSizeIterator for Either +where + A: ExactSizeIterator, + B: ExactSizeIterator, +{ +} + + +/// Convert an `EnumMember` into a `Enum64Member`. +fn enum64_member_from_enum_member(other: types::EnumMember<'_>) -> types::Enum64Member<'_> { + types::Enum64Member { + name: other.name, + value: other.value.into(), + } +} + + +type EitherEnum<'btf> = Either, types::Enum64<'btf>>; + +impl EitherEnum<'_> { + fn size(&self) -> usize { + match self { + Self::A(t) => t.size(), + Self::B(t) => t.size(), + } + } + + fn is_signed(&self) -> bool { + match self { + Self::A(t) => t.is_signed(), + Self::B(t) => t.is_signed(), + } + } + + fn iter(&self) -> impl ExactSizeIterator> { + match self { + Self::A(t) => Either::A(t.iter().map(enum64_member_from_enum_member)), + Self::B(t) => Either::B(t.iter()), + } + } +} + +impl<'btf> From> for EitherEnum<'btf> { + fn from(other: types::Enum<'btf>) -> Self { + Self::A(other) + } +} + +impl<'btf> From> for EitherEnum<'btf> { + fn from(other: types::Enum64<'btf>) -> Self { + Self::B(other) + } +} + +impl<'btf> Deref for EitherEnum<'btf> { + type Target = BtfType<'btf>; + + fn deref(&self) -> &Self::Target { + match self { + Self::A(t) => t, + Self::B(t) => t, + } + } +} + /// Check whether the provided type is "unsafe" to use. /// /// A type is considered unsafe by this function if it is not valid for @@ -439,7 +526,8 @@ impl StructOps {{ .type_definition_for_composites(def, &mut self.deps, t)? } } - BtfKind::Enum(t) => self.btf.type_definition_for_enums(def, t)?, + BtfKind::Enum(t) => self.btf.type_definition_for_enums(def, t.into())?, + BtfKind::Enum64(t) => self.btf.type_definition_for_enums(def, t.into())?, _ => bail!("Invalid type: {:?}", ty.kind()), }); } @@ -548,7 +636,8 @@ impl<'s> GenBtf<'s> { btf_type_match!(match ty { BtfKind::Composite(t) => self.type_definition_for_composites(&mut def, &mut dependent_types, t)?, - BtfKind::Enum(t) => self.type_definition_for_enums(&mut def, t)?, + BtfKind::Enum(t) => self.type_definition_for_enums(&mut def, t.into())?, + BtfKind::Enum64(t) => self.type_definition_for_enums(&mut def, t.into())?, BtfKind::DataSec(t) => self.type_definition_for_datasec(&mut def, &mut dependent_types, t)?, _ => bail!("Invalid type: {:?}", ty.kind()), @@ -786,7 +875,7 @@ impl<'s> GenBtf<'s> { Ok(()) } - fn type_definition_for_enums(&self, def: &mut String, t: types::Enum<'_>) -> Result<()> { + fn type_definition_for_enums(&self, def: &mut String, t: EitherEnum<'_>) -> Result<()> { let repr_size = match t.size() { 1 => "8", 2 => "16", diff --git a/libbpf-cargo/src/test.rs b/libbpf-cargo/src/test.rs index 8003f3a5..0443aada 100644 --- a/libbpf-cargo/src/test.rs +++ b/libbpf-cargo/src/test.rs @@ -1947,6 +1947,94 @@ impl Default for Foo { assert_definition(&btf, &struct_bar, expected_output); } +#[test] +fn test_btf_dump_definition_enum64() { + let prog_text = r#" +#include "vmlinux.h" +#include + +enum Foo { + Zero = 0, + Infinite = 0xffffffffffffffff, +}; + +struct Bar { + enum Foo foo; +}; +struct Bar bar; +"#; + + let expected_output = r#" +#[derive(Debug, Default, Copy, Clone)] +#[repr(C)] +pub struct Bar { + pub foo: Foo, +} +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(transparent)] +pub struct Foo(pub u64); +#[allow(non_upper_case_globals)] +impl Foo { + pub const Zero: Foo = Foo(0); + pub const Infinite: Foo = Foo(18446744073709551615); +} +impl Default for Foo { + fn default() -> Self { Foo::Zero } +} +"#; + + let mmap = build_btf_mmap(prog_text); + let btf = btf_from_mmap(&mmap); + + // Find our struct + let struct_bar = find_type_in_btf!(btf, types::Struct<'_>, "Bar"); + assert_definition(&btf, &struct_bar, expected_output); +} + +#[test] +fn test_btf_dump_definition_enum64_signed() { + let prog_text = r#" +#include "vmlinux.h" +#include + +enum Foo { + Zero = 0, + Whatevs = -922337854775808, +}; + +struct Bar { + enum Foo foo; +}; +struct Bar bar; +"#; + + let expected_output = r#" +#[derive(Debug, Default, Copy, Clone)] +#[repr(C)] +pub struct Bar { + pub foo: Foo, +} +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(transparent)] +pub struct Foo(pub i64); +#[allow(non_upper_case_globals)] +impl Foo { + pub const Zero: Foo = Foo(0); + pub const Whatevs: Foo = Foo(-922337854775808); +} +impl Default for Foo { + fn default() -> Self { Foo::Zero } +} +"#; + + let mmap = build_btf_mmap(prog_text); + let btf = btf_from_mmap(&mmap); + + // Find our struct + let struct_bar = find_type_in_btf!(btf, types::Struct<'_>, "Bar"); + assert_definition(&btf, &struct_bar, expected_output); +} + #[test] fn test_btf_dump_definition_union() { let prog_text = r#" diff --git a/libbpf-rs/src/btf/types.rs b/libbpf-rs/src/btf/types.rs index bf22458c..b87cd368 100644 --- a/libbpf-rs/src/btf/types.rs +++ b/libbpf-rs/src/btf/types.rs @@ -852,6 +852,15 @@ gen_collection_concrete_type! { } } +impl Enum64<'_> { + /// Check whether the enum is signed or not. + #[inline] + pub fn is_signed(&self) -> bool { + self.kind_flag() + } +} + + /// A macro that allows matching on the type of a [`BtfType`] as if it was an enum. /// /// Each pattern can be of two types.