libdwfl: Use elf_compress[_gnu] to decompress string, symbol and reloc data.
authorMark Wielaard <mjw@redhat.com>
Wed, 30 Dec 2015 13:39:18 +0000 (14:39 +0100)
committerMark Wielaard <mjw@redhat.com>
Wed, 6 Jan 2016 13:27:10 +0000 (14:27 +0100)
This makes usage of the libdwfl symbol functions work out of the box even
when some sections (string, symbol or xndx) are compressed. For ET_REL
files this makes relocations just work by making sure the target section
is decompressed first before relocations are applied.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
libdwfl/ChangeLog
libdwfl/dwfl_module_getdwarf.c
libdwfl/relocate.c
libebl/ChangeLog
libebl/eblopenbackend.c

index 06b8469..7bb9b35 100644 (file)
@@ -1,3 +1,13 @@
+2015-12-18  Mark Wielaard  <mjw@redhat.com>
+
+       * dwfl_module_getdwarf.c (find_symtab): Uncompress symstr, xndx, sym
+       sections and aux_str, aux_xndx and aux_sym sections if necessary.
+       * relocate.c (relocate_getsym): Uncompress symtab and symtab_shndx
+       if necessary.
+       (resolve_symbol): Uncompress strtab section if necessary.
+       (relocate_section): Uncompress the section the relocations apply to
+       if necessary.
+
 2015-11-18  Chih-Hung Hsieh <chh@google.com>
 
        * linux-proc-maps.c (proc_maps_report): Move nested function
index e9589b3..0e8810b 100644 (file)
@@ -1130,10 +1130,39 @@ find_symtab (Dwfl_Module *mod)
       goto aux_cleanup; /* This cleans up some more and tries find_dynsym.  */
     }
 
-  /* Cache the data; MOD->syments and MOD->first_global were set above.  */
+  /* Cache the data; MOD->syments and MOD->first_global were set
+     above.  If any of the sections is compressed, uncompress it
+     first.  Only the string data setion could theoretically be
+     compressed GNU style (as .zdebug_str).  Everything else only ELF
+     gabi style (SHF_COMPRESSED).  */
+
+  Elf_Scn *symstrscn = elf_getscn (mod->symfile->elf, strshndx);
+  if (symstrscn == NULL)
+    goto elferr;
+
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = gelf_getshdr (symstrscn, &shdr_mem);
+  if (shdr == NULL)
+    goto elferr;
+
+  size_t shstrndx;
+  if (elf_getshdrstrndx (mod->symfile->elf, &shstrndx) < 0)
+    goto elferr;
+
+  const char *sname = elf_strptr (mod->symfile->elf, shstrndx, shdr->sh_name);
+  if (sname == NULL)
+    goto elferr;
+
+  if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+    /* Try to uncompress, but it might already have been, an error
+       might just indicate, already uncompressed.  */
+    elf_compress_gnu (symstrscn, 0, 0);
 
-  mod->symstrdata = elf_getdata (elf_getscn (mod->symfile->elf, strshndx),
-                                NULL);
+  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+    if (elf_compress (symstrscn, 0, 0) < 0)
+      goto elferr;
+
+  mod->symstrdata = elf_getdata (symstrscn, NULL);
   if (mod->symstrdata == NULL || mod->symstrdata->d_buf == NULL)
     goto elferr;
 
@@ -1141,17 +1170,33 @@ find_symtab (Dwfl_Module *mod)
     mod->symxndxdata = NULL;
   else
     {
+      shdr = gelf_getshdr (xndxscn, &shdr_mem);
+      if (shdr == NULL)
+       goto elferr;
+
+      if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+       if (elf_compress (xndxscn, 0, 0) < 0)
+         goto elferr;
+
       mod->symxndxdata = elf_getdata (xndxscn, NULL);
       if (mod->symxndxdata == NULL || mod->symxndxdata->d_buf == NULL)
        goto elferr;
     }
 
+  shdr = gelf_getshdr (symscn, &shdr_mem);
+  if (shdr == NULL)
+    goto elferr;
+
+  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+    if (elf_compress (symscn, 0, 0) < 0)
+      goto elferr;
+
   mod->symdata = elf_getdata (symscn, NULL);
   if (mod->symdata == NULL || mod->symdata->d_buf == NULL)
     goto elferr;
 
   // Sanity check number of symbols.
-  GElf_Shdr shdr_mem, *shdr = gelf_getshdr (symscn, &shdr_mem);
+  shdr = gelf_getshdr (symscn, &shdr_mem);
   if (shdr == NULL || shdr->sh_entsize == 0
       || mod->syments > mod->symdata->d_size / shdr->sh_entsize
       || (size_t) mod->first_global > mod->syments)
