[v4,2/5] objdump: New feature display of global variable information based on DWARF info section

Message ID 20250909124344.318803-2-guillaume.vacherias@foss.st.com
State New
Headers
Series [v4,1/5] Add suitable defines to use at call and use sites |

Commit Message

Guillaume VACHERIAS - foss Sept. 9, 2025, 12:43 p.m. UTC
  Add in objdump option --map-global-vars to display global variable
information. This option relies on DWARF2 debug information. It is useful for
tracking the location and type of all global variables in the given object
file.

binutils/

	* objdump.c (dump_global_vars_info): New function. Entry point of
	display of variable information option.
	(dump_global_variable_info): New function. Load and go through dwarf
	info section to process information.
	* dwarf.c (insert_element_in_list): New function. Retrieve valuable
	information from dwarf attributes of all DWARF debug information
	entries.
	(get_location_expression): New function. Useful to retrieve
	DW_AT_location value which gives the data object location.
	(read_and_display_attr_value): Add arguments useful to identify DWARF
	information entry, and instrument with function
	insert_element_in_list.
	(read_and_display_attr): Add arguments useful to identify DWARF
	information entry.
	(process_debug_info): Add argument to process DWARF information entry
	for the display of global variable information.
	(display_variable_mapping_info): New function.

binutils/doc/

	* binutils.texi: Add documentation for new option --map-global-vars.

Signed-off-by: Guillaume VACHERIAS <guillaume.vacherias@foss.st.com>
---
 binutils/doc/binutils.texi |   7 +
 binutils/dwarf.c           | 390 +++++++++++++++++++++++++++++++++++--
 binutils/dwarf.h           |   1 +
 binutils/objdump.c         |  74 ++++++-
 4 files changed, 449 insertions(+), 23 deletions(-)
  

Patch

diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 89425b8a15b..21c99a6475e 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2294,6 +2294,7 @@  objdump [@option{-a}|@option{--archive-headers}]
         [@option{-WN}|@option{--dwarf=no-follow-links}]
         [@option{-wD}|@option{--dwarf=use-debuginfod}]
         [@option{-wE}|@option{--dwarf=do-not-use-debuginfod}]
+        [@option{--map-global-vars}]
         [@option{-L}|@option{--process-links}]
         [@option{--ctf=}@var{section}]
         [@option{--sframe=}@var{section}]
@@ -2540,6 +2541,12 @@  for specification with @option{-b} or @option{-m}.
 Display information for section @var{name}.  This option may be
 specified multiple times.
 
+@item --map-global-vars
+Display global variable information.  This option only works when the object
+file is compiled with the debug option.  More precisely, it relies on DWARF2
+debug information entries.  It is useful for tracking the location and type of
+all global variables in the given object file.
+
 @item -L
 @itemx --process-links
 Display the contents of non-debug sections found in separate debuginfo
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index b2bb3e595fe..9beea4d0a91 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -53,6 +53,7 @@ 
 
 #define DO_LOC     0x1
 #define DO_TYPES   0x2
+#define DO_GLOBAL_VARS 0x4
 
 static const char *regname (unsigned int regno, int row);
 static const char *regname_internal_by_table_only (unsigned int regno);
@@ -1165,6 +1166,141 @@  display_block (unsigned char *data,
   return data;
 }
 
