Support reading .zdebug_* DWARF sections compressed via zlib.
authorRoland McGrath <roland@redhat.com>
Thu, 24 Feb 2011 03:52:46 +0000 (19:52 -0800)
committerRoland McGrath <roland@redhat.com>
Thu, 24 Feb 2011 03:52:46 +0000 (19:52 -0800)
NEWS
libdw/ChangeLog
libdw/dwarf_begin_elf.c
libdw/dwarf_end.c
libdw/libdwP.h
src/ChangeLog
src/readelf.c

diff --git a/NEWS b/NEWS
index 115261e..e4b2402 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+Version 0.153
+
+libdw: Support reading .zdebug_* DWARF sections compressed via zlib.
+
 Version 0.152
 
 Various build and warning nits fixed for newest GCC and Autoconf.
index 8ec7485..b0ba7e0 100644 (file)
@@ -1,3 +1,14 @@
+2011-02-23  Roland McGrath  <roland@redhat.com>
+
+       * libdwP.h (struct Dwarf) [USE_ZLIB]: New member sectiondata_gzip_mask.
+       Declare __libdw_free_zdata.
+       * dwarf_end.c [USE_ZLIB] (__libdw_free_zdata): New function.
+       (dwarf_end): Call it.
+
+       * dwarf_begin_elf.c (valid_p): Likewise.
+       (check_section, scngrp_read): Likewise.
+       (check_section) [USE_ZLIB]: Grok .z* flavors of sections.
+
 2010-10-13  Roland McGrath  <roland@redhat.com>
 
        * dwarf.h: Add DW_LANG_Go.
index b5fb7c9..9ec7d51 100644 (file)
@@ -1,5 +1,5 @@
 /* Create descriptor from ELF descriptor for processing file.
-   Copyright (C) 2002-2010 Red Hat, Inc.
+   Copyright (C) 2002-2011 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
 
 #include "libdwP.h"
 
+#if USE_ZLIB
+# include <endian.h>
+# define crc32         loser_crc32
+# include <zlib.h>
+# undef crc32
+#endif
+
 
 /* Section names.  */
 static const char dwarf_scnnames[IDX_last][17] =
@@ -117,6 +124,7 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
     {
       /* The section name must be valid.  Otherwise is the ELF file
         invalid.  */
+      __libdw_free_zdata (result);
       __libdw_seterrno (DWARF_E_INVALID_ELF);
       free (result);
       return NULL;
@@ -141,6 +149,76 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
 
        break;
       }
+#if USE_ZLIB
+    else if (scnname[0] == '.' && scnname[1] == 'z'
+            && strcmp (&scnname[2], &dwarf_scnnames[cnt][1]) == 0)
+      {
+       /* A compressed section.  */
+
+       if (unlikely (result->sectiondata[cnt] != NULL))
+         /* A section appears twice.  That's bad.  We ignore the section.  */
+         break;
+
+       /* Get the section data.  */
+       Elf_Data *data = elf_getdata (scn, NULL);
+       if (data != NULL && data->d_size != 0)
+         {
+           /* There is a 12-byte header of "ZLIB" followed by
+              an 8-byte big-endian size.  */
+
+           if (unlikely (data->d_size < 4 + 8)
+               || unlikely (memcmp (data->d_buf, "ZLIB", 4) != 0))
+             break;
+
+           uint64_t size;
+           memcpy (&size, data->d_buf + 4, sizeof size);
+           size = be64toh (size);
+
+           Elf_Data *zdata = malloc (sizeof (Elf_Data) + size);
+           if (unlikely (zdata == NULL))
+             break;
+
+           zdata->d_buf = &zdata[1];
+           zdata->d_type = ELF_T_BYTE;
+           zdata->d_version = EV_CURRENT;
+           zdata->d_size = size;
+           zdata->d_off = 0;
+           zdata->d_align = 1;
+
+           z_stream z =
+             {
+               .next_in = data->d_buf + 4 + 8,
+               .avail_in = data->d_size - 4 - 8,
+               .next_out = zdata->d_buf,
+               .avail_out = zdata->d_size
+             };
+           int zrc = inflateInit (&z);
+           while (z.avail_in > 0 && likely (zrc == Z_OK))
+             {
+               z.next_out = zdata->d_buf + (zdata->d_size - z.avail_out);
+               zrc = inflate (&z, Z_FINISH);
+               if (unlikely (zrc != Z_STREAM_END))
+                 {
+                   zrc = Z_DATA_ERROR;
+                   break;
+                 }
+               zrc = inflateReset (&z);
+             }
+           if (likely (zrc == Z_OK))
+             zrc = inflateEnd (&z);
+
+           if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
+             free (zdata);
+           else
+             {
+               result->sectiondata[cnt] = zdata;
+               result->sectiondata_gzip_mask |= 1U << cnt;
+             }
+         }
+
+       break;
+      }
+#endif
 
   return result;
 }
