S/390: Add support for pgste marker
[deliverable/binutils-gdb.git] / bfd / elf64-s390.c
index 7cf4706d9c6da765c4cb1d324e9c495e3a827aef..1af1200f6cdf4cc6653f1de2e1586b1092338b4d 100644 (file)
@@ -1,6 +1,5 @@
 /* IBM S/390-specific support for 64-bit ELF
-   Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2000-2017 Free Software Foundation, Inc.
    Contributed Martin Schwidefsky (schwidefsky@de.ibm.com).
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -26,6 +25,8 @@
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/s390.h"
+#include "elf-s390.h"
+#include <stdarg.h>
 
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value
    from smaller values.  Start with zero, widen, *then* decrement.  */
@@ -43,7 +44,7 @@ static reloc_howto_type elf_howto_table[] =
 {
   HOWTO (R_390_NONE,           /* type */
         0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
+        3,                     /* size (0 = byte, 1 = 2 byte, 2 = 4 byte) */
         0,                     /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
@@ -169,6 +170,16 @@ static reloc_howto_type elf_howto_table[] =
        s390_elf_ldisp_reloc, "R_390_GOTPLT20", FALSE, 0,0x0fffff00, FALSE),
   HOWTO(R_390_TLS_GOTIE20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
        s390_elf_ldisp_reloc, "R_390_TLS_GOTIE20", FALSE, 0,0x0fffff00, FALSE),
+  HOWTO(R_390_IRELATIVE, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_390_IRELATIVE", FALSE, 0, MINUS_ONE, FALSE),
+  HOWTO(R_390_PC12DBL,   1, 1, 12,  TRUE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_390_PC12DBL",  FALSE, 0,0x00000fff, TRUE),
+  HOWTO(R_390_PLT12DBL,  1, 1, 12,  TRUE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_390_PLT12DBL", FALSE, 0,0x00000fff, TRUE),
+  HOWTO(R_390_PC24DBL,   1, 2, 24,  TRUE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_390_PC24DBL",  FALSE, 0,0x00ffffff, TRUE),
+  HOWTO(R_390_PLT24DBL,  1, 2, 24,  TRUE, 0, complain_overflow_bitfield,
+       bfd_elf_generic_reloc, "R_390_PLT24DBL", FALSE, 0,0x00ffffff, TRUE),
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
@@ -219,10 +230,18 @@ elf_s390_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       return &elf_howto_table[(int) R_390_GOT16];
     case BFD_RELOC_16_PCREL:
       return &elf_howto_table[(int) R_390_PC16];
+    case BFD_RELOC_390_PC12DBL:
+      return &elf_howto_table[(int) R_390_PC12DBL];
+    case BFD_RELOC_390_PLT12DBL:
+      return &elf_howto_table[(int) R_390_PLT12DBL];
     case BFD_RELOC_390_PC16DBL:
       return &elf_howto_table[(int) R_390_PC16DBL];
     case BFD_RELOC_390_PLT16DBL:
       return &elf_howto_table[(int) R_390_PLT16DBL];
+    case BFD_RELOC_390_PC24DBL:
+      return &elf_howto_table[(int) R_390_PC24DBL];
+    case BFD_RELOC_390_PLT24DBL:
+      return &elf_howto_table[(int) R_390_PLT24DBL];
     case BFD_RELOC_390_PC32DBL:
       return &elf_howto_table[(int) R_390_PC32DBL];
     case BFD_RELOC_390_PLT32DBL:
@@ -295,6 +314,8 @@ elf_s390_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       return &elf_howto_table[(int) R_390_GOTPLT20];
     case BFD_RELOC_390_TLS_GOTIE20:
       return &elf_howto_table[(int) R_390_TLS_GOTIE20];
+    case BFD_RELOC_390_IRELATIVE:
+      return &elf_howto_table[(int) R_390_IRELATIVE];
     case BFD_RELOC_VTABLE_INHERIT:
       return &elf64_s390_vtinherit_howto;
     case BFD_RELOC_VTABLE_ENTRY:
@@ -318,10 +339,10 @@ elf_s390_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
        && strcasecmp (elf_howto_table[i].name, r_name) == 0)
       return &elf_howto_table[i];
 
-    if (strcasecmp (elf64_s390_vtinherit_howto.name, r_name) == 0)
-      return &elf64_s390_vtinherit_howto;
-    if (strcasecmp (elf64_s390_vtentry_howto.name, r_name) == 0)
-      return &elf64_s390_vtentry_howto;
+  if (strcasecmp (elf64_s390_vtinherit_howto.name, r_name) == 0)
+    return &elf64_s390_vtinherit_howto;
+  if (strcasecmp (elf64_s390_vtentry_howto.name, r_name) == 0)
+    return &elf64_s390_vtentry_howto;
 
   return NULL;
 }
@@ -348,8 +369,9 @@ elf_s390_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
     default:
       if (r_type >= sizeof (elf_howto_table) / sizeof (elf_howto_table[0]))
        {
-         (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
-                                abfd, (int) r_type);
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%B: invalid relocation type %d"),
+                             abfd, (int) r_type);
          r_type = R_390_NONE;
        }
       cache_ptr->howto = &elf_howto_table[r_type];
@@ -410,7 +432,7 @@ s390_elf_ldisp_reloc (bfd *abfd,
       relocation -= reloc_entry->address;
     }
 
-  insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); 
+  insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
   insn |= (relocation & 0xfff) << 16 | (relocation & 0xff000) >> 4;
   bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
 
@@ -450,6 +472,8 @@ elf_s390_is_local_label_name (bfd *abfd, const char *name)
 
 #define GOT_ENTRY_SIZE 8
 
+#define RELA_ENTRY_SIZE sizeof (Elf64_External_Rela)
+
 /* The first three entries in a procedure linkage table are reserved,
    and the initial contents are unimportant (we zero them out).
    Subsequent entries look like this.  See the SVR4 ABI 386
@@ -471,8 +495,8 @@ elf_s390_is_local_label_name (bfd *abfd, const char *name)
 
    The GOT holds the address in the PLT to be executed.
    The loader then gets:
-   24(15) =  Pointer to the structure describing the object.
-   28(15) =  Offset in symbol table
+   48(15) =  Pointer to the structure describing the object.
+   56(15) =  Offset in symbol table
    The loader  must  then find the module where the function is
    and insert the address in the GOT.
 
@@ -552,17 +576,43 @@ struct elf_s390_link_hash_entry
 #define GOT_TLS_IE     3
 #define GOT_TLS_IE_NLT 3
   unsigned char tls_type;
+
+  /* For pointer equality reasons we might need to change the symbol
+     type from STT_GNU_IFUNC to STT_FUNC together with its value and
+     section entry.  So after alloc_dynrelocs only these values should
+     be used.  In order to check whether a symbol is IFUNC use
+     s390_is_ifunc_symbol_p.  */
+  bfd_vma ifunc_resolver_address;
+  asection *ifunc_resolver_section;
 };
 
 #define elf_s390_hash_entry(ent) \
   ((struct elf_s390_link_hash_entry *)(ent))
 
+/* This structure represents an entry in the local PLT list needed for
+   local IFUNC symbols.  */
+struct plt_entry
+{
+  /* The section of the local symbol.
+     Set in relocate_section and used in finish_dynamic_sections.  */
+  asection *sec;
+
+  union
+    {
+      bfd_signed_vma refcount;
+      bfd_vma offset;
+    } plt;
+};
+
 /* NOTE: Keep this structure in sync with
    the one declared in elf32-s390.c.  */
 struct elf_s390_obj_tdata
 {
   struct elf_obj_tdata root;
 
+  /* A local PLT is needed for ifunc symbols.  */
+  struct plt_entry *local_plt;
+
   /* TLS type for each local got entry.  */
   char *local_got_tls_type;
 };
@@ -570,6 +620,9 @@ struct elf_s390_obj_tdata
 #define elf_s390_tdata(abfd) \
   ((struct elf_s390_obj_tdata *) (abfd)->tdata.any)
 
+#define elf_s390_local_plt(abfd) \
+  (elf_s390_tdata (abfd)->local_plt)
+
 #define elf_s390_local_got_tls_type(abfd) \
   (elf_s390_tdata (abfd)->local_got_tls_type)
 
@@ -599,8 +652,7 @@ struct elf_s390_link_hash_table
   struct elf_link_hash_table elf;
 
   /* Short-cuts to get to dynamic linker sections.  */
-  asection *sdynbss;
-  asection *srelbss;
+  asection *irelifunc;
 
   union {
     bfd_signed_vma refcount;
@@ -609,13 +661,19 @@ struct elf_s390_link_hash_table
 
   /* Small local sym cache.  */
   struct sym_cache sym_cache;
+
+  /* Options passed from the linker.  */
+  struct s390_elf_params *params;
 };
 
 /* Get the s390 ELF linker hash table from a link_info structure.  */
 
-#define elf_s390_hash_table(p) \
-  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
-  == S390_ELF_DATA ? ((struct elf_s390_link_hash_table *) ((p)->hash)) : NULL)
+#define elf_s390_hash_table(p)                                         \
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash))      \
+   == S390_ELF_DATA ? ((struct elf_s390_link_hash_table *) ((p)->hash)) : NULL)
+
+#define ELF64 1
+#include "elf-s390-common.c"
 
 /* Create an entry in an s390 ELF linker hash table.  */
 
@@ -644,6 +702,8 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
       eh->dyn_relocs = NULL;
       eh->gotplt_refcount = 0;
       eh->tls_type = GOT_UNKNOWN;
+      eh->ifunc_resolver_address = 0;
+      eh->ifunc_resolver_section = NULL;
     }
 
   return entry;
@@ -657,7 +717,7 @@ elf_s390_link_hash_table_create (bfd *abfd)
   struct elf_s390_link_hash_table *ret;
   bfd_size_type amt = sizeof (struct elf_s390_link_hash_table);
 
-  ret = (struct elf_s390_link_hash_table *) bfd_malloc (amt);
+  ret = (struct elf_s390_link_hash_table *) bfd_zmalloc (amt);
   if (ret == NULL)
     return NULL;
 
@@ -669,76 +729,9 @@ elf_s390_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
-  ret->elf.sgot = NULL;
-  ret->elf.sgotplt = NULL;
-  ret->elf.srelgot = NULL;
-  ret->elf.splt = NULL;
-  ret->elf.srelplt = NULL;
-  ret->sdynbss = NULL;
-  ret->srelbss = NULL;
-  ret->tls_ldm_got.refcount = 0;
-  ret->sym_cache.abfd = NULL;
-
   return &ret->elf.root;
 }
 
