Updated support for new hash table format.

Fix handling of discarded COMDAT symbols in ld.
diff --git a/NEWS b/NEWS
index d2071d4..2edfbb3 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@
 elflint: fix and extend DT_RELCOUNT/DT_RELACOUNT checks
 
 elflint, readelf: add support for DT_GNU_HASH
+libelf: add elf_gnu_hash
 
 elflint, readelf: add support for 64-bit SysV-style hash tables
 
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index 23a2153..c4f5c65 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,9 @@
+2006-07-06  Ulrich Drepper  <drepper@redhat.com>
+
+	* ebldynamictagname.c: Add support for DT_GNU_HASH.
+	* ebldynamictagcheck.c: Likewise.
+	* eblsectiontypename.c: Add support for SHT_GNU_HASH.
+
 2006-07-05  Ulrich Drepper  <drepper@redhat.com>
 
 	* Makefile.am (gen_SOURCES): Add eblsysvhashentrysize.c.
diff --git a/libebl/ebldynamictagcheck.c b/libebl/ebldynamictagcheck.c
index b082e83..1953a9c 100644
--- a/libebl/ebldynamictagcheck.c
+++ b/libebl/ebldynamictagcheck.c
@@ -1,5 +1,5 @@
 /* Check dynamic tag.
-   Copyright (C) 2001, 2002 Red Hat, Inc.
+   Copyright (C) 2001, 2002, 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -66,7 +66,7 @@
   if (!res
       && ((tag >= 0 && tag < DT_NUM)
 	  || (tag >= DT_GNU_PRELINKED && tag <= DT_SYMINENT)
-	  || (tag >= DT_GNU_CONFLICT && tag <= DT_SYMINFO)
+	  || (tag >= DT_GNU_HASH && tag <= DT_SYMINFO)
 	  || tag == DT_VERSYM
 	  || (tag >= DT_RELACOUNT && tag <= DT_VERNEEDNUM)
 	  || tag == DT_AUXILIARY
diff --git a/libebl/ebldynamictagname.c b/libebl/ebldynamictagname.c
index 1267758..d9aa7df 100644
--- a/libebl/ebldynamictagname.c
+++ b/libebl/ebldynamictagname.c
@@ -1,5 +1,5 @@
 /* Return dynamic tag name.
-   Copyright (C) 2001, 2002 Red Hat, Inc.
+   Copyright (C) 2001, 2002, 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -96,15 +96,16 @@
 
 	  res = valrntags[tag - DT_GNU_PRELINKED];
 	}
-      else if (tag >= DT_GNU_CONFLICT && tag <= DT_SYMINFO)
+      else if (tag >= DT_GNU_HASH && tag <= DT_SYMINFO)
 	{
 	  static const char *addrrntags[] =
 	    {
+	      "GNU_HASH", "TLSDESC_PLT", "TLSDESC_DOT",
 	      "GNU_CONFLICT", "GNU_LIBLIST", "CONFIG", "DEPAUDIT", "AUDIT",
 	      "PLTPAD", "MOVETAB", "SYMINFO"
 	    };
 
-	  res = addrrntags[tag - DT_GNU_CONFLICT];
+	  res = addrrntags[tag - DT_GNU_HASH];
 	}
       else if (tag >= DT_RELACOUNT && tag <= DT_VERNEEDNUM)
 	{
diff --git a/libebl/eblrelativerelocp.c b/libebl/eblrelativerelocp.c
new file mode 100644
index 0000000..8ea97b8
--- /dev/null
+++ b/libebl/eblrelativerelocp.c
@@ -0,0 +1,64 @@
+/* Check whether given relocation is a relocation relocation.
+   Copyright (C) 2006 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+   Written by Ulrich Drepper <drepper@redhat.com>, 2006.
+
+   Red Hat elfutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by the
+   Free Software Foundation; version 2 of the License.
+
+   Red Hat elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+
+bool
+ebl_relative_reloc_p (ebl, reloc)
+     Ebl *ebl;
+     int reloc;
+{
+  return ebl->relative_reloc_p (reloc);
+}
diff --git a/libebl/eblsectiontypename.c b/libebl/eblsectiontypename.c
index 18cbd76..b62c37b 100644
--- a/libebl/eblsectiontypename.c
+++ b/libebl/eblsectiontypename.c
@@ -1,5 +1,5 @@
 /* Return section type name.
-   Copyright (C) 2001, 2002 Red Hat, Inc.
+   Copyright (C) 2001, 2002, 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -116,6 +116,8 @@
 	    res = "CHECKSUM";
 	  else if (section == SHT_GNU_LIBLIST)
 	    res = "GNU_LIBLIST";
+	  else if (section == SHT_GNU_HASH)
+	    res = "GNU_HASH";
 	  /* Handle OS-specific section names.  */
 	  else
 	    {
diff --git a/libebl/eblsysvhashentrysize.c b/libebl/eblsysvhashentrysize.c
new file mode 100644
index 0000000..341979c
--- /dev/null
+++ b/libebl/eblsysvhashentrysize.c
@@ -0,0 +1,63 @@
+/* Return OS ABI name
+   Copyright (C) 2006 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+   Written by Ulrich Drepper <drepper@redhat.com>, 2006.
+
+   Red Hat elfutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by the
+   Free Software Foundation; version 2 of the License.
+
+   Red Hat elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+
+int
+ebl_sysvhash_entrysize (ebl)
+     Ebl *ebl;
+{
+  return ebl->sysvhash_entrysize;
+}
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index d42bf18..db4108d 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,10 @@
+2006-07-06  Ulrich Drepper  <drepper@redhat.com>
+
+	* elf_gnu_hash.c: New file.
+	* libelf.h: Declare elf_gnu_hash.
+	* Makefile.am (libelf_a_SOURCES): Add elf_gnu_hash.
+	* libelf.map: Add elf_gnu_map for version ELFUTILS_1.2.
+
 2006-05-28  Ulrich Drepper  <drepper@redhat.com>
 
 	* elf32_updatefile.c (updatemmap): Preserve section content if
diff --git a/libelf/Makefile.am b/libelf/Makefile.am
index 51965ce..fb177d0 100644
--- a/libelf/Makefile.am
+++ b/libelf/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 1996-2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+## Copyright (C) 1996-2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
 ## This file is part of Red Hat elfutils.
 ##
 ## Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -96,7 +96,8 @@
 		   elf_clone.c \
 		   gelf_getlib.c gelf_update_lib.c \
 		   elf32_offscn.c elf64_offscn.c gelf_offscn.c \
-		   elf_getaroff.c
+		   elf_getaroff.c \
+		   elf_gnu_hash.c
 
 if !MUDFLAP
 libelf_pic_a_SOURCES =
diff --git a/libelf/elf_gnu_hash.c b/libelf/elf_gnu_hash.c
new file mode 100644
index 0000000..efaee43
--- /dev/null
+++ b/libelf/elf_gnu_hash.c
@@ -0,0 +1,68 @@
+/* GNU-style Hash function used in ELF implementations.
+   Copyright (C) 2006 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
+
+   Red Hat elfutils is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by the
+   Free Software Foundation; version 2 of the License.
+
+   Red Hat elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libelfP.h>
+
+/* Get the implementation.  */
+#include <dl-hash.h>
+
+unsigned long int
+elf_gnu_hash (string)
+     const char *string;
+{
+  uint_fast32_t h = 5381;
+  for (unsigned char c = *string; c != '\0'; c = *++string)
+    h = h * 33 + c;
+  return h & 0xffffffff;
+}
diff --git a/libelf/libelf.h b/libelf/libelf.h
index 7d7d39e..2727184 100644
--- a/libelf/libelf.h
+++ b/libelf/libelf.h
@@ -1,5 +1,5 @@
 /* Interface for libelf.
-   Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 1998, 1999, 2000, 2002, 2004, 2005, 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -371,6 +371,10 @@
 extern unsigned long int elf_hash (const char *__string)
        __attribute__ ((__pure__));
 
+/* Compute hash value using the GNU-specific hash function.  */
+extern unsigned long int elf_gnu_hash (const char *__string)
+       __attribute__ ((__pure__));
+
 
 /* Compute simple checksum from permanent parts of the ELF file.  */
 extern long int elf32_checksum (Elf *__elf);
diff --git a/libelf/libelf.map b/libelf/libelf.map
index b2a65e8..9549c31 100644
--- a/libelf/libelf.map
+++ b/libelf/libelf.map
@@ -112,3 +112,8 @@
     gelf_offscn;
     elf_getaroff;
 } ELFUTILS_1.1;
