From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>,
Jiri Olsa <jolsa@kernel.org>,
Clark Williams <williams@redhat.com>,
dwarves@vger.kernel.org, bpf@vger.kernel.org,
Andrii Nakryiko <andrii@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Mark Wieelard <mjw@redhat.com>,
rust-for-linux <rust-for-linux@vger.kernel.org>,
Gary Guo <gary@garyguo.net>
Subject: Re: [PATCHES 00/12] pahole: Support more rust tags and references to dwz alternate debug files
Date: Fri, 7 Aug 2026 17:30:53 -0300 [thread overview]
Message-ID: <anZAfc7sikAx8ikS@x1> (raw)
In-Reply-To: <anYpwcDBtkzE3WIt@x1>
On Fri, Aug 07, 2026 at 03:53:53PM -0300, Arnaldo Carvalho de Melo wrote:
> On Fri, Aug 07, 2026 at 07:34:10PM +0200, Miguel Ojeda wrote:
> > On Fri, Aug 7, 2026 at 2:43 PM Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> > > Adding Miguel to the CC list, Miguel we're trying to improve BTF support
> > > for Rust, this series handles multiple enabling fixes/improvements that
> > > gets us to the next level, i.e. we can load everything from DWARF, now
> > Thanks for the Cc! Cc'ing rust-for-linux and Gary.
> > > its time to try to map it to existing BTF kinds and propose BTF
> > > extensions when we can't use existing encodings.
> > Sounds cool :)
> > > Inferring from "does any member have a niche layout" means
> > > reverse-engineering rustc's layout algorithm, which is explicitly
> > > unstable and unspecified. It'd break silently on a compiler bump, with
> > > no way to tell from the DWARF that you got it wrong.
> > Yeah, the LLM is correct here: the Rust compiler gives very few
> > guarantees for the default `repr`, i.e. in common cases it has the
> > freedom to use whatever layout it wants (i.e. even for essentially
> > equivalent types in the same compilation unit) -- the official docs
> > are at:
> > https://doc.rust-lang.org/reference/type-layout.html
> > > u8 __discriminant; /* 0 1 */
> > For the niche cases like `Option<NonZero<u32>>`, what would be printed?
> Can you think about one such niche case that is present in the kernel
> rust .o files right now? I tried finding the `Option<NonZero<u32>>` to
> look at its DWARF but couldn't find one.
Good, so I went ahead and built the exact case, together with the
explicit `Option<u32>` one, so we can compare them. The source,
containing the struct I dump below:
use std::num::NonZeroU32;
#[no_mangle]
pub struct Container {
pub a: Option<NonZeroU32>, // niche: 4 bytes, tag == payload
pub b: Option<u32>, // explicit: tag at 0, payload at 4
pub c: Option<i32>,
}
#[no_mangle]
pub fn consume(c: Container) -> u32 {
c.a.map(|x| x.get()).unwrap_or(0) + c.b.unwrap_or(0)
}
fn main() {
let c = Container { a: None, b: Some(3), c: Some(-1) };
let _ = consume(c);
}
Compile (rustc 1.93.x here, don't link, just get an object file,
otherwise you'll debug 1M+ lines of std DWARF and the type will be
there but buried):
acme@number:~/git/pahole$ rustc --edition 2021 --emit=obj -C debuginfo=2 \
option_non_zero.rs -o option_non_zero.o
then dump the DWARF, note that the DW_AT_name has the expanded generic,
i.e. 'Option<core::num::nonzero::NonZero<u32>>', the NonZeroU32 alias
seems to be expanded at compile time and, thus, doesn't appear in DWARF:
acme@number:~/git/pahole$ readelf --debug-dump=info option_non_zero.o \
| grep -n 'nonzero::NonZero<u32>'
acme@number:~$ readelf --debug-dump=info option_non_zero.o | grep -n 'nonzero::NonZero<u32>'
295: <2ca> DW_AT_name : (indirect string, offset: 0x213): Option<core::num::nonzero::NonZero<u32>>
348: <32e> DW_AT_name : (indirect string, offset: 0x2ef): map<core::num::nonzero::NonZero<u32>, u32, option_non_zero::consume::{closure_env#0}>
acme@number:~$
and you can see the niche case layout, size 4, the discriminant member
is the payload itself, same offset, same size, and the Some variant
doesn't even get a DW_AT_discr_value, it is just the default, so the
answer: nothing separate to print, the tag IS the payload.
Here is the full DIE for the niche case:
acme@number:~$ readelf --debug-dump=info option_non_zero.o | grep -B1 -A25 'Option<core::num::nonzero::NonZero<u32>>'
<3><2c9>: Abbrev Number: 19 (DW_TAG_structure_type)
<2ca> DW_AT_name : (indirect string, offset: 0x213): Option<core::num::nonzero::NonZero<u32>>
<2ce> DW_AT_byte_size : 4
<2cf> DW_AT_accessibility: 1 (public)
<2d0> DW_AT_alignment : 4
<4><2d1>: Abbrev Number: 26 (DW_TAG_variant_part)
<2d2> DW_AT_discr : <0x2d6>
<5><2d6>: Abbrev Number: 27 (DW_TAG_member)
<2d7> DW_AT_type : <0x5b2>
<2db> DW_AT_alignment : 4
<2dc> DW_AT_data_member_location: 0
<2dd> DW_AT_artificial : 1
<5><2dd>: Abbrev Number: 28 (DW_TAG_variant)
<2de> DW_AT_discr_value : 0
<6><2df>: Abbrev Number: 4 (DW_TAG_member)
<2e0> DW_AT_name : (indirect string, offset: 0x1ce): None
<2e4> DW_AT_type : <0x2f9>
<2e8> DW_AT_alignment : 4
<2e9> DW_AT_data_member_location: 0
<6><2ea>: Abbrev Number: 0
<5><2eb>: Abbrev Number: 29 (DW_TAG_variant)
<6><2ec>: Abbrev Number: 4 (DW_TAG_member)
<2ed> DW_AT_name : (indirect string, offset: 0x20e): Some
<2f1> DW_AT_type : <0x30b>
<2f5> DW_AT_alignment : 4
<2f6> DW_AT_data_member_location: 0
<6><2f7>: Abbrev Number: 0
acme@number:~$
On the explicit `Option<u32>` case the picture is different, the tag is
a real member at offset 0 with 4 bytes and Some.__0 moved to offset 4:
acme@number:~$ readelf --debug-dump=info option_non_zero.o | grep -B1 -A25 'Option<u32>'
<3><360>: Abbrev Number: 19 (DW_TAG_structure_type)
<361> DW_AT_name : (indirect string, offset: 0x345): Option<u32>
<365> DW_AT_byte_size : 8
<366> DW_AT_accessibility: 1 (public)
<367> DW_AT_alignment : 4
<4><368>: Abbrev Number: 26 (DW_TAG_variant_part)
<369> DW_AT_discr : <0x36d>
<5><36d>: Abbrev Number: 27 (DW_TAG_member)
<36e> DW_AT_type : <0x5b2>
<372> DW_AT_alignment : 4
<373> DW_AT_data_member_location: 0
<374> DW_AT_artificial : 1
<5><374>: Abbrev Number: 28 (DW_TAG_variant)
<375> DW_AT_discr_value : 0
<6><376>: Abbrev Number: 4 (DW_TAG_member)
<377> DW_AT_name : (indirect string, offset: 0x1ce): None
<37b> DW_AT_type : <0x391>
<37f> DW_AT_alignment : 4
<380> DW_AT_data_member_location: 0
<6><381>: Abbrev Number: 0
<5><382>: Abbrev Number: 28 (DW_TAG_variant)
<383> DW_AT_discr_value : 1
<6><384>: Abbrev Number: 4 (DW_TAG_member)
<385> DW_AT_name : (indirect string, offset: 0x20e): Some
<389> DW_AT_type : <0x3a3>
<38d> DW_AT_alignment : 4
<38e> DW_AT_data_member_location: 0
acme@number:~$
So this is the one where we want a synthetic __discriminant member.
And what we do today for the niche one, with current pahole (built from
this series):
acme@number:~$ pahole -C 'Option<core::num::nonzero::NonZero<u32>>' option_non_zero.o
struct Option<core::num::nonzero::NonZero<u32>> {
struct None {
/* size: 4, cachelines: 1, members: 0 */
/* padding: 4 */
/* last cacheline: 4 bytes */
} __attribute__((__aligned__(4)));
struct Some {
public:
struct NonZero<u32> __0 __attribute__((__aligned__(4))); /* 0 4 */
/* size: 4, cachelines: 1, members: 1 */
/* forced alignments: 1 */
/* last cacheline: 4 bytes */
} __attribute__((__aligned__(4)));
struct Option<u32> map<core::num::nonzero::NonZero<u32>, u32, option_non_zero::consume::{closure_env#0}>(struct Option<core::num::nonzero::NonZero<u32>>, struct {closure_env#0});
/* size: 4, cachelines: 1, members: 0 */
/* padding: 4 */
/* last cacheline: 4 bytes */
} __attribute__((__aligned__(4)));
acme@number:~$
and on the BTF side, also with this series:
acme@number:~$ pahole --btf_encode option_non_zero.o
acme@number:~$ pahole -F btf -C 'Option<core::num::nonzero::NonZero<u32>>' option_non_zero.o
union Option<core::num::nonzero::NonZero<u32>> {
struct None None; /* 0 4 */
struct Some Some; /* 0 4 */
};
acme@number:~$ pahole --expand_types -F btf -C 'Option<core::num::nonzero::NonZero<u32>>' option_non_zero.o
union Option<core::num::nonzero::NonZero<u32>> {
struct None {
} __attribute__((__aligned__(8))) None; /* 0 4 */
struct Some {
struct NonZero<u32> {
struct NonZeroU32Inner {
u32 __0; /* 0 4 */
} __0; /* 0 4 */
} __0; /* 0 4 */
} Some; /* 0 4 */
};
acme@number:~$
matches the real in-memory layout, size 4, no extra member.
So for the future __discriminant work the rule should be: only emit it
when its location doesn't overlap with the payload, i.e. on the explicit
form; on the niche that field is the data itself, adding one would mean
two members covering the same bytes, and we'd be lying about the layout.
I'll add a test with both forms, pinning this behavior, so we don't
regress it, and then, when we improve support for the synthetic
discriminants, we can update it.
And yes, I used OpenCode + DeepSeek to iterate multiple times and
manually checked everything before hitting send, it helps me a lot and I
hope that if I missed something, you will correct me so that we can make
progress.
Regards,
- Arnaldo
prev parent reply other threads:[~2026-08-07 20:30 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-31 19:30 [PATCHES 00/12] pahole: Support more rust tags and references to dwz alternate debug files Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 01/12] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 02/12] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 03/12] dwarf_loader: Populate DW_TAG_variant children in DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 04/12] btf_encoder: Encode variant parts as union members in BTF Arnaldo Carvalho de Melo
2026-08-11 0:29 ` Yonghong Song
2026-07-31 19:30 ` [PATCH 05/12] dwarf_loader: Handle DW_FORM_block in attr_numeric for Rust discriminant values Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 06/12] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 07/12] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 08/12] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 09/12] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type() Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 10/12] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 11/12] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
2026-07-31 19:31 ` [PATCH 12/12] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis Arnaldo Carvalho de Melo
2026-08-07 8:09 ` [PATCHES 00/12] pahole: Support more rust tags and references to dwz alternate debug files Alan Maguire
2026-08-07 12:43 ` Arnaldo Carvalho de Melo
2026-08-07 17:34 ` Miguel Ojeda
2026-08-07 18:53 ` Arnaldo Carvalho de Melo
2026-08-07 19:26 ` Gary Guo
2026-08-07 20:30 ` Arnaldo Carvalho de Melo [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=anZAfc7sikAx8ikS@x1 \
--to=acme@kernel.org \
--cc=alan.maguire@oracle.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=dwarves@vger.kernel.org \
--cc=gary@garyguo.net \
--cc=jolsa@kernel.org \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=mjw@redhat.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=williams@redhat.com \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.