Add support for a MIPS specific .MIPS.xhash section.
authorMihailo Stojanovic <mihailo.stojanovic@rt-rk.com>
Fri, 9 Aug 2019 10:06:37 +0000 (11:06 +0100)
committerNick Clifton <nickc@redhat.com>
Fri, 9 Aug 2019 10:06:37 +0000 (11:06 +0100)
  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.

  In this patch, the new section name and dynamic tag are changed to
reflect the fact that the section should be treated as MIPS-specific
(.MIPS.xhash and DT_MIPS_XHASH).

  This patch addresses the alignment issue as reported in [3], which is
caused by the leading word added to the .MIPS.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 .MIPS.xhash,
marking the need of support for .MIPS.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 is submitted at:
https://sourceware.org/ml/libc-alpha/2019-06/msg00456.html

[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      * emulparams/elf32bmip.sh: Add .MIPS.xhash section.
        * emulparams/elf32bmipn32-defs.sh: Add .MIPS.xhash section.
        * emulparams/elf64bmip-defs.sh: Add .MIPS.xhash section.
        * emultempl/mipself.em: Remove mips_after_parse function.
        * testsuite/ld-elf/hash.d: Update comment.
        * 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/hash2.d: New test.
        * testsuite/ld-mips-elf/mips-elf.exp: New tests.
        * testsuite/ld-mips-elf/start.s: New test.

bfd     * elf-bfd.h (struct elf_backend_data): New members.
        * elflink.c (_bfd_elf_link_create_dynamic_sections): Create
        .gnu.hash section if necessary.
        (struct collect_gnu_hash_codes): New member.
        (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 .MIPS.xhash entry.
        (bfd_elf_size_dynamic_sections): Add DT_GNU_HASH dynamic tag to
        dynamic section if necessary.
        (GNU_HASH_SECTION_NAME): New define.
        (bfd_elf_size_dynsym_hash_dynstr): Get .MIPS.xhash section.
        Update the section size info.
        * 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 .MIPS.xhash translation
        table location.
        (mips_elf_sort_hash_table): Initialize the pointer to the
        .MIPS.xhash section.
        (mips_elf_sort_hash_table_f): Populate the .MIPS.xhash
        translation table entry with the symbol dynindx.
        (_bfd_mips_elf_section_from_shdr): Add SHT_MIPS_XHASH.
        (_bfd_mips_elf_fake_sections): Initialize .MIPS.xhash section
        info.
        (_bfd_mips_elf_create_dynamic_sections): Create .MIPS.xhash
        section.
        (_bfd_mips_elf_size_dynamic_sections): Add DT_MIPS_XHASH tag to
        dynamic section.
        (_bfd_mips_elf_finish_synamic_sections): Add DT_MIPS_XHASH.
        (_bfd_mips_elf_final_write_processing): Set .MIPS.xhash section
        sh_link info.
        (_bfd_mips_elf_get_target_dtag): Get DT_MIPS_XHASH tag.
        (MIPS_LIBC_ABI_XHASH): New ABI version enum value.
        (_bfd_mips_post_process_headers): Mark the ABI version as
        MIPS_LIBC_ABI_XHASH if there exists a .MIPS.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 * elf/mips.h (SHT_GNU_XHASH): New define.
        (DT_GNU_XHASH): New define.

binutils * readelf.c (get_mips_dynamic_type): Return MIPS_XHASH dynamic type.
        (get_mips_section_type_name): Return MI{S_XHASH name string.
        (dynamic_section_mips_val): Initialize the .MIPS.xhash dynamic
        info.
        (process_symbol_table): Initialize the .MIPS.xhash section
        pointer. Adjust the readelf output to support the new section.
        (process_object): Set the .MIPS.xhash dynamic info to zero.

24 files changed:
bfd/ChangeLog
bfd/elf-bfd.h
bfd/elflink.c
bfd/elfxx-mips.c
bfd/elfxx-mips.h
bfd/elfxx-target.h
binutils/ChangeLog
binutils/readelf.c
include/ChangeLog
include/elf/mips.h
ld/ChangeLog
ld/emulparams/elf32bmip.sh
ld/emulparams/elf32bmipn32-defs.sh
ld/emulparams/elf64bmip-defs.sh
ld/emultempl/mipself.em
ld/testsuite/ld-elf/hash.d
ld/testsuite/ld-mips-elf/hash1.d [new file with mode: 0644]
ld/testsuite/ld-mips-elf/hash1.s
ld/testsuite/ld-mips-elf/hash1a.d [deleted file]
ld/testsuite/ld-mips-elf/hash1b.d [deleted file]
ld/testsuite/ld-mips-elf/hash1c.d [deleted file]
ld/testsuite/ld-mips-elf/hash2.d [new file with mode: 0644]
ld/testsuite/ld-mips-elf/mips-elf.exp
ld/testsuite/ld-mips-elf/start.s [new file with mode: 0644]

index 7b0da656230775b352d19dda47a94c162341cc00..cf2fec78810e97567ce4ec6c6d09deb9b82d897f 100644 (file)
@@ -1,3 +1,49 @@
+2019-08-09  Mihailo Stojanovic  <mihailo.stojanovic@rt-rk.com>
+
+       * elf-bfd.h (struct elf_backend_data): New members.
+       * elflink.c (_bfd_elf_link_create_dynamic_sections): Create
+       .gnu.hash section if necessary.
+       (struct collect_gnu_hash_codes): New member.
+       (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 .MIPS.xhash entry.
+       (bfd_elf_size_dynamic_sections): Add DT_GNU_HASH dynamic tag to
+       dynamic section if necessary.
+       (GNU_HASH_SECTION_NAME): New define.
+       (bfd_elf_size_dynsym_hash_dynstr): Get .MIPS.xhash section.
+       Update the section size info.
+       * 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 .MIPS.xhash translation
+       table location.
+       (mips_elf_sort_hash_table): Initialize the pointer to the
+       .MIPS.xhash section.
+       (mips_elf_sort_hash_table_f): Populate the .MIPS.xhash
+       translation table entry with the symbol dynindx.
+       (_bfd_mips_elf_section_from_shdr): Add SHT_MIPS_XHASH.
+       (_bfd_mips_elf_fake_sections): Initialize .MIPS.xhash section
+       info.
+       (_bfd_mips_elf_create_dynamic_sections): Create .MIPS.xhash
+       section.
+       (_bfd_mips_elf_size_dynamic_sections): Add DT_MIPS_XHASH tag to
+       dynamic section.
+       (_bfd_mips_elf_finish_synamic_sections): Add DT_MIPS_XHASH.
+       (_bfd_mips_elf_final_write_processing): Set .MIPS.xhash section
+       sh_link info.
+       (_bfd_mips_elf_get_target_dtag): Get DT_MIPS_XHASH tag.
+       (MIPS_LIBC_ABI_XHASH): New ABI version enum value.
+       (_bfd_mips_post_process_headers): Mark the ABI version as
+       MIPS_LIBC_ABI_XHASH if there exists a .MIPS.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.
+
 2019-08-07  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
        * elf64-bpf.c (bpf_elf_relocate_section): New function.
index 225a8e5c19f4e2f5e250b4e629e091596ca304d5..521d35debb05009b9a818b01626b743acd0c022b 100644 (file)
@@ -1392,6 +1392,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
+     .MIPS.xhash at which real final dynindx for H will be written.
+     If XLAT_LOC is zero, the symbol is not included in
+     .MIPS.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);
 
index 597184582f345c554e123e19ae7e2778f3da7c5a..c7440d19ff0f82bf14e01a398d010bc1ff22e840 100644 (file)
@@ -339,7 +339,7 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry;
     }
 
-  if (info->emit_gnu_hash)
+  if (info->emit_gnu_hash && bed->record_xhash_symbol == NULL)
     {
       s = bfd_make_section_anyway_with_flags (abfd, ".gnu.hash",
                                              flags | SEC_READONLY);
@@ -5853,6 +5853,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;
@@ -5917,10 +5918,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 .MIPS.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;
@@ -5934,7 +5937,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;
     }
 
@@ -5951,7 +5962,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;
 }
 
@@ -6980,7 +6998,8 @@ 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_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)
@@ -7105,6 +7124,9 @@ _bfd_elf_init_2_index_sections (bfd *output_bfd, struct bfd_link_info *info)
   elf_hash_table (info)->text_index_section = found;
 }
 
