* [PATCH 0/2] pahole: BTF alignment inference improvements @ 2025-06-25 15:23 Antoine Tenart 2025-06-25 15:23 ` [PATCH 1/2] btf_loader: infer alignment for zero-length arrays Antoine Tenart 2025-06-25 15:23 ` [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields Antoine Tenart 0 siblings, 2 replies; 7+ messages in thread From: Antoine Tenart @ 2025-06-25 15:23 UTC (permalink / raw) To: dwarves; +Cc: Antoine Tenart, pvalerio Hello, While generating C code using pahole we noticed alignment issues in rare cases: when having variable size and 0-length arrays, and when a hole follows a bitfield. It's worth noticing alignment is inferred when using BTF as a source as the format does not contain such information. As such the logic is tricky. The first patch fixes alignment inference for zero-length and variable length arrays. The second patch fixes alignment when a hole follows a bitfield. I generated a full vmlinux.h using `pahole --compile` before and after the changes, the differences looked reasonable to me. I've also ran the project's tests using GH actions. Thanks, Antoine Antoine Tenart (2): btf_loader: infer alignment for zero-length arrays btf_loader: fix smallest offset in case of bitfields btf_loader.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) -- 2.49.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] btf_loader: infer alignment for zero-length arrays 2025-06-25 15:23 [PATCH 0/2] pahole: BTF alignment inference improvements Antoine Tenart @ 2025-06-25 15:23 ` Antoine Tenart 2025-07-04 16:34 ` Alan Maguire 2025-06-25 15:23 ` [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields Antoine Tenart 1 sibling, 1 reply; 7+ messages in thread From: Antoine Tenart @ 2025-06-25 15:23 UTC (permalink / raw) To: dwarves; +Cc: Antoine Tenart, pvalerio Zero sized arrays do not go through the bitfield fixup logic, which in turn makes them to skip the alignment infer logic. The result is zero-length arrays being incorrectly aligned when an explicit statement is needed. E.g. this happens for variable length arrays at end of structures, or for 0-length ones. Before this patch we can see: struct skb_ext { refcount_t refcnt; /* 0 4 */ u8 offset[4]; /* 4 4 */ u8 chunks; /* 8 1 */ /* XXX 7 bytes hole, try to pack */ char data[]; /* 16 0 */ /* size: 16, cachelines: 1, members: 4 */ /* sum members: 9, holes: 1, sum holes: 7 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); offsetof(struct skb_ext, data) returns 9. After this patch we get: struct skb_ext { refcount_t refcnt; /* 0 4 */ u8 offset[4]; /* 4 4 */ u8 chunks; /* 8 1 */ /* XXX 7 bytes hole, try to pack */ char data[] __attribute__((__aligned__(8))); /* 16 0 */ /* size: 16, cachelines: 1, members: 4 */ /* sum members: 9, holes: 1, sum holes: 7 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 7 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); offsetof(struct skb_ext, data) returns 16. Signed-off-by: Antoine Tenart <atenart@kernel.org> --- btf_loader.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/btf_loader.c b/btf_loader.c index f4f9f65289b5..76771afedd95 100644 --- a/btf_loader.c +++ b/btf_loader.c @@ -648,6 +648,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * /* if BTF data is incorrect and has size == 0, skip field, * instead of crashing */ if (pos->byte_size == 0) { + pos->alignment = class__infer_alignment(conf, + pos->byte_offset, + tag__natural_alignment(type, cu), + smallest_offset); continue; } -- 2.49.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] btf_loader: infer alignment for zero-length arrays 2025-06-25 15:23 ` [PATCH 1/2] btf_loader: infer alignment for zero-length arrays Antoine Tenart @ 2025-07-04 16:34 ` Alan Maguire 2025-07-08 13:55 ` Antoine Tenart 0 siblings, 1 reply; 7+ messages in thread From: Alan Maguire @ 2025-07-04 16:34 UTC (permalink / raw) To: Antoine Tenart, dwarves; +Cc: pvalerio On 25/06/2025 16:23, Antoine Tenart wrote: > Zero sized arrays do not go through the bitfield fixup logic, which in > turn makes them to skip the alignment infer logic. The result is > zero-length arrays being incorrectly aligned when an explicit statement > is needed. E.g. this happens for variable length arrays at end of > structures, or for 0-length ones. > > Before this patch we can see: > > struct skb_ext { > refcount_t refcnt; /* 0 4 */ > u8 offset[4]; /* 4 4 */ > u8 chunks; /* 8 1 */ > > /* XXX 7 bytes hole, try to pack */ > > char data[]; /* 16 0 */ > > /* size: 16, cachelines: 1, members: 4 */ > /* sum members: 9, holes: 1, sum holes: 7 */ > /* last cacheline: 16 bytes */ > } __attribute__((__aligned__(8))); > > offsetof(struct skb_ext, data) returns 9. > > After this patch we get: > > struct skb_ext { > refcount_t refcnt; /* 0 4 */ > u8 offset[4]; /* 4 4 */ > u8 chunks; /* 8 1 */ > > /* XXX 7 bytes hole, try to pack */ > > char data[] __attribute__((__aligned__(8))); /* 16 0 */ > > /* size: 16, cachelines: 1, members: 4 */ > /* sum members: 9, holes: 1, sum holes: 7 */ > /* forced alignments: 1, forced holes: 1, sum forced holes: 7 */ > /* last cacheline: 16 bytes */ > } __attribute__((__aligned__(8))); > > offsetof(struct skb_ext, data) returns 16. > > Signed-off-by: Antoine Tenart <atenart@kernel.org> Reviewed-by: Alan Maguire <alan.maguire@oracle.com> with a small suggestion below.. > --- > btf_loader.c | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/btf_loader.c b/btf_loader.c > index f4f9f65289b5..76771afedd95 100644 > --- a/btf_loader.c > +++ b/btf_loader.c > @@ -648,6 +648,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * > /* if BTF data is incorrect and has size == 0, skip field, > * instead of crashing */ nit: could we update the comment here to reflect the change, adding something like "however we still need to infer alignment for cases like zero/variable-length arays". > if (pos->byte_size == 0) { > + pos->alignment = class__infer_alignment(conf, > + pos->byte_offset, > + tag__natural_alignment(type, cu), > + smallest_offset); > continue; > } > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] btf_loader: infer alignment for zero-length arrays 2025-07-04 16:34 ` Alan Maguire @ 2025-07-08 13:55 ` Antoine Tenart 0 siblings, 0 replies; 7+ messages in thread From: Antoine Tenart @ 2025-07-08 13:55 UTC (permalink / raw) To: Alan Maguire; +Cc: Antoine Tenart, dwarves, pvalerio On Fri, Jul 04, 2025 at 05:34:47PM +0100, Alan Maguire wrote: > On 25/06/2025 16:23, Antoine Tenart wrote: > > Zero sized arrays do not go through the bitfield fixup logic, which in > > turn makes them to skip the alignment infer logic. The result is > > zero-length arrays being incorrectly aligned when an explicit statement > > is needed. E.g. this happens for variable length arrays at end of > > structures, or for 0-length ones. > > > > Before this patch we can see: > > > > struct skb_ext { > > refcount_t refcnt; /* 0 4 */ > > u8 offset[4]; /* 4 4 */ > > u8 chunks; /* 8 1 */ > > > > /* XXX 7 bytes hole, try to pack */ > > > > char data[]; /* 16 0 */ > > > > /* size: 16, cachelines: 1, members: 4 */ > > /* sum members: 9, holes: 1, sum holes: 7 */ > > /* last cacheline: 16 bytes */ > > } __attribute__((__aligned__(8))); > > > > offsetof(struct skb_ext, data) returns 9. > > > > After this patch we get: > > > > struct skb_ext { > > refcount_t refcnt; /* 0 4 */ > > u8 offset[4]; /* 4 4 */ > > u8 chunks; /* 8 1 */ > > > > /* XXX 7 bytes hole, try to pack */ > > > > char data[] __attribute__((__aligned__(8))); /* 16 0 */ > > > > /* size: 16, cachelines: 1, members: 4 */ > > /* sum members: 9, holes: 1, sum holes: 7 */ > > /* forced alignments: 1, forced holes: 1, sum forced holes: 7 */ > > /* last cacheline: 16 bytes */ > > } __attribute__((__aligned__(8))); > > > > offsetof(struct skb_ext, data) returns 16. > > > > Signed-off-by: Antoine Tenart <atenart@kernel.org> > > Reviewed-by: Alan Maguire <alan.maguire@oracle.com> > > with a small suggestion below.. > > > --- > > btf_loader.c | 4 ++++ > > 1 file changed, 4 insertions(+) > > > > diff --git a/btf_loader.c b/btf_loader.c > > index f4f9f65289b5..76771afedd95 100644 > > --- a/btf_loader.c > > +++ b/btf_loader.c > > @@ -648,6 +648,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * > > /* if BTF data is incorrect and has size == 0, skip field, > > * instead of crashing */ > > nit: could we update the comment here to reflect the change, adding > something like "however we still need to infer alignment for cases like > zero/variable-length arays". Makes sense, I'll improve the comment in v2. Thanks! > > > > if (pos->byte_size == 0) { > > + pos->alignment = class__infer_alignment(conf, > > + pos->byte_offset, > > + tag__natural_alignment(type, cu), > > + smallest_offset); > > continue; > > } > > > ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields 2025-06-25 15:23 [PATCH 0/2] pahole: BTF alignment inference improvements Antoine Tenart 2025-06-25 15:23 ` [PATCH 1/2] btf_loader: infer alignment for zero-length arrays Antoine Tenart @ 2025-06-25 15:23 ` Antoine Tenart 2025-07-04 16:40 ` Alan Maguire 1 sibling, 1 reply; 7+ messages in thread From: Antoine Tenart @ 2025-06-25 15:23 UTC (permalink / raw) To: dwarves; +Cc: Antoine Tenart, pvalerio In case the member is a bitfield, using pos->byte_size can be misleading as it can be larger than the actual size. In such case we could miss an actual hole in the struct and not infer the alignment as expected. Before this patch we can see: $ pahole nf_tables.btf -C nft_rule_dp struct nft_rule_dp { u64 is_last:1; /* 0: 0 8 */ u64 dlen:12; /* 0: 1 8 */ u64 handle:42; /* 0:13 8 */ /* XXX 9 bits hole, try to pack */ unsigned char data[]; /* 8 0 */ /* size: 8, cachelines: 1, members: 4 */ /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ /* last cacheline: 8 bytes */ }; offsetof(struct nft_rule_dp, data) returns 7. Where `data` won't be aligned as expected, as the first member fits on the byte left after the bitfield. After this patch we get: $ pahole nf_tables.btf -C nft_rule_dp struct nft_rule_dp { u64 is_last:1; /* 0: 0 8 */ u64 dlen:12; /* 0: 1 8 */ u64 handle:42; /* 0:13 8 */ /* XXX 9 bits hole, try to pack */ unsigned char data[] __attribute__((__aligned__(2))); /* 8 0 */ /* size: 8, cachelines: 1, members: 4 */ /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ /* forced alignments: 1 */ /* last cacheline: 8 bytes */ }; offsetof(struct nft_rule_dp, data) returns 8. Thanks to Paolo Valerio for helping, including narrowing down where the issue was (BTF decoder). Signed-off-by: Antoine Tenart <atenart@kernel.org> --- btf_loader.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/btf_loader.c b/btf_loader.c index 76771afedd95..3beb5bba5f94 100644 --- a/btf_loader.c +++ b/btf_loader.c @@ -676,7 +676,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * pos->byte_offset, tag__natural_alignment(type, cu), smallest_offset); - smallest_offset = pos->byte_offset + pos->byte_size; + smallest_offset = pos->byte_offset; + smallest_offset += pos->bitfield_size ? + (pos->bitfield_offset + pos->bitfield_size + 7) / 8 : + pos->byte_size; } tag_type->alignment = class__infer_alignment(conf, -- 2.49.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields 2025-06-25 15:23 ` [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields Antoine Tenart @ 2025-07-04 16:40 ` Alan Maguire 2025-07-08 14:01 ` Antoine Tenart 0 siblings, 1 reply; 7+ messages in thread From: Alan Maguire @ 2025-07-04 16:40 UTC (permalink / raw) To: Antoine Tenart, dwarves; +Cc: pvalerio On 25/06/2025 16:23, Antoine Tenart wrote: > In case the member is a bitfield, using pos->byte_size can be misleading > as it can be larger than the actual size. In such case we could miss an > actual hole in the struct and not infer the alignment as expected. > > Before this patch we can see: > > $ pahole nf_tables.btf -C nft_rule_dp > struct nft_rule_dp { > u64 is_last:1; /* 0: 0 8 */ > u64 dlen:12; /* 0: 1 8 */ > u64 handle:42; /* 0:13 8 */ > > /* XXX 9 bits hole, try to pack */ > > unsigned char data[]; /* 8 0 */ > > /* size: 8, cachelines: 1, members: 4 */ > /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ > /* last cacheline: 8 bytes */ > }; > > offsetof(struct nft_rule_dp, data) returns 7. > > Where `data` won't be aligned as expected, as the first member fits on > the byte left after the bitfield. > > After this patch we get: > > $ pahole nf_tables.btf -C nft_rule_dp > struct nft_rule_dp { > u64 is_last:1; /* 0: 0 8 */ > u64 dlen:12; /* 0: 1 8 */ > u64 handle:42; /* 0:13 8 */ > > /* XXX 9 bits hole, try to pack */ > > unsigned char data[] __attribute__((__aligned__(2))); /* 8 0 */ > > /* size: 8, cachelines: 1, members: 4 */ > /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ > /* forced alignments: 1 */ > /* last cacheline: 8 bytes */ > }; > > offsetof(struct nft_rule_dp, data) returns 8. > > Thanks to Paolo Valerio for helping, including narrowing down where the > issue was (BTF decoder). > > Signed-off-by: Antoine Tenart <atenart@kernel.org> Reviewed-by: Alan Maguire <alan.maguire@oracle.com> > --- > btf_loader.c | 5 ++++- > 1 file changed, 4 insertions(+), 1 deletion(-) > > diff --git a/btf_loader.c b/btf_loader.c > index 76771afedd95..3beb5bba5f94 100644 > --- a/btf_loader.c > +++ b/btf_loader.c > @@ -676,7 +676,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * > pos->byte_offset, > tag__natural_alignment(type, cu), > smallest_offset); > - smallest_offset = pos->byte_offset + pos->byte_size; > + smallest_offset = pos->byte_offset; > + smallest_offset += pos->bitfield_size ? > + (pos->bitfield_offset + pos->bitfield_size + 7) / 8 : > + pos->byte_size; would be good to have a comment here explaining the situation too; it's quite a subtle problem so anything that helps explain things would be great. Thanks for tracking this down! > } > > tag_type->alignment = class__infer_alignment(conf, ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields 2025-07-04 16:40 ` Alan Maguire @ 2025-07-08 14:01 ` Antoine Tenart 0 siblings, 0 replies; 7+ messages in thread From: Antoine Tenart @ 2025-07-08 14:01 UTC (permalink / raw) To: Alan Maguire; +Cc: dwarves, pvalerio On Fri, Jul 04, 2025 at 05:40:36PM +0100, Alan Maguire wrote: > On 25/06/2025 16:23, Antoine Tenart wrote: > > In case the member is a bitfield, using pos->byte_size can be misleading > > as it can be larger than the actual size. In such case we could miss an > > actual hole in the struct and not infer the alignment as expected. > > > > Before this patch we can see: > > > > $ pahole nf_tables.btf -C nft_rule_dp > > struct nft_rule_dp { > > u64 is_last:1; /* 0: 0 8 */ > > u64 dlen:12; /* 0: 1 8 */ > > u64 handle:42; /* 0:13 8 */ > > > > /* XXX 9 bits hole, try to pack */ > > > > unsigned char data[]; /* 8 0 */ > > > > /* size: 8, cachelines: 1, members: 4 */ > > /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ > > /* last cacheline: 8 bytes */ > > }; > > > > offsetof(struct nft_rule_dp, data) returns 7. > > > > Where `data` won't be aligned as expected, as the first member fits on > > the byte left after the bitfield. > > > > After this patch we get: > > > > $ pahole nf_tables.btf -C nft_rule_dp > > struct nft_rule_dp { > > u64 is_last:1; /* 0: 0 8 */ > > u64 dlen:12; /* 0: 1 8 */ > > u64 handle:42; /* 0:13 8 */ > > > > /* XXX 9 bits hole, try to pack */ > > > > unsigned char data[] __attribute__((__aligned__(2))); /* 8 0 */ > > > > /* size: 8, cachelines: 1, members: 4 */ > > /* sum bitfield members: 55 bits, bit holes: 1, sum bit holes: 9 bits */ > > /* forced alignments: 1 */ > > /* last cacheline: 8 bytes */ > > }; > > > > offsetof(struct nft_rule_dp, data) returns 8. > > > > Thanks to Paolo Valerio for helping, including narrowing down where the > > issue was (BTF decoder). > > > > Signed-off-by: Antoine Tenart <atenart@kernel.org> > > Reviewed-by: Alan Maguire <alan.maguire@oracle.com> > > > --- > > btf_loader.c | 5 ++++- > > 1 file changed, 4 insertions(+), 1 deletion(-) > > > > diff --git a/btf_loader.c b/btf_loader.c > > index 76771afedd95..3beb5bba5f94 100644 > > --- a/btf_loader.c > > +++ b/btf_loader.c > > @@ -676,7 +676,10 @@ static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag * > > pos->byte_offset, > > tag__natural_alignment(type, cu), > > smallest_offset); > > - smallest_offset = pos->byte_offset + pos->byte_size; > > + smallest_offset = pos->byte_offset; > > + smallest_offset += pos->bitfield_size ? > > + (pos->bitfield_offset + pos->bitfield_size + 7) / 8 : > > + pos->byte_size; > > > would be good to have a comment here explaining the situation too; it's > quite a subtle problem so anything that helps explain things would be > great. Thanks for tracking this down! Will do. Thanks! ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-07-08 14:01 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-06-25 15:23 [PATCH 0/2] pahole: BTF alignment inference improvements Antoine Tenart 2025-06-25 15:23 ` [PATCH 1/2] btf_loader: infer alignment for zero-length arrays Antoine Tenart 2025-07-04 16:34 ` Alan Maguire 2025-07-08 13:55 ` Antoine Tenart 2025-06-25 15:23 ` [PATCH 2/2] btf_loader: fix smallest offset in case of bitfields Antoine Tenart 2025-07-04 16:40 ` Alan Maguire 2025-07-08 14:01 ` Antoine Tenart
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox