]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Linker work.
authorUlrich Drepper <drepper@redhat.com>
Wed, 5 Jul 2006 07:34:27 +0000 (07:34 +0000)
committerUlrich Drepper <drepper@redhat.com>
Wed, 5 Jul 2006 07:34:27 +0000 (07:34 +0000)
GNU hash support.
64-bit SysV hash support.

24 files changed:
ChangeLog
NEWS
backends/ChangeLog
backends/alpha_init.c
backends/common-reloc.c
backends/ia64_reloc.def
backends/s390_init.c
configure.ac
libebl/ChangeLog
libebl/Makefile.am
libebl/ebl-hooks.h
libebl/eblnonerelocp.c
libebl/eblopenbackend.c
libebl/libebl.h
libebl/libeblP.h
libelf/elf.h
src/ChangeLog
src/Makefile.am
src/elflint.c
src/i386_ld.c
src/ld.c
src/ld.h
src/ldgeneric.c
src/readelf.c

index 1932f30f3703fc48af61974e72c4a09177171bc2..ece0af15b27b13237eb6a88b90c766680332b76f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2006-07-05  Ulrich Drepper  <drepper@redhat.com>
+
+       * configure.ac: Add dummy automake conditional to get dependencies
+       for non-generic linker right.  See src/Makefile.am.
+
 2005-11-18  Roland McGrath  <roland@redhat.com>
 
        * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): New variable.
diff --git a/NEWS b/NEWS
index 486cd7ed29b7b77d8e0e020303e616331233b178..d2071d46e14bd13df85d95c3005c4ebbb44d7c09 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,13 @@
+Version 0.122:
+
+libebl:add function to test for relative relocation
+
+elflint: fix and extend DT_RELCOUNT/DT_RELACOUNT checks
+
+elflint, readelf: add support for DT_GNU_HASH
+
+elflint, readelf: add support for 64-bit SysV-style hash tables
+
 Version 0.121:
 
 libelf: bug fixes for rewriting existing files when using mmap.
index b5ae13045358c9abb7eb19944ec1241b62565eaf..e741633278f62db475b272d687d03eba5390788d 100644 (file)
@@ -1,3 +1,14 @@
+2006-07-05  Ulrich Drepper  <drepper@redhat.com>
+
+       * alpha_init.c: Initialize sysvhash_entrysize.
+       * s390_init.c: Likewise.
+
+2006-07-04  Ulrich Drepper  <drepper@redhat.com>
+
+       * common-reloc.c (relative_reloc_p): New function.
+       (init_reloc): Hook it up.
+       * ia64_reloc.def: Define NO_RELATIVE_RELOC.
+
 2006-06-13  Roland McGrath  <roland@redhat.com>
 
        * ppc64_retval.c: Remove SVR4_STRUCT_RETURN braino.
@@ -5,7 +16,7 @@
 2006-06-12  Ulrich Drepper  <drepper@redhat.com>
 
        * common-reloc.c (none_reloc_p): New function.
-       (init_reloc): Hoop it up.
+       (init_reloc): Hook it up.
 
 2006-02-22  Roland McGrath  <roland@redhat.com>
 
index 9840028074f3fc6c140ec01ea8831f5c3c2b7bcf..1590fc88e4c87ad7f5ff93db052c5dd4a0346516 100644 (file)
@@ -1,5 +1,5 @@
 /* Initialization of Alpha specific backend library.
-   Copyright (C) 2002, 2005 Red Hat, Inc.
+   Copyright (C) 2002, 2005, 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -54,6 +54,7 @@ alpha_init (elf, machine, eh, ehlen)
   HOOK (eh, dynamic_tag_check);
   HOOK (eh, reloc_simple_type);
   HOOK (eh, return_value_location);
+  eh->sysvhash_entrysize = sizeof (Elf64_Xword);
 
   return MODVERSION;
 }
index 9b956557c4ae013a5fce9204aba261b4c06a5d9f..191b22a2b13d6437568112a53e684ca2d90d3b01 100644 (file)
@@ -121,6 +121,14 @@ EBLHOOK(none_reloc_p) (int reloc)
   return reloc == R_TYPE (NONE);
 }
 
+#ifndef NO_RELATIVE_RELOC
+bool
+EBLHOOK(relative_reloc_p) (int reloc)
+{
+  return reloc == R_TYPE (RELATIVE);
+}
+#endif
+
 static void
 EBLHOOK(init_reloc) (Ebl *ebl)
 {
@@ -129,4 +137,7 @@ EBLHOOK(init_reloc) (Ebl *ebl)
   ebl->reloc_valid_use = EBLHOOK(reloc_valid_use);
   ebl->copy_reloc_p = EBLHOOK(copy_reloc_p);
   ebl->none_reloc_p = EBLHOOK(none_reloc_p);
+#ifndef NO_RELATIVE_RELOC
+  ebl->relative_reloc_p = EBLHOOK(relative_reloc_p);
+#endif
 }
index 0dc001da9f33995635a1cfce6071c509aa6e8605..a423465623cee8f2b510931ffa89c75a5d92f06a 100644 (file)
@@ -1,5 +1,5 @@
 /* List the relocation types for ia64.  -*- C -*-
-   Copyright (C) 2005 Red Hat, Inc.
+   Copyright (C) 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
@@ -106,3 +106,5 @@ RELOC_TYPE (DTPREL32LSB,    REL|EXEC|DYN)
 RELOC_TYPE (DTPREL64MSB,       REL|EXEC|DYN)
 RELOC_TYPE (DTPREL64LSB,       REL|EXEC|DYN)
 RELOC_TYPE (LTOFF_DTPREL22,    REL)
+
+#define NO_RELATIVE_RELOC      1
index 64e5639bdd3766cdd2d888ab14a9a6c0701c9155..80cbb8329930d06cd19e0bc935f1c81a2ff8ae81 100644 (file)
@@ -53,5 +53,9 @@ s390_init (elf, machine, eh, ehlen)
   HOOK (eh, register_name);
   HOOK (eh, return_value_location);
 
+  /* Only the 64-bit format uses the incorrect hash table entry size.  */
+  if (eh->class == ELFCLASS64)
+    eh->sysvhash_entrysize = sizeof (Elf64_Xword);
+
   return MODVERSION;
 }
index c03e8a9209f1f1e1d5f5add2d71290ee08319026..ec6286edb98ecfc47594ed0683014410c3d00d27 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl Configure input file for elfutils.                     -*-autoconf-*-
 dnl
-dnl Copyright (C) 1996-2002, 2003, 2004, 2005 Red Hat, Inc.
+dnl Copyright (C) 1996-2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
 dnl
 dnl This program is free software; you can redistribute it and/or modify
 dnl it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public License
 dnl along with this program; if not, write to the Free Software Foundation,
 dnl Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 dnl
-AC_INIT([Red Hat elfutils],[0.121],[http://bugzilla.redhat.com/bugzilla/],
+AC_INIT([Red Hat elfutils],[0.122],[http://bugzilla.redhat.com/bugzilla/],
        [elfutils])
 
 AC_CONFIG_AUX_DIR([config])
@@ -108,6 +108,9 @@ dnl of files if at any time there is no such file, even if the filename
 dnl would not be used.
 AS_IF([test -z "$base_cpu"], [base_cpu=none])
 AC_SUBST(base_cpu)
+dnl Support to work around automake's inflexible dependency generation.
+dnl See src/Makefile.am for more information.
+AM_CONDITIONAL(NEVER, false)
 
 dnl Enable debugging via mudflap.  This option will cause most libraries
 dnl to be built as archives which are statically linked into the applications.
index 1d9fcfd52174a0f6c4c4456dcbe6028137a9a76f..23a215359762364ae60b7c3c3b4eb3b1428554e3 100644 (file)
@@ -1,3 +1,21 @@
+2006-07-05  Ulrich Drepper  <drepper@redhat.com>
+
+       * Makefile.am (gen_SOURCES): Add eblsysvhashentrysize.c.
+       * libeblP.h (struct ebl): Add sysvhash_entrysize element.
+       * eblopenbackend.c (fill_defaults): Initialize sysvhash_entrysize.
+
+       * eblopenbackend.c (openbackend): If possible, fill machine, class,
+       and data values in from the ELF file.
+
+2006-07-04  Ulrich Drepper  <drepper@redhat.com>
+
+       * Makefile.am (gen_SOURCES): Add eblrelativerelocp.c.
+       * eblrelativerelocp.c: New file.
+       * ebl-hooks.c: Add relative_reloc_p.
+       * eblopenbackend.c (default_relative_reloc_p): New function.
+       (fill_defaults): Hook it up.
+       * libebl.h: Declare ebl_relative_reloc_p.
+
 2006-06-12  Ulrich Drepper  <drepper@redhat.com>
 
        * Makefile.am (gen_SOURCES): Add eblnonerelocp.c.
index 982692723c6ef402152980b9aaab65ec5cefb3e0..278d93e0dfc1bb0731cb91ad1683f03b88a52949 100644 (file)
@@ -56,7 +56,8 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \
              eblgotpcreloccheck.c eblcopyrelocp.c eblsectionstripp.c \
              eblelfclass.c eblelfdata.c eblelfmachine.c \
              ebl_check_special_symbol.c eblbsspltp.c eblretval.c \
-             eblregname.c eblnonerelocp.c
+             eblregname.c eblnonerelocp.c eblrelativerelocp.c \
+             eblsysvhashentrysize.c
 
 libebl_a_SOURCES = $(gen_SOURCES)
 
index 4227c24fc55c8ddcd5f008266fedd9f1093cdc83..e4923b2ede322c7f1241e62f6437ba8ed0f19ecf 100644 (file)
@@ -119,6 +119,9 @@ bool EBLHOOK(copy_reloc_p) (int);
 /* Check whether given relocation is a no-op relocation.  */
 bool EBLHOOK(none_reloc_p) (int);
 
+/* Check whether given relocation is a relative relocation.  */
+bool EBLHOOK(relative_reloc_p) (int);
+
 /* Check whether given symbol's value is ok despite normal checks.  */
 bool EBLHOOK(check_special_symbol) (Elf *, GElf_Ehdr *, const GElf_Sym *,
                              const char *, const GElf_Shdr *);
index 3d62a0b479b817fb6866c90d723b99ba971475e7..3bca98b1c5fa783960954947886045ece63aec3c 100644 (file)
@@ -1,4 +1,4 @@
-/* Check whether given relocation is a copy relocation.
+/* Check whether given relocation is a no-op relocation.
    Copyright (C) 2006 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2006.
index 092068cb0500dd61457ddda471c6f4889ecb01a2..5916e0de3e69f30624a2909bfab2d58d375d9f4b 100644 (file)
@@ -54,7 +54,7 @@
 #include <assert.h>
 #include <dlfcn.h>
 #include <error.h>
-#include <gelf.h>
+#include <libelfP.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -89,6 +89,7 @@ static const struct
   { "sparc", "elf_sparcv9", "sparc", 5, EM_SPARCV9, 0, 0 },
   { "sparc", "elf_sparc", "sparc", 5, EM_SPARC, 0, 0 },
   { "sparc", "elf_sparcv8plus", "sparc", 5, EM_SPARC32PLUS, 0, 0 },
+  { "s390", "ebl_s390", "s390", 4, EM_S390, 0, 0 },
 
   { "m32", "elf_m32", "m32", 3, EM_M32, 0, 0 },
   { "m68k", "elf_m68k", "m68k", 4, EM_68K, 0, 0 },
@@ -99,7 +100,6 @@ static const struct
   { "vpp500", "elf_vpp500", "vpp500", 5, EM_VPP500, 0, 0 },
   { "sparc", "elf_v8plus", "v8plus", 6, EM_SPARC32PLUS, 0, 0 },
   { "i960", "elf_i960", "i960", 4, EM_960, 0, 0 },
-  { "s390", "ebl_s390", "s390", 4, EM_S390, 0, 0 },
   { "v800", "ebl_v800", "v800", 4, EM_V800, 0, 0 },
   { "fr20", "ebl_fr20", "fr20", 4, EM_FR20, 0, 0 },
   { "rh32", "ebl_rh32", "rh32", 4, EM_RH32, 0, 0 },
@@ -191,6 +191,7 @@ static bool default_object_note (const char *name, uint32_t type,
 static bool default_debugscn_p (const char *name);
 static bool default_copy_reloc_p (int reloc);
 static bool default_none_reloc_p (int reloc);
+static bool default_relative_reloc_p (int reloc);
 static bool default_check_special_symbol (Elf *elf, GElf_Ehdr *ehdr,
                                          const GElf_Sym *sym,
                                          const char *name,
@@ -231,11 +232,13 @@ fill_defaults (Ebl *result)
   result->debugscn_p = default_debugscn_p;
   result->copy_reloc_p = default_copy_reloc_p;
   result->none_reloc_p = default_none_reloc_p;
+  result->relative_reloc_p = default_relative_reloc_p;
   result->check_special_symbol = default_check_special_symbol;
   result->bss_plt_p = default_bss_plt_p;
   result->return_value_location = default_return_value_location;
   result->register_name = default_register_name;
   result->destr = default_destr;
+  result->sysvhash_entrysize = sizeof (Elf32_Word);
 }
 
 
@@ -280,9 +283,28 @@ openbackend (elf, emulation, machine)
        /* Well, we know the emulation name now.  */
        result->emulation = machines[cnt].emulation;
 
-       result->machine = machines[cnt].em;
-       result->class = machines[cnt].class;
-       result->data = machines[cnt].data;
+       /* We access some data structures directly.  Make sure the 32 and
+          64 bit variants are laid out the same.  */
+       assert (offsetof (Elf32_Ehdr, e_machine)
+               == offsetof (Elf64_Ehdr, e_machine));
+       assert (sizeof (((Elf32_Ehdr *) 0)->e_machine)
+               == sizeof (((Elf64_Ehdr *) 0)->e_machine));
+       assert (offsetof (Elf, state.elf32.ehdr)
+               == offsetof (Elf, state.elf64.ehdr));
+
+       /* Prefer taking the information from the ELF file.  */
+       if (elf == NULL)
+         {
+           result->machine = machines[cnt].em;
+           result->class = machines[cnt].class;
+           result->data = machines[cnt].data;
+         }
+       else
+         {
+           result->machine = elf->state.elf32.ehdr->e_machine;
+           result->class = elf->state.elf32.ehdr->e_ident[EI_CLASS];
+           result->data = elf->state.elf32.ehdr->e_ident[EI_DATA];
+         }
 
 #ifndef LIBEBL_SUBDIR
 # define LIBEBL_SUBDIR PACKAGE
@@ -608,6 +630,7 @@ default_copy_reloc_p (int reloc __attribute__ ((unused)))
   return false;
 }
 strong_alias (default_copy_reloc_p, default_none_reloc_p)
+strong_alias (default_copy_reloc_p, default_relative_reloc_p)
 
 static bool
 default_check_special_symbol (Elf *elf __attribute__ ((unused)),
index c27dfd4a67be4e353ef22f4d4ecd5292d8d70a3a..6969c550ea79f95c60a688cdb5e927fa5bd4f236 100644 (file)
@@ -192,6 +192,9 @@ extern bool ebl_copy_reloc_p (Ebl *ebl, int reloc);
 /* Check whether given relocation is a no-op relocation.  */
 extern bool ebl_none_reloc_p (Ebl *ebl, int reloc);
 
+/* Check whether given relocation is a relative relocation.  */
+extern bool ebl_relative_reloc_p (Ebl *ebl, int reloc);
+
 /* Check whether section should be stripped.  */
 extern bool ebl_section_strip_p (Ebl *ebl, const GElf_Ehdr *ehdr,
                                 const GElf_Shdr *shdr, const char *name,
@@ -200,6 +203,9 @@ extern bool ebl_section_strip_p (Ebl *ebl, const GElf_Ehdr *ehdr,
 /* Check if backend uses a bss PLT in this file.  */
 extern bool ebl_bss_plt_p (Ebl *ebl, GElf_Ehdr *ehdr);
 
+/* Return size of entry in SysV-style hash table.  */
+extern int ebl_sysvhash_entrysize (Ebl *ebl);
+
 /* Return location expression to find return value given a
    DW_TAG_subprogram, DW_TAG_subroutine_type, or similar DIE describing
    function itself (whose DW_AT_type attribute describes its return type).
index ff70b679038e84f0c8de7185ec2717890b63d530..7bb4c376f2fd6ae5e4028c44a94950a52ba6490a 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal definitions for interface for libebl.
-   Copyright (C) 2000, 2001, 2002, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 2000, 2001, 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
@@ -77,6 +77,9 @@ struct ebl
 # include "ebl-hooks.h"
 # undef EBLHOOK
 
+  /* Size of entry in Sysv-style hash table.  */
+  int sysvhash_entrysize;
+
   /* Internal data.  */
   void *dlhandle;
 };
@@ -87,6 +90,7 @@ typedef const char *(*ebl_bhinit_t) (Elf *, GElf_Half, Ebl *, size_t);
 
 
 /* gettext helper macros.  */
+#undef _
 #define _(Str) dgettext ("elfutils", Str)
 
 #endif /* libeblP.h */
index c98bb5233dab6e1846f019de0a48c93ab345f10a..57b099e2fc3f1ea790372df00e2e5603db957239 100644 (file)
@@ -1,5 +1,5 @@
 /* This file defines standard ELF types, structures, and macros.
-   Copyright (C) 1995-2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 1995-2003, 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -330,6 +330,7 @@ typedef struct
 #define SHT_SYMTAB_SHNDX  18           /* Extended section indeces */
 #define        SHT_NUM           19            /* Number of defined types.  */
 #define SHT_LOOS         0x60000000    /* Start OS-specific */
+#define SHT_GNU_HASH     0x6ffffff6    /* GNU style symbol hash table.  */
 #define SHT_GNU_LIBLIST          0x6ffffff7    /* Prelink library list */
 #define SHT_CHECKSUM     0x6ffffff8    /* Checksum for DSO content.  */
 #define SHT_LOSUNW       0x6ffffffa    /* Sun-specific low bound.  */
@@ -699,6 +700,9 @@ typedef struct
    If any adjustment is made to the ELF object after it has been
    built these entries will need to be adjusted.  */
 #define DT_ADDRRNGLO   0x6ffffe00
+#define DT_GNU_HASH    0x6ffffef5      /* Address of GNU symbol hash table */
+#define DT_TLSDESC_PLT 0x6ffffef6
+#define DT_TLSDESC_GOT 0x6ffffef7
 #define DT_GNU_CONFLICT        0x6ffffef8      /* Start of conflict section */
 #define DT_GNU_LIBLIST 0x6ffffef9      /* Library list */
 #define DT_CONFIG      0x6ffffefa      /* Configuration information.  */
index 87ea97443c4ffe2858094edfdc44cca044302806..c08940c18d96ab57159cb76bacbc85759efd9c60 100644 (file)
@@ -1,3 +1,34 @@
+2006-07-05  Ulrich Drepper  <drepper@redhat.com>
+
+       * i386_ld.c (elf_i386_count_relocations): Lot of corrections.
+       (elf_i386_create_relocations): Likewise.
+       * ld.h (struct symbol): Add local and hidden bits.
+       * ld.c (create_special_section_symbol): These synthsized symbols
+       are local and hidden.
+       * ldgeneric.c (file_process2): Check whether input file matches
+       the emulation.
+       (fillin_special_symbol): Create symbols as local and/or hidden
+       if requested.
+       (ld_generic_create_outfile): Make local copy of symbol.
+       Don't hide global, defined symbols in dynamic symbol table unless
+       requested.  Synthetic symbols have no version information.
+
+       * elflint.c: Add support for checking 64-bit SyV-stlye hash tables.
+       * readelf.c: Add support for printing 64-bit SyV-stlye hash tables.
+
+2006-07-04  Ulrich Drepper  <drepper@redhat.com>
+
+       * elflint.c (is_rel_dyn): Fix and extend DT_RELCOUNT/DT_RELACOUNT
+       testing.
+
+2006-07-03  Ulrich Drepper  <drepper@redhat.com>
+
+       * elflint.c: Add testing of DT_GNU_HASH.
+       * readelf.c: Implement showing histogram for DT_GNU_HASH section.
+
+       * Makefile.am: Add hacks to create dependency files for non-generic
+       linker.
+
 2006-06-12  Ulrich Drepper  <drepper@redhat.com>
 
        * ldgeneric.c (ld_generic_generate_sections): Don't create .interp
index 49e017405041b54ed872cfc5175f74b03576d2a9..604483ab6fc70652ebc40bd0c18a9041a5174d20 100644 (file)
@@ -63,6 +63,12 @@ else
 noinst_LIBRARIES = libld_elf.a $(ld_dsos)
 noinst_PROGRAMS = $(ld_dsos:_pic.a=.so)
 endif
+if NEVER
+# We never build this library but we need to get the dependency files
+# of all the linker backends that might be used in a non-generic linker.
+noinst_LIBRARIES += libdummy.a
+libdummy_a_SOURCES = i386_ld.c
+endif
 
 textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
 
@@ -102,6 +108,7 @@ size_LDADD = $(libelf) $(libeu) $(libmudflap)
 strip_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl
 ld_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl
 if NATIVE_LD
+# -ldl is always needed for libebl.
 ld_LDADD += libld_elf.a
 endif
 ld_LDFLAGS = -rdynamic
@@ -117,9 +124,10 @@ ldlex.o: ldscript.c
 ldlex_no_Werror = yes
 ldscript.h: ldscript.c
 
+if NATIVE_LD
 # Machine-specific linker code.
-libld_elf_a_SOURCES = $(base_cpu)_ld.c
-
+libld_elf_a_SOURCES := $(base_cpu)_ld.c
+else
 libld_elf_i386_pic_a_SOURCES =
 am_libld_elf_i386_pic_a_OBJECTS = i386_ld.os
 
@@ -129,6 +137,7 @@ libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map
              $(libelf) $(libeu) \
              -Wl,--version-script,$(srcdir)/libld_elf_i386.map
        $(textrel_check)
+endif
 
 
 %.os: %.c %.o
index cd335fe6f21776b0b005cb15d09266113586c4e3..a3d7d5cb3e86d6bc789797509f2fef5dbff4697f 100644 (file)
@@ -951,14 +951,14 @@ section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segm
 
 static bool
 is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
-           bool rela)
+           bool is_rela)
 {
   /* If this is no executable or DSO it cannot be a .rel.dyn section.  */
   if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
     return false;
 
   /* Check the section name.  Unfortunately necessary.  */
-  if (strcmp (section_name (ebl, idx), rela ? ".rela.dyn" : ".rel.dyn"))
+  if (strcmp (section_name (ebl, idx), is_rela ? ".rela.dyn" : ".rel.dyn"))
     return false;
 
   /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section
@@ -984,14 +984,106 @@ is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
 
              if (dyn->d_tag == DT_RELCOUNT)
                {
-                 /* Found it.  One last check: does the number
-                    specified number of relative relocations exceed
-                    the total number of relocations?  */
-                 if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
+                 /* Found it.  Does the type match.  */
+                 if (is_rela)
                    ERROR (gettext ("\
+section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"),
+                          idx, section_name (ebl, idx));
+                 else
+                   {
+                     /* Does the number specified number of relative
+                        relocations exceed the total number of
+                        relocations?  */
+                     if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
+                       ERROR (gettext ("\
 section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
-                          idx, section_name (ebl, idx),
-                          (int) dyn->d_un.d_val);
+                              idx, section_name (ebl, idx),
+                              (int) dyn->d_un.d_val);
+
+                     /* Make sure the specified number of relocations are
+                        relative.  */
+                     Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
+                                                                  idx), NULL);
+                     if (reldata != NULL)
+                       for (size_t inner = 0;
+                            inner < shdr->sh_size / shdr->sh_entsize;
+                            ++inner)
+                         {
+                           GElf_Rel rel_mem;
+                           GElf_Rel *rel = gelf_getrel (reldata, inner,
+                                                        &rel_mem);
+                           if (rel == NULL)
+                             /* The problem will be reported elsewhere.  */
+                             break;
+
+                           if (ebl_relative_reloc_p (ebl,
+                                                     GELF_R_TYPE (rel->r_info)))
+                             {
+                               if (inner >= dyn->d_un.d_val)
+                                 ERROR (gettext ("\
+section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"),
+                                        idx, section_name (ebl, idx),
+                                        (int) dyn->d_un.d_val);
+                             }
+                           else if (inner < dyn->d_un.d_val)
+                             ERROR (gettext ("\
+section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"),
+                                    idx, section_name (ebl, idx),
+                                    inner, (int) dyn->d_un.d_val);
+                         }
+                   }
+               }
+
+             if (dyn->d_tag == DT_RELACOUNT)
+               {
+                 /* Found it.  Does the type match.  */
+                 if (!is_rela)
+                   ERROR (gettext ("\
+section [%2d] '%s': DT_RELACOUNT used for this REL section\n"),
+                          idx, section_name (ebl, idx));
+                 else
+                   {
+                     /* Does the number specified number of relative
+                        relocations exceed the total number of
+                        relocations?  */
+                     if (dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
+                       ERROR (gettext ("\
+section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
+                              idx, section_name (ebl, idx),
+                              (int) dyn->d_un.d_val);
+
+                     /* Make sure the specified number of relocations are
+                        relative.  */
+                     Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
+                                                                  idx), NULL);
+                     if (reldata != NULL)
+                       for (size_t inner = 0;
+                            inner < shdr->sh_size / shdr->sh_entsize;
+                            ++inner)
+                         {
+                           GElf_Rela rela_mem;
+                           GElf_Rela *rela = gelf_getrela (reldata, inner,
+                                                           &rela_mem);
+                           if (rela == NULL)
+                             /* The problem will be reported elsewhere.  */
+                             break;
+
+                           if (ebl_relative_reloc_p (ebl,
+                                                     GELF_R_TYPE (rela->r_info)))
+                             {
+                               if (inner >= dyn->d_un.d_val)
+                                 ERROR (gettext ("\
+section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"),
+                                        idx, section_name (ebl, idx),
+                                        (int) dyn->d_un.d_val);
+                             }
+                           else if (inner < dyn->d_un.d_val)
+                             ERROR (gettext ("\
+section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"),
+                                    idx, section_name (ebl, idx),
+                                    inner, (int) dyn->d_un.d_val);
+                         }
+                   }
                }
            }
 
