This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] S/390: STT_GNU_IFUNC support


Hi,

the attached patch implements support for STT_GNU_IFUNC for s390 and
s390x.

To big parts I've tried to copy the x86 approach except the following
things:

- PLT infos for local IFUNC symbols is stored in the target specific
  data area for an object file (elf_obj_tdata).

- The iplt section is also used for dynamic linking in order to keep
  all the IFUNC related relocations together at the end of the
  rela.plt to avoid the problem described in BZ13302

- On x86 for a non-PIE executable an error is reported when an ifunc
  symbol in the executable is referenced by a shared lib. For S/390
  the ifunc symbol in the main executable is turned into a normal
  STT_FUNC symbol pointing to the PLT slot of the IFUNC symbol.


H.J.Lu, Alan: I would appreciate it very much if you could have a
closer look. I did a lot of testing but I'm not exactly an experienced
BFD hacker.

Glibc and Binutils testsuites are clean with that patch.

I'll post the respective Glibc patch today as well.

Bye,

-Andreas-


2012-07-02  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* elf64-s390.c: Include elf-s390-common.c.
	(R_390_IRELATIVE): New reloc.
	(elf_s390_reloc_type_lookup): Support R_390_IRELATIVE.
	(RELA_ENTRY_SIZE): New macro.
	(elf_s390_link_hash_entry): New fields ifunc_resolver_address and
	ifunc_resolver_section.
	(struct plt_entry): New struct.
	(struct elf_s390_obj_tdata): New field local_plt.
	(elf_s390_local_plt): New macro.
	(struct elf_s390_link_hash_table): New field irelifunc.
	(ELF64): New macro.
	(link_hash_newfunc): Initialize new fields.
	(elf_s390_check_relocs): Handle IFUNC symbols.
	(elf_s390_adjust_dynamic_symbol): Don't do anything for IFUNC
	symbols.
	(allocate_dynrelocs): Call s390_elf_allocate_ifunc_dyn_relocs for
	IFUNC symbols.
	(elf_s390_size_dynamic_sections): Handle IFUNC symbols.
	(elf_s390_relocate_section): Likewise.
	(elf_s390_finish_dynamic_symbol): Likewise.
	(elf_s390_finish_dynamic_sections): Handle local IFUNC symbols.
	(elf_s390_finish_ifunc_symbol): New function.
	* elf32-s390.c: See elf64-s390.c changes.
	* elf-s390-common.c: New file.
	* bfd-in2.h (BFD_RELOC_390_IRELATIVE): New enum field.
	* libbfd.h (BFD_RELOC_390_IRELATIVE): New entry for
	BFD_RELOC_390_IRELATIVE.
	* reloc.c (BFD_RELOC_390_IRELATIVE): Document new relocation.


2012-07-02  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* elf/s390.h (START_RELOC_NUMBERS): Define R_390_IRELATIVE reloc.


2012-07-02  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* emulparams/elf_s390.sh (IREL_IN_PLT): Define.
	* emulparams/elf64_s390.sh (IREL_IN_PLT): Define.


---
 bfd/bfd-in2.h               |    1 
 bfd/elf-s390-common.c       |  225 ++++++++++++
 bfd/elf32-s390.c            |  811 ++++++++++++++++++++++++++++++++++----------
 bfd/elf64-s390.c            |  652 +++++++++++++++++++++++++++++------
 bfd/libbfd.h                |    1 
 bfd/reloc.c                 |    5 
 include/elf/s390.h          |    1 
 ld/emulparams/elf64_s390.sh |    1 
 ld/emulparams/elf_s390.sh   |    1 
 9 files changed, 1418 insertions(+), 280 deletions(-)

Index: bfd/elf64-s390.c
===================================================================
--- bfd/elf64-s390.c.orig
+++ bfd/elf64-s390.c
@@ -219,6 +219,9 @@ 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),
+
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
@@ -346,6 +349,8 @@ elf_s390_reloc_type_lookup (abfd, code)
       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:
@@ -508,6 +513,8 @@ elf_s390_is_local_label_name (abfd, 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
@@ -610,17 +617,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;
 };
@@ -628,6 +661,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)
 
@@ -660,6 +696,7 @@ struct elf_s390_link_hash_table
   /* Short-cuts to get to dynamic linker sections.  */
   asection *sdynbss;
   asection *srelbss;
+  asection *irelifunc;
 
   union {
     bfd_signed_vma refcount;
@@ -676,6 +713,9 @@ struct elf_s390_link_hash_table
   (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.  */
 
 static struct bfd_hash_entry *
@@ -704,6 +744,8 @@ link_hash_newfunc (entry, table, string)
       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;
@@ -933,6 +975,7 @@ 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);
 
@@ -945,7 +988,31 @@ elf_s390_check_relocs (bfd *abfd,
 	}
 
       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 (!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];
@@ -983,18 +1050,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:
@@ -1010,6 +1070,25 @@ elf_s390_check_relocs (bfd *abfd,
 	    }
 	}
 
