[PATCH 3/7] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration

From: Arnaldo Carvalho de Melo

Date: Thu Jun 18 2026 - 11:22:31 EST


From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>

In Rust enums can have subprograms, add initial support for it.

Example of a Rust enumeration with a DW_TAG_subprogram tag.

$ pahole -C ProgramKind /tmp/build/perf-tools-next/tests/workloads/code_with_type.a
enum ProgramKind {
PathLookup = 0,
Relative = 1,
Absolute = 2,
enum ProgramKind new(struct &std::ffi::os_str::OsStr),
} __attribute__((__packed__));
$

Reviewed-by: Alan Maguire <alan.maguire@xxxxxxxxxx>
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
btf_encoder.c | 13 +++++++++----
ctf_encoder.c | 16 +++++++++++-----
dwarf_loader.c | 39 +++++++++++++++++++++++++++++++--------
dwarves.c | 3 +++
dwarves.h | 2 +-
dwarves_emit.c | 10 +++++-----
dwarves_fprintf.c | 44 ++++++++++++++++++++++++++++++++------------
pahole.c | 9 ++++++---
8 files changed, 98 insertions(+), 38 deletions(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 633bc6162ce013d0..be880f3fdb32f792 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1786,7 +1786,7 @@ static uint32_t array_type__nelems(struct tag *tag)
}

