[PATCH v6 4/5] objdump: Link and display all variables information

Guillaume VACHERIAS guillaume.vacherias@foss.st.com
Fri Nov 21 12:51:31 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_ptr, type_ref and variable_type structures.

Implement the display for variables and types. It is based on recursively
accessing fields `ptr_type`, allowing the system to print complex types such
as pointers to structs, arrays of typedefs, or nested const or volatile types.
When displaying a variable information, it starts by accessing and printing
all information held by structure variable_type. It then recursevely access
the member `field_type` to traverse and display all underlying types of the
variable.

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_type_ptr): Likewise for type_ptr structure.
	(display_type_ref): Likewise for type_ref 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   | 570 +++++++++++++++++++++++++++++++++++++++++++++
 binutils/dwarf.h   |   7 +
 binutils/objdump.c |   1 +
 3 files changed, 578 insertions(+)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 9f45d108100..b0d8a073d70 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);
 
@@ -2590,6 +2608,16 @@ display_lang (uint64_t uvalue)
     }
 }
 
+static void
+display_base_type (const base_type bt)
+{
+  printf ("type: %s", bt.name);
+  if (bt.size_type == UNSIGNED_S)
+    printf (", size: %#lx\n", bt.size.usize);
+  else if (bt.size_type == SIGNED_S)
+    printf (", size: %#lx\n", bt.size.ssize);
+}
+
 static base_type *
 get_or_create_base_type (uint64_t die_offset)
 {
@@ -2617,6 +2645,12 @@ get_or_create_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 type_def*
 get_or_create_type_def (uint64_t die_offset)
 {
@@ -2645,6 +2679,18 @@ get_or_create_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 = %ld\n", head->name, head->value);
+      head = head->next;
+    }
+}
+
 static enum_constant *
 get_or_create_enum_constant (enum_type *et, uint64_t die_offset)
 {
@@ -2671,6 +2717,18 @@ 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)
+{
+  if (et.name == NULL)
+    printf ("type: enum <anonymous>, size: %#lx\n", et.size.usize);
+  else
+    printf ("type: enum %s, size: %#lx\n", et.name, et.size.usize);
+  PRINT_LBRACE (nb_tab);
+  display_enum (et.enum_const_head, nb_tab + 1);
+  PRINT_RBRACE (nb_tab);
+}
+
 static enum_type *
 get_or_create_enum_type (uint64_t die_offset)
 {
@@ -2700,6 +2758,20 @@ get_or_create_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->size_type == UNSIGNED_S)
+	printf ("[%lu]", head->size.usize);
+      else if (head->size_type == SIGNED_S)
+	printf ("[%ld]", head->size.ssize);
+      head = head->next;
+    }
+}
+
 static subrange_type *
 get_or_create_subrange_type (tab_type *tt, uint64_t die_offset)
 {
@@ -2726,6 +2798,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 tab_type *
 get_or_create_tab_type (uint64_t die_offset)
 {
@@ -2755,6 +2835,15 @@ get_or_create_tab_type (uint64_t die_offset)
   return tab_type_list.tail;
 }
 
+static void
+display_member_type (const member_type mt)
+{
+  if (mt.name == NULL)
+    printf ("member: (anonymous), offset: %#lx\n", mt.member_offset);
+  else
+    printf ("member: %s, offset: %#lx\n", mt.name, mt.member_offset);
+}
+
 static member_type *
 get_or_create_member_type (member_parent *parent, uint64_t die_offset)
 {
@@ -2785,6 +2874,39 @@ get_or_create_member_type (member_parent *parent, 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)
+    {
+      if (mp.name == NULL)
+	printf ("type: union <anonymous>");
+      else
+	printf ("type: union %s", mp.name);
+    }
+  else if (mp.type == STRUCT_TYPE)
+    {
+      if (mp.name == NULL)
+	printf ("type: struct <anonymous>");
+      else
+	printf ("type: struct %s", mp.name);
+    }
+
+  if (mp.is_declaration)
+    printf (", incomplete or non-defining declaration\n");
+  else if (mp.size_type == UNSIGNED_S)
+    printf (", size: %#lx\n", mp.size.usize);
+  else if (mp.size_type == SIGNED_S)
+    printf (", size: %#lx\n", mp.size.ssize);
+
+  return false;
+}
+
 static member_parent *
 get_or_create_member_parent (struct member_parent_l *mp_list,
 				     uint64_t die_offset)
@@ -2803,6 +2925,8 @@ get_or_create_member_parent (struct member_parent_l *mp_list,
       ret->field_type = MEMBER_PARENT;
       ret->type = UNION_TYPE;
       ret->is_declaration = false;
+      ret->linked = false;
+      ret->displayed = false;
       if (mp_list->head == NULL)
 	{
 	  mp_list->head = ret;
@@ -2817,6 +2941,15 @@ get_or_create_member_parent (struct member_parent_l *mp_list,
   return mp_list->tail;
 }
 
+static void
+display_type_ptr (const type_ptr tp)
+{
+  if (tp.size_type == UNSIGNED_S)
+    printf ("type: ptr, size: %#lx\n", tp.size.usize);
+  else if (tp.size_type == SIGNED_S)
+    printf ("type: ptr, size: %#lx\n", tp.size.ssize);
+}
+
 static type_ptr *
 get_or_create_type_ptr (uint64_t die_offset)
 {
@@ -2846,6 +2979,20 @@ get_or_create_type_ptr (uint64_t die_offset)
   return ptr_type_list.tail;
 }
 
+static void
+display_type_ref (const type_ref tr)
+{
+  switch (tr.type)
+    {
+    case CONST_TYPE:
+      printf ("type: const\n");
+      break;
+    case VOLATILE_TYPE:
+      printf ("type: volatile\n");
+      break;
+    }
+}
+
 static type_ref *
 get_or_create_type_ref (struct type_ref_l *tr_list,
 			uint64_t die_offset)
@@ -2876,6 +3023,92 @@ get_or_create_type_ref (struct type_ref_l *tr_list,
   return tr_list->tail;
 }
 
+static void
+display_variable_type (generic_type gt,
+		       int nb_tab)
+{
+  switch (gt.field_type)
+    {
+    case NO_TYPE:
+      break;
+    case BASE_TYPE:
+      PRINT_SPACE (nb_tab);
+      display_base_type (*gt.die_type.base_type);
+      break;
+    case TYPE_DEF:
+      PRINT_SPACE (nb_tab);
+      display_type_def (*gt.die_type.type_def);
+      PRINT_LBRACE (nb_tab);
+      display_variable_type (gt.die_type.type_def->ptr_type, nb_tab + 1);
+      PRINT_RBRACE (nb_tab);
+      break;
+    case ENUM_TYPE:
+      PRINT_SPACE (nb_tab);
+      display_enum_type (*gt.die_type.enum_type, nb_tab);
+      break;
+    case TAB_TYPE:
+      PRINT_SPACE (nb_tab);
+      display_tab_type (*gt.die_type.tab_type);
+      display_variable_type (gt.die_type.tab_type->ptr_type, nb_tab + 1);
+      break;
+    case MEMBER_PARENT:
+      {
+	PRINT_SPACE (nb_tab);
+	if (display_member_parent (*gt.die_type.member_parent))
+	  break;
+	gt.die_type.member_parent->displayed = true;
+	if (gt.die_type.member_parent->member_head != NULL)
+	  {
+	    PRINT_LBRACE (nb_tab);
+	    generic_type generic_mt
+	      = { { .member_type = gt.die_type.member_parent->member_head },
+		  MEMBER_TYPE };
+	    display_variable_type (generic_mt, nb_tab + 1);
+	    PRINT_RBRACE (nb_tab);
+	  }
+      }
+      break;
+    case MEMBER_TYPE:
+      {
+	PRINT_SPACE (nb_tab + 1);
+	display_member_type (*gt.die_type.member_type);
+	display_variable_type (gt.die_type.member_type->ptr_type, nb_tab + 2);
+	if (gt.die_type.member_type->next != NULL)
+	  {
+	    generic_type generic_mt
+	      = { { .member_type = gt.die_type.member_type->next },
+		  MEMBER_TYPE };
+	    display_variable_type (generic_mt, nb_tab);
+	  }
+      }
+      break;
+    case TYPE_PTR:
+      PRINT_SPACE (nb_tab);
+      display_type_ptr (*gt.die_type.type_ptr);
+      display_variable_type (gt.die_type.type_ptr->ptr_type, nb_tab + 1);
+      break;
+    case TYPE_REF:
+      PRINT_SPACE (nb_tab);
+      display_type_ref (*gt.die_type.type_ref);
+      display_variable_type (gt.die_type.type_ref->ptr_type, nb_tab + 1);
+      break;
+    case VARIABLE_TYPE:
+      if (gt.die_type.variable_type->is_specification)
+	printf ("Incomplete, non-defining or separate declaration:\n");
+      else if (gt.die_type.variable_type->is_abstract_origin)
+	printf ("Inlined instance:\n");
+      else
+	{
+	  uint64_t origin = gt.die_type.variable_type->location_addr;
+	  uint64_t end = origin + gt.die_type.variable_type->total_size;
+	  printf ("%s @ 0x%08lx 0x%08lx\n", gt.die_type.variable_type->name,
+		  origin, end);
+	}
+      display_variable_type (gt.die_type.variable_type->ptr_type, nb_tab + 1);
+      break;
+    }
+}
+
 static variable_type *
 get_or_create_variable_type (uint64_t die_offset)
 {
@@ -13883,6 +14116,343 @@ struct dwarf_section_display debug_displays[] =
 /* A static assertion.  */
 extern int debug_displays_assert[ARRAY_SIZE (debug_displays) == max ? 1 : -1];
 
+static generic_type
+do_link_variable_information (generic_type src_die,
+			      generic_type target_die)
+{
+  assert (src_die.field_type != NO_TYPE);
+
+  generic_type res = { { .base_type = NULL }, NO_TYPE };
+
+  switch (src_die.field_type)
+    {
+    case NO_TYPE:
+    case BASE_TYPE:
+    case ENUM_TYPE:
+    case MEMBER_PARENT:
+      break;
+    case MEMBER_TYPE:
+      src_die.die_type.member_type->ptr_type = target_die;
+      res = target_die;
+      break;
+    case TYPE_DEF:
+      src_die.die_type.type_def->ptr_type = target_die;
+      res = target_die;
+      break;
+    case TAB_TYPE:
+      src_die.die_type.tab_type->ptr_type = target_die;
+      res = target_die;
+      break;
+    case TYPE_PTR:
+      src_die.die_type.type_ptr->ptr_type = target_die;
+      res = target_die;
+      break;
+    case TYPE_REF:
+      src_die.die_type.type_ref->ptr_type = target_die;
+      res = target_die;
+      break;
+    case VARIABLE_TYPE:
+      src_die.die_type.variable_type->ptr_type = target_die;
+      /* If the variable_type of src_die has either DW_AT_specification or
+	 DW_AT_abstract_origin present then src_die's variable_type could
+	 have missing attributes such as DW_AT_name.  Hence move the attribute
+	 DW_AT_location held by src_die to target_die which have complete
+	 attributes.  */
+      if (src_die.die_type.variable_type->is_specification
+	  || src_die.die_type.variable_type->is_abstract_origin)
+	{
+	  assert (target_die.die_type.variable_type != NULL
+		  && target_die.field_type == VARIABLE_TYPE);
+	  target_die.die_type.variable_type->location_addr
+	    = src_die.die_type.variable_type->location_addr;
+	}
+      res = target_die;
+      break;
+    }
+
+  return res;
+}
+
+static bool
+link_variable_information (generic_type src_die,  uint64_t ptr_die_offset)
+{
+  assert (src_die.field_type != NO_TYPE
+	  && src_die.die_type.base_type != NULL);
+
+  base_type *i_base_type = base_type_list.head;
+  while (i_base_type != NULL)
+    {
+      if (i_base_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_bt = { { .base_type = i_base_type }, BASE_TYPE };
+	  do_link_variable_information (src_die, generic_bt);
+	  return true;
+	}
+      i_base_type = i_base_type->next;
+    }
+
+  type_def *i_type_def = type_def_list.head;
+  while (i_type_def != NULL)
+    {
+      if (i_type_def->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_td = { { .type_def = i_type_def }, TYPE_DEF };
+	  generic_td = do_link_variable_information (src_die, generic_td);
+	  return link_variable_information (generic_td,
+					    i_type_def->ptr_die_offset);
+	}
+      i_type_def = i_type_def->next;
+    }
+
+  enum_type *i_enum_type = enum_type_list.head;
+  while (i_enum_type != NULL)
+    {
+      if (i_enum_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_et = { { .enum_type = i_enum_type }, ENUM_TYPE };
+	  do_link_variable_information (src_die, generic_et);
+	  return true;
+	}
+      i_enum_type = i_enum_type->next;
+    }
+
+  tab_type *i_tab_type = tab_type_list.head;
+  while (i_tab_type != NULL)
+    {
+      if (i_tab_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_tt = { { .tab_type = i_tab_type }, TAB_TYPE };
+	  generic_tt = do_link_variable_information (src_die, generic_tt);
+	  return link_variable_information (generic_tt,
+					    i_tab_type->ptr_die_offset);
+	}
+      i_tab_type = i_tab_type->next;
+    }
+
+  member_parent *i_struct_type = struct_type_list.head;
+  while (i_struct_type != NULL)
+    {
+      if (i_struct_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_mp = { { .member_parent = i_struct_type },
+				      MEMBER_PARENT };
+	  do_link_variable_information (src_die, generic_mp);
+
+	  if (i_struct_type->linked)
+	    return true;
+
+	  i_struct_type->linked = true;
+	  member_type *i_member_type = i_struct_type->member_head;
+	  while (i_member_type != NULL)
+	    {
+	      generic_type generic_mt = { { .member_type = i_member_type },
+					  MEMBER_TYPE };
+	      link_variable_information (generic_mt,
+					 i_member_type->ptr_die_offset);
+	      i_member_type = i_member_type->next;
+	    }
+	  return true;
+	}
+      i_struct_type = i_struct_type->next;
+    }
+
+  member_parent *i_union_type = union_type_list.head;
+  while (i_union_type != NULL)
+    {
+      if (i_union_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_mp = { { .member_parent = i_union_type },
+				      MEMBER_PARENT };
+	  do_link_variable_information (src_die, generic_mp);
+
+	  if (i_union_type->linked)
+	    return true;
+
+	  i_union_type->linked = true;
+	  member_type *i_member_type = i_union_type->member_head;
+	  while (i_member_type != NULL)
+	    {
+	      generic_type generic_mt = { { .member_type = i_member_type },
+					  MEMBER_TYPE };
+	      link_variable_information (generic_mt,
+					 i_member_type->ptr_die_offset);
+	      i_member_type = i_member_type->next;
+	    }
+	  return true;
+	}
+      i_union_type = i_union_type->next;
+    }
+
+  type_ptr *i_ptr_type = ptr_type_list.head;
+  while (i_ptr_type != NULL)
+    {
+      if (i_ptr_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_pt = { { .type_ptr = i_ptr_type }, TYPE_PTR };
+	  generic_pt = do_link_variable_information (src_die, generic_pt);
+	  return link_variable_information (generic_pt,
+					    i_ptr_type->ptr_die_offset);
+	}
+      i_ptr_type = i_ptr_type->next;
+    }
+
+  type_ref *i_const_type = const_type_list.head;
+  while (i_const_type != NULL)
+    {
+      if (i_const_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_ref = { { .type_ref = i_const_type }, TYPE_REF };
+	  generic_ref = do_link_variable_information (src_die, generic_ref);
+	  return link_variable_information (generic_ref,
+					    i_const_type->ptr_die_offset);
+	}
+      i_const_type = i_const_type->next;
+    }
+
+  type_ref *i_volatile_type = volatile_type_list.head;
+  while (i_volatile_type != NULL)
+    {
+      if (i_volatile_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_ref = { { .type_ref = i_volatile_type },
+				       TYPE_REF };
+	  generic_ref = do_link_variable_information (src_die, generic_ref);
+	  return link_variable_information (generic_ref,
+					    i_volatile_type->ptr_die_offset);
+	}
+      i_volatile_type = i_volatile_type->next;
+    }
+
+  variable_type *i_variable_type = variable_type_list.head;
+  while (i_variable_type != NULL)
+    {
+      if (i_variable_type->die_offset == ptr_die_offset)
+	{
+	  generic_type generic_vt = { { .variable_type = i_variable_type },
+				      VARIABLE_TYPE };
+	  generic_vt = do_link_variable_information (src_die, generic_vt);
+	  return link_variable_information (generic_vt,
+					    i_variable_type->ptr_die_offset);
+	}
+      i_variable_type = i_variable_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
+      && i_variable_type == NULL)
+    return false;
+
+  return true;
+}
+
+static uint64_t
+compute_variable_total_size (generic_type src_die)
+{
+  uint64_t size = 0;
+
+  switch (src_die.field_type)
+    {
+    case NO_TYPE:
+      break;
+    case BASE_TYPE:
+      if (src_die.die_type.base_type->size_type == UNSIGNED_S)
+	size = src_die.die_type.base_type->size.usize;
+      else if (src_die.die_type.base_type->size_type == SIGNED_S)
+	size = src_die.die_type.base_type->size.ssize;
+      return size;
+    case TYPE_DEF:
+      return compute_variable_total_size (src_die.die_type.type_def->ptr_type);
+    case ENUM_TYPE:
+      if (src_die.die_type.enum_type->size_type == UNSIGNED_S)
+	size = src_die.die_type.enum_type->size.usize;
+      else if (src_die.die_type.enum_type->size_type == SIGNED_S)
+	size = src_die.die_type.enum_type->size.ssize;
+      return size;
+    case TAB_TYPE:
+      {
+	size = (size == 0) ? 1 : size;
+	subrange_type *i_subrange = src_die.die_type.tab_type->subrange_head;
+	while (i_subrange != NULL)
+	  {
+	    if (i_subrange->size_type == UNSIGNED_S)
+	      size = i_subrange->size.usize * size;
+	    else if (i_subrange->size_type == SIGNED_S)
+	      size = i_subrange->size.ssize * size;
+	    i_subrange = i_subrange->next;
+	  }
+	return size
+	  * compute_variable_total_size (src_die.die_type.tab_type->ptr_type);
+      }
+    case MEMBER_TYPE:
+      return 0;
+    case MEMBER_PARENT:
+      if (src_die.die_type.member_parent->size_type == UNSIGNED_S)
+	size = src_die.die_type.member_parent->size.usize;
+      else if (src_die.die_type.member_parent->size_type == SIGNED_S)
+	size = src_die.die_type.member_parent->size.ssize;
+      return size;
+    case TYPE_PTR:
+      if (src_die.die_type.type_ptr->size_type == UNSIGNED_S)
+	size = src_die.die_type.type_ptr->size.usize;
+      else if (src_die.die_type.type_ptr->size_type == SIGNED_S)
+	size = src_die.die_type.type_ptr->size.ssize;
+      return size;
+    case TYPE_REF:
+      return compute_variable_total_size (src_die.die_type.type_ref->ptr_type);
+    case VARIABLE_TYPE:
+      return compute_variable_total_size (
+	src_die.die_type.variable_type->ptr_type);
+    }
+  return size;
+}
+
+static void
+reset_displayed_field (member_parent *mp_type)
+{
+  member_parent *i_mp = mp_type;
+  while (i_mp != NULL)
+    {
+      i_mp->displayed = false;
+      i_mp = i_mp->next;
+    }
+}
+
+void
+resolve_and_display_variable_info (void)
+{
+  variable_type *i_vt = variable_type_list.head;
+  while (i_vt != NULL)
+    {
+     generic_type generic_vt = { { .variable_type = i_vt }, VARIABLE_TYPE };
+      if (i_vt->has_location_addr
+	  && link_variable_information (generic_vt, i_vt->ptr_die_offset))
+	{
+	  uint64_t vt_size = compute_variable_total_size (generic_vt);
+	  /* When is_specification or is_abstract_origin is true.  The current
+	     variable_type i_vt only holds DW_AT_location value and could be
+	     missing DW_AT_name for example.  Hence the move the size value to
+	     the underlying DW_TAG_variable and display the underlying
+	     DW_TAG_variable.  */
+	  if (i_vt->is_specification || i_vt->is_abstract_origin)
+	    {
+	      assert (i_vt->ptr_type.field_type == VARIABLE_TYPE
+		      && i_vt->ptr_type.die_type.variable_type != NULL);
+	      i_vt->ptr_type.die_type.variable_type->total_size = vt_size;
+	    }
+	  else
+	    i_vt->total_size = vt_size;
+	  display_variable_type (generic_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_base_type (base_type *bt)
 {
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index bdb6f669c8d..60b6ddeebf5 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -252,6 +252,12 @@ struct member_parent
      this attribute is present the stucture/union is either incomplete,
      non-defining or in a separate entity.  */
   bool is_declaration;
+  /* This field exists to avoid linking indefinitively nested structure.  It is
+     set to true when the underlying type is known (linked).  */
+  bool linked;
+  /* This field exists to avoid displaying indefinitvely nested structure.  It
+     is set to true when informations from member_parent are displayed.  */
+  bool displayed;
 };
 
 /* Structure used to hold DW_TAG_pointer_type.  */
@@ -498,6 +504,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 97ec2698a0e..e4e30f0f58a 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -4552,6 +4552,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.43.0



More information about the Binutils mailing list