@@ -1718,7 +1810,150 @@ extended section index is %" PRIu32 " but symbol index is not XINDEX\n"),
 
 
 static void
-check_hash (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+check_sysv_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 nchain = ((Elf32_Word *) data->d_buf)[1];
+
+  if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize)
+    ERROR (gettext ("\
+section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
+          idx, section_name (ebl, idx), (long int) shdr->sh_size,
+          (long int) ((2 + nbucket + nchain) * shdr->sh_entsize));
+
+  size_t maxidx = nchain;
+
+  if (symshdr != NULL)
+    {
+      size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
+
+      if (nchain > symshdr->sh_size / symshdr->sh_entsize)
+       ERROR (gettext ("section [%2d] '%s': chain array too large\n"),
+              idx, section_name (ebl, idx));
+
+      maxidx = symsize;
+    }
+
+  size_t cnt;
+  for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+    if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx)
+      ERROR (gettext ("\
+section [%2d] '%s': hash bucket reference %zu out of bounds\n"),
+            idx, section_name (ebl, idx), cnt - 2);
+
+  for (; cnt < 2 + nbucket + nchain; ++cnt)
+    if (((Elf32_Word *) data->d_buf)[cnt] >= maxidx)
+      ERROR (gettext ("\
+section [%2d] '%s': hash chain reference %zu out of bounds\n"),
+            idx, section_name (ebl, idx), cnt - 2 - nbucket);
+}
+
+
+static void
+check_sysv_hash64 (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+                GElf_Shdr *symshdr)
+{
+  Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0];
+  Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1];
+
+  if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize)
+    ERROR (gettext ("\
+section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
+          idx, section_name (ebl, idx), (long int) shdr->sh_size,
+          (long int) ((2 + nbucket + nchain) * shdr->sh_entsize));
+
+  size_t maxidx = nchain;
+
+  if (symshdr != NULL)
+    {
+      size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
+
+      if (nchain > symshdr->sh_size / symshdr->sh_entsize)
+       ERROR (gettext ("section [%2d] '%s': chain array too large\n"),
+              idx, section_name (ebl, idx));
+
+      maxidx = symsize;
+    }
+
+  size_t cnt;
+  for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+    if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx)
+      ERROR (gettext ("\
+section [%2d] '%s': hash bucket reference %zu out of bounds\n"),
+            idx, section_name (ebl, idx), cnt - 2);
+
+  for (; cnt < 2 + nbucket + nchain; ++cnt)
+    if (((Elf64_Xword *) data->d_buf)[cnt] >= maxidx)
+      ERROR (gettext ("\
+section [%2d] '%s': hash chain reference %zu out of bounds\n"),
+            idx, section_name (ebl, idx), cnt - 2 - nbucket);
+}
+
+
+static void
+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 symbias = ((Elf32_Word *) data->d_buf)[1];
+
+  if (shdr->sh_size < (2 + nbucket) * shdr->sh_entsize)
+    {
+      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));
+      return;
+    }
+
+  size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (2 + nbucket);
+
+  if (symshdr != NULL)
+    maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize);
+
+  /* We need the symbol section data.  */
+  Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL);
+
+  size_t cnt;
+  for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+    {
+      Elf32_Word chainidx = ((Elf32_Word *) data->d_buf)[cnt];
+
+      if (chainidx == ~0u)
+       /* Nothing in here.  */
+       continue;
+
+      while (chainidx < maxidx
+            && ((((Elf32_Word *) data->d_buf)[2 + nbucket + chainidx] & 1)
+                == 0))
+       ++chainidx;
+
+      if (chainidx >= maxidx)
+       ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
+              idx, section_name (ebl, idx), cnt - 2);
+      else if (symshdr != NULL
+              && symbias + chainidx > 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)
+       ERROR (gettext ("\
+section [%2d] '%s': symbol reference in chain for bucket %zu is undefined\n"),
+              idx, section_name (ebl, idx), cnt - 2);
+       }
+    }
+}
+
+
+static void
+check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
 {
   if (ehdr->e_type == ET_REL)
     {
@@ -1761,35 +1996,21 @@ section [%2d] '%s': hash table has not even room for nbucket and nchain\n"),
       return;
     }
 
-  Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
-  Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1];
-
-  if (shdr->sh_size < (2 + nbucket + nchain) * shdr->sh_entsize)
-    ERROR (gettext ("\
-section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
-          idx, section_name (ebl, idx), (long int) shdr->sh_size,
-          (long int) ((2 + nbucket + nchain) * shdr->sh_entsize));
-
-  if (symshdr != NULL)
+  switch (tag)
     {
-      size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
-      size_t cnt;
-
-      if (nchain < symshdr->sh_size / symshdr->sh_entsize)
-       ERROR (gettext ("section [%2d] '%s': chain array not large enough\n"),
-              idx, section_name (ebl, idx));
+    case SHT_HASH:
+      if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword))
+       check_sysv_hash64 (ebl, shdr, data, idx, symshdr);
+      else
+       check_sysv_hash (ebl, shdr, data, idx, symshdr);
+      break;
 
