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 <m...@redhat.com>
---
 libdwfl/ChangeLog              |  10 ++++
 libdwfl/dwfl_module_getdwarf.c |  99 ++++++++++++++++++++++++++++++++---
 libdwfl/relocate.c             | 116 ++++++++++++++++++++++++++++++++---------
 libebl/ChangeLog               |   4 ++
 libebl/eblopenbackend.c        |   4 +-
 5 files changed, 201 insertions(+), 32 deletions(-)

diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 30a5205..b653564 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,13 @@
+2015-12-18  Mark Wielaard  <m...@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-12-01  Mark Wielaard  <m...@redhat.com>
 
        * link_map.c (dwfl_link_map_report): Track whether in.d_buf comes
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 8483fa2..f86baf7 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -1120,10 +1120,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;
 
@@ -1131,17 +1160,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)
@@ -1164,9 +1209,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;
 
@@ -1174,12 +1243,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;
diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
index 2dc6737..fc88df3 100644
--- a/libdwfl/relocate.c
+++ b/libdwfl/relocate.c
@@ -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)
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index d040c08..4d365c5 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,7 @@
+2015-12-18  Mark Wielaard  <m...@redhat.com>
+
+       * eblopenbackend.c (default_debugscn_p): Also match .zdebug sections.
+
 2015-09-24  Jose E. Marchesi  <jose.march...@oracle.com>
 
        * Makefile.am (AM_CFLAGS): Use -fPIC instead of -fpic to avoid
diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c
index b301400..372ef2a 100644
--- a/libebl/eblopenbackend.c
+++ b/libebl/eblopenbackend.c
@@ -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;
-- 
2.5.0

Reply via email to