include/
authorJakub Jelinek <jakub@redhat.com>
Mon, 10 Jul 2006 21:40:25 +0000 (21:40 +0000)
committerJakub Jelinek <jakub@redhat.com>
Mon, 10 Jul 2006 21:40:25 +0000 (21:40 +0000)
* bfdlink.h (struct bfd_link_info): Add emit_hash and
emit_gnu_hash bitfields.
include/elf/
* common.h (SHT_GNU_HASH, DT_GNU_HASH): Define.
ld/
* scripttempl/elf.sc: Add .gnu.hash section.
* emultempl/elf32.em (OPTION_HASH_STYLE): Define.
(gld${EMULATION_NAME}_add_options): Register --hash-style option.
(gld${EMULATION_NAME}_handle_option): Handle it.
(gld${EMULATION_NAME}_list_options): Document it.
* ldmain.c (main): Initialize emit_hash and emit_gnu_hash.
* ld.texinfo: Document --hash-style option.
bfd/
* elf.c (_bfd_elf_print_private_bfd_data): Handle DT_GNU_HASH.
(bfd_section_from_shdr, elf_fake_sections, assign_section_numbers):
Handle SHT_GNU_HASH.
(special_sections_g): Include .gnu.hash section.
(bfd_elf_gnu_hash): New function.
* elf-bfd.h (bfd_elf_gnu_hash, _bfd_elf_hash_symbol): New prototypes.
(struct elf_backend_data): Add elf_hash_symbol method.
* elflink.c (_bfd_elf_link_create_dynamic_sections): Create .hash
only if info->emit_hash, create .gnu.hash section if
info->emit_gnu_hash.
(struct collect_gnu_hash_codes): New type.
(elf_collect_gnu_hash_codes, elf_renumber_gnu_hash_syms,
_bfd_elf_hash_symbol): New functions.
(compute_bucket_count): Don't compute HASHCODES array, instead add
that and NSYMS as arguments.  Use bed->s->sizeof_hash_entry
instead of bed->s->arch_size / 8.  Fix .hash size estimation.
When not optimizing, use the number of hashed symbols rather than
dynsymcount.
(bfd_elf_size_dynamic_sections): Only add DT_HASH if info->emit_hash,
and ADD DT_GNU_HASH if info->emit_gnu_hash.
(bfd_elf_size_dynsym_hash_dynstr): Size .hash only if info->emit_hash,
adjust compute_bucket_count caller.  Create and populate .gnu.hash
section if info->emit_gnu_hash.
(elf_link_output_extsym): Only populate .hash section if
finfo->hash_sec != NULL.
(bfd_elf_final_link): Adjust assertion.  Handle DT_GNU_HASH.
* elfxx-target.h (elf_backend_hash_symbol): Define if not yet defined.
(elfNN_bed): Add elf_backend_hash_symbol.
* elf64-x86-64.c (elf64_x86_64_hash_symbol): New function.
(elf_backend_hash_symbol): Define.
* elf32-i386.c (elf_i386_hash_symbol): New function.
(elf_backend_hash_symbol): Define.
binutils/
* readelf.c (get_dynamic_type): Handle DT_GNU_HASH.
(get_section_type_name): Handle SHT_GNU_HASH.
(dynamic_info_DT_GNU_HASH): New variable.
(process_dynamic_section): Handle DT_GNU_HASH.
(process_symbol_table): Print also DT_GNU_HASH histogram.
ld/testsuite/
* ld-powerpc/tlsso32.r: Adjust.
* ld-powerpc/tlsso32.d: Adjust.
* ld-powerpc/tlsso32.g: Adjust.
* ld-powerpc/tlsso.r: Adjust.
* ld-powerpc/tlsso.g: Adjust.
* ld-powerpc/tlstocso.g: Adjust.

25 files changed:
bfd/ChangeLog
bfd/elf-bfd.h
bfd/elf.c
bfd/elf32-i386.c
bfd/elf64-x86-64.c
bfd/elflink.c
bfd/elfxx-target.h
binutils/ChangeLog
binutils/readelf.c
include/ChangeLog
include/bfdlink.h
include/elf/ChangeLog
include/elf/common.h
ld/ChangeLog
ld/emultempl/elf32.em
ld/ld.texinfo
ld/ldmain.c
ld/scripttempl/elf.sc
ld/testsuite/ChangeLog
ld/testsuite/ld-powerpc/tlsso.g
ld/testsuite/ld-powerpc/tlsso.r
ld/testsuite/ld-powerpc/tlsso32.d
ld/testsuite/ld-powerpc/tlsso32.g
ld/testsuite/ld-powerpc/tlsso32.r
ld/testsuite/ld-powerpc/tlstocso.g

index 55b1332..5603c24 100644 (file)
@@ -1,3 +1,38 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * elf.c (_bfd_elf_print_private_bfd_data): Handle DT_GNU_HASH.
+       (bfd_section_from_shdr, elf_fake_sections, assign_section_numbers):
+       Handle SHT_GNU_HASH.
+       (special_sections_g): Include .gnu.hash section.
+       (bfd_elf_gnu_hash): New function.
+       * elf-bfd.h (bfd_elf_gnu_hash, _bfd_elf_hash_symbol): New prototypes.
+       (struct elf_backend_data): Add elf_hash_symbol method.
+       * elflink.c (_bfd_elf_link_create_dynamic_sections): Create .hash
+       only if info->emit_hash, create .gnu.hash section if
+       info->emit_gnu_hash.
+       (struct collect_gnu_hash_codes): New type.
+       (elf_collect_gnu_hash_codes, elf_renumber_gnu_hash_syms,
+       _bfd_elf_hash_symbol): New functions.
+       (compute_bucket_count): Don't compute HASHCODES array, instead add
+       that and NSYMS as arguments.  Use bed->s->sizeof_hash_entry
+       instead of bed->s->arch_size / 8.  Fix .hash size estimation.
+       When not optimizing, use the number of hashed symbols rather than
+       dynsymcount.
+       (bfd_elf_size_dynamic_sections): Only add DT_HASH if info->emit_hash,
+       and ADD DT_GNU_HASH if info->emit_gnu_hash.
+       (bfd_elf_size_dynsym_hash_dynstr): Size .hash only if info->emit_hash,
+       adjust compute_bucket_count caller.  Create and populate .gnu.hash
+       section if info->emit_gnu_hash.
+       (elf_link_output_extsym): Only populate .hash section if
+       finfo->hash_sec != NULL.
+       (bfd_elf_final_link): Adjust assertion.  Handle DT_GNU_HASH.
+       * elfxx-target.h (elf_backend_hash_symbol): Define if not yet defined.
+       (elfNN_bed): Add elf_backend_hash_symbol.
+       * elf64-x86-64.c (elf64_x86_64_hash_symbol): New function.
+       (elf_backend_hash_symbol): Define.
+       * elf32-i386.c (elf_i386_hash_symbol): New function.
+       (elf_backend_hash_symbol): Define.
+
 2006-07-05  Nick Clifton  <nickc@redhat.com>
 
        PR ld/2659
index 95686d3..e7d3b18 100644 (file)
@@ -1038,6 +1038,9 @@ struct elf_backend_data
                               bfd_boolean *, bfd_boolean *,
                               bfd *, asection **);
 
+  /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
+  bfd_boolean (*elf_hash_symbol) (struct elf_link_hash_entry *);
+
   /* Used to handle bad SHF_LINK_ORDER input.  */
   bfd_error_handler_type link_order_error_handler;
 
@@ -1481,6 +1484,8 @@ extern bfd_vma _bfd_elf_section_offset
 
 extern unsigned long bfd_elf_hash
   (const char *);
+extern unsigned long bfd_elf_gnu_hash
+  (const char *);
 
 extern bfd_reloc_status_type bfd_elf_generic_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
@@ -1651,6 +1656,8 @@ extern bfd_boolean _bfd_elf_merge_symbol
    struct elf_link_hash_entry **, bfd_boolean *,
    bfd_boolean *, bfd_boolean *, bfd_boolean *);
 
+extern bfd_boolean _bfd_elf_hash_symbol (struct elf_link_hash_entry *);
+
 extern bfd_boolean _bfd_elf_add_default_symbol
   (bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
    const char *, Elf_Internal_Sym *, asection **, bfd_vma *,
index abb5082..85f9e0f 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -206,6 +206,21 @@ bfd_elf_hash (const char *namearg)
   return h & 0xffffffff;
 }
 
+/* DT_GNU_HASH hash function.  Do not change this function; you will
+   cause invalid hash tables to be generated.  */
+
+unsigned long
+bfd_elf_gnu_hash (const char *namearg)
+{
+  const unsigned char *name = (const unsigned char *) namearg;
+  unsigned long h = 5381;
+  unsigned char ch;
+
+  while ((ch = *name++) != '\0')
+    h = (h << 5) + h + ch;
+  return h & 0xffffffff;
+}
+
 bfd_boolean
 bfd_elf_mkobject (bfd *abfd)
 {
@@ -1240,6 +1255,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
            case DT_AUXILIARY: name = "AUXILIARY"; stringp = TRUE; break;
            case DT_USED: name = "USED"; break;
            case DT_FILTER: name = "FILTER"; stringp = TRUE; break;
+           case DT_GNU_HASH: name = "GNU_HASH"; break;
            }
 
          fprintf (f, "  %-11s ", name);
@@ -1824,6 +1840,7 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
     case SHT_FINI_ARRAY:       /* .fini_array section.  */
     case SHT_PREINIT_ARRAY:    /* .preinit_array section.  */
     case SHT_GNU_LIBLIST:      /* .gnu.liblist section.  */
+    case SHT_GNU_HASH:         /* .gnu.hash section.  */
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
 
     case SHT_DYNAMIC:  /* Dynamic linking information.  */
@@ -2296,6 +2313,7 @@ static const struct bfd_elf_special_section special_sections_g[] =
   { ".gnu.version_r", 14,  0, SHT_GNU_verneed, 0 },
   { ".gnu.liblist",   12,  0, SHT_GNU_LIBLIST, SHF_ALLOC },
   { ".gnu.conflict",  13,  0, SHT_RELA,     SHF_ALLOC },
+  { ".gnu.hash",       9,  0, SHT_GNU_HASH, SHF_ALLOC },
   { NULL,              0,  0, 0,            0 }
 };
 
@@ -2812,6 +2830,10 @@ elf_fake_sections (bfd *abfd, asection *asect, void *failedptrarg)
     case SHT_GROUP:
       this_hdr->sh_entsize = 4;
       break;
+
+    case SHT_GNU_HASH:
+      this_hdr->sh_entsize = bed->s->arch_size == 64 ? 0 : 4;
+      break;
     }
 
   if ((asect->flags & SEC_ALLOC) != 0)
@@ -3257,6 +3279,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
          break;
 
        case SHT_HASH:
+       case SHT_GNU_HASH:
        case SHT_GNU_versym:
          /* sh_link is the section header index of the symbol table
             this hash table or version table is for.  */
index 35b0525..ab02b2c 100644 (file)
@@ -3839,6 +3839,18 @@ elf_i386_plt_sym_val (bfd_vma i, const asection *plt,
   return plt->vma + (i + 1) * PLT_ENTRY_SIZE;
 }
 
+/* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
+
+static bfd_boolean
+elf_i386_hash_symbol (struct elf_link_hash_entry *h)
+{
+  if (h->plt.offset != (bfd_vma) -1
+      && !h->def_regular
+      && !h->pointer_equality_needed)
+    return FALSE;
+
+  return _bfd_elf_hash_symbol (h);
+}
 
 #define TARGET_LITTLE_SYM              bfd_elf32_i386_vec
 #define TARGET_LITTLE_NAME             "elf32-i386"
@@ -3879,6 +3891,7 @@ elf_i386_plt_sym_val (bfd_vma i, const asection *plt,
 #define elf_backend_size_dynamic_sections     elf_i386_size_dynamic_sections
 #define elf_backend_always_size_sections      elf_i386_always_size_sections
 #define elf_backend_plt_sym_val                      elf_i386_plt_sym_val
+#define elf_backend_hash_symbol                      elf_i386_hash_symbol
 
 #include "elf32-target.h"
 
index b9cf0a3..6ca622a 100644 (file)
@@ -3620,6 +3620,19 @@ elf64_x86_64_additional_program_headers (bfd *abfd,
   return count;
 }
 
+/* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
+
+static bfd_boolean
+elf64_x86_64_hash_symbol (struct elf_link_hash_entry *h)
+{
+  if (h->plt.offset != (bfd_vma) -1
+      && !h->def_regular
+      && !h->pointer_equality_needed)
+    return FALSE;
+
+  return _bfd_elf_hash_symbol (h);
+}
+
 static const struct bfd_elf_special_section 
   elf64_x86_64_special_sections[]=
 {
@@ -3693,5 +3706,7 @@ static const struct bfd_elf_special_section
   elf64_x86_64_special_sections
 #define elf_backend_additional_program_headers \
   elf64_x86_64_additional_program_headers
+#define elf_backend_hash_symbol \
+  elf64_x86_64_hash_symbol
 
 #include "elf64-target.h"
index 4b20749..9d65b5f 100644 (file)
@@ -240,12 +240,30 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   if (!_bfd_elf_define_linkage_sym (abfd, info, s, "_DYNAMIC"))
     return FALSE;
 
-  s = bfd_make_section_with_flags (abfd, ".hash",
-                                  flags | SEC_READONLY);
-  if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
-    return FALSE;
-  elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry;
+  if (info->emit_hash)
+    {
+      s = bfd_make_section_with_flags (abfd, ".hash", flags | SEC_READONLY);
+      if (s == NULL
+         || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+       return FALSE;
+      elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry;
+    }
+
+  if (info->emit_gnu_hash)
+    {
+      s = bfd_make_section_with_flags (abfd, ".gnu.hash",
+                                      flags | SEC_READONLY);
+      if (s == NULL
+         || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+       return FALSE;
+      /* For 64-bit ELF, .gnu.hash is a non-uniform entity size section:
+        4 32-bit words followed by variable count of 64-bit words, then
+        variable count of 32-bit words.  */
+      if (bed->s->arch_size == 64)
+       elf_section_data (s)->this_hdr.sh_entsize = 0;
+      else
+       elf_section_data (s)->this_hdr.sh_entsize = 4;
+    }
 
   /* Let the backend create the rest of the sections.  This lets the
      backend set the right flags.  The backend will normally create
@@ -4811,6 +4829,131 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
   return TRUE;
 }
 
+struct collect_gnu_hash_codes
+{
+  bfd *output_bfd;
+  const struct elf_backend_data *bed;
+  unsigned long int nsyms;
+  unsigned long int maskbits;
+  unsigned long int *hashcodes;
+  unsigned long int *hashval;
+  unsigned long int *indx;
+  unsigned long int *counts;
+  bfd_vma *bitmask;
+  bfd_byte *contents;
+  long int min_dynindx;
+  unsigned long int bucketcount;
+  unsigned long int symindx;
+  long int local_indx;
+  long int shift1, shift2;
+  unsigned long int mask;
+};
+
+/* This function will be called though elf_link_hash_traverse to store
+   all hash value of the exported symbols in an array.  */
+
+static bfd_boolean
+elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
+{
+  struct collect_gnu_hash_codes *s = data;
+  const char *name;
+  char *p;
+  unsigned long ha;
+  char *alc = NULL;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  /* Ignore indirect symbols.  These are added by the versioning code.  */
+  if (h->dynindx == -1)
+    return TRUE;
+
+  /* Ignore also local symbols and undefined symbols.  */
+  if (! (*s->bed->elf_hash_symbol) (h))
+    return TRUE;
+
+  name = h->root.root.string;
+  p = strchr (name, ELF_VER_CHR);
+  if (p != NULL)
+    {
+      alc = bfd_malloc (p - name + 1);
+      memcpy (alc, name, p - name);
+      alc[p - name] = '\0';
+      name = alc;
+    }
+
+  /* Compute the hash value.  */
+  ha = bfd_elf_gnu_hash (name);
+
+  /* Store the found hash value in the array for compute_bucket_count,
+     and also for .dynsym reordering purposes.  */
+  s->hashcodes[s->nsyms] = ha;
+  s->hashval[h->dynindx] = ha;
+  ++s->nsyms;
+  if (s->min_dynindx < 0 || s->min_dynindx > h->dynindx)
+    s->min_dynindx = h->dynindx;
+
+  if (alc != NULL)
+    free (alc);
+
+  return TRUE;
+}
+
+/* This function will be called though elf_link_hash_traverse to do
+   final dynaminc symbol renumbering.  */
+
+static bfd_boolean
+elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data)
+{
+  struct collect_gnu_hash_codes *s = data;
+  unsigned long int bucket;
+  unsigned long int val;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  /* Ignore indirect symbols.  */
+  if (h->dynindx == -1)
+    return TRUE;
+
+  /* Ignore also local symbols and undefined symbols.  */
+  if (! (*s->bed->elf_hash_symbol) (h))
+    {
+      if (h->dynindx >= s->min_dynindx)
+       h->dynindx = s->local_indx++;
+      return TRUE;
+    }
+
+  bucket = s->hashval[h->dynindx] % s->bucketcount;
+  val = (s->hashval[h->dynindx] >> s->shift1)
+       & ((s->maskbits >> s->shift1) - 1);
+  s->bitmask[val] |= ((bfd_vma) 1) << (s->hashval[h->dynindx] & s->mask);
+  s->bitmask[val]
+    |= ((bfd_vma) 1) << ((s->hashval[h->dynindx] >> s->shift2) & s->mask);
+  val = s->hashval[h->dynindx] & ~(unsigned long int) 1;
+  if (s->counts[bucket] == 1)
+    /* Last element terminates the chain.  */
+    val |= 1;
+  bfd_put_32 (s->output_bfd, val,
+             s->contents + (s->indx[bucket] - s->symindx) * 4);
+  --s->counts[bucket];
+  h->dynindx = s->indx[bucket]++;
+  return TRUE;
+}
+
+/* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
+
+bfd_boolean
+_bfd_elf_hash_symbol (struct elf_link_hash_entry *h)
+{
+  return !(h->forced_local
+          || h->root.type == bfd_link_hash_undefined
+          || h->root.type == bfd_link_hash_undefweak
+          || ((h->root.type == bfd_link_hash_defined
+               || h->root.type == bfd_link_hash_defweak)
+              && h->root.u.def.section->output_section == NULL));
+}
+
 /* Array used to determine the number of hash table buckets to use
    based on the number of symbols there are.  If there are fewer than
    3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets,
@@ -4832,42 +4975,26 @@ static const size_t elf_buckets[] =
    Therefore the result is always a good payoff between few collisions
    (= short chain lengths) and table size.  */
 static size_t
-compute_bucket_count (struct bfd_link_info *info)
+compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
+                     unsigned long int nsyms, int gnu_hash)
 {
   size_t dynsymcount = elf_hash_table (info)->dynsymcount;
   size_t best_size = 0;
-  unsigned long int *hashcodes;
-  unsigned long int *hashcodesp;
   unsigned long int i;
   bfd_size_type amt;
 
-  /* Compute the hash values for all exported symbols.  At the same
-     time store the values in an array so that we could use them for
-     optimizations.  */
-  amt = dynsymcount;
-  amt *= sizeof (unsigned long int);
-  hashcodes = bfd_malloc (amt);
-  if (hashcodes == NULL)
-    return 0;
-  hashcodesp = hashcodes;
-
-  /* Put all hash values in HASHCODES.  */
-  elf_link_hash_traverse (elf_hash_table (info),
-                         elf_collect_hash_codes, &hashcodesp);
-
   /* We have a problem here.  The following code to optimize the table
      size requires an integer type with more the 32 bits.  If
      BFD_HOST_U_64_BIT is set we know about such a type.  */
 #ifdef BFD_HOST_U_64_BIT
   if (info->optimize)
     {
-      unsigned long int nsyms = hashcodesp - hashcodes;
       size_t minsize;
       size_t maxsize;
       BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0);
-      unsigned long int *counts ;
       bfd *dynobj = elf_hash_table (info)->dynobj;
       const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
+      unsigned long int *counts;
 
       /* Possible optimization parameters: if we have NSYMS symbols we say
         that the hashing table must at least have NSYMS/4 and at most
@@ -4876,6 +5003,13 @@ compute_bucket_count (struct bfd_link_info *info)
       if (minsize == 0)
        minsize = 1;
       best_size = maxsize = nsyms * 2;
+      if (gnu_hash)
+       {
+         if (minsize < 2)
+           minsize = 2;
+         if ((best_size & 31) == 0)
+           ++best_size;
+       }
 
       /* Create array where we count the collisions in.  We must use bfd_malloc
         since the size could be large.  */
@@ -4883,10 +5017,7 @@ compute_bucket_count (struct bfd_link_info *info)
       amt *= sizeof (unsigned long int);
       counts = bfd_malloc (amt);
       if (counts == NULL)
-       {
-         free (hashcodes);
-         return 0;
-       }
+       return 0;
 
       /* Compute the "optimal" size for the hash table.  The criteria is a
         minimal chain length.  The minor criteria is (of course) the size
@@ -4898,6 +5029,9 @@ compute_bucket_count (struct bfd_link_info *info)
          unsigned long int j;
          unsigned long int fact;
 
+         if (gnu_hash && (i & 31) == 0)
+           continue;
+
          memset (counts, '\0', i * sizeof (unsigned long int));
 
          /* Determine how often each hash bucket is used.  */
@@ -4913,9 +5047,9 @@ compute_bucket_count (struct bfd_link_info *info)
 #  define BFD_TARGET_PAGESIZE  (4096)
 # endif
 
-         /* We in any case need 2 + NSYMS entries for the size values and
-            the chains.  */
-         max = (2 + nsyms) * (bed->s->arch_size / 8);
+         /* We in any case need 2 + DYNSYMCOUNT entries for the size values
+            and the chains.  */
+         max = (2 + dynsymcount) * bed->s->sizeof_hash_entry;
 
 # if 1
          /* Variant 1: optimize for short chains.  We add the squares
@@ -4925,7 +5059,7 @@ compute_bucket_count (struct bfd_link_info *info)
            max += counts[j] * counts[j];
 
          /* This adds penalties for the overall size of the table.  */
-         fact = i / (BFD_TARGET_PAGESIZE / (bed->s->arch_size / 8)) + 1;
+         fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1;
          max *= fact * fact;
 # else
          /* Variant 2: Optimize a lot more for small table.  Here we
@@ -4936,7 +5070,7 @@ compute_bucket_count (struct bfd_link_info *info)
 
          /* The overall size of the table is considered, but not as
             strong as in variant 1, where it is squared.  */
-         fact = i / (BFD_TARGET_PAGESIZE / (bed->s->arch_size / 8)) + 1;
+         fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1;
          max *= fact;
 # endif
 
@@ -4959,14 +5093,13 @@ compute_bucket_count (struct bfd_link_info *info)
       for (i = 0; elf_buckets[i] != 0; i++)
        {
          best_size = elf_buckets[i];
-         if (dynsymcount < elf_buckets[i + 1])
+         if (nsyms < elf_buckets[i + 1])
            break;
        }
+      if (gnu_hash && best_size < 2)
+       best_size = 2;
     }
 
-  /* Free the arrays we needed.  */
-  free (hashcodes);
-
   return best_size;
 }
 
@@ -5324,7 +5457,10 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
          bfd_size_type strsize;
 
          strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
-         if (!_bfd_elf_add_dynamic_entry (info, DT_HASH, 0)
+         if ((info->emit_hash
+              && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0))
+             || (info->emit_gnu_hash
+                 && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0))
              || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0)
              || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0)
              || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize)
@@ -5726,8 +5862,6 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
       asection *s;
       bfd_size_type dynsymcount;
       unsigned long section_sym_count;
-      size_t bucketcount = 0;
-      size_t hash_entry_size;
       unsigned int dtagcount;
 
       dynobj = elf_hash_table (info)->dynobj;
@@ -5778,23 +5912,215 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          memset (s->contents, 0, section_sym_count * bed->s->sizeof_sym);
        }
 
+      elf_hash_table (info)->bucketcount = 0;
+
       /* Compute the size of the hashing table.  As a side effect this
         computes the hash values for all the names we export.  */
-      bucketcount = compute_bucket_count (info);
+      if (info->emit_hash)
+       {
+         unsigned long int *hashcodes;
+         unsigned long int *hashcodesp;
+         bfd_size_type amt;
+         unsigned long int nsyms;
+         size_t bucketcount;
+         size_t hash_entry_size;
+
+         /* Compute the hash values for all exported symbols.  At the same
+            time store the values in an array so that we could use them for
+            optimizations.  */
+         amt = dynsymcount * sizeof (unsigned long int);
+         hashcodes = bfd_malloc (amt);
+         if (hashcodes == NULL)
+           return FALSE;
+         hashcodesp = hashcodes;
 
-      s = bfd_get_section_by_name (dynobj, ".hash");
-      BFD_ASSERT (s != NULL);
-      hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize;
-      s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size);
-      s->contents = bfd_zalloc (output_bfd, s->size);
-      if (s->contents == NULL)
-       return FALSE;
+         /* Put all hash values in HASHCODES.  */
+         elf_link_hash_traverse (elf_hash_table (info),
+                                 elf_collect_hash_codes, &hashcodesp);
+
+         nsyms = hashcodesp - hashcodes;
+         bucketcount
+           = compute_bucket_count (info, hashcodes, nsyms, 0);
+         free (hashcodes);
+
+         if (bucketcount == 0)
+           return FALSE;
 
-      bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents);
-      bfd_put (8 * hash_entry_size, output_bfd, dynsymcount,
-              s->contents + hash_entry_size);
+         elf_hash_table (info)->bucketcount = bucketcount;
 
-      elf_hash_table (info)->bucketcount = bucketcount;
+         s = bfd_get_section_by_name (dynobj, ".hash");
+         BFD_ASSERT (s != NULL);
+         hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize;
+         s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size);
+         s->contents = bfd_zalloc (output_bfd, s->size);
+         if (s->contents == NULL)
+           return FALSE;
+
+         bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents);
+         bfd_put (8 * hash_entry_size, output_bfd, dynsymcount,
+                  s->contents + hash_entry_size);
+       }
+
+      if (info->emit_gnu_hash)
+       {
+         size_t i, cnt;
+         unsigned char *contents;
+         struct collect_gnu_hash_codes cinfo;
+         bfd_size_type amt;
+         size_t bucketcount;
+
+         memset (&cinfo, 0, sizeof (cinfo));
+
+         /* Compute the hash values for all exported symbols.  At the same
+            time store the values in an array so that we could use them for
+            optimizations.  */
+         amt = dynsymcount * 2 * sizeof (unsigned long int);
+         cinfo.hashcodes = bfd_malloc (amt);
+         if (cinfo.hashcodes == NULL)
+           return FALSE;
+
+         cinfo.hashval = cinfo.hashcodes + dynsymcount;
+         cinfo.min_dynindx = -1;
+         cinfo.output_bfd = output_bfd;
+         cinfo.bed = bed;
+
+         /* Put all hash values in HASHCODES.  */
+         elf_link_hash_traverse (elf_hash_table (info),
+                                 elf_collect_gnu_hash_codes, &cinfo);
+
+         bucketcount
+           = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
+
+         if (bucketcount == 0)
+           {
+             free (cinfo.hashcodes);
+             return FALSE;
+           }
+
+         s = bfd_get_section_by_name (dynobj, ".gnu.hash");
+         BFD_ASSERT (s != NULL);
+
+         if (cinfo.nsyms == 0)
+           {
+             /* Empty .gnu.hash section is special.  */
+             BFD_ASSERT (cinfo.min_dynindx == -1);
+             free (cinfo.hashcodes);
+             s->size = 5 * 4 + bed->s->arch_size / 8;
+             contents = bfd_zalloc (output_bfd, s->size);
+             if (contents == NULL)
+               return FALSE;
+             s->contents = contents;
+             /* 1 empty bucket.  */
+             bfd_put_32 (output_bfd, 1, contents);
+             /* SYMIDX above the special symbol 0.  */
+             bfd_put_32 (output_bfd, 1, contents + 4);
+             /* Just one word for bitmask.  */
+             bfd_put_32 (output_bfd, 1, contents + 8);
+             /* Only hash fn bloom filter.  */
+             bfd_put_32 (output_bfd, 0, contents + 12);
+             /* No hashes are valid - empty bitmask.  */
+             bfd_put (bed->s->arch_size, output_bfd, 0, contents + 16);
+             /* No hashes in the only bucket.  */
+             bfd_put_32 (output_bfd, 0,
+                         contents + 16 + bed->s->arch_size / 8);
+           }
+         else
+           {
+             BFD_ASSERT (cinfo.min_dynindx != -1);
+             unsigned long int maskwords, maskbitslog2;
+
+             maskbitslog2 = bfd_log2 (cinfo.nsyms) + 1;
+             if (maskbitslog2 < 3)
+               maskbitslog2 = 5;
+             else if ((1 << (maskbitslog2 - 2)) & cinfo.nsyms)
+               maskbitslog2 = maskbitslog2 + 3;
+             else
+               maskbitslog2 = maskbitslog2 + 2;
+             if (bed->s->arch_size == 64)
+               {
+                 if (maskbitslog2 == 5)
+                   maskbitslog2 = 6;
+                 cinfo.shift1 = 6;
+               }
+             else
+               cinfo.shift1 = 5;
+             cinfo.mask = (1 << cinfo.shift1) - 1;
+             cinfo.shift2 = maskbitslog2 + cinfo.shift1;
+             cinfo.maskbits = 1 << maskbitslog2;
+             maskwords = 1 << (maskbitslog2 - cinfo.shift1);
+             amt = bucketcount * sizeof (unsigned long int) * 2;
+             amt += maskwords * sizeof (bfd_vma);
+             cinfo.bitmask = bfd_malloc (amt);
+             if (cinfo.bitmask == NULL)
+               {
+                 free (cinfo.hashcodes);
+                 return FALSE;
+               }
+
+             cinfo.counts = (void *) (cinfo.bitmask + maskwords);
+             cinfo.indx = cinfo.counts + bucketcount;
+             cinfo.symindx = dynsymcount - cinfo.nsyms;
+             memset (cinfo.bitmask, 0, maskwords * sizeof (bfd_vma));
+
+             /* Determine how often each hash bucket is used.  */
+             memset (cinfo.counts, 0, bucketcount * sizeof (cinfo.counts[0]));
+             for (i = 0; i < cinfo.nsyms; ++i)
+               ++cinfo.counts[cinfo.hashcodes[i] % bucketcount];
+
+             for (i = 0, cnt = cinfo.symindx; i < bucketcount; ++i)
+               if (cinfo.counts[i] != 0)
+                 {
+                   cinfo.indx[i] = cnt;
+                   cnt += cinfo.counts[i];
+                 }
+             BFD_ASSERT (cnt == dynsymcount);
+             cinfo.bucketcount = bucketcount;
+             cinfo.local_indx = cinfo.min_dynindx;
+
+             s->size = (4 + bucketcount + cinfo.nsyms) * 4;
+             s->size += cinfo.maskbits / 8;
+             contents = bfd_zalloc (output_bfd, s->size);
+             if (contents == NULL)
+               {
+                 free (cinfo.bitmask);
+                 free (cinfo.hashcodes);
+                 return FALSE;
+               }
+
+             s->contents = contents;
+             bfd_put_32 (output_bfd, bucketcount, contents);
+             bfd_put_32 (output_bfd, cinfo.symindx, contents + 4);
+             bfd_put_32 (output_bfd, maskwords, contents + 8);
+             bfd_put_32 (output_bfd, cinfo.shift2, contents + 12);
+             contents += 16 + cinfo.maskbits / 8;
+
+             for (i = 0; i < bucketcount; ++i)
+               {
+                 if (cinfo.counts[i] == 0)
+                   bfd_put_32 (output_bfd, 0, contents);
+                 else
+                   bfd_put_32 (output_bfd, cinfo.indx[i], contents);
+                 contents += 4;
+               }
+
+             cinfo.contents = contents;
+
+             /* Renumber dynamic symbols, populate .gnu.hash section.  */
+             elf_link_hash_traverse (elf_hash_table (info),
+                                     elf_renumber_gnu_hash_syms, &cinfo);
+
+             contents = s->contents + 16;
+             for (i = 0; i < maskwords; ++i)
+               {
+                 bfd_put (bed->s->arch_size, output_bfd, cinfo.bitmask[i],
+                          contents);
+                 contents += bed->s->arch_size / 8;
+               }
+
+             free (cinfo.bitmask);
+             free (cinfo.hashcodes);
+           }
+       }
 
       s = bfd_get_section_by_name (dynobj, ".dynstr");
       BFD_ASSERT (s != NULL);
@@ -6663,9 +6989,6 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
     {
       size_t bucketcount;
       size_t bucket;
-      size_t hash_entry_size;
-      bfd_byte *bucketpos;
-      bfd_vma chain;
       bfd_byte *esym;
 
       sym.st_name = h->dynstr_index;
@@ -6679,15 +7002,23 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
 
       bucketcount = elf_hash_table (finfo->info)->bucketcount;
       bucket = h->u.elf_hash_value % bucketcount;
-      hash_entry_size
-       = elf_section_data (finfo->hash_sec)->this_hdr.sh_entsize;
-      bucketpos = ((bfd_byte *) finfo->hash_sec->contents
-                  + (bucket + 2) * hash_entry_size);
-      chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos);
-      bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos);
-      bfd_put (8 * hash_entry_size, finfo->output_bfd, chain,
-              ((bfd_byte *) finfo->hash_sec->contents
-               + (bucketcount + 2 + h->dynindx) * hash_entry_size));
+
+      if (finfo->hash_sec != NULL)
+       {
+         size_t hash_entry_size;
+         bfd_byte *bucketpos;
+         bfd_vma chain;
+
+         hash_entry_size
+           = elf_section_data (finfo->hash_sec)->this_hdr.sh_entsize;
+         bucketpos = ((bfd_byte *) finfo->hash_sec->contents
+                      + (bucket + 2) * hash_entry_size);
+         chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos);
+         bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos);
+         bfd_put (8 * hash_entry_size, finfo->output_bfd, chain,
+                  ((bfd_byte *) finfo->hash_sec->contents
+                   + (bucketcount + 2 + h->dynindx) * hash_entry_size));
+       }
 
       if (finfo->symver_sec != NULL && finfo->symver_sec->contents != NULL)
        {
@@ -7861,7 +8192,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
     {
       finfo.dynsym_sec = bfd_get_section_by_name (dynobj, ".dynsym");
       finfo.hash_sec = bfd_get_section_by_name (dynobj, ".hash");
-      BFD_ASSERT (finfo.dynsym_sec != NULL && finfo.hash_sec != NULL);
+      BFD_ASSERT (finfo.dynsym_sec != NULL);
       finfo.symver_sec = bfd_get_section_by_name (dynobj, ".gnu.version");
       /* Note that it is OK if symver_sec is NULL.  */
     }
@@ -8621,6 +8952,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
            case DT_HASH:
              name = ".hash";
              goto get_vma;
+           case DT_GNU_HASH:
+             name = ".gnu.hash";
+             goto get_vma;
            case DT_STRTAB:
              name = ".dynstr";
              goto get_vma;
index 38aa373..fed6ea9 100644 (file)
 #define elf_backend_merge_symbol NULL
 #endif
 
+#ifndef elf_backend_hash_symbol
+#define elf_backend_hash_symbol _bfd_elf_hash_symbol
+#endif
+
 extern const struct elf_size_info _bfd_elfNN_size_info;
 
 #ifndef INCLUDED_TARGET_FILE
@@ -643,6 +647,7 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_common_section_index,
   elf_backend_common_section,
   elf_backend_merge_symbol,
+  elf_backend_hash_symbol,
   elf_backend_link_order_error_handler,
   elf_backend_relplt_name,
   ELF_MACHINE_ALT1,
index c7e4b3f..8508a18 100644 (file)
@@ -1,3 +1,11 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * readelf.c (get_dynamic_type): Handle DT_GNU_HASH.
+       (get_section_type_name): Handle SHT_GNU_HASH.
+       (dynamic_info_DT_GNU_HASH): New variable.
+       (process_dynamic_section): Handle DT_GNU_HASH.
+       (process_symbol_table): Print also DT_GNU_HASH histogram.
+
 2006-07-06  Mohammed Adnène Trojette  <adn@diwi.org>
 
        PR binutils/2879
index 8550ce4..55f5a43 100644 (file)
@@ -135,6 +135,7 @@ static unsigned long dynamic_syminfo_offset;
 static unsigned int dynamic_syminfo_nent;
 static char program_interpreter[64];
 static bfd_vma dynamic_info[DT_JMPREL + 1];
+static bfd_vma dynamic_info_DT_GNU_HASH;
 static bfd_vma version_info[16];
 static Elf_Internal_Ehdr elf_header;
 static Elf_Internal_Shdr *section_headers;
@@ -1501,6 +1502,7 @@ get_dynamic_type (unsigned long type)
     case DT_GNU_CONFLICTSZ: return "GNU_CONFLICTSZ";
     case DT_GNU_LIBLIST: return "GNU_LIBLIST";
     case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ";
+    case DT_GNU_HASH:  return "GNU_HASH";
 
     default:
       if ((type >= DT_LOPROC) && (type <= DT_HIPROC))
@@ -2571,6 +2573,7 @@ get_section_type_name (unsigned int sh_type)
     case SHT_INIT_ARRAY:       return "INIT_ARRAY";
     case SHT_FINI_ARRAY:       return "FINI_ARRAY";
     case SHT_PREINIT_ARRAY:    return "PREINIT_ARRAY";
+    case SHT_GNU_HASH:         return "GNU_HASH";
     case SHT_GROUP:            return "GROUP";
     case SHT_SYMTAB_SHNDX:     return "SYMTAB SECTION INDICIES";
     case SHT_GNU_verdef:       return "VERDEF";
@@ -6245,6 +6248,15 @@ process_dynamic_section (FILE *file)
            }
          break;
 
+       case DT_GNU_HASH:
+         dynamic_info_DT_GNU_HASH = entry->d_un.d_val;
+         if (do_dynamic)
+           {
+             print_vma (entry->d_un.d_val, PREFIX_HEX);
+             putchar ('\n');
+           }
+         break;
+
        default:
          if ((entry->d_tag >= DT_VERSYM) && (entry->d_tag <= DT_VERNEEDNUM))
            version_info[DT_VERSIONTAGIDX (entry->d_tag)] =
@@ -6920,6 +6932,9 @@ process_symbol_table (FILE *file)
   bfd_vma nchains = 0;
   bfd_vma *buckets = NULL;
   bfd_vma *chains = NULL;
+  bfd_vma ngnubuckets = 0;
+  bfd_vma *gnubuckets = NULL;
+  bfd_vma *gnuchains = NULL;
 
   if (! do_syms && !do_histogram)
     return 1;
@@ -7299,6 +7314,166 @@ process_symbol_table (FILE *file)
       free (chains);
     }
 
+  if (do_histogram && dynamic_info_DT_GNU_HASH)
+    {
+      unsigned char nb[16];
+      bfd_vma i, maxchain = 0xffffffff, symidx, bitmaskwords;
+      unsigned long *lengths;
+      unsigned long *counts;
+      unsigned long hn;
+      unsigned long maxlength = 0;
+      unsigned long nzero_counts = 0;
+      unsigned long nsyms = 0;
+      bfd_vma buckets_vma;
+
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, dynamic_info_DT_GNU_HASH,
+                                    sizeof nb)),
+                SEEK_SET))
+       {
+         error (_("Unable to seek to start of dynamic information"));
+         return 0;
+       }
+
+      if (fread (nb, 16, 1, file) != 1)
+       {
+         error (_("Failed to read in number of buckets\n"));
+         return 0;
+       }
+
+      ngnubuckets = byte_get (nb, 4);
+      symidx = byte_get (nb + 4, 4);
+      bitmaskwords = byte_get (nb + 8, 4);
+      buckets_vma = dynamic_info_DT_GNU_HASH + 16;
+      if (is_32bit_elf)
+       buckets_vma += bitmaskwords * 4;
+      else
+       buckets_vma += bitmaskwords * 8;
+
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, buckets_vma, 4)),
+                SEEK_SET))
+       {
+         error (_("Unable to seek to start of dynamic information"));
+         return 0;
+       }
+
+      gnubuckets = get_dynamic_data (file, ngnubuckets, 4);
+
+      if (gnubuckets == NULL)
+       return 0;
+
+      for (i = 0; i < ngnubuckets; i++)
+       if (gnubuckets[i] != 0)
+         {
+           if (gnubuckets[i] < symidx)
+             return 0;
+
+           if (maxchain == 0xffffffff || gnubuckets[i] > maxchain)
+             maxchain = gnubuckets[i];
+         }
+
+      if (maxchain == 0xffffffff)
+       return 0;
+
+      maxchain -= symidx;
+
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, buckets_vma
+                                          + 4 * (ngnubuckets + maxchain), 4)),
+                SEEK_SET))
+       {
+         error (_("Unable to seek to start of dynamic information"));
+         return 0;
+       }
+
+      do
+       {
+         if (fread (nb, 4, 1, file) != 1)
+           {
+             error (_("Failed to determine last chain length\n"));
+             return 0;
+           }
+
+         if (maxchain + 1 == 0)
+           return 0;
+
+         ++maxchain;
+       }
+      while ((byte_get (nb, 4) & 1) == 0);
+
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, buckets_vma + 4 * ngnubuckets, 4)),
+                SEEK_SET))
+       {
+         error (_("Unable to seek to start of dynamic information"));
+         return 0;
+       }
+
+      gnuchains = get_dynamic_data (file, maxchain, 4);
+
+      if (gnuchains == NULL)
+       return 0;
+
+      lengths = calloc (ngnubuckets, sizeof (*lengths));
+      if (lengths == NULL)
+       {
+         error (_("Out of memory"));
+         return 0;
+       }
+
+      printf (_("\nHistogram for `.gnu.hash' bucket list length (total of %lu buckets):\n"),
+             (unsigned long) ngnubuckets);
+      printf (_(" Length  Number     %% of total  Coverage\n"));
+
+      for (hn = 0; hn < ngnubuckets; ++hn)
+       if (gnubuckets[hn] != 0)
+         {
+           bfd_vma off, length = 1;
+
+           for (off = gnubuckets[hn] - symidx;
+                (gnuchains[off] & 1) == 0; ++off)
+             ++length;
+           lengths[hn] = length;
+           if (length > maxlength)
+             maxlength = length;
+           nsyms += length;
+         }
+
+      counts = calloc (maxlength + 1, sizeof (*counts));
+      if (counts == NULL)
+       {
+         error (_("Out of memory"));
+         return 0;
+       }
+
+      for (hn = 0; hn < ngnubuckets; ++hn)
+       ++counts[lengths[hn]];
+
+      if (ngnubuckets > 0)
+       {
+         unsigned long j;
+         printf ("      0  %-10lu (%5.1f%%)\n",
+                 counts[0], (counts[0] * 100.0) / ngnubuckets);
+         for (j = 1; j <= maxlength; ++j)
+           {
+             nzero_counts += counts[j] * j;
+             printf ("%7lu  %-10lu (%5.1f%%)    %5.1f%%\n",
+                     j, counts[j], (counts[j] * 100.0) / ngnubuckets,
+                     (nzero_counts * 100.0) / nsyms);
+           }
+       }
+
+      free (counts);
+      free (lengths);
+      free (gnubuckets);
+      free (gnuchains);
+    }
+
   return 1;
 }
 
index 6f695ff..75f1ed2 100644 (file)
@@ -1,3 +1,8 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * bfdlink.h (struct bfd_link_info): Add emit_hash and
+       emit_gnu_hash bitfields.
+
 2006-04-11  Jim Blandy  <jimb@codesourcery.com>
 
        * libiberty.h (pex_input_file, pex_input_pipe): New declarations.
index f4b7aa7..761bd05 100644 (file)
@@ -324,6 +324,12 @@ struct bfd_link_info
   /* TRUE if unreferenced sections should be removed.  */
   unsigned int gc_sections: 1;
 
+  /* TRUE if .hash section should be created.  */
+  unsigned int emit_hash: 1;
+
+  /* TRUE if .gnu.hash section should be created.  */
+  unsigned int emit_gnu_hash: 1;
+
   /* What to do with unresolved symbols in an object file.
      When producing executables the default is GENERATE_ERROR.
      When producing shared libraries the default is IGNORE.  The
index 285457e..f1c15b5 100644 (file)
@@ -1,3 +1,7 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * common.h (SHT_GNU_HASH, DT_GNU_HASH): Define.
+
 2006-05-31  H.J. Lu  <hongjiu.lu@intel.com>
 
        * internal.h (ELF_SECTION_SIZE): New.
index b11171b..0380a8f 100644 (file)
 #define SHT_LOOS       0x60000000      /* First of OS specific semantics */
 #define SHT_HIOS       0x6fffffff      /* Last of OS specific semantics */
 
+#define SHT_GNU_HASH   0x6ffffff6      /* GNU style symbol hash table */
 #define SHT_GNU_LIBLIST        0x6ffffff7      /* List of prelink dependencies */
 
 /* The next three section types are defined by Solaris, and are named
 #define DT_VALRNGHI    0x6ffffdff
 
 #define DT_ADDRRNGLO   0x6ffffe00
+#define DT_GNU_HASH    0x6ffffef5
 #define DT_TLSDESC_PLT 0x6ffffef6
 #define DT_TLSDESC_GOT 0x6ffffef7
 #define DT_GNU_CONFLICT        0x6ffffef8
index 1d1cc1b..24141f6 100644 (file)
@@ -1,3 +1,13 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * scripttempl/elf.sc: Add .gnu.hash section.
+       * emultempl/elf32.em (OPTION_HASH_STYLE): Define.
+       (gld${EMULATION_NAME}_add_options): Register --hash-style option.
+       (gld${EMULATION_NAME}_handle_option): Handle it.
+       (gld${EMULATION_NAME}_list_options): Document it.
+       * ldmain.c (main): Initialize emit_hash and emit_gnu_hash.
+       * ld.texinfo: Document --hash-style option.
+
 2006-07-10  Nick Clifton  <nickc@redhat.com>
 
        * po/zh_TW.po: New Chinese (traditional) translation.
index 9eb5323..7ce112a 100644 (file)
@@ -1719,6 +1719,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
 #define OPTION_GROUP                   (OPTION_ENABLE_NEW_DTAGS + 1)
 #define OPTION_EH_FRAME_HDR            (OPTION_GROUP + 1)
 #define OPTION_EXCLUDE_LIBS            (OPTION_EH_FRAME_HDR + 1)
+#define OPTION_HASH_STYLE              (OPTION_EXCLUDE_LIBS + 1)
 
 static void
 gld${EMULATION_NAME}_add_options
@@ -1735,6 +1736,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
     {"enable-new-dtags", no_argument, NULL, OPTION_ENABLE_NEW_DTAGS},
     {"eh-frame-hdr", no_argument, NULL, OPTION_EH_FRAME_HDR},
     {"exclude-libs", required_argument, NULL, OPTION_EXCLUDE_LIBS},
+    {"hash-style", required_argument, NULL, OPTION_HASH_STYLE},
     {"Bgroup", no_argument, NULL, OPTION_GROUP},
 EOF
 fi
@@ -1791,6 +1793,22 @@ cat >>e${EMULATION_NAME}.c <<EOF
       add_excluded_libs (optarg);
       break;
 
+    case OPTION_HASH_STYLE:
+      link_info.emit_hash = FALSE;
+      link_info.emit_gnu_hash = FALSE;
+      if (strcmp (optarg, "sysv") == 0)
+       link_info.emit_hash = TRUE;
+      else if (strcmp (optarg, "gnu") == 0)
+       link_info.emit_gnu_hash = TRUE;
+      else if (strcmp (optarg, "both") == 0)
+       {
+         link_info.emit_hash = TRUE;
+         link_info.emit_gnu_hash = TRUE;
+       }
+      else
+       einfo (_("%P%F: invalid hash style \`%s'\n"), optarg);
+      break;
+
     case 'z':
       if (strcmp (optarg, "initfirst") == 0)
        link_info.flags_1 |= (bfd_vma) DF_1_INITFIRST;
@@ -1894,6 +1912,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
   fprintf (file, _("  --disable-new-dtags\tDisable new dynamic tags\n"));
   fprintf (file, _("  --enable-new-dtags\tEnable new dynamic tags\n"));
   fprintf (file, _("  --eh-frame-hdr\tCreate .eh_frame_hdr section\n"));
+  fprintf (file, _("  --hash-style=STYLE\tSet hash style to sysv, gnu or both\n"));
   fprintf (file, _("  -z combreloc\t\tMerge dynamic relocs into one section and sort\n"));
   fprintf (file, _("  -z defs\t\tReport unresolved symbols in object files.\n"));
   fprintf (file, _("  -z execstack\t\tMark executable as requiring executable stack\n"));
index 72a3044..687f19c 100644 (file)
@@ -1883,6 +1883,14 @@ time it takes the linker to perform its tasks, at the expense of
 increasing the linker's memory requirements.  Similarly reducing this
 value can reduce the memory requirements at the expense of speed.
 
+@kindex --hash-style=@var{style}
+@item --hash-style=@var{style}
+Set the type of linker's hash table(s).  @var{style} can be either
+@code{sysv} for classic ELF @code{.hash} section, @code{gnu} for
+new style GNU @code{.gnu.hash} section or @code{both} for both
+the classic ELF @code{.hash} and new style GNU @code{.gnu.hash}
+hash tables.  The default is @code{sysv}.
+
 @kindex --reduce-memory-overheads
 @item --reduce-memory-overheads
 This option reduces memory requirements at ld runtime, at the expense of
index 8881190..ec31310 100644 (file)
@@ -304,6 +304,8 @@ main (int argc, char **argv)
   link_info.create_object_symbols_section = NULL;
   link_info.gc_sym_list = NULL;
   link_info.base_file = NULL;
+  link_info.emit_hash = TRUE;
+  link_info.emit_gnu_hash = FALSE;
   /* SVR4 linkers seem to set DT_INIT and DT_FINI based on magic _init
      and _fini symbols.  We are compatible.  */
   link_info.init_function = "_init";
index ac0c725..ebe387e 100644 (file)
@@ -260,6 +260,7 @@ SECTIONS
   ${INITIAL_READONLY_SECTIONS}
   ${TEXT_DYNAMIC+${DYNAMIC}}
   .hash         ${RELOCATING-0} : { *(.hash) }
+  .gnu.hash     ${RELOCATING-0} : { *(.gnu.hash) }
   .dynsym       ${RELOCATING-0} : { *(.dynsym) }
   .dynstr       ${RELOCATING-0} : { *(.dynstr) }
   .gnu.version  ${RELOCATING-0} : { *(.gnu.version) }
index 3507142..501335e 100644 (file)
@@ -1,3 +1,12 @@
+2006-07-10  Jakub Jelinek  <jakub@redhat.com>
+
+       * ld-powerpc/tlsso32.r: Adjust.
+       * ld-powerpc/tlsso32.d: Adjust.
+       * ld-powerpc/tlsso32.g: Adjust.
+       * ld-powerpc/tlsso.r: Adjust.
+       * ld-powerpc/tlsso.g: Adjust.
+       * ld-powerpc/tlstocso.g: Adjust.
+
 2006-07-05  Thiemo Seufer  <ths@mips.com>
 
        * ld-mips-elf/multi-got-1.d, ld-mips-elf/tls-multi-got-1.got,
index caef9dd..6113155 100644 (file)
@@ -7,7 +7,7 @@
 .*: +file format elf64-powerpc
 
 Contents of section \.got:
-.* 00000000 000187f0 00000000 00000000  .*
+.* 00000000 000187b8 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
index e0e0f00..db54154 100644 (file)
@@ -49,9 +49,9 @@ Relocation section '\.rela\.dyn' at offset .* contains 16 entries:
 [0-9a-f ]+R_PPC64_TPREL16 +0+60 le0 \+ 0
 [0-9a-f ]+R_PPC64_TPREL16_HA +0+68 le1 \+ 0
 [0-9a-f ]+R_PPC64_TPREL16_LO +0+68 le1 \+ 0
-[0-9a-f ]+R_PPC64_TPREL16_DS +0+10668 \.tdata \+ 28
-[0-9a-f ]+R_PPC64_TPREL16_HA +0+10668 \.tdata \+ 30
-[0-9a-f ]+R_PPC64_TPREL16_LO +0+10668 \.tdata \+ 30
+[0-9a-f ]+R_PPC64_TPREL16_DS +0+10630 \.tdata \+ 28
+[0-9a-f ]+R_PPC64_TPREL16_HA +0+10630 \.tdata \+ 30
+[0-9a-f ]+R_PPC64_TPREL16_LO +0+10630 \.tdata \+ 30
 [0-9a-f ]+R_PPC64_DTPMOD64 +0+
 [0-9a-f ]+R_PPC64_DTPMOD64 +0+
 [0-9a-f ]+R_PPC64_DTPREL64 +0+
index 45432db..dafca7d 100644 (file)
@@ -42,5 +42,5 @@ Disassembly of section \.got:
 .* <\.got>:
        \.\.\.
 .*:    4e 80 00 21     blrl
-.*:    00 01 04 38     .*
+.*:    00 01 04 00     .*
        \.\.\.
index 7014419..c097ffa 100644 (file)
@@ -9,5 +9,5 @@
 Contents of section \.got:
 .* 00000000 00000000 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
-.* 00000000 4e800021 00010438 00000000  .*
+.* 00000000 4e800021 00010400 00000000  .*
 .* 00000000                             .*
index 7044c35..a0ede5f 100644 (file)
@@ -52,9 +52,9 @@ Relocation section '\.rela\.dyn' at offset 0x[0-9a-f]+ contains 18 entries:
 [0-9a-f ]+R_PPC_TPREL16 +0+30 +le0 \+ 0
 [0-9a-f ]+R_PPC_TPREL16_HA +0+34 +le1 \+ 0
 [0-9a-f ]+R_PPC_TPREL16_LO +0+34 +le1 \+ 0
-[0-9a-f ]+R_PPC_TPREL16 +0+1041c +\.tdata \+ 10430
-[0-9a-f ]+R_PPC_TPREL16_HA +0+1041c +\.tdata \+ 10434
-[0-9a-f ]+R_PPC_TPREL16_LO +0+1041c +\.tdata \+ 10434
+[0-9a-f ]+R_PPC_TPREL16 +0+103e4 +\.tdata \+ 103f8
+[0-9a-f ]+R_PPC_TPREL16_HA +0+103e4 +\.tdata \+ 103fc
+[0-9a-f ]+R_PPC_TPREL16_LO +0+103e4 +\.tdata \+ 103fc
 [0-9a-f ]+R_PPC_DTPMOD32 +0+
 [0-9a-f ]+R_PPC_DTPREL32 +0+
 [0-9a-f ]+R_PPC_DTPMOD32 +0+
index b5d7d64..3d59c43 100644 (file)
@@ -7,7 +7,7 @@
 .*: +file format elf64-powerpc
 
 Contents of section \.got:
-.* 00000000 00018738 00000000 00000000  .*
+.* 00000000 00018700 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*
 .* 00000000 00000000 00000000 00000000  .*