+static int
+get_location_expression (int die_tag,
+			 unsigned long attribute,
+			 unsigned char * data,
+			 unsigned int pointer_size,
+			 unsigned int offset_size,
+			 int dwarf_version,
+			 uint64_t length,
+			 uint64_t die_offset,
+			 uint64_t cu_offset,
+			 struct dwarf_section * section)
+{
+  unsigned op;
+  uint64_t uvalue = 0;
+  int64_t svalue;
+  unsigned char *end = data + length;
+  int need_frame_base = 0;
+
+  while (data < end)
+    {
+      op = *data++;
+
+      switch (op)
+	{
+	case DW_OP_addr:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, pointer_size, end);
+	  break;
+	case DW_OP_const1u:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+	  break;
+	case DW_OP_const1s:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 1, end);
+	  break;
+	case DW_OP_const2u:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 2, end);
+	  break;
+	case DW_OP_const2s:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 2, end);
+	  break;
+	case DW_OP_const4u:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 4, end);
+	  break;
+	case DW_OP_const4s:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 4, end);
+	  break;
+	case DW_OP_const8u:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 8, end);
+	  break;
+	case DW_OP_const8s:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 8, end);
+	  break;
+	case DW_OP_bra:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 2, end);
+	  break;
+	case DW_OP_skip:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 2, end);
+	  break;
+
+	case DW_OP_deref_size:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+	  break;
+	case DW_OP_xderef_size:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+	  break;
+
+	case DW_OP_call2:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 2, end);
+	  break;
+	case DW_OP_call4:
+	  SAFE_SIGNED_BYTE_GET_AND_INC (svalue, data, 4, end);
+	  break;
+	case DW_OP_call_ref:
+	  if (dwarf_version == -1)
+	    /* No way to tell where the next op is, so just bail.  */
+	    return need_frame_base;
+	  else if (dwarf_version == 2)
+	    SAFE_BYTE_GET_AND_INC (uvalue, data, pointer_size, end);
+	  else
+	    SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+	  break;
+	case DW_OP_implicit_value:
+	  READ_ULEB (uvalue, data, end);
+	  break;
+	case DW_OP_implicit_pointer:
+	case DW_OP_GNU_implicit_pointer:
+	  if (dwarf_version == -1)
+      return need_frame_base;
+	  else if (dwarf_version == 2)
+      SAFE_BYTE_GET_AND_INC (uvalue, data, pointer_size, end);
+	  else
+      SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+	  break;
+	case DW_OP_entry_value:
+	case DW_OP_GNU_entry_value:
+	  READ_ULEB (uvalue, data, end);
+	  /* PR 17531: file: 0cc9cd00.  */
+	  if (uvalue > (size_t) (end - data))
+	    uvalue = end - data;
+	  if (get_location_expression
+	      (die_tag, attribute, data, pointer_size, offset_size,
+	       dwarf_version, uvalue, die_offset, cu_offset, section))
+	    need_frame_base = 1;
+	  data += uvalue;
+	  break;
+	case DW_OP_const_type:
+	case DW_OP_GNU_const_type:
+	  READ_ULEB (uvalue, data, end);
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+	  break;
+	case DW_OP_deref_type:
+	case DW_OP_GNU_deref_type:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+	  break;
+	case DW_OP_GNU_parameter_ref:
+	  SAFE_BYTE_GET_AND_INC (uvalue, data, 4, end);
+	  break;
+	case DW_OP_addrx:
+	  READ_ULEB (uvalue, data, end);
+	  break;
+	case DW_OP_GNU_variable_value:
+	  if (dwarf_version == -1)
+	    return need_frame_base;
+	  else if (dwarf_version == 2)
+	    SAFE_BYTE_GET_AND_INC (uvalue, data, pointer_size, end);
+	  else
+	    SAFE_BYTE_GET_AND_INC (uvalue, data, offset_size, end);
+	  break;
+
+	default:
+	  return need_frame_base;
+	}
+    }
+  return need_frame_base;
+}
+
 static int
 decode_location_expression (unsigned char * data,
 			    unsigned int pointer_size,
@@ -2435,6 +2571,47 @@  display_lang (uint64_t uvalue)
     }
 }
 
