Blame SOURCES/binutils-aa64-ifunc.patch

881b8e
diff -ruP binutils-2.23.52.0.1-orig/bfd/configure binutils-2.23.52.0.1/bfd/configure
881b8e
--- binutils-2.23.52.0.1-orig/bfd/configure	2014-06-16 10:18:57.265495580 -0700
881b8e
+++ binutils-2.23.52.0.1/bfd/configure	2014-06-16 11:45:14.935495580 -0700
881b8e
@@ -15387,7 +15387,7 @@
881b8e
     bfd_elf32_xtensa_be_vec)	tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;;
881b8e
     bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_alpha_vec)	tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;;
881b8e
-    bfd_elf64_bigaarch64_vec)	tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
+    bfd_elf64_bigaarch64_vec)	tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_big_generic_vec) 	tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_bigmips_vec) 	tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
881b8e
     bfd_elf64_hppa_linux_vec)	tb="$tb elf64-hppa.lo elf64.lo $elf"; target_size=64 ;;
881b8e
@@ -15396,7 +15396,7 @@
881b8e
     bfd_elf64_ia64_hpux_big_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_ia64_little_vec)	tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_ia64_vms_vec)	tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;;
881b8e
-    bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
+    bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_littlemips_vec) 	tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
881b8e
     bfd_elf64_mmix_vec) 	tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;;
881b8e
diff -ruP binutils-2.23.52.0.1-orig/bfd/configure.in binutils-2.23.52.0.1/bfd/configure.in
881b8e
--- binutils-2.23.52.0.1-orig/bfd/configure.in	2014-06-16 10:18:56.445495580 -0700
881b8e
+++ binutils-2.23.52.0.1/bfd/configure.in	2014-06-16 10:50:29.815495580 -0700
881b8e
@@ -860,7 +860,7 @@
881b8e
     bfd_elf32_xtensa_be_vec)	tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;;
881b8e
     bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_alpha_vec)	tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;;
881b8e
-    bfd_elf64_bigaarch64_vec)	tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
+    bfd_elf64_bigaarch64_vec)	tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_big_generic_vec) 	tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_bigmips_vec) 	tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
881b8e
     bfd_elf64_hppa_linux_vec)	tb="$tb elf64-hppa.lo elf64.lo $elf"; target_size=64 ;;
881b8e
@@ -869,7 +869,7 @@
881b8e
     bfd_elf64_ia64_hpux_big_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_ia64_little_vec)	tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_ia64_vms_vec)	tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;;    
881b8e
-    bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;;
881b8e
+    bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
881b8e
     bfd_elf64_littlemips_vec) 	tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
881b8e
     bfd_elf64_mmix_vec) 	tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;;
881b8e
diff -ruP binutils-2.23.52.0.1-orig/bfd/elf64-aarch64.c binutils-2.23.52.0.1/bfd/elf64-aarch64.c
881b8e
--- binutils-2.23.52.0.1-orig/bfd/elf64-aarch64.c	2014-06-16 10:18:56.315495580 -0700
881b8e
+++ binutils-2.23.52.0.1/bfd/elf64-aarch64.c	2014-06-24 08:28:08.832196620 -0700
881b8e
@@ -142,6 +142,7 @@
881b8e
 #include "bfd_stdint.h"
881b8e
 #include "elf-bfd.h"
881b8e
 #include "bfdlink.h"
881b8e
+#include "objalloc.h"
881b8e
 #include "elf/aarch64.h"
881b8e
 
881b8e
 static bfd_reloc_status_type
881b8e
@@ -398,6 +399,19 @@
881b8e
 	 ALL_ONES,		/* dst_mask */
881b8e
 	 FALSE),		/* pcrel_offset */
881b8e
 
881b8e
+  HOWTO (R_AARCH64_IRELATIVE,	/* type */
881b8e
+	 0,			/* rightshift */
881b8e
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
881b8e
+	 64,			/* bitsize */
881b8e
+	 FALSE,			/* pc_relative */
881b8e
+	 0,			/* bitpos */
881b8e
+	 complain_overflow_bitfield,	/* complain_on_overflow */
881b8e
+	 bfd_elf_generic_reloc,	/* special_function */
881b8e
+	 "R_AARCH64_IRELATIVE",	/* name */
881b8e
+	 FALSE,			/* partial_inplace */
881b8e
+	 0,			/* src_mask */
881b8e
+	 ALL_ONES,		/* dst_mask */
881b8e
+	 FALSE),		/* pcrel_offset */
881b8e
 };
881b8e
 
881b8e
 /* Note: code such as elf64_aarch64_reloc_type_lookup expect to use e.g.
881b8e
@@ -1887,6 +1901,10 @@
881b8e
      loader via DT_TLSDESC_GOT.  The magic value (bfd_vma) -1
881b8e
      indicates an offset is not allocated.  */
881b8e
   bfd_vma dt_tlsdesc_got;
881b8e
+
881b8e
+  /* Used by local STT_GNU_IFUNC symbols.  */
881b8e
+  htab_t loc_hash_table;
881b8e
+  void * loc_hash_memory;
881b8e
 };
881b8e
 
881b8e
 
881b8e
@@ -1997,6 +2015,72 @@
881b8e
   return entry;
881b8e
 }
881b8e
 
881b8e
+/* Compute a hash of a local hash entry.  We use elf_link_hash_entry
881b8e
+  for local symbol so that we can handle local STT_GNU_IFUNC symbols
881b8e
+  as global symbol.  We reuse indx and dynstr_index for local symbol
881b8e
+  hash since they aren't used by global symbols in this backend.  */
881b8e
+
881b8e
+static hashval_t
881b8e
+elf64_aarch64_local_htab_hash (const void *ptr)
881b8e
+{
881b8e
+  struct elf_link_hash_entry *h
881b8e
+    = (struct elf_link_hash_entry *) ptr;
881b8e
+  return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index);
881b8e
+}
881b8e
+
881b8e
+/* Compare local hash entries.  */
881b8e
+
881b8e
+static int
881b8e
+elf64_aarch64_local_htab_eq (const void *ptr1, const void *ptr2)
881b8e
+{
881b8e
+  struct elf_link_hash_entry *h1
881b8e
+    = (struct elf_link_hash_entry *) ptr1;
881b8e
+  struct elf_link_hash_entry *h2
881b8e
+    = (struct elf_link_hash_entry *) ptr2;
881b8e
+
881b8e
+  return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index;
881b8e
+}
881b8e
+
881b8e
+/* Find and/or create a hash entry for local symbol.  */
881b8e
+
881b8e
+static struct elf_link_hash_entry *
881b8e
+elf64_aarch64_get_local_sym_hash (struct elf64_aarch64_link_hash_table *htab,
881b8e
+				  bfd *abfd, const Elf_Internal_Rela *rel,
881b8e
+				  bfd_boolean create)
881b8e
+{
881b8e
+  struct elf64_aarch64_link_hash_entry e, *ret;
881b8e
+  asection *asec = abfd->sections;
881b8e
+  hashval_t h = ELF_LOCAL_SYMBOL_HASH (asec->id,
881b8e
+				       ELF64_R_SYM (rel->r_info));
881b8e
+  void **slot;
881b8e
+
881b8e
+  e.root.indx = asec->id;
881b8e
+  e.root.dynstr_index = ELF64_R_SYM (rel->r_info);
881b8e
+  slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h,
881b8e
+				   create ? INSERT : NO_INSERT);
881b8e
+
881b8e
+  if (!slot)
881b8e
+    return NULL;
881b8e
+
881b8e
+  if (*slot)
881b8e
+    {
881b8e
+      ret = (struct elf64_aarch64_link_hash_entry *) *slot;
881b8e
+      return &ret->root;
881b8e
+    }
881b8e
+
881b8e
+  ret = (struct elf64_aarch64_link_hash_entry *)
881b8e
+	objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
881b8e
+			sizeof (struct elf64_aarch64_link_hash_entry));
881b8e
+  if (ret)
881b8e
+    {
881b8e
+      memset (ret, 0, sizeof (*ret));
881b8e
+      ret->root.indx = asec->id;
881b8e
+      ret->root.dynstr_index = ELF64_R_SYM (rel->r_info);
881b8e
+      ret->root.dynindx = -1;
881b8e
+      *slot = ret;
881b8e
+    }
881b8e
+  return &ret->root;
881b8e
+}
881b8e
 