@@ -1174,9 +1219,33 @@ find_symtab (Dwfl_Module *mod)
          return;
        }
 
-      mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf,
-                                                    aux_strshndx),
-                                        NULL);
+      Elf_Scn *aux_strscn = elf_getscn (mod->aux_sym.elf, aux_strshndx);
+      if (aux_strscn == NULL)
+       goto elferr;
+
+      shdr = gelf_getshdr (aux_strscn, &shdr_mem);
+      if (shdr == NULL)
+       goto elferr;
+
+      size_t aux_shstrndx;
+      if (elf_getshdrstrndx (mod->aux_sym.elf, &aux_shstrndx) < 0)
+       goto elferr;
+
+      sname = elf_strptr (mod->aux_sym.elf, aux_shstrndx,
+                                     shdr->sh_name);
+      if (sname == NULL)
+       goto elferr;
+
+      if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+       /* Try to uncompress, but it might already have been, an error
+          might just indicate, already uncompressed.  */
+       elf_compress_gnu (aux_strscn, 0, 0);
+
+      if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+       if (elf_compress (aux_strscn, 0, 0) < 0)
+         goto elferr;
+
+      mod->aux_symstrdata = elf_getdata (aux_strscn, NULL);
       if (mod->aux_symstrdata == NULL || mod->aux_symstrdata->d_buf == NULL)
        goto aux_cleanup;
 
@@ -1184,12 +1253,28 @@ find_symtab (Dwfl_Module *mod)
        mod->aux_symxndxdata = NULL;
       else
        {
+         shdr = gelf_getshdr (aux_xndxscn, &shdr_mem);
+         if (shdr == NULL)
+           goto elferr;
+
+         if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+           if (elf_compress (aux_xndxscn, 0, 0) < 0)
+             goto elferr;
+
          mod->aux_symxndxdata = elf_getdata (aux_xndxscn, NULL);
          if (mod->aux_symxndxdata == NULL
              || mod->aux_symxndxdata->d_buf == NULL)
            goto aux_cleanup;
        }
 
+      shdr = gelf_getshdr (aux_symscn, &shdr_mem);
+      if (shdr == NULL)
+       goto elferr;
+
+      if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+       if (elf_compress (aux_symscn, 0, 0) < 0)
+         goto elferr;
+
       mod->aux_symdata = elf_getdata (aux_symscn, NULL);
       if (mod->aux_symdata == NULL || mod->aux_symdata->d_buf == NULL)
        goto aux_cleanup;
index 2dc6737..fc88df3 100644 (file)
@@ -123,23 +123,32 @@ relocate_getsym (Dwfl_Module *mod,
            {
              GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
              if (shdr != NULL)
-               switch (shdr->sh_type)
-                 {
-                 default:
-                   continue;
-                 case SHT_SYMTAB:
-                   cache->symelf = relocated;
-                   cache->symdata = elf_getdata (scn, NULL);
-                   cache->strtabndx = shdr->sh_link;
-                   if (unlikely (cache->symdata == NULL))
-                     return DWFL_E_LIBELF;
-                   break;
-                 case SHT_SYMTAB_SHNDX:
-                   cache->symxndxdata = elf_getdata (scn, NULL);
-                   if (unlikely (cache->symxndxdata == NULL))
+               {
+                 /* We need uncompressed data.  */
+                 if ((shdr->sh_type == SHT_SYMTAB
+                      || shdr->sh_type == SHT_SYMTAB_SHNDX)
+                     && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+                   if (elf_compress (scn, 0, 0) < 0)
                      return DWFL_E_LIBELF;
-                   break;
-                 }
+
+                 switch (shdr->sh_type)
+                   {
+                   default:
+                     continue;
+                   case SHT_SYMTAB:
+                     cache->symelf = relocated;
+                     cache->symdata = elf_getdata (scn, NULL);
+                     cache->strtabndx = shdr->sh_link;
+                     if (unlikely (cache->symdata == NULL))
+                       return DWFL_E_LIBELF;
+                     break;
+                   case SHT_SYMTAB_SHNDX:
+                     cache->symxndxdata = elf_getdata (scn, NULL);
+                     if (unlikely (cache->symxndxdata == NULL))
+                       return DWFL_E_LIBELF;
+                     break;
+                   }
+               }
              if (cache->symdata != NULL && cache->symxndxdata != NULL)
                break;
            }
@@ -203,9 +212,34 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
          /* Cache the strtab for this symtab.  */
          assert (referer->symfile == NULL
                  || referer->symfile->elf != symtab->symelf);
-         symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
-                                                       symtab->strtabndx),
-                                           NULL);
+
+         Elf_Scn *scn = elf_getscn (symtab->symelf, symtab->strtabndx);
+         if (scn == NULL)
+           return DWFL_E_LIBELF;
+
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         if (shdr == NULL)
+           return DWFL_E_LIBELF;
+
+         if (symtab->symshstrndx == SHN_UNDEF
+             && elf_getshdrstrndx (symtab->symelf, &symtab->symshstrndx) < 0)
+           return DWFL_E_LIBELF;
+
+         const char *sname = elf_strptr (symtab->symelf, symtab->symshstrndx,
+                                         shdr->sh_name);
+         if (sname == NULL)
+           return DWFL_E_LIBELF;
+
+         /* If the section is already decompressed, that isn't an error.  */
+         if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+           elf_compress_gnu (scn, 0, 0);
+
+         if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+           if (elf_compress (scn, 0, 0) < 0)
+             return DWFL_E_LIBELF;
+
+         symtab->symstrdata = elf_getdata (scn, NULL);
          if (unlikely (symtab->symstrdata == NULL
                        || symtab->symstrdata->d_buf == NULL))
            return DWFL_E_LIBELF;