+static void
+insert_element_in_list (enum dwarf_tag dw_tag,
+			enum dwarf_attribute dw_attr ATTRIBUTE_UNUSED,
+			uint64_t die_offset ATTRIBUTE_UNUSED,
+			const uint64_t *uvalue ATTRIBUTE_UNUSED,
+			const int64_t *svalue ATTRIBUTE_UNUSED,
+			const char *data ATTRIBUTE_UNUSED,
+			bool is_union ATTRIBUTE_UNUSED)
+{
+  /* TODO: Retrieve attributes information from
+     below dwarf entries tag.  */
+  switch (dw_tag)
+    {
+    case DW_TAG_base_type:
+      break;
+    case DW_TAG_typedef:
+      break;
+    case DW_TAG_enumerator:
+      break;
+    case DW_TAG_enumeration_type:
+      break;
+    case DW_TAG_subrange_type:
+      break;
+    case DW_TAG_array_type:
+      break;
+    case DW_TAG_member:
+      break;
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+      break;
+    case DW_TAG_const_type:
+    case DW_TAG_volatile_type:
+    case DW_TAG_pointer_type:
+      break;
+    case DW_TAG_variable:
+      break;
+    default:
+      break;
+    }
+}
+
 static unsigned char *
 read_and_display_attr_value (unsigned long attribute,
 			     unsigned long form,
@@ -2451,7 +2628,11 @@  read_and_display_attr_value (unsigned long attribute,
 			     struct dwarf_section *section,
 			     struct cu_tu_set *this_set,
 			     char delimiter,
-			     int level)
+			     int level,
+			     bool do_var_map,
+			     int die_tag,
+			     unsigned long die_offset,
+			     bool is_union)
 {
   int64_t svalue;
   uint64_t uvalue = 0;
@@ -2577,7 +2758,9 @@  read_and_display_attr_value (unsigned long attribute,
 					  cu_offset, pointer_size,
 					  offset_size, dwarf_version,
 					  debug_info_p, do_loc,
-					  section, this_set, delimiter, level);
+					  section, this_set, delimiter, level,
+					  do_var_map, die_tag, die_offset,
+					  is_union);
 
     case DW_FORM_implicit_const:
       uvalue = implicit_const;
