Many improvements in elflint, mostly symbol versioning related.
authorUlrich Drepper <drepper@redhat.com>
Sat, 6 Aug 2005 01:37:23 +0000 (01:37 +0000)
committerUlrich Drepper <drepper@redhat.com>
Sat, 6 Aug 2005 01:37:23 +0000 (01:37 +0000)
Cosmetic changes in readelf.

NEWS
src/ChangeLog
src/elflint.c
src/readelf.c

diff --git a/NEWS b/NEWS
index 5e206a1..264742f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,7 @@ Version 0.112:
 
 elfcmp: some more relaxation.
 
-elflint: some more tests.
+elflint: many more tests, especially regarding to symbol versioning.
 
 libelf: Add elfXX_offscn and gelf_offscn.
 
index f6b5284..6533861 100644 (file)
@@ -1,3 +1,10 @@
+2005-08-05  Ulrich Drepper  <drepper@redhat.com>
+
+       * elflint.c: Many, many more tests.  Mostly related to symbol
+       versioning.  Those sections should now be completely checked.
+
+       * readelf.c (print_dynamic): Use gelf_offscn.
+
 2005-08-04  Ulrich Drepper  <drepper@redhat.com>
 
        * elflint.c: Add lots more tests: more extension symbol table sanity,
index f97f7a9..0896496 100644 (file)
@@ -540,20 +540,11 @@ section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"),
 
 
 static void
-check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
+check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
 {
   bool no_xndx_warned = false;
   int no_pt_tls = 0;
-
-  Elf_Scn *scn = elf_getscn (ebl->elf, idx);
-  GElf_Shdr shdr_mem;
-  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
-  GElf_Shdr strshdr_mem;
-  GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
-                                    &strshdr_mem);
-  if (shdr == NULL || strshdr == NULL)
-    return;
-  Elf_Data *data = elf_getdata (scn, NULL);
+  Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
   if (data == NULL)
     {
       ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
@@ -561,6 +552,12 @@ check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx)
       return;
     }
 
+  GElf_Shdr strshdr_mem;
+  GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+                                    &strshdr_mem);
+  if (strshdr == NULL)
+    return;
+
   if (strshdr->sh_type != SHT_STRTAB)
     ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
           shdr->sh_link, section_name (ebl, shdr->sh_link),
@@ -1863,9 +1860,9 @@ static const struct
     { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
 
     /* The following are GNU extensions.  */
-    { ".gnu.version", 12, SHT_GNU_versym, exact, SHF_ALLOC, 0 },
-    { ".gnu.version_d", 14, SHT_GNU_verdef, exact, SHF_ALLOC, 0 },
-    { ".gnu.version_r", 14, SHT_GNU_verneed, exact, SHF_ALLOC, 0 }
+    { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 },
+    { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 },
+    { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 }
   };
 #define nspecial_sections \
   (sizeof (special_sections) / sizeof (special_sections[0]))
@@ -1922,14 +1919,117 @@ section_flags_string (GElf_Word flags, char *buf, size_t len)
 }
 
 
