[PATCH 10/15] gendwarfksyms: Expand structure types

Sami Tolvanen
2024-06-17T17:58:44Z

Recursively expand DWARF structure types, e.g. structs, unions, and
enums. Type strings also encode structure member layout, which allows
us to determine ABI breakages if the compiler decides to reorder
members or otherwise change the layout.

Example output with --debug:

subprogram(
formal_parameter pointer_type *mut &str {
structure_type &str {
member pointer_type <unnamed> {
base_type u8 byte_size(1)
} data_member_location(0),
member base_type usize byte_size(8) data_member_location(8),
} byte_size(16) alignment(8)
},
)
-> base_type void;

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
tools/gendwarfksyms/gendwarfksyms.h | 5 ++
tools/gendwarfksyms/types.c | 127 +++++++++++++++++++++++++++-
2 files changed, 130 insertions(+), 2 deletions(-)

diff --git a/tools/gendwarfksyms/gendwarfksyms.h b/tools/gendwarfksyms/gendwarfksyms.h
index 03d8a4a039c3..4646eaf5c85e 100644
--- a/tools/gendwarfksyms/gendwarfksyms.h
+++ b/tools/gendwarfksyms/gendwarfksyms.h
@@ -52,8 +52,13 @@ extern bool no_pretty_print;
})

/* Consistent aliases (DW_TAG_<type>_type) for DWARF tags */
+#define DW_TAG_enumerator_type DW_TAG_enumerator
#define DW_TAG_formal_parameter_type DW_TAG_formal_parameter
+#define DW_TAG_member_type DW_TAG_member
+#define DW_TAG_template_type_parameter_type DW_TAG_template_type_parameter
#define DW_TAG_typedef_type DW_TAG_typedef
+#define DW_TAG_variant_part_type DW_TAG_variant_part
+#define DW_TAG_variant_type DW_TAG_variant

/*
* symbols.c
diff --git a/tools/gendwarfksyms/types.c b/tools/gendwarfksyms/types.c
index b1b82d166eb8..fa74e6fc26e3 100644
--- a/tools/gendwarfksyms/types.c
+++ b/tools/gendwarfksyms/types.c
@@ -166,8 +166,10 @@ static int process_fqn(struct state *state, struct cached_die *cache,
return 0; \
}

+DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
+DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)

/* Match functions -- die_match_callback_t */
#define DEFINE_MATCH(type) \
@@ -176,8 +178,11 @@ DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
return dwarf_tag(die) == DW_TAG_##type##_type; \
}

+DEFINE_MATCH(enumerator)
DEFINE_MATCH(formal_parameter)
+DEFINE_MATCH(member)
DEFINE_MATCH(subrange)
+DEFINE_MATCH(variant)