@@ -2592,6 +2775,9 @@  read_and_display_attr_value (unsigned long attribute,
     case DW_FORM_ref_addr:
       if (!do_loc)
 	printf ("%c<%#" PRIx64 ">", delimiter, uvalue);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       break;
 
     case DW_FORM_GNU_ref_alt:
@@ -2603,6 +2789,9 @@  read_and_display_attr_value (unsigned long attribute,
 	  else
 	    printf ("%c<alt %#" PRIx64 ">", delimiter, uvalue);
 	}
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       /* FIXME: Follow the reference...  */
       break;
 
@@ -2611,8 +2800,14 @@  read_and_display_attr_value (unsigned long attribute,
     case DW_FORM_ref4:
     case DW_FORM_ref_sup4:
     case DW_FORM_ref_udata:
-      if (!do_loc)
-	printf ("%c<%#" PRIx64 ">", delimiter, uvalue + cu_offset);
+      {
+	uint64_t utmp = uvalue + cu_offset;
+	if (!do_loc)
+	  printf ("%c<%#" PRIx64 ">", delimiter, utmp);
+	if (do_var_map)
+	  insert_element_in_list (die_tag, attribute, die_offset,
+				 &utmp, NULL, NULL, is_union);
+      }
       break;
 
     case DW_FORM_data4:
@@ -2620,6 +2815,9 @@  read_and_display_attr_value (unsigned long attribute,
     case DW_FORM_sec_offset:
       if (!do_loc)
 	printf ("%c%#" PRIx64, delimiter, uvalue);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       break;
 
     case DW_FORM_flag_present:
@@ -2629,27 +2827,40 @@  read_and_display_attr_value (unsigned long attribute,
     case DW_FORM_sdata:
       if (!do_loc)
 	printf ("%c%" PRId64, delimiter, uvalue);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       break;
 
     case DW_FORM_udata:
       if (!do_loc)
 	printf ("%c%" PRIu64, delimiter, uvalue);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       break;
 
     case DW_FORM_implicit_const:
       if (!do_loc)
 	printf ("%c%" PRId64, delimiter, implicit_const);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       NULL, &implicit_const, NULL, is_union);
       break;
 
     case DW_FORM_ref_sup8:
     case DW_FORM_ref8:
     case DW_FORM_data8:
-      if (!do_loc)
+      if (!do_loc || do_var_map)
 	{
 	  uint64_t utmp = uvalue;
 	  if (form == DW_FORM_ref8)
 	    utmp += cu_offset;
-	  printf ("%c%#" PRIx64, delimiter, utmp);
+	  if (do_var_map)
+	    insert_element_in_list (die_tag, attribute, die_offset,
+				   &utmp, NULL, NULL, is_union);
+	  else
+	    printf ("%c%#" PRIx64, delimiter, utmp);
 	}
       break;
 
@@ -2661,11 +2872,24 @@  read_and_display_attr_value (unsigned long attribute,
 	  else
 	    printf (" %#" PRIx64 "%016" PRIx64, uvalue_hi, uvalue);
 	}
+      if (do_var_map)
+	{
+	  if (uvalue_hi == 0)
+	    insert_element_in_list (die_tag, attribute, die_offset,
+				   &uvalue, NULL, NULL, is_union);
+	  else
+	    insert_element_in_list (die_tag, attribute, die_offset,
+				   &uvalue_hi, NULL, NULL, is_union);
+	}
       break;
 
     case DW_FORM_string:
       if (!do_loc)
 	printf ("%c%.*s", delimiter, (int) (end - data), data);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       NULL, NULL, (const char *) data,
+			       is_union);
       data += strnlen ((char *) data, end - data);
       if (data < end)
 	data++;
@@ -2719,6 +2943,10 @@  read_and_display_attr_value (unsigned long attribute,
 	    printf (_("%c(indirect string, offset: %#" PRIx64 "): %s"),
 		    delimiter, uvalue, fetch_indirect_string (uvalue));
 	}
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       NULL, NULL, (const char *)
+			       fetch_indirect_string (uvalue), is_union);
       break;
 
     case DW_FORM_line_strp:
@@ -2732,6 +2960,10 @@  read_and_display_attr_value (unsigned long attribute,
 	    printf (_("%c(indirect line string, offset: %#" PRIx64 "): %s"),
 		    delimiter, uvalue, fetch_indirect_line_string (uvalue));
 	}
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       NULL, NULL, (const char *)
+			       fetch_indirect_line_string (uvalue), is_union);
       break;
 
     case DW_FORM_GNU_str_index:
@@ -2755,6 +2987,9 @@  read_and_display_attr_value (unsigned long attribute,
 	  else
 	    printf (_("%c(indexed string: %#" PRIx64 "): %s"),
 		    delimiter, uvalue, strng);
+	  if (do_var_map)
+	    insert_element_in_list (die_tag, attribute, die_offset,
+				   NULL, NULL, strng, is_union);
 	}
       break;
 
@@ -2769,6 +3004,10 @@  read_and_display_attr_value (unsigned long attribute,
 	    printf (_("%c(alt indirect string, offset: %#" PRIx64 ") %s"),
 		    delimiter, uvalue, fetch_alt_indirect_string (uvalue));
 	}
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       NULL, NULL, fetch_alt_indirect_string (uvalue),
+			       is_union);
       break;
 
     case DW_FORM_indirect:
@@ -2779,6 +3018,9 @@  read_and_display_attr_value (unsigned long attribute,
       if (!do_loc)
 	printf ("%c%s: %#" PRIx64, delimiter, do_wide ? "" : "signature",
 		uvalue);
+      if (do_var_map)
+	insert_element_in_list (die_tag, attribute, die_offset,
+			       &uvalue, NULL, NULL, is_union);
       break;
 
     case DW_FORM_GNU_addr_index:
@@ -2855,14 +3097,25 @@  read_and_display_attr_value (unsigned long attribute,
 
 	  /* We have already displayed the form name.  */
 	  if (idx != (uint64_t) -1)
-	    printf (_("%c(index: %#" PRIx64 "): %#" PRIx64),
-		    delimiter, uvalue, idx);
+	    {
+	      printf (_("%c(index: %#" PRIx64 "): %#" PRIx64),
+		      delimiter, uvalue, idx);
+	      if (do_var_map)
+		insert_element_in_list (die_tag, attribute, die_offset,
+				       &uvalue, NULL, NULL, is_union);
+	    }
 	}
       break;
 
     case DW_FORM_strp_sup:
-      if (!do_loc)
-	printf ("%c<%#" PRIx64 ">", delimiter, uvalue + cu_offset);
+      {
+	uint64_t utmp = uvalue + cu_offset;
+	if (!do_loc)
+	  printf ("%c<%#" PRIx64 ">", delimiter, utmp);
+	if (do_var_map)
+	  insert_element_in_list (die_tag, attribute, die_offset,
+				 &utmp, NULL, NULL, is_union);
+      }
       break;
 
     default:
@@ -3143,6 +3396,72 @@  read_and_display_attr_value (unsigned long attribute,
 	}
     }
 
+  if (do_var_map)
+    {
+      switch (attribute)
+	{
+	case DW_AT_frame_base:
+	case DW_AT_location:
+	case DW_AT_loclists_base:
+	case DW_AT_rnglists_base:
+	case DW_AT_str_offsets_base:
+	case DW_AT_string_length:
+	case DW_AT_return_addr:
+	case DW_AT_data_member_location:
+	case DW_AT_vtable_elem_location:
+	case DW_AT_segment:
+	case DW_AT_static_link:
+	case DW_AT_use_location:
+	case DW_AT_call_value:
+	case DW_AT_GNU_call_site_value:
+	case DW_AT_call_data_value:
+	case DW_AT_GNU_call_site_data_value:
+	case DW_AT_call_target:
+	case DW_AT_GNU_call_site_target:
+	case DW_AT_call_target_clobbered:
+	case DW_AT_GNU_call_site_target_clobbered:
+	case DW_AT_allocated:
+	case DW_AT_associated:
+	case DW_AT_data_location:
+	case DW_AT_stride:
+	case DW_AT_upper_bound:
+	case DW_AT_lower_bound:
+	case DW_AT_rank:
+	  if (block_start)
+	    get_location_expression (die_tag,
+	      attribute,
+	      block_start,
+	      pointer_size,
+	      offset_size,
+	      dwarf_version,
+	      die_offset,
+	      uvalue,
+	      cu_offset,
+	      section);
+	  break;
+	case DW_AT_data_bit_offset:
+	case DW_AT_byte_size:
+	case DW_AT_bit_size:
+	case DW_AT_string_length_byte_size:
+	case DW_AT_string_length_bit_size:
+	case DW_AT_bit_stride:
+	  if (form == DW_FORM_exprloc)
+	    get_location_expression (die_tag,
+	      attribute,
+	      block_start,
+	      pointer_size,
+	      offset_size,
+	      dwarf_version,
+	      die_offset,
+	      uvalue,
+	      cu_offset,
+	      section);
+	  break;
+	default:
+	  break;
+	}
+    }
+
   if (do_loc || attribute == 0)
     return data;
 