+static int
+has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx)
+{
+  /* First find the relocation section for the symbol table.  */
+  Elf_Scn *scn = NULL;
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = NULL;
+  while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+    {
+      shdr = gelf_getshdr (scn, &shdr_mem);
+      if (shdr != NULL
+         && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+         && shdr->sh_link == symscnndx)
+       /* Found the section.  */
+       break;
+    }
+
+  if (scn == NULL)
+    return 0;
+
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    return 0;
+
+  if (shdr->sh_type == SHT_REL)
+    for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+      {
+       GElf_Rel rel_mem;
+       GElf_Rel *rel = gelf_getrel (data, i, &rel_mem);
+       if (rel == NULL)
+         continue;
+
+       if (GELF_R_SYM (rel->r_info) == symndx
+           && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
+         return 1;
+      }
+  else
+    for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+      {
+       GElf_Rela rela_mem;
+       GElf_Rela *rela = gelf_getrela (data, i, &rela_mem);
+       if (rela == NULL)
+         continue;
+
+       if (GELF_R_SYM (rela->r_info) == symndx
+           && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
+         return 1;
+      }
+
+  return 0;
+}
+
+
+static struct version_namelist
+{
+  const char *objname;
+  const char *name;
+  GElf_Word ndx;
+  enum { ver_def, ver_need } type;
+  struct version_namelist *next;
+} *version_namelist;
+
+
+static int
+add_version (const char *objname, const char *name, GElf_Word ndx, int type)
+{
+  /* Check that there are no duplications.  */
+  struct version_namelist *nlp = version_namelist;
+  while (nlp != NULL)
+    {
+      if (((nlp->objname == NULL && objname == NULL)
+          || (nlp->objname != NULL && objname != NULL
+              && strcmp (nlp->objname, objname) == 0))
+         && strcmp (nlp->name, name) == 0)
+       return nlp->type == ver_def ? 1 : -1;
+      nlp = nlp->next;
+    }
+
+  nlp = xmalloc (sizeof (*nlp));
+  nlp->objname = objname;
+  nlp->name = name;
+  nlp->ndx = ndx;
+  nlp->type = type;
+  nlp->next = version_namelist;
+  version_namelist = nlp;
+
+  return 0;
+}
+
+
 static void
-check_versym (Ebl *ebl, GElf_Shdr *shdr, int idx)
+check_versym (Ebl *ebl, int idx)
 {
-  /* The number of elements in the version symbol table must be the
-     same as the number of symbols.  */
+  Elf_Scn *scn = elf_getscn (ebl->elf, idx);
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+  if (shdr == NULL)
+    /* The error has already been reported.  */
+    return;
+
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    {
+      ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+            idx, section_name (ebl, idx));
+      return;
+    }
+
+  Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
   GElf_Shdr symshdr_mem;
-  GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
-                                    &symshdr_mem);
+  GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
   if (symshdr == NULL)
     /* The error has already been reported.  */
     return;
@@ -1943,6 +2043,8 @@ section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic s
       return;
     }
 