881b8e
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
881b8e
 
881b8e
@@ -2117,6 +2201,17 @@
881b8e
       return NULL;
881b8e
     }
881b8e
 
881b8e
+  ret->loc_hash_table = htab_try_create (1024,
881b8e
+					 elf64_aarch64_local_htab_hash,
881b8e
+					 elf64_aarch64_local_htab_eq,
881b8e
+					 NULL);
881b8e
+  ret->loc_hash_memory = objalloc_create ();
881b8e
+  if (!ret->loc_hash_table || !ret->loc_hash_memory)
881b8e
+    {
881b8e
+      free (ret);
881b8e
+      return NULL;
881b8e
+    }
881b8e
+
881b8e
   return &ret->root.root;
881b8e
 }
881b8e
 
881b8e
@@ -2128,6 +2223,11 @@
881b8e
   struct elf64_aarch64_link_hash_table *ret
881b8e
     = (struct elf64_aarch64_link_hash_table *) hash;
881b8e
 
881b8e
+  if (ret->loc_hash_table)
881b8e
+    htab_delete (ret->loc_hash_table);
881b8e
+  if (ret->loc_hash_memory)
881b8e
+    objalloc_free ((struct objalloc *) ret->loc_hash_memory);
881b8e
+
881b8e
   bfd_hash_table_free (&ret->stub_hash_table);
881b8e
   _bfd_elf_link_hash_table_free (hash);
881b8e
 }
881b8e
@@ -3825,8 +3925,10 @@
881b8e
 				   struct elf_link_hash_entry *h,
881b8e
 				   bfd_boolean *unresolved_reloc_p,
881b8e
 				   bfd_boolean save_addend,