+#define GNU_HASH_SECTION_NAME(bed)                         \
+  (bed)->record_xhash_symbol != NULL ? ".MIPS.xhash" : ".gnu.hash"
+
 bfd_boolean
 bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 {
@@ -7271,12 +7293,12 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
              return FALSE;
            }
 
-         s = bfd_get_linker_section (dynobj, ".gnu.hash");
+         s = bfd_get_linker_section (dynobj, GNU_HASH_SECTION_NAME (bed));
          BFD_ASSERT (s != NULL);
 
          if (cinfo.nsyms == 0)
            {
-             /* Empty .gnu.hash section is special.  */
+             /* Empty .gnu.hash or .MIPS.xhash section is special.  */
              BFD_ASSERT (cinfo.min_dynindx == -1);
              free (cinfo.hashcodes);
              s->size = 5 * 4 + bed->s->arch_size / 8;
@@ -7356,6 +7378,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)
                {
@@ -7382,9 +7406,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 .MIPS.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)
index 8e577b29e1bb39f976d63b3c11deb0f5b3c53bde..e845c90ee410f7e7bcbd7e2b148d288cd7605a7f 100644 (file)
@@ -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 .MIPS.xhash finalization.  */
+  bfd *output_bfd;
+  /* If non-NULL, pointer to contents of .MIPS.xhash for filling in
+     real final dynindx.  */
+  bfd_byte *mipsxhash;
 };
 
 /* 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 .MIPS.xhash to write real final dynindx.  */