+  /* The number of elements in the version symbol table must be the
+     same as the number of symbols.  */
   if (shdr->sh_size / shdr->sh_entsize
       != symshdr->sh_size / symshdr->sh_entsize)
     ERROR (gettext ("\
@@ -1950,17 +2052,123 @@ section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\
           idx, section_name (ebl, idx),
           shdr->sh_link, section_name (ebl, shdr->sh_link));
 
-  // XXX TODO A lot more tests
-  // check value of the fields.  local symbols must have zero entries.
-  // nonlocal symbols refer to valid version.  Check that version index
-  // in bound.
+  Elf_Data *symdata = elf_getdata (symscn, NULL);
+  if (symdata == NULL)
+    /* The error has already been reported.  */
+    return;
+
+  for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+    {
+      GElf_Versym versym_mem;
+      GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem);
+      if (versym == NULL)
+       {
+         ERROR (gettext ("\
+section [%2d] '%s': symbol %d: cannot read version data\n"),
+                idx, section_name (ebl, idx), cnt);
+         break;
+       }
+
+      GElf_Sym sym_mem;
+      GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem);
+      if (sym == NULL)
+       /* Already reported elsewhere.  */
+       continue;
+
+      if (*versym == VER_NDX_LOCAL)
+       {
+         /* Local symbol.  Make sure it is defined unless the
+            reference is weak.  */
+         if (sym->st_shndx == SHN_UNDEF
+             && GELF_ST_BIND (sym->st_info) != STB_WEAK)
+           ERROR (gettext ("\
+section [%2d] '%s': symbol %d: undefined symbol with local scope\n"),
+                  idx, section_name (ebl, idx), cnt);
+       }
+      else if (*versym == VER_NDX_GLOBAL)
+       {
+         /* Global symbol.  Make sure it is not defined as local.  */
+         if (GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+           ERROR (gettext ("\
+section [%2d] '%s': symbol %d: local symbol with global scope\n"),
+                  idx, section_name (ebl, idx), cnt);
+       }
+      else
+       {
+         /* Look through the list of defined versions and locate the
+            index we need for this symbol.  */
+         struct version_namelist *runp = version_namelist;
+         while (runp != NULL)
+           if (runp->ndx == *versym)
+             break;
+           else
+             runp = runp->next;
+
+         if (runp == NULL)
+           ERROR (gettext ("\
+section [%2d] '%s': symbol %d: invalid version index %d\n"),
+                  idx, section_name (ebl, idx), cnt, (int) *versym);
+         else if (sym->st_shndx == SHN_UNDEF
+                  && runp->type == ver_def)
+           ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for defined version\n"),
+                  idx, section_name (ebl, idx), cnt, (int) *versym);
+         else if (sym->st_shndx != SHN_UNDEF
+                  && runp->type == ver_need)
+           {
+             /* Unless this symbol has a copy relocation associated
+                this must not happen.  */
+             if (!has_copy_reloc (ebl, shdr->sh_link, cnt))
+               ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for requested version\n"),
+                      idx, section_name (ebl, idx), cnt, (int) *versym);
+           }
+       }
+    }
+}
+
+
+static int
+unknown_dependency_p (Elf *elf, GElf_Ehdr *ehdr, const char *fname)
+{
+  GElf_Phdr phdr_mem;
+  GElf_Phdr *phdr = NULL;
+
+  int i;
+  for (i = 0; i < ehdr->e_phnum; ++i)
+    if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL
+       && phdr->p_type == PT_DYNAMIC)
+      break;
+
+  if (i == ehdr->e_phnum)
+    return 1;
+  assert (phdr != NULL);
+  Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset);
+  GElf_Shdr shdr_mem;
+  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL)
+    for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j)
+      {
+       GElf_Dyn dyn_mem;
+       GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+       if (dyn != NULL && dyn->d_tag == DT_NEEDED)
+         {
+           const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val);
+           if (str != NULL && strcmp (str, fname) == 0)
+             /* Found it.  */
+             return 0;
+         }
+      }
+
+  return 1;
 }
 
 
 static unsigned int nverneed;
 
 static void
-check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx)
+check_verneed (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
 {
   if (++nverneed == 2)
     ERROR (gettext ("more than one version reference section present\n"));
@@ -1974,6 +2182,115 @@ check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx)
     ERROR (gettext ("\
 section [%2d] '%s': sh_link does not link to string table\n"),
           idx, section_name (ebl, idx));
+
+  Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+  if (data == NULL)
+    {
+      ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+            idx, section_name (ebl, idx));
+      return;
+    }
+  unsigned int offset = 0;
+  for (int cnt = shdr->sh_info; --cnt >= 0; )
+    {
+      /* Get the data at the next offset.  */
+      GElf_Verneed needmem;
+      GElf_Verneed *need = gelf_getverneed (data, offset, &needmem);
+      if (need == NULL)
+       break;
+
+      unsigned int auxoffset = offset + need->vn_aux;
+
+      if (need->vn_version != EV_CURRENT)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+              idx, section_name (ebl, idx), cnt, (int) need->vn_version);
+
+      if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED,
+                                                        1, EV_CURRENT))
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+              idx, section_name (ebl, idx), cnt);
+
+      const char *libname = elf_strptr (ebl->elf, shdr->sh_link,
+                                       need->vn_file);
+      if (libname == NULL)
+       {
+         ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid file reference\n"),
+                idx, section_name (ebl, idx), cnt);
+         goto next_need;
+       }
+
+      /* Check that there is a DT_NEEDED entry for the referenced library.  */
+      if (unknown_dependency_p (ebl->elf, ehdr, libname))
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d references unknown dependency\n"),
+              idx, section_name (ebl, idx), cnt);
+
+      for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
+       {
+         GElf_Vernaux auxmem;
+         GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem);
+         if (aux == NULL)
+           break;
+
+         if ((aux->vna_flags & ~VER_FLG_WEAK) != 0)
+           ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"),
+                  idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+
+         const char *verstr = elf_strptr (ebl->elf, shdr->sh_link,
+                                          aux->vna_name);
+         if (verstr == NULL)
+           ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"),
+                  idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+         else
+           {
+             GElf_Word hashval = elf_hash (verstr);
+             if (hashval != aux->vna_hash)
+               ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"),
+                      idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+                      cnt, (int) hashval, (int) aux->vna_hash);
+
+             int res = add_version (libname, verstr, aux->vna_other,
+                                    ver_need);
+             if (unlikely (res !=0))
+               {
+                 assert (res > 0);
+                 ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"),
+                        idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+                        cnt, verstr);
+               }
+           }
+
+         if ((aux->vna_next != 0 || cnt2 > 0)
+             && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1,
+                                            EV_CURRENT))
+           {
+             ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"),
+                    idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+             break;
+           }
+
+         auxoffset += MAX (aux->vna_next,
+                           gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT));
+       }
+
+      /* Find the next offset.  */
+    next_need:
+      offset += need->vn_next;
+
+      if ((need->vn_next != 0 || cnt > 0)
+         && offset < auxoffset)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+              idx, section_name (ebl, idx), cnt);
+    }
 }
 
 