881b8e
-				   bfd_vma *saved_addend)
881b8e
+				   bfd_vma *saved_addend,
881b8e
+				   Elf_Internal_Sym *sym)
881b8e
 {
881b8e
+  Elf_Internal_Shdr *symtab_hdr;
881b8e
   unsigned int r_type = howto->type;
881b8e
   unsigned long r_symndx;
881b8e
   bfd_byte *hit_data = contents + rel->r_offset;
881b8e
@@ -3837,6 +3939,8 @@
881b8e
 
881b8e
   globals = elf64_aarch64_hash_table (info);
881b8e
 
881b8e
+  symtab_hdr = &elf_symtab_hdr (input_bfd);
881b8e
+
881b8e
   BFD_ASSERT (is_aarch64_elf (input_bfd));
881b8e
 
881b8e
   r_symndx = ELF64_R_SYM (rel->r_info);
881b8e
@@ -3858,6 +3962,180 @@
881b8e
 
881b8e
   weak_undef_p = (h ? h->root.type == bfd_link_hash_undefweak
881b8e
 		  : bfd_is_und_section (sym_sec));
881b8e
+
881b8e
+  /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
881b8e
+     it here if it is defined in a non-shared object.  */
881b8e
+  if (h != NULL
881b8e
+      && h->type == STT_GNU_IFUNC
881b8e
+      && h->def_regular)
881b8e
+    {
881b8e
+      asection *plt;
881b8e
+      const char *name;
881b8e
+      asection *base_got;
881b8e
+      bfd_vma off;
881b8e
+
881b8e
+      if ((input_section->flags & SEC_ALLOC) == 0
881b8e
+	  || h->plt.offset == (bfd_vma) -1)
881b8e
+	abort ();
881b8e
+
881b8e
+      /* STT_GNU_IFUNC symbol must go through PLT. */
881b8e
+      plt = globals->root.splt ? globals->root.splt : globals->root.iplt;
881b8e
+      value = (plt->output_section->vma + plt->output_offset + h->plt.offset);
881b8e
+
881b8e
+      switch (r_type)
881b8e
+	{
881b8e
+	default:
881b8e
+	  if (h->root.root.string)
881b8e
+	    name = h->root.root.string;
881b8e
+	  else
881b8e
+	    name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
881b8e
+				     NULL);
881b8e
+	  (*_bfd_error_handler)
881b8e
+	    (_("%B: relocation %s against STT_GNU_IFUNC "
881b8e
+	       "symbol `%s' isn't handled by %s"), input_bfd,
881b8e
+	     howto->name, name, __FUNCTION__);
881b8e
+	  bfd_set_error (bfd_error_bad_value);
881b8e
+	  return FALSE;
881b8e
+
881b8e
+	case R_AARCH64_ABS64:
881b8e
+	  if (rel->r_addend != 0)
881b8e
+	    {
881b8e
+	      if (h->root.root.string)
881b8e
+		name = h->root.root.string;
881b8e
+	      else
881b8e
+		name = bfd_elf_sym_name (input_bfd, symtab_hdr,
881b8e
+					 sym, NULL);
881b8e
+	      (*_bfd_error_handler)
881b8e
+		(_("%B: relocation %s against STT_GNU_IFUNC "
881b8e
+		   "symbol `%s' has non-zero addend: %d"),
881b8e
+		 input_bfd, howto->name, name, rel->r_addend);
881b8e
+	      bfd_set_error (bfd_error_bad_value);
881b8e
+	      return FALSE;
881b8e
+	    }
881b8e
+
881b8e
+	  /* Generate dynamic relocation only when there is a
881b8e
+	     non-GOT reference in a shared object.  */
881b8e
+	  if (info->shared && h->non_got_ref)
881b8e
+	    {
881b8e
+	      Elf_Internal_Rela outrel;
881b8e
+	      asection *sreloc;
881b8e
+
881b8e
+	      /* Need a dynamic relocation to get the real function
881b8e
+		 address.  */
881b8e
+	      outrel.r_offset = _bfd_elf_section_offset (output_bfd,
881b8e
+							 info,
881b8e
+							 input_section,
881b8e
+							 rel->r_offset);
881b8e
+	      if (outrel.r_offset == (bfd_vma) -1
881b8e
+		  || outrel.r_offset == (bfd_vma) -2)
881b8e
+		abort ();
881b8e
+
881b8e
+	      outrel.r_offset += (input_section->output_section->vma
881b8e
+				  + input_section->output_offset);
881b8e
+
881b8e
+	      if (h->dynindx == -1
881b8e
+		  || h->forced_local
881b8e
+		  || info->executable)
881b8e
+		{
881b8e
+		  /* This symbol is resolved locally.  */
881b8e
+		  outrel.r_info = ELF64_R_INFO (0, R_AARCH64_IRELATIVE);
881b8e
+		  outrel.r_addend = (h->root.u.def.value
881b8e
+				     + h->root.u.def.section->output_section->vma
881b8e
+				     + h->root.u.def.section->output_offset);
881b8e
+		}
881b8e
+	      else
881b8e
+		{
881b8e
+		  outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
881b8e
+		  outrel.r_addend = 0;
881b8e
+		}
881b8e
+
881b8e
+	      sreloc = globals->root.irelifunc;
881b8e
+	      elf_append_rela (output_bfd, sreloc, &outrel);
881b8e
+
881b8e
+	      /* If this reloc is against an external symbol, we
881b8e
+		 do not want to fiddle with the addend.  Otherwise,
881b8e
+		 we need to include the symbol value so that it
881b8e
+		 becomes an addend for the dynamic reloc.  For an
881b8e
+		 internal symbol, we have updated addend.  */
881b8e
+	      return bfd_reloc_ok;
881b8e
+	    }
881b8e
+	  /* FALLTHROUGH */
881b8e
+	case R_AARCH64_JUMP26:
881b8e
+	case R_AARCH64_CALL26:
881b8e
+	  value = aarch64_resolve_relocation (r_type, place, value,
881b8e
+					      signed_addend,
881b8e
+					      weak_undef_p);
881b8e
+	  return bfd_elf_aarch64_put_addend (input_bfd, hit_data,
881b8e
+					     howto, value);
881b8e
+	case R_AARCH64_LD64_GOT_LO12_NC:
881b8e
+	case R_AARCH64_ADR_GOT_PAGE:
881b8e
+	case R_AARCH64_GOT_LD_PREL19:
881b8e
+	  base_got = globals->root.sgot;
881b8e
+	  off = h->got.offset;
881b8e
+
881b8e
+	  if (base_got == NULL)
881b8e
+	    abort ();
881b8e
+
881b8e
+	  if (off == (bfd_vma) -1)
881b8e
+	    {
881b8e
+	      bfd_vma plt_index;
881b8e
+
881b8e
+	      /* We can't use h->got.offset to save state, or
881b8e
+		 even just remember the offset, as finish_dynamic_symbol
881b8e
+		 would use that as offset into .got.  */
881b8e
+
881b8e
+	      if (globals->root.splt != NULL)
881b8e
+		{
881b8e
+		  plt_index = h->plt.offset / globals->plt_entry_size - 1;
881b8e
+		  off = (plt_index + 3) * GOT_ENTRY_SIZE;
881b8e
+		  base_got = globals->root.sgotplt;
881b8e
+		}
881b8e
+	      else
881b8e
+		{
881b8e
+		  plt_index = h->plt.offset / globals->plt_entry_size;
881b8e
+		  off = plt_index * GOT_ENTRY_SIZE;
881b8e
+		  base_got = globals->root.igotplt;
881b8e
+		}
881b8e
+
881b8e
+	      if (h->dynindx == -1
881b8e
+		  || h->forced_local
881b8e
+		  || info->symbolic)
881b8e
+		{
881b8e
+		  /* This references the local definition.  We must
881b8e
+		     initialize this entry in the global offset table.
881b8e
+		     Since the offset must always be a multiple of 8,
881b8e
+		     we use the least significant bit to record
881b8e
+		     whether we have initialized it already.
881b8e
+
881b8e
+		     When doing a dynamic link, we create a .rela.got
881b8e
+		     relocation entry to initialize the value.  This
881b8e
+		     is done in the finish_dynamic_symbol routine.  */
881b8e
+		  if ((off & 1) != 0)
881b8e
+		    off &= ~1;
881b8e
+		  else
881b8e
+		    {
881b8e
+		      bfd_put_64 (output_bfd, value,
881b8e
+				  base_got->contents + off);
881b8e
+		      /* Note that this is harmless as -1 | 1 is still -1.  */
881b8e
+		      h->got.offset |= 1;
881b8e
+		    }
881b8e
+		}
881b8e
+	      value = (base_got->output_section->vma
881b8e
+		       + base_got->output_offset + off);
881b8e
+	    }
881b8e
+	  else
881b8e
+	    value = aarch64_calculate_got_entry_vma (h, globals, info,
881b8e
+						     value, output_bfd,
881b8e
+						     unresolved_reloc_p);
881b8e
+	  value = aarch64_resolve_relocation (r_type, place, value,
881b8e
+					      0, weak_undef_p);
881b8e
+	  return bfd_elf_aarch64_put_addend (input_bfd, hit_data, howto, value);
881b8e
+	case R_AARCH64_ADR_PREL_PG_HI21:
881b8e
+	case R_AARCH64_ADD_ABS_LO12_NC:
881b8e
+	  break;
881b8e
+	}
881b8e
+    }
881b8e
+
881b8e
   switch (r_type)
881b8e
     {
881b8e
     case R_AARCH64_NONE:
881b8e
@@ -3884,11 +4162,6 @@
881b8e
 
881b8e
 	  *unresolved_reloc_p = FALSE;
881b8e
 
881b8e
-	  sreloc = _bfd_elf_get_dynamic_reloc_section (input_bfd,
881b8e
-						       input_section, 1);
881b8e
-	  if (sreloc == NULL)
881b8e
-	    return bfd_reloc_notsupported;
881b8e
-
881b8e
 	  skip = FALSE;
881b8e
 	  relocate = FALSE;
881b8e
 
881b8e
@@ -3925,10 +4198,14 @@
881b8e
 	      outrel.r_addend += value;
881b8e
 	    }
881b8e
 
881b8e
-	  loc = sreloc->contents + sreloc->reloc_count++ * RELOC_SIZE (htab);
881b8e
+	  sreloc = elf_section_data (input_section)->sreloc;
881b8e
+	  if (sreloc == NULL || sreloc->contents == NULL)
881b8e
+	    return bfd_reloc_notsupported;
881b8e
+
881b8e
+	  loc = sreloc->contents + sreloc->reloc_count++ * RELOC_SIZE (globals);
881b8e
 	  bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
881b8e
 
