From bcd213b2cfbca2df53fb7e5d187fd67ea8eb7185 Mon Sep 17 00:00:00 2001
From: Nick Clifton <nickc@redhat.com>
Date: Tue, 27 Oct 2020 16:17:13 +0000
Subject: [PATCH] Fix the decoding of DW_FORM_ref_addr DWARF attribute.

	* dwarf.c (struct abbrev_list): New structure.  Used to collect
	lists of abbreviation sets.
	(struct abbrev_map): New structure.  Used to map CU offsets to
	abbreviation offsets.
	(record_abbrev_list): New function.  A new entry to an
	abbreviation list.
	(free_all_abbrevs): Update to free abbreviation lists.
	(new_abbrev_list): New function.  Start a new abbreviation
	list.
	(find_abbrev_list_by_abbrev_offset): New function.
	(find_abbrev_map_by_offset): New function.
	(add_abbrev): Add abbrev_list parameter.
	(add_abbrev_attr): Likewise.
	(process_abbrev_section): Rename to process_abbrev_set and add
	list parameter.
	(get_type_abbrev_from_form): New function.  Attempts to decode the
	forms used by DW_AT_type attributes.
	(get_type_signedness): Display type names if operating in wide
	mode.  Use get_type_abbrev_from_form.
	(read_and_display_attr_value): Use get_type_abbrev_from_form.
	(process_debug_info): Pre-parse the CU headers to collate all the
	abbrevs before starting the main scan.
	(process_debug_abbrev): Do not free any loaded abbrevs.
	(free_debug_memory): Free the abbrev maps.
---
 binutils/dwarf.c   | 585 ++++++++++++++++++++++++++++++++++-----------
 1 files changed, 447 insertions(+), 138 deletions(-)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index c842aba6c1e7..5cfa528594ff 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -849,101 +849,204 @@ fetch_indexed_value (dwarf_vma offset, dwarf_vma bytes)
 /* FIXME:  There are better and more efficient ways to handle
    these structures.  For now though, I just want something that
    is simple to implement.  */
+/* Records a single attribute in an abbrev.  */
 typedef struct abbrev_attr
 {
-  unsigned long attribute;
-  unsigned long form;
-  bfd_signed_vma implicit_const;
-  struct abbrev_attr *next;
+  unsigned long          attribute;
+  unsigned long          form;
+  bfd_signed_vma         implicit_const;
+  struct abbrev_attr *   next;
 }
 abbrev_attr;
 
+/* Records a single abbrev.  */
 typedef struct abbrev_entry
 {
-  unsigned long entry;
-  unsigned long tag;
-  int children;
-  struct abbrev_attr *first_attr;
-  struct abbrev_attr *last_attr;
-  struct abbrev_entry *next;
+  unsigned long          number;
+  unsigned long          tag;
+  int                    children;
+  struct abbrev_attr *   first_attr;
+  struct abbrev_attr *   last_attr;
+  struct abbrev_entry *  next;
 }
 abbrev_entry;
 
-static abbrev_entry *first_abbrev = NULL;
-static abbrev_entry *last_abbrev = NULL;
+/* Records a set of abbreviations.  */
+typedef struct abbrev_list
+{
+  abbrev_entry *        first_abbrev;
+  abbrev_entry *        last_abbrev;
+  dwarf_vma             abbrev_offset;
+  struct abbrev_list *  next;
+  unsigned char *       start_of_next_abbrevs;
+}
+abbrev_list;
+
+/* Records all the abbrevs found so far.  */
+static struct abbrev_list * abbrev_lists = NULL;
+
+typedef struct abbrev_map
+{
+  dwarf_vma      start;
+  dwarf_vma      end;
+  abbrev_list *  list;
+} abbrev_map;
+
+/* Maps between CU offsets and abbrev sets.  */
+static abbrev_map *   cu_abbrev_map = NULL;
+static unsigned long  num_abbrev_map_entries = 0;
+static unsigned long  next_free_abbrev_map_entry = 0;
+
+#define INITIAL_NUM_ABBREV_MAP_ENTRIES 8
+#define ABBREV_MAP_ENTRIES_INCREMENT   8
+
+static void
+record_abbrev_list_for_cu (dwarf_vma start, dwarf_vma end, abbrev_list * list)
+{
+  if (cu_abbrev_map == NULL)
+    {
+      num_abbrev_map_entries = INITIAL_NUM_ABBREV_MAP_ENTRIES;
+      cu_abbrev_map = xmalloc (num_abbrev_map_entries * sizeof (* cu_abbrev_map));
+    }
+  else if (next_free_abbrev_map_entry == num_abbrev_map_entries)
+    {
+      num_abbrev_map_entries += ABBREV_MAP_ENTRIES_INCREMENT;
+      cu_abbrev_map = xrealloc (cu_abbrev_map, num_abbrev_map_entries * sizeof (* cu_abbrev_map));
+    }
+
+  cu_abbrev_map[next_free_abbrev_map_entry].start = start;
+  cu_abbrev_map[next_free_abbrev_map_entry].end = end;
+  cu_abbrev_map[next_free_abbrev_map_entry].list = list;
+  next_free_abbrev_map_entry ++;
+}
 
 static void