@@ -1994,6 +2311,160 @@ check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx)
     ERROR (gettext ("\
 section [%2d] '%s': sh_link does not link to string table\n"),
           idx, section_name (ebl, idx));
+
+  Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+  if (data == NULL)
+    {
+    no_data:
+      ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+            idx, section_name (ebl, idx));
+      return;
+    }
+
+  /* Iterate over all version definition entries.  We check that there
+     is a BASE entry and that each index is unique.  To do the later
+     we collection the information in a list which is later
+     examined.  */
+  struct namelist
+  {
+    const char *name;
+    struct namelist *next;
+  } *namelist = NULL;
+  struct namelist *refnamelist = NULL;
+
+  bool has_base = false;
+  unsigned int offset = 0;
+  for (int cnt = shdr->sh_info; --cnt >= 0; )
+    {
+      /* Get the data at the next offset.  */
+      GElf_Verdef defmem;
+      GElf_Verdef *def = gelf_getverdef (data, offset, &defmem);
+      if (def == NULL)
+       goto no_data;
+
+      if ((def->vd_flags & VER_FLG_BASE) != 0)
+       {
+         if (has_base)
+           ERROR (gettext ("\
+section [%2d] '%s': more than one BASE definition\n"),
+                  idx, section_name (ebl, idx));
+         if (def->vd_ndx != VER_NDX_GLOBAL)
+           ERROR (gettext ("\
+section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"),
+                  idx, section_name (ebl, idx));
+         has_base = true;
+       }
+      if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has unknown flag\n"),
+              idx, section_name (ebl, idx), cnt);
+
+      if (def->vd_version != EV_CURRENT)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+              idx, section_name (ebl, idx), cnt, (int) def->vd_version);
+
+      if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF,
+                                                      1, EV_CURRENT))
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+              idx, section_name (ebl, idx), cnt);
+
+      unsigned int auxoffset = offset + def->vd_aux;
+      GElf_Verdaux auxmem;
+      GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem);
+      if (aux == NULL)
+       goto no_data;
+
+      const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+      if (name == NULL)
+       {
+         ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid name reference\n"),
+                idx, section_name (ebl, idx), cnt);
+         goto next_def;
+       }
+      GElf_Word hashval = elf_hash (name);
+      if (def->vd_hash != hashval)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"),
+              idx, section_name (ebl, idx), cnt, (int) hashval,
+              (int) def->vd_hash);
+
+      int res = add_version (NULL, name, def->vd_ndx, ver_def);
+      if (unlikely (res !=0))
+       {
+         assert (res > 0);
+         ERROR (gettext ("\
+section [%2d] '%s': entry %d has duplicate version name '%s'\n"),
+                idx, section_name (ebl, idx), cnt, name);
+       }
+
+      struct namelist *newname = alloca (sizeof (*newname));
+      newname->name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+      newname->next = namelist;
+      namelist = newname;
+
+      auxoffset += aux->vda_next;
+      for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
+       {
+         aux = gelf_getverdaux (data, auxoffset, &auxmem);
+         if (aux == NULL)
+           goto no_data;
+
+         newname = alloca (sizeof (*newname));
+         newname->name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+         newname->next = refnamelist;
+         refnamelist = newname;
+
+         if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt)
+             && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1,
+                                            EV_CURRENT))
+           {
+             ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"),
+                    idx, section_name (ebl, idx), cnt);
+             break;
+           }
+
+         auxoffset += MAX (aux->vda_next,
+                           gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT));
+       }
+
+      /* Find the next offset.  */
+    next_def:
+      offset += def->vd_next;
+
+      if ((def->vd_next != 0 || cnt > 0)
+         && offset < auxoffset)
+       ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+              idx, section_name (ebl, idx), cnt);
+    }
+
+  if (!has_base)
+    ERROR (gettext ("section [%2d] '%s': no BASE definition\n"),
+          idx, section_name (ebl, idx));
+
+  /* Check whether the referenced names are available.  */
+  while (namelist != NULL)
+    {
+      struct version_namelist *runp = version_namelist;
+      while (runp != NULL)
+       {
+         if (runp->type == ver_def
+             && strcmp (runp->name, namelist->name) == 0)
+           break;
+         runp = runp->next;
+       }
+
+      if (runp == NULL)
+       ERROR (gettext ("\
+section [%2d] '%s': unknown parent version '%s'\n"),
+              idx, section_name (ebl, idx), namelist->name);
+
+      namelist = namelist->next;
+    }
 }
 
 