-      for (cnt = 2; cnt < 2 + nbucket; ++cnt)
-       if (((Elf32_Word *) data->d_buf)[cnt] >= symsize)
-         ERROR (gettext ("\
-section [%2d] '%s': hash bucket reference %zu out of bounds\n"),
-                idx, section_name (ebl, idx), cnt - 2);
+    case SHT_GNU_HASH:
+      check_gnu_hash (ebl, shdr, data, idx, symshdr);
+      break;
 
-      for (; cnt < 2 + nbucket + nchain; ++cnt)
-       if (((Elf32_Word *) data->d_buf)[cnt] >= symsize)
-         ERROR (gettext ("\
-section [%2d] '%s': hash chain reference %zu out of bounds\n"),
-                idx, section_name (ebl, idx), cnt - 2 - nbucket);
+    default:
+      assert (! "should not  happen");
     }
 }
 
@@ -2945,7 +3166,8 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
          break;
 
        case SHT_HASH:
-         check_hash (ebl, ehdr, shdr, cnt);
+       case SHT_GNU_HASH:
+         check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt);
          break;
 
        case SHT_NULL:
index a0c77dbefb99be4f6fe21a77e6e1509e50444d57..33403eaf90e9da768860781aad07b1d93e32e1d3 100644 (file)
@@ -527,13 +527,15 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
 
          /* Symbols in COMDAT group sections which are discarded do
             not have to be relocated.  */