+  bfd_vma mipsxhash_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->mipsxhash_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, ".MIPS.xhash");
+      BFD_ASSERT (s != NULL);
+      hsd.mipsxhash = s->contents;
+      BFD_ASSERT (hsd.mipsxhash != NULL);
+    }
+  else
+    hsd.mipsxhash = 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 .MIPS.xhash translation table entry with
+     the symbol dynindx.  */
+  if (h->mipsxhash_loc != 0 && hsd->mipsxhash != NULL)
+    bfd_put_32 (hsd->output_bfd, h->root.dynindx,
+               hsd->mipsxhash + h->mipsxhash_loc);
+
   return TRUE;
 }
 
@@ -7475,6 +7502,9 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
          && ! CONST_STRNEQ (name, ".MIPS.post_rel"))
        return FALSE;
       break;
+    case SHT_MIPS_XHASH:
+      if (strcmp (name, ".MIPS.xhash") != 0)
+       return FALSE;
     default:
       break;
     }
@@ -7708,6 +7738,12 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
       hdr->sh_flags |= SHF_ALLOC;
       hdr->sh_entsize = 8;
     }
+  else if (strcmp (name, ".MIPS.xhash") == 0)
+    {
+      hdr->sh_type = SHT_MIPS_XHASH;
+      hdr->sh_flags |= SHF_ALLOC;
+      hdr->sh_entsize = get_elf_backend_data(abfd)->s->arch_size == 64 ? 0 : 4;
+    }
 
   /* The generic elf_fake_sections will set up REL_HDR using the default
    kind of relocations.  We used to set up a second header for the
@@ -7994,6 +8030,11 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
        return FALSE;
     }
 
+  /* Create .MIPS.xhash section.  */
+  if (info->emit_gnu_hash)
+    s = bfd_make_section_anyway_with_flags (abfd, ".MIPS.xhash",
+                                           flags | SEC_READONLY);
+
   /* On IRIX5, we adjust add some additional symbols and change the
      alignments of several sections.  There is no ABI documentation
      indicating that this is necessary on IRIX6, nor any evidence that
@@ -10176,6 +10217,10 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
          if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
            return FALSE;
 
+         if (info->emit_gnu_hash
+             && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_XHASH, 0))
+           return FALSE;
+
          if (IRIX_COMPAT (dynobj) == ict_irix5
              && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
            return FALSE;
@@ -11953,6 +11998,12 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
                swap_out_p = FALSE;
              break;
 
+           case DT_MIPS_XHASH:
+             name = ".MIPS.xhash";
+             s = bfd_get_linker_section (dynobj, name);
+             dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+             break;
+
            default:
              swap_out_p = FALSE;
              if (htab->is_vxworks
@@ -12440,6 +12491,10 @@ _bfd_mips_final_write_processing (bfd *abfd)
          (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
          break;
 
+       case SHT_MIPS_XHASH:
+         sec = bfd_get_section_by_name (abfd, ".dynsym");
+         if (sec != NULL)
+           (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
        }
     }
 }
@@ -15943,6 +15998,8 @@ _bfd_mips_elf_get_target_dtag (bfd_vma dtag)
       return "DT_MIPS_PLTGOT";
     case DT_MIPS_RWPLT:
       return "DT_MIPS_RWPLT";
+    case DT_MIPS_XHASH:
+      return "DT_MIPS_XHASH";
     }
 }
 
@@ -16276,6 +16333,7 @@ const struct bfd_elf_special_section _bfd_mips_elf_special_sections[] =
   { STRING_COMMA_LEN (".sbss"),         -2, SHT_NOBITS,     SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
   { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
   { STRING_COMMA_LEN (".ucode"),  0, SHT_MIPS_UCODE, 0 },
+  { STRING_COMMA_LEN (".MIPS.xhash"),  0, SHT_MIPS_XHASH,   SHF_ALLOC },
   { NULL,                    0,  0, 0,              0 }
 };
 
@@ -16590,6 +16648,7 @@ enum
   MIPS_LIBC_ABI_UNIQUE,
   MIPS_LIBC_ABI_MIPS_O32_FP64,
   MIPS_LIBC_ABI_ABSOLUTE,
+  MIPS_LIBC_ABI_XHASH,
   MIPS_LIBC_ABI_MAX
 };
 
@@ -16617,6 +16676,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 .MIPS.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_XHASH;
+
   _bfd_elf_post_process_headers (abfd, link_info);
 }
 
@@ -16635,3 +16699,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->mipsxhash_loc = xlat_loc;
+}
index 0a901c6208a48275292894c354f60f73508bcd7e..8e796a99d9d485781a13e1218463c01062af4047 100644 (file)
@@ -173,6 +173,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)
 {
@@ -198,4 +201,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
index aecc60f08b0dfe19efb551ac7efa9ae857922575..0b737a4ffa9061e1f58e2e96239cf9901c9fbf2b 100644 (file)
 #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,
index 22406f6816a1a2d0563074eff8bd88df1da3899f..54dbf9a81dc9eaf7622a10c433a5ff57684f9544 100644 (file)
@@ -1,3 +1,13 @@
+2019-08-09  Mihailo Stojanovic  <mihailo.stojanovic@rt-rk.com>
+
+       * readelf.c (get_mips_dynamic_type): Return MIPS_XHASH dynamic type.
+       (get_mips_section_type_name): Return MI{S_XHASH name string.
+       (dynamic_section_mips_val): Initialize the .MIPS.xhash dynamic
+       info.
+       (process_symbol_table): Initialize the .MIPS.xhash section
+       pointer. Adjust the readelf output to support the new section.
+       (process_object): Set the .MIPS.xhash dynamic info to zero.
+
 2019-08-09  Tamar Christina  <tamar.christina@arm.com>
 
        * testsuite/binutils-all/arm/in-order-all.d: Skip on pe, wince, coff.
index 5bfbac9e9cc2ce2ebca82ff8b24a27788185623e..b90d3bc885cec18ba2fa6effde4df278d1f0ecf1 100644 (file)
@@ -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_MIPS_XHASH;
 static bfd_vma version_info[16];
 static Elf_Internal_Dyn *  dynamic_section;
 static elf_section_list * symtab_shndx_list;
@@ -335,6 +336,10 @@ static const char * get_symbol_version_string
        (ADDR) &= ~1;                           \
     }                                          \
   while (0)
+
+/* Get the correct GNU hash section name.  */
+#define GNU_HASH_SECTION_NAME                  \
+  dynamic_info_DT_MIPS_XHASH ? ".MIPS.xhash" : ".gnu.hash"
 \f
 /* Print a BFD_VMA to an internal buffer, for use in error messages.
    BFD_FMA_FMT can't be used in translated strings.  */
@@ -1872,6 +1877,7 @@ get_mips_dynamic_type (unsigned long type)
     case DT_MIPS_AUX_DYNAMIC: return "MIPS_AUX_DYNAMIC";
     case DT_MIPS_PLTGOT: return "MIPS_PLTGOT";
     case DT_MIPS_RWPLT: return "MIPS_RWPLT";
+    case DT_MIPS_XHASH: return "MIPS_XHASH";
     default:
       return NULL;
     }
@@ -4113,6 +4119,7 @@ get_mips_section_type_name (unsigned int sh_type)
     case SHT_MIPS_XLATE_OLD:    return "MIPS_XLATE_OLD";
     case SHT_MIPS_PDR_EXCEPTION: return "MIPS_PDR_EXCEPTION";
     case SHT_MIPS_ABIFLAGS:     return "MIPS_ABIFLAGS";
+    case SHT_MIPS_XHASH:        return "MIPS_XHASH";
     default:
       break;
     }
@@ -9525,6 +9532,11 @@ dynamic_section_mips_val (Elf_Internal_Dyn * entry)
       print_vma (entry->d_un.d_val, DEC);
       break;
 
+    case DT_MIPS_XHASH:
+      dynamic_info_DT_MIPS_XHASH = entry->d_un.d_val;
+      dynamic_info_DT_GNU_HASH = entry->d_un.d_val;
+      /* Falls through.  */
+
     default:
       print_vma (entry->d_un.d_ptr, PREFIX_HEX);
     }