@@ -2044,6 +2515,7 @@ zeroth section has nonzero link value while ELF header does not signal overflow
 
   bool dot_interp_section = false;
 
+  size_t versym_scnndx = 0;
   for (size_t cnt = 1; cnt < shnum; ++cnt)
     {
       shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem);
@@ -2298,7 +2770,7 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
                   cnt, section_name (ebl, cnt));
          /* FALLTHROUGH */
        case SHT_SYMTAB:
-         check_symtab (ebl, ehdr, cnt);
+         check_symtab (ebl, ehdr, shdr, cnt);
          break;
 
        case SHT_RELA:
@@ -2330,11 +2802,16 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
          break;
 
        case SHT_GNU_versym:
-         check_versym (ebl, shdr, cnt);
+         /* We cannot process this section now since we have no guarantee
+            that the verneed and verdef sections have already been read.
+            Just remember the section index.  */
+         if (versym_scnndx != 0)
+           ERROR (gettext ("more than one version symbol table present\n"));
+         versym_scnndx = cnt;
          break;
 
        case SHT_GNU_verneed:
-         check_verneed (ebl, shdr, cnt);
+         check_verneed (ebl, ehdr, shdr, cnt);
          break;
 
        case SHT_GNU_verdef:
@@ -2350,6 +2827,39 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
   if (has_interp_segment && !dot_interp_section)
     ERROR (gettext ("INTERP program header entry but no .interp section\n"));
 
+  if (version_namelist != NULL)
+    {
+      if (versym_scnndx == 0)
+    ERROR (gettext ("\
+no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n"));
+      else
+       check_versym (ebl, versym_scnndx);
+
+      /* Check for duplicate index numbers.  */
+      do
+       {
+         struct version_namelist *runp = version_namelist->next;
+         while (runp != NULL)
+           {
+             if (version_namelist->ndx == runp->ndx)
+               {
+                 ERROR (gettext ("duplicate version index %d\n"),
+                        (int) version_namelist->ndx);
+                 break;
+               }
+             runp = runp->next;
+           }
+
+         struct version_namelist *old = version_namelist;
+         version_namelist = version_namelist->next;
+         free (old);
+       }
+      while (version_namelist != NULL);
+    }
+  else if (versym_scnndx != 0)
+    ERROR (gettext ("\
+.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n"));
+
   free (scnref);
 }
 
index 95532fa..7100b8e 100644 (file)
@@ -159,7 +159,7 @@ static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_scngrp (Ebl *ebl);
-static void print_dynamic (Ebl *ebl);
+static void print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr);
 static void print_relocs (Ebl *ebl);
 static void handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
 static void handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