+
+ELFUTILS_1.2 {
+  global:
+    elf_gnu_hash;
+} ELFUTILS_1.1.1;
diff --git a/src/ChangeLog b/src/ChangeLog
index b75a766..1c153b0 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,11 @@
+2006-07-06  Ulrich Drepper  <drepper@redhat.com>
+
+	* elflint.c: Adjust for latest new hash table format.
+	* readelf.c: Likewise.
+
+	* elflint.c (check_versym): Ignore hidden bit when comparing version
+	numbers.
+
 2006-07-05  Ulrich Drepper  <drepper@redhat.com>
 
 	* ldgeneric.c (ld_generic_create_outfile): Correctly recognize
diff --git a/src/elflint.c b/src/elflint.c
index a3d7d5c..a679acc 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -1895,19 +1895,19 @@
 check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
 		GElf_Shdr *symshdr)
 {
-  Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+  Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0];
   Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
 
-  if (shdr->sh_size < (2 + nbucket) * shdr->sh_entsize)
+  if (shdr->sh_size < (2 + 2 * nbuckets) * sizeof (Elf32_Word))
     {
       ERROR (gettext ("\
 section [%2d] '%s': hash table section is too small (is %ld, expected at least%ld)\n"),
 	     idx, section_name (ebl, idx), (long int) shdr->sh_size,
-	     (long int) ((2 + nbucket) * shdr->sh_entsize));
+	     (long int) ((2 + 2 * nbuckets) * sizeof (Elf32_Word)));
       return;
     }
 