@@ -11673,6 +11685,7 @@ process_symbol_table (Filedata * filedata)
   bfd_vma ngnubuckets = 0;
   bfd_vma * gnubuckets = NULL;
   bfd_vma * gnuchains = NULL;
+  bfd_vma * mipsxlat = NULL;
   bfd_vma gnusymidx = 0;
   bfd_size_type ngnuchains = 0;
 
@@ -11838,7 +11851,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_MIPS_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;
+           }
+
+         mipsxlat = get_dynamic_data (filedata, maxchain, 4);
+       }
+
     no_gnu_hash:
+      if (dynamic_info_DT_MIPS_XHASH && mipsxlat == NULL)
+       {
+         free (gnuchains);
+         gnuchains = NULL;
+       }
       if (gnuchains == NULL)
        {
          free (gnubuckets);
@@ -11888,7 +11925,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"),
+                 GNU_HASH_SECTION_NAME);
          if (is_32bit_elf)
            printf (_("  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name\n"));
          else
@@ -11902,7 +11940,10 @@ process_symbol_table (Filedata * filedata)
 
                do
                  {
-                   print_dynamic_symbol (filedata, si, hn);
+                   if (dynamic_info_DT_MIPS_XHASH)
+                     print_dynamic_symbol (filedata, mipsxlat[off], hn);
+                   else
+                     print_dynamic_symbol (filedata, si, hn);
                    si++;
                  }
                while (off < ngnuchains && (gnuchains[off++] & 1) == 0);
@@ -12125,11 +12166,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),
+             GNU_HASH_SECTION_NAME,
              (unsigned long) ngnubuckets);
 
       lengths = (unsigned long *) calloc (ngnubuckets, sizeof (*lengths));
@@ -12186,6 +12228,7 @@ process_symbol_table (Filedata * filedata)
       free (lengths);
       free (gnubuckets);
       free (gnuchains);
+      free (mipsxlat);
     }
 
   return TRUE;
@@ -19744,6 +19787,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_MIPS_XHASH = 0;
 
   /* Process the file.  */
   if (show_name)