bool match_all(Dwarf_Die *die)
{
@@ -222,6 +227,8 @@ static int __process_list_type(struct state *state, struct cached_die *cache,
{
check(process(state, cache, type));
check(process_type_attr(state, cache, die));
+ check(process_accessibility_attr(state, cache, die));
+ check(process_data_member_location_attr(state, cache, die));
check(process(state, cache, ","));
return check(process_linebreak(cache, 0));
}
@@ -234,6 +241,7 @@ static int __process_list_type(struct state *state, struct cached_die *cache,
}

DEFINE_PROCESS_LIST_TYPE(formal_parameter)
+DEFINE_PROCESS_LIST_TYPE(member)

/* Container types with DW_AT_type */
static int __process_type(struct state *state, struct cached_die *cache,
@@ -266,6 +274,7 @@ DEFINE_PROCESS_TYPE(reference)
DEFINE_PROCESS_TYPE(restrict)
DEFINE_PROCESS_TYPE(rvalue_reference)
DEFINE_PROCESS_TYPE(shared)
+DEFINE_PROCESS_TYPE(template_type_parameter)
DEFINE_PROCESS_TYPE(volatile)
DEFINE_PROCESS_TYPE(typedef)

@@ -318,6 +327,110 @@ static int process_subroutine_type(struct state *state,
return check(__process_subroutine_type(state, cache, die,
"subroutine_type"));
}
+
+static int process_variant_type(struct state *state, struct cached_die *cache,
+ Dwarf_Die *die)
+{
+ return check(process_die_container(state, cache, die, process_type,
+ match_member_type));
+}
+
+static int process_variant_part_type(struct state *state,
+ struct cached_die *cache, Dwarf_Die *die)
+{
+ check(process(state, cache, "variant_part {"));
+ check(process_linebreak(cache, 1));
+ check(process_die_container(state, cache, die, process_type,
+ match_variant_type));
+ check(process_linebreak(cache, -1));
+ check(process(state, cache, "},"));
+ return check(process_linebreak(cache, 0));
+}
+
+static int ___process_structure_type(struct state *state,
+ struct cached_die *cache, Dwarf_Die *die)
+{
+ switch (dwarf_tag(die)) {
+ case DW_TAG_member:
+ case DW_TAG_variant_part:
+ return check(process_type(state, cache, die));
+ case DW_TAG_class_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_union_type:
+ check(process_type(state, cache, die));
+ check(process(state, cache, ","));
+ return check(process_linebreak(cache, 0));
+ case DW_TAG_subprogram:
+ return 0; /* Skip member functions */
+ default:
+ error("unexpected structure_type child: %x", dwarf_tag(die));
+ return -1;
+ }
+}
+
+static int __process_structure_type(struct state *state,
+ struct cached_die *cache, Dwarf_Die *die,
+ const char *type,
+ die_callback_t process_func,
+ die_match_callback_t match_func)
+{
+ check(process(state, cache, type));
+ check(process_fqn(state, cache, die));
+ check(process(state, cache, " {"));
+ check(process_linebreak(cache, 1));
+
+ check(process_die_container(state, cache, die, process_func,
+ match_func));
+
+ check(process_linebreak(cache, -1));
+ check(process(state, cache, "}"));
+
+ check(process_byte_size_attr(state, cache, die));
+ check(process_alignment_attr(state, cache, die));
+
+ return 0;
+}
+
+#define DEFINE_PROCESS_STRUCTURE_TYPE(structure) \
+ static int process_##structure##_type( \
+ struct state *state, struct cached_die *cache, Dwarf_Die *die) \
+ { \
+ return check(__process_structure_type( \
+ state, cache, die, #structure "_type ", \
+ ___process_structure_type, match_all)); \
+ }
+
+DEFINE_PROCESS_STRUCTURE_TYPE(class)
+DEFINE_PROCESS_STRUCTURE_TYPE(structure)
+DEFINE_PROCESS_STRUCTURE_TYPE(union)
+
+static int process_enumerator_type(struct state *state,
+ struct cached_die *cache, Dwarf_Die *die)
+{
+ Dwarf_Word value;
+
+ check(process(state, cache, "enumerator "));
+ check(process_fqn(state, cache, die));
+
+ if (get_udata_attr(die, DW_AT_const_value, &value)) {
+ check(process(state, cache, " = "));
+ check(process_fmt(state, cache, "%" PRIu64, value));
+ }
+
+ check(process(state, cache, ","));
+ return check(process_linebreak(cache, 0));
+}
+
+static int process_enumeration_type(struct state *state,
+ struct cached_die *cache, Dwarf_Die *die)
+{
+ return check(__process_structure_type(state, cache, die,
+ "enumeration_type ", process_type,
+ match_enumerator_type));
+}
+
static int process_base_type(struct state *state, struct cached_die *cache,
Dwarf_Die *die)
{
@@ -402,17 +515,27 @@ static int process_type(struct state *state, struct cached_die *parent,
PROCESS_TYPE(rvalue_reference)
PROCESS_TYPE(shared)
PROCESS_TYPE(volatile)
+ /* Container types */
+ PROCESS_TYPE(class)
+ PROCESS_TYPE(structure)
+ PROCESS_TYPE(union)
+ PROCESS_TYPE(enumeration)
/* Subtypes */
+ PROCESS_TYPE(enumerator)
PROCESS_TYPE(formal_parameter)
+ PROCESS_TYPE(member)
PROCESS_TYPE(subrange)
+ PROCESS_TYPE(template_type_parameter)
+ PROCESS_TYPE(variant)
+ PROCESS_TYPE(variant_part)
/* Other types */
PROCESS_TYPE(array)
PROCESS_TYPE(base)
PROCESS_TYPE(subroutine)
PROCESS_TYPE(typedef)
default:
- debug("unimplemented type: %x", tag);
- break;
+ error("unexpected type: %x", tag);
+ return -1;
}

if (!no_cache) {
--
2.45.2.627.g7a2c4fd464-goog