-  size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + nbucket);
+  size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + 2 * nbuckets);
 
   if (symshdr != NULL)
     maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize);
@@ -1916,38 +1916,82 @@
   Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL);
 
   size_t cnt;
-  for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+  for (cnt = 2; cnt < 2 + 2 * nbuckets; cnt += 2)
     {
-      Elf32_Word chainidx = ((Elf32_Word *) data->d_buf)[cnt];
+      Elf32_Word bitset = ((Elf32_Word *) data->d_buf)[cnt];
+      Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt + 1];
 
-      if (chainidx == ~0u)
-	/* Nothing in here.  */
-	continue;
+      if (symidx == 0)
+	{
+	  /* Nothing in here.  No bit in the bitset should be set either.  */
+	  if (bitset != 0)
+	    ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu empty but bitset is not\n"),
+		   idx, section_name (ebl, idx), cnt / 2 - 1);
 
-      while (chainidx < maxidx
-	     && ((((Elf32_Word *) data->d_buf)[2 + nbucket + chainidx] & 1)
-		 == 0))
-	++chainidx;
+	  continue;
+	}
 
-      if (chainidx >= maxidx)
+      if (symidx < symbias)
+	{
+	  ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu lower than symbol index bias\n"),
+		 idx, section_name (ebl, idx), cnt / 2 - 1);
+	  continue;
+	}
+
+      Elf32_Word collected_bitset = 0;
+      while (symidx - symbias < maxidx)
+	{
+	  Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[2 + 2 * nbuckets
+							      + symidx
+							      - symbias];
+
+	  if (symdata != NULL)
+	    {
+	      /* Check that the referenced symbol is not undefined.  */
+	      GElf_Sym sym_mem;
+	      GElf_Sym *sym = gelf_getsym (symdata, symidx, &sym_mem);
+	      if (sym != NULL && sym->st_shndx == SHN_UNDEF)
+		ERROR (gettext ("\
+section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"),
+		       idx, section_name (ebl, idx), symidx, cnt / 2 - 1);
+
+	      const char *symname = elf_strptr (ebl->elf, symshdr->sh_link,
+						sym->st_name);
+	      if (symname != NULL)
+		{
+		  Elf32_Word hval = elf_gnu_hash (symname);
+		  if ((hval & ~1u) != (chainhash & ~1u))
+		    ERROR (gettext ("\
+section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"),
+			   idx, section_name (ebl, idx), symidx, cnt / 2 - 1);
+		}
+	    }
+
+	  collected_bitset |= 1 << ((chainhash >> 5) & 31);
+
+	  if ((chainhash & 1) != 0)
+	    break;
+
+	  ++symidx;
+	}
+
+      if (symidx - symbias >= maxidx)
 	ERROR (gettext ("\
 section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
-	       idx, section_name (ebl, idx), cnt - 2);
+	       idx, section_name (ebl, idx), cnt / 2 - 1);
       else if (symshdr != NULL
-	       && symbias + chainidx > symshdr->sh_size / symshdr->sh_entsize)
+	       && symidx > symshdr->sh_size / symshdr->sh_entsize)
 	ERROR (gettext ("\
 section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"),
-	       idx, section_name (ebl, idx), cnt - 2);
-      else if (symdata != NULL)
-	{
-	  /* Check that the referenced symbol is not undefined.  */
-	  GElf_Sym sym_mem;
-	  GElf_Sym *sym = gelf_getsym (symdata, symbias + cnt - 2, &sym_mem);
-	  if (sym != NULL && sym->st_shndx == SHN_UNDEF)
+	       idx, section_name (ebl, idx), cnt / 2 - 1);
+
+      if (bitset != collected_bitset)
 	ERROR (gettext ("\
-section [%2d] '%s': symbol reference in chain for bucket %zu is undefined\n"),
-	       idx, section_name (ebl, idx), cnt - 2);
-	}
+section [%2d] '%s': bitset for bucket %zu does not match chain entries: computed %#x, reported %#x\n"),
+	       idx, section_name (ebl, idx), cnt / 2 - 1,
+	       collected_bitset, bitset);
     }
 }
 