+      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;
+	      h->plt.refcount++;
+	    }
+	}
       switch (r_type)
 	{
 	case R_390_GOTOFF16:
@@ -1017,7 +1096,10 @@ elf_s390_check_relocs (bfd *abfd,
 	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_PLT16DBL:
@@ -1164,7 +1246,7 @@ elf_s390_check_relocs (bfd *abfd,
 	case R_390_PC32:
 	case R_390_PC32DBL:
 	case R_390_PC64:
-	  if (h != NULL && !info->shared)
+	  if (h != NULL)
 	    {
 	      /* If this reloc is in a read-only section, we might
 		 need a copy reloc.  We can't check reliably at this
@@ -1174,9 +1256,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 (!info->shared)
+		{
+		  /* 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
@@ -1249,7 +1334,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);
@@ -1522,6 +1606,10 @@ elf_s390_adjust_dynamic_symbol (struct b
   struct elf_s390_link_hash_table *htab;
   asection *s;
 
+  /* STT_GNU_IFUNC symbol must go through PLT. */
+  if (s390_is_ifunc_symbol_p (h))
+    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
      (although we could actually do it here).  */
@@ -1648,7 +1736,7 @@ allocate_dynrelocs (struct elf_link_hash
 {
   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)
@@ -1659,8 +1747,13 @@ allocate_dynrelocs (struct elf_link_hash
   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,
+					       &eh->dyn_relocs);
+  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.  */
@@ -1777,7 +1870,6 @@ allocate_dynrelocs (struct elf_link_hash
   else
     h->got.offset = (bfd_vma) -1;
 
-  eh = (struct elf_s390_link_hash_entry *) h;
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -1936,6 +2028,8 @@ elf_s390_size_dynamic_sections (bfd *out
       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;
@@ -1988,6 +2082,20 @@ elf_s390_size_dynamic_sections (bfd *out
 	  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)
@@ -2016,7 +2124,10 @@ elf_s390_size_dynamic_sections (bfd *out
       if (s == htab->elf.splt
 	  || s == htab->elf.sgot
 	  || s == htab->elf.sgotplt
-	  || s == htab->sdynbss)
+	  || s == htab->sdynbss
+	  || s == htab->elf.iplt
+	  || s == htab->elf.igotplt
+	  || s == htab->irelifunc)
 	{
 	  /* Strip this section if we don't need it; see the
 	     comment below.  */
@@ -2210,6 +2321,7 @@ elf_s390_relocate_section (bfd *output_b
       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
@@ -2232,7 +2344,55 @@ elf_s390_relocate_section (bfd *output_b
 	{
 	  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_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
 	{
@@ -2272,18 +2432,28 @@ elf_s390_relocate_section (bfd *output_b
 	    {
 	      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 4, 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.  */
@@ -2296,7 +2466,7 @@ elf_s390_relocate_section (bfd *output_b
 	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)
@@ -2305,11 +2475,29 @@ elf_s390_relocate_section (bfd *output_b
 
 	      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, info->shared, h)
+		       || (info->shared
+			   && 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
@@ -2328,7 +2516,7 @@ elf_s390_relocate_section (bfd *output_b
 		  else
 		    {
 		      bfd_put_64 (output_bfd, relocation,
-				  htab->elf.sgot->contents + off);
+				  base_got->contents + off);
 		      h->got.offset |= 1;
 		    }
 		}
@@ -2379,7 +2567,7 @@ elf_s390_relocate_section (bfd *output_b
 	  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
@@ -2387,7 +2575,7 @@ elf_s390_relocate_section (bfd *output_b
 	     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;
 
@@ -2425,17 +2613,21 @@ elf_s390_relocate_section (bfd *output_b
 	    break;
 
 	  if (h->plt.offset == (bfd_vma) -1
-	      || htab->elf.splt == NULL)
+	      || (htab->elf.splt == NULL && htab->elf.iplt == NULL))
 	    {
 	      /* 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;
 
@@ -2455,10 +2647,16 @@ elf_s390_relocate_section (bfd *output_b
 	      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;
 
@@ -2471,6 +2669,68 @@ elf_s390_relocate_section (bfd *output_b
 	case R_390_PC32:
 	case R_390_PC32DBL:
 	case R_390_PC64:
+
+	  if (h != NULL
+	      && s390_is_ifunc_symbol_p (h)
+	      && h->def_regular)
+	    {
+	      if (!info->shared || !h->non_got_ref)
+		{
+		  /* For a non-shared object STT_GNU_IFUNC symbol must
+		     go through PLT.  */
+		  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
+		      || info->executable)
+		    {
+		      /* 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;
 
@@ -2956,6 +3216,8 @@ elf_s390_relocate_section (bfd *output_b
 	   howto->name,
 	   h->root.root.string);
 
+    do_relocation:
+
       if (r_type == R_390_20
 	  || r_type == R_390_GOT20
 	  || r_type == R_390_GOTPLT20
@@ -3012,6 +3274,92 @@ elf_s390_relocate_section (bfd *output_b
   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
+      || ((info->executable
+	   || 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.  */
 
@@ -3022,6 +3370,7 @@ elf_s390_finish_dynamic_symbol (bfd *out
 				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)
@@ -3036,65 +3385,82 @@ elf_s390_finish_dynamic_symbol (bfd *out
 
       /* This symbol has an entry in the procedure linkage table.  Set
 	 it up.  */
+      if (s390_is_ifunc_symbol_p (h))
+	{
+	  /* If we can resolve the IFUNC symbol locally we generate an
+	     IRELATIVE reloc.  */
+	  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);
+				 ;
+	  /* Fallthrough.  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;
+
+	  /* 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);
 
-      /* 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);
+	  /* 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);
 
-      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 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;
+	    }
 	}
     }
 
@@ -3115,14 +3481,37 @@ elf_s390_finish_dynamic_symbol (bfd *out
 		       + 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
+      if (h->def_regular && s390_is_ifunc_symbol_p (h))
+	{
+	  if (info->shared)
+	    {
+	      /* 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 (info->shared
 	  && 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)
 	    return FALSE;
 	  BFD_ASSERT((h->got.offset & 1) != 0);
@@ -3134,6 +3523,7 @@ elf_s390_finish_dynamic_symbol (bfd *out
       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;
@@ -3205,6 +3595,8 @@ elf_s390_finish_dynamic_sections (bfd *o
   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)
@@ -3298,6 +3690,38 @@ elf_s390_finish_dynamic_sections (bfd *o
       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;
 }
 
Index: ld/emulparams/elf64_s390.sh
===================================================================
--- ld/emulparams/elf64_s390.sh.orig
+++ ld/emulparams/elf64_s390.sh
@@ -12,6 +12,7 @@ TEMPLATE_NAME=elf32
 GENERATE_SHLIB_SCRIPT=yes 
 GENERATE_PIE_SCRIPT=yes
 NO_SMALL_DATA=yes
+IREL_IN_PLT=
 
 # Treat a host that matches the target with the possible exception of "x"
 # in the name as if it were native.
Index: include/elf/s390.h
===================================================================
--- include/elf/s390.h.orig
+++ include/elf/s390.h
@@ -119,6 +119,7 @@ START_RELOC_NUMBERS (elf_s390_reloc_type
     RELOC_NUMBER (R_390_GOTPLT20, 59)	/* 20 bit offset to jump slot.  */
     RELOC_NUMBER (R_390_TLS_GOTIE20, 60)/* 20 bit GOT offset for statis TLS
 					   block offset.  */
+    RELOC_NUMBER (R_390_IRELATIVE, 61)  /* IFUNC relocation.  */
     /* These are GNU extensions to enable C++ vtable garbage collection.  */
     RELOC_NUMBER (R_390_GNU_VTINHERIT, 250)
     RELOC_NUMBER (R_390_GNU_VTENTRY, 251)
Index: bfd/bfd-in2.h
===================================================================
--- bfd/bfd-in2.h.orig
+++ bfd/bfd-in2.h
@@ -4291,6 +4291,7 @@ in .byte hlo8(symbol)  */
   BFD_RELOC_390_GOT20,
   BFD_RELOC_390_GOTPLT20,
   BFD_RELOC_390_TLS_GOTIE20,
+  BFD_RELOC_390_IRELATIVE,
 
 /* Score relocations
 Low 16 bit for load/store  */
Index: bfd/libbfd.h
===================================================================
--- bfd/libbfd.h.orig
+++ bfd/libbfd.h
@@ -1996,6 +1996,7 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_390_GOT20",
   "BFD_RELOC_390_GOTPLT20",
   "BFD_RELOC_390_TLS_GOTIE20",
+  "BFD_RELOC_390_IRELATIVE",
   "BFD_RELOC_SCORE_GPREL15",
   "BFD_RELOC_SCORE_DUMMY2",
   "BFD_RELOC_SCORE_JMP",
Index: bfd/reloc.c
===================================================================
--- bfd/reloc.c.orig
+++ bfd/reloc.c
@@ -4694,6 +4694,11 @@ ENUMDOC
   Long displacement extension.
 
 ENUM
+  BFD_RELOC_390_IRELATIVE
+ENUMDOC
+  STT_GNU_IFUNC relocation.
+
+ENUM
   BFD_RELOC_SCORE_GPREL15
 ENUMDOC
   Score relocations
Index: bfd/elf32-s390.c
===================================================================
--- bfd/elf32-s390.c.orig
+++ bfd/elf32-s390.c
@@ -211,6 +211,8 @@ 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, 2, 32, TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_IRELATIVE", FALSE, 0, 0xffffffff, FALSE),
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
@@ -324,6 +326,8 @@ elf_s390_reloc_type_lookup (abfd, code)
       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 &elf32_s390_vtinherit_howto;
     case BFD_RELOC_VTABLE_ENTRY:
@@ -485,6 +489,8 @@ elf_s390_is_local_label_name (abfd, name
 
 #define GOT_ENTRY_SIZE 4
 
+#define RELA_ENTRY_SIZE sizeof (Elf32_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
@@ -699,17 +705,43 @@ struct elf_s390_link_hash_entry
 #define GOT_TLS_IE	3
 #define GOT_TLS_IE_NLT	4
   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 elf64-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;
 };
@@ -717,6 +749,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)
 
@@ -749,6 +784,7 @@ struct elf_s390_link_hash_table
   /* Short-cuts to get to dynamic linker sections.  */
   asection *sdynbss;
   asection *srelbss;
+  asection *irelifunc;
 
   union {
     bfd_signed_vma refcount;
@@ -765,6 +801,9 @@ struct elf_s390_link_hash_table
   (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
   == S390_ELF_DATA ? ((struct elf_s390_link_hash_table *) ((p)->hash)) : NULL)
 
+#undef ELF64
+#include "elf-s390-common.c"
+
 /* Create an entry in an s390 ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -793,6 +832,8 @@ link_hash_newfunc (entry, table, string)
       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;
@@ -998,6 +1039,7 @@ elf_s390_check_relocs (abfd, info, sec, 
   asection *sreloc;
   bfd_signed_vma *local_got_refcounts;
   int tls_type, old_tls_type;
+  Elf_Internal_Sym *isym;
 
   if (info->relocatable)
     return TRUE;
@@ -1028,7 +1070,31 @@ elf_s390_check_relocs (abfd, info, sec, 
 	}
 
       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 (!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];
@@ -1064,17 +1130,9 @@ elf_s390_check_relocs (abfd, info, sec, 
 	  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:
@@ -1090,13 +1148,35 @@ elf_s390_check_relocs (abfd, info, sec, 
 	    }
 	}
 
+      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;
+	      h->plt.refcount++;
+	    }
+	}
       switch (r_type)
 	{
 	case R_390_GOTOFF16:
 	case R_390_GOTOFF32:
 	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_PLT16DBL:
@@ -1237,7 +1317,7 @@ elf_s390_check_relocs (abfd, info, sec, 
 	case R_390_PC16DBL:
 	case R_390_PC32DBL:
 	case R_390_PC32:
-	  if (h != NULL && !info->shared)
+	  if (h != NULL)
 	    {
 	      /* If this reloc is in a read-only section, we might
 		 need a copy reloc.  We can't check reliably at this
@@ -1247,9 +1327,12 @@ elf_s390_check_relocs (abfd, info, sec, 
 		 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 (!info->shared)
+		{
+		  /* 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
@@ -1321,7 +1404,6 @@ elf_s390_check_relocs (abfd, info, sec, 
 		     easily.  Oh well.  */
 		  asection *s;
 		  void *vpp;
-		  Elf_Internal_Sym *isym;
 
 		  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
 						abfd, r_symndx);
@@ -1583,6 +1665,10 @@ elf_s390_adjust_dynamic_symbol (info, h)
   struct elf_s390_link_hash_table *htab;
   asection *s;
 
+  /* STT_GNU_IFUNC symbol must go through PLT. */
+  if (s390_is_ifunc_symbol_p (h))
+    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
      (although we could actually do it here).  */
@@ -1708,7 +1794,7 @@ allocate_dynrelocs (h, inf)
 {
   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)
@@ -1717,8 +1803,13 @@ allocate_dynrelocs (h, inf)
   info = (struct bfd_link_info *) inf;
   htab = elf_s390_hash_table (info);
 
-  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,
+					       &eh->dyn_relocs);
+  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.  */
@@ -1835,7 +1926,6 @@ allocate_dynrelocs (h, inf)
   else
     h->got.offset = (bfd_vma) -1;
 
-  eh = (struct elf_s390_link_hash_entry *) h;
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -1993,6 +2083,8 @@ elf_s390_size_dynamic_sections (output_b
       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;
@@ -2045,6 +2137,19 @@ elf_s390_size_dynamic_sections (output_b
 	  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 += RELA_ENTRY_SIZE;
+	    }
+	  else
+            local_plt[i].plt.offset = (bfd_vma) -1;
+	}
     }
 
   if (htab->tls_ldm_got.refcount > 0)
@@ -2073,7 +2178,10 @@ elf_s390_size_dynamic_sections (output_b
       if (s == htab->elf.splt
 	  || s == htab->elf.sgot
 	  || s == htab->elf.sgotplt
-	  || s == htab->sdynbss)
+	  || s == htab->sdynbss
+	  || s == htab->elf.iplt
+	  || s == htab->elf.igotplt
+	  || s == htab->irelifunc)
 	{
 	  /* Strip this section if we don't need it; see the
 	     comment below.  */
@@ -2266,6 +2374,7 @@ elf_s390_relocate_section (output_bfd, i
       bfd_boolean unresolved_reloc;
       bfd_reloc_status_type r;
       int tls_type;
+      asection *base_got = htab->elf.sgot;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == (int) R_390_GNU_VTINHERIT
@@ -2288,7 +2397,52 @@ elf_s390_relocate_section (output_bfd, i
 	{
 	  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_GOTPLT12:
+		case R_390_GOTPLT16:
+		case R_390_GOTPLT20:
+		case R_390_GOTPLT32:
+		case R_390_GOTPLTENT:
+		case R_390_GOT12:
+		case R_390_GOT16:
+		case R_390_GOT20:
+		case R_390_GOT32:
+		case R_390_GOTENT:
+		  {
+		    /* Write the PLT slot address into the GOT slot.  */
+		    bfd_put_32 (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
 	{
@@ -2327,19 +2481,29 @@ elf_s390_relocate_section (output_bfd, i
 	    {
 	      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 4, 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.  */
 
@@ -2350,7 +2514,7 @@ elf_s390_relocate_section (output_bfd, i
 	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)
@@ -2359,11 +2523,30 @@ elf_s390_relocate_section (output_bfd, i
 
 	      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, info->shared, h)
+		       || (info->shared
+			   && 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
@@ -2382,7 +2565,7 @@ elf_s390_relocate_section (output_bfd, i
 		  else
 		    {
 		      bfd_put_32 (output_bfd, relocation,
-				  htab->elf.sgot->contents + off);
+				  base_got->contents + off);
 		      h->got.offset |= 1;
 		    }
 		}
@@ -2433,7 +2616,7 @@ elf_s390_relocate_section (output_bfd, i
 	  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
@@ -2441,7 +2624,7 @@ elf_s390_relocate_section (output_bfd, i
 	     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;
 
@@ -2477,7 +2660,7 @@ elf_s390_relocate_section (output_bfd, i
 	    break;
 
 	  if (h->plt.offset == (bfd_vma) -1
-	      || htab->elf.splt == NULL)
+	      || (htab->elf.splt == NULL && htab->elf.iplt == NULL))
 	    {
 	      /* We didn't make a PLT entry for this symbol.  This
 		 happens when statically linking PIC code, or when
@@ -2485,9 +2668,14 @@ elf_s390_relocate_section (output_bfd, i
 	      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;
 
@@ -2506,10 +2694,16 @@ elf_s390_relocate_section (output_bfd, i
 	      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;
 
@@ -2520,6 +2714,67 @@ elf_s390_relocate_section (output_bfd, i
 	case R_390_PC16DBL:
 	case R_390_PC32DBL:
 	case R_390_PC32:
+	  if (h != NULL
+	      && s390_is_ifunc_symbol_p (h)
+	      && h->def_regular)
+	    {
+	      if (!info->shared || !h->non_got_ref)
+		{
+		  /* For a non-shared object STT_GNU_IFUNC symbol must
+		     go through PLT.  */
+		  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
+		      || info->executable)
+		    {
+		      /* This symbol is resolved locally.  */
+		      outrel.r_info = ELF32_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 = ELF32_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;
 
@@ -3045,6 +3300,8 @@ elf_s390_relocate_section (output_bfd, i
 	   howto->name,
 	   h->root.root.string);
 
+    do_relocation:
+
       if (r_type == R_390_20
 	  || r_type == R_390_GOT20
 	  || r_type == R_390_GOTPLT20
@@ -3101,6 +3358,152 @@ elf_s390_relocate_section (output_bfd, i
   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 iplt_offset,
+			      bfd_vma resolver_address)
+{
+  bfd_vma iplt_index;
+  bfd_vma got_offset;
+  bfd_vma igotiplt_offset;
+  Elf_Internal_Rela rela;
+  bfd_byte *loc;
+  asection *plt, *gotplt, *relplt;
+  bfd_vma relative_offset;
+
+  if (htab->elf.iplt == NULL
+      || htab->elf.igotplt == NULL
+      || htab->elf.irelplt == NULL)
+    abort ();
+
+  gotplt = htab->elf.igotplt;
+  relplt = htab->elf.irelplt;
+
+  /* Index of the PLT slot within iplt section.  */
+  iplt_index = iplt_offset / PLT_ENTRY_SIZE;
+  plt = htab->elf.iplt;
+  /* Offset into the igot.plt section.  */
+  igotiplt_offset = iplt_index * GOT_ENTRY_SIZE;
+  /* Offset into the got section.  */
+  got_offset = igotiplt_offset + gotplt->output_offset;
+
+  /* S390 uses halfwords for relative branch calc!  */
+  relative_offset = - (plt->output_offset +
+		       (PLT_ENTRY_SIZE * iplt_index) + 18) / 2;
+/* If offset is > 32768, branch to a previous branch
+   390 can only handle +-64 K jumps.  */
+  if ( -32768 > (int) relative_offset )
+    relative_offset
+      = -(unsigned) (((65536 / PLT_ENTRY_SIZE - 1) * PLT_ENTRY_SIZE) / 2);
+
+  /* Fill in the entry in the procedure linkage table.  */
+  if (!info->shared)
+    {
+      memcpy (plt->contents + iplt_offset, elf_s390_plt_entry,
+	      PLT_ENTRY_SIZE);
+
+      /* Adjust jump to the first plt entry.  */
+      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+		  plt->contents + iplt_offset + 20);
+
+      /* Push the GOT offset field.  */
+      bfd_put_32 (output_bfd,
+		  (gotplt->output_section->vma
+		   + got_offset),
+		  plt->contents + iplt_offset + 24);
+    }
+  else if (got_offset < 4096)
+    {
+      /* The GOT offset is small enough to be used directly as
+	 displacement.  */
+      memcpy (plt->contents + iplt_offset,
+	      elf_s390_plt_pic12_entry,
+	      PLT_ENTRY_SIZE);
+
+      /* Put in the GOT offset as displacement value.  The 0xc000
+	 value comes from the first word of the plt entry.  Look
+	 at the elf_s390_plt_pic16_entry content.  */
+      bfd_put_16 (output_bfd, (bfd_vma)0xc000 | got_offset,
+		  plt->contents + iplt_offset + 2);
+
+      /* Adjust the jump to the first plt entry.  */
+      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+		  plt->contents + iplt_offset + 20);
+    }
+  else if (got_offset < 32768)
+    {
+      /* The GOT offset is too big for a displacement but small
+	 enough to be a signed 16 bit immediate value as it can be
+	 used in an lhi instruction.  */
+      memcpy (plt->contents + iplt_offset,
+	      elf_s390_plt_pic16_entry,
+	      PLT_ENTRY_SIZE);
+
+      /* Put in the GOT offset for the lhi instruction.  */
+      bfd_put_16 (output_bfd, (bfd_vma)got_offset,
+		  plt->contents + iplt_offset + 2);
+
+      /* Adjust the jump to the first plt entry.  */
+      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+		  plt->contents + iplt_offset + 20);
+    }
+  else
+    {
+      memcpy (plt->contents + iplt_offset,
+	      elf_s390_plt_pic_entry,
+	      PLT_ENTRY_SIZE);
+
+      /* Adjust the jump to the first plt entry.  */
+      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+		  plt->contents + iplt_offset + 20);
+
+      /* Push the GOT offset field.  */
+      bfd_put_32 (output_bfd, got_offset,
+		  plt->contents + iplt_offset + 24);
+    }
+  /* Insert offset into  reloc. table here.  */
+  bfd_put_32 (output_bfd, relplt->output_offset +
+	      iplt_index * RELA_ENTRY_SIZE,
+	      plt->contents + iplt_offset + 28);
+
+  /* Fill in the entry in the global offset table.
+     Points to instruction after GOT offset.  */
+  bfd_put_32 (output_bfd,
+	      (plt->output_section->vma
+	       + plt->output_offset
+	       + iplt_offset
+	       + 12),
+	      gotplt->contents + igotiplt_offset);
+
+  /* Fill in the entry in the .rela.plt section.  */
+  rela.r_offset = gotplt->output_section->vma + got_offset;
+
+  if (!h
+      || h->dynindx == -1
+      || ((info->executable
+	   || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+	  && h->def_regular))
+    {
+      /* The symbol can be locally resolved.  */
+      rela.r_info = ELF32_R_INFO (0, R_390_IRELATIVE);
+      rela.r_addend = resolver_address;
+    }
+  else
+    {
+      rela.r_info = ELF32_R_INFO (h->dynindx, R_390_JMP_SLOT);
+      rela.r_addend = 0;
+    }
+
+  loc = relplt->contents + iplt_index * RELA_ENTRY_SIZE;
+  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -3112,6 +3515,7 @@ elf_s390_finish_dynamic_symbol (output_b
      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);
 
@@ -3125,125 +3529,139 @@ elf_s390_finish_dynamic_symbol (output_b
 
       /* This symbol has an entry in the procedure linkage table.  Set
 	 it up.  */
-      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;
-
-      /* Offset in GOT is PLT index plus GOT headers(3) times 4,
-	 addr & GOT addr.  */
-      got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
-
-      /* S390 uses halfwords for relative branch calc!  */
-      relative_offset = - ((PLT_FIRST_ENTRY_SIZE +
-			    (PLT_ENTRY_SIZE * plt_index) + 18) / 2);
-      /* If offset is > 32768, branch to a previous branch
-	 390 can only handle +-64 K jumps.  */
-      if ( -32768 > (int) relative_offset )
-	relative_offset
-	  = -(unsigned) (((65536 / PLT_ENTRY_SIZE - 1) * PLT_ENTRY_SIZE) / 2);
-
-      /* Fill in the entry in the procedure linkage table.  */
-      if (!info->shared)
-	{
-	  memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390_plt_entry,
-		  PLT_ENTRY_SIZE);
-
-	  /* Adjust jump to the first plt entry.  */
-	  bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
-		      htab->elf.splt->contents + h->plt.offset + 20);
-
-	  /* Push the GOT offset field.  */
-	  bfd_put_32 (output_bfd,
-		      (htab->elf.sgotplt->output_section->vma
-		       + htab->elf.sgotplt->output_offset
-		       + got_offset),
-		      htab->elf.splt->contents + h->plt.offset + 24);
-	}
-      else if (got_offset < 4096)
-	{
-	  /* The GOT offset is small enough to be used directly as
-	     displacement.  */
-	  memcpy (htab->elf.splt->contents + h->plt.offset,
-		  elf_s390_plt_pic12_entry,
-		  PLT_ENTRY_SIZE);
-
-	  /* Put in the GOT offset as displacement value.  The 0xc000
-	     value comes from the first word of the plt entry.  Look
-	     at the elf_s390_plt_pic16_entry content.  */
-	  bfd_put_16 (output_bfd, (bfd_vma)0xc000 | got_offset,
-		      htab->elf.splt->contents + h->plt.offset + 2);
-
-	  /* Adjust the jump to the first plt entry.  */
-	  bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
-		      htab->elf.splt->contents + h->plt.offset + 20);
-	}
-      else if (got_offset < 32768)
-	{
-	  /* The GOT offset is too big for a displacement but small
-	     enough to be a signed 16 bit immediate value as it can be
-	     used in an lhi instruction.  */
-	  memcpy (htab->elf.splt->contents + h->plt.offset,
-		  elf_s390_plt_pic16_entry,
-		  PLT_ENTRY_SIZE);
-
-	  /* Put in the GOT offset for the lhi instruction.  */
-	  bfd_put_16 (output_bfd, (bfd_vma)got_offset,
-		      htab->elf.splt->contents + h->plt.offset + 2);
-
-	  /* Adjust the jump to the first plt entry.  */
-	  bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
-		      htab->elf.splt->contents + h->plt.offset + 20);
+      if (s390_is_ifunc_symbol_p (h))
+	{
+	  /* If we can resolve the IFUNC symbol locally we generate an
+	     IRELATIVE reloc.  */
+	  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);
+	  /* Fallthrough.  Handling of explicit GOT slots of IFUNC
+	     symbols is below.  */
 	}
       else
 	{
-	  memcpy (htab->elf.splt->contents + h->plt.offset,
-		  elf_s390_plt_pic_entry,
-		  PLT_ENTRY_SIZE);
-
-	  /* Adjust the jump to the first plt entry.  */
-	  bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
-		      htab->elf.splt->contents + h->plt.offset + 20);
-
-	  /* Push the GOT offset field.  */
-	  bfd_put_32 (output_bfd, got_offset,
-		      htab->elf.splt->contents + h->plt.offset + 24);
-	}
-      /* Insert offset into  reloc. table here.  */
-      bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_External_Rela),
-		  htab->elf.splt->contents + h->plt.offset + 28);
+	  if (h->dynindx == -1
+	      || htab->elf.splt == NULL
+	      || htab->elf.sgotplt == NULL
+	      || htab->elf.srelplt == NULL)
+	    abort ();
 
-      /* Fill in the entry in the global offset table.
-	 Points to instruction after GOT offset.  */
-      bfd_put_32 (output_bfd,
-		  (htab->elf.splt->output_section->vma
-		   + htab->elf.splt->output_offset
-		   + h->plt.offset
-		   + 12),
-		  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 = ELF32_R_INFO (h->dynindx, R_390_JMP_SLOT);
-      rela.r_addend = 0;
-      loc = htab->elf.srelplt->contents + plt_index * sizeof (Elf32_External_Rela);
-      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+	  /* 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.  */
+	  got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
+
+	  /* S390 uses halfwords for relative branch calc!  */
+	  relative_offset = - ((PLT_FIRST_ENTRY_SIZE +
+				(PLT_ENTRY_SIZE * plt_index) + 18) / 2);
+	  /* If offset is > 32768, branch to a previous branch
+	     390 can only handle +-64 K jumps.  */
+	  if ( -32768 > (int) relative_offset )
+	    relative_offset
+	      = -(unsigned) (((65536 / PLT_ENTRY_SIZE - 1) * PLT_ENTRY_SIZE) / 2);
 
-      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 entry in the procedure linkage table.  */
+	  if (!info->shared)
+	    {
+	      memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390_plt_entry,
+		      PLT_ENTRY_SIZE);
+
+	      /* Adjust jump to the first plt entry.  */
+	      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+			  htab->elf.splt->contents + h->plt.offset + 20);
+
+	      /* Push the GOT offset field.  */
+	      bfd_put_32 (output_bfd,
+			  (htab->elf.sgotplt->output_section->vma
+			   + htab->elf.sgotplt->output_offset
+			   + got_offset),
+			  htab->elf.splt->contents + h->plt.offset + 24);
+	    }
+	  else if (got_offset < 4096)
+	    {
+	      /* The GOT offset is small enough to be used directly as
+		 displacement.  */
+	      memcpy (htab->elf.splt->contents + h->plt.offset,
+		      elf_s390_plt_pic12_entry,
+		      PLT_ENTRY_SIZE);
+
+	      /* Put in the GOT offset as displacement value.  The 0xc000
+		 value comes from the first word of the plt entry.  Look
+		 at the elf_s390_plt_pic16_entry content.  */
+	      bfd_put_16 (output_bfd, (bfd_vma)0xc000 | got_offset,
+			  htab->elf.splt->contents + h->plt.offset + 2);
+
+	      /* Adjust the jump to the first plt entry.  */
+	      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+			  htab->elf.splt->contents + h->plt.offset + 20);
+	    }
+	  else if (got_offset < 32768)
+	    {
+	      /* The GOT offset is too big for a displacement but small
+		 enough to be a signed 16 bit immediate value as it can be
+		 used in an lhi instruction.  */
+	      memcpy (htab->elf.splt->contents + h->plt.offset,
+		      elf_s390_plt_pic16_entry,
+		      PLT_ENTRY_SIZE);
+
+	      /* Put in the GOT offset for the lhi instruction.  */
+	      bfd_put_16 (output_bfd, (bfd_vma)got_offset,
+			  htab->elf.splt->contents + h->plt.offset + 2);
+
+	      /* Adjust the jump to the first plt entry.  */
+	      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+			  htab->elf.splt->contents + h->plt.offset + 20);
+	    }
+	  else
+	    {
+	      memcpy (htab->elf.splt->contents + h->plt.offset,
+		      elf_s390_plt_pic_entry,
+		      PLT_ENTRY_SIZE);
+
+	      /* Adjust the jump to the first plt entry.  */
+	      bfd_put_32 (output_bfd, (bfd_vma) 0+(relative_offset << 16),
+			  htab->elf.splt->contents + h->plt.offset + 20);
+
+	      /* Push the GOT offset field.  */
+	      bfd_put_32 (output_bfd, got_offset,
+			  htab->elf.splt->contents + h->plt.offset + 24);
+	    }
+	  /* Insert offset into  reloc. table here.  */
+	  bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_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_32 (output_bfd,
+		      (htab->elf.splt->output_section->vma
+		       + htab->elf.splt->output_offset
+		       + h->plt.offset
+		       + 12),
+		      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 = ELF32_R_INFO (h->dynindx, R_390_JMP_SLOT);
+	  rela.r_addend = 0;
+	  loc = htab->elf.srelplt->contents + plt_index * sizeof (Elf32_External_Rela);
+	  bfd_elf32_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;
+	    }
 	}
     }
 
@@ -3270,9 +3688,37 @@ elf_s390_finish_dynamic_symbol (output_b
 	 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
+      if (h->def_regular && s390_is_ifunc_symbol_p (h))
+	{
+	  if (info->shared)
+	    {
+	      /* 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_32 (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 (info->shared
 	  && 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)
 	    return FALSE;
 	  BFD_ASSERT((h->got.offset & 1) != 0);
@@ -3284,6 +3730,7 @@ elf_s390_finish_dynamic_symbol (output_b
       else
 	{
 	  BFD_ASSERT((h->got.offset & 1) == 0);
+	do_glob_dat:
 	  bfd_put_32 (output_bfd, (bfd_vma) 0, htab->elf.sgot->contents + h->got.offset);
 	  rela.r_info = ELF32_R_INFO (h->dynindx, R_390_GLOB_DAT);
 	  rela.r_addend = 0;
@@ -3356,6 +3803,8 @@ elf_s390_finish_dynamic_sections (output
   struct elf_s390_link_hash_table *htab;
   bfd *dynobj;
   asection *sdyn;
+  bfd *ibfd;
+  unsigned int i;
 
   htab = elf_s390_hash_table (info);
   dynobj = htab->elf.dynobj;
@@ -3441,6 +3890,36 @@ elf_s390_finish_dynamic_sections (output
       elf_section_data (htab->elf.sgotplt->output_section)
 	->this_hdr.sh_entsize = 4;
     }
+  /* 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;
 }
 
Index: ld/emulparams/elf_s390.sh
===================================================================
--- ld/emulparams/elf_s390.sh.orig
+++ ld/emulparams/elf_s390.sh
@@ -11,3 +11,4 @@ TEMPLATE_NAME=elf32
 GENERATE_SHLIB_SCRIPT=yes 
 GENERATE_PIE_SCRIPT=yes 
 NO_SMALL_DATA=yes
+IREL_IN_PLT=
Index: bfd/elf-s390-common.c
===================================================================
--- /dev/null
+++ bfd/elf-s390-common.c
@@ -0,0 +1,225 @@
+/* IBM S/390-specific support for ELF 32 and 64 bit functions
+   Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+   2011, 2012 Free Software Foundation, Inc.
+   Contributed by Andreas Krebbel.
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+
+/* Return TRUE if H is an IFUNC symbol.  Simply checking for the
+   symbol type might not be enough since it might get changed to
+   STT_FUNC for pointer equality reasons.  */
+static inline bfd_boolean
+s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
+{
+  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
+  return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
+}
+
+/* Create sections needed by STT_GNU_IFUNC symbol.  */
+
+static bfd_boolean
+s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  flagword flags;
+  asection *s;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+
+  if (htab->iplt != NULL)
+    return TRUE;
+
+  flags = bed->dynamic_sec_flags;
+
+  if (info->shared)
+    {
+      s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
+				       flags | SEC_READONLY);
+      if (s == NULL
+	  || ! bfd_set_section_alignment (abfd, s,
+					  bed->s->log_file_align))
+	return FALSE;
+      htab->irelifunc = s;
+    }
+
+  /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
+  s = bfd_make_section_with_flags (abfd, ".iplt",
+				   flags | SEC_CODE | SEC_READONLY);
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+    return FALSE;
+  htab->iplt = s;
+
+  s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s,
+				      bed->s->log_file_align))
+    return FALSE;
+  htab->irelplt = s;
+
+  s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
+  if (s == NULL
+      || !bfd_set_section_alignment (abfd, s,
+				     bed->s->log_file_align))
+    return FALSE;
+  htab->igotplt = s;
+
+  return TRUE;
+}
+
+
+/* Allocate space in .plt, .got and associated reloc sections for
+   dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
+
+static bfd_boolean
+s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
+				    struct elf_link_hash_entry *h,
+				    struct elf_dyn_relocs **head)
+{
+  struct elf_dyn_relocs *p;
+  struct elf_link_hash_table *htab;
+  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
+
+  htab = elf_hash_table (info);
+  eh->ifunc_resolver_address = h->root.u.def.value;
+  eh->ifunc_resolver_section = h->root.u.def.section;
+
+  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
+  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
+    {
+      /* When building shared library, we need to handle the case
+         where it is marked with regular reference, but not non-GOT
+	 reference.  It may happen if we didn't see STT_GNU_IFUNC
+	 symbol at the time when checking relocations.  */
+      if (info->shared
+	  && !h->non_got_ref
+	  && h->ref_regular)
+	for (p = *head; p != NULL; p = p->next)
+	  if (p->count)
+	    {
+	      h->non_got_ref = 1;
+	      goto keep;
+	    }
+
+      h->got = htab->init_got_offset;
+      h->plt = htab->init_plt_offset;
+      *head = NULL;
+      return TRUE;
+    }
+
+  /* Return and discard space for dynamic relocations against it if
+     it is never referenced in a non-shared object.  */
+  if (!h->ref_regular)
+    {
+      if (h->plt.refcount > 0
+	  || h->got.refcount > 0)
+	abort ();
+      h->got = htab->init_got_offset;
+      h->plt = htab->init_plt_offset;
+      *head = NULL;
+      return TRUE;
+    }
+
+keep:
+  /* Without checking h->plt.refcount here we allocate a PLT slot.
+     When setting plt.refcount in check_relocs it might not have been
+     known that this will be an IFUNC symol.  */
+  h->plt.offset = htab->iplt->size;
+  h->needs_plt = 1;
+  htab->iplt->size += PLT_ENTRY_SIZE;
+  htab->igotplt->size += GOT_ENTRY_SIZE;
+  htab->irelplt->size += RELA_ENTRY_SIZE;
+  htab->irelplt->reloc_count++;
+
+  /* In order to make pointer equality work with IFUNC symbols defined
+     in a non-PIE executable and referenced in a shared lib, we turn
+     the symbol into a STT_FUNC symbol and make the symbol value to
+     point to the IPLT slot.  That way the referencing shared lib will
+     always get the PLT slot address when resolving the respective
+     R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
+  if (info->executable && !info->shared && h->def_regular && h->ref_dynamic)
+    {
+      h->root.u.def.section = htab->iplt;
+      h->root.u.def.value = h->plt.offset;
+      h->size = PLT_ENTRY_SIZE;
+      h->type = STT_FUNC;
+    }
+
+  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
+     there is a non-GOT reference in a shared object.  */
+  if (!info->shared || !h->non_got_ref)
+    *head = NULL;
+
+  /* Finally, allocate space.  */
+  p = *head;
+  if (p != NULL)
+    {
+      bfd_size_type count = 0;
+      do
+	{
+	  count += p->count;
+	  p = p->next;
+	}
+      while (p != NULL);
+      htab->irelifunc->size += count * RELA_ENTRY_SIZE;
+    }
+
+  /* Decide whether the got.iplt slot can be used.  This has to be
+     avoided if the values in the GOT slots could differ for pointer
+     equality reasons.  */
+  if (h->got.refcount <= 0
+      || (info->shared
+	  && (h->dynindx == -1 || h->forced_local))
+      || (info->executable && info->shared)
+      || htab->sgot == NULL)
+    {
+      /* Use .got.iplt.  */
+      h->got.offset = (bfd_vma) -1;
+    }
+  else
+    {
+      h->got.offset = htab->sgot->size;
+      htab->sgot->size += GOT_ENTRY_SIZE;
+      if (info->shared)
+	htab->srelgot->size += RELA_ENTRY_SIZE;
+    }
+
+  return TRUE;
+}
+
+static bfd_boolean
+elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
+{
+  bfd_size_type size;
+
+  size = symtab_hdr->sh_info;
+  size *= (sizeof (bfd_signed_vma)       /* local got */
+	   + sizeof (struct plt_entry)   /* local plt */
+	   + sizeof(char));              /* local tls type */
+  elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
+				    bfd_zalloc (abfd, size));
+  if (elf_local_got_refcounts (abfd) == NULL)
+    return FALSE;
+  elf_s390_local_plt (abfd)
+    = (struct plt_entry*)(elf_local_got_refcounts (abfd)
+			  + symtab_hdr->sh_info);
+  elf_s390_local_got_tls_type (abfd)
+    = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info);
+
+  return TRUE;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]