From 76868f3606fb9de04f49c441c1e3cdd3e943a34d Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Thu, 29 Oct 2020 09:19:25 -0700
Subject: [PATCH] dwarf: Also match abbrev base when searching abbrev list

A .debug_abbrev section can have multiple CUs.  When caching abbrev list,
we need to check abbrev base to support multiple CUs.

	PR binutils/26808
	* dwarf.c (abbrev_list): Add abbrev_base.
	(new_abbrev_list): Add an abbrev_base argument and record it.
	(find_abbrev_list_by_abbrev_offset): Add an abbrev_base argument
	and match it.
	(process_debug_info): Pass abbrev_base to new_abbrev_list and
	find_abbrev_list_by_abbrev_offset.
	(display_debug_abbrev): Pass 0 abbrev_base to new_abbrev_list
	and find_abbrev_list_by_abbrev_offset.
	* testsuite/binutils-all/x86-64/pr26808.dump: New file.
	* testsuite/binutils-all/x86-64/pr26808.dwp.bz2: Likewise.
	* testsuite/binutils-all/x86-64/x86-64.exp: Run PR binutils/26808
	test.
---
 binutils/dwarf.c                              |   52 +-
 .../testsuite/binutils-all/x86-64/x86-64.exp  |   31 +
 2 files changed, 61 insertions(+), 22 deletions(-)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 5cfa528594ff..14a7791c8cc4 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -876,6 +876,7 @@ typedef struct abbrev_list
 {
   abbrev_entry *        first_abbrev;
   abbrev_entry *        last_abbrev;
+  dwarf_vma             abbrev_base;
   dwarf_vma             abbrev_offset;
   struct abbrev_list *  next;
   unsigned char *       start_of_next_abbrevs;
@@ -955,10 +956,11 @@ free_all_abbrevs (void)
 }
 
 static abbrev_list *
-new_abbrev_list (dwarf_vma abbrev_offset)
+new_abbrev_list (dwarf_vma abbrev_base, dwarf_vma abbrev_offset)
 {
   abbrev_list * list = (abbrev_list *) xcalloc (sizeof * list, 1);
 
+  list->abbrev_base = abbrev_base;
   list->abbrev_offset = abbrev_offset;
 
   list->next = abbrev_lists;
@@ -968,12 +970,14 @@ new_abbrev_list (dwarf_vma abbrev_offset)
 }
 
 static abbrev_list *
-find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_offset)
+find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base,
+				   dwarf_vma abbrev_offset)
 {
   abbrev_list * list;
 
   for (list = abbrev_lists; list != NULL; list = list->next)
-    if (list->abbrev_offset == abbrev_offset)
+    if (list->abbrev_base == abbrev_base
+	&& list->abbrev_offset == abbrev_offset)
       return list;
 
   return NULL;
@@ -3455,6 +3459,8 @@ process_debug_info (struct dwarf_section *           section,
     {
       DWARF2_Internal_CompUnit  compunit;
       unsigned char *           hdrptr;
+      dwarf_vma                 abbrev_base;
+      size_t                    abbrev_size;
       dwarf_vma                 cu_offset;
       unsigned int              offset_size;
       unsigned int              initial_length_size;
@@ -3499,25 +3505,25 @@ process_debug_info (struct dwarf_section *           section,
 
       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 (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 = find_abbrev_list_by_abbrev_offset (abbrev_base,
+						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);
+	  list = new_abbrev_list (abbrev_base,
+				  compunit.cu_abbrev_offset);
 	  next = process_abbrev_set
 	    (((unsigned char *) debug_displays [abbrev_sec].section.start
 	      + abbrev_base + compunit.cu_abbrev_offset),
@@ -3739,12 +3745,14 @@ process_debug_info (struct dwarf_section *           section,
 	      (unsigned long) debug_displays [abbrev_sec].section.size);
       else
 	{
-	  list = find_abbrev_list_by_abbrev_offset (compunit.cu_abbrev_offset);
+	  list = find_abbrev_list_by_abbrev_offset (abbrev_base,
+						    compunit.cu_abbrev_offset);
 	  if (list == NULL)
 	    {
 	      unsigned char * next;
 
-	      list = new_abbrev_list (compunit.cu_abbrev_offset);
+	      list = new_abbrev_list (abbrev_base,
+				      compunit.cu_abbrev_offset);
 	      next = process_abbrev_set
 		(((unsigned char *) debug_displays [abbrev_sec].section.start
 		  + abbrev_base + compunit.cu_abbrev_offset),
@@ -6071,10 +6079,10 @@ display_debug_abbrev (struct dwarf_section *section,
       dwarf_vma        offset;
 
       offset = start - section->start;
-      list = find_abbrev_list_by_abbrev_offset (offset);
+      list = find_abbrev_list_by_abbrev_offset (0, offset);
       if (list == NULL)
 	{
-	  list = new_abbrev_list (offset);
+	  list = new_abbrev_list (0, offset);
 	  start = process_abbrev_set (start, end, list);
 	  list->start_of_next_abbrevs = start;
 	}
diff --git a/binutils/testsuite/binutils-all/x86-64/x86-64.exp b/binutils/testsuite/binutils-all/x86-64/x86-64.exp
index 7bd49ab619df..5f02ed35ade7 100644
--- a/binutils/testsuite/binutils-all/x86-64/x86-64.exp
+++ b/binutils/testsuite/binutils-all/x86-64/x86-64.exp
@@ -27,3 +27,34 @@ foreach t $test_list {
     verbose [file rootname $t]
     run_dump_test [file rootname $t]
 }
+
+set t $srcdir/$subdir/pr26808.dwp.bz2
+# We need to strip the ".bz2", but can leave the dirname.
+set test $subdir/[file tail $t]
+set testname [file rootname $test]
+verbose $testname
+if {[catch "system \"bzip2 -dc $t > $tempfile\""] != 0} {
+  untested "bzip2 -dc ($testname)"
+} else {
+    send_log "$READELF -wi $tempfile > tmpdir/pr26808.out 2> /dev/null\n"
+    verbose "$READELF -wi $tempfile > tmpdir/pr26808.out 2> /dev/null" 1
+    set got [catch "system \"$READELF -wi $tempfile > tmpdir/pr26808.out 2> /dev/null\""]
+
+    if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]]} then {
+	fail $testname
+    } else {
+	send_log "cmp tmpdir/pr26808.out $srcdir/$subdir/pr26808.dump\n"
+	verbose "cmp tmpdir/pr26808.out $srcdir/$subdir/pr26808.dump" 1
+	set status [remote_exec build cmp "tmpdir/pr26808.out $srcdir/$subdir/pr26808.dump"]
+	set exec_output [lindex $status 1]
+	set exec_output [prune_warnings $exec_output]
+
+	if [string match "" $exec_output] then {
+	    pass "readelf -wi ($testname)"
+	} else {
+	    send_log "$exec_output\n"
+	    verbose "$exec_output" 1
+	    fail "readelf -wi ($testname)"
+	}
+    }
+}