@@ -1979,9 +2023,11 @@
 section [%2d] '%s': hash table not for dynamic symbol table\n"),
 	   idx, section_name (ebl, idx));
 
-  if (shdr->sh_entsize != sizeof (Elf32_Word))
+  if (shdr->sh_entsize != (tag == SHT_GNU_HASH
+			   ? sizeof (Elf32_Word)
+			   : (size_t) ebl_sysvhash_entrysize (ebl)))
     ERROR (gettext ("\
-section [%2d] '%s': entry size does not match Elf32_Word\n"),
+section [%2d] '%s': hash table entry size incorrect\n"),
 	   idx, section_name (ebl, idx));
 
   if ((shdr->sh_flags & SHF_ALLOC) == 0)
@@ -1991,7 +2037,7 @@
   if (shdr->sh_size < 2 * shdr->sh_entsize)
     {
       ERROR (gettext ("\
-section [%2d] '%s': hash table has not even room for nbucket and nchain\n"),
+section [%2d] '%s': hash table has not even room for initial two administrative entries\n"),
 	     idx, section_name (ebl, idx));
       return;
     }
@@ -2399,7 +2445,7 @@
 	     index we need for this symbol.  */
 	  struct version_namelist *runp = version_namelist;
 	  while (runp != NULL)
-	    if (runp->ndx == *versym)
+	    if (runp->ndx == (*versym & 0x7fff))
 	      break;
 	    else
 	      runp = runp->next;
diff --git a/src/i386_ld.c b/src/i386_ld.c
index 60e45f3..c79804c 100644
--- a/src/i386_ld.c
+++ b/src/i386_ld.c
@@ -149,8 +149,8 @@
 	  assert (xndx < SHN_LORESERVE || xndx > SHN_HIRESERVE);
 
 	  /* We fortunately don't have to do much.  The relocations
-	     mostly get only updates of the offset.  Only is a
-	     relocation referred to a section do we have to do
+	     mostly get only updates of the offset.  Only for a
+	     relocation referring to a section do we have to do
 	     something.  In this case the reference to the sections
 	     has no direct equivalent since the part the input section
 	     contributes need not start at the same offset as in the
diff --git a/src/ldgeneric.c b/src/ldgeneric.c
index 6913d67..d282a1d 100644
--- a/src/ldgeneric.c
+++ b/src/ldgeneric.c
@@ -1436,9 +1436,9 @@
 	     _GLOBAL_OFFSET_TABLE_, _DYNAMIC.  */
 	  // XXX This loop is hot and the following tests hardly ever match.
 	  // XXX Maybe move the tests somewhere they are executed less often.