@@ -3490,7 +3809,11 @@  read_and_display_attr (unsigned long attribute,
 		       int do_loc,
 		       struct dwarf_section *section,
 		       struct cu_tu_set *this_set,
-		       int level)
+		       int level,
+		       bool do_var_map,
+		       int die_tag,
+		       unsigned long die_offset,
+		       bool is_union)
 {
   if (!do_loc)
     printf ("   %-18s:", get_AT_name (attribute));
@@ -3498,7 +3821,9 @@  read_and_display_attr (unsigned long attribute,
 				      start, data, end,
 				      cu_offset, pointer_size, offset_size,
 				      dwarf_version, debug_info_p,
-				      do_loc, section, this_set, ' ', level);
+				      do_loc, section, this_set, ' ', level,
+				      do_var_map, die_tag, die_offset,
+				      is_union);
   if (!do_loc)
     printf ("\n");
   return data;
@@ -3753,6 +4078,8 @@  read_bases (abbrev_entry *   entry,
    and we do not want to display anything to the user.
    If do_types is TRUE, we are processing a .debug_types section instead of
    a .debug_info section.
+   If do_var_map is TRUE, we are processing a .debug_types section without
+   displaying anything to the user yet.
    The information displayed is restricted by the values in DWARF_START_DIE
    and DWARF_CUTOFF_LEVEL.
    Returns TRUE upon success.  Otherwise an error or warning message is
@@ -3834,7 +4161,7 @@  process_debug_info (struct dwarf_section * section,
       alloc_num_debug_info_entries = num_units;
     }
 
-  if (!(do_flags & DO_LOC))
+  if (!(do_flags & DO_LOC) || (do_flags & DO_GLOBAL_VARS))
     {
       load_debug_section_with_follow (str, file);
       load_debug_section_with_follow (line_str, file);
@@ -3961,6 +4288,7 @@  process_debug_info (struct dwarf_section * section,
       uint64_t abbrev_base;
       size_t abbrev_size;
       unsigned char *end_cu;
+      bool is_union;
 
       hdrptr = start;
       cu_offset = start - section_begin;
@@ -4132,6 +4460,7 @@  process_debug_info (struct dwarf_section * section,
       level = 0;
       last_level = level;
       saved_level = -1;
+      is_union = false;
       while (tags < start)
 	{
 	  unsigned long abbrev_number;
@@ -4255,6 +4584,12 @@  process_debug_info (struct dwarf_section * section,
 		/* Don't reset that for nested subprogram.  */
 		have_frame_base = 0;
 	      break;
+	    case DW_TAG_structure_type:
+	      is_union = false;
+	      break;
+	    case DW_TAG_union_type:
+	      is_union = true;
+	      break;
 	    }
 
 	  debug_info *debug_info_p = ((debug_information
@@ -4316,7 +4651,11 @@  process_debug_info (struct dwarf_section * section,
 					    || ! do_printing,
 					    section,
 					    this_set,
-					    level);
+					    level,
+					    do_flags & DO_GLOBAL_VARS,
+					    entry->tag,
+					    die_offset,
+					    is_union);
 	    }
 
 	  /* If a locview attribute appears before a location one,
@@ -4620,7 +4959,8 @@  display_formatted_table (unsigned char *data,
 						  0, 0, linfo->li_offset_size,
 						  linfo->li_version, NULL,
 			    ((content_type == DW_LNCT_path) != (namepass == 1)),
-						  section, NULL, '\t', -1);
+						  section, NULL, '\t', -1,
+						  false, 0, 0, false);
 	    }
 	}
 
@@ -5280,7 +5620,8 @@  display_debug_lines_decoded (struct dwarf_section *  section,
 							  linfo.li_offset_size,
 							  linfo.li_version,
 							  NULL, 1, section,
-							  NULL, '\t', -1);
+							  NULL, '\t', -1,
+							  false, 0, 0, false);
 		    }
 		  if (data >= end)
 		    {
@@ -5379,7 +5720,8 @@  display_debug_lines_decoded (struct dwarf_section *  section,
 							  linfo.li_offset_size,
 							  linfo.li_version,
 							  NULL, 1, section,
-							  NULL, '\t', -1);
+							  NULL, '\t', -1,
+							  false, 0, 0, false);
 		    }
 		  if (data >= end)
 		    {
@@ -6595,7 +6937,8 @@  display_debug_macro (struct dwarf_section *section,
 						       start, curr, end, 0, 0,
 						       offset_size, version,
 						       NULL, 0, section,
-						       NULL, ' ', -1);
+						       NULL, ' ', -1,
+						       false, 0, 0, false);
 		      if (n != nargs - 1)
 			printf (",");
 		    }
@@ -7683,6 +8026,13 @@  display_debug_str (struct dwarf_section *section,
   return 1;
 }
 
+static int
+display_variable_mapping_info (struct dwarf_section *section, void *file)
+{
+  return process_debug_info (section, file, section->abbrev_sec,
+			     DO_LOC | DO_GLOBAL_VARS);
+}
+
 static int
 display_debug_info (struct dwarf_section *section, void *file)
 {
@@ -10972,7 +11322,8 @@  display_debug_names (struct dwarf_section *section, void *file)
 							  0, 0, offset_size,
 							  dwarf_version, NULL,
 							  (tagno < 0), section,
-							  NULL, '=', -1);
+							  NULL, '=', -1,
+							  false, 0, 0, false);
 		}
 	      ++tagno;
 	    }
@@ -12926,6 +13277,7 @@  struct dwarf_section_display debug_displays[] =
      in the main file.	Hence we need to have two entries for .debug_str.  */
   { { ".debug_str",	    ".zdebug_str",	     "",	 NO_ABBREVS },	    display_debug_str,	    &do_debug_str,	false },
   { { ".note.gnu.build-id", "",                      "",	 NO_ABBREVS },	    display_debug_not_supported, NULL,		false },
+  { { ".debug_info",	    ".zdebug_info",	     ".dwinfo",	 ABBREV (abbrev)},  display_variable_mapping_info, &do_debug_info, true }
 };
 
 /* A static assertion.  */
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index 13afb4a0da3..8efd0c37b5b 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -124,6 +124,7 @@  enum dwarf_section_display_enum
   debug_sup,
   separate_debug_str,
   note_gnu_build_id,
+  debug_variable_info,
   max
 };
 
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 0bea4d0761c..20ee609c1f9 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -138,6 +138,7 @@  static bool color_output = false;	/* --visualize-jumps=color.  */
 static bool extended_color_output = false; /* --visualize-jumps=extended-color.  */
 static int process_links = false;       /* --process-links.  */
 static int show_all_symbols;            /* --show-all-symbols.  */
+static bool dump_global_vars;              /* --map-global-vars.  */
 static bool decompressed_dumps = false; /* -Z, --decompress.  */
 
 static struct symbol_entry
@@ -327,6 +328,8 @@  usage (FILE *stream, int status)
   -WE --dwarf=do-not-use-debuginfod\n\
                            When following links, do not query debuginfod servers\n"));
 #endif
+  fprintf (stream, _("\
+      --map-global-vars    Display memory mapping of global variables\n"));
   fprintf (stream, _("\
   -L, --process-links      Display the contents of non-debug sections in\n\
                             separate debuginfo files.  (Implies -WK)\n"));
@@ -496,7 +499,8 @@  enum option_values
 #endif
     OPTION_SFRAME,
     OPTION_VISUALIZE_JUMPS,
-    OPTION_DISASSEMBLER_COLOR
+    OPTION_DISASSEMBLER_COLOR,
+    OPTION_MAP_GLOBAL_VARS
   };
 
 static struct option long_options[]=
@@ -567,6 +571,7 @@  static struct option long_options[]=
   {"visualize-jumps", optional_argument, 0, OPTION_VISUALIZE_JUMPS},
   {"wide", no_argument, NULL, 'w'},
   {"disassembler-color", required_argument, NULL, OPTION_DISASSEMBLER_COLOR},
+  {"map-global-vars", no_argument, NULL, OPTION_MAP_GLOBAL_VARS},
   {NULL, no_argument, NULL, 0}
 };
 
@@ -4528,10 +4533,47 @@  dump_dwarf_section (bfd *abfd, asection *section,
       }
 }
 
