Skip to content

Commit

Permalink
libbpf-cargo: Fix skeleton generation for enum64 types
Browse files Browse the repository at this point in the history
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 <deso@posteo.net>
  • Loading branch information
d-e-s-o committed Dec 11, 2024
1 parent f3ebab9 commit bb1753e
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 3 deletions.
5 changes: 5 additions & 0 deletions libbpf-cargo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
95 changes: 92 additions & 3 deletions libbpf-cargo/src/gen/btf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,93 @@ use super::InternalMapType;

const ANON_PREFIX: &str = "__anon_";


#[derive(Clone, Debug)]
pub(crate) enum Either<A, B> {
A(A),
B(B),
}

impl<A, B, T> Iterator for Either<A, B>
where
A: Iterator<Item = T>,
B: Iterator<Item = T>,
{
type Item = T;

fn next(&mut self) -> Option<Self::Item> {
match self {
Self::A(a) => a.next(),
Self::B(b) => b.next(),
}
}
}

impl<A, B, T> ExactSizeIterator for Either<A, B>
where
A: ExactSizeIterator<Item = T>,
B: ExactSizeIterator<Item = T>,
{
}


/// 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::Enum<'btf>, 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<Item = types::Enum64Member<'_>> {
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<types::Enum<'btf>> for EitherEnum<'btf> {
fn from(other: types::Enum<'btf>) -> Self {
Self::A(other)
}
}

impl<'btf> From<types::Enum64<'btf>> 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
Expand Down Expand Up @@ -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()),
});
}
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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",
Expand Down
88 changes: 88 additions & 0 deletions libbpf-cargo/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <bpf/bpf_helpers.h>
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 <bpf/bpf_helpers.h>
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#"
Expand Down
9 changes: 9 additions & 0 deletions libbpf-rs/src/btf/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit bb1753e

Please sign in to comment.