-free_abbrevs (void)
+free_all_abbrevs (void)
 {
-  abbrev_entry *abbrv;
+  abbrev_list *  list;
 
-  for (abbrv = first_abbrev; abbrv;)
+  for (list = abbrev_lists; list != NULL;)
     {
-      abbrev_entry *next_abbrev = abbrv->next;
-      abbrev_attr *attr;
+      abbrev_list *   next = list->next;
+      abbrev_entry *  abbrv;
 
-      for (attr = abbrv->first_attr; attr;)
+      for (abbrv = list->first_abbrev; abbrv != NULL;)
 	{
-	  abbrev_attr *next_attr = attr->next;
+	  abbrev_entry *  next_abbrev = abbrv->next;
+	  abbrev_attr *   attr;
+
+	  for (attr = abbrv->first_attr; attr;)
+	    {
+	      abbrev_attr *next_attr = attr->next;
 
-	  free (attr);
-	  attr = next_attr;
+	      free (attr);
+	      attr = next_attr;
+	    }
+
+	  free (abbrv);
+	  abbrv = next_abbrev;
 	}
 
-      free (abbrv);
-      abbrv = next_abbrev;
+      free (list);
+      list = next;
     }
 
-  last_abbrev = first_abbrev = NULL;
+  abbrev_lists = NULL;
+}
+
+static abbrev_list *
+new_abbrev_list (dwarf_vma abbrev_offset)
+{
+  abbrev_list * list = (abbrev_list *) xcalloc (sizeof * list, 1);
+
+  list->abbrev_offset = abbrev_offset;
+
+  list->next = abbrev_lists;
+  abbrev_lists = list;
+
+  return list;
+}
+
+static abbrev_list *
+find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_offset)
+{
+  abbrev_list * list;
+
+  for (list = abbrev_lists; list != NULL; list = list->next)
+    if (list->abbrev_offset == abbrev_offset)
+      return list;
+
+  return NULL;
+}
+
+/* Find the abbreviation map for the CU that includes OFFSET.
+   OFFSET is an absolute offset from the start of the .debug_info section.  */
+/* FIXME: This function is going to slow down readelf & objdump.
+   Consider using a better algorithm to mitigate this effect.  */
+
+static  abbrev_map *
+find_abbrev_map_by_offset (dwarf_vma offset)
+{
+  unsigned long i;
+
+  for (i = 0; i < next_free_abbrev_map_entry; i++)
+    if (cu_abbrev_map[i].start <= offset
+	&& cu_abbrev_map[i].end > offset)
+      return cu_abbrev_map + i;
+
+  return NULL;	
 }
 
 static void
-add_abbrev (unsigned long number, unsigned long tag, int children)
+add_abbrev (unsigned long  number,
+	    unsigned long  tag,
+	    int            children,
+	    abbrev_list *  list)
 {
-  abbrev_entry *entry;
+  abbrev_entry *  entry;
 
-  entry = (abbrev_entry *) malloc (sizeof (*entry));
-  if (entry == NULL)
-    /* ugg */
-    return;
+  entry = (abbrev_entry *) xmalloc (sizeof (*entry));
 
-  entry->entry      = number;
+  entry->number     = number;
   entry->tag        = tag;
   entry->children   = children;
   entry->first_attr = NULL;
   entry->last_attr  = NULL;
   entry->next       = NULL;
 
-  if (first_abbrev == NULL)
-    first_abbrev = entry;
+  assert (list != NULL);
+
+  if (list->first_abbrev == NULL)
+    list->first_abbrev = entry;
   else
-    last_abbrev->next = entry;
+    list->last_abbrev->next = entry;
 
-  last_abbrev = entry;
+  list->last_abbrev = entry;
 }
 
 static void
