[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