-/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up
-   shortcuts to them in our hash table.  */
-
-static bfd_boolean
-create_got_section (bfd *dynobj,
-                   struct bfd_link_info *info)
-{
-  struct elf_s390_link_hash_table *htab;
-
-  if (! _bfd_elf_create_got_section (dynobj, info))
-    return FALSE;
-
-  htab = elf_s390_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  htab->elf.sgot = bfd_get_linker_section (dynobj, ".got");
-  htab->elf.sgotplt = bfd_get_linker_section (dynobj, ".got.plt");
-  htab->elf.srelgot = bfd_get_linker_section (dynobj, ".rela.got");
-  if (!htab->elf.sgot || !htab->elf.sgotplt || !htab->elf.srelgot)
-    abort ();
-  return TRUE;
-}
-
-/* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and
-   .rela.bss sections in DYNOBJ, and set up shortcuts to them in our
-   hash table.  */
-
-static bfd_boolean
-elf_s390_create_dynamic_sections (bfd *dynobj,
-                                 struct bfd_link_info *info)
-{
-  struct elf_s390_link_hash_table *htab;
-
-  htab = elf_s390_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  if (!htab->elf.sgot && !create_got_section (dynobj, info))
-    return FALSE;
-
-  if (!_bfd_elf_create_dynamic_sections (dynobj, info))
-    return FALSE;
-
-  htab->elf.splt = bfd_get_linker_section (dynobj, ".plt");
-  htab->elf.srelplt = bfd_get_linker_section (dynobj, ".rela.plt");
-  htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
-  if (!info->shared)
-    htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
-
-  if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
-      || (!info->shared && !htab->srelbss))
-    abort ();
-
-  return TRUE;
-}
-
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
@@ -796,7 +789,8 @@ elf_s390_copy_indirect_symbol (struct bfd_link_info *info,
       /* If called to transfer flags for a weakdef during processing
         of elf_adjust_dynamic_symbol, don't copy non_got_ref.
         We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
-      dir->ref_dynamic |= ind->ref_dynamic;
+      if (dir->versioned != versioned_hidden)
+       dir->ref_dynamic |= ind->ref_dynamic;
       dir->ref_regular |= ind->ref_regular;
       dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
       dir->needs_plt |= ind->needs_plt;
@@ -810,7 +804,7 @@ elf_s390_tls_transition (struct bfd_link_info *info,
                         int r_type,
                         int is_local)
 {
-  if (info->shared)
+  if (bfd_link_pic (info))
     return r_type;
 
   switch (r_type)
@@ -850,7 +844,7 @@ elf_s390_check_relocs (bfd *abfd,
   bfd_signed_vma *local_got_refcounts;
   int tls_type, old_tls_type;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   BFD_ASSERT (is_s390_elf (abfd));
@@ -871,25 +865,57 @@ elf_s390_check_relocs (bfd *abfd,
       unsigned int r_type;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
+      Elf_Internal_Sym *isym;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
 
       if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
        {
-         (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
-                                abfd,
-                                r_symndx);
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%B: bad symbol index: %d"),
+                             abfd, r_symndx);
          return FALSE;
        }
 
       if (r_symndx < symtab_hdr->sh_info)
-       h = NULL;
+       {
+         /* A local symbol.  */
+         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                                       abfd, r_symndx);
+         if (isym == NULL)
+           return FALSE;
+
+         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+           {
+             struct plt_entry *plt;
+
+             if (htab->elf.dynobj == NULL)
+               htab->elf.dynobj = abfd;
+
+             if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+               return FALSE;
+
+             if (local_got_refcounts == NULL)
+               {
+                 if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
+                   return FALSE;
+                 local_got_refcounts = elf_local_got_refcounts (abfd);
+               }
+             plt = elf_s390_local_plt (abfd);
+             plt[r_symndx].plt.refcount++;
+           }
+         h = NULL;
+       }
       else
        {
          h = sym_hashes[r_symndx - symtab_hdr->sh_info];
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         /* PR15323, ref flags aren't set for references in the same
+            object.  */
+         h->root.non_ir_ref_regular = 1;
        }
 
       /* Create got section and local_got_refcounts array if they
@@ -921,18 +947,11 @@ elf_s390_check_relocs (bfd *abfd,
          if (h == NULL
              && local_got_refcounts == NULL)
            {
-             bfd_size_type size;
-
-             size = symtab_hdr->sh_info;
-             size *= (sizeof (bfd_signed_vma) + sizeof(char));
-             local_got_refcounts = ((bfd_signed_vma *)
-                                    bfd_zalloc (abfd, size));
-             if (local_got_refcounts == NULL)
+             if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
                return FALSE;
-             elf_local_got_refcounts (abfd) = local_got_refcounts;
-             elf_s390_local_got_tls_type (abfd)
-               = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+             local_got_refcounts = elf_local_got_refcounts (abfd);
            }
+
          /* Fall through.  */
        case R_390_GOTOFF16:
        case R_390_GOTOFF32:
@@ -943,22 +962,49 @@ elf_s390_check_relocs (bfd *abfd,
            {
              if (htab->elf.dynobj == NULL)
                htab->elf.dynobj = abfd;
-             if (!create_got_section (htab->elf.dynobj, info))
+             if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
                return FALSE;
            }
        }
 
