[PATCH] MIPS support for GNU hash

Mihailo Stojanovic mihailo.stojanovic@rt-rk.com
Wed Jun 19 16:31:00 GMT 2019


Hello everyone,

  This patch is a reimplementation of [1] which was submitted in 2015 by
Neil Schellenberger. Copyright issue was sorted out [2] last year.
It proposed a new section (.gnu.xhash) and related dynamic tag
(DT_GNU_XHASH). The new section would be virtually identical to the
existing .gnu.hash except for the translation table (xlat) which would
contain correct MIPS .dynsym indexes corresponding to the hashvals in
chains. This is because MIPS ABI imposes a different ordering on the
dynsyms than the one expected by the .gnu.hash section. Another addition
would be a leading word (ngnusyms) which would contain the number of
entries in the translation table.

  This patch addresses the alignment issue as reported in [3], which is
caused by the leading word added to the .gnu.xhash section. Leading word
is removed in this patch, and the number of entries in the translation
table is now calculated using DT_MIPS_SYMTABNO dynamic tag (this is
addressed by the corresponding glibc patch).

  Suggestions on coding style in [4] were taken into account. Existing
GNU hash testcase was covered, and another one was added in the MIPS
part of the testsuite.

  The other major change is reserving MIPS ABI version 5 for .gnu.xhash,
marking the need of support for .gnu.xhash in the dynamic linker (again,
addressed in the corresponding glibc patch). This is something which I
am not sure of, especially after reading [5]. I am confused on whether
this ABI version is reserved for IFUNC, or it can be used for this
purpose.

Already mentioned glibc patch should appear soon on:
https://www.sourceware.org/ml/libc-alpha/2019-06/

Regards,
Mihailo

[1] https://sourceware.org/ml/binutils/2015-10/msg00057.html
[2] https://sourceware.org/ml/binutils/2018-03/msg00025.html
[3] https://sourceware.org/ml/binutils/2016-01/msg00006.html
[4] https://sourceware.org/ml/binutils/2016-02/msg00097.html
[5] https://sourceware.org/ml/libc-alpha/2016-12/msg00853.html

ld/ChangeLog:

        * emultempl/mipself.em: Remove mips_after_parse function.
        * scripttempl/elf.sc: Add .gnu.xhash section to the ld scripts
        * testsuite/ld-elf/hash.d: Modify testcase to cover .gnu.xhash
        * testsuite/ld-mips-elf/hash1.d: New test.
        * testsuite/ld-mips-elf/hash1.s: Ditto.
        * testsuite/ld-mips-elf/hash1a.d: Remove.
        * testsuite/ld-mips-elf/hash1b.d: Ditto.
        * testsuite/ld-mips-elf/hash1c.d: Ditto
        * testsuite/ld-mips-elf/mips-elf.exp: New test.

bfd/ChangeLog:

        * elf-bfd.h (struct elf_backend_data): New members.
        * elf.c (_bfd_elf_print_private_bfd_data): .gnu.xhash dynamic
        tag.
        (bfd_section_from_shdr): Make .gnu.xhash section from shdr.
        (elf_fake_sections): .gnu.xhash entry size.
        (assign_section_numbers): Set sh_link of .gnu.xhash section.
        * elflink.c (_bfd_elf_link_create_dynamic_sections): Create
        .gnu.xhash section.
        (struct collect_gnu_hash_codes): Comment correction.
        (elf_gnu_hash_process_symidx): New function name.
        (elf_renumber_gnu_hash_syms): Ignore local and undefined
        symbols. Record xlat location for every symbol which should have
        a .gnu.xhash entry.
        (bfd_elf_size_dynamic_sections): Add DT_GNU_XHASH dynamic tag to
        dynamic section.
        (bfd_elf_size_dynsym_hash_dynstr): Get .gnu.xhash section.
        (bfd_elf_final_link): Add .gnu.xhash section name string.
        * elfxx-mips.c (struct mips_elf_hash_sort_data): New members.
        (struct mips_elf_link_hash_entry): New member.
        (mips_elf_link_hash_newfunc): Initialize .gnu.xhash translation
        table location.
        (mips_elf_sort_hash_table): Initialize the pointer to the
        .gnu.xhash section.
        (mips_elf_sort_hash_table_f): Populate the .gnu.xhash
        translation table entry with the symbol dynindx.
        (MIPS_LIBC_ABI_GNU_XHASH): New ABI version enum value.
        (_bfd_mips_post_process_headers): Mark the ABI version as
        MIPS_LIBC_ABI_GNU_XHASH if there exists a .gnu.xhash section,
        but not a .hash section.
        (_bfd_mips_elf_record_xhash_symbol): New function. Record a
        position in the translation table, associated with the hash
        entry.
        * elfxx-mips.h (literal_reloc_p): Define
        elf_backend_record_xhash_symbol backend hook.
        * elfxx-target.h: Initialize elf_backend_record_xhash_symbol
        backend hook.

include/ChangeLog:

        * elf/common.h (SHT_GNU_XHASH): New macro.
        (DT_GNU_XHASH): New macro.

binutils/ChangeLog:

        * readelf.c (get_dynamic_type): Return GNU_XHASH dynamic type.
        (get_section_type_name): Return GNU_XHASH name string.
        (process_dynamic_section): Initialize the .gnu.xhash dynamic
        info.
        (process_symbol_table): Initialize the .gnu.xhash section
        pointer. Adjust the readelf output to support the new section.
        (process_object): Set the .gnu.xhash dynamic info to zero.
---
 bfd/elf-bfd.h                         |  7 +++++
 bfd/elf.c                             |  5 ++++
 bfd/elflink.c                         | 56 ++++++++++++++++++++++++++++-------
 bfd/elfxx-mips.c                      | 47 +++++++++++++++++++++++++++++
 bfd/elfxx-mips.h                      |  4 +++
 bfd/elfxx-target.h                    |  5 ++++
 binutils/readelf.c                    | 46 +++++++++++++++++++++++++---
 include/elf/common.h                  |  2 ++
 ld/emultempl/mipself.em               | 16 ----------
 ld/scripttempl/elf.sc                 |  1 +
 ld/testsuite/ld-elf/hash.d            |  5 +---
 ld/testsuite/ld-mips-elf/hash1.d      | 10 +++++++
 ld/testsuite/ld-mips-elf/hash1.s      |  8 ++++-
 ld/testsuite/ld-mips-elf/hash1a.d     |  5 ----
 ld/testsuite/ld-mips-elf/hash1b.d     |  4 ---
 ld/testsuite/ld-mips-elf/hash1c.d     |  4 ---
 ld/testsuite/ld-mips-elf/mips-elf.exp |  4 +--
 17 files changed, 177 insertions(+), 52 deletions(-)
 create mode 100644 ld/testsuite/ld-mips-elf/hash1.d
 delete mode 100644 ld/testsuite/ld-mips-elf/hash1a.d
 delete mode 100644 ld/testsuite/ld-mips-elf/hash1b.d
 delete mode 100644 ld/testsuite/ld-mips-elf/hash1c.d

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 0d12f45..52c6ba4 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1393,6 +1393,13 @@ struct elf_backend_data
   /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
   bfd_boolean (*elf_hash_symbol) (struct elf_link_hash_entry *);
 
+  /* If non-NULL, called to register the location of XLAT_LOC within
+     .gnu.xhash at which real final dynindx for H will be written.
+     If XLAT_LOC is zero, the symbol is not included in
+     .gnu.xhash and no dynindx will be written.  */
+  void (*record_xhash_symbol)
+    (struct elf_link_hash_entry *h, bfd_vma xlat_loc);
+
   /* Return TRUE if type is a function symbol type.  */
   bfd_boolean (*is_function_type) (unsigned int type);
 
diff --git a/bfd/elf.c b/bfd/elf.c
index b463f1d..537e4c0 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1777,6 +1777,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
 	    case DT_USED: name = "USED"; break;
 	    case DT_FILTER: name = "FILTER"; stringp = TRUE; break;
 	    case DT_GNU_HASH: name = "GNU_HASH"; break;
+	    case DT_GNU_XHASH: name = "GNU_XHASH"; break;
 	    }
 
 	  fprintf (f, "  %-20s ", name);
@@ -2072,6 +2073,7 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
     case SHT_PREINIT_ARRAY:	/* .preinit_array section.  */
     case SHT_GNU_LIBLIST:	/* .gnu.liblist section.  */
     case SHT_GNU_HASH:		/* .gnu.hash section.  */
+    case SHT_GNU_XHASH:         /* .gnu.xhash section.  */
       ret = _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
       goto success;
 
@@ -2658,6 +2660,7 @@ static const struct bfd_elf_special_section special_sections_g[] =
   { STRING_COMMA_LEN (".gnu.liblist"),	   0, SHT_GNU_LIBLIST, SHF_ALLOC },
   { STRING_COMMA_LEN (".gnu.conflict"),	   0, SHT_RELA,	       SHF_ALLOC },
   { STRING_COMMA_LEN (".gnu.hash"),	   0, SHT_GNU_HASH,    SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu.xhash"),	   0, SHT_GNU_XHASH,   SHF_ALLOC },
   { NULL,			 0,	   0, 0,	       0 }
 };
 
@@ -3360,6 +3363,7 @@ elf_fake_sections (bfd *abfd, asection *asect, void *fsarg)
       break;
 
     case SHT_GNU_HASH:
+    case SHT_GNU_XHASH:
       this_hdr->sh_entsize = bed->s->arch_size == 64 ? 0 : 4;
       break;
     }
@@ -3975,6 +3979,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
 
 	case SHT_HASH:
 	case SHT_GNU_HASH:
+	case SHT_GNU_XHASH:
 	case SHT_GNU_versym:
 	  /* sh_link is the section header index of the symbol table
 	     this hash table or version table is for.  */
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 02ea2dc..1c0a7c6 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -341,8 +341,12 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
 
   if (info->emit_gnu_hash)
     {
-      s = bfd_make_section_anyway_with_flags (abfd, ".gnu.hash",
-					      flags | SEC_READONLY);
+      if (bed->record_xhash_symbol != NULL)
+	s = bfd_make_section_anyway_with_flags (abfd, ".gnu.xhash",
+						flags | SEC_READONLY);
+      else
+	s = bfd_make_section_anyway_with_flags (abfd, ".gnu.hash",
+						flags | SEC_READONLY);
       if (s == NULL
 	  || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
 	return FALSE;
@@ -5845,6 +5849,7 @@ struct collect_gnu_hash_codes
   unsigned long int *counts;
   bfd_vma *bitmask;
   bfd_byte *contents;
+  bfd_size_type xlat;
   long int min_dynindx;
   unsigned long int bucketcount;
   unsigned long int symindx;
@@ -5909,10 +5914,12 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
 }
 
 /* This function will be called though elf_link_hash_traverse to do
-   final dynaminc symbol renumbering.  */
+   final dynamic symbol renumbering in case of .gnu.hash.
+   If using .gnu.xhash, invoke record_xhash_symbol to add symbol index
+   to the translation table.  */
 
 static bfd_boolean
-elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data)
+elf_gnu_hash_process_symidx (struct elf_link_hash_entry *h, void *data)
 {
   struct collect_gnu_hash_codes *s = (struct collect_gnu_hash_codes *) data;
   unsigned long int bucket;
@@ -5926,7 +5933,15 @@ elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data)
   if (! (*s->bed->elf_hash_symbol) (h))
     {
       if (h->dynindx >= s->min_dynindx)
-	h->dynindx = s->local_indx++;
+	{
+	  if (s->bed->record_xhash_symbol != NULL)
+	    {
+	      (*s->bed->record_xhash_symbol) (h, 0);
+	      s->local_indx++;
+	    }
+	  else
+	    h->dynindx = s->local_indx++;
+	}
       return TRUE;
     }
 
@@ -5943,7 +5958,14 @@ elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data)
   bfd_put_32 (s->output_bfd, val,
 	      s->contents + (s->indx[bucket] - s->symindx) * 4);
   --s->counts[bucket];
-  h->dynindx = s->indx[bucket]++;
+  if (s->bed->record_xhash_symbol != NULL)
+    {
+      bfd_vma xlat_loc = s->xlat + (s->indx[bucket]++ - s->symindx) * 4;
+
+      (*s->bed->record_xhash_symbol) (h, xlat_loc);
+    }
+  else
+    h->dynindx = s->indx[bucket]++;
   return TRUE;
 }
 
@@ -6972,7 +6994,9 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 	  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))
+		  && (bed->record_xhash_symbol != NULL
+		      ? !_bfd_elf_add_dynamic_entry (info, DT_GNU_XHASH, 0)
+		      : !_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)
@@ -7259,12 +7283,15 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 	      return FALSE;
 	    }
 
-	  s = bfd_get_linker_section (dynobj, ".gnu.hash");
+	  if (bed->record_xhash_symbol != NULL)
+	    s = bfd_get_linker_section (dynobj, ".gnu.xhash");
+	  else
+	    s = bfd_get_linker_section (dynobj, ".gnu.hash");
 	  BFD_ASSERT (s != NULL);
 
 	  if (cinfo.nsyms == 0)
 	    {
-	      /* Empty .gnu.hash section is special.  */
+	      /* Empty .gnu.hash or .gnu.xhash section is special.  */
 	      BFD_ASSERT (cinfo.min_dynindx == -1);
 	      free (cinfo.hashcodes);
 	      s->size = 5 * 4 + bed->s->arch_size / 8;
@@ -7344,6 +7371,8 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 
 	      s->size = (4 + bucketcount + cinfo.nsyms) * 4;
 	      s->size += cinfo.maskbits / 8;
+	      if (bed->record_xhash_symbol != NULL)
+		s->size += cinfo.nsyms * 4;
 	      contents = (unsigned char *) bfd_zalloc (output_bfd, s->size);
 	      if (contents == NULL)
 		{
@@ -7370,9 +7399,11 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 
 	      cinfo.contents = contents;
 
-	      /* Renumber dynamic symbols, populate .gnu.hash section.  */
+	      cinfo.xlat = contents + cinfo.nsyms * 4 - s->contents;
+	      /* Renumber dynamic symbols, if populating .gnu.hash section.
+		 If using .gnu.xhash, populate the translation table.  */
 	      elf_link_hash_traverse (elf_hash_table (info),
-				      elf_renumber_gnu_hash_syms, &cinfo);
+				      elf_gnu_hash_process_symidx, &cinfo);
 
 	      contents = s->contents + 16;
 	      for (i = 0; i < maskwords; ++i)
@@ -12612,6 +12643,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	    case DT_GNU_HASH:
 	      name = ".gnu.hash";
 	      goto get_vma;
+	    case DT_GNU_XHASH:
+	      name = ".gnu.xhash";
+	      goto get_vma;
 	    case DT_STRTAB:
 	      name = ".dynstr";
 	      goto get_vma;
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 55f891d..bd26668 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -322,6 +322,11 @@ struct mips_elf_hash_sort_data
   /* The greatest dynamic symbol table index corresponding to an external
      symbol without a GOT entry.  */
   bfd_size_type max_non_got_dynindx;
+  /* If non-NULL, output BFD for .gnu.xhash finalization.  */
+  bfd *output_bfd;
+  /* If non-NULL, pointer to contents of .gnu.xhash for filling in
+     real final dynindx.  */
+  bfd_byte *gnuxhash;
 };
 
 /* We make up to two PLT entries if needed, one for standard MIPS code
@@ -379,6 +384,9 @@ struct mips_elf_link_hash_entry
      being called returns a floating point value.  */
   asection *call_fp_stub;
 
+  /* If non-zero, location in .gnu.xhash to write real final dynindx.  */
+  bfd_vma gnuxhash_loc;
+
   /* The highest GGA_* value that satisfies all references to this symbol.  */
   unsigned int global_got_area : 2;
 
@@ -1335,6 +1343,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->fn_stub = NULL;
       ret->call_stub = NULL;
       ret->call_fp_stub = NULL;
+      ret->gnuxhash_loc = 0;
       ret->global_got_area = GGA_NONE;
       ret->got_only_for_calls = TRUE;
       ret->readonly_reloc = FALSE;
@@ -3907,6 +3916,18 @@ mips_elf_sort_hash_table (bfd *abfd, struct bfd_link_info *info)
      at the head of the table; see `_bfd_elf_link_renumber_dynsyms'.  */
   hsd.max_local_dynindx = count_section_dynsyms (abfd, info) + 1;
   hsd.max_non_got_dynindx = htab->root.local_dynsymcount + 1;
+  hsd.output_bfd = abfd;
+  if (htab->root.dynobj != NULL
+      && htab->root.dynamic_sections_created
+      && info->emit_gnu_hash)
+    {
+      asection *s = bfd_get_linker_section (htab->root.dynobj, ".gnu.xhash");
+      BFD_ASSERT (s != NULL);
+      hsd.gnuxhash = s->contents;
+      BFD_ASSERT (hsd.gnuxhash != NULL);
+    }
+  else
+    hsd.gnuxhash = NULL;
   mips_elf_link_hash_traverse (htab, mips_elf_sort_hash_table_f, &hsd);
 
   /* There should have been enough room in the symbol table to
@@ -3958,6 +3979,12 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
       break;
     }
 
+  /* Populate the .gnu.xhash translation table entry with
+     the symbol dynindx.  */
+  if (h->gnuxhash_loc != 0 && hsd->gnuxhash != NULL)
+    bfd_put_32 (hsd->output_bfd, h->root.dynindx,
+		hsd->gnuxhash + h->gnuxhash_loc);
+
   return TRUE;
 }
 
@@ -16578,6 +16605,7 @@ enum
   MIPS_LIBC_ABI_UNIQUE,
   MIPS_LIBC_ABI_MIPS_O32_FP64,
   MIPS_LIBC_ABI_ABSOLUTE,
+  MIPS_LIBC_ABI_GNU_XHASH,
   MIPS_LIBC_ABI_MAX
 };
 
@@ -16605,6 +16633,11 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
   if (htab != NULL && htab->use_absolute_zero && htab->gnu_target)
     i_ehdrp->e_ident[EI_ABIVERSION] = MIPS_LIBC_ABI_ABSOLUTE;
 
+  /* Mark that we need support for .gnu.xhash in the dynamic linker,
+     if it is the only hash section that will be created.  */
+  if (link_info && link_info->emit_gnu_hash && !link_info->emit_hash)
+    i_ehdrp->e_ident[EI_ABIVERSION] = MIPS_LIBC_ABI_GNU_XHASH;
+
   _bfd_elf_post_process_headers (abfd, link_info);
 }
 
@@ -16623,3 +16656,17 @@ _bfd_mips_elf_cant_unwind_opcode
 {
   return COMPACT_EH_CANT_UNWIND_OPCODE;
 }
+
+/* Record a position XLAT_LOC in the xlat translation table, associated with
+   the hash entry H.  The entry in the translation table will later be
+   populated with the real symbol dynindx.  */
+
+void
+_bfd_mips_elf_record_xhash_symbol (struct elf_link_hash_entry *h,
+				   bfd_vma xlat_loc)
+{
+  struct mips_elf_link_hash_entry *hmips;
+
+  hmips = (struct mips_elf_link_hash_entry *) h;
+  hmips->gnuxhash_loc = xlat_loc;
+}
diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h
index acd9d79..bbbd672 100644
--- a/bfd/elfxx-mips.h
+++ b/bfd/elfxx-mips.h
@@ -171,6 +171,9 @@ extern bfd_boolean _bfd_mips_elf_common_definition (Elf_Internal_Sym *);
 extern int _bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *);
 extern int _bfd_mips_elf_cant_unwind_opcode (struct bfd_link_info *);
 
+extern void _bfd_mips_elf_record_xhash_symbol
+  (struct elf_link_hash_entry *h, bfd_vma xlat_loc);
+
 static inline bfd_boolean
 gprel16_reloc_p (unsigned int r_type)
 {
@@ -196,4 +199,5 @@ literal_reloc_p (int r_type)
 #define elf_backend_post_process_headers _bfd_mips_post_process_headers
 #define elf_backend_compact_eh_encoding _bfd_mips_elf_compact_eh_encoding
 #define elf_backend_cant_unwind_opcode _bfd_mips_elf_cant_unwind_opcode
+#define elf_backend_record_xhash_symbol _bfd_mips_elf_record_xhash_symbol
 #define elf_backend_always_renumber_dynsyms TRUE
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index e4e7546..e5f8ea2 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -734,6 +734,10 @@
 #define elf_backend_hash_symbol _bfd_elf_hash_symbol
 #endif
 
+#ifndef elf_backend_record_xhash_symbol
+#define elf_backend_record_xhash_symbol NULL
+#endif
+
 #ifndef elf_backend_is_function_type
 #define elf_backend_is_function_type _bfd_elf_is_function_type
 #endif
@@ -858,6 +862,7 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_common_section,
   elf_backend_merge_symbol,
   elf_backend_hash_symbol,
+  elf_backend_record_xhash_symbol,
   elf_backend_is_function_type,
   elf_backend_maybe_function_sym,
   elf_backend_get_reloc_section,
diff --git a/binutils/readelf.c b/binutils/readelf.c
index febfd1a..652e044 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -232,6 +232,7 @@ static unsigned int dynamic_syminfo_nent;
 static char program_interpreter[PATH_MAX];
 static bfd_vma dynamic_info[DT_ENCODING];
 static bfd_vma dynamic_info_DT_GNU_HASH;
+static bfd_vma dynamic_info_DT_GNU_XHASH;
 static bfd_vma version_info[16];
 static Elf_Internal_Dyn *  dynamic_section;
 static elf_section_list * symtab_shndx_list;
@@ -2188,6 +2189,7 @@ get_dynamic_type (Filedata * filedata, unsigned long type)
     case DT_GNU_LIBLIST: return "GNU_LIBLIST";
     case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ";
     case DT_GNU_HASH:	return "GNU_HASH";
+    case DT_GNU_XHASH:  return "GNU_XHASH";
 
     default:
       if ((type >= DT_LOPROC) && (type <= DT_HIPROC))
@@ -4268,6 +4270,7 @@ get_section_type_name (Filedata * filedata, unsigned int sh_type)
     case SHT_FINI_ARRAY:	return "FINI_ARRAY";
     case SHT_PREINIT_ARRAY:	return "PREINIT_ARRAY";
     case SHT_GNU_HASH:		return "GNU_HASH";
+    case SHT_GNU_XHASH:         return "GNU_XHASH";
     case SHT_GROUP:		return "GROUP";
     case SHT_SYMTAB_SHNDX:	return "SYMTAB SECTION INDICES";
     case SHT_GNU_verdef:	return "VERDEF";
@@ -10416,6 +10419,9 @@ process_dynamic_section (Filedata * filedata)
 	    }
 	  break;
 
+	case DT_GNU_XHASH:
+	  dynamic_info_DT_GNU_XHASH = entry->d_un.d_val;
+	  /* Falls through.  */
 	case DT_GNU_HASH:
 	  dynamic_info_DT_GNU_HASH = entry->d_un.d_val;
 	  if (do_dynamic)
@@ -11632,6 +11638,7 @@ process_symbol_table (Filedata * filedata)
   bfd_vma ngnubuckets = 0;
   bfd_vma * gnubuckets = NULL;
   bfd_vma * gnuchains = NULL;
+  bfd_vma * gnuxlat = NULL;
   bfd_vma gnusymidx = 0;
   bfd_size_type ngnuchains = 0;
 
@@ -11797,7 +11804,31 @@ process_symbol_table (Filedata * filedata)
       gnuchains = get_dynamic_data (filedata, maxchain, 4);
       ngnuchains = maxchain;
 
+      if (gnuchains == NULL)
+	goto no_gnu_hash;
+
+      if (dynamic_info_DT_GNU_XHASH)
+	{
+	  if (fseek (filedata->handle,
+		     (archive_file_offset
+		      + offset_from_vma (filedata, (buckets_vma
+						    + 4 * (ngnubuckets
+							   + maxchain)), 4)),
+		     SEEK_SET))
+	    {
+	      error (_("Unable to seek to start of dynamic information\n"));
+	      goto no_gnu_hash;
+	    }
+
+	  gnuxlat = get_dynamic_data (filedata, maxchain, 4);
+	}
+
     no_gnu_hash:
+      if (dynamic_info_DT_GNU_XHASH && gnuxlat == NULL)
+	{
+	  free (gnuchains);
+	  gnuchains = NULL;
+	}
       if (gnuchains == NULL)
 	{
 	  free (gnubuckets);
@@ -11847,7 +11878,8 @@ process_symbol_table (Filedata * filedata)
 
       if (dynamic_info_DT_GNU_HASH)
 	{
-	  printf (_("\nSymbol table of `.gnu.hash' for image:\n"));
+	  printf (_("\nSymbol table of `%s' for image:\n"),
+		  dynamic_info_DT_GNU_XHASH ? ".gnu.xhash" : ".gnu.hash");
 	  if (is_32bit_elf)
 	    printf (_("  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name\n"));
 	  else
@@ -11861,7 +11893,10 @@ process_symbol_table (Filedata * filedata)
 
 		do
 		  {
-		    print_dynamic_symbol (filedata, si, hn);
+		    if (dynamic_info_DT_GNU_XHASH)
+		      print_dynamic_symbol (filedata, gnuxlat[off], hn);
+		    else
+		      print_dynamic_symbol (filedata, si, hn);
 		    si++;
 		  }
 		while (off < ngnuchains && (gnuchains[off++] & 1) == 0);
@@ -12084,11 +12119,12 @@ process_symbol_table (Filedata * filedata)
       unsigned long nzero_counts = 0;
       unsigned long nsyms = 0;
 
-      printf (ngettext ("\nHistogram for `.gnu.hash' bucket list length "
+      printf (ngettext ("\nHistogram for `%s' bucket list length "
 			"(total of %lu bucket):\n",
-			"\nHistogram for `.gnu.hash' bucket list length "
+			"\nHistogram for `%s' bucket list length "
 			"(total of %lu buckets):\n",
 			(unsigned long) ngnubuckets),
+	      dynamic_info_DT_GNU_XHASH ? ".gnu.xhash" : ".gnu.hash",
 	      (unsigned long) ngnubuckets);
 
       lengths = (unsigned long *) calloc (ngnubuckets, sizeof (*lengths));
@@ -12145,6 +12181,7 @@ process_symbol_table (Filedata * filedata)
       free (lengths);
       free (gnubuckets);
       free (gnuchains);
+      free (gnuxlat);
     }
 
   return TRUE;
@@ -19625,6 +19662,7 @@ process_object (Filedata * filedata)
   for (i = ARRAY_SIZE (dynamic_info); i--;)
     dynamic_info[i] = 0;
   dynamic_info_DT_GNU_HASH = 0;
+  dynamic_info_DT_GNU_XHASH = 0;
 
   /* Process the file.  */
   if (show_name)
diff --git a/include/elf/common.h b/include/elf/common.h
index 75c4fb7..8994b4e 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -510,6 +510,7 @@
 #define SHT_HIOS	0x6fffffff	/* Last of OS specific semantics */
 
 #define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700   /* incremental build data */
+#define SHT_GNU_XHASH 0x6ffffff4	/* GNU style symbol hash table with xlat */
 #define SHT_GNU_ATTRIBUTES 0x6ffffff5	/* Object attributes */
 #define SHT_GNU_HASH	0x6ffffff6	/* GNU style symbol hash table */
 #define SHT_GNU_LIBLIST	0x6ffffff7	/* List of prelink dependencies */
@@ -1012,6 +1013,7 @@
 #define DT_VALRNGHI	0x6ffffdff
 
 #define DT_ADDRRNGLO	0x6ffffe00
+#define DT_GNU_XHASH	0x6ffffef4
 #define DT_GNU_HASH	0x6ffffef5
 #define DT_TLSDESC_PLT	0x6ffffef6
 #define DT_TLSDESC_GOT	0x6ffffef7
diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em
index ec908d7..fe46e0d 100644
--- a/ld/emultempl/mipself.em
+++ b/ld/emultempl/mipself.em
@@ -46,21 +46,6 @@ static bfd_boolean insn32;
 static bfd_boolean ignore_branch_isa;
 static bfd_boolean compact_branches;
 
-static void
-mips_after_parse (void)
-{
-  /* .gnu.hash and the MIPS ABI require .dynsym to be sorted in different
-     ways.  .gnu.hash needs symbols to be grouped by hash code whereas the
-     MIPS ABI requires a mapping between the GOT and the symbol table.  */
-  if (link_info.emit_gnu_hash)
-    {
-      einfo (_("%X%P: .gnu.hash is incompatible with the MIPS ABI\n"));
-      link_info.emit_hash = TRUE;
-      link_info.emit_gnu_hash = FALSE;
-    }
-  gld${EMULATION_NAME}_after_parse ();
-}
-
 struct hook_stub_info
 {
   lang_statement_list_type add;
@@ -337,6 +322,5 @@ PARSE_AND_LIST_ARGS_CASES='
       break;
 '
 
-LDEMUL_AFTER_PARSE=mips_after_parse
 LDEMUL_BEFORE_ALLOCATION=mips_before_allocation
 LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=mips_create_output_section_statements
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index f9f0f7d..b16f4fa 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -391,6 +391,7 @@ cat > ldscripts/dyntmp.$$ <<EOF
   ${TEXT_DYNAMIC+${DYNAMIC}}
   .hash         ${RELOCATING-0} : { *(.hash) }
   .gnu.hash     ${RELOCATING-0} : { *(.gnu.hash) }
+  .gnu.xhash    ${RELOCATING-0} : { *(.gnu.xhash) }
   .dynsym       ${RELOCATING-0} : { *(.dynsym) }
   .dynstr       ${RELOCATING-0} : { *(.dynstr) }
   .gnu.version  ${RELOCATING-0} : { *(.gnu.version) }
diff --git a/ld/testsuite/ld-elf/hash.d b/ld/testsuite/ld-elf/hash.d
index fb07912..6570abe 100644
--- a/ld/testsuite/ld-elf/hash.d
+++ b/ld/testsuite/ld-elf/hash.d
@@ -2,12 +2,9 @@
 #readelf: -d -s -D
 #ld: -shared --hash-style=gnu
 #target: *-*-linux* *-*-gnu* arm*-*-uclinuxfdpiceabi
-#xfail: mips*-*-*
-# GNU hash is not supported for MIPS targets due to psABI restrictions
-# on dynsym table ordering.
 
 #...
- +0x[0-9a-z]+ +\(GNU_HASH\) +0x[0-9a-z]+
+ +0x[0-9a-z]+ +\(GNU_X?HASH\) +0x[0-9a-z]+
 #...
  +[0-9]+ +[0-9]+: +[0-9a-f]+ +[0-9]+ +NOTYPE +GLOBAL +DEFAULT +[1-9] _start
 #...
diff --git a/ld/testsuite/ld-mips-elf/hash1.d b/ld/testsuite/ld-mips-elf/hash1.d
new file mode 100644
index 0000000..e1bccf4
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/hash1.d
@@ -0,0 +1,10 @@
+#source: hash1.s
+#readelf: -d -I
+#ld: -shared --hash-style=gnu
+#target: [check_shared_lib_support]
+
+#...
+ +0x[0-9a-z]+ +\(GNU_XHASH\) +0x[0-9a-z]+
+#...
+ +1 +1 +\( 50.0%\) +100.0%
+#...
diff --git a/ld/testsuite/ld-mips-elf/hash1.s b/ld/testsuite/ld-mips-elf/hash1.s
index 4e7fe2f..587cef1 100644
--- a/ld/testsuite/ld-mips-elf/hash1.s
+++ b/ld/testsuite/ld-mips-elf/hash1.s
@@ -1 +1,7 @@
-	nop
+.globl foo
+
+.text
+
+foo:
+    jr $ra
+    nop
diff --git a/ld/testsuite/ld-mips-elf/hash1a.d b/ld/testsuite/ld-mips-elf/hash1a.d
deleted file mode 100644
index c189c93..0000000
--- a/ld/testsuite/ld-mips-elf/hash1a.d
+++ /dev/null
@@ -1,5 +0,0 @@
-#source: hash1.s
-#ld: -shared --hash-style=sysv
-#objdump: -dr
-#target: [check_shared_lib_support]
-#pass
diff --git a/ld/testsuite/ld-mips-elf/hash1b.d b/ld/testsuite/ld-mips-elf/hash1b.d
deleted file mode 100644
index 5cafede..0000000
--- a/ld/testsuite/ld-mips-elf/hash1b.d
+++ /dev/null
@@ -1,4 +0,0 @@
-#source: hash1.s
-#ld: -shared --hash-style=both
-#target: [check_shared_lib_support]
-#error: .gnu.hash is incompatible with the MIPS ABI
diff --git a/ld/testsuite/ld-mips-elf/hash1c.d b/ld/testsuite/ld-mips-elf/hash1c.d
deleted file mode 100644
index 379620a..0000000
--- a/ld/testsuite/ld-mips-elf/hash1c.d
+++ /dev/null
@@ -1,4 +0,0 @@
-#source: hash1.s
-#ld: -shared --hash-style=gnu
-#target: [check_shared_lib_support]
-#error: .gnu.hash is incompatible with the MIPS ABI
diff --git a/ld/testsuite/ld-mips-elf/mips-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
index 1fee8ef..b3f8432 100644
--- a/ld/testsuite/ld-mips-elf/mips-elf.exp
+++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
@@ -854,9 +854,7 @@ if { $linux_gnu } {
 
 run_dump_test_n32 "emit-relocs-1" {{as -EB} {ld -EB}}
 
-run_dump_test "hash1a"
-run_dump_test "hash1b"
-run_dump_test "hash1c"
+run_dump_test "hash1"
 
 if { $linux_gnu && $has_abi(o32) } {
     # The number of symbols that are always included in the symbol table
-- 
2.7.4



More information about the Binutils mailing list