-add_abbrev_attr (unsigned long attribute, unsigned long form,
-		 bfd_signed_vma implicit_const)
+add_abbrev_attr (unsigned long   attribute,
+		 unsigned long   form,
+		 bfd_signed_vma  implicit_const,
+		 abbrev_list *   list)
 {
   abbrev_attr *attr;
 
-  attr = (abbrev_attr *) malloc (sizeof (*attr));
-  if (attr == NULL)
-    /* ugg */
-    return;
+  attr = (abbrev_attr *) xmalloc (sizeof (*attr));
 
   attr->attribute = attribute;
   attr->form      = form;
   attr->implicit_const = implicit_const;
   attr->next      = NULL;
 
-  if (last_abbrev->first_attr == NULL)
-    last_abbrev->first_attr = attr;
+  assert (list != NULL && list->last_abbrev != NULL);
+
+  if (list->last_abbrev->first_attr == NULL)
+    list->last_abbrev->first_attr = attr;
   else
-    last_abbrev->last_attr->next = attr;
+    list->last_abbrev->last_attr->next = attr;
 
-  last_abbrev->last_attr = attr;
+  list->last_abbrev->last_attr = attr;
 }
 
 /* Processes the (partial) contents of a .debug_abbrev section.
@@ -952,11 +1055,10 @@ add_abbrev_attr (unsigned long attribute, unsigned long form,
    an abbreviation set was found.  */
 
 static unsigned char *
-process_abbrev_section (unsigned char *start, unsigned char *end)
+process_abbrev_set (unsigned char *        start,
+		    const unsigned char *  end,
+		    abbrev_list *          list)
 {
-  if (first_abbrev != NULL)
-    return NULL;
-
   while (start < end)
     {
       unsigned long entry;
@@ -966,7 +1068,7 @@ process_abbrev_section (unsigned char *start, unsigned char *end)
 
       READ_ULEB (entry, start, end);
 
-      /* A single zero is supposed to end the section according
+      /* A single zero is supposed to end the set according
 	 to the standard.  If there's more, then signal that to
 	 the caller.  */
       if (start == end)
@@ -980,7 +1082,7 @@ process_abbrev_section (unsigned char *start, unsigned char *end)
 
       children = *start++;
 
-      add_abbrev (entry, tag, children);
+      add_abbrev (entry, tag, children, list);
 
       do
 	{
@@ -1003,7 +1105,7 @@ process_abbrev_section (unsigned char *start, unsigned char *end)
 		break;
 	    }
 
-	  add_abbrev_attr (attribute, form, implicit_const);
+	  add_abbrev_attr (attribute, form, implicit_const, list);
 	}
       while (attribute != 0);
     }
@@ -1969,36 +2071,123 @@ skip_attr_bytes (unsigned long          form,
   return data;
 }
 