+      if (h != NULL)
+       {
+         if (htab->elf.dynobj == NULL)
+           htab->elf.dynobj = abfd;
+         if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+           return FALSE;
+
+         /* Make sure an IFUNC symbol defined in a non-shared object
+            always gets a PLT slot.  */
+         if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+           {
+             /* The symbol is called by the dynamic loader in order
+                to resolve the relocation.  So it is in fact also
+                referenced.  */
+             h->ref_regular = 1;
+             h->needs_plt = 1;
+           }
+       }
+
       switch (r_type)
        {
-       case R_390_GOTOFF16:
-       case R_390_GOTOFF32:
-       case R_390_GOTOFF64:
        case R_390_GOTPC:
        case R_390_GOTPCDBL:
-         /* Got is created, nothing to be done.  */
+         /* These relocs do not need a GOT slot.  They just load the
+            GOT pointer itself or address something else relative to
+            the GOT.  Since the GOT pointer has been set up above we
+            are done.  */
          break;
+       case R_390_GOTOFF16:
+       case R_390_GOTOFF32:
+       case R_390_GOTOFF64:
+         if (h == NULL || !s390_is_ifunc_symbol_p (h) || !h->def_regular)
+           break;
+         /* Fall through.  */
 
+       case R_390_PLT12DBL:
        case R_390_PLT16DBL:
+       case R_390_PLT24DBL:
        case R_390_PLT32:
        case R_390_PLT32DBL:
        case R_390_PLT64:
@@ -1014,7 +1060,7 @@ elf_s390_check_relocs (bfd *abfd,
        case R_390_TLS_GOTIE20:
        case R_390_TLS_GOTIE64:
        case R_390_TLS_IEENT:
-         if (info->shared)
+         if (bfd_link_pic (info))
            info->flags |= DF_STATIC_TLS;
          /* Fall through */
 
@@ -1066,7 +1112,8 @@ elf_s390_check_relocs (bfd *abfd,
            {
              if (old_tls_type == GOT_NORMAL || tls_type == GOT_NORMAL)
                {
-                 (*_bfd_error_handler)
+                 _bfd_error_handler
+                   /* xgettext:c-format */
                    (_("%B: `%s' accessed both as normal and thread local symbol"),
                     abfd, h->root.root.string);
                  return FALSE;
@@ -1088,7 +1135,13 @@ elf_s390_check_relocs (bfd *abfd,
          /* Fall through */
 
        case R_390_TLS_LE64:
-         if (!info->shared)
+         /* For static linking and executables this reloc will be
+            calculated at linktime otherwise a TLS_TPOFF runtime
+            reloc will be generated.  */
+         if (r_type == R_390_TLS_LE64 && bfd_link_pie (info))
+           break;
+
+         if (!bfd_link_pic (info))
            break;
          info->flags |= DF_STATIC_TLS;
          /* Fall through */
@@ -1097,12 +1150,14 @@ elf_s390_check_relocs (bfd *abfd,
        case R_390_16:
        case R_390_32:
        case R_390_64:
+       case R_390_PC12DBL:
        case R_390_PC16:
        case R_390_PC16DBL:
+       case R_390_PC24DBL:
        case R_390_PC32:
        case R_390_PC32DBL:
        case R_390_PC64:
-         if (h != NULL && !info->shared)
+         if (h != NULL && bfd_link_executable (info))
            {
              /* If this reloc is in a read-only section, we might
                 need a copy reloc.  We can't check reliably at this
@@ -1112,9 +1167,12 @@ elf_s390_check_relocs (bfd *abfd,
                 adjust_dynamic_symbol.  */
              h->non_got_ref = 1;
 
-             /* We may need a .plt entry if the function this reloc
-                refers to is in a shared lib.  */
-             h->plt.refcount += 1;
+             if (!bfd_link_pic (info))
+               {
+                 /* We may need a .plt entry if the function this reloc
+                    refers to is in a shared lib.  */
+                 h->plt.refcount += 1;
+               }
            }
 
          /* If we are creating a shared library, and this is a reloc
@@ -1138,10 +1196,12 @@ elf_s390_check_relocs (bfd *abfd,
             may need to keep relocations for symbols satisfied by a
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
-         if ((info->shared
+         if ((bfd_link_pic (info)
               && (sec->flags & SEC_ALLOC) != 0
               && ((ELF64_R_TYPE (rel->r_info) != R_390_PC16
+                   && ELF64_R_TYPE (rel->r_info) != R_390_PC12DBL
                    && ELF64_R_TYPE (rel->r_info) != R_390_PC16DBL
+                   && ELF64_R_TYPE (rel->r_info) != R_390_PC24DBL
                    && ELF64_R_TYPE (rel->r_info) != R_390_PC32
                    && ELF64_R_TYPE (rel->r_info) != R_390_PC32DBL
                    && ELF64_R_TYPE (rel->r_info) != R_390_PC64)
@@ -1150,7 +1210,7 @@ elf_s390_check_relocs (bfd *abfd,
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
              || (ELIMINATE_COPY_RELOCS
-                 && !info->shared
+                 && !bfd_link_pic (info)
                  && (sec->flags & SEC_ALLOC) != 0
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
@@ -1187,7 +1247,6 @@ elf_s390_check_relocs (bfd *abfd,
                     easily.  Oh well.  */
                  asection *s;
                  void *vpp;
-                 Elf_Internal_Sym *isym;
 
                  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
                                                abfd, r_symndx);
@@ -1219,6 +1278,8 @@ elf_s390_check_relocs (bfd *abfd,
 
              p->count += 1;
              if (ELF64_R_TYPE (rel->r_info) == R_390_PC16
+                 || ELF64_R_TYPE (rel->r_info) == R_390_PC12DBL
+                 || ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
                  || ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
                  || ELF64_R_TYPE (rel->r_info) == R_390_PC32
                  || ELF64_R_TYPE (rel->r_info) == R_390_PC32DBL
@@ -1286,7 +1347,7 @@ elf_s390_gc_sweep_hook (bfd *abfd,
   bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel, *relend;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   htab = elf_s390_hash_table (info);
@@ -1327,6 +1388,23 @@ elf_s390_gc_sweep_hook (bfd *abfd,
                break;
              }
        }
+      else
+       {
+         Elf_Internal_Sym *isym;
+
+         /* A local symbol.  */
+         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                                       abfd, r_symndx);
+         if (isym == NULL)
+           return FALSE;
+
+         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+           {
+             struct plt_entry *plt = elf_s390_local_plt (abfd);
+             if (plt[r_symndx].plt.refcount > 0)
+               plt[r_symndx].plt.refcount--;
+           }
+       }
 
       r_type = ELF64_R_TYPE (rel->r_info);
       r_type = elf_s390_tls_transition (info, r_type, h != NULL);
@@ -1336,6 +1414,18 @@ elf_s390_gc_sweep_hook (bfd *abfd,
          if (htab->tls_ldm_got.refcount > 0)
            htab->tls_ldm_got.refcount -= 1;
          break;
+       case R_390_GOTOFF16:
+       case R_390_GOTOFF32:
+       case R_390_GOTOFF64:
+         if (h != NULL && s390_is_ifunc_symbol_p (h) && h->def_regular)
+           {
+             h->plt.refcount--;
+             break;
+           }
+
+       case R_390_GOTPC:
+       case R_390_GOTPCDBL:
+         break;
 
        case R_390_TLS_GD64:
        case R_390_TLS_IE64:
@@ -1348,11 +1438,6 @@ elf_s390_gc_sweep_hook (bfd *abfd,
        case R_390_GOT20:
        case R_390_GOT32:
        case R_390_GOT64:
-       case R_390_GOTOFF16:
-       case R_390_GOTOFF32:
-       case R_390_GOTOFF64:
-       case R_390_GOTPC:
-       case R_390_GOTPCDBL:
        case R_390_GOTENT:
          if (h != NULL)
            {
@@ -1373,15 +1458,19 @@ elf_s390_gc_sweep_hook (bfd *abfd,
        case R_390_32:
        case R_390_64:
        case R_390_PC16:
+       case R_390_PC12DBL:
        case R_390_PC16DBL:
+       case R_390_PC24DBL:
        case R_390_PC32:
        case R_390_PC32DBL:
        case R_390_PC64:
-         if (info->shared)
+         if (bfd_link_pic (info))
            break;
          /* Fall through */
 
+       case R_390_PLT12DBL:
        case R_390_PLT16DBL:
+       case R_390_PLT24DBL:
        case R_390_PLT32:
        case R_390_PLT32DBL:
        case R_390_PLT64:
@@ -1457,7 +1546,51 @@ elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info,
                                struct elf_link_hash_entry *h)
 {
   struct elf_s390_link_hash_table *htab;
-  asection *s;
+  asection *s, *srel;
+
+  /* STT_GNU_IFUNC symbol must go through PLT. */
+  if (s390_is_ifunc_symbol_p (h))
+    {
+      /* All local STT_GNU_IFUNC references must be treated as local
+        calls via local PLT.  */
+      if (h->ref_regular && SYMBOL_CALLS_LOCAL (info, h))
+       {
+         bfd_size_type pc_count = 0, count = 0;
+         struct elf_dyn_relocs **pp;
+         struct elf_s390_link_hash_entry *eh;
+         struct elf_dyn_relocs *p;
+
+         eh = (struct elf_s390_link_hash_entry *) h;
+         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+           {
+             pc_count += p->pc_count;
+             p->count -= p->pc_count;
+             p->pc_count = 0;
+             count += p->count;
+             if (p->count == 0)
+               *pp = p->next;
+             else
+               pp = &p->next;
+           }
+
+         if (pc_count || count)
+           {
+             h->needs_plt = 1;
+             h->non_got_ref = 1;
+             if (h->plt.refcount <= 0)
+               h->plt.refcount = 1;
+             else
+               h->plt.refcount += 1;
+           }
+       }
+
+      if (h->plt.refcount <= 0)
+       {
+         h->plt.offset = (bfd_vma) -1;
+         h->needs_plt = 0;
+       }
+      return TRUE;
+    }
 
   /* If this is a function, put it in the procedure linkage table.  We
      will fill in the contents of the procedure linkage table later
@@ -1511,7 +1644,7 @@ elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info,
      only references to the symbol are via the global offset table.
      For such cases we need not do anything here; the relocations will
      be handled correctly by relocate_section.  */
-  if (info->shared)
+  if (bfd_link_pic (info))
     return TRUE;
 
   /* If there are no references to this symbol that do not use the
@@ -1565,15 +1698,23 @@ elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* We must generate a R_390_COPY reloc to tell the dynamic linker to
      copy the initial value out of the dynamic object and into the
      runtime process image.  */
+  if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+    {
+      s = htab->elf.sdynrelro;
+      srel = htab->elf.sreldynrelro;
+    }
+  else
+    {
+      s = htab->elf.sdynbss;
+      srel = htab->elf.srelbss;
+    }
   if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
-      htab->srelbss->size += sizeof (Elf64_External_Rela);
+      srel->size += sizeof (Elf64_External_Rela);
       h->needs_copy = 1;
     }
 
-  s = htab->sdynbss;
-
-  return _bfd_elf_adjust_dynamic_copy (h, s);
+  return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
@@ -1585,7 +1726,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
 {
   struct bfd_link_info *info;
   struct elf_s390_link_hash_table *htab;
-  struct elf_s390_link_hash_entry *eh;
+  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry *)h;
   struct elf_dyn_relocs *p;
 
   if (h->root.type == bfd_link_hash_indirect)
@@ -1596,8 +1737,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
   if (htab == NULL)
     return FALSE;
 
-  if (htab->elf.dynamic_sections_created
-      && h->plt.refcount > 0)
+  /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
+     here if it is defined and referenced in a non-shared object.  */
+  if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+    return s390_elf_allocate_ifunc_dyn_relocs (info, h);
+  else if (htab->elf.dynamic_sections_created
+          && h->plt.refcount > 0)
     {
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
@@ -1608,7 +1753,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
            return FALSE;
        }
 
-      if (info->shared
+      if (bfd_link_pic (info)
          || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
        {
          asection *s = htab->elf.splt;
@@ -1625,7 +1770,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
             location in the .plt.  This is required to make function
             pointers compare as equal between the normal executable and
             the shared library.  */
-         if (! info->shared
+         if (! bfd_link_pic (info)
              && !h->def_regular)
            {
              h->root.u.def.section = s;
@@ -1661,7 +1806,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
      to R_390_TLS_LE64 requiring no TLS entry. For GOTIE12 and IEENT
      we can save the dynamic TLS relocation.  */
   if (h->got.refcount > 0
-      && !info->shared
+      && !bfd_link_pic (info)
       && h->dynindx == -1
       && elf_s390_hash_entry(h)->tls_type >= GOT_TLS_IE)
     {
@@ -1707,14 +1852,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
        htab->elf.srelgot->size += 2 * sizeof (Elf64_External_Rela);
       else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
                || h->root.type != bfd_link_hash_undefweak)
-              && (info->shared
+              && (bfd_link_pic (info)
                   || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
        htab->elf.srelgot->size += sizeof (Elf64_External_Rela);
     }
   else
     h->got.offset = (bfd_vma) -1;
 
-  eh = (struct elf_s390_link_hash_entry *) h;
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -1724,7 +1868,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h,
      space for pc-relative relocs that have become local due to symbol
      visibility changes.  */
 
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       if (SYMBOL_CALLS_LOCAL (info, h))
        {
@@ -1851,7 +1995,7 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (htab->elf.dynamic_sections_created)
     {
       /* Set the contents of the .interp section to the interpreter.  */
-      if (info->executable)
+      if (bfd_link_executable (info) && !info->nointerp)
        {
          s = bfd_get_linker_section (dynobj, ".interp");
          if (s == NULL)
@@ -1863,7 +2007,7 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
@@ -1871,6 +2015,8 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *srela;
+      struct plt_entry *local_plt;
+      unsigned int i;
 
       if (! is_s390_elf (ibfd))
        continue;
@@ -1917,12 +2063,26 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              s->size += GOT_ENTRY_SIZE;
              if (*local_tls_type == GOT_TLS_GD)
                s->size += GOT_ENTRY_SIZE;
-             if (info->shared)
+             if (bfd_link_pic (info))
                srela->size += sizeof (Elf64_External_Rela);
            }
          else
            *local_got = (bfd_vma) -1;
        }
+
+      local_plt = elf_s390_local_plt (ibfd);
+      for (i = 0; i < symtab_hdr->sh_info; i++)
+       {
+         if (local_plt[i].plt.refcount > 0)
+           {
+             local_plt[i].plt.offset = htab->elf.iplt->size;
+             htab->elf.iplt->size += PLT_ENTRY_SIZE;
+             htab->elf.igotplt->size += GOT_ENTRY_SIZE;
+             htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+           }
+         else
+           local_plt[i].plt.offset = (bfd_vma) -1;
+       }
     }
 
   if (htab->tls_ldm_got.refcount > 0)
@@ -1951,7 +2111,11 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       if (s == htab->elf.splt
          || s == htab->elf.sgot
          || s == htab->elf.sgotplt
-         || s == htab->sdynbss)
+         || s == htab->elf.sdynbss
+         || s == htab->elf.sdynrelro
+         || s == htab->elf.iplt
+         || s == htab->elf.igotplt
+         || s == htab->irelifunc)
        {
          /* Strip this section if we don't need it; see the
             comment below.  */
@@ -2010,7 +2174,7 @@ elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 #define add_dynamic_entry(TAG, VAL) \
   _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (info->executable)
+      if (bfd_link_executable (info))
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -2088,7 +2252,8 @@ invalid_tls_insn (bfd *input_bfd,
   reloc_howto_type *howto;
 
   howto = elf_howto_table + ELF64_R_TYPE (rel->r_info);
-  (*_bfd_error_handler)
+  _bfd_error_handler
+    /* xgettext:c-format */
     (_("%B(%A+0x%lx): invalid instruction for TLS relocation %s"),
      input_bfd,
      input_section,
@@ -2141,6 +2306,7 @@ elf_s390_relocate_section (bfd *output_bfd,
       bfd_boolean unresolved_reloc;
       bfd_reloc_status_type r;
       int tls_type;
+      asection *base_got = htab->elf.sgot;
 
       r_type = ELF64_R_TYPE (rel->r_info);
       if (r_type == (int) R_390_GNU_VTINHERIT
@@ -2163,23 +2329,77 @@ elf_s390_relocate_section (bfd *output_bfd,
        {
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+         if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+           {
+             struct plt_entry *local_plt = elf_s390_local_plt (input_bfd);
+             if (local_plt == NULL)
+               return FALSE;
+
+             /* Address of the PLT slot.  */
+             relocation = (htab->elf.iplt->output_section->vma
+                           + htab->elf.iplt->output_offset
+                           + local_plt[r_symndx].plt.offset);
+
+             switch (r_type)
+               {
+               case R_390_PLTOFF16:
+               case R_390_PLTOFF32:
+               case R_390_PLTOFF64:
+                 relocation -= htab->elf.sgot->output_section->vma;
+                 break;
+               case R_390_GOTPLT12:
+               case R_390_GOTPLT16:
+               case R_390_GOTPLT20:
+               case R_390_GOTPLT32:
+               case R_390_GOTPLT64:
+               case R_390_GOTPLTENT:
+               case R_390_GOT12:
+               case R_390_GOT16:
+               case R_390_GOT20:
+               case R_390_GOT32:
+               case R_390_GOT64:
+               case R_390_GOTENT:
+                 {
+                   /* Write the PLT slot address into the GOT slot.  */
+                   bfd_put_64 (output_bfd, relocation,
+                               htab->elf.sgot->contents +
+                               local_got_offsets[r_symndx]);
+                   relocation = (local_got_offsets[r_symndx] +
+                                 htab->elf.sgot->output_offset);
+
+                   if (r_type == R_390_GOTENT || r_type == R_390_GOTPLTENT)
+                     relocation += htab->elf.sgot->output_section->vma;
+                   break;
+                 }
+               default:
+                 break;
+               }
+             /* The output section is needed later in
+                finish_dynamic_section when creating the dynamic
+                relocation.  */
+             local_plt[r_symndx].sec = sec;
+             goto do_relocation;
+           }
+         else
+           relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
        }
       else
        {
          bfd_boolean warned ATTRIBUTE_UNUSED;
+         bfd_boolean ignored ATTRIBUTE_UNUSED;
 
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
        }
 
       if (sec != NULL && discarded_section (sec))
        RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
                                         rel, 1, relend, howto, 0, contents);
 
-      if (info->relocatable)
+      if (bfd_link_relocatable (info))
        continue;
 
       switch (r_type)
@@ -2203,18 +2423,28 @@ elf_s390_relocate_section (bfd *output_bfd,
            {
              bfd_vma plt_index;
 
-             /* Calc. index no.
-                Current offset - size first entry / entry size.  */
-             plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
-               PLT_ENTRY_SIZE;
-
-             /* Offset in GOT is PLT index plus GOT headers(3) times 4,
-                addr & GOT addr.  */
-             relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+             if (s390_is_ifunc_symbol_p (h))
+               {
+                 plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+                 relocation = (plt_index * GOT_ENTRY_SIZE +
+                               htab->elf.igotplt->output_offset);
+                 if (r_type == R_390_GOTPLTENT)
+                   relocation += htab->elf.igotplt->output_section->vma;
+               }
+             else
+               {
+                 /* Calc. index no.
+                    Current offset - size first entry / entry size.  */
+                 plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
+                   PLT_ENTRY_SIZE;
+
+                 /* Offset in GOT is PLT index plus GOT headers(3)
+                    times 8, addr & GOT addr.  */
+                 relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+                 if (r_type == R_390_GOTPLTENT)
+                   relocation += htab->elf.sgot->output_section->vma;
+               }
              unresolved_reloc = FALSE;
-
-             if (r_type == R_390_GOTPLTENT)
-               relocation += htab->elf.sgot->output_section->vma;
              break;
            }
          /* Fall through.  */
@@ -2227,7 +2457,7 @@ elf_s390_relocate_section (bfd *output_bfd,
        case R_390_GOTENT:
          /* Relocation is to the entry for this symbol in the global
             offset table.  */
-         if (htab->elf.sgot == NULL)
+         if (base_got == NULL)
            abort ();
 
          if (h != NULL)
@@ -2236,11 +2466,31 @@ elf_s390_relocate_section (bfd *output_bfd,
 
              off = h->got.offset;
              dyn = htab->elf.dynamic_sections_created;
-             if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
-                 || (info->shared
-                     && SYMBOL_REFERENCES_LOCAL (info, h))
-                 || (ELF_ST_VISIBILITY (h->other)
-                     && h->root.type == bfd_link_hash_undefweak))
+
+             if (s390_is_ifunc_symbol_p (h))
+               {
+                 BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
+                 if (off == (bfd_vma)-1)
+                   {
+                     /* No explicit GOT usage so redirect to the
+                        got.iplt slot.  */
+                     base_got = htab->elf.igotplt;
+                     off = h->plt.offset / PLT_ENTRY_SIZE * GOT_ENTRY_SIZE;
+                   }
+                 else
+                   {
+                     /* Explicit GOT slots must contain the address
+                        of the PLT slot. This will be handled in
+                        finish_dynamic_symbol.  */
+                   }
+               }
+             else if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn,
+                                                         bfd_link_pic (info),
+                                                         h)
+                      || (bfd_link_pic (info)
+                          && SYMBOL_REFERENCES_LOCAL (info, h))
+                      || (ELF_ST_VISIBILITY (h->other)
+                          && h->root.type == bfd_link_hash_undefweak))
                {
                  /* This is actually a static link, or it is a
                     -Bsymbolic link and the symbol is defined
@@ -2259,9 +2509,40 @@ elf_s390_relocate_section (bfd *output_bfd,
                  else
                    {
                      bfd_put_64 (output_bfd, relocation,
-                                 htab->elf.sgot->contents + off);
+                                 base_got->contents + off);
                      h->got.offset |= 1;
                    }
+
+                 if ((h->def_regular
+                      && bfd_link_pic (info)
+                      && SYMBOL_REFERENCES_LOCAL (info, h))
+                     /* lgrl rx,sym@GOTENT -> larl rx, sym */
+                     && ((r_type == R_390_GOTENT
+                          && (bfd_get_16 (input_bfd,
+                                          contents + rel->r_offset - 2)
+                              & 0xff0f) == 0xc408)
+                         /* lg rx, sym@GOT(r12) -> larl rx, sym */
+                         || (r_type == R_390_GOT20
+                             && (bfd_get_32 (input_bfd,
+                                             contents + rel->r_offset - 2)
+                                 & 0xff00f000) == 0xe300c000
+                             && bfd_get_8 (input_bfd,
+                                           contents + rel->r_offset + 3) == 0x04)))
+
+                   {
+                     unsigned short new_insn =
+                       (0xc000 | (bfd_get_8 (input_bfd,
+                                             contents + rel->r_offset - 1) & 0xf0));
+                     bfd_put_16 (output_bfd, new_insn,
+                                 contents + rel->r_offset - 2);
+                     r_type = R_390_PC32DBL;
+                     rel->r_addend = 2;
+                     howto = elf_howto_table + r_type;
+                     relocation = h->root.u.def.value
+                       + h->root.u.def.section->output_section->vma
+                       + h->root.u.def.section->output_offset;
+                     goto do_relocation;
+                   }
                }
              else
                unresolved_reloc = FALSE;
@@ -2283,7 +2564,7 @@ elf_s390_relocate_section (bfd *output_bfd,
                  bfd_put_64 (output_bfd, relocation,
                              htab->elf.sgot->contents + off);
 
-                 if (info->shared)
+                 if (bfd_link_pic (info))
                    {
                      asection *s;
                      Elf_Internal_Rela outrel;
@@ -2310,7 +2591,7 @@ elf_s390_relocate_section (bfd *output_bfd,
          if (off >= (bfd_vma) -2)
            abort ();
 
-         relocation = htab->elf.sgot->output_offset + off;
+         relocation = base_got->output_offset + off;
 
          /* For @GOTENT the relocation is against the offset between
             the instruction and the symbols entry in the GOT and not
@@ -2318,7 +2599,7 @@ elf_s390_relocate_section (bfd *output_bfd,
             add the vma of the GOT to get the correct value.  */
          if (   r_type == R_390_GOTENT
              || r_type == R_390_GOTPLTENT)
-           relocation += htab->elf.sgot->output_section->vma;
+           relocation += base_got->output_section->vma;
 
          break;
 
@@ -2328,6 +2609,18 @@ elf_s390_relocate_section (bfd *output_bfd,
          /* Relocation is relative to the start of the global offset
             table.  */
 
+         if (h != NULL
+             && s390_is_ifunc_symbol_p (h)
+             && h->def_regular
+             && !bfd_link_executable (info))
+           {
+             relocation = (htab->elf.iplt->output_section->vma
+                           + htab->elf.iplt->output_offset
+                           + h->plt.offset
+                           - htab->elf.sgot->output_section->vma);
+             goto do_relocation;
+           }
+
          /* Note that sgot->output_offset is not involved in this
             calculation.  We always want the start of .got.  If we
             defined _GLOBAL_OFFSET_TABLE in a different way, as is
@@ -2343,7 +2636,9 @@ elf_s390_relocate_section (bfd *output_bfd,
          unresolved_reloc = FALSE;
          break;
 
+       case R_390_PLT12DBL:
        case R_390_PLT16DBL:
+       case R_390_PLT24DBL:
        case R_390_PLT32:
        case R_390_PLT32DBL:
        case R_390_PLT64:
@@ -2356,17 +2651,21 @@ elf_s390_relocate_section (bfd *output_bfd,
            break;
 
          if (h->plt.offset == (bfd_vma) -1
-             || htab->elf.splt == NULL)
+             || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
            {
              /* We didn't make a PLT entry for this symbol.  This
                 happens when statically linking PIC code, or when
                 using -Bsymbolic.  */
              break;
            }
-
-         relocation = (htab->elf.splt->output_section->vma
-                       + htab->elf.splt->output_offset
-                       + h->plt.offset);
+         if (s390_is_ifunc_symbol_p (h))
+           relocation = (htab->elf.iplt->output_section->vma
+                         + htab->elf.iplt->output_offset
+                         + h->plt.offset);
+         else
+           relocation = (htab->elf.splt->output_section->vma
+                         + htab->elf.splt->output_offset
+                         + h->plt.offset);
          unresolved_reloc = FALSE;
          break;
 
@@ -2378,45 +2677,145 @@ elf_s390_relocate_section (bfd *output_bfd,
 
          /* For local symbols or if we didn't make a PLT entry for
             this symbol resolve the symbol directly.  */
-         if (   h == NULL
+         if (h == NULL
              || h->plt.offset == (bfd_vma) -1
-             || htab->elf.splt == NULL)
+             || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
            {
              relocation -= htab->elf.sgot->output_section->vma;
              break;
            }
 
-         relocation = (htab->elf.splt->output_section->vma
-                       + htab->elf.splt->output_offset
-                       + h->plt.offset
-                       - htab->elf.sgot->output_section->vma);
+         if (s390_is_ifunc_symbol_p (h))
+           relocation = (htab->elf.iplt->output_section->vma
+                         + htab->elf.iplt->output_offset
+                         + h->plt.offset
+                         - htab->elf.sgot->output_section->vma);
+         else
+           relocation = (htab->elf.splt->output_section->vma
+                         + htab->elf.splt->output_offset
+                         + h->plt.offset
+                         - htab->elf.sgot->output_section->vma);
          unresolved_reloc = FALSE;
          break;
 
-       case R_390_8:
-       case R_390_16:
-       case R_390_32:
-       case R_390_64:
        case R_390_PC16:
+       case R_390_PC12DBL:
        case R_390_PC16DBL:
+       case R_390_PC24DBL:
        case R_390_PC32:
        case R_390_PC32DBL:
        case R_390_PC64:
+         if (h != NULL
+             && bfd_link_pie (info)
+             && !h->def_regular)
+           {
+             _bfd_error_handler (_("%B: `%s' non-PLT reloc for symbol defined "
+                                   "in shared library and accessed "
+                                   "from executable "
+                                   "(rebuild file with -fPIC ?)"),
+                                 input_bfd, h->root.root.string);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
+         /* The target of these relocs are instruction operands
+            residing in read-only sections.  We cannot emit a runtime
+            reloc for it.  */
+         if (h != NULL
+             && s390_is_ifunc_symbol_p (h)
+             && h->def_regular
+             && bfd_link_pic (info))
+           {
+             relocation = (htab->elf.iplt->output_section->vma
+                           + htab->elf.iplt->output_offset
+                           + h->plt.offset);
+             goto do_relocation;
+           }
+         /* Fall through.  */
+
+       case R_390_8:
+       case R_390_16:
+       case R_390_32:
+       case R_390_64:
+
+         if (h != NULL
+             && s390_is_ifunc_symbol_p (h)
+             && h->def_regular)
+           {
+             if (!bfd_link_pic (info))
+               {
+                 /* For a non-shared object the symbol will not
+                    change.  Hence we can write the address of the
+                    target IPLT slot now.  */
+                 relocation = (htab->elf.iplt->output_section->vma
+                               + htab->elf.iplt->output_offset
+                               + h ->plt.offset);
+                 goto do_relocation;
+               }
+             else
+               {
+                 /* For shared objects a runtime relocation is needed.  */
+
+                 Elf_Internal_Rela outrel;
+                 asection *sreloc;
+
+                 /* Need a dynamic relocation to get the real function
+                    address.  */
+                 outrel.r_offset = _bfd_elf_section_offset (output_bfd,
+                                                            info,
+                                                            input_section,
+                                                            rel->r_offset);
+                 if (outrel.r_offset == (bfd_vma) -1
+                     || outrel.r_offset == (bfd_vma) -2)
+                   abort ();
+
+                 outrel.r_offset += (input_section->output_section->vma
+                                     + input_section->output_offset);
+
+                 if (h->dynindx == -1
+                     || h->forced_local
+                     || bfd_link_executable (info))
+                   {
+                     /* This symbol is resolved locally.  */
+                     outrel.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+                     outrel.r_addend = (h->root.u.def.value
+                                        + h->root.u.def.section->output_section->vma
+                                        + h->root.u.def.section->output_offset);
+                   }
+                 else
+                   {
+                     outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
+                     outrel.r_addend = 0;
+                   }
+
+                 sreloc = htab->elf.irelifunc;
+                 elf_append_rela (output_bfd, sreloc, &outrel);
+
+                 /* If this reloc is against an external symbol, we
+                    do not want to fiddle with the addend.  Otherwise,
+                    we need to include the symbol value so that it
+                    becomes an addend for the dynamic reloc.  For an
+                    internal symbol, we have updated addend.  */
+                 continue;
+               }
+           }
+
          if ((input_section->flags & SEC_ALLOC) == 0)
            break;
 
-         if ((info->shared
+         if ((bfd_link_pic (info)
               && (h == NULL
                   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
                   || h->root.type != bfd_link_hash_undefweak)
               && ((r_type != R_390_PC16
+                   && r_type != R_390_PC12DBL
                    && r_type != R_390_PC16DBL
+                   && r_type != R_390_PC24DBL
                    && r_type != R_390_PC32
                    && r_type != R_390_PC32DBL
                    && r_type != R_390_PC64)
                   || !SYMBOL_CALLS_LOCAL (info, h)))
              || (ELIMINATE_COPY_RELOCS
-                 && !info->shared
+                 && !bfd_link_pic (info)
                  && h != NULL
                  && h->dynindx != -1
                  && !h->non_got_ref
@@ -2452,11 +2851,13 @@ elf_s390_relocate_section (bfd *output_bfd,
              else if (h != NULL
                       && h->dynindx != -1
                       && (r_type == R_390_PC16
+                          || r_type == R_390_PC12DBL
                           || r_type == R_390_PC16DBL
+                          || r_type == R_390_PC24DBL
                           || r_type == R_390_PC32
                           || r_type == R_390_PC32DBL
                           || r_type == R_390_PC64
-                          || !info->shared
+                          || !bfd_link_pic (info)
                           || !SYMBOLIC_BIND (info, h)
                           || !h->def_regular))
                {
@@ -2528,7 +2929,7 @@ elf_s390_relocate_section (bfd *output_bfd,
 
          /* Relocations for tls literal pool entries.  */
        case R_390_TLS_IE64:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              Elf_Internal_Rela outrel;
              asection *sreloc;
@@ -2556,7 +2957,7 @@ elf_s390_relocate_section (bfd *output_bfd,
          else if (h != NULL)
            {
              tls_type = elf_s390_hash_entry(h)->tls_type;
-             if (!info->shared && h->dynindx == -1 && tls_type >= GOT_TLS_IE)
+             if (!bfd_link_pic (info) && h->dynindx == -1 && tls_type >= GOT_TLS_IE)
                r_type = R_390_TLS_LE64;
            }
          if (r_type == R_390_TLS_GD64 && tls_type >= GOT_TLS_IE)
@@ -2667,14 +3068,14 @@ elf_s390_relocate_section (bfd *output_bfd,
              if (local_got_offsets == NULL)
                abort();
              off = local_got_offsets[r_symndx];
-             if (info->shared)
+             if (bfd_link_pic (info))
                goto emit_tls_relocs;
            }
          else
            {
              off = h->got.offset;
              tls_type = elf_s390_hash_entry(h)->tls_type;
-             if (info->shared || h->dynindx != -1 || tls_type < GOT_TLS_IE)
+             if (bfd_link_pic (info) || h->dynindx != -1 || tls_type < GOT_TLS_IE)
                goto emit_tls_relocs;
            }
 
@@ -2691,7 +3092,7 @@ elf_s390_relocate_section (bfd *output_bfd,
          break;
 
        case R_390_TLS_LDM64:
-         if (! info->shared)
+         if (! bfd_link_pic (info))
            /* The literal pool entry this relocation refers to gets ignored
               by the optimized code of the local exec model. Do nothing
               and the value will turn out zero.  */
@@ -2729,7 +3130,7 @@ elf_s390_relocate_section (bfd *output_bfd,
          break;
 
        case R_390_TLS_LE64:
-         if (info->shared)
+         if (bfd_link_dll (info))
            {
              /* Linking a shared library with non-fpic code requires
                 a R_390_TLS_TPOFF relocation.  */
@@ -2766,7 +3167,7 @@ elf_s390_relocate_section (bfd *output_bfd,
          continue;
 
        case R_390_TLS_LDO64:
-         if (info->shared || (input_section->flags & SEC_DEBUGGING))
+         if (bfd_link_pic (info) || (input_section->flags & SEC_DEBUGGING))
            relocation -= dtpoff_base (info);
          else
            /* When converting LDO to LE, we must negate.  */
@@ -2788,7 +3189,7 @@ elf_s390_relocate_section (bfd *output_bfd,
 
          if (r_type == R_390_TLS_LOAD)
            {
-             if (!info->shared && (h == NULL || h->dynindx == -1))
+             if (!bfd_link_pic (info) && (h == NULL || h->dynindx == -1))
                {
                  /* IE->LE transition. Four valid cases:
                     lg %rx,(0,%ry)    -> sllg %rx,%ry,0
@@ -2800,8 +3201,10 @@ elf_s390_relocate_section (bfd *output_bfd,
                  insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
                  insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
                  if (insn1 != 0x0004)
-                   invalid_tls_insn (input_bfd, input_section, rel);
-                 ry = 0;
+                   {
+                     invalid_tls_insn (input_bfd, input_section, rel);
+                     return FALSE;
+                   }
                  if ((insn0 & 0xff00f000) == 0xe3000000)
                    /* lg %rx,0(%ry,0) -> sllg %rx,%ry,0  */
                    ry = (insn0 & 0x000f0000);
@@ -2815,7 +3218,10 @@ elf_s390_relocate_section (bfd *output_bfd,
                    /* lg %rx,0(%r12,%ry) -> sllg %rx,%ry,0  */
                    ry = (insn0 & 0x0000f000) << 4;
                  else
-                   invalid_tls_insn (input_bfd, input_section, rel);
+                   {
+                     invalid_tls_insn (input_bfd, input_section, rel);
+                     return FALSE;
+                   }
                  insn0 = 0xeb000000 | (insn0 & 0x00f00000) | ry;
                  insn1 = 0x000d;
                  bfd_put_32 (output_bfd, insn0, contents + rel->r_offset);
@@ -2829,8 +3235,11 @@ elf_s390_relocate_section (bfd *output_bfd,
              insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
              insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
              if ((insn0 & 0xffff0000) != 0xc0e50000)
-               invalid_tls_insn (input_bfd, input_section, rel);
-             if (!info->shared && (h == NULL || h->dynindx == -1))
+               {
+                 invalid_tls_insn (input_bfd, input_section, rel);
+                 return FALSE;
+               }
+             if (!bfd_link_pic (info) && (h == NULL || h->dynindx == -1))
                {
                  /* GD->LE transition.
                     brasl %r14,__tls_get_addr@plt -> brcl 0,. */
@@ -2849,14 +3258,17 @@ elf_s390_relocate_section (bfd *output_bfd,
            }
          else if (r_type == R_390_TLS_LDCALL)
            {
-             if (!info->shared)
+             if (!bfd_link_pic (info))
                {
                  unsigned int insn0, insn1;
 
                  insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
                  insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
                  if ((insn0 & 0xffff0000) != 0xc0e50000)
-                   invalid_tls_insn (input_bfd, input_section, rel);
+                   {
+                     invalid_tls_insn (input_bfd, input_section, rel);
+                     return FALSE;
+                   }
                  /* LD->LE transition.
                     brasl %r14,__tls_get_addr@plt -> brcl 0,. */
                  insn0 = 0xc0040000;
@@ -2879,7 +3291,8 @@ elf_s390_relocate_section (bfd *output_bfd,
               && h->def_dynamic)
          && _bfd_elf_section_offset (output_bfd, info, input_section,
                                      rel->r_offset) != (bfd_vma) -1)
-       (*_bfd_error_handler)
+       _bfd_error_handler
+         /* xgettext:c-format */
          (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
           input_bfd,
           input_section,
@@ -2887,6 +3300,15 @@ elf_s390_relocate_section (bfd *output_bfd,
           howto->name,
           h->root.root.string);
 
+    do_relocation:
+
+      /* When applying a 24 bit reloc we need to start one byte
+        earlier.  Otherwise the 32 bit get/put bfd operations might
+        access a byte after the actual section.  */
+      if (r_type == R_390_PC24DBL
+         || r_type == R_390_PLT24DBL)
+       rel->r_offset--;
+
       if (r_type == R_390_20
          || r_type == R_390_GOT20
          || r_type == R_390_GOTPLT20
@@ -2921,17 +3343,13 @@ elf_s390_relocate_section (bfd *output_bfd,
            }
 
          if (r == bfd_reloc_overflow)
-           {
-
-             if (! ((*info->callbacks->reloc_overflow)
-                    (info, (h ? &h->root : NULL), name, howto->name,
-                     (bfd_vma) 0, input_bfd, input_section,
-                     rel->r_offset)))
-               return FALSE;
-           }
+           (*info->callbacks->reloc_overflow)
+             (info, (h ? &h->root : NULL), name, howto->name,
+              (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
          else
            {
-             (*_bfd_error_handler)
+             _bfd_error_handler
+               /* xgettext:c-format */
                (_("%B(%A+0x%lx): reloc against `%s': error %d"),
                 input_bfd, input_section,
                 (long) rel->r_offset, name, (int) r);
@@ -2943,6 +3361,92 @@ elf_s390_relocate_section (bfd *output_bfd,
   return TRUE;
 }
 
+/* Generate the PLT slots together with the dynamic relocations needed
+   for IFUNC symbols.  */
+
+static void
+elf_s390_finish_ifunc_symbol (bfd *output_bfd,
+                             struct bfd_link_info *info,
+                             struct elf_link_hash_entry *h,
+                             struct elf_s390_link_hash_table *htab,
+                             bfd_vma plt_offset,
+                             bfd_vma resolver_address)
+{
+  bfd_vma plt_index;
+  bfd_vma got_offset;
+  Elf_Internal_Rela rela;
+  bfd_byte *loc;
+  asection *plt, *gotplt, *relplt;
+
+  if (htab->elf.iplt == NULL
+      || htab->elf.igotplt == NULL
+      || htab->elf.irelplt == NULL)
+    abort ();
+
+  /* Index of the PLT slot within iplt section.  */
+  plt_index = plt_offset / PLT_ENTRY_SIZE;
+  plt = htab->elf.iplt;
+  /* Offset into the igot.plt section.  */
+  got_offset = plt_index * GOT_ENTRY_SIZE;
+  gotplt = htab->elf.igotplt;
+  relplt = htab->elf.irelplt;
+
+  /* Fill in the blueprint of a PLT.  */
+  memcpy (plt->contents + plt_offset, elf_s390x_plt_entry,
+         PLT_ENTRY_SIZE);
+
+  /* Fixup the relative address to the GOT entry */
+  bfd_put_32 (output_bfd,
+             (gotplt->output_section->vma +
+              gotplt->output_offset + got_offset
+              - (plt->output_section->vma +
+                 plt->output_offset +
+                 plt_offset))/2,
+             plt->contents + plt_offset + 2);
+  /* Fixup the relative branch to PLT 0 */
+  bfd_put_32 (output_bfd, - (plt->output_offset +
+                            (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+             plt->contents + plt_offset + 24);
+  /* Fixup offset into .rela.plt section.  */
+  bfd_put_32 (output_bfd, relplt->output_offset +
+             plt_index * sizeof (Elf64_External_Rela),
+             plt->contents + plt_offset + 28);
+
+  /* Fill in the entry in the global offset table.
+     Points to instruction after GOT offset.  */
+  bfd_put_64 (output_bfd,
+             (plt->output_section->vma
+              + plt->output_offset
+              + plt_offset
+              + 14),
+             gotplt->contents + got_offset);
+
+  /* Fill in the entry in the .rela.plt section.  */
+  rela.r_offset = (gotplt->output_section->vma
+                  + gotplt->output_offset
+                  + got_offset);
+
+  if (!h
+      || h->dynindx == -1
+      || ((bfd_link_executable (info)
+          || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+         && h->def_regular))
+    {
+      /* The symbol can be locally resolved.  */
+      rela.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+      rela.r_addend = resolver_address;
+    }
+  else
+    {
+      rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+      rela.r_addend = 0;
+    }
+
+  loc = relplt->contents + plt_index * sizeof (Elf64_External_Rela);
+  bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+}
+
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -2953,6 +3457,7 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
                                Elf_Internal_Sym *sym)
 {
   struct elf_s390_link_hash_table *htab;
+  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
 
   htab = elf_s390_hash_table (info);
   if (htab == NULL)
@@ -2967,65 +3472,81 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
 
       /* This symbol has an entry in the procedure linkage table.  Set
         it up.  */
+      if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+       {
+         elf_s390_finish_ifunc_symbol (output_bfd, info, h,
+           htab, h->plt.offset,
+           eh->ifunc_resolver_address +
+           eh->ifunc_resolver_section->output_offset +
+           eh->ifunc_resolver_section->output_section->vma);
+
+         /* Do not return yet.  Handling of explicit GOT slots of
+            IFUNC symbols is below.  */
+       }
+      else
+       {
+         if (h->dynindx == -1
+             || htab->elf.splt == NULL
+             || htab->elf.sgotplt == NULL
+             || htab->elf.srelplt == NULL)
+           abort ();
 
-      if (h->dynindx == -1
-         || htab->elf.splt == NULL
-         || htab->elf.sgotplt == NULL
-         || htab->elf.srelplt == NULL)
-       abort ();
+         /* Calc. index no.
+            Current offset - size first entry / entry size.  */
+         plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
 
-      /* Calc. index no.
-        Current offset - size first entry / entry size.  */
-      plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
-
-      /* Offset in GOT is PLT index plus GOT headers(3) times 8,
-        addr & GOT addr.  */
-      got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
-
-      /* Fill in the blueprint of a PLT.  */
-      memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
-             PLT_ENTRY_SIZE);
-
-      /* Fixup the relative address to the GOT entry */
-      bfd_put_32 (output_bfd,
-                 (htab->elf.sgotplt->output_section->vma +
-                  htab->elf.sgotplt->output_offset + got_offset
-                  - (htab->elf.splt->output_section->vma + h->plt.offset))/2,
-                 htab->elf.splt->contents + h->plt.offset + 2);
-      /* Fixup the relative branch to PLT 0 */
-      bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
-                                (PLT_ENTRY_SIZE * plt_index) + 22)/2,
-                 htab->elf.splt->contents + h->plt.offset + 24);
-      /* Fixup offset into .rela.plt section.  */
-      bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
-                 htab->elf.splt->contents + h->plt.offset + 28);
-
-      /* Fill in the entry in the global offset table.
-        Points to instruction after GOT offset.  */
-      bfd_put_64 (output_bfd,
-                 (htab->elf.splt->output_section->vma
-                  + htab->elf.splt->output_offset
-                  + h->plt.offset
-                  + 14),
-                 htab->elf.sgotplt->contents + got_offset);
-
-      /* Fill in the entry in the .rela.plt section.  */
-      rela.r_offset = (htab->elf.sgotplt->output_section->vma
-                      + htab->elf.sgotplt->output_offset
-                      + got_offset);
-      rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
-      rela.r_addend = 0;
-      loc = htab->elf.srelplt->contents + plt_index * sizeof (Elf64_External_Rela);
-      bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+         /* Offset in GOT is PLT index plus GOT headers(3) times 8,
+            addr & GOT addr.  */
+         got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
 
-      if (!h->def_regular)
-       {
-         /* Mark the symbol as undefined, rather than as defined in
-            the .plt section.  Leave the value alone.  This is a clue
-            for the dynamic linker, to make function pointer
-            comparisons work between an application and shared
-            library.  */
-         sym->st_shndx = SHN_UNDEF;
+         /* Fill in the blueprint of a PLT.  */
+         memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
+                 PLT_ENTRY_SIZE);
+
+         /* Fixup the relative address to the GOT entry */
+         bfd_put_32 (output_bfd,
+                     (htab->elf.sgotplt->output_section->vma +
+                      htab->elf.sgotplt->output_offset + got_offset
+                      - (htab->elf.splt->output_section->vma +
+                         htab->elf.splt->output_offset +
+                         h->plt.offset))/2,
+                     htab->elf.splt->contents + h->plt.offset + 2);
+         /* Fixup the relative branch to PLT 0 */
+         bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
+                                    (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+                     htab->elf.splt->contents + h->plt.offset + 24);
+         /* Fixup offset into .rela.plt section.  */
+         bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
+                     htab->elf.splt->contents + h->plt.offset + 28);
+
+         /* Fill in the entry in the global offset table.
+            Points to instruction after GOT offset.  */
+         bfd_put_64 (output_bfd,
+                     (htab->elf.splt->output_section->vma
+                      + htab->elf.splt->output_offset
+                      + h->plt.offset
+                      + 14),
+                     htab->elf.sgotplt->contents + got_offset);
+
+         /* Fill in the entry in the .rela.plt section.  */
+         rela.r_offset = (htab->elf.sgotplt->output_section->vma
+                          + htab->elf.sgotplt->output_offset
+                          + got_offset);
+         rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+         rela.r_addend = 0;
+         loc = htab->elf.srelplt->contents + plt_index *
+           sizeof (Elf64_External_Rela);
+         bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+
+         if (!h->def_regular)
+           {
+             /* Mark the symbol as undefined, rather than as defined in
+                the .plt section.  Leave the value alone.  This is a clue
+                for the dynamic linker, to make function pointer
+                comparisons work between an application and shared
+                library.  */
+             sym->st_shndx = SHN_UNDEF;
+           }
        }
     }
 
@@ -3046,15 +3567,38 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
                       + htab->elf.sgot->output_offset
                       + (h->got.offset &~ (bfd_vma) 1));
 
-      /* If this is a static link, or it is a -Bsymbolic link and the
-        symbol is defined locally or was forced to be local because
-        of a version file, we just want to emit a RELATIVE reloc.
-        The entry in the global offset table will already have been
-        initialized in the relocate_section function.  */
-      if (info->shared
-         && SYMBOL_REFERENCES_LOCAL (info, h))
+      if (h->def_regular && s390_is_ifunc_symbol_p (h))
        {
-         if (!h->def_regular)
+         if (bfd_link_pic (info))
+           {
+             /* An explicit GOT slot usage needs GLOB_DAT.  If the
+                symbol references local the implicit got.iplt slot
+                will be used and the IRELATIVE reloc has been created
+                above.  */
+             goto do_glob_dat;
+           }
+         else
+           {
+             /* For non-shared objects explicit GOT slots must be
+                filled with the PLT slot address for pointer
+                equality reasons.  */
+             bfd_put_64 (output_bfd, (htab->elf.iplt->output_section->vma
+                                      + htab->elf.iplt->output_offset
+                                      + h->plt.offset),
+                         htab->elf.sgot->contents + h->got.offset);
+             return TRUE;
+           }
+       }
+      else if (bfd_link_pic (info)
+              && SYMBOL_REFERENCES_LOCAL (info, h))
+       {
+         /* If this is a static link, or it is a -Bsymbolic link and
+            the symbol is defined locally or was forced to be local
+            because of a version file, we just want to emit a
+            RELATIVE reloc.  The entry in the global offset table
+            will already have been initialized in the
+            relocate_section function.  */
+         if (!(h->def_regular || ELF_COMMON_DEF_P (h)))
            return FALSE;
          BFD_ASSERT((h->got.offset & 1) != 0);
          rela.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
@@ -3065,6 +3609,7 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
       else
        {
          BFD_ASSERT((h->got.offset & 1) == 0);
+       do_glob_dat:
          bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgot->contents + h->got.offset);
          rela.r_info = ELF64_R_INFO (h->dynindx, R_390_GLOB_DAT);
          rela.r_addend = 0;
@@ -3078,6 +3623,7 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
   if (h->needs_copy)
     {
       Elf_Internal_Rela rela;
+      asection *s;
       bfd_byte *loc;
 
       /* This symbols needs a copy reloc.  Set it up.  */
@@ -3085,7 +3631,7 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
       if (h->dynindx == -1
          || (h->root.type != bfd_link_hash_defined
              && h->root.type != bfd_link_hash_defweak)
-         || htab->srelbss == NULL)
+         || htab->elf.srelbss == NULL)
        abort ();
 
       rela.r_offset = (h->root.u.def.value
@@ -3093,13 +3639,16 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
                       + h->root.u.def.section->output_offset);
       rela.r_info = ELF64_R_INFO (h->dynindx, R_390_COPY);
       rela.r_addend = 0;
-      loc = htab->srelbss->contents;
-      loc += htab->srelbss->reloc_count++ * sizeof (Elf64_External_Rela);
+      if (h->root.u.def.section == htab->elf.sdynrelro)
+       s = htab->elf.sreldynrelro;
+      else
+       s = htab->elf.srelbss;
+      loc = s->contents + s->reloc_count++ * sizeof (Elf64_External_Rela);
       bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
     }
 
   /* Mark some specially defined symbols as absolute.  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
+  if (h == htab->elf.hdynamic
       || h == htab->elf.hgot
       || h == htab->elf.hplt)
     sym->st_shndx = SHN_ABS;
@@ -3111,8 +3660,27 @@ elf_s390_finish_dynamic_symbol (bfd *output_bfd,
    dynamic linker, before writing them out.  */
 
 static enum elf_reloc_type_class
-elf_s390_reloc_type_class (const Elf_Internal_Rela *rela)
+elf_s390_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                          const asection *rel_sec ATTRIBUTE_UNUSED,
+                          const Elf_Internal_Rela *rela)
 {
+  bfd *abfd = info->output_bfd;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+  unsigned long r_symndx = ELF64_R_SYM (rela->r_info);
+  Elf_Internal_Sym sym;
+
+  if (htab->elf.dynsym == NULL
+      || !bed->s->swap_symbol_in (abfd,
+                                 (htab->elf.dynsym->contents
+                                  + r_symndx * bed->s->sizeof_sym),
+                                 0, &sym))
+    abort ();
+
+  /* Check relocation against STT_GNU_IFUNC symbol.  */
+  if (ELF_ST_TYPE (sym.st_info) == STT_GNU_IFUNC)
+    return reloc_class_ifunc;
+
   switch ((int) ELF64_R_TYPE (rela->r_info))
     {
     case R_390_RELATIVE:
@@ -3135,6 +3703,8 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
   struct elf_s390_link_hash_table *htab;
   bfd *dynobj;
   asection *sdyn;
+  bfd *ibfd;
+  unsigned int i;
 
   htab = elf_s390_hash_table (info);
   if (htab == NULL)
@@ -3165,16 +3735,17 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
              continue;
 
            case DT_PLTGOT:
-             dyn.d_un.d_ptr = htab->elf.sgot->output_section->vma;
+             s = htab->elf.sgotplt;
+             dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
              break;
 
            case DT_JMPREL:
-             dyn.d_un.d_ptr = htab->elf.srelplt->output_section->vma;
+             s = htab->elf.srelplt;
+             dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
              break;
 
            case DT_PLTRELSZ:
-             s = htab->elf.srelplt->output_section;
-             dyn.d_un.d_val = s->size;
+             dyn.d_un.d_val = htab->elf.srelplt->size + htab->elf.irelplt->size;
              break;
 
            case DT_RELASZ:
@@ -3185,8 +3756,7 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
                 linker script arranges for .rela.plt to follow all
                 other relocation sections, we don't have to worry
                 about changing the DT_RELA entry.  */
-             s = htab->elf.srelplt->output_section;
-             dyn.d_un.d_val -= s->size;
+             dyn.d_un.d_val -= htab->elf.srelplt->size + htab->elf.irelplt->size;
              break;
            }
 
@@ -3201,13 +3771,15 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
                  PLT_FIRST_ENTRY_SIZE);
          /* Fixup relative address to start of GOT */
          bfd_put_32 (output_bfd,
-                     (htab->elf.sgotplt->output_section->vma +
-                      htab->elf.sgotplt->output_offset
-                      - htab->elf.splt->output_section->vma - 6)/2,
+                     (htab->elf.sgotplt->output_section->vma
+                      + htab->elf.sgotplt->output_offset
+                      - htab->elf.splt->output_section->vma
+                      - htab->elf.splt->output_offset - 6)/2,
                      htab->elf.splt->contents + 8);
        }
-      elf_section_data (htab->elf.splt->output_section)
-       ->this_hdr.sh_entsize = PLT_ENTRY_SIZE;
+      if (elf_section_data (htab->elf.splt->output_section) != NULL)
+       elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize
+         = PLT_ENTRY_SIZE;
     }
 
   if (htab->elf.sgotplt)
@@ -3222,15 +3794,160 @@ elf_s390_finish_dynamic_sections (bfd *output_bfd,
          /* One entry for shared object struct ptr.  */
          bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 8);
          /* One entry for _dl_runtime_resolve.  */
-         bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 12);
+         bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 16);
        }
 
       elf_section_data (htab->elf.sgot->output_section)
        ->this_hdr.sh_entsize = 8;
     }
+
+  /* Finish dynamic symbol for local IFUNC symbols.  */
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      struct plt_entry *local_plt;
+      Elf_Internal_Sym *isym;
+      Elf_Internal_Shdr *symtab_hdr;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+
+      local_plt = elf_s390_local_plt (ibfd);
+      if (local_plt != NULL)
+       for (i = 0; i < symtab_hdr->sh_info; i++)
+         {
+           if (local_plt[i].plt.offset != (bfd_vma) -1)
+             {
+               asection *sec = local_plt[i].sec;
+               isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, i);
+               if (isym == NULL)
+                 return FALSE;
+
+               if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+                 elf_s390_finish_ifunc_symbol (output_bfd, info, NULL, htab,
+                                               local_plt[i].plt.offset,
+                                               isym->st_value
+                                               + sec->output_section->vma
+                                               + sec->output_offset);
+
+             }
+         }
+    }
+
   return TRUE;
 }
+\f
+/* Support for core dump NOTE sections.  */
+
+static bfd_boolean
+elf_s390_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+  int offset;
+  size_t size;
+
+  switch (note->descsz)
+    {
+    default:
+      return FALSE;
+
+    case 336:                  /* sizeof(struct elf_prstatus) on s390x */
+      /* pr_cursig */
+      elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
+
+      /* pr_pid */
+      elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 32);
 
+      /* pr_reg */
+      offset = 112;
+      size = 216;
+      break;
+    }
+
+  /* Make a ".reg/999" section.  */
+  return _bfd_elfcore_make_pseudosection (abfd, ".reg",
+                                         size, note->descpos + offset);
+}
+
+static bfd_boolean
+elf_s390_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+  switch (note->descsz)
+    {
+    default:
+      return FALSE;
+
+    case 136:                  /* sizeof(struct elf_prpsinfo) on s390x */
+      elf_tdata (abfd)->core->pid
+       = bfd_get_32 (abfd, note->descdata + 24);
+      elf_tdata (abfd)->core->program
+       = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16);
+      elf_tdata (abfd)->core->command
+       = _bfd_elfcore_strndup (abfd, note->descdata + 56, 80);
+    }
+
+  /* Note that for some reason, a spurious space is tacked
+     onto the end of the args in some (at least one anyway)
+     implementations, so strip it off if it exists.  */
+
+  {
+    char *command = elf_tdata (abfd)->core->command;
+    int n = strlen (command);
+
+    if (0 < n && command[n - 1] == ' ')
+      command[n - 1] = '\0';
+  }
+
+  return TRUE;
+}
+
+static char *
+elf_s390_write_core_note (bfd *abfd, char *buf, int *bufsiz,
+                         int note_type, ...)
+{
+  va_list ap;
+
+  switch (note_type)
+    {
+    default:
+      return NULL;
+
+    case NT_PRPSINFO:
+      {
+       char data[136] = { 0 };
+       const char *fname, *psargs;
+
+       va_start (ap, note_type);
+       fname = va_arg (ap, const char *);
+       psargs = va_arg (ap, const char *);
+       va_end (ap);
+
+       strncpy (data + 40, fname, 16);
+       strncpy (data + 56, psargs, 80);
+       return elfcore_write_note (abfd, buf, bufsiz, "CORE", note_type,
+                                  &data, sizeof (data));
+      }
+
+    case NT_PRSTATUS:
+      {
+       char data[336] = { 0 };
+       long pid;
+       int cursig;
+       const void *gregs;
+
+       va_start (ap, note_type);
+       pid = va_arg (ap, long);
+       cursig = va_arg (ap, int);
+       gregs = va_arg (ap, const void *);
+       va_end (ap);
+
+       bfd_put_16 (abfd, cursig, data + 12);
+       bfd_put_32 (abfd, pid, data + 32);
+       memcpy (data + 112, gregs, 216);
+       return elfcore_write_note (abfd, buf, bufsiz, "CORE", note_type,
+                                  &data, sizeof (data));
+      }
+    }
+  /* NOTREACHED */
+}
+\f
 /* Return address for Ith PLT stub in section PLT, for relocation REL
    or (bfd_vma) -1 if it should not be included.  */
 
@@ -3241,6 +3958,81 @@ elf_s390_plt_sym_val (bfd_vma i, const asection *plt,
   return plt->vma + PLT_FIRST_ENTRY_SIZE + i * PLT_ENTRY_SIZE;
 }
 
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+static bfd_boolean
+elf64_s390_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
+{
+  if (!is_s390_elf (ibfd) || !is_s390_elf (info->output_bfd))
+    return TRUE;
+
+  return elf_s390_merge_obj_attributes (ibfd, info);
+}
+
+/* We may add a PT_S390_PGSTE program header.  */
+
+static int
+elf_s390_additional_program_headers (bfd *abfd ATTRIBUTE_UNUSED,
+                                    struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab;
+
+  htab = elf_s390_hash_table (info);
+  return htab->params->pgste;
+}
+
+
+/* Add the PT_S390_PGSTE program header.  */
+
+static bfd_boolean
+elf_s390_modify_segment_map (bfd *abfd ATTRIBUTE_UNUSED,
+                            struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab;
+  struct elf_segment_map *m, *pm = NULL;
+
+  htab = elf_s390_hash_table (info);
+  if (!htab->params->pgste)
+    return TRUE;
+
+  /* If there is already a PT_S390_PGSTE header, avoid adding
+     another.  */
+  m = elf_seg_map (abfd);
+  while (m && m->p_type != PT_S390_PGSTE)
+    {
+      pm = m;
+      m = m->next;
+    }
+
+  if (m)
+    return TRUE;
+
+  m = (struct elf_segment_map *)
+    bfd_zalloc (abfd, sizeof (struct elf_segment_map));
+  if (m == NULL)
+    return FALSE;
+  m->p_type = PT_S390_PGSTE;
+  m->count = 0;
+  m->next = NULL;
+  if (pm)
+    pm->next = m;
+
+  return TRUE;
+}
+
+bfd_boolean
+bfd_elf_s390_set_options (struct bfd_link_info *info,
+                         struct s390_elf_params *params)
+{
+  struct elf_s390_link_hash_table *htab;
+
+  htab = elf_s390_hash_table (info);
+  htab->params = params;
+
+  return TRUE;
+}
+
 
 /* Why was the hash table entry size definition changed from
    ARCH_SIZE/8 to 4? This breaks the 64 bit dynamic linker and
@@ -3277,7 +4069,7 @@ const struct elf_size_info s390_elf64_size_info =
   bfd_elf64_swap_reloca_out
 };
 
-#define TARGET_BIG_SYM bfd_elf64_s390_vec
+#define TARGET_BIG_SYM s390_elf64_vec
 #define TARGET_BIG_NAME        "elf64-s390"
 #define ELF_ARCH       bfd_arch_s390
 #define ELF_TARGET_ID  S390_ELF_DATA
@@ -3293,6 +4085,7 @@ const struct elf_size_info s390_elf64_size_info =
 #define elf_backend_plt_readonly       1
 #define elf_backend_want_plt_sym       0
 #define elf_backend_got_header_size    24
+#define elf_backend_want_dynrelro      1
 #define elf_backend_rela_normal                1
 
 #define elf_info_to_howto              elf_s390_info_to_howto
@@ -3300,12 +4093,13 @@ const struct elf_size_info s390_elf64_size_info =
 #define bfd_elf64_bfd_is_local_label_name     elf_s390_is_local_label_name
 #define bfd_elf64_bfd_link_hash_table_create  elf_s390_link_hash_table_create
 #define bfd_elf64_bfd_reloc_type_lookup              elf_s390_reloc_type_lookup
-#define bfd_elf64_bfd_reloc_name_lookup elf_s390_reloc_name_lookup
+#define bfd_elf64_bfd_reloc_name_lookup       elf_s390_reloc_name_lookup
+#define bfd_elf64_bfd_merge_private_bfd_data  elf64_s390_merge_private_bfd_data
 
 #define elf_backend_adjust_dynamic_symbol     elf_s390_adjust_dynamic_symbol
 #define elf_backend_check_relocs             elf_s390_check_relocs
 #define elf_backend_copy_indirect_symbol      elf_s390_copy_indirect_symbol
-#define elf_backend_create_dynamic_sections   elf_s390_create_dynamic_sections
+#define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_finish_dynamic_sections   elf_s390_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol     elf_s390_finish_dynamic_symbol
 #define elf_backend_gc_mark_hook             elf_s390_gc_mark_hook
@@ -3314,25 +4108,16 @@ const struct elf_size_info s390_elf64_size_info =
 #define elf_backend_relocate_section         elf_s390_relocate_section
 #define elf_backend_size_dynamic_sections     elf_s390_size_dynamic_sections
 #define elf_backend_init_index_section       _bfd_elf_init_1_index_section
-#define elf_backend_reloc_type_class         elf_s390_reloc_type_class
+#define elf_backend_grok_prstatus            elf_s390_grok_prstatus
+#define elf_backend_grok_psinfo                      elf_s390_grok_psinfo
+#define elf_backend_write_core_note          elf_s390_write_core_note
 #define elf_backend_plt_sym_val                      elf_s390_plt_sym_val
+#define elf_backend_add_symbol_hook           elf_s390_add_symbol_hook
+#define elf_backend_sort_relocs_p             elf_s390_elf_sort_relocs_p
+#define elf_backend_additional_program_headers elf_s390_additional_program_headers
+#define elf_backend_modify_segment_map       elf_s390_modify_segment_map
 
 #define bfd_elf64_mkobject             elf_s390_mkobject
 #define elf_backend_object_p           elf_s390_object_p
 
-/* Enable ELF64 archive functions.  */
-#define bfd_elf64_archive_functions
-extern bfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
-extern bfd_boolean bfd_elf64_archive_write_armap (bfd *, unsigned int, struct orl *, unsigned int, int);
-
-#define bfd_elf64_archive_slurp_extended_name_table    _bfd_archive_coff_slurp_extended_name_table
-#define bfd_elf64_archive_construct_extended_name_table _bfd_archive_coff_construct_extended_name_table
-#define bfd_elf64_archive_truncate_arname              _bfd_archive_coff_truncate_arname
-#define bfd_elf64_archive_read_ar_hdr                  _bfd_archive_coff_read_ar_hdr
-#define bfd_elf64_archive_write_ar_hdr                 _bfd_archive_coff_write_ar_hdr
-#define bfd_elf64_archive_openr_next_archived_file     _bfd_archive_coff_openr_next_archived_file
-#define bfd_elf64_archive_get_elt_at_index             _bfd_archive_coff_get_elt_at_index
-#define bfd_elf64_archive_generic_stat_arch_elt        _bfd_archive_coff_generic_stat_arch_elt
-#define bfd_elf64_archive_update_armap_timestamp       _bfd_archive_coff_update_armap_timestamp
-
 #include "elf64-target.h"
This page took 0.078835 seconds and 4 git commands to generate.