libdw: dwarf_elf_begin should use either plain, dwo or lto DWARF sections.
authorMark Wielaard <mark@klomp.org>
Mon, 8 Nov 2021 08:27:51 +0000 (09:27 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 9 Nov 2021 17:28:00 +0000 (18:28 +0100)
When opening an ELF file that contained a mix of plain, dwo or lto .debug
sections the result could be confusing. Add a check to pick just the plain
.debug sections, or the .dwo sections or the .gnu.debuglto_.debug sections
(in that order of preference). That way there is always a consistent set.

https://sourceware.org/bugzilla/show_bug.cgi?id=27367

Signed-off-by: Mark Wielaard <mark@klomp.org>
libdw/ChangeLog
libdw/dwarf_begin_elf.c
libdw/libdwP.h
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-fat-lto.sh [new file with mode: 0755]
tests/testfile-dwarf5-fat-lto.o.bz2 [new file with mode: 0644]

index b383683..38e6efb 100644 (file)
@@ -1,3 +1,12 @@
+2021-11-08  Mark Wielaard  <mark@klomp.org>
+
+       * dwarf_begin_elf.c (scn_dwarf_type): New function.
+       (check_section): Check result->type.
+       (global_read): First check type.
+       (scngrp_read): Likewise.
+       * libdw/libdwP.h (enum dwarf_type): New enumeration.
+       (struct Dwarf): New field type.
+
 2021-02-14  Alexander Miller  <alex.miller@gmx.de>
 
        * dwarf_aggregate_size.c (dwarf_aggregate_size): Move NEW_VERSION
index 53b44cd..a48dada 100644 (file)
@@ -72,6 +72,31 @@ static const char dwarf_scnnames[IDX_last][19] =
 };
 #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0]))
 
+static enum dwarf_type
+scn_dwarf_type (Dwarf *result, size_t shstrndx, Elf_Scn *scn)
+{
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+  if (shdr == NULL)
+    return TYPE_UNKNOWN;
+
+  const char *scnname = elf_strptr (result->elf, shstrndx,
+                                   shdr->sh_name);
+  if (scnname != NULL)
+    {
+      if (startswith (scnname, ".gnu.debuglto_.debug"))
+       return TYPE_GNU_LTO;
+      else if (startswith (scnname, ".debug_") || startswith (scnname, ".zdebug_"))
+       {
+         size_t len = strlen (scnname);
+         if (strcmp (scnname + len - 4, ".dwo") == 0)
+           return TYPE_DWO;
+         else
+           return TYPE_PLAIN;
+       }
+    }
+  return TYPE_UNKNOWN;
+}
 static Dwarf *
 check_section (Dwarf *result, size_t shstrndx, Elf_Scn *scn, bool inscngrp)
 {
@@ -116,7 +141,11 @@ check_section (Dwarf *result, size_t shstrndx, Elf_Scn *scn, bool inscngrp)
       return NULL;
     }
 
-  /* Recognize the various sections.  Most names start with .debug_.  */
+  /* Recognize the various sections.  Most names start with .debug_.
+     They might be compressed (and start with .z).  Or end with .dwo
+     for split dwarf sections.  Or start with .gnu.debuglto_ for
+     LTO debug sections.  We should only use one consistent set at
+     a time.  We prefer PLAIN over DWO over LTO.  */
   size_t cnt;
   bool gnu_compressed = false;
   for (cnt = 0; cnt < ndwarf_scnnames; ++cnt)
@@ -127,7 +156,15 @@ check_section (Dwarf *result, size_t shstrndx, Elf_Scn *scn, bool inscngrp)
          && (dbglen == scnlen
              || (scnlen == dbglen + 4
                  && strstr (scnname, ".dwo") == scnname + dbglen)))