@@ -159,6 +237,7 @@ valid_p (Dwarf *result)
   if (likely (result != NULL)
       && unlikely (result->sectiondata[IDX_debug_info] == NULL))
     {
+      __libdw_free_zdata (result);
       __libdw_seterrno (DWARF_E_NO_DWARF);
       free (result);
       result = NULL;
@@ -189,6 +268,7 @@ scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp)
   if (data == NULL)
     {
       /* We cannot read the section content.  Fail!  */
+      __libdw_free_zdata (result);
       free (result);
       return NULL;
     }
@@ -204,6 +284,7 @@ scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp)
        {
          /* A section group refers to a non-existing section.  Should
             never happen.  */
+         __libdw_free_zdata (result);
          __libdw_seterrno (DWARF_E_INVALID_ELF);
          free (result);
          return NULL;
index ec10542..1e733ca 100644 (file)
@@ -1,5 +1,5 @@
 /* Release debugging handling context.
-   Copyright (C) 2002-2010 Red Hat, Inc.
+   Copyright (C) 2002-2011 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -54,6 +54,8 @@
 
 #include <search.h>
 #include <stdlib.h>
+#include <assert.h>
+#include <string.h>
 
 #include "libdwP.h"
 #include "cfi.h"
@@ -76,6 +78,24 @@ cu_free (void *arg)
 }
 
 
+#if USE_ZLIB
+void
+internal_function
+__libdw_free_zdata (Dwarf *dwarf)
+{
+  unsigned int gzip_mask = dwarf->sectiondata_gzip_mask;
+  while (gzip_mask != 0)
+    {
+      int i = ffs (gzip_mask);
+      assert (i > 0);
+      --i;
+      assert (i < IDX_last);
+      free (dwarf->sectiondata[i]);
+      gzip_mask &= ~(1U << i);
+    }
+}
+#endif
+
 int
 dwarf_end (dwarf)
      Dwarf *dwarf;
@@ -106,6 +126,8 @@ dwarf_end (dwarf)
       /* Free the pubnames helper structure.  */
       free (dwarf->pubnames_sets);
 
+      __libdw_free_zdata (dwarf);
+
       /* Free the ELF descriptor if necessary.  */
       if (dwarf->free_elf)
        elf_end (dwarf->elf);
index da6efc5..b84bf02 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal definitions for libdwarf.
-   Copyright (C) 2002-2010 Red Hat, Inc.
+   Copyright (C) 2002-2011 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -150,6 +150,11 @@ struct Dwarf
   /* The section data.  */
   Elf_Data *sectiondata[IDX_last];
 
+#if USE_ZLIB
+  /* The 1 << N bit is set if sectiondata[N] is malloc'd decompressed data.  */
+  unsigned int sectiondata_gzip_mask:IDX_last;
+#endif
+
   /* True if the file has a byte order different from the host.  */
   bool other_byte_order;
 
@@ -385,6 +390,12 @@ extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
 /* Default OOM handler.  */
 extern void __libdw_oom (void) __attribute ((noreturn, visibility ("hidden")));
 
+#if USE_ZLIB
+extern void __libdw_free_zdata (Dwarf *dwarf) internal_function;
+#else
+# define __libdw_free_zdata(dwarf)     ((void) (dwarf))
+#endif
+
 /* Allocate the internal data for a unit not seen before.  */
 extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
      __nonnull_attribute__ (1) internal_function;
index c8b897a..a7c2513 100644 (file)
@@ -1,5 +1,19 @@
 2011-02-23  Roland McGrath  <roland@redhat.com>
 
+       * readelf.c (section_name): New function.
+       (print_debug_abbrev_section): Use it instead of constant.
+       (print_debug_aranges_section): Likewise.
+       (print_debug_ranges_section): Likewise.
+       (print_debug_units): Likewise.
+       (print_debug_line_section): Likewise.
+       (print_debug_loc_section): Likewise.
+       (print_debug_macinfo_section): Likewise.
+       (print_debug_pubnames_section): Likewise.
+       (print_debug_str_section): Likewise.
+       (print_debug) [USE_ZLIB]: Match .zdebug_* sections too.
+       (print_debug_abbrev_section): Use decoded d_size, not sh_size.
+       (print_debug_str_section): Likewise.
+
        * readelf.c (dwarf_attr_string): Grok DW_AT_GNU_odr_signature.
 
 2011-02-11  Roland McGrath  <roland@redhat.com>
index 0e50a69..78ee2b7 100644 (file)
@@ -1154,6 +1154,13 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr)
 }
 
 