-/* Return IS_SIGNED set to TRUE if the type at
-   DATA can be determined to be a signed type.  */
+/* Given form FORM with value UVALUE, locate and return the abbreviation
+   associated with it.  */
+
+static abbrev_entry *
+get_type_abbrev_from_form (unsigned long                 form,
+			   unsigned long                 uvalue,
+			   dwarf_vma                     cu_offset,
+			   const struct dwarf_section *  section,
+			   unsigned long *               abbrev_num_return,
+			   unsigned char **              data_return,
+			   unsigned long *               cu_offset_return)
+{
+  unsigned long   abbrev_number;
+  abbrev_map *    map;
+  abbrev_entry *  entry;
+  unsigned char * data;
+
+  if (abbrev_num_return != NULL)
+    * abbrev_num_return = 0;
+  if (data_return != NULL)
+    * data_return = NULL;
+
+  switch (form)
+    {
+    case DW_FORM_GNU_ref_alt:
+      /* FIXME: We are unable to handle this form at the moment.  */
+      return NULL;
+
+    case DW_FORM_ref_addr:
+      if (uvalue >= section->size)
+	{
+	  warn (_("Unable to resolve ref_addr form: uvalue %lx > section size %lx (%s)\n"),
+		uvalue, (long) section->size, section->name);
+	  return NULL;
+	}
+      break;
+
+    case DW_FORM_ref1:
+    case DW_FORM_ref2:
+    case DW_FORM_ref4:
+    case DW_FORM_ref_udata:
+      if (uvalue + cu_offset > section->size)
+	{
+	  warn (_("Unable to resolve ref form: uvalue %lx + cu_offset %lx > section size %lx\n"),
+		uvalue, (long) cu_offset, (long) section->size);
+	  return NULL;
+	}
+      uvalue += cu_offset;
+      break;
+
+      /* FIXME: Are there other DW_FORMs that can be used by types ?  */
+
+    default:
+      warn (_("Unexpected form %lx encountered whilst finding abbreviation for type\n"), form);
+      return NULL;
+    }
+
+  data = (unsigned char *) section->start + uvalue;
+  map = find_abbrev_map_by_offset (uvalue);
+  
+  if (map == NULL)
+    {
+      warn (_("Unable to find abbreviations for CU offset %#lx\n"), uvalue);
+      return NULL;
+    }
+  if (map->list == NULL)
+    {
+      warn (_("Empty abbreviation list encountered for CU offset %lx\n"), uvalue);
+      return NULL;
+    }
+
+  if (cu_offset_return != NULL)
+    {
+      if (form == DW_FORM_ref_addr)
+	* cu_offset_return = map->start;
+      else
+	* cu_offset_return = cu_offset;
+    }
+	
+  READ_ULEB (abbrev_number, data, section->start + section->size);
+
+  for (entry = map->list->first_abbrev; entry != NULL; entry = entry->next)
+    if (entry->number == abbrev_number)
+      break;
+  
+  if (abbrev_num_return != NULL)
+    * abbrev_num_return = abbrev_number;
+
+  if (data_return != NULL)
+    * data_return = data;
+
+  if (entry == NULL)
+    warn (_("Unable to find entry for abbreviation %lu\n"), abbrev_number);
+
+  return entry;
+}
+
+/* Return IS_SIGNED set to TRUE if the type using abbreviation ENTRY
+   can be determined to be a signed type.  The data for ENTRY can be
+   found starting at DATA.  */
 
 static void