-       break;
+       {
+         if (dbglen == scnlen)
+           {
+             if (result->type == TYPE_PLAIN)
+               break;
+           }
+         else if (result->type == TYPE_DWO)
+           break;
+       }
       else if (scnname[0] == '.' && scnname[1] == 'z'
               && (strncmp (&scnname[2], &dwarf_scnnames[cnt][1],
                            dbglen - 1) == 0
@@ -136,13 +173,27 @@ check_section (Dwarf *result, size_t shstrndx, Elf_Scn *scn, bool inscngrp)
                           && strstr (scnname,
                                      ".dwo") == scnname + dbglen + 1))))
        {
-         gnu_compressed = true;
-         break;
+         if (scnlen == dbglen + 1)
+           {
+             if (result->type == TYPE_PLAIN)
+               {
+                 gnu_compressed = true;
+                 break;
+               }
+           }
+         else if (result->type <= TYPE_DWO)
+           {
+             gnu_compressed = true;
+             break;
+           }
        }
       else if (scnlen > 14 /* .gnu.debuglto_ prefix. */
               && startswith (scnname, ".gnu.debuglto_")
               && strcmp (&scnname[14], dwarf_scnnames[cnt]) == 0)
-       break;
+       {
+         if (result->type == TYPE_GNU_LTO)
+           break;
+       }
     }
 
   if (cnt >= ndwarf_scnnames)
@@ -344,6 +395,16 @@ global_read (Dwarf *result, Elf *elf, size_t shstrndx)
 {
   Elf_Scn *scn = NULL;
 
+  /* First check the type (PLAIN, DWO, LTO) we are looking for.  We
+     prefer PLAIN if available over DWO, over LTO.  */
+  while ((scn = elf_nextscn (elf, scn)) != NULL && result->type != TYPE_PLAIN)
+    {
+      enum dwarf_type type = scn_dwarf_type (result, shstrndx, scn);
+      if (type > result->type)
+       result->type = type;
+    }
+
+  scn = NULL;
   while (result != NULL && (scn = elf_nextscn (elf, scn)) != NULL)
     result = check_section (result, shstrndx, scn, false);
 
@@ -388,6 +449,9 @@ scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx, Elf_Scn *scngrp)
      represent section indices.  The first word is a flag word.  */
   Elf32_Word *scnidx = (Elf32_Word *) data->d_buf;
   size_t cnt;
+
+  /* First check the type (PLAIN, DWO, LTO) we are looking for.  We
+     prefer PLAIN if available over DWO, over LTO.  */
   for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size; ++cnt)
     {
       Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
@@ -401,6 +465,15 @@ scngrp_read (Dwarf *result, Elf *elf, size_t shstrndx, Elf_Scn *scngrp)
          return NULL;
        }
 