+static const char *
+section_name (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr)
+{
+  return elf_strptr (ebl->elf, ehdr->e_shstrndx, shdr->sh_name) ?: "???";
+}
+
+
 static void
 handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 {
@@ -4406,16 +4413,16 @@ skip_listptr_hole (struct listptr_table *table, size_t *idxp,
 
 static void
 print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                           Ebl *ebl __attribute__ ((unused)),
-                           GElf_Ehdr *ehdr __attribute__ ((unused)),
+                           Ebl *ebl, GElf_Ehdr *ehdr,
                            Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
   printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"
                   " [ Code]\n"),
-         elf_ndxscn (scn), ".debug_abbrev", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
 
   Dwarf_Off offset = 0;
-  while (offset < shdr->sh_size)
+  while (offset < dbg->sectiondata[IDX_debug_abbrev]->d_size)
     {
       printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"),
              offset);
@@ -4477,8 +4484,7 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
    takes care of it.  */
 static void
 print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                            Ebl *ebl __attribute__ ((unused)),
-                            GElf_Ehdr *ehdr, Elf_Scn *scn,
+                            Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
                             GElf_Shdr *shdr, Dwarf *dbg)
 {
   Dwarf_Aranges *aranges;
@@ -4495,7 +4501,8 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
                    "\
 \nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entries:\n",
                    cnt),
-         elf_ndxscn (scn), ".debug_aranges", (uint64_t) shdr->sh_offset, cnt);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset, cnt);
 
   /* Compute floor(log16(cnt)).  */
   size_t tmp = cnt;