@@ -446,22 +480,56 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
                  Elf_Scn *scn, GElf_Shdr *shdr,
                  Elf_Scn *tscn, bool debugscn, bool partial)
 {
-  /* First, fetch the name of the section these relocations apply to.  */
+  /* First, fetch the name of the section these relocations apply to.
+     Then try to decompress both relocation and target section.  */
   GElf_Shdr tshdr_mem;
   GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+  if (tshdr == NULL)
+    return DWFL_E_LIBELF;
+
   const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
   if (tname == NULL)
     return DWFL_E_LIBELF;
 
-  if (unlikely (tshdr->sh_type == SHT_NOBITS) || unlikely (tshdr->sh_size == 0))
-    /* No contents to relocate.  */
-    return DWFL_E_NOERROR;
-
   if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
     /* This relocation section is not for a debugging section.
        Nothing to do here.  */
     return DWFL_E_NOERROR;
 
+  if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
+    elf_compress_gnu (tscn, 0, 0);
+
+  if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
+    if (elf_compress (tscn, 0, 0) < 0)
+      return DWFL_E_LIBELF;
+
+  /* Reload Shdr in case section was just decompressed.  */
+  tshdr = gelf_getshdr (tscn, &tshdr_mem);
+  if (tshdr == NULL)
+    return DWFL_E_LIBELF;
+
+  if (unlikely (tshdr->sh_type == SHT_NOBITS)
+      || unlikely (tshdr->sh_size == 0))
+    /* No contents to relocate.  */
+    return DWFL_E_NOERROR;
+
+  const char *sname = elf_strptr (relocated, shstrndx, shdr->sh_name);
+  if (sname == NULL)
+    return DWFL_E_LIBELF;
+
+  if (strncmp (sname, ".zdebug", strlen ("zdebug")) == 0)
+    elf_compress_gnu (scn, 0, 0);
+
+  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+    if (elf_compress (scn, 0, 0) < 0)
+      return DWFL_E_LIBELF;
+
+  /* Reload Shdr in case section was just decompressed.  */
+  GElf_Shdr shdr_mem;
+  shdr = gelf_getshdr (scn, &shdr_mem);
+  if (shdr == NULL)
+    return DWFL_E_LIBELF;
+
   /* Fetch the section data that needs the relocations applied.  */
   Elf_Data *tdata = elf_rawdata (tscn, NULL);
   if (tdata == NULL)
index a1a1022..312cf90 100644 (file)
@@ -1,3 +1,7 @@
+2015-12-18  Mark Wielaard  <mjw@redhat.com>
+
+       * eblopenbackend.c (default_debugscn_p): Also match .zdebug sections.
+
 2015-12-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
        * libebl.h: Prototype for ebl_ra_offset.
index b301400..372ef2a 100644 (file)
@@ -662,7 +662,9 @@ default_debugscn_p (const char *name)
   const size_t ndwarf_scn_names = (sizeof (dwarf_scn_names)
                                   / sizeof (dwarf_scn_names[0]));
   for (size_t cnt = 0; cnt < ndwarf_scn_names; ++cnt)
-    if (strcmp (name, dwarf_scn_names[cnt]) == 0)
+    if (strcmp (name, dwarf_scn_names[cnt]) == 0
+       || (strncmp (name, ".zdebug", strlen (".zdebug")) == 0
+           && strcmp (&name[2], &dwarf_scn_names[cnt][1]) == 0))
       return true;
 
   return false;