Skip to content

Commit 3ae5ce4

Browse files
d-e-s-odanielocfb
authored andcommitted
libbpf-cargo: Improve handling of trailing bitfields
Improve the handling of trailing bitfields in struct types, for which we missed the emitting of padding bytes. Closes: #1285 Signed-off-by: Daniel Müller <[email protected]>
1 parent ce01e91 commit 3ae5ce4

File tree

3 files changed

+99
-6
lines changed

3 files changed

+99
-6
lines changed

libbpf-cargo/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Unreleased
22
----------
3+
- Fixed Rust type generation for trailing bitfields in composite C types
34
- Allowlisted `libbpf-sys` `1.6.2`
45

56

libbpf-cargo/src/gen/btf.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,17 @@ fn is_struct_packed(composite: &types::Composite<'_>, btf: &Btf<'_>) -> Result<b
164164
/// Given a `current_offset` (in bytes) into a struct and a `required_offset` (in bytes) that
165165
/// type `type_id` needs to be placed at, returns how much padding must be inserted before
166166
/// `type_id`.
167+
///
168+
/// If `force` is `true`, then padding will be reported even if the
169+
/// current offset matches the required one, when taking into account
170+
/// type alignment (this can be necessary for bitfield handling, for
171+
/// example).
167172
fn required_padding(
168173
current_offset: usize,
169174
required_offset: usize,
170175
ty: &BtfType<'_>,
171176
packed: bool,
177+
force: bool,
172178
) -> Result<usize> {
173179
ensure!(
174180
current_offset <= required_offset,
@@ -193,7 +199,7 @@ fn required_padding(
193199

194200
// If we aren't aligning to the natural offset, padding needs to be inserted
195201
let aligned_offset = (current_offset + align.get() - 1) / align * align.get();
196-
if aligned_offset == required_offset {
202+
if !force && aligned_offset == required_offset {
197203
Ok(0)
198204
} else {
199205
Ok(required_offset - current_offset)
@@ -776,14 +782,24 @@ impl<'s> GenBtf<'s> {
776782
let mut impl_default: Vec<String> = Vec::new(); // output for impl Default
777783
let mut gen_impl_default = false; // whether to output impl Default or use #[derive]
778784

785+
// An indication as to whether to force emitting of padding
786+
// bytes. This is necessary for somewhat sensible handling of
787+
// a bitfield that makes up the last member of a struct.
788+
let mut force_pad = false;
779789
let mut offset = 0; // In bytes
780790
for member in t.iter() {
781791
let member_offset = match member.attr {
782-
MemberAttr::Normal { offset } => offset,
792+
MemberAttr::Normal { offset } => {
793+
force_pad = false;
794+
offset
795+
}
783796
// Bitfields are tricky to get correct, if at all possible. For
784797
// now we just skip them, which results in them being covered by
785798
// padding bytes.
786-
MemberAttr::BitField { .. } => continue,
799+
MemberAttr::BitField { .. } => {
800+
force_pad = true;
801+
continue;
802+
}
787803
};
788804

789805
let field_ty = self
@@ -819,6 +835,7 @@ impl<'s> GenBtf<'s> {
819835
member_offset as usize / 8,
820836
&self.type_by_id::<BtfType<'_>>(member.ty).unwrap(),
821837
packed,
838+
false,
822839
)?;
823840

824841
if padding != 0 {
@@ -885,7 +902,7 @@ impl<'s> GenBtf<'s> {
885902

886903
if t.is_struct {
887904
let struct_size = t.size();
888-
let padding = required_padding(offset, struct_size, &t, packed)?;
905+
let padding = required_padding(offset, struct_size, &t, packed, force_pad)?;
889906
if padding != 0 {
890907
agg_content.push(format!(r#" pub __pad_{offset}: [u8; {padding}],"#,));
891908
impl_default.push(format!(
@@ -1055,8 +1072,13 @@ impl<'s> GenBtf<'s> {
10551072
dependent_types.push(next_ty);
10561073
}
10571074

1058-
let padding =
1059-
required_padding(offset as usize, datasec_var.offset as usize, &var, false)?;
1075+
let padding = required_padding(
1076+
offset as usize,
1077+
datasec_var.offset as usize,
1078+
&var,
1079+
false,
1080+
false,
1081+
)?;
10601082
if padding != 0 {
10611083
writeln!(def, r#" __pad_{offset}: [u8; {padding}],"#)?;
10621084
}

libbpf-cargo/src/test.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,76 @@ fn test_btf_dump_insane_align() {
15881588
assert_definition(&btf, &struct_foo, expected_output);
15891589
}
15901590

1591+
#[test]
1592+
fn test_btf_dump_align_trailing_bitfield() {
1593+
let prog_text = indoc! {r#"
1594+
#include "vmlinux.h"
1595+
#include <bpf/bpf_helpers.h>
1596+
1597+
struct Foo {
1598+
__u8 x;
1599+
__u32 y : 23;
1600+
};
1601+
1602+
struct Bar {
1603+
__u8 x;
1604+
__u32 y : 23;
1605+
} __attribute__((__packed__));
1606+
1607+
struct Baz {
1608+
__u8 x;
1609+
__u32 y : 20;
1610+
__u32 z : 3;
1611+
};
1612+
1613+
struct Foo foo = {};
1614+
struct Bar bar = {};
1615+
struct Baz baz = {};
1616+
"#};
1617+
1618+
let expected_foo_output = indoc! {r#"
1619+
#[derive(Debug, Default, Copy, Clone)]
1620+
#[repr(C)]
1621+
pub struct Foo {
1622+
pub x: u8,
1623+
pub __pad_1: [u8; 3],
1624+
}
1625+
"#};
1626+
1627+
// Note that ideally the `repr` would be `packed` as well, but we
1628+
// have no way of telling that this is the desired outcome based on
1629+
// the BTF (it is simply identical to the non-packed `Foo`).
1630+
let expected_bar_output = indoc! {r#"
1631+
#[derive(Debug, Default, Copy, Clone)]
1632+
#[repr(C)]
1633+
pub struct Bar {
1634+
pub x: u8,
1635+
pub __pad_1: [u8; 3],
1636+
}
1637+
"#};
1638+
1639+
let expected_baz_output = indoc! {r#"
1640+
#[derive(Debug, Default, Copy, Clone)]
1641+
#[repr(C)]
1642+
pub struct Baz {
1643+
pub x: u8,
1644+
pub __pad_1: [u8; 3],
1645+
}
1646+
"#};
1647+
1648+
let mmap = build_btf_mmap(prog_text);
1649+
let btf = btf_from_mmap(&mmap);
1650+
1651+
let struct_foo = find_type_in_btf!(btf, types::Struct<'_>, "Foo");
1652+
assert_definition(&btf, &struct_foo, expected_foo_output);
1653+
1654+
let struct_bar = find_type_in_btf!(btf, types::Struct<'_>, "Bar");
1655+
assert_definition(&btf, &struct_bar, expected_bar_output);
1656+
1657+
let struct_baz = find_type_in_btf!(btf, types::Struct<'_>, "Baz");
1658+
assert_definition(&btf, &struct_baz, expected_baz_output);
1659+
}
1660+
15911661
#[test]
15921662
fn test_btf_dump_basic_long_array() {
15931663
let prog_text = indoc! {r#"

0 commit comments

Comments
 (0)