@@ -479,7 +479,7 @@ process_elf_file (Elf *elf, const char *prefix, const char *fname,
   if (print_section_groups)
     print_scngrp (ebl);
   if (print_dynamic_table)
-    print_dynamic (ebl);
+    print_dynamic (ebl, ehdr);
   if (print_relocations)
     print_relocs (ebl);
   if (print_histogram)
@@ -1264,20 +1264,20 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 
 /* Print the dynamic segment.  */
 static void
-print_dynamic (Ebl *ebl)
+print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr)
 {
-  /* Find all relocation sections and handle them.  */
-  Elf_Scn *scn = NULL;
-
-  while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+  for (int i = 0; i < ehdr->e_phnum; ++i)
     {
-       /* Handle the section if it is a symbol table.  */
-      GElf_Shdr shdr_mem;
-      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      GElf_Phdr phdr_mem;
+      GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem);
 
-      if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
+      if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
        {
-         handle_dynamic (ebl, scn, shdr);
+         Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset);
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
+           handle_dynamic (ebl, scn, shdr);
          break;
        }
     }
@@ -1943,23 +1943,20 @@ get_ver_flags (unsigned int flags)
 static void
 handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 {
-  Elf_Data *data;
   int class = gelf_getclass (ebl->elf);
-  GElf_Shdr glink;
-  int cnt;
-  unsigned int offset;
-  size_t shstrndx;
 
   /* Get the data of the section.  */
-  data = elf_getdata (scn, NULL);
+  Elf_Data *data = elf_getdata (scn, NULL);
   if (data == NULL)
     return;
 
   /* Get the section header string table index.  */
+  size_t shstrndx;
   if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
     error (EXIT_FAILURE, 0,
           gettext ("cannot get section header string table index"));
 
+  GElf_Shdr glink;
   printf (ngettext ("\
 \nVersion needs section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
                    "\
@@ -1974,16 +1971,12 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
                      gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
                                    &glink)->sh_name));
 
-  offset = 0;
-  for (cnt = shdr->sh_info; --cnt >= 0; )
+  unsigned int offset = 0;
+  for (int cnt = shdr->sh_info; --cnt >= 0; )
     {
-      GElf_Verneed needmem;
-      GElf_Verneed *need;
-      unsigned int auxoffset;
-      int cnt2;
-
       /* Get the data at the next offset.  */
-      need = gelf_getverneed (data, offset, &needmem);
+      GElf_Verneed needmem;
+      GElf_Verneed *need = gelf_getverneed (data, offset, &needmem);
       if (need == NULL)
        break;
 
@@ -1992,13 +1985,11 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
              elf_strptr (ebl->elf, shdr->sh_link, need->vn_file),
              (unsigned short int) need->vn_cnt);
 
-      auxoffset = offset + need->vn_aux;
-      for (cnt2 = need->vn_cnt; --cnt2 >= 0; )
+      unsigned int auxoffset = offset + need->vn_aux;
+      for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
        {
          GElf_Vernaux auxmem;
-         GElf_Vernaux *aux;
-
-         aux = gelf_getvernaux (data, auxoffset, &auxmem);
+         GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem);
          if (aux == NULL)
            break;
 
@@ -2020,23 +2011,19 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 static void
 handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 {
-  Elf_Data *data;
-  int class = gelf_getclass (ebl->elf);
-  GElf_Shdr glink;
-  int cnt;
-  unsigned int offset;
-  size_t shstrndx;
-
   /* Get the data of the section.  */
-  data = elf_getdata (scn, NULL);
+  Elf_Data *data = elf_getdata (scn, NULL);
   if (data == NULL)
     return;
 
   /* Get the section header string table index.  */
+  size_t shstrndx;
   if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
     error (EXIT_FAILURE, 0,
           gettext ("cannot get section header string table index"));
 
+  int class = gelf_getclass (ebl->elf);
+  GElf_Shdr glink;
   printf (ngettext ("\
 \nVersion definition section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
                    "\
@@ -2052,23 +2039,18 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
                      gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
                                    &glink)->sh_name));
 