@@ -4533,8 +4540,8 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 /* Print content of DWARF .debug_ranges section.  */
 static void
 print_debug_ranges_section (Dwfl_Module *dwflmod,
-                           Ebl *ebl __attribute__ ((unused)),
-                           GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr,
+                           Ebl *ebl, GElf_Ehdr *ehdr,
+                           Elf_Scn *scn, GElf_Shdr *shdr,
                            Dwarf *dbg)
 {
   Elf_Data *data = elf_rawdata (scn, NULL);
@@ -4548,7 +4555,8 @@ print_debug_ranges_section (Dwfl_Module *dwflmod,
 
   printf (gettext ("\
 \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
-         elf_ndxscn (scn), ".debug_ranges", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
 
   sort_listptr (&known_rangelistptr, "rangelistptr");
   size_t listptr_idx = 0;
@@ -5644,14 +5652,12 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
 
 static void
 print_debug_units (Dwfl_Module *dwflmod,
-                  Ebl *ebl __attribute__ ((unused)),
-                  GElf_Ehdr *ehdr __attribute__ ((unused)),
-                  Elf_Scn *scn,
-                  GElf_Shdr *shdr, Dwarf *dbg,
-                  bool debug_types)
+                  Ebl *ebl, GElf_Ehdr *ehdr,
+                  Elf_Scn *scn, GElf_Shdr *shdr,
+                  Dwarf *dbg, bool debug_types)
 {
   const bool silent = !(print_debug_sections & section_info);
-  const char *secname = debug_types ? ".debug_types" : ".debug_info";
+  const char *secname = section_name (ebl, ehdr, shdr);
 
   if (!silent)
     printf (gettext ("\
@@ -5814,13 +5820,13 @@ print_debug_types_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
 
 
 static void
-print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
-                         GElf_Ehdr *ehdr __attribute__ ((unused)),
+print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
                          Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
   printf (gettext ("\
 \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
-         elf_ndxscn (scn), ".debug_line", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
 
   if (shdr->sh_size == 0)
     return;
@@ -5853,7 +5859,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
            {
            invalid_data:
              error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
-                    elf_ndxscn (scn), ".debug_line");
+                    elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
              return;
            }
          unit_length = read_8ubyte_unaligned_inc (dbg, linep);
@@ -5921,7 +5927,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
          error (0, 0,
                 gettext ("invalid data at offset %tu in section [%zu] '%s'"),
                 linep - (const unsigned char *) data->d_buf,
-                elf_ndxscn (scn), ".debug_line");
+                elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
          linep = lineendp;
          continue;
        }
@@ -6304,7 +6310,7 @@ advance address by fixed value %u to %s\n"),
 
 static void
 print_debug_loc_section (Dwfl_Module *dwflmod,
-                        Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr,
+                        Ebl *ebl, GElf_Ehdr *ehdr,
                         Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
   Elf_Data *data = elf_rawdata (scn, NULL);
@@ -6318,7 +6324,8 @@ print_debug_loc_section (Dwfl_Module *dwflmod,
 
   printf (gettext ("\
 \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
-         elf_ndxscn (scn), ".debug_loc", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
 
   sort_listptr (&known_loclistptr, "loclistptr");
   size_t listptr_idx = 0;
@@ -6427,13 +6434,13 @@ mac_compare (const void *p1, const void *p2)
 
 static void
 print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                            Ebl *ebl __attribute__ ((unused)),
-                            GElf_Ehdr *ehdr __attribute__ ((unused)),
+                            Ebl *ebl, GElf_Ehdr *ehdr,
                             Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
   printf (gettext ("\
 \nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
-         elf_ndxscn (scn), ".debug_macinfo", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
   putc_unlocked ('\n', stdout);
 
   /* There is no function in libdw to iterate over the raw content of
@@ -6599,12 +6606,12 @@ print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global,
 /* Print the known exported symbols in the DWARF section '.debug_pubnames'.  */
 static void
 print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                             Ebl *ebl __attribute__ ((unused)),
-                             GElf_Ehdr *ehdr __attribute__ ((unused)),
+                             Ebl *ebl, GElf_Ehdr *ehdr,
                              Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
   printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
-         elf_ndxscn (scn), ".debug_pubnames", (uint64_t) shdr->sh_offset);
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
 
   int n = 0;
   (void) dwarf_getpubnames (dbg, print_pubnames, &n, 0);
@@ -6613,12 +6620,13 @@ print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
 /* Print the content of the DWARF string section '.debug_str'.  */
 static void
 print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                        Ebl *ebl __attribute__ ((unused)),
-                        GElf_Ehdr *ehdr __attribute__ ((unused)),
+                        Ebl *ebl, GElf_Ehdr *ehdr,
                         Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
 {
+  const size_t sh_size = dbg->sectiondata[IDX_debug_str]->d_size;
+
   /* Compute floor(log16(shdr->sh_size)).  */
-  GElf_Addr tmp = shdr->sh_size;
+  GElf_Addr tmp = sh_size;
   int digits = 1;
   while (tmp >= 16)
     {
@@ -6630,12 +6638,12 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
   printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"
                   " %*s  String\n"),
          elf_ndxscn (scn),
-         ".debug_str", (uint64_t) shdr->sh_offset,
+         section_name (ebl, ehdr, shdr), (uint64_t) shdr->sh_offset,
          /* TRANS: the debugstr| prefix makes the string unique.  */
          digits + 2, sgettext ("debugstr|Offset"));
 
   Dwarf_Off offset = 0;
-  while (offset < shdr->sh_size)
+  while (offset < sh_size)
     {
       size_t len;
       const char *str = dwarf_getstring (dbg, offset, &len);
@@ -6981,7 +6989,13 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
          int n;
 
          for (n = 0; n < ndebug_sections; ++n)
-           if (strcmp (name, debug_sections[n].name) == 0)
+           if (strcmp (name, debug_sections[n].name) == 0
+#if USE_ZLIB
+               || (name[0] == '.' && name[1] == 'z'
+                   && debug_sections[n].name[1] == 'd'
+                   && strcmp (&name[2], &debug_sections[n].name[1]) == 0)
+#endif
+               )
              {
                if ((print_debug_sections | implicit_debug_sections)
                    & debug_sections[n].bitmask)