881b8e
-	  if (sreloc->reloc_count * RELOC_SIZE (htab) > sreloc->size)
881b8e
+	  if (sreloc->reloc_count * RELOC_SIZE (globals) > sreloc->size)
881b8e
 	    {
881b8e
 	      /* Sanity to check that we have previously allocated
881b8e
 		 sufficient space in the relocation section for the
881b8e
@@ -4337,6 +4614,20 @@
881b8e
 	    }
881b8e
 
881b8e
 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
881b8e
+
881b8e
+	  /* Relocate against local STT_GNU_IFUNC symbol.  */
881b8e
+	  if (!info->relocatable
881b8e
+	      && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
881b8e
+	    {
881b8e
+	      h = elf64_aarch64_get_local_sym_hash (globals, input_bfd,
881b8e
+						    rel, FALSE);
881b8e
+	      if (h == NULL)
881b8e
+		abort ();
881b8e
+
881b8e
+	      /* Set STT_GNU_IFUNC symbol value.  */
881b8e
+	      h->root.u.def.value = sym->st_value;
881b8e
+	      h->root.u.def.section = sec;
881b8e
+	    }
881b8e
 	}
881b8e
       else
881b8e
 	{
881b8e
@@ -4426,7 +4717,7 @@
881b8e
 					       input_section, contents, rel,
881b8e
 					       relocation, info, sec,
881b8e
 					       h, &unresolved_reloc,
881b8e
-					       save_addend, &addend);
881b8e
+					       save_addend, &addend, sym);
881b8e
 
881b8e
       switch (r_type)
881b8e
 	{
881b8e
@@ -4907,25 +5198,11 @@
881b8e
 
881b8e
       if (r_symndx >= symtab_hdr->sh_info)
881b8e
 	{
881b8e
-	  struct elf64_aarch64_link_hash_entry *eh;
881b8e
-	  struct elf_dyn_relocs **pp;
881b8e
-	  struct elf_dyn_relocs *p;
881b8e
 
881b8e
 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
881b8e
 	  while (h->root.type == bfd_link_hash_indirect
881b8e
 		 || h->root.type == bfd_link_hash_warning)
881b8e
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
881b8e
-	  eh = (struct elf64_aarch64_link_hash_entry *) h;
881b8e
-
881b8e
-	  for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
881b8e
-	    {
881b8e
-	      if (p->sec == sec)
881b8e
-		{
881b8e
-		  /* Everything must go for SEC.  */
881b8e
-		  *pp = p->next;
881b8e
-		  break;
881b8e
-		}
881b8e
-	    }
881b8e
         }
881b8e
       else
881b8e
 	{
881b8e
@@ -4934,8 +5211,32 @@
881b8e
 	  /* A local symbol.  */
881b8e
 	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
881b8e
 					abfd, r_symndx);
881b8e
-	  if (isym == NULL)
881b8e
-	    return FALSE;
881b8e
+
881b8e
+	  /* Check relocation against local STT_GNU_IFUNC symbol.  */
881b8e
+	  if (isym != NULL
881b8e
+	      && ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
881b8e
+	    {
881b8e
+	      h = elf64_aarch64_get_local_sym_hash (htab, abfd, rel, FALSE);
881b8e
+	      if (h == NULL)
881b8e
+		abort ();
881b8e
+	    }
881b8e
+	}
881b8e
+
881b8e
+      if (h)
881b8e
+	{
881b8e
+	  struct elf64_aarch64_link_hash_entry *eh;
881b8e
+	  struct elf_dyn_relocs **pp;
881b8e
+	  struct elf_dyn_relocs *p;
881b8e
+
881b8e
+	  eh = (struct elf64_aarch64_link_hash_entry *) h;
881b8e
+
881b8e
+	  for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
881b8e
+	    if (p->sec == sec)
881b8e
+	      {
881b8e
+		/* Everything must go for SEC.  */
881b8e
+		*pp = p->next;
881b8e
+		break;
881b8e
+	      }
881b8e
 	}
881b8e
 
881b8e
       r_type = ELF64_R_TYPE (rel->r_info);
881b8e
@@ -4964,6 +5265,12 @@
881b8e
 	    {
881b8e
 	      if (h->got.refcount > 0)
881b8e
 		h->got.refcount -= 1;
881b8e
+
881b8e
+	      if (h->type == STT_GNU_IFUNC)
881b8e
+		{
881b8e
+		  if (h->plt.refcount > 0)
881b8e
+		    h->plt.refcount -= 1;
881b8e
+		}
881b8e
 	    }
881b8e
 	  else if (local_got_refcounts != NULL)
881b8e
 	    {
881b8e
@@ -5025,12 +5332,13 @@
881b8e
   /* If this is a function, put it in the procedure linkage table.  We
881b8e
      will fill in the contents of the procedure linkage table later,
881b8e
      when we know the address of the .got section.  */
881b8e
-  if (h->type == STT_FUNC || h->needs_plt)
881b8e
+  if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt)
881b8e
     {
881b8e
       if (h->plt.refcount <= 0
881b8e
-	  || SYMBOL_CALLS_LOCAL (info, h)
881b8e
-	  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
881b8e
-	      && h->root.type == bfd_link_hash_undefweak))
881b8e
+	  || (h->type != STT_GNU_IFUNC
881b8e
+	      && (SYMBOL_CALLS_LOCAL (info, h)
881b8e
+		  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
881b8e
+		      && h->root.type == bfd_link_hash_undefweak))))
881b8e
 	{
881b8e
 	  /* This case can occur if we saw a CALL26 reloc in
881b8e
 	     an input file, but the symbol wasn't referred to
881b8e
@@ -5226,6 +5534,7 @@
881b8e
       struct elf_link_hash_entry *h;
881b8e
       unsigned long r_symndx;
881b8e
       unsigned int r_type;
881b8e
+      Elf_Internal_Sym *isym;
881b8e
 
881b8e
       r_symndx = ELF64_R_SYM (rel->r_info);
881b8e
       r_type = ELF64_R_TYPE (rel->r_info);
881b8e
@@ -5249,7 +5558,31 @@
881b8e
 	}
881b8e
 
881b8e
       if (nsyms == 0 || r_symndx < symtab_hdr->sh_info)
881b8e
-	h = NULL;
881b8e
+	{
881b8e
+	  /* A local symbol.  */
881b8e
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
881b8e
+					abfd, r_symndx);
881b8e
+	  if (isym == NULL)
881b8e
+	    return FALSE;
881b8e
+
881b8e
+	  /* Check relocation against local STT_GNU_IFUNC symbol.  */
881b8e
+	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
881b8e
+	    {
881b8e
+	      h = elf64_aarch64_get_local_sym_hash (htab, abfd, rel,
881b8e
+						    TRUE);
881b8e
+	      if (h == NULL)
881b8e
+		return FALSE;
881b8e
+
881b8e
+	      /* Fake a STT_GNU_IFUNC symbol.  */
881b8e
+	      h->type = STT_GNU_IFUNC;
881b8e
+	      h->def_regular = 1;
881b8e
+	      h->ref_regular = 1;
881b8e
+	      h->forced_local = 1;
881b8e
+	      h->root.type = bfd_link_hash_defined;
881b8e
+	    }
881b8e
+	  else
881b8e
+	    h = NULL;
881b8e
+	}
881b8e
       else