-get_type_signedness (unsigned char *        start,
+get_type_signedness (abbrev_entry *         entry,
+		     const struct dwarf_section * section,
 		     unsigned char *        data,
 		     unsigned const char *  end,
+		     dwarf_vma              cu_offset,
 		     dwarf_vma              pointer_size,
 		     dwarf_vma              offset_size,
 		     int                    dwarf_version,
 		     bfd_boolean *          is_signed,
 		     unsigned int	    nesting)
 {
-  unsigned long   abbrev_number;
-  abbrev_entry *  entry;
   abbrev_attr *   attr;
 
   * is_signed = FALSE;
 
-  READ_ULEB (abbrev_number, data, end);
-
-  for (entry = first_abbrev;
-       entry != NULL && entry->entry != abbrev_number;
-       entry = entry->next)
-    continue;
-
-  if (entry == NULL)
-    /* FIXME: Issue a warning ?  */
-    return;
-
 #define MAX_NESTING 20
   if (nesting > MAX_NESTING)
     {
@@ -2011,6 +2200,7 @@ get_type_signedness (unsigned char *        start,
        attr != NULL && attr->attribute;
        attr = attr->next)
     {
+      unsigned char * orig_data = data;
       dwarf_vma uvalue = 0;
 
       data = skip_attr_bytes (attr->form, data, end, pointer_size,
@@ -2020,21 +2210,38 @@ get_type_signedness (unsigned char *        start,
 
       switch (attr->attribute)
 	{
-#if 0 /* FIXME: It would be nice to print the name of the type,
-	 but this would mean updating a lot of binutils tests.  */
+	case DW_AT_linkage_name:
 	case DW_AT_name:
-	  if (attr->form == DW_FORM_strp)
-	    printf ("%s", fetch_indirect_string (uvalue));
+	  if (do_wide)
+	    {
+	      if (attr->form == DW_FORM_strp)
+		printf (", %s", fetch_indirect_string (uvalue));
+	      else if (attr->form == DW_FORM_string)
+		printf (", %s", orig_data);
+	    }
 	  break;
-#endif
+
 	case DW_AT_type:
 	  /* Recurse.  */
-	  if (uvalue >= (size_t) (end - start))
-	    return;
-	  /* We cannot correctly process DW_FORM_ref_addr at the moment.  */
-	  if (attr->form != DW_FORM_ref_addr)
-	    get_type_signedness (start, start + uvalue, end, pointer_size,
-				 offset_size, dwarf_version, is_signed, nesting + 1);
+	  {
+	    abbrev_entry *  type_abbrev;
+	    unsigned char * type_data;
+	    unsigned long   type_cu_offset;
+
+	    type_abbrev = get_type_abbrev_from_form (attr->form,
+						     uvalue,
+						     cu_offset,
+						     section,
+						     NULL /* abbrev num return */,
+						     & type_data,
+						     & type_cu_offset);
+	    if (type_abbrev == NULL)
+	      break;
+
+	    get_type_signedness (type_abbrev, section, type_data, end, type_cu_offset,
+				 pointer_size, offset_size, dwarf_version,
+				 is_signed, nesting + 1);
+	  }
 	  break;
 
 	case DW_AT_encoding:
@@ -2276,12 +2483,12 @@ read_and_display_attr_value (unsigned long           attribute,
     {
     case DW_FORM_ref_addr:
       if (!do_loc)
-	printf ("%c<0x%s>", delimiter, dwarf_vmatoa ("x",uvalue));
+	printf ("%c<0x%s>", delimiter, dwarf_vmatoa ("x", uvalue));
       break;
 
     case DW_FORM_GNU_ref_alt:
       if (!do_loc)
-	printf ("%c<alt 0x%s>", delimiter, dwarf_vmatoa ("x",uvalue));
+	printf ("%c<alt 0x%s>", delimiter, dwarf_vmatoa ("x", uvalue));
       /* FIXME: Follow the reference...  */
       break;
 
@@ -2667,11 +2874,18 @@ read_and_display_attr_value (unsigned long           attribute,
 	  && uvalue < (size_t) (end - start))
 	{
 	  bfd_boolean is_signed = FALSE;
-
-	  /* We cannot correctly process DW_FORM_ref_addr at the moment.  */
-	  if (form != DW_FORM_ref_addr)
-	    get_type_signedness (start, start + uvalue, end, pointer_size,
-				 offset_size, dwarf_version, & is_signed, 0);
+	  abbrev_entry *  type_abbrev;
+	  unsigned char * type_data;
+	  unsigned long   type_cu_offset;
+	  
+	  type_abbrev = get_type_abbrev_from_form (form, uvalue, cu_offset,
+						   section, NULL, & type_data, & type_cu_offset);
+	  if (type_abbrev != NULL)
+	    {
+	      get_type_signedness (type_abbrev, section, type_data, end, type_cu_offset,
+				   pointer_size, offset_size, dwarf_version,
+				   & is_signed, 0);
+	    }
 	  level_type_signed[level] = is_signed;
 	}
       break;
@@ -2993,40 +3207,22 @@ read_and_display_attr_value (unsigned long           attribute,
 
     case DW_AT_import:
       {
-	if (form == DW_FORM_ref_sig8
-	    || form == DW_FORM_GNU_ref_alt)
-	  break;
+	unsigned long abbrev_number;
+	abbrev_entry *entry;
 
-	if (form == DW_FORM_ref1
-	    || form == DW_FORM_ref2
-	    || form == DW_FORM_ref4
-	    || form == DW_FORM_ref_udata)
-	  uvalue += cu_offset;
-
-	if (uvalue >= section->size)
-	  warn (_("Offset %s used as value for DW_AT_import attribute of DIE at offset 0x%lx is too big.\n"),
-		dwarf_vmatoa ("x", uvalue),
-		(unsigned long) (orig_data - section->start));
+	entry = get_type_abbrev_from_form (form, uvalue, cu_offset,
+					   section, & abbrev_number, NULL, NULL);
+	if (entry == NULL)
+	  {
+	    if (form != DW_FORM_GNU_ref_alt)
+	      warn (_("Offset %s used as value for DW_AT_import attribute of DIE at offset 0x%lx is too big.\n"),
+		    dwarf_vmatoa ("x", uvalue),
+		    (unsigned long) (orig_data - section->start));
+	  }
 	else
 	  {
-	    unsigned long abbrev_number;
-	    abbrev_entry *entry;
-	    unsigned char *p = section->start + uvalue;
-
-	    READ_ULEB (abbrev_number, p, end);
-
 	    printf (_("\t[Abbrev Number: %ld"), abbrev_number);
-	    /* Don't look up abbrev for DW_FORM_ref_addr, as it very often will
-	       use different abbrev table, and we don't track .debug_info chunks
-	       yet.  */
-	    if (form != DW_FORM_ref_addr)
-	      {
-		for (entry = first_abbrev; entry != NULL; entry = entry->next)
-		  if (entry->entry == abbrev_number)
-		    break;
-		if (entry != NULL)
-		  printf (" (%s)", get_TAG_name (entry->tag));
-	      }
+	    printf (" (%s)", get_TAG_name (entry->tag));
 	    printf ("]");
 	  }
       }
@@ -3245,8 +3441,98 @@ process_debug_info (struct dwarf_section *           section,
 
   if (!do_loc && dwarf_start_die == 0)
     introduce (section, FALSE);
+
+  free_all_abbrevs ();
+  free (cu_abbrev_map);
+  cu_abbrev_map = NULL;
+  next_free_abbrev_map_entry = 0;
   
-  for (section_begin = start, unit = 0; start < end; unit++)
+  /* In order to be able to resolve DW_FORM_ref_attr forms we need
+     to load *all* of the abbrevs for all CUs in this .debug_info
+     section.  This does effectively mean that we (partially) read
+     every CU header twice.  */
+  for (section_begin = start; start < end;)
+    {
+      DWARF2_Internal_CompUnit  compunit;
+      unsigned char *           hdrptr;
+      dwarf_vma                 cu_offset;
+      unsigned int              offset_size;
+      unsigned int              initial_length_size;
+      struct cu_tu_set *        this_set;
+      abbrev_list *             list;
+
+      hdrptr = start;
+
+      SAFE_BYTE_GET_AND_INC (compunit.cu_length, hdrptr, 4, end);
+
+      if (compunit.cu_length == 0xffffffff)
+	{
+	  SAFE_BYTE_GET_AND_INC (compunit.cu_length, hdrptr, 8, end);
+	  offset_size = 8;
+	  initial_length_size = 12;
+	}
+      else
+	{
+	  offset_size = 4;
+	  initial_length_size = 4;
+	}
+
+      SAFE_BYTE_GET_AND_INC (compunit.cu_version, hdrptr, 2, end);
+
+      cu_offset = start - section_begin;
+
+      this_set = find_cu_tu_set_v2 (cu_offset, do_types);
+
+      if (compunit.cu_version < 5)
+	{
+	  compunit.cu_unit_type = DW_UT_compile;
+	  /* Initialize it due to a false compiler warning.  */
+	  compunit.cu_pointer_size = -1;
+	}
+      else
+	{
+	  SAFE_BYTE_GET_AND_INC (compunit.cu_unit_type, hdrptr, 1, end);
+	  do_types = (compunit.cu_unit_type == DW_UT_type);
+
+	  SAFE_BYTE_GET_AND_INC (compunit.cu_pointer_size, hdrptr, 1, end);
+	}
+
+      SAFE_BYTE_GET_AND_INC (compunit.cu_abbrev_offset, hdrptr, offset_size, end);
+
+      list = find_abbrev_list_by_abbrev_offset (compunit.cu_abbrev_offset);
+      if (list == NULL)
+	{
+	  dwarf_vma        abbrev_base;
+	  size_t           abbrev_size;
+	  unsigned char *  next;
+
+	  if (this_set == NULL)
+	    {
+	      abbrev_base = 0;
+	      abbrev_size = debug_displays [abbrev_sec].section.size;
+	    }
+	  else
+	    {
+	      abbrev_base = this_set->section_offsets [DW_SECT_ABBREV];
+	      abbrev_size = this_set->section_sizes [DW_SECT_ABBREV];
+	    }
+
+	  list = new_abbrev_list (compunit.cu_abbrev_offset);
+	  next = process_abbrev_set
+	    (((unsigned char *) debug_displays [abbrev_sec].section.start
+	      + abbrev_base + compunit.cu_abbrev_offset),
+	     ((unsigned char *) debug_displays [abbrev_sec].section.start
+	      + abbrev_base + abbrev_size),
+	     list);
+	  list->start_of_next_abbrevs = next;
+	}
+		       
+      start = section_begin + cu_offset + compunit.cu_length
+	+ initial_length_size;
+      record_abbrev_list_for_cu (cu_offset, start - section_begin, list);
+    }
+
+  for (start = section_begin, unit = 0; start < end; unit++)
     {
       DWARF2_Internal_CompUnit compunit;
       unsigned char *hdrptr;
@@ -3262,6 +3548,7 @@ process_debug_info (struct dwarf_section *           section,
       struct cu_tu_set *this_set;
       dwarf_vma abbrev_base;
       size_t abbrev_size;
+      abbrev_list * list = NULL;
 
       hdrptr = start;
 
@@ -3439,8 +3726,6 @@ process_debug_info (struct dwarf_section *           section,
 	  continue;
 	}
 
-      free_abbrevs ();
-
       /* Process the abbrevs used by this compilation unit.  */
       if (compunit.cu_abbrev_offset >= abbrev_size)
 	warn (_("Debug info is corrupted, abbrev offset (%lx) is larger than abbrev section size (%lx)\n"),
@@ -3453,11 +3738,22 @@ process_debug_info (struct dwarf_section *           section,
 	      (unsigned long) abbrev_base + abbrev_size,
 	      (unsigned long) debug_displays [abbrev_sec].section.size);
       else
-	process_abbrev_section
-	  (((unsigned char *) debug_displays [abbrev_sec].section.start
-	    + abbrev_base + compunit.cu_abbrev_offset),
-	   ((unsigned char *) debug_displays [abbrev_sec].section.start
-	    + abbrev_base + abbrev_size));
+	{
+	  list = find_abbrev_list_by_abbrev_offset (compunit.cu_abbrev_offset);
+	  if (list == NULL)
+	    {
+	      unsigned char * next;
+
+	      list = new_abbrev_list (compunit.cu_abbrev_offset);
+	      next = process_abbrev_set
+		(((unsigned char *) debug_displays [abbrev_sec].section.start
+		  + abbrev_base + compunit.cu_abbrev_offset),
+		 ((unsigned char *) debug_displays [abbrev_sec].section.start
+		  + abbrev_base + abbrev_size),
+		 list);
+	      list->start_of_next_abbrevs = next;
+	    }
+	}
 
       level = 0;
       last_level = level;
@@ -3537,11 +3833,13 @@ process_debug_info (struct dwarf_section *           section,
 
 	  /* Scan through the abbreviation list until we reach the
 	     correct entry.  */
-	  for (entry = first_abbrev;
-	       entry && entry->entry != abbrev_number;
-	       entry = entry->next)
+	  if (list == NULL)
 	    continue;
 
+	  for (entry = list->first_abbrev; entry != NULL; entry = entry->next)
+	    if (entry->number == abbrev_number)
+	      break;
+
 	  if (entry == NULL)
 	    {
 	      if (!do_loc && do_printing)
@@ -5763,30 +6061,37 @@ display_debug_abbrev (struct dwarf_section *section,
 {
   abbrev_entry *entry;
   unsigned char *start = section->start;
-  unsigned char *end = start + section->size;
+  const unsigned char *end = start + section->size;
 
   introduce (section, FALSE);
 
   do
     {
-      unsigned char *last;
+      abbrev_list *    list;
+      dwarf_vma        offset;
 
-      free_abbrevs ();
-
-      last = start;
-      start = process_abbrev_section (start, end);
+      offset = start - section->start;
+      list = find_abbrev_list_by_abbrev_offset (offset);
+      if (list == NULL)
+	{
+	  list = new_abbrev_list (offset);
+	  start = process_abbrev_set (start, end, list);
+	  list->start_of_next_abbrevs = start;
+	}
+      else
+	start = list->start_of_next_abbrevs;
 
-      if (first_abbrev == NULL)
+      if (list->first_abbrev == NULL)
 	continue;
 
-      printf (_("  Number TAG (0x%lx)\n"), (long) (last - section->start));
+      printf (_("  Number TAG (0x%lx)\n"), (long) offset);
 
-      for (entry = first_abbrev; entry; entry = entry->next)
+      for (entry = list->first_abbrev; entry; entry = entry->next)
 	{
 	  abbrev_attr *attr;
 
 	  printf ("   %ld      %s    [%s]\n",
-		  entry->entry,
+		  entry->number,
 		  get_TAG_name (entry->tag),
 		  entry->children ? _("has children") : _("no children"));
 
@@ -10828,8 +11133,12 @@ free_debug_memory (void)
 {
   unsigned int i;
 
-  free_abbrevs ();
+  free_all_abbrevs ();
 
+  free (cu_abbrev_map);
+  cu_abbrev_map = NULL;
+  next_free_abbrev_map_entry = 0;
+  
   for (i = 0; i < max; i++)
     free_debug_section ((enum dwarf_section_display_enum) i);
 
