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