881b8e
 	{
881b8e
 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
881b8e
@@ -5261,6 +5594,37 @@
881b8e
       /* Could be done earlier, if h were already available.  */
881b8e
       r_type = aarch64_tls_transition (abfd, info, r_type, h, r_symndx);
881b8e
 
881b8e
+      if (h != NULL)
881b8e
+	{
881b8e
+	  /* Create the ifunc sections for static executables.  If we
881b8e
+	     never see an indirect function symbol nor we are building
881b8e
+	     a static executable, those sections will be empty and
881b8e
+	     won't appear in output.  */
881b8e
+	  switch (r_type)
881b8e
+	    {
881b8e
+	    default:
881b8e
+	      break;
881b8e
+
881b8e
+	    case R_AARCH64_ABS64:
881b8e
+	    case R_AARCH64_CALL26:
881b8e
+	    case R_AARCH64_JUMP26:
881b8e
+	    case R_AARCH64_LD64_GOT_LO12_NC:
881b8e
+	    case R_AARCH64_ADR_GOT_PAGE:
881b8e
+	    case R_AARCH64_GOT_LD_PREL19:
881b8e
+	    case R_AARCH64_ADR_PREL_PG_HI21:
881b8e
+	    case R_AARCH64_ADD_ABS_LO12_NC:
881b8e
+	      if (htab->root.dynobj == NULL)
881b8e
+		htab->root.dynobj = abfd;
881b8e
+	      if (!_bfd_elf_create_ifunc_sections (htab->root.dynobj, info))
881b8e
+		return FALSE;
881b8e
+	      break;
881b8e
+	    }
881b8e
+
881b8e
+	  /* It is referenced by a non-shared object.  */
881b8e
+	  h->ref_regular = 1;
881b8e
+	  h->root.non_ir_ref = 1;
881b8e
+	}
881b8e
+
881b8e
       switch (r_type)
881b8e
 	{
881b8e
 	case R_AARCH64_ABS64:
881b8e
@@ -5319,7 +5683,6 @@
881b8e
 
881b8e
 		asection *s;
881b8e
 		void **vpp;
881b8e
-		Elf_Internal_Sym *isym;
881b8e
 
881b8e
 		isym = bfd_sym_from_r_symndx (&htab->sym_cache,
881b8e
 					      abfd, r_symndx);
881b8e
@@ -5469,7 +5832,10 @@
881b8e
 	    continue;
881b8e
 
881b8e
 	  h->needs_plt = 1;
881b8e
-	  h->plt.refcount += 1;
881b8e
+	  if (h->plt.refcount <= 0)
881b8e
+	    h->plt.refcount = 1;
881b8e
+	  else
881b8e
+	    h->plt.refcount += 1;
881b8e
 	  break;
881b8e
 	}
881b8e
     }
881b8e
@@ -5619,8 +5985,9 @@
881b8e
   Elf_Internal_Ehdr *i_ehdrp;	/* ELF file header, internal form.  */
881b8e
 
881b8e
   i_ehdrp = elf_elfheader (abfd);
881b8e
-  i_ehdrp->e_ident[EI_OSABI] = 0;
881b8e
   i_ehdrp->e_ident[EI_ABIVERSION] = AARCH64_ELF_ABI_VERSION;
881b8e
+
881b8e
+  _bfd_elf_set_osabi (abfd, link_info);
881b8e
 }
881b8e
 
881b8e
 static enum elf_reloc_type_class
881b8e
@@ -6010,12 +6377,6 @@
881b8e
   return _bfd_free_cached_info (abfd);
881b8e
 }
881b8e
 
881b8e
-static bfd_boolean
881b8e
-elf64_aarch64_is_function_type (unsigned int type)
881b8e
-{
881b8e
-  return type == STT_FUNC;
881b8e
-}
881b8e
-
881b8e
 /* Create dynamic sections. This is different from the ARM backend in that
881b8e
    the got, plt, gotplt and their relocation sections are all created in the
881b8e
    standard part of the bfd elf backend.  */
881b8e
@@ -6075,7 +6436,12 @@
881b8e
   info = (struct bfd_link_info *) inf;
881b8e
   htab = elf64_aarch64_hash_table (info);
881b8e
 
881b8e
-  if (htab->root.dynamic_sections_created && h->plt.refcount > 0)
881b8e
+  /* Since STT_GNU_IFUNC symbol must go through the PLT, we handle it
881b8e
+     here if it is defined and referenced in a non-shared object.  */
881b8e
+  if (h->type == STT_GNU_IFUNC
881b8e
+      && h->def_regular)
881b8e
+    return TRUE;
881b8e
+  else if (htab->root.dynamic_sections_created && h->plt.refcount > 0)
881b8e
     {
881b8e
       /* Make sure this symbol is output as a dynamic symbol.
881b8e
          Undefined weak syms won't yet be marked as dynamic.  */
881b8e
@@ -6330,8 +6696,258 @@
881b8e
   return TRUE;
881b8e
 }
881b8e
 