-         if (unlikely (scninfo->fileinfo->symref[r_sym] == NULL))
+         if (r_sym >= scninfo->fileinfo->nlocalsymbols
+             && unlikely (scninfo->fileinfo->symref[r_sym] == NULL))
            continue;
 
          switch (XELF_R_TYPE (rel->r_info))
            {
            case R_386_GOT32:
-             if (! scninfo->fileinfo->symref[r_sym]->defined)
+             if (! scninfo->fileinfo->symref[r_sym]->defined
+                 || scninfo->fileinfo->symref[r_sym]->in_dso)
                relsize += sizeof (Elf32_Rel);
 
              /* This relocation is not emitted in the output file but
@@ -556,6 +558,8 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
                  if (statep->file_type == dso_file_type)
                    {
                      relsize += sizeof (Elf32_Rel);
+                     // XXX Do we have to check whether the target
+                     // XXX section is read-only first?
                      statep->dt_flags |= DF_TEXTREL;
                    }
                  else
@@ -575,10 +579,9 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
                    }
                }
              else if (statep->file_type == dso_file_type
-                      && r_sym >= SCNINFO_SHDR (scninfo->fileinfo->scninfo[shdr->sh_link].shdr).sh_info
-                      && scninfo->fileinfo->symref[r_sym]->outdynsymidx != 0
                       && XELF_R_TYPE (rel->r_info) == R_386_32)
                relsize += sizeof (Elf32_Rel);
+
              break;
 
            case R_386_PLT32:
@@ -702,8 +705,7 @@ elf_i386_create_relocations (struct ld_state *statep,
       /* Cache the access to the symbol table data.  */
       Elf_Data *symdata = elf_getdata (scninfo[rshdr->sh_link].scn, NULL);
 
-      int cnt;
-      for (cnt = 0; cnt < nrels; ++cnt)
+      for (int cnt = 0; cnt < nrels; ++cnt)
        {
          XElf_Rel_vardef (rel);
          XElf_Rel *rel2;
@@ -812,8 +814,7 @@ elf_i386_create_relocations (struct ld_state *statep,
                    }
                }
              else if (statep->file_type == dso_file_type
-                      && idx >= SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info
-                      && symref[idx]->outdynsymidx != 0)
+                      && XELF_R_TYPE (rel->r_info) == R_386_32)
                {
 #if NATIVE_ELF != 0
                  xelf_getrel_ptr (reldyndata, nreldyn, rel2);
@@ -821,8 +822,15 @@ elf_i386_create_relocations (struct ld_state *statep,
                  rel2 = &rel_mem;
 #endif
                  rel2->r_offset = value;
-                 rel2->r_info
-                   = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32);
+
+                 /* For symbols we do not export we generate a relative
+                    relocation.  */
+                 if (idx < SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info
+                     || symref[idx]->outdynsymidx == 0)
+                   rel2->r_info = XELF_R_INFO (0, R_386_RELATIVE);
+                 else
+                   rel2->r_info
+                     = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32);
                  (void) xelf_update_rel (reldyndata, nreldyn, rel2);
                  ++nreldyn;
 