index 9d55a1f2916e52bb34fa3193ec731d3fa3d0fb76..1813cb38d8e290e74a9a1a3ac61491375da11cc2 100644 (file)
@@ -1,3 +1,8 @@
+2019-08-09  Mihailo Stojanovic  <mihailo.stojanovic@rt-rk.com>
+
+       * elf/mips.h (SHT_GNU_XHASH): New define.
+       (DT_GNU_XHASH): New define.
+
 2019-08-08  Yoshinori Sato  <ysato@users.sourceforge.jp>
 
        * opcode/h8300.h (EXPAND_UNOP_EXTENDED_B): Add MODEL.
index b76d450ae2b6fafca9c36b23074a03f8d833a45c..cd6779f22c991c77b5d5aa08931c7e9cb363abc0 100644 (file)
@@ -452,6 +452,9 @@ END_RELOC_NUMBERS (R_MIPS_maxext)
 /* ABI related flags section.  */
 #define SHT_MIPS_ABIFLAGS      0x7000002a
 
+/* GNU style symbol hash table with xlat.  */
+#define SHT_MIPS_XHASH         0x7000002b
+
 /* A section of type SHT_MIPS_LIBLIST contains an array of the
    following structure.  The sh_link field is the section index of the
    string table.  The sh_info field is the number of entries in the
@@ -759,6 +762,9 @@ extern void bfd_mips_elf32_swap_reginfo_out
 
 /* Relative offset of run time loader map, used for debugging.  */
 #define DT_MIPS_RLD_MAP_REL    0x70000035
+
+/* Address of .MIPS.xhash section.  */
+#define DT_MIPS_XHASH         0x70000036
 \f
 /* Flags which may appear in a DT_MIPS_FLAGS entry.  */
 
index 3010abb26e5aeb6e3f18d828e20332e0b0e4ce05..6ff1a4727a41727ceb9877f4d81434144f48161f 100644 (file)
@@ -1,3 +1,19 @@
+2019-08-09  Mihailo Stojanovic  <mihailo.stojanovic@rt-rk.com>
+
+       * emulparams/elf32bmip.sh: Add .MIPS.xhash section.
+       * emulparams/elf32bmipn32-defs.sh: Add .MIPS.xhash section.
+       * emulparams/elf64bmip-defs.sh: Add .MIPS.xhash section.
+       * emultempl/mipself.em: Remove mips_after_parse function.
+       * testsuite/ld-elf/hash.d: Update comment.
+       * 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/hash2.d: New test.
+       * testsuite/ld-mips-elf/mips-elf.exp: New tests.
+       * testsuite/ld-mips-elf/start.s: New test.
+
 2019-08-08  Nick Clifton  <nickc@redhat.com>
 
        PR 24887
index abbcb5be35cb1613dd93b8ed7e02b29d73ed1631..1e0200a73b6ff062b2412d713c254800e1d24b4d 100644 (file)
@@ -19,6 +19,7 @@ fi
 INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS}
   .MIPS.abiflags ${RELOCATING-0} : { *(.MIPS.abiflags) }
   .reginfo       ${RELOCATING-0} : { *(.reginfo) }
+  .MIPS.xhash    ${RELOCATING-0} : { *(.MIPS.xhash) }
 "
 OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)'
 # Unlike most targets, the MIPS backend puts all dynamic relocations
index bd017909e0eff72b044fc0687472ce4d34dcb2a9..80dce8fbeae9b2ae12217424a6843c26f846cfac 100644 (file)
@@ -87,6 +87,7 @@ if test -z "${CREATE_SHLIB}"; then
 fi
 INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS}
   .MIPS.abiflags      ${RELOCATING-0} : { *(.MIPS.abiflags) }
+  .MIPS.xhash      ${RELOCATING-0} : { *(.MIPS.xhash) }
   .reginfo      ${RELOCATING-0} : { *(.reginfo) }"
 # Discard any .MIPS.content* or .MIPS.events* sections.  The linker
 # doesn't know how to adjust them.
index 61d6c00472eeb58d04ffd08cff8ac78d87d11362..4165f51e9e5db40af8e9342ebe82df2bdefc985d 100644 (file)
@@ -2,5 +2,6 @@ source_sh ${srcdir}/emulparams/elf32bmipn32-defs.sh
 COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
 INITIAL_READONLY_SECTIONS="
   .MIPS.abiflags      ${RELOCATING-0} : { *(.MIPS.abiflags) }
+  .MIPS.xhash      ${RELOCATING-0} : { *(.MIPS.xhash) }
   .MIPS.options : { *(.MIPS.options) }
 "
index ec908d738dce2518ca45f798ecdb6f58099b80fe..fe46e0d8a898fb1bc7c9fb8b07e90ebcd4f8697f 100644 (file)
@@ -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
index fb079124e5bc04b65b97d89abda42779129941bc..efe675e0c7fffef8076077290b7a2750904e22e6 100644 (file)
@@ -3,7 +3,7 @@
 #ld: -shared --hash-style=gnu
 #target: *-*-linux* *-*-gnu* arm*-*-uclinuxfdpiceabi
 #xfail: mips*-*-*
-# GNU hash is not supported for MIPS targets due to psABI restrictions
+# MIPS uses a different style of GNU hash due to psABI restrictions
 # on dynsym table ordering.
 
 #...
diff --git a/ld/testsuite/ld-mips-elf/hash1.d b/ld/testsuite/ld-mips-elf/hash1.d
new file mode 100644 (file)
index 0000000..3b9725d
--- /dev/null
@@ -0,0 +1,11 @@
+#source: hash1.s
+#readelf: -d -I
+#ld: -nostdlib -shared --hash-style=gnu
+#target: [check_shared_lib_support]
+#xfail: mips*-*-irix*
+
+#...
+ +0x[0-9a-z]+ +\(MIPS_XHASH\) +0x[0-9a-z]+
+#...
+ +1 +1 +\( 50.0%\) +100.0%
+#...
index 4e7fe2f0a49411d0f9fb85ec72b230436e74b051..587cef1d761a49599be2195986ab24e4920660a9 100644 (file)
@@ -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 (file)
index c189c93..0000000
+++ /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 (file)
index 5cafede..0000000
+++ /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 (file)
index 379620a..0000000
+++ /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/hash2.d b/ld/testsuite/ld-mips-elf/hash2.d
new file mode 100644 (file)
index 0000000..79fda3c
--- /dev/null
@@ -0,0 +1,17 @@
+#source: start.s
+#readelf: -d -s -D
+#ld: -shared --hash-style=gnu
+#target: [check_shared_lib_support] 
+#xfail: mips*-*-irix*
+
+#...
+ +0x[0-9a-z]+ +\(MIPS_XHASH\) +0x[0-9a-z]+
+#...
+ +[0-9]+ +[0-9]+: +[0-9a-f]+ +[0-9]+ +NOTYPE +GLOBAL +DEFAULT +[1-9] _start
+#...
+ +[0-9]+ +[0-9]+: +[0-9a-f]+ +[0-9]+ +NOTYPE +GLOBAL +DEFAULT +[1-9] main
+#...
+ +[0-9]+ +[0-9]+: +[0-9a-f]+ +[0-9]+ +NOTYPE +GLOBAL +DEFAULT +[1-9] start
+#...
+ +[0-9]+ +[0-9]+: +[0-9a-f]+ +[0-9]+ +NOTYPE +GLOBAL +DEFAULT +[1-9] __start
+#...
index 1fee8efb8476e45a69fcef50e8f12fb872114a98..3c7ce6af14bcd8c4e2ed9c7aa4549308a0a20dc7 100644 (file)
@@ -854,9 +854,8 @@ 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"
+run_dump_test "hash2"
 
 if { $linux_gnu && $has_abi(o32) } {
     # The number of symbols that are always included in the symbol table
diff --git a/ld/testsuite/ld-mips-elf/start.s b/ld/testsuite/ld-mips-elf/start.s
new file mode 100644 (file)
index 0000000..ee26ab3
--- /dev/null
@@ -0,0 +1,12 @@
+       .text
+       .globl start
+start:
+       .globl _start
+_start:
+       .globl __start
+__start:
+       .globl main
+main:
+       .globl  _main
+_main:
+       .dc.a 0