[PATCH v4 4/5] objdump: Link and display all variables information
Guillaume VACHERIAS
guillaume.vacherias@foss.st.com
Tue Sep 9 12:43:44 GMT 2025
Implement linking mechanism to recursively resolve the DWARF type information
for variables, members and other type constructs. The linking mechanism is
central to populating field `ptr_type` in type_def, tab_type, member_type,
member_parent, type_ref and variable_type structures.
Implement the display for variables and types. It is based on recursively
accessing fields `type` and `ptr_type`, allowing the system to print complex
types such as pointers to structs, arrays of typedefs, or nested const or
volatile types. When printing a variable or member, the code checks the `type`
field to determine the kind of type, and then follows the `ptr_type` pointer
to the next type structure.
binutils/
* dwarf.h (resolve_and_display_variable_info): New definition.
* dwarf.c (PRINT_SPACE, PRINT_LBRACE, PRINT_RBRACE): New macros.
(display_base_type): New function. Display information hold by
base_type structure.
(display_type_def): Likewise for type_def structure.
(display_enum): Likewise for enum_constant structure.
(display_enum_type): Likewise for enum_type structure.
(display_subrange_type): Likewise for subrange_type structure.
(display_tab_type): Likewise for tab_type structure.
(display_member_type): Likewise for member_type structure.
(display_member_parent): Likewise for member_parent structure.
(display_ptr_type): Likewise for ptr_type structure.
(display_variable_type): Likewise for variable_type structure.
(do_link_variable_information): New function. Link a structure which
represents a DWARF information entry to another by populating field
`ptr_type`.
(link_variable_information): New function. Go through all existing
structures that hold DWARF information to link each variable_type
element to the type of structure it represents.
(compute_variable_total_size): New function. Recursively go through
variable_type element and its structure information to calculate the
total size.
(reset_displayed_field): New function. Set field `displayed` of
member_parent to false to mark already displayed information for a
nested structure field.
(resolve_and_display_variable_info): New function. Go through all
elements in variable_type_list to link variable's information,
calculate variable's size and display variable's information.
* objdump.c (dump_global_variable_info): Add call to
resolve_and_display_variable_info function.
Signed-off-by: Guillaume VACHERIAS <guillaume.vacherias@foss.st.com>
---
binutils/dwarf.c | 479 +++++++++++++++++++++++++++++++++++++++++++++
binutils/dwarf.h | 1 +
binutils/objdump.c | 1 +
3 files changed, 481 insertions(+)
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index fbce63e5981..d483fd9a898 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -55,6 +55,24 @@
#define DO_TYPES 0x2
#define DO_GLOBAL_VARS 0x4
+#define PRINT_SPACE(n) do { \
+ int _i; \
+ for (_i = 0; _i < (n); ++_i) \
+ putchar (' '); \
+} while (0)
+
+#define PRINT_LBRACE(n) do { \
+ PRINT_SPACE (n); \
+ putchar ('{'); \
+ putchar ('\n'); \
+} while (0)
+
+#define PRINT_RBRACE(n) do { \
+ PRINT_SPACE (n); \
+ putchar ('}'); \
+ putchar ('\n'); \
+} while (0)
+
static const char *regname (unsigned int regno, int row);
static const char *regname_internal_by_table_only (unsigned int regno);
@@ -2592,6 +2610,16 @@ display_lang (uint64_t uvalue)
}
}
+static void
+display_base_type (const base_type bt)
+{
+ printf ("type: %s, size: ", bt.name);
+ if (bt.usize != 0)
+ printf ("0x%" PRIx64 "\n", bt.usize);
+ else if (bt.ssize != 0)
+ printf ("0x%" PRIx64 "\n", bt.ssize);
+}
+
static generic_type *
get_or_create_generic_base_type (uint64_t die_offset)
{
@@ -2618,6 +2646,12 @@ get_or_create_generic_base_type (uint64_t die_offset)
return base_type_list.tail;
}
+static void
+display_type_def (const type_def td)
+{
+ printf ("type: typedef %s\n", td.name);
+}
+
static generic_type*
get_or_create_generic_type_def (uint64_t die_offset)
{
@@ -2644,6 +2678,18 @@ get_or_create_generic_type_def (uint64_t die_offset)
return type_def_list.tail;
}
+static void
+display_enum (enum_constant *ec, int nb_tab)
+{
+ enum_constant *head = ec;
+ while (head != NULL)
+ {
+ PRINT_SPACE (nb_tab);
+ printf ("value: %s = %" PRIu64 "\n", head->name, head->value);
+ head = head->next;
+ }
+}
+
static enum_constant *
get_or_create_enum_constant (enum_type *et, uint64_t die_offset)
{
@@ -2670,6 +2716,15 @@ get_or_create_enum_constant (enum_type *et, uint64_t die_offset)
return et->enum_const_tail;
}
+static void
+display_enum_type (const enum_type et, int nb_tab)
+{
+ printf ("type: enum %s, size: 0x%" PRIx64 "\n", et.name, et.usize);
+ PRINT_LBRACE (nb_tab);
+ display_enum (et.enum_const_head, nb_tab + 1);
+ PRINT_RBRACE (nb_tab);
+}
+
static generic_type *
get_or_create_generic_enum_type (uint64_t die_offset)
{
@@ -2696,6 +2751,20 @@ get_or_create_generic_enum_type (uint64_t die_offset)
return enum_type_list.tail;
}
+static void
+display_subrange_type (subrange_type *st)
+{
+ subrange_type *head = st;
+ while (head != NULL)
+ {
+ if (head->usize != 0)
+ printf ("[%" PRIu64 "]", head->usize);
+ else if (head->ssize != 0)
+ printf ("[%" PRId64 "]", head->ssize);
+ head = head->next;
+ }
+}
+
static subrange_type *
get_or_create_subrange_type (tab_type *tt, uint64_t die_offset)
{
@@ -2722,6 +2791,14 @@ get_or_create_subrange_type (tab_type *tt, uint64_t die_offset)
return tt->subrange_tail;
}
+static void
+display_tab_type (const tab_type tt)
+{
+ printf ("type: array");
+ display_subrange_type (tt.subrange_head);
+ printf ("\n");
+}
+
static generic_type *
get_or_create_generic_tab_type (uint64_t die_offset)
{
@@ -2748,6 +2825,12 @@ get_or_create_generic_tab_type (uint64_t die_offset)
return tab_type_list.tail;
}
+static void
+display_member_type (const member_type mt)
+{
+ printf ("member: %s, offset: 0x%" PRIx64 "\n", mt.name, mt.member_offset);
+}
+
static generic_type *
get_or_create_generic_member_type (generic_type *mp, uint64_t die_offset)
{
@@ -2776,6 +2859,29 @@ get_or_create_generic_member_type (generic_type *mp, uint64_t die_offset)
return parent->member_tail;
}
+static bool
+display_member_parent (const member_parent mp)
+{
+ if (mp.displayed)
+ {
+ printf ("nested: struct %s\n", mp.name);
+ return true;
+ }
+ else if (mp.type == UNION_TYPE)
+ printf ("type: union %s, size: ", mp.name);
+ else
+ printf ("type: struct %s, size: ", mp.name);
+
+ if (mp.ssize != 0)
+ printf ("0x%" PRIx64 "\n", mp.ssize);
+ else if (mp.usize != 0)
+ printf ("0x%" PRIx64 "\n", mp.usize);
+ else
+ printf ("Unknown\n");
+
+ return false;
+}
+
static generic_type *
get_or_create_generic_member_parent (generic_type_l *mp_list,
uint64_t die_offset)
@@ -2805,6 +2911,29 @@ get_or_create_generic_member_parent (generic_type_l *mp_list,
return mp_list->tail;
}
+static void
+display_type_ref (const type_ref tr)
+{
+ switch (tr.type)
+ {
+ case PTR_TYPE:
+ printf ("type: ptr, size: ");
+ if (tr.usize != 0)
+ printf ("0x%" PRIx64 "\n", tr.usize);
+ else if (tr.ssize != 0)
+ printf ("0x%" PRIx64 "\n", tr.ssize);
+ else
+ printf ("Unknown\n");
+ break;
+ case CONST_TYPE:
+ printf ("type: const\n");
+ break;
+ case VOLATILE_TYPE:
+ printf ("type: volatile\n");
+ break;
+ }
+}
+
static generic_type *
get_or_create_generic_type_ref (generic_type_l *tr_list,
uint64_t die_offset)
@@ -2833,6 +2962,75 @@ get_or_create_generic_type_ref (generic_type_l *tr_list,
return tr_list->tail;
}
+static void
+display_variable_type (generic_type *die_type,
+ int nb_tab)
+{
+ if (die_type == NULL)
+ return;
+
+ switch (die_type->type)
+ {
+ case BASE_TYPE:
+ PRINT_SPACE (nb_tab);
+ display_base_type (die_type->die_type.base_type);
+ break;
+ case TYPE_DEF:
+ PRINT_SPACE (nb_tab);
+ display_type_def (die_type->die_type.type_def);
+ PRINT_LBRACE (nb_tab);
+ display_variable_type (die_type->die_type.type_def.ptr_type, nb_tab + 1);
+ PRINT_RBRACE (nb_tab);
+ break;
+ case ENUM_TYPE:
+ PRINT_SPACE (nb_tab);
+ display_enum_type (die_type->die_type.enum_type, nb_tab);
+ break;
+ case TAB_TYPE:
+ PRINT_SPACE (nb_tab);
+ display_tab_type (die_type->die_type.tab_type);
+ display_variable_type (die_type->die_type.tab_type.ptr_type, nb_tab + 1);
+ break;
+ case MEMBER_PARENT:
+ PRINT_SPACE (nb_tab);
+ if (display_member_parent (die_type->die_type.member_parent))
+ break;
+ die_type->die_type.member_parent.displayed = true;
+ PRINT_LBRACE (nb_tab);
+ display_variable_type (die_type->die_type.member_parent.member_head,
+ nb_tab + 1);
+ PRINT_RBRACE (nb_tab);
+ break;
+ case MEMBER_TYPE:
+ PRINT_SPACE (nb_tab + 1);
+ display_member_type (die_type->die_type.member_type);
+ display_variable_type (die_type->die_type.member_type.ptr_type,
+ nb_tab + 2);
+ display_variable_type (die_type->next, nb_tab);
+ break;
+ case TYPE_REF:
+ PRINT_SPACE (nb_tab);
+ display_type_ref (die_type->die_type.type_ref);
+ display_variable_type (die_type->die_type.type_ref.ptr_type, nb_tab + 1);
+ break;
+ case VARIABLE_TYPE:
+ if (die_type->die_type.variable_type.name == NULL)
+ printf ("Incomplete declaration variable with location: 0x%08" PRIx64,
+ die_type->die_type.variable_type.location_addr);
+ else if (die_type->die_type.variable_type.location_addr == 0)
+ printf ("of variable %s\n", die_type->die_type.variable_type.name);
+ else
+ printf ("%s @ 0x%08" PRIx64 " 0x%08" PRIx64 "\n",
+ die_type->die_type.variable_type.name,
+ die_type->die_type.variable_type.location_addr,
+ die_type->die_type.variable_type.location_addr
+ + die_type->die_type.variable_type.ttsize);
+ display_variable_type (die_type->die_type.variable_type.ptr_type,
+ nb_tab + 1);
+ break;
+ }
+}
+
static generic_type *
get_or_create_generic_variable_type (uint64_t die_offset)
{
@@ -13780,6 +13978,287 @@ struct dwarf_section_display debug_displays[] =
/* A static assertion. */
extern int debug_displays_assert[ARRAY_SIZE (debug_displays) == max ? 1 : -1];
+static void
+do_link_variable_information (generic_type *die_type,
+ generic_type *target_die_type)
+{
+ switch (die_type->type)
+ {
+ /* Structure where there's no field ptr_type. */
+ case BASE_TYPE:
+ case ENUM_TYPE:
+ case MEMBER_PARENT:
+ break;
+ case MEMBER_TYPE:
+ die_type->die_type.member_type.ptr_type = target_die_type;
+ die_type->linked = true;
+ break;
+ case TYPE_DEF:
+ die_type->die_type.type_def.ptr_type = target_die_type;
+ die_type->linked = true;
+ break;
+ case TAB_TYPE:
+ die_type->die_type.tab_type.ptr_type = target_die_type;
+ die_type->linked = true;
+ break;
+ case TYPE_REF:
+ die_type->die_type.type_ref.ptr_type = target_die_type;
+ die_type->linked = true;
+ break;
+ case VARIABLE_TYPE:
+ die_type->die_type.variable_type.ptr_type = target_die_type;
+ die_type->linked = true;
+ break;
+ }
+}
+
+static bool
+link_variable_information (generic_type *die_type, uint64_t ptr_die_offset)
+{
+ if (die_type == NULL || die_type->linked)
+ return false;
+
+ generic_type *i_base_type = base_type_list.head;
+ while (i_base_type != NULL)
+ {
+ if (i_base_type->die_type.base_type.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_base_type);
+ break;
+ }
+ i_base_type = i_base_type->next;
+ }
+
+ generic_type *i_type_def = type_def_list.head;
+ while (i_type_def != NULL)
+ {
+ if (i_type_def->die_type.type_def.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_type_def);
+ link_variable_information (i_type_def,
+ i_type_def->die_type.type_def.ptr_die_offset);
+ break;
+ }
+ i_type_def = i_type_def->next;
+ }
+
+ generic_type *i_enum_type = enum_type_list.head;
+ while (i_enum_type != NULL)
+ {
+ if (i_enum_type->die_type.enum_type.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_enum_type);
+ break;
+ }
+ i_enum_type = i_enum_type->next;
+ }
+
+ generic_type *i_tab_type = tab_type_list.head;
+ while (i_tab_type != NULL)
+ {
+ if (i_tab_type->die_type.tab_type.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_tab_type);
+ link_variable_information (i_tab_type,
+ i_tab_type->die_type.tab_type.ptr_die_offset);
+ break;
+ }
+ i_tab_type = i_tab_type->next;
+ }
+
+ generic_type *i_struct_type = struct_type_list.head;
+ while (i_struct_type != NULL)
+ {
+ if (i_struct_type->die_type.member_parent.die_offset == ptr_die_offset)
+ {
+ generic_type *i_member_type =
+ i_struct_type->die_type.member_parent.member_head;
+ while (i_member_type != NULL)
+ {
+ link_variable_information (i_member_type,
+ i_member_type->die_type.member_type.ptr_die_offset);
+ i_member_type = i_member_type->next;
+ }
+ do_link_variable_information (die_type, i_struct_type);
+ break;
+ }
+ i_struct_type = i_struct_type->next;
+ }
+
+ generic_type *i_union_type = union_type_list.head;
+ while (i_union_type != NULL)
+ {
+ if (i_union_type->die_type.member_parent.die_offset == ptr_die_offset)
+ {
+ generic_type *i_member_type =
+ i_union_type->die_type.member_parent.member_head;
+ while (i_member_type != NULL)
+ {
+ link_variable_information (i_member_type,
+ i_member_type->die_type.member_type.ptr_die_offset);
+ i_member_type = i_member_type->next;
+ }
+ do_link_variable_information (die_type, i_union_type);
+ break;
+ }
+ i_union_type = i_union_type->next;
+ }
+
+ generic_type *i_ptr_type = ptr_type_list.head;
+ while (i_ptr_type != NULL)
+ {
+ if (i_ptr_type->die_type.type_ref.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_ptr_type);
+ link_variable_information (i_ptr_type,
+ i_ptr_type->die_type.type_ref.ptr_die_offset);
+ break;
+ }
+ i_ptr_type = i_ptr_type->next;
+ }
+
+ generic_type *i_const_type = const_type_list.head;
+ while (i_const_type != NULL)
+ {
+ if (i_const_type->die_type.type_ref.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_const_type);
+ link_variable_information (i_const_type,
+ i_const_type->die_type.type_ref.ptr_die_offset);
+ break;
+ }
+ i_const_type = i_const_type->next;
+ }
+
+ generic_type *i_volatile_type = volatile_type_list.head;
+ while (i_volatile_type != NULL)
+ {
+ if (i_volatile_type->die_type.type_ref.die_offset == ptr_die_offset)
+ {
+ do_link_variable_information (die_type, i_volatile_type);
+ link_variable_information (i_volatile_type,
+ i_volatile_type->die_type.type_ref.ptr_die_offset);
+ break;
+ }
+ i_volatile_type = i_volatile_type->next;
+ }
+
+ if (i_base_type == NULL && i_type_def == NULL
+ && i_enum_type == NULL && i_tab_type == NULL
+ && i_struct_type == NULL && i_union_type == NULL
+ && i_union_type == NULL && i_ptr_type == NULL
+ && i_const_type == NULL && i_volatile_type == NULL)
+ return false;
+
+ return true;
+}
+
+static uint64_t
+compute_variable_total_size (generic_type *die_type)
+{
+ uint64_t size = 0;
+ if (die_type == NULL)
+ return 0;
+
+ switch (die_type->type)
+ {
+ case BASE_TYPE:
+ if (die_type->die_type.base_type.usize != 0)
+ size = die_type->die_type.base_type.usize;
+ else if (die_type->die_type.base_type.ssize != 0)
+ size = die_type->die_type.base_type.ssize;
+ return size;
+ case TYPE_DEF:
+ return compute_variable_total_size (die_type->die_type.type_def.ptr_type);
+ case ENUM_TYPE:
+ if (die_type->die_type.enum_type.usize != 0)
+ size = die_type->die_type.enum_type.usize;
+ else if (die_type->die_type.enum_type.ssize != 0)
+ size = die_type->die_type.enum_type.ssize;
+ return size;
+ case TAB_TYPE:
+ {
+ size = (size == 0) ? 1 : size;
+ subrange_type *i_subrange = die_type->die_type.tab_type.subrange_head;
+ while (i_subrange != NULL)
+ {
+ if (i_subrange->usize != 0)
+ size = i_subrange->usize * size;
+ else if (i_subrange->ssize != 0)
+ size = i_subrange->ssize * size;
+ i_subrange = i_subrange->next;
+ }
+ return size *
+ compute_variable_total_size (die_type->die_type.tab_type.ptr_type);
+ }
+ case MEMBER_TYPE:
+ return 0;
+ case MEMBER_PARENT:
+ if (die_type->die_type.member_parent.ssize != 0)
+ size = die_type->die_type.member_parent.ssize;
+ else if (die_type->die_type.member_parent.usize != 0)
+ size = die_type->die_type.member_parent.usize;
+ return size;
+ case TYPE_REF:
+ if (die_type->die_type.type_ref.type == PTR_TYPE)
+ {
+ if (die_type->die_type.type_ref.ssize != 0)
+ size = die_type->die_type.type_ref.ssize;
+ else if (die_type->die_type.type_ref.usize != 0)
+ size = die_type->die_type.type_ref.usize;
+ return size;
+ }
+ else
+ return compute_variable_total_size (
+ die_type->die_type.type_ref.ptr_type);
+ case VARIABLE_TYPE:
+ return compute_variable_total_size (
+ die_type->die_type.variable_type.ptr_type);
+ }
+ return size;
+}
+
+static void
+reset_displayed_field (generic_type *mp_type)
+{
+ generic_type *i_mp = mp_type;
+ while (i_mp != NULL)
+ {
+ i_mp->die_type.member_parent.displayed = false;
+ i_mp = i_mp->next;
+ }
+}
+
+void
+resolve_and_display_variable_info (void)
+{
+ generic_type *i_vt = variable_type_list.head;
+ while (i_vt != NULL)
+ {
+ if (!link_variable_information (i_vt,
+ i_vt->die_type.variable_type.ptr_die_offset))
+ {
+ fprintf (stderr,
+ _("failed linking variable informations with variable %s "
+ "with die_offset %lx, ptr_offset %lx and location %lx\n"),
+ i_vt->die_type.variable_type.name,
+ i_vt->die_type.variable_type.die_offset,
+ i_vt->die_type.variable_type.ptr_die_offset,
+ i_vt->die_type.variable_type.location_addr);
+ }
+ if (i_vt->die_type.variable_type.has_location_addr)
+ {
+ i_vt->die_type.variable_type.ttsize =
+ compute_variable_total_size (i_vt);
+ display_variable_type (i_vt, 0);
+ reset_displayed_field (struct_type_list.head);
+ reset_displayed_field (union_type_list.head);
+ printf ("\n");
+ }
+ i_vt = i_vt->next;
+ }
+}
+
static void
free_enum_constant (enum_constant *ec)
{
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index 818d280db5a..01606a3cd3f 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -419,6 +419,7 @@ extern void free_debug_section (enum dwarf_section_display_enum);
extern bool load_separate_debug_files (void *, const char *);
extern void close_debug_file (void *);
extern void *open_debug_file (const char *);
+extern void resolve_and_display_variable_info (void);
extern void free_debug_memory (void);
extern void free_mapping_info_struct (void);
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 71420cb2542..add45dc7a6e 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -4564,6 +4564,7 @@ dump_global_variable_info (bfd *abfd, asection *section,
if (load_specific_debug_section (info, section, abfd))
{
debug_displays [debug_variable_info].display (sec, abfd);
+ resolve_and_display_variable_info ();
free_mapping_info_struct ();
free_debug_section (info);
}
--
2.25.1
More information about the Binutils
mailing list