index 5c20f4757395feb0760cdae71c21154b0f75bf3a..61627e58578d3714cdb5b2f9fcce242065c412fa 100644 (file)
--- a/src/ld.c
+++ b/src/ld.c
@@ -1519,6 +1519,8 @@ create_special_section_symbol (struct symbol **symp, const char *name)
     abort ();
 
   (*symp)->defined = 1;
+  (*symp)->local = 1;
+  (*symp)->hidden = 1;
   (*symp)->type = STT_OBJECT;
 
   ++ld_state.nsymtab;
index bdabee4451159b36afcc0341bc09c96f8fd0a30e..59633e6587581881d823e85a07c37a59f25aea85 100644 (file)
--- a/src/ld.h
+++ b/src/ld.h
@@ -446,6 +446,8 @@ struct symbol
   unsigned int weak:1;
   unsigned int added:1;
   unsigned int merged:1;
+  unsigned int local:1;
+  unsigned int hidden:1;
   /* Nonzero if the symbol is on the from_dso list.  */
   unsigned int on_dsolist:1;
   /* Nonzero if symbol needs copy relocation, reset when the
@@ -961,7 +963,7 @@ struct ld_state
   /* Execuatable stack selection.  */
   enum execstack
     {
-      execstack_false = 0, 
+      execstack_false = 0,
       execstack_true,
       execstack_false_force
     } execstack;
index 47015b169f55f08192a870de1d3001bce885e48d..36b9d6f030cc62b7d01537c4d898a4b775561b26 100644 (file)
@@ -1008,7 +1008,7 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo)
                        = find_section_group (runp->fileinfo,
                                              elf_ndxscn (runp->scn),
                                              &grpscndata);
-                 
+
                      if (strcmp (grpscn->symbols->name,
                                  grpscn2->symbols->name) == 0)
                        {
@@ -1932,6 +1932,18 @@ file_process2 (struct usedfiles *fileinfo)
              return 1;
            }
 
+         /* Make sure the file type matches the backend.  */
+         if (FILEINFO_EHDR (fileinfo->ehdr).e_machine
+             != ebl_get_elfmachine (ld_state.ebl))
+           {
+             fprintf (stderr, gettext ("\
+%s: input file incompatible with ELF machine type %s\n"),
+                      fileinfo->rfname,
+                      ebl_backend_name (ld_state.ebl));
+             fileinfo->status = closed;
+             return 1;
+           }
+
          /* Determine the section header string table section index.  */
          if (unlikely (elf_getshstrndx (fileinfo->elf, &fileinfo->shstrndx)
                        < 0))
@@ -2535,7 +2547,7 @@ ld_generic_open_outfile (struct ld_state *statep, int machine, int klass,
     {
       strcpy (mempcpy (tempfname, ld_state.outfname, outfname_len), ".XXXXXX");
 
-      /* The useof mktemp() here is fine.  We do not want to use
+      /* The use of mktemp() here is fine.  We do not want to use
         mkstemp() since then the umask isn't used.  And the output
         file will have these permissions anyhow.  Any intruder could
         change the file later if it would be possible now.  */
@@ -3242,7 +3254,7 @@ reduce_symbol_p (XElf_Sym *sym, struct Ebl_Strent *strent)
     {
       search.id = strndupa (str, version - str);
       if (*++version == VER_CHR)
-       /* Skip the second '@' signalling a default definition.  */
+       /* Skip the second '@' signaling a default definition.  */
        ++version;
     }
   else
@@ -3560,9 +3572,9 @@ fillin_special_symbol (struct symbol *symst, size_t scnidx, size_t nsym,
   /* The name offset will be filled in later.  */
   sym->st_name = 0;
   /* Traditionally: globally visible.  */
-  sym->st_info = XELF_ST_INFO (STB_GLOBAL, symst->type);
-  /* No special visibility or so.  */
-  sym->st_other = 0;
+  sym->st_info = XELF_ST_INFO (symst->local ? STB_LOCAL : STB_GLOBAL,
+                              symst->type);
+  sym->st_other = symst->hidden ? STV_HIDDEN : 0;
   /* Reference to the GOT or dynamic section.  Since the GOT and
      dynamic section are only created for executables and DSOs it
      cannot be that the section index is too large.  */
@@ -4645,11 +4657,11 @@ ld_generic_create_outfile (struct ld_state *statep)
            continue;
 
 #if NATIVE_ELF != 0
-         /* Copy old data.  */
-         XElf_Sym *sym2 = sym;
-         assert (nsym < nsym_allocated);
-         xelf_getsym (symdata, nsym, sym);
-         *sym = *sym2;
+         /* Copy old data.  We create a temporary copy because the
+            symbol might still be discarded.  */
+         XElf_Sym sym_mem;
+         sym_mem = *sym;
+         sym = &sym_mem;
 #endif
 
          if (sym->st_shndx != SHN_UNDEF
@@ -4764,7 +4776,7 @@ section index too large in dynamic symbol table"));
 
          /* Create the record in the output sections.  */
          assert (nsym < nsym_allocated);
-         xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 0);
+         xelf_update_symshndx (symdata, xndxdata, nsym, sym, xndx, 1);
 
          /* Add the reference to the symbol record in case we need it.
             Find the symbol if this has not happened yet.  We do
@@ -5228,7 +5240,8 @@ section index too large in dynamic symbol table"));
          if (XELF_ST_TYPE (sym->st_info) == STT_FILE
              || XELF_ST_VISIBILITY (sym->st_other) == STV_INTERNAL
              || XELF_ST_VISIBILITY (sym->st_other) == STV_HIDDEN
-             || (!ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined))
+             || (!ld_state.export_all_dynamic
+                 && !ndxtosym[cnt]->in_dso && ndxtosym[cnt]->defined))
            {
              symstrent[cnt] = NULL;
              continue;
@@ -5251,7 +5264,9 @@ section index too large in dynamic symbol table"));
            {
              struct symbol *symp = ndxtosym[cnt];
 
-             if (symp->file->verdefdata != NULL)
+             /* Synthetic symbols (i.e., those with no file attached)
+                have no version information.  */
+             if (symp->file != NULL && symp->file->verdefdata != NULL)
                {
                  GElf_Versym versym;
 
index 135abe364046822dca52a23cdc9fbc974583e7ce..b4b6a5aaa3e7fe4ac9bf4b57d640e988febb8e24 100644 (file)
@@ -2320,6 +2320,193 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
 }
 
 
+static void
+print_hash_info (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx,
+                uint_fast32_t maxlength, Elf32_Word nbucket,
+                uint_fast32_t nsyms, uint32_t *lengths)
+{
+  uint32_t *counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t));
+
+  for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+    ++counts[lengths[cnt]];
+
+  GElf_Shdr glink;
+  printf (ngettext ("\
+\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
+                   "\
+\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
+                   nbucket),
+         (unsigned int) elf_ndxscn (scn),
+         elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+         (int) nbucket,
+         gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18,
+         shdr->sh_addr,
+         shdr->sh_offset,
+         (unsigned int) shdr->sh_link,
+         elf_strptr (ebl->elf, shstrndx,
+                     gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+                                   &glink)->sh_name));
+
+  if (nbucket > 0)
+    {
+      uint64_t success = 0;
+
+      fputs_unlocked (gettext ("\
+ Length  Number  % of total  Coverage\n"), stdout);
+      printf (gettext ("      0  %6" PRIu32 "      %5.1f%%\n"),
+             counts[0], (counts[0] * 100.0) / nbucket);
+
+      uint64_t nzero_counts = 0;
+      for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
+       {
+         nzero_counts += counts[cnt] * cnt;
+         printf (gettext ("\
+%7d  %6" PRIu32 "      %5.1f%%    %5.1f%%\n"),
+                 (int) cnt, counts[cnt], (counts[cnt] * 100.0) / nbucket,
+                 (nzero_counts * 100.0) / nsyms);
+       }
+
+      Elf32_Word acc = 0;
+      for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
+       {
+         acc += cnt;
+         success += counts[cnt] * acc;
+       }
+
+      printf (gettext ("\
+ Average number of tests:   successful lookup: %f\n\
+                          unsuccessful lookup: %f\n"),
+             (double) success / (double) nzero_counts,
+             (double) nzero_counts / (double) nbucket);
+    }
+
+  free (counts);
+}
+
+
+/* This function handles the traditional System V-style hash table format.  */
+static void
+handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    {
+      error (0, 0, gettext ("cannot get data for section %d: %s"),
+            (int) elf_ndxscn (scn), elf_errmsg (-1));
+      return;
+    }
+
+  Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+  Elf32_Word nchain = ((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];
+
+  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)
+    {
+      Elf32_Word inner = bucket[cnt];
+      while (inner > 0 && inner < nchain)
+       {
+         ++nsyms;
+         if (maxlength < ++lengths[cnt])
+           ++maxlength;
+
+         inner = chain[inner];
+       }
+    }
+
+  print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+                  lengths);
+
+  free (lengths);
+}
+
+
+/* This function handles the incorrect, System V-style hash table
+   format some 64-bit architectures use.  */
+static void
+handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    {
+      error (0, 0, gettext ("cannot get data for section %d: %s"),
+            (int) elf_ndxscn (scn), elf_errmsg (-1));
+      return;
+    }
+
+  Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0];
+  Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1];
+  Elf64_Xword *bucket = &((Elf64_Xword *) data->d_buf)[2];
+  Elf64_Xword *chain = &((Elf64_Xword *) data->d_buf)[2 + nbucket];
+
+  uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
+
+  uint_fast32_t maxlength = 0;
+  uint_fast32_t nsyms = 0;
+  for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt)
+    {
+      Elf64_Xword inner = bucket[cnt];
+      while (inner > 0 && inner < nchain)
+       {
+         ++nsyms;
+         if (maxlength < ++lengths[cnt])
+           ++maxlength;
+
+         inner = chain[inner];
+       }
+    }
+
+  print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+                  lengths);
+
+  free (lengths);
+}
+
+
+/* This function handles the GNU-style hash table format.  */
+static void
+handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+  Elf_Data *data = elf_getdata (scn, NULL);
+  if (data == NULL)
+    {
+      error (0, 0, gettext ("cannot get data for section %d: %s"),
+            (int) elf_ndxscn (scn), elf_errmsg (-1));
+      return;
+    }
+
+  Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+  Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2];
+  Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[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)
+      {
+       Elf32_Word inner = bucket[cnt];
+       do
+         {
+           ++nsyms;
+           if (maxlength < ++lengths[cnt])
+             ++maxlength;
+         }
+       while ((chain[inner++] & 1) == 0);
+      }
+
+  print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+                  lengths);
+
+  free (lengths);
+}
+
+
 /* Find the symbol table(s).  For this we have to search through the
    section table.  */
 static void
@@ -2338,100 +2525,17 @@ handle_hash (Ebl *ebl)
       GElf_Shdr shdr_mem;
       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
 
-      if (shdr != NULL && shdr->sh_type == SHT_HASH)
+      if (shdr != NULL)
        {
-         Elf_Data *data = elf_getdata (scn, NULL);
-         if (data == NULL)
-           {
-             error (0, 0, gettext ("cannot get data for section %d: %s"),
-                    (int) elf_ndxscn (scn), elf_errmsg (-1));
-             continue;
-           }
-
-         Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
-         Elf32_Word nchain = ((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];
-
-         GElf_Shdr glink;
-         printf (ngettext ("\
-\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
-                           "\
-\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 "  Offset: %#08" PRIx64 "  Link to section: [%2u] '%s'\n",
-                           nbucket),
-                 (unsigned int) elf_ndxscn (scn),
-                 elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
-                 (int) nbucket,
-                 gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18,
-                 shdr->sh_addr,
-                 shdr->sh_offset,
-                 (unsigned int) shdr->sh_link,
-                 elf_strptr (ebl->elf, shstrndx,
-                             gelf_getshdr (elf_getscn (ebl->elf,
-                                                       shdr->sh_link),
-                                           &glink)->sh_name));
-
-         uint32_t *lengths = (uint32_t *) xcalloc (nbucket,
-                                                   sizeof (uint32_t));
-
-         Elf32_Word maxlength = 0;
-         Elf32_Word nsyms = 0;
-         for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
-           if (bucket[cnt] != 0)
-             {
-               Elf32_Word inner = bucket[cnt];
-               while (inner > 0 && inner < nchain)
-                 {
-                   ++nsyms;
-                   if (maxlength < ++lengths[cnt])
-                     ++maxlength;
-
-                   inner = chain[inner];
-                 }
-             }
-
-         uint32_t *counts = (uint32_t *) xcalloc (maxlength + 1,
-                                                  sizeof (uint32_t));
-
-         for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
-           ++counts[lengths[cnt]];
-
-         if (nbucket > 0)
+         if (shdr->sh_type == SHT_HASH)
            {
-             uint64_t success = 0;
-
-             fputs_unlocked (gettext ("\
- Length  Number  % of total  Coverage\n"), stdout);
-             printf (gettext ("      0  %6" PRIu32 "      %5.1f%%\n"),
-                     counts[0], (counts[0] * 100.0) / nbucket);
-
-             uint64_t nzero_counts = 0;
-             for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
-               {
-                 nzero_counts += counts[cnt] * cnt;
-                 printf (gettext ("\
-%7d  %6" PRIu32 "      %5.1f%%    %5.1f%%\n"),
-                         (int) cnt,
-                         counts[cnt], (counts[cnt] * 100.0) / nbucket,
-                         (nzero_counts * 100.0) / nsyms);
-               }
-
-             Elf32_Word acc = 0;
-             for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
-               {
-                 acc += cnt;
-                 success += counts[cnt] * acc;
-               }
-
-             printf (gettext ("\
- Average number of tests:   successful lookup: %f\n\
-                          unsuccessful lookup: %f\n"),
-                     (double) success / (double) nzero_counts,
-                     (double) nzero_counts / (double) nbucket);
+             if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword))
+               handle_sysv_hash64 (ebl, scn, shdr, shstrndx);
+             else
+               handle_sysv_hash (ebl, scn, shdr, shstrndx);
            }
-
-         free (counts);
-         free (lengths);
+         else if (shdr->sh_type == SHT_GNU_HASH)
+           handle_gnu_hash (ebl, scn, shdr, shstrndx);
        }
     }
 }