-	  if (((unlikely (hval == 165832675)
+	  if (((unlikely (hval == 165832675ul)
 		&& strcmp (search.name, "_DYNAMIC") == 0)
-	       || (unlikely (hval == 102264335)
+	       || (unlikely (hval == 102264335ul)
 		   && strcmp (search.name, "_GLOBAL_OFFSET_TABLE_") == 0))
 	      && sym->st_shndx != SHN_UNDEF
 	      /* If somebody defines such a variable in a relocatable we
@@ -1451,7 +1451,7 @@
 	  struct symbol *newp;
 	  if (likely (oldp == NULL))
 	    {
-	      /* No symbol of this name know.  Add it.  */
+	      /* No symbol of this name known.  Add it.  */
 	      newp = (struct symbol *) obstack_alloc (&ld_state.smem,
 						      sizeof (*newp));
 	      newp->name = search.name;
@@ -1467,6 +1467,8 @@
 	      newp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK;
 	      newp->added = 0;
 	      newp->merged = 0;
+	      newp->local = 0;
+	      newp->hidden = 0;
 	      newp->need_copy = 0;
 	      newp->on_dsolist = 0;
 	      newp->in_dso = secttype == SHT_DYNSYM;
@@ -4767,6 +4769,7 @@
 	  /* Once we know the name this field will get the correct
 	     offset.  For now set it to zero which means no name
 	     associated.  */
+	  GElf_Word st_name = sym->st_name;
 	  sym->st_name = 0;
 
 	  /* If we had to merge sections we have a completely new
@@ -4783,24 +4786,44 @@
 	     Find the symbol if this has not happened yet.  We do
 	     not need the information for local symbols.  */
 	  if (defp == NULL && cnt >= file->nlocalsymbols)
-	    defp = file->symref[cnt];
-
-	  /* Ignore symbols in discarded COMDAT group sections.  */
-	  if (defp != NULL || cnt < file->nlocalsymbols)
 	    {
-	      /* Store the reference to the symbol record.  The
-		 sorting code will have to keep this array in the
-		 correct order, too.  */
-	      ndxtosym[nsym] = defp;
+	      defp = file->symref[cnt];
 
-	      /* One more entry finished.  */
-	      if (cnt >= file->nlocalsymbols)
+	      if (defp == NULL)
 		{
-		  assert (file->symref[cnt]->outsymidx == 0);
-		  file->symref[cnt]->outsymidx = nsym;
+		  /* This is a symbol in a discarded COMDAT section.
+		     Find the definition we actually use.  */
+		  // XXX The question is: do we have to do this here
+		  // XXX or can we do it earlier when we discard the
+		  // XXX section.
+		  struct symbol search;
+		  search.name = elf_strptr (file->elf, file->symstridx,
+					    st_name);
+		  struct symbol *realp
+		    = ld_symbol_tab_find (&ld_state.symbol_tab,
+					  elf_hash (search.name), &search);
+		  if (realp == NULL)
+		    // XXX What to do here?
+		    error (EXIT_FAILURE, 0,
+			   "couldn't find symbol from COMDAT section");
+
+		  file->symref[cnt] = realp;
+
+		  continue;
 		}
-	      file->symindirect[cnt] = nsym++;
 	    }
+
+	  /* Store the reference to the symbol record.  The sorting
+	     code will have to keep this array in the correct order, too.  */
+	  ndxtosym[nsym] = defp;
+
+	  /* One more entry finished.  */
+	  if (cnt >= file->nlocalsymbols)
+	    {
+	      assert (file->symref[cnt]->outsymidx == 0);
+	      file->symref[cnt]->outsymidx = nsym;
+	    }
+	  file->symindirect[cnt] = nsym++;
 	}
     }
   while ((file = file->next) != ld_state.relfiles->next);
diff --git a/src/readelf.c b/src/readelf.c
index b4b6a5a..9fbc24d 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2480,17 +2480,18 @@
     }
 
   Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+  Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
   Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2];
-  Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket];
+  Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + 2 * nbucket];
 
   uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
 
   uint_fast32_t maxlength = 0;
   uint_fast32_t nsyms = 0;
   for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
-    if (bucket[cnt] != ~0u)
+    if (bucket[2 * cnt + 1] != 0)
       {
-	Elf32_Word inner = bucket[cnt];
+	Elf32_Word inner = bucket[2 * cnt + 1] - symbias;
 	do
 	  {
 	    ++nsyms;