881b8e
+/* Allocate space in .plt, .got and associated reloc sections for
881b8e
+   dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
881b8e
+
881b8e
+static bfd_boolean
881b8e
+_bfd_elf_aarch64_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
881b8e
+					    struct elf_link_hash_entry *h,
881b8e
+					    struct elf_dyn_relocs **head,
881b8e
+					    unsigned int plt_entry_size,
881b8e
+					    unsigned int plt_header_size,
881b8e
+					    unsigned int got_entry_size)
881b8e
+{
881b8e
+  asection *plt, *gotplt, *relplt;
881b8e
+  struct elf_dyn_relocs *p;
881b8e
+  unsigned int sizeof_reloc;
881b8e
+  const struct elf_backend_data *bed;
881b8e
+  struct elf_link_hash_table *htab;
881b8e
+
881b8e
+  /* When a shared library references a STT_GNU_IFUNC symbol defined
881b8e
+     in executable, the address of the resolved function may be used.
881b8e
+     But in non-shared executable, the address of its .plt slot may
881b8e
+     be used.  Pointer equality may not work correctly.  PIE should
881b8e
+     be used if pointer equality is required here.  */
881b8e
+  if (!info->shared
881b8e
+      && (h->dynindx != -1
881b8e
+	  || info->export_dynamic)
881b8e
+      && h->pointer_equality_needed)
881b8e
+    {
881b8e
+      info->callbacks->einfo
881b8e
+	(_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
881b8e
+	   "equality in `%B' can not be used when making an "
881b8e
+	   "executable; recompile with -fPIE and relink with -pie\n"),
881b8e
+	 h->root.root.string,
881b8e
+	 h->root.u.def.section->owner);
881b8e
+      bfd_set_error (bfd_error_bad_value);
881b8e
+      return FALSE;
881b8e
+    }
881b8e
+
881b8e
+  htab = elf_hash_table (info);
881b8e
+
881b8e
+  /* When building a shared library, we need to handle the case where it is
881b8e
+     marked with a regular reference, but not a non-GOT reference since the
881b8e
+     non-GOT reference bit may not be set here.  */
881b8e
+  if (info->shared && !h->non_got_ref && h->ref_regular)
881b8e
+    for (p = *head; p != NULL; p = p->next)
881b8e
+      if (p->count)
881b8e
+	{
881b8e
+	  h->non_got_ref = 1;
881b8e
+	  goto keep;
881b8e
+	}
881b8e
+
881b8e
+  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
881b8e
+  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
881b8e
+    {
881b8e
+      h->got = htab->init_got_offset;
881b8e
+      h->plt = htab->init_plt_offset;
881b8e
+      *head = NULL;
881b8e
+      return TRUE;
881b8e
+    }
881b8e
+
881b8e
+  /* Return and discard space for dynamic relocations against it if
881b8e
+     it is never referenced in a non-shared object.  */
881b8e
+  if (!h->ref_regular)
881b8e
+    {
881b8e
+      if (h->plt.refcount > 0
881b8e
+	  || h->got.refcount > 0)
881b8e
+	abort ();
881b8e
+      h->got = htab->init_got_offset;
881b8e
+      h->plt = htab->init_plt_offset;
881b8e
+      *head = NULL;
881b8e
+      return TRUE;
881b8e
+    }
881b8e
+
881b8e
+keep:
881b8e
+  bed = get_elf_backend_data (info->output_bfd);
881b8e
+  if (bed->rela_plts_and_copies_p)
881b8e
+    sizeof_reloc = bed->s->sizeof_rela;
881b8e
+  else
881b8e
+    sizeof_reloc = bed->s->sizeof_rel;
881b8e
+
881b8e
+  /* When building a static executable, use .iplt, .igot.plt and
881b8e
+     .rel[a].iplt sections for STT_GNU_IFUNC symbols.  */
881b8e
+  if (htab->splt != NULL)
881b8e
+    {
881b8e
+      plt = htab->splt;
881b8e
+      gotplt = htab->sgotplt;
881b8e
+      relplt = htab->srelplt;
881b8e
+
881b8e
+      /* If this is the first .plt entry, make room for the special
881b8e
+	 first entry.  */
881b8e
+      if (plt->size == 0)
881b8e
+	plt->size += plt_header_size;
881b8e
+    }
881b8e
+  else
881b8e
+    {
881b8e
+      plt = htab->iplt;
881b8e
+      gotplt = htab->igotplt;
881b8e
+      relplt = htab->irelplt;
881b8e
+    }
881b8e
+
881b8e
+  /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
881b8e
+     the original value for R_*_IRELATIVE.  */
881b8e
+  h->plt.offset = plt->size;
881b8e
+
881b8e
+  /* Make room for this entry in the .plt/.iplt section.  */
881b8e
+  plt->size += plt_entry_size;
881b8e
+
881b8e
+  /* We also need to make an entry in the .got.plt/.got.iplt section,
881b8e
+     which will be placed in the .got section by the linker script.  */
881b8e
+  gotplt->size += got_entry_size;
881b8e
+
881b8e
+  /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
881b8e
+     section.  */
881b8e
+  relplt->size += sizeof_reloc;
881b8e
+  relplt->reloc_count++;
881b8e
+
881b8e
+  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
881b8e
+     there is a non-GOT reference in a shared object.  */
881b8e
+  if (!info->shared
881b8e
+      || !h->non_got_ref)
881b8e
+    *head = NULL;
881b8e
+
881b8e
+  /* Finally, allocate space.  */
881b8e
+  p = *head;
881b8e
+  if (p != NULL)
881b8e
+    {
881b8e
+      bfd_size_type count = 0;
881b8e
+      do
881b8e
+	{
881b8e
+	  count += p->count;
881b8e
+	  p = p->next;
881b8e
+	}
881b8e
+      while (p != NULL);
881b8e
+      htab->irelifunc->size += count * sizeof_reloc;
881b8e
+    }
881b8e
+
881b8e
+  /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
881b8e
+     and .got has the PLT entry adddress.  We will load the GOT entry
881b8e
+     with the PLT entry in finish_dynamic_symbol if it is used.  For
881b8e
+     branch, it uses .got.plt.  For symbol value,
881b8e
+     1. Use .got.plt in a shared object if it is forced local or not
881b8e
+     dynamic.
881b8e
+     2. Use .got.plt in a non-shared object if pointer equality isn't
881b8e
+     needed.
881b8e
+     3. Use .got.plt in PIE.
881b8e
+     4. Use .got.plt if .got isn't used.
881b8e
+     5. Otherwise use .got so that it can be shared among different
881b8e
+     objects at run-time.
881b8e
+     We only need to relocate .got entry in shared object.  */
881b8e
+  if (h->got.refcount <= 0
881b8e
+      || (info->shared
881b8e
+	  && (h->dynindx == -1
881b8e
+	      || h->forced_local))
881b8e
+      || (!info->shared
881b8e
+	  && !h->pointer_equality_needed)
881b8e
+      || (info->executable && info->shared)
881b8e
+      || htab->sgot == NULL)
881b8e
+    {
881b8e
+      /* Use .got.plt.  */
881b8e
+      h->got.offset = (bfd_vma) -1;
881b8e
+    }
881b8e
+  else
881b8e
+    {
881b8e
+      h->got.offset = htab->sgot->size;
881b8e
+      htab->sgot->size += got_entry_size;
881b8e
+      if (info->shared)
881b8e
+	htab->srelgot->size += sizeof_reloc;
881b8e
+    }
881b8e
+
881b8e
+  return TRUE;
881b8e
+}
881b8e
+
881b8e
+/* Allocate space in .plt, .got and associated reloc sections for
881b8e
+   ifunc dynamic relocs.  */
881b8e
 
881b8e
+static bfd_boolean
881b8e
+elf64_aarch64_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
881b8e
+					void *inf)
881b8e
+{
881b8e
+  struct bfd_link_info *info;
881b8e
+  struct elf64_aarch64_link_hash_table *htab;
881b8e
+  struct elf64_aarch64_link_hash_entry *eh;
881b8e
 
881b8e
+  /* An example of a bfd_link_hash_indirect symbol is versioned
881b8e
+     symbol. For example: __gxx_personality_v0(bfd_link_hash_indirect)
881b8e
+     -> __gxx_personality_v0(bfd_link_hash_defined)
881b8e
+
881b8e
+     There is no need to process bfd_link_hash_indirect symbols here
881b8e
+     because we will also be presented with the concrete instance of
881b8e
+     the symbol and elf64_aarch64_copy_indirect_symbol () will have been
881b8e
+     called to copy all relevant data from the generic to the concrete
881b8e
+     symbol instance.
881b8e
+  */
881b8e
+  if (h->root.type == bfd_link_hash_indirect)
881b8e
+    return TRUE;
881b8e
+
881b8e
+  if (h->root.type == bfd_link_hash_warning)
881b8e
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
881b8e
+
881b8e
+  info = (struct bfd_link_info *) inf;
881b8e
+  htab = elf64_aarch64_hash_table (info);
881b8e
+
881b8e
+  eh = (struct elf64_aarch64_link_hash_entry *) h;
881b8e
+
881b8e
+  /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
881b8e
+     here if it is defined and referenced in a non-shared object.  */
881b8e
+  if (h->type == STT_GNU_IFUNC
881b8e
+      && h->def_regular)
881b8e
+    return _bfd_elf_aarch64_allocate_ifunc_dyn_relocs (info, h,
881b8e
+						       &eh->dyn_relocs,
881b8e
+						       htab->plt_entry_size,
881b8e
+						       htab->plt_header_size,
881b8e
+						       GOT_ENTRY_SIZE);
881b8e
+  return TRUE;
881b8e
+}
881b8e
+
881b8e
+/* Allocate space in .plt, .got and associated reloc sections for
881b8e
+   local dynamic relocs.  */
881b8e
+
881b8e
+static bfd_boolean
881b8e
+elf64_aarch64_allocate_local_dynrelocs (void **slot, void *inf)
881b8e
+{
881b8e
+  struct elf_link_hash_entry *h
881b8e
+    = (struct elf_link_hash_entry *) *slot;
881b8e
+
881b8e
+  if (h->type != STT_GNU_IFUNC
881b8e
+      || !h->def_regular
881b8e
+      || !h->ref_regular
881b8e
+      || !h->forced_local
881b8e
+      || h->root.type != bfd_link_hash_defined)
881b8e
+    abort ();
881b8e
+
881b8e
+  return elf64_aarch64_allocate_dynrelocs (h, inf);
881b8e
+}
881b8e
+
881b8e
+/* Allocate space in .plt, .got and associate reloc sections for
881b8e
+   local ifunc dynamic relocs.  */
881b8e
+
881b8e
+static bfd_boolean
881b8e
+elf64_aarch64_allocate_local_ifunc_dynrelocs (void **slot, void *inf)
881b8e
+{
881b8e
+  struct elf_link_hash_entry *h
881b8e
+    = (struct elf_link_hash_entry *) *slot;
881b8e
+
881b8e
+  if (h->type != STT_GNU_IFUNC
881b8e
+      || !h->def_regular
881b8e
+      || !h->ref_regular
881b8e
+      || !h->forced_local
881b8e
+      || h->root.type != bfd_link_hash_defined)
881b8e
+    abort ();
881b8e
+
881b8e
+  return elf64_aarch64_allocate_ifunc_dynrelocs (h, inf);
881b8e
+}
881b8e
 