-/* Dump the dwarf debugging information.  */
+static void
+dump_global_variable_info (bfd *abfd, asection *section,
+		    void *arg)
+{
+  const char *name = bfd_section_name (section);
+  const char *match;
+  bool is_mainfile = *(bool *) arg;
+
+  if (*name == 0)
+    return;
+
+  if (!is_mainfile && !process_links
+      && (section->flags & SEC_DEBUGGING) == 0)
+    return;
+
+  if (startswith (name, ".gnu.linkonce.wi."))
+    match = ".debug_info";
+  else
+    match = name;
+
+  if (((strcmp (debug_displays [info].section.uncompressed_name,match) == 0
+  || strcmp (debug_displays [info].section.compressed_name, match) == 0
+  || strcmp (debug_displays [info].section.xcoff_name, match) == 0))
+  && debug_displays [info].enabled != NULL
+  && *debug_displays [info].enabled)
+    {
+      struct dwarf_section *sec = &debug_displays [info].section;
 
+      if (load_specific_debug_section (info, section, abfd))
+	{
+	  debug_displays [debug_variable_info].display (sec, abfd);
+	  free_debug_section (info);
+	}
+    }
+}
+
+/* Dump the dwarf debugging information helper.  */
 static void
-dump_dwarf (bfd *abfd, bool is_mainfile)
+dump_dwarf_info (bfd *abfd,
+		 void (*operation) (bfd *, asection *, void *),
+		 bool is_mainfile)
 {
   /* The byte_get pointer should have been set at the start of dump_bfd().  */
   if (byte_get == NULL)
@@ -4557,7 +4599,23 @@  dump_dwarf (bfd *abfd, bool is_mainfile)
   init_dwarf_by_bfd_arch_and_mach (bfd_get_arch (abfd),
 				   bfd_get_mach (abfd));
 
-  bfd_map_over_sections (abfd, dump_dwarf_section, (void *) &is_mainfile);
+  bfd_map_over_sections (abfd, operation, (void *) &is_mainfile);
+}
+
+/* Dump the dwarf debugging information.  */
+
+static void
+dump_dwarf (bfd *abfd, bool is_mainfile)
+{
+  dump_dwarf_info (abfd, dump_dwarf_section, is_mainfile);
+}
+
+/* Dump all global variable information in memory.  */
+
+static void
+dump_global_vars_info (bfd *abfd, bool is_mainfile)
+{
+  dump_dwarf_info (abfd, dump_global_variable_info, is_mainfile);
 }
 
 /* Read ABFD's section SECT_NAME into *CONTENTS, and return a pointer to
@@ -5829,6 +5887,8 @@  dump_bfd (bfd *abfd, bool is_mainfile)
       if (dump_dynamic_symtab)
 	dump_symbols (abfd, true);
     }
+  if (dump_global_vars)
+    dump_global_vars_info (abfd, is_mainfile);
   if (dump_dwarf_section_info)
     dump_dwarf (abfd, is_mainfile);
   if (is_mainfile || process_links)
@@ -6284,6 +6344,11 @@  main (int argc, char **argv)
 	  process_links = true;
 	  do_follow_links = true;
 	  break;
+	case OPTION_MAP_GLOBAL_VARS:
+	  dump_global_vars = true;
+	  dwarf_select_sections_all ();
+	  seenflag = true;
+	  break;
 	case 'W':
 	  seenflag = true;
 	  if (optarg)
@@ -6423,6 +6488,7 @@  main (int argc, char **argv)
 
   dump_any_debugging = (dump_debugging
 			|| dump_dwarf_section_info
+			|| dump_global_vars
 			|| process_links
 			|| with_source_code);