Rust for Linux List
 help / color / mirror / Atom feed
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

      parent reply	other threads:[~2026-08-07 20:30 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260731193102.110693-1-acme@kernel.org>
     [not found] ` <18b557a8-e1ca-46cf-b03f-5e68490c9229@oracle.com>
     [not found]   ` <anXTAComw6XAvnXU@x1>
2026-08-07 17:34     ` [PATCHES 00/12] pahole: Support more rust tags and references to dwz alternate debug files 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox