From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Alan Maguire <alan.maguire@oracle.com>
Cc: Jiri Olsa <jolsa@kernel.org>,
Clark Williams <williams@redhat.com>,
dwarves@vger.kernel.org,
Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH 09/16] dwarf_loader: Populate DW_TAG_variant children in DW_TAG_variant_part
Date: Mon, 22 Jun 2026 17:24:32 -0300 [thread overview]
Message-ID: <20260622202441.14799-10-acme@kernel.org> (raw)
In-Reply-To: <20260622202441.14799-1-acme@kernel.org>
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Rust discriminated unions (enums like Option<T> and Result<T,E>) are
represented in DWARF as:
DW_TAG_structure_type
DW_TAG_variant_part (DW_AT_discr -> discriminant member)
DW_TAG_variant (DW_AT_discr_value = 0)
DW_TAG_member "None" -> struct None
DW_TAG_variant (DW_AT_discr_value = 1)
DW_TAG_member "Some" -> struct Some
Commit 7b135647cbf22e0c ("dwarf_loader: Initial support for
DW_TAG_variant_part") added the variant_part container to pahole's
internal representation, but did not process its DW_TAG_variant children.
This meant the variant parts were always empty, and any Rust struct
backed by a variant_part appeared as a zero-member struct in the output.
Add a struct variant with name and discriminant value fields, a
variant__new() loader that extracts the DW_AT_discr_value and the child
DW_TAG_member's name and type reference, and wire it into
variant_part__new() so DW_TAG_variant children are populated at load
time.
Also add type recoding for variant type references in
namespace__recode_dwarf_types, so variant member types are resolved from
DWARF offsets to CU-local type indices, following the same pattern used
for regular struct/union members.
The variant_part__delete destructor is made non-static and extended to
clean up variant children, and helper functions and iterator macros are
added for the new types.
Before, with a Rust binary like sashiko-cli:
$ pahole -F btf -C 'Option<u32>' code_with_type.o
struct Option<u32> {
/* size: 8, cachelines: 1, members: 0 */
/* padding: 8 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(16)));
After:
$ pahole -F dwarf -C 'Option<u32>' code_with_type.o
struct Option<u32> {
struct None { ... } __attribute__((__aligned__(4)));
struct Some {
u32 __0 __attribute__((__aligned__(4))); /* 4 4 */
} __attribute__((__aligned__(4)));
/* size: 8, cachelines: 1, members: 0 */
} __attribute__((__aligned__(4)));
The variant parts are now populated in pahole's internal representation
and available for downstream consumers (BTF/CTF encoders, pretty
printers).
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
dwarves.c | 42 ++++++++++++++++++++++++++++-
dwarves.h | 24 +++++++++++++++++
3 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 956532bbc5a35410..fc88221ae339e7cd 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1209,6 +1209,32 @@ static struct template_parameter_pack *template_parameter_pack__new(Dwarf_Die *d
return pack;
}
+static struct variant *variant__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
+{
+ struct variant *var = tag__alloc(cu, sizeof(*var));
+
+ if (var != NULL) {
+ tag__init(&var->tag, cu, die);
+ var->discr_value = attr_numeric(die, DW_AT_discr_value);
+ var->name = NULL;
+
+ Dwarf_Die child;
+ if (dwarf_child(die, &child) == 0) {
+ do {
+ if (dwarf_tag(&child) == DW_TAG_member) {
+ struct dwarf_tag *dtag = tag__dwarf(&var->tag);
+
+ var->name = attr_string(&child, DW_AT_name, conf);
+ dwarf_tag__set_attr_type(dtag, type, &child, DW_AT_type);
+ break;
+ }
+ } while (dwarf_siblingof(&child, &child) == 0);
+ }
+ }
+
+ return var;
+}
+
static struct variant_part *variant_part__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
{
struct variant_part *vpart = tag__alloc(cu, sizeof(*vpart));
@@ -1216,6 +1242,20 @@ static struct variant_part *variant_part__new(Dwarf_Die *die, struct cu *cu, str
if (vpart != NULL) {
tag__init(&vpart->tag, cu, die);
INIT_LIST_HEAD(&vpart->variants);
+
+ Dwarf_Die child;
+ if (dwarf_child(die, &child) == 0) {
+ do {
+ if (dwarf_tag(&child) == DW_TAG_variant) {
+ struct variant *var = variant__new(&child, cu, conf);
+ if (var == NULL) {
+ variant_part__delete(vpart, cu);
+ return NULL;
+ }
+ variant_part__add_variant(vpart, var);
+ }
+ } while (dwarf_siblingof(&child, &child) == 0);
+ }
}
return vpart;
@@ -2730,6 +2770,38 @@ check_type:
next:
pos->type = dtype->small_id;
}
+
+ if (tag__is_struct(tag) || tag__is_union(tag)) {
+ struct type *type = tag__type(tag);
+ struct variant_part *vpart;
+ struct dwarf_cu *dcu = cu->priv;
+
+ type__for_each_variant_part(type, vpart) {
+ struct variant *variant;
+ struct dwarf_tag *dvpart = tag__dwarf(&vpart->tag);
+
+ if (dvpart->type != 0) {
+ struct dwarf_tag *dtype = dwarf_cu__find_tag_by_ref(dcu, dvpart, type);
+ if (dtype != NULL)
+ vpart->tag.type = dtype->small_id;
+ }
+
+ variant_part__for_each_variant(vpart, variant) {
+ struct dwarf_tag *dvar = tag__dwarf(&variant->tag);
+
+ if (dvar->type == 0)
+ continue;
+
+ struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(dcu, dvar, type);
+ if (dtype == NULL) {
+ tag__print_type_not_found(&variant->tag);
+ continue;
+ }
+ variant->tag.type = dtype->small_id;
+ }
+ }
+ }
+
return 0;
}
diff --git a/dwarves.c b/dwarves.c
index 5fae87fa5fe35a28..04fe4a5ad62fffde 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -1289,11 +1289,18 @@ static void type__delete_class_members(struct type *type, struct cu *cu)
}
}
-static void variant_part__delete(struct variant_part *vpart, struct cu *cu)
+void variant_part__delete(struct variant_part *vpart, struct cu *cu)
{
+ struct variant *pos, *next;
+
if (vpart == NULL)
return;
+ list_for_each_entry_safe(pos, next, &vpart->variants, tag.node) {
+ list_del_init(&pos->tag.node);
+ cu__tag_free(cu, &pos->tag);
+ }
+
cu__tag_free(cu, &vpart->tag);
}
@@ -1387,6 +1394,11 @@ void type__add_variant_part(struct type *type, struct variant_part *vpart)
list_add_tail(&vpart->tag.node, &type->variant_parts);
}
+void variant_part__add_variant(struct variant_part *vpart, struct variant *var)
+{
+ list_add_tail(&var->tag.node, &vpart->variants);
+}
+
struct class_member *type__last_member(struct type *type)
{
struct class_member *pos;
@@ -1413,6 +1425,34 @@ static int type__clone_members(struct type *type, const struct type *from, struc
type__add_member(type, clone);
}
+ struct variant_part *vpart;
+
+ type__for_each_variant_part(from, vpart) {
+ struct variant_part *vp_clone = cu__tag_alloc(cu, sizeof(*vp_clone));
+
+ if (vp_clone == NULL)
+ return -1;
+
+ memcpy(vp_clone, vpart, sizeof(*vp_clone));
+ INIT_LIST_HEAD(&vp_clone->variants);
+
+ struct variant *variant;
+
+ variant_part__for_each_variant(vpart, variant) {
+ struct variant *v_clone = cu__tag_alloc(cu, sizeof(*v_clone));
+
+ if (v_clone == NULL) {
+ variant_part__delete(vp_clone, cu);
+ return -1;
+ }
+
+ memcpy(v_clone, variant, sizeof(*v_clone));
+ variant_part__add_variant(vp_clone, v_clone);
+ }
+
+ type__add_variant_part(type, vp_clone);
+ }
+
return 0;
}
diff --git a/dwarves.h b/dwarves.h
index 638a469b11535061..f2dac86b05ed6d2e 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1016,6 +1016,12 @@ static inline struct formal_parameter_pack *tag__formal_parameter_pack(const str
void formal_parameter_pack__add(struct formal_parameter_pack *pack, struct parameter *param);
+struct variant {
+ struct tag tag;
+ const char *name;
+ uint64_t discr_value;
+};
+
struct variant_part {
struct tag tag;
struct list_head variants;
@@ -1418,10 +1424,28 @@ static inline struct class_member *class_member__next(struct class_member *membe
#define type__for_each_variant_part_safe_reverse(type, pos, n) \
list_for_each_entry_safe_reverse(pos, n, &(type)->variant_parts, tag.node)
+/**
+ * type__for_each_variant_part - iterate thru all variant_parts in a type
+ * @type: struct type instance to iterate
+ * @pos: struct variant_part iterator
+ */
+#define type__for_each_variant_part(type, pos) \
+ list_for_each_entry(pos, &(type)->variant_parts, tag.node)
+
void type__add_member(struct type *type, struct class_member *member);
void type__add_template_type_param(struct type *type, struct template_type_param *ttparm);
void type__add_template_value_param(struct type *type, struct template_value_param *tvparam);
void type__add_variant_part(struct type *type, struct variant_part *vpart);
+void variant_part__delete(struct variant_part *vpart, struct cu *cu);
+void variant_part__add_variant(struct variant_part *vpart, struct variant *var);
+
+/**
+ * variant_part__for_each_variant - iterate thru all variants in a variant_part
+ * @vpart: struct variant_part instance to iterate
+ * @pos: struct variant iterator
+ */
+#define variant_part__for_each_variant(vpart, pos) \
+ list_for_each_entry(pos, &(vpart)->variants, tag.node)
struct class_member *
type__find_first_biggest_size_base_type_member(struct type *type,
--
2.54.0
next prev parent reply other threads:[~2026-06-22 20:25 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-22 20:24 [PATCHES v3 0/7] Initial support for some Rust tags, DW_TAG_imported_unit Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 01/16] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 02/16] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 03/16] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 04/16] encoders: Fix diagnostic messages for unexpected tags in enumerations Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 05/16] dwarves_fprintf: Accumulate function__fprintf return value in enumeration printing Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 06/16] dwarves: Use tag__delete for enumeration children Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 07/16] btf_encoder: Fix types__match parameter comparison in BTF_KIND_FUNC_PROTO Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 08/16] encoders: Handle DW_TAG_subprogram in enumerations during BTF/CTF encoding Arnaldo Carvalho de Melo
2026-06-22 20:24 ` Arnaldo Carvalho de Melo [this message]
2026-06-22 20:24 ` [PATCH 10/16] btf_encoder: Encode variant parts as union members in BTF Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 11/16] dwarf_loader: Handle DW_FORM_block in attr_numeric for Rust discriminant values Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 12/16] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 13/16] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 14/16] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 15/16] tests: Guard cleanup() against empty outdir to prevent rm /* Arnaldo Carvalho de Melo
2026-06-22 20:24 ` [PATCH 16/16] tests: Source test_lib.sh via dirname so tests run from any directory Arnaldo Carvalho de Melo
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=20260622202441.14799-10-acme@kernel.org \
--to=acme@kernel.org \
--cc=acme@redhat.com \
--cc=alan.maguire@oracle.com \
--cc=dwarves@vger.kernel.org \
--cc=jolsa@kernel.org \
--cc=williams@redhat.com \
/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