881b8e
 /* This is the most important function of all . Innocuosly named
881b8e
    though !  */
881b8e
@@ -6470,6 +7086,20 @@
881b8e
   elf_link_hash_traverse (&htab->root, elf64_aarch64_allocate_dynrelocs,
881b8e
 			  info);
881b8e
 
881b8e
+  /* Allocate global ifunc sym .plt and .got entries, and space for global
881b8e
+     ifunc sym dynamic relocs.  */
881b8e
+  elf_link_hash_traverse (&htab->root, elf64_aarch64_allocate_ifunc_dynrelocs,
881b8e
+			  info);
881b8e
+
881b8e
+  /* Allocate .plt and .got entries, and space for local symbols.  */
881b8e
+  htab_traverse (htab->loc_hash_table,
881b8e
+		 elf64_aarch64_allocate_local_dynrelocs,
881b8e
+		 info);
881b8e
+
881b8e
+  /* Allocate .plt and .got entries, and space for local ifunc symbols.  */
881b8e
+  htab_traverse (htab->loc_hash_table,
881b8e
+		 elf64_aarch64_allocate_local_ifunc_dynrelocs,
881b8e
+		 info);
881b8e
 
881b8e
   /* For every jump slot reserved in the sgotplt, reloc_count is
881b8e
      incremented.  However, when we reserve space for TLS descriptors,
881b8e
@@ -6625,7 +7255,8 @@
881b8e
 static void
881b8e
 elf64_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
881b8e
 				       struct elf64_aarch64_link_hash_table
881b8e
-				       *htab, bfd *output_bfd)
881b8e
+				       *htab, bfd *output_bfd,
881b8e
+				       struct bfd_link_info *info)
881b8e
 {
881b8e
   bfd_byte *plt_entry;
881b8e
   bfd_vma plt_index;
881b8e
@@ -6634,17 +7265,50 @@
881b8e
   bfd_vma plt_entry_address;
881b8e
   Elf_Internal_Rela rela;
881b8e
   bfd_byte *loc;
881b8e
+  asection *plt, *gotplt, *relplt;
881b8e
 
881b8e
-  plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
881b8e
+  /* When building a static executable, use .iplt, .igot.plt and
881b8e
+     .rela.iplt sections for STT_GNU_IFUNC symbols.  */
881b8e
+  if (htab->root.splt != NULL)
881b8e
+    {
881b8e
+      plt = htab->root.splt;
881b8e
+      gotplt = htab->root.sgotplt;
881b8e
+      relplt = htab->root.srelplt;
881b8e
+    }
881b8e
+  else
881b8e
+    {
881b8e
+      plt = htab->root.iplt;
881b8e
+      gotplt = htab->root.igotplt;
881b8e
+      relplt = htab->root.irelplt;
881b8e
+    }
881b8e
+
881b8e
+  /* Get the index in the procedure linkage table which
881b8e
+     corresponds to this symbol.  This is the index of this symbol
881b8e
+     in all the symbols for which we are making plt entries.  The
881b8e
+     first entry in the procedure linkage table is reserved.
881b8e
 
881b8e
-  /* Offset in the GOT is PLT index plus got GOT headers(3)
881b8e
-     times 8.  */
881b8e
-  got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
881b8e
-  plt_entry = htab->root.splt->contents + h->plt.offset;
881b8e
-  plt_entry_address = htab->root.splt->output_section->vma
881b8e
-    + htab->root.splt->output_offset + h->plt.offset;
881b8e
-  gotplt_entry_address = htab->root.sgotplt->output_section->vma +
881b8e
-    htab->root.sgotplt->output_offset + got_offset;
881b8e
+     Get the offset into the .got table of the entry that
881b8e
+     correponds to this function.  Each .got entry is GOT_ENTRY_SIZE
881b8e
+     bytes. The first three are reserved for the dynamic linker.
881b8e
+
881b8e
+     For static executables, we don't reserve anything.  */
881b8e
+
881b8e
+  if (plt == htab->root.splt)
881b8e
+    {
881b8e
+      plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
881b8e
+      got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
881b8e
+    }
881b8e
+  else
881b8e
+    {
881b8e
+      plt_index = h->plt.offset / htab->plt_entry_size;
881b8e
+      got_offset = plt_index * GOT_ENTRY_SIZE;
881b8e
+    }
881b8e
+
881b8e
+  plt_entry = plt->contents + h->plt.offset;
881b8e
+  plt_entry_address = plt->output_section->vma
881b8e
+    + plt->output_section->output_offset + h->plt.offset;
881b8e
+  gotplt_entry_address = gotplt->output_section->vma +
881b8e
+    gotplt->output_offset + got_offset;
881b8e
 
881b8e
   /* Copy in the boiler-plate for the PLTn entry.  */
881b8e
   memcpy (plt_entry, elf64_aarch64_small_plt_entry, PLT_SMALL_ENTRY_SIZE);
881b8e
@@ -6668,19 +7332,35 @@
881b8e
 
881b8e
   /* All the GOTPLT Entries are essentially initialized to PLT0.  */
881b8e
   bfd_put_64 (output_bfd,
881b8e
-	      (htab->root.splt->output_section->vma
881b8e
-	       + htab->root.splt->output_offset),
881b8e
-	      htab->root.sgotplt->contents + got_offset);
881b8e
+	      plt->output_section->vma + plt->output_offset,
881b8e
+	      gotplt->contents + got_offset);
881b8e
 