+      enum dwarf_type type = scn_dwarf_type (result, shstrndx, scn);
+      if (type > result->type)
+       result->type = type;
+    }
+
+  for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size && result != NULL; ++cnt)
+    {
+      Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
+      assert (scn != NULL); // checked above
       result = check_section (result, shstrndx, scn, true);
       if (result == NULL)
        break;
index 7174ea9..48f3a94 100644 (file)
@@ -145,6 +145,16 @@ enum
 
 #include "dwarf_sig8_hash.h"
 
+/* The type of Dwarf object, sorted by preference
+   (if there is a higher order type, we pick that one over the others).  */
+enum dwarf_type
+  {
+    TYPE_UNKNOWN = 0,
+    TYPE_GNU_LTO = 16,
+    TYPE_DWO = 32,
+    TYPE_PLAIN = 64,
+  };
+
 /* This is the structure representing the debugging state.  */
 struct Dwarf
 {
@@ -216,6 +226,8 @@ struct Dwarf
   /* Similar for addrx/constx, which will come from .debug_addr section.  */
   struct Dwarf_CU *fake_addr_cu;
 
+  enum dwarf_type type;
+
   /* Supporting lock for internal memory handling.  Ensures threads that have
      an entry in the mem_tails array are not disturbed by new threads doing
      allocations for this Dwarf.  */
index b791cd7..c5d0002 100644 (file)
@@ -1,3 +1,11 @@
+2021-11-08  Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am (TESTS): Add run-readelf-fat-lto.sh.
+       (EXTRA_DIST): Add run-readelf-fat-lto.sh and
+       testfile-dwarf5-fat-lto.o.bz2.
+       * run-readelf-fat-lto.sh: New test.
+       * testfile-dwarf5-fat-lto.o.bz2: New test file.
+
 2021-11-04  Frank Ch. Eigler  <fche@redhat.com>
 
        PR28514
index 54b3895..ccc4c05 100644 (file)
@@ -139,7 +139,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
        run-low_high_pc.sh run-macro-test.sh run-elf_cntl_gelf_getshdr.sh \
        run-test-archive64.sh run-readelf-vmcoreinfo.sh \
        run-readelf-mixed-corenote.sh run-dwfllines.sh \
-       run-readelf-variant.sh \
+       run-readelf-variant.sh run-readelf-fat-lto.sh \
        run-dwfl-report-elf-align.sh run-addr2line-test.sh \
        run-addr2line-i-test.sh run-addr2line-i-lex-test.sh \
        run-addr2line-i-demangle-test.sh run-addr2line-alt-debugpath.sh \
@@ -379,6 +379,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             testfilebazminppc64.bz2 testfilebazminppc64_pl.bz2 \
             testfilebazminppc64_plr.bz2 testfilebaztabppc64.bz2 \
             run-readelf-variant.sh testfile-ada-variant.bz2 \
+            run-readelf-fat-lto.sh testfile-dwarf5-fat-lto.o.bz2 \
             run-dwflsyms.sh \
             run-unstrip-n.sh testcore-rtlib.bz2 testcore-rtlib-ppc.bz2 \
             run-low_high_pc.sh testfile_low_high_pc.bz2 \
diff --git a/tests/run-readelf-fat-lto.sh b/tests/run-readelf-fat-lto.sh
new file mode 100755 (executable)
index 0000000..e03cec3
--- /dev/null
@@ -0,0 +1,53 @@
+. $srcdir/test-subr.sh
+
+# - s.c
+# int main_argc_remaining;
+#
+# int main_argc() {
+#   int result = 0;
+#   if (main_argc_remaining)
+#     result = 0;
+#
+#   return 0;
+# }
+#
+# gcc -gdwarf-5 -c -o testfile-dwarf5-fat-lto.o -flto -O s.c -g -ffat-lto-objects
+
+testfiles testfile-dwarf5-fat-lto.o
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=loc --debug-dump=ranges -N -U testfile-dwarf5-fat-lto.o << EOF
+
+DWARF section [26] '.debug_loclists' at offset 0x7db:
+Table at Offset 0x0:
+
+ Length:               24
+ DWARF version:         5
+ Address size:          8
+ Segment size:          0
+ Offset entries:        0
+ CU [     c] base: 000000000000000000
+
+  Offset: c, Index: 0
+    view pair 2, 3
+
+  Offset: e, Index: 2
+    start_length 0x0, 0
+        [ 0] lit0
+        [ 1] stack_value
+    end_of_list
+
+
+DWARF section [30] '.debug_rnglists' at offset 0x827:
+Table at Offset 0x0:
+
+ Length:               19
+ DWARF version:         5
+ Address size:          8
+ Segment size:          0
+ Offset entries:        0
+ CU [     c] base: 000000000000000000
+
+  Offset: c, Index: 0
+    start_length 0x0, 8
+    end_of_list
+
+EOF
diff --git a/tests/testfile-dwarf5-fat-lto.o.bz2 b/tests/testfile-dwarf5-fat-lto.o.bz2
new file mode 100644 (file)
index 0000000..ce3659f
Binary files /dev/null and b/tests/testfile-dwarf5-fat-lto.o.bz2 differ