-  offset = 0;
-  for (cnt = shdr->sh_info; --cnt >= 0; )
+  unsigned int offset = 0;
+  for (int cnt = shdr->sh_info; --cnt >= 0; )
     {
-      GElf_Verdef defmem;
-      GElf_Verdef *def;
-      GElf_Verdaux auxmem;
-      GElf_Verdaux *aux;
-      unsigned int auxoffset;
-      int cnt2;
-
       /* Get the data at the next offset.  */
-      def = gelf_getverdef (data, offset, &defmem);
+      GElf_Verdef defmem;
+      GElf_Verdef *def = gelf_getverdef (data, offset, &defmem);
       if (def == NULL)
        break;
 
-      auxoffset = offset + def->vd_aux;
-      aux = gelf_getverdaux (data, auxoffset, &auxmem);
+      unsigned int auxoffset = offset + def->vd_aux;
+      GElf_Verdaux auxmem;
+      GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem);
       if (aux == NULL)
        break;
 
@@ -2081,7 +2063,7 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
              elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name));
 
       auxoffset += aux->vda_next;
-      for (cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
+      for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
        {
          aux = gelf_getverdaux (data, auxoffset, &auxmem);
          if (aux == NULL)
@@ -2103,34 +2085,28 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 static void
 handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 {
-  Elf_Data *data;
   int class = gelf_getclass (ebl->elf);
-  Elf_Scn *verscn;
   GElf_Shdr glink;
-  Elf_Scn *defscn;
-  Elf_Scn *needscn;
   const char **vername;
   const char **filename;
-  size_t nvername;
-  unsigned int cnt;
-  size_t shstrndx;
 
   /* Get the data of the section.  */
-  data = elf_getdata (scn, NULL);
+  Elf_Data *data = elf_getdata (scn, NULL);
   if (data == NULL)
     return;
 
   /* Get the section header string table index.  */
+  size_t shstrndx;
   if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
     error (EXIT_FAILURE, 0,
           gettext ("cannot get section header string table index"));
 
   /* We have to find the version definition section and extract the
      version names.  */
-  defscn = NULL;
-  needscn = NULL;
+  Elf_Scn *defscn = NULL;
+  Elf_Scn *needscn = NULL;
 
-  verscn = NULL;
+  Elf_Scn *verscn = NULL;
   while ((verscn = elf_nextscn (ebl->elf, verscn)) != NULL)
     {
       GElf_Shdr vershdr_mem;
@@ -2145,6 +2121,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
        }
     }
 
+  size_t nvername;
   if (defscn != NULL || needscn != NULL)
     {
       /* We have a version information (better should have).  Now get
@@ -2167,7 +2144,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
          if (defshdr == NULL)
            return;
 
-         for (cnt = 0; cnt < defshdr->sh_info; ++cnt)
+         for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt)
            {
              GElf_Verdef defmem;
              GElf_Verdef *def;
@@ -2197,7 +2174,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
          if (needshdr == NULL)
            return;
 
-         for (cnt = 0; cnt < needshdr->sh_info; ++cnt)
+         for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt)
            {
              GElf_Verneed needmem;
              GElf_Verneed *need;
@@ -2255,7 +2232,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
          if (defshdr == NULL)
            return;
 
-         for (cnt = 0; cnt < defshdr->sh_info; ++cnt)
+         for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt)
            {
              GElf_Verdef defmem;
              GElf_Verdef *def;
@@ -2293,7 +2270,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
          if (needshdr == NULL)
            return;
 
-         for (cnt = 0; cnt < needshdr->sh_info; ++cnt)
+         for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt)
            {
              GElf_Verneed needmem;
              GElf_Verneed *need;
@@ -2352,7 +2329,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
                                    &glink)->sh_name));
 
   /* Now we can finally look at the actual contents of this section.  */
-  for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+  for (unsigned int cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
     {
       GElf_Versym symmem;
       GElf_Versym *sym;