881b8e
-  /* Fill in the entry in the .rela.plt section.  */
881b8e
   rela.r_offset = gotplt_entry_address;
881b8e
-  rela.r_info = ELF64_R_INFO (h->dynindx, R_AARCH64_JUMP_SLOT);
881b8e
-  rela.r_addend = 0;
881b8e
+
881b8e
+  if (h->dynindx == -1
881b8e
+      || ((info->executable
881b8e
+	  || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
881b8e
+      && h->def_regular
881b8e
+      && h->type == STT_GNU_IFUNC))
881b8e
+    {
881b8e
+      /* If an STT_GNU_IFUNC symbol is locally defined, generate
881b8e
+	 R_AARCH64_IRELATIVE instead of R_AARCH64_JUMP_SLOT.  */
881b8e
+      rela.r_info = ELF64_R_INFO (0, R_AARCH64_IRELATIVE);
881b8e
+      rela.r_addend = (h->root.u.def.value
881b8e
+		       + h->root.u.def.section->output_section->vma
881b8e
+		       + h->root.u.def.section->output_offset);
881b8e
+    }
881b8e
+  else
881b8e
+    {
881b8e
+      /* Fill in the entry in the .rela.plt section.  */
881b8e
+      rela.r_info = ELF64_R_INFO (h->dynindx, R_AARCH64_JUMP_SLOT);
881b8e
+      rela.r_addend = 0;
881b8e
+    }
881b8e
 
881b8e
   /* Compute the relocation entry to used based on PLT index and do
881b8e
      not adjust reloc_count. The reloc_count has already been adjusted
881b8e
      to account for this entry.  */
881b8e
-  loc = htab->root.srelplt->contents + plt_index * RELOC_SIZE (htab);
881b8e
+  loc = relplt->contents + plt_index * RELOC_SIZE (htab);
881b8e
   bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
881b8e
 }
881b8e
 
881b8e
@@ -6740,15 +7420,35 @@
881b8e
 
881b8e
   if (h->plt.offset != (bfd_vma) - 1)
881b8e
     {
881b8e
+      asection *plt, *gotplt, *relplt;
881b8e
+
881b8e
+      /* When building a static executable, use .iplt, .igot.plt and
881b8e
+	 .rela.iplt sections for STT_GNU_IFUNC symbols.  */
881b8e
+      if (htab->root.splt != NULL)
881b8e
+	{
881b8e
+	  plt = htab->root.splt;
881b8e
+	  gotplt = htab->root.sgotplt;
881b8e
+	  relplt = htab->root.srelplt;
881b8e
+	}
881b8e
+      else
881b8e
+	{
881b8e
+	  plt = htab->root.iplt;
881b8e
+	  gotplt = htab->root.igotplt;
881b8e
+	  relplt = htab->root.irelplt;
881b8e
+	}
881b8e
+
881b8e
       /* This symbol has an entry in the procedure linkage table.  Set
881b8e
          it up.  */
881b8e
-
881b8e
-      if (h->dynindx == -1
881b8e
-	  || htab->root.splt == NULL
881b8e
-	  || htab->root.sgotplt == NULL || htab->root.srelplt == NULL)
881b8e
+      if ((h->dynindx == -1
881b8e
+	  && !((h->forced_local || info->executable)
881b8e
+	       && h->def_regular
881b8e
+	       && h->type == STT_GNU_IFUNC))
881b8e
+	  || plt == NULL
881b8e
+	  || gotplt == NULL
881b8e
+	  || relplt == NULL)
881b8e
 	abort ();
881b8e
 
881b8e
-      elf64_aarch64_create_small_pltn_entry (h, htab, output_bfd);
881b8e
+      elf64_aarch64_create_small_pltn_entry (h, htab, output_bfd, info);
881b8e
       if (!h->def_regular)
881b8e
 	{
881b8e
 	  /* Mark the symbol as undefined, rather than as defined in
881b8e
@@ -6833,6 +7533,21 @@
881b8e
   return TRUE;
881b8e
 }
881b8e
 
881b8e
+/* Finish up local dynamic symbol handling.  We set the contents of
881b8e
+   various dynamic sections here.  */
881b8e
+
881b8e
+static bfd_boolean
881b8e
+elf64_aarch64_finish_local_dynamic_symbol (void **slot, void *inf)
881b8e
+{
881b8e
+  struct elf_link_hash_entry *h
881b8e
+    = (struct elf_link_hash_entry *) *slot;
881b8e
+  struct bfd_link_info *info
881b8e
+    = (struct bfd_link_info *) inf;
881b8e
+
881b8e
+  return elf64_aarch64_finish_dynamic_symbol (info->output_bfd,
881b8e
+					      info, h, NULL);
881b8e
+}
881b8e
+
881b8e
 static void
881b8e
 elf64_aarch64_init_small_plt0_entry (bfd *output_bfd ATTRIBUTE_UNUSED,
881b8e
 				     struct elf64_aarch64_link_hash_table
881b8e
@@ -7078,6 +7793,11 @@
881b8e
     elf_section_data (htab->root.sgot->output_section)->this_hdr.sh_entsize
881b8e
       = GOT_ENTRY_SIZE;
881b8e
 
881b8e
+  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
881b8e
+  htab_traverse (htab->loc_hash_table,
881b8e
+		 elf64_aarch64_finish_local_dynamic_symbol,
881b8e
+		 info);
881b8e
+
881b8e
   return TRUE;
881b8e
 }
881b8e
 
881b8e
@@ -7197,9 +7917,6 @@
881b8e
 #define elf_backend_init_index_section		\
881b8e
   _bfd_elf_init_2_index_sections
881b8e
 
881b8e
-#define elf_backend_is_function_type		\
881b8e
-  elf64_aarch64_is_function_type
881b8e
-
881b8e
 #define elf_backend_finish_dynamic_sections	\
881b8e
   elf64_aarch64_finish_dynamic_sections
881b8e
 
881b8e
diff -ruP binutils-2.23.52.0.1-orig/include/elf/aarch64.h binutils-2.23.52.0.1/include/elf/aarch64.h
881b8e
--- binutils-2.23.52.0.1-orig/include/elf/aarch64.h	2013-02-27 12:28:03.000000000 -0800
881b8e
+++ binutils-2.23.52.0.1/include/elf/aarch64.h	2014-06-16 10:50:29.815495580 -0700
881b8e
@@ -208,7 +208,9 @@
881b8e
 RELOC_NUMBER (R_AARCH64_TLS_DTPREL64, 1029)
881b8e
 RELOC_NUMBER (R_AARCH64_TLS_TPREL64, 1030)
881b8e
 RELOC_NUMBER (R_AARCH64_TLSDESC, 1031)
881b8e
-FAKE_RELOC (R_AARCH64_dyn_max, 1032)
881b8e
+RELOC_NUMBER (R_AARCH64_IRELATIVE, 1032)
881b8e
+
881b8e
+FAKE_RELOC (R_AARCH64_dyn_max, 1033)
881b8e
 
881b8e
 END_RELOC_NUMBERS (R_AARCH64_end)
881b8e