static int32_t btf_encoder__add_enum_type(struct btf_encoder *encoder, struct tag *tag,
- struct conf_load *conf_load)
+ const struct cu *cu, struct conf_load *conf_load)
{
struct type *etype = tag__type(tag);
struct enumerator *pos;
@@ -1798,6 +1798,11 @@ static int32_t btf_encoder__add_enum_type(struct btf_encoder *encoder, struct ta
return type_id;

type__for_each_enumerator(etype, pos) {
+ if (pos->tag.tag != DW_TAG_enumerator) {
+ fprintf(stderr, "Unexpected DW_TAG_%s <%llx>, skipping it...\n",
+ dwarf_tag_name(tag->tag), tag__orig_id(tag, cu));
+ continue;
+ }
name = enumerator__name(pos);
if (btf_encoder__add_enum_val(encoder, name, pos->value, etype, conf_load))
return -1;
@@ -1807,7 +1812,7 @@ static int32_t btf_encoder__add_enum_type(struct btf_encoder *encoder, struct ta
}

static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag,
- struct conf_load *conf_load)
+ const struct cu *cu, struct conf_load *conf_load)
{
/* single out type 0 as it represents special type "void" */
uint32_t ref_type_id = tag->type == 0 ? 0 : encoder->type_id_off + tag->type;
@@ -1846,7 +1851,7 @@ static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag,
encoder->need_index_type = true;
return btf_encoder__add_array(encoder, ref_type_id, encoder->array_index_id, array_type__nelems(tag));
case DW_TAG_enumeration_type:
- return btf_encoder__add_enum_type(encoder, tag, conf_load);
+ return btf_encoder__add_enum_type(encoder, tag, cu, conf_load);
case DW_TAG_subroutine_type:
return btf_encoder__add_func_proto_for_ftype(encoder, tag__ftype(tag));
case DW_TAG_unspecified_type:
@@ -2905,7 +2910,7 @@ int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu, struct co
}

cu__for_each_type(cu, core_id, pos) {
- btf_type_id = btf_encoder__encode_tag(encoder, pos, conf_load);
+ btf_type_id = btf_encoder__encode_tag(encoder, pos, cu, conf_load);

if (btf_type_id == 0) {
++skipped_types;
diff --git a/ctf_encoder.c b/ctf_encoder.c
index b761287d45348c59..1c61c76ba20e06bc 100644
--- a/ctf_encoder.c
+++ b/ctf_encoder.c
@@ -142,7 +142,7 @@ static int subroutine_type__encode(struct tag *tag, uint32_t core_id, struct ctf
return 0;
}

-static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+static int enumeration_type__encode(struct tag *tag, const struct cu *cu, uint32_t core_id, struct ctf *ctf)
{
struct type *etype = tag__type(tag);
int64_t position;
@@ -154,13 +154,19 @@ static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ct
return -1;

struct enumerator *pos;
- type__for_each_enumerator(etype, pos)
+ type__for_each_enumerator(etype, pos) {
+ if (pos->tag.tag != DW_TAG_enumerator) {
+ fprintf(stderr, "Unexpected DW_TAG_%s <%llx>, skipping it...\n",
+ dwarf_tag_name(tag->tag), tag__orig_id(tag, cu));
+ continue;
+ }
ctf__add_enumerator(ctf, pos->name, pos->value, &position);
+ }

return 0;
}

-static void tag__encode_ctf(struct tag *tag, uint32_t core_id, struct ctf *ctf)
+static void tag__encode_ctf(struct tag *tag, const struct cu *cu, uint32_t core_id, struct ctf *ctf)
{
switch (tag->tag) {
case DW_TAG_base_type:
@@ -190,7 +196,7 @@ static void tag__encode_ctf(struct tag *tag, uint32_t core_id, struct ctf *ctf)
subroutine_type__encode(tag, core_id, ctf);
break;
case DW_TAG_enumeration_type:
- enumeration_type__encode(tag, core_id, ctf);
+ enumeration_type__encode(tag, cu, core_id, ctf);
break;
}
}
@@ -253,7 +259,7 @@ int cu__encode_ctf(struct cu *cu, int verbose)
uint32_t id;
struct tag *pos;
cu__for_each_type(cu, id, pos)
- tag__encode_ctf(pos, id, ctf);
+ tag__encode_ctf(pos, cu, id, ctf);

struct hlist_head hash_addr[HASHADDR__SIZE];

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 54cc66748ef685fc..2a62059dc66517dc 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1943,6 +1943,8 @@ out_delete:
return NULL;
}

+static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struct conf_load *conf);
+
static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
{
Dwarf_Die child;
@@ -1964,18 +1966,39 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, st

die = &child;
do {
- struct enumerator *enumerator;
+ switch (dwarf_tag(die)) {
+ case DW_TAG_enumerator: {
+ struct enumerator *enumerator = enumerator__new(die, cu, conf);
+
+ if (enumerator == NULL)
+ goto out_delete;
+
+ enumeration__add(enumeration, enumerator);
+ cu__hash(cu, &enumerator->tag);
+ }
+ continue;
+ case DW_TAG_subprogram: {
+ struct tag *tag = die__create_new_function(die, cu, conf);
+ uint32_t id;
+
+ if (tag == NULL)
+ goto out_delete;
+
+ if (cu__table_add_tag(cu, tag, &id) < 0) {
+ tag__delete(tag, cu);
+ goto out_delete;
+ }

- if (dwarf_tag(die) != DW_TAG_enumerator) {
+ struct dwarf_tag *dtag = tag__dwarf(tag);
+ dtag->small_id = id;
+ namespace__add_tag(&enumeration->namespace, tag);
+ cu__hash(cu, tag);
+ break;
+ }
+ default:
cu__tag_not_handled(cu, die);
continue;
}
- enumerator = enumerator__new(die, cu, conf);
- if (enumerator == NULL)
- goto out_delete;
-
- enumeration__add(enumeration, enumerator);
- cu__hash(cu, &enumerator->tag);
} while (dwarf_siblingof(die, die) == 0);
out:
return &enumeration->namespace.tag;
diff --git a/dwarves.c b/dwarves.c
index 37f769ee65caad72..2af10588ff2f20c5 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -2015,6 +2015,9 @@ static void enumeration__calc_prefix(struct type *enumeration)
struct enumerator *entry;

type__for_each_enumerator(enumeration, entry) {
+ if (entry->tag.tag != DW_TAG_enumerator)
+ continue;
+
const char *curr_name = enumerator__name(entry);

if (previous_name) {
diff --git a/dwarves.h b/dwarves.h
index 6595c16434966f09..6ad2995d2e33be0d 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1646,7 +1646,7 @@ static inline const char *enumerator__name(const struct enumerator *enumerator)

void enumeration__delete(struct type *type, struct cu *cu);
void enumeration__add(struct type *type, struct enumerator *enumerator);
-size_t enumeration__fprintf(const struct tag *tag_enum,
+size_t enumeration__fprintf(const struct tag *tag_enum, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp);

int dwarves__init(void);
diff --git a/dwarves_emit.c b/dwarves_emit.c
index 01b33b7ec41eb947..aaf0f8f9a7ea815a 100644
--- a/dwarves_emit.c
+++ b/dwarves_emit.c
@@ -100,7 +100,7 @@ static struct type *type_emissions__find_fwd_decl(const struct type_emissions *e
return NULL;
}

-static int enumeration__emit_definitions(struct tag *tag,
+static int enumeration__emit_definitions(struct tag *tag, const struct cu *cu,
struct type_emissions *emissions,
const struct conf_fprintf *conf,
FILE *fp)
@@ -121,7 +121,7 @@ static int enumeration__emit_definitions(struct tag *tag,
return 0;
}

- enumeration__fprintf(tag, conf, fp);
+ enumeration__fprintf(tag, cu, conf, fp);
fputs(";\n", fp);

// See comment on enumeration__fprintf(), it seems this happens with DWARF as well
@@ -198,10 +198,10 @@ static int typedef__emit_definitions(struct tag *tdef, struct cu *cu,
if (type__name(ctype) == NULL) {
fputs("typedef ", fp);
conf.suffix = type__name(def);
- enumeration__emit_definitions(type, emissions, &conf, fp);
+ enumeration__emit_definitions(type, cu, emissions, &conf, fp);
goto out;
} else
- enumeration__emit_definitions(type, emissions, &conf, fp);
+ enumeration__emit_definitions(type, cu, emissions, &conf, fp);
}
break;
case DW_TAG_structure_type:
@@ -380,7 +380,7 @@ next_indirection:
struct conf_fprintf conf = {
.suffix = NULL,
};
- return enumeration__emit_definitions(type, emissions, &conf, fp);
+ return enumeration__emit_definitions(type, cu, emissions, &conf, fp);
}
break;
case DW_TAG_structure_type:
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index 1ec478c2a027be10..a3c8b391b9222fcd 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -153,6 +153,8 @@ const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";

static size_t union__fprintf(struct type *type, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp);
+static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);

/*
* In dwarves_emit.c we can call type__emit() using a locally setup conf_fprintf for which
@@ -404,7 +406,7 @@ next_type:
struct conf_fprintf tconf = *pconf;

tconf.suffix = type__name(type);
- return printed + enumeration__fprintf(tag_type, &tconf, fp);
+ return printed + enumeration__fprintf(tag_type, cu, &tconf, fp);
}
}

@@ -448,7 +450,12 @@ static int enumeration__max_entry_name_len(struct type *type)
struct enumerator *pos;

type__for_each_enumerator(type, pos) {
- int len = strlen(enumerator__name(pos));
+ int len = 0;
+
+ if (pos->tag.tag == DW_TAG_enumerator)
+ len = strlen(enumerator__name(pos));
+ else if (pos->tag.tag == DW_TAG_subprogram)
+ len = strlen(function__name(tag__function(&pos->tag)));

if (type->max_tag_name_len < len)
type->max_tag_name_len = len;
@@ -457,7 +464,8 @@ out:
return type->max_tag_name_len;
}

-size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *conf, FILE *fp)
+size_t enumeration__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
{
struct type *type = tag__type(tag);
struct enumerator *pos;
@@ -478,13 +486,25 @@ size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *co
}

type__for_each_enumerator(type, pos) {
- printed += fprintf(fp, "%.*s\t%-*s = ", indent, tabs,
- max_entry_name_len, enumerator__name(pos));
- if (conf->hex_fmt)
- printed += fprintf(fp, "%#llx", (unsigned long long)pos->value);
- else
- printed += fprintf(fp, type->is_signed_enum ? "%lld" : "%llu",
- (unsigned long long)pos->value);
+ printed += fprintf(fp, "%.*s\t", indent, tabs);
+
+ switch (pos->tag.tag) {
+ case DW_TAG_subprogram:
+ function__fprintf(&pos->tag, cu, conf, fp);
+ break;
+ case DW_TAG_enumerator:
+ printed += fprintf(fp, "%-*s = ", max_entry_name_len, enumerator__name(pos));
+ if (conf->hex_fmt)
+ printed += fprintf(fp, "%#llx", (unsigned long long)pos->value);
+ else
+ printed += fprintf(fp, type->is_signed_enum ? "%lld" : "%llu",
+ (unsigned long long)pos->value);
+ break;
+ default:
+ printed += fprintf(fp, "/* Unexpected %s <%llx> */\n", dwarf_tag_name(tag->tag),
+ tag__orig_id(tag, cu));
+ continue;
+ }
printed += fprintf(fp, ",\n");
}

@@ -934,7 +954,7 @@ print_modifier: {
if (type__name(ctype) != NULL && !expand_types)
printed += fprintf(fp, "enum %-*s %s", tconf.type_spacing - 5, type__name(ctype), name ?: "");
else
- printed += enumeration__fprintf(type, &tconf, fp);
+ printed += enumeration__fprintf(type, cu, &tconf, fp);
break;
case DW_TAG_LLVM_annotation: {
struct tag *ttype = cu__type(cu, type->type);
@@ -2158,7 +2178,7 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu,
printed += array_type__fprintf(tag, cu, "array", pconf, fp);
break;
case DW_TAG_enumeration_type:
- printed += enumeration__fprintf(tag, pconf, fp);
+ printed += enumeration__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_typedef:
printed += typedef__fprintf(tag, cu, pconf, fp);
diff --git a/pahole.c b/pahole.c
index 28d8f8832773520b..765da575f3dab39a 100644
--- a/pahole.c
+++ b/pahole.c
@@ -2155,7 +2155,7 @@ static const char *enumeration__lookup_value(struct type *enumeration, uint64_t
struct enumerator *entry;

type__for_each_enumerator(enumeration, entry) {
- if (entry->value == value)
+ if (entry->tag.tag == DW_TAG_enumerator && entry->value == value)
return enumerator__name(entry);
}

@@ -2180,7 +2180,7 @@ static struct enumerator *enumeration__lookup_entry_from_value(struct type *enum
struct enumerator *entry;

type__for_each_enumerator(enumeration, entry) {
- if (entry->value == value)
+ if (entry->tag.tag == DW_TAG_enumerator && entry->value == value)
return entry;
}

@@ -2206,6 +2206,9 @@ static struct enumerator *enumeration__find_enumerator(struct type *enumeration,
struct enumerator *entry;

type__for_each_enumerator(enumeration, entry) {
+ if (entry->tag.tag != DW_TAG_enumerator)
+ continue;
+
const char *entry_name = enumerator__name(entry);

if (!strcmp(entry_name, name))
@@ -3204,7 +3207,7 @@ static bool print_enumeration_with_enumerator(struct cu *cu, const char *name)

cu__for_each_enumeration(cu, id, enumeration) {
if (enumeration__find_enumerator(enumeration, name) != NULL) {
- enumeration__fprintf(type__tag(enumeration), &conf, stdout);
+ enumeration__fprintf(type__tag(enumeration), cu, &conf, stdout);
fputc('\n', stdout);
return true;
}
--
2.54.0