OSDN Git Service

STT_GNU_IFUNC support for PowerPC.
authorAlan Modra <amodra@bigpond.net.au>
Fri, 10 Jul 2009 12:19:58 +0000 (12:19 +0000)
committerAlan Modra <amodra@bigpond.net.au>
Fri, 10 Jul 2009 12:19:58 +0000 (12:19 +0000)
bfd/ChangeLog
bfd/elf32-ppc.c
bfd/elf64-ppc.c
include/elf/ChangeLog
include/elf/ppc.h
include/elf/ppc64.h
ld/ChangeLog
ld/emulparams/elf32ppc.sh
ld/testsuite/ChangeLog
ld/testsuite/ld-ifunc/ifunc.exp

index c2e281b..ce12cc8 100644 (file)
@@ -1,5 +1,58 @@
 2009-07-10  Alan Modra  <amodra@bigpond.net.au>
 
+       * elf32-ppc.c (ppc_elf_howto_raw): Add R_PPC_IRELATIVE.
+       (ppc_elf_get_synthetic_symtab): Report addend.
+       (PLT_IFUNC): Define.
+       (struct ppc_elf_link_hash_table): Add iplt and reliplt.
+       (ppc_elf_create_glink): New function.
+       (ppc_elf_create_dynamic_sections): Use it.
+       (ppc_elf_add_symbol_hook): Set has_ifunc_symbols.
+       (update_local_sym_info): Allocate space for local_plt array.
+       Don't bump local_got_refcounts for PLT_IFUNC.  Return local_plt
+       entry pointer.
+       (is_branch_reloc): New function.
+       (ppc_elf_check_relocs): Handle STT_GNU_IFUNC symbols.
+       (ppc_elf_gc_sweep_hook): Likewise.
+       (ppc_elf_adjust_dynamic_symbol): Likewise.
+       (allocate_dynrelocs): Likewise.
+       (ppc_elf_size_dynamic_sections): Likewise.
+       (ppc_elf_relocate_section): Likewise.
+       (branch_reloc_hash_match): Use is_branch_reloc.
+       (ppc_elf_tls_optimize): Adjust for local_plt.
+       (write_glink_stub): New function, extracted from..
+       (ppc_elf_finish_dynamic_symbol): ..here.  Handle STT_GNU_IFUNC.
+       (ppc_elf_finish_dynamic_sections): Only write plt resolver and
+       branch table when dynamic.
+       (elf_backend_post_process_headers): Define.
+       * elf64-ppc.c (elf_backend_post_process_headers): Define.
+       (ppc64_elf_howto_raw): Add R_PPC64_IRELATIVE.
+       (ppc64_elf_get_synthetic_symtab): Report addend.
+       (struct ppc_stub_hash_entry): Add plt_ent.
+       (PLT_IFUNC): Define.
+       (struct ppc_link_hash_table): Add iplt and reliplt.
+       (create_linkage_sections): Make .iplt and .rela.iplt sections.
+       (ppc64_elf_add_symbol_hook): Set has_ifunc_symbols.
+       (update_local_sym_info): Allocate space for local_plt array.
+       Don't bump local_got_ents for PLT_IFUNC.  Return local_plt
+       entry pointer.
+       (update_plt_info): Pass pointer to plt_entry rather than sym hash,
+       and don't change hash flags here.
+       (is_branch_reloc): New function.
+       (ppc64_elf_check_relocs): Handle STT_GNU_IFUNC.
+       (ppc64_elf_gc_sweep_hook): Likewise.
+       (ppc64_elf_adjust_dynamic_symbol): Likewise.
+       (allocate_dynrelocs): Likewise.
+       (ppc64_elf_size_dynamic_sections): Likewise.
+       (ppc_build_one_stub, ppc_size_one_stub): Likewise.
+       (ppc64_elf_size_stubs): Likewise.
+       (ppc64_elf_relocate_section): Likewise.
+       (get_sym_h): Adjust for local_plt.
+       (branch_reloc_hash_match): Use is_branch_reloc.
+       (ppc_type_of_stub): Pass plt_entry pointer and handle ifunc.
+       (ppc64_elf_toc): Ignore SEC_EXCLUDE sections.
+
+2009-07-10  Alan Modra  <amodra@bigpond.net.au>
+
        * elf.c (_bfd_elf_get_synthetic_symtab): Report addends.
 
 2009-07-10  Alan Modra  <amodra@bigpond.net.au>
index 0a27ffd..814c8a0 100644 (file)
@@ -1382,6 +1382,20 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  HOWTO (R_PPC_IRELATIVE,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_IRELATIVE",     /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   /* A 16 bit relative relocation.  */
   HOWTO (R_PPC_REL16,          /* type */
         0,                     /* rightshift */
@@ -2486,7 +2500,11 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
   size = count * sizeof (asymbol);
   p = relplt->relocation;
   for (i = 0; i < count; i++, p++)
-    size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+    {
+      size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+      if (p->addend != 0)
+       size += sizeof ("+0x") - 1 + 8;
+    }
 
   size += sizeof (asymbol) + sizeof ("__glink");
 
@@ -2516,6 +2534,13 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
       len = strlen ((*p->sym_ptr_ptr)->name);
       memcpy (names, (*p->sym_ptr_ptr)->name, len);
       names += len;
+      if (p->addend != 0)
+       {
+         memcpy (names, "+0x", sizeof ("+0x") - 1);
+         names += sizeof ("+0x") - 1;
+         bfd_sprintf_vma (abfd, names, p->addend);
+         names += strlen (names);
+       }
       memcpy (names, "@plt", sizeof ("@plt"));
       names += sizeof ("@plt");
       ++s;
@@ -2665,6 +2690,7 @@ struct ppc_elf_link_hash_entry
 #define TLS_DTPREL      8      /* DTPREL reloc, => LD. */
 #define TLS_TLS                16      /* Any TLS reloc.  */
 #define TLS_TPRELGD    32      /* TPREL reloc resulting from GD->IE. */
+#define PLT_IFUNC      64      /* STT_GNU_IFUNC.  */
   char tls_mask;
 
   /* Nonzero if we have seen a small data relocation referring to this
@@ -2686,6 +2712,8 @@ struct ppc_elf_link_hash_table
   asection *glink;
   asection *plt;
   asection *relplt;
+  asection *iplt;
+  asection *reliplt;
   asection *dynbss;
   asection *relbss;
   asection *dynsbss;
@@ -2852,6 +2880,38 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
+static bfd_boolean
+ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
+{
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+  asection *s;
+  flagword flags;
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS
+          | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
+  htab->glink = s;
+  if (s == NULL
+      || !bfd_set_section_alignment (abfd, s, 4))
+    return FALSE;
+
+  flags = SEC_ALLOC | SEC_LINKER_CREATED;
+  s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags);
+  htab->iplt = s;
+  if (s == NULL
+      || !bfd_set_section_alignment (abfd, s, 4))
+    return FALSE;
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+          | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags);
+  htab->reliplt = s;
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s, 2))
+    return FALSE;
+  return TRUE;
+}
+
 /* We have to create .dynsbss and .rela.sbss here so that they get mapped
    to output sections (just like _bfd_elf_create_dynamic_sections has
    to create .dynbss and .rela.bss).  */
@@ -2872,13 +2932,8 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   if (!_bfd_elf_create_dynamic_sections (abfd, info))
     return FALSE;
 
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
-          | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-
-  s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags | SEC_CODE);
-  htab->glink = s;
-  if (s == NULL
-      || !bfd_set_section_alignment (abfd, s, 4))
+  if (htab->glink == NULL
+      && !ppc_elf_create_glink (abfd, info))
     return FALSE;
 
   htab->dynbss = bfd_get_section_by_name (abfd, ".dynbss");
@@ -2891,6 +2946,8 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   if (! info->shared)
     {
       htab->relbss = bfd_get_section_by_name (abfd, ".rela.bss");
+      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
       s = bfd_make_section_with_flags (abfd, ".rela.sbss", flags);
       htab->relsbss = s;
       if (s == NULL
@@ -3064,6 +3121,9 @@ ppc_elf_add_symbol_hook (bfd *abfd,
       *valp = sym->st_size;
     }
 
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+
   return TRUE;
 }
 \f
@@ -3212,30 +3272,35 @@ elf_create_pointer_linker_section (bfd *abfd,
   return TRUE;
 }
 
-static bfd_boolean
+static struct plt_entry **
 update_local_sym_info (bfd *abfd,
                       Elf_Internal_Shdr *symtab_hdr,
                       unsigned long r_symndx,
                       int tls_type)
 {
   bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+  struct plt_entry **local_plt;
   char *local_got_tls_masks;
 
   if (local_got_refcounts == NULL)
     {
       bfd_size_type size = symtab_hdr->sh_info;
 
-      size *= sizeof (*local_got_refcounts) + sizeof (*local_got_tls_masks);
+      size *= (sizeof (*local_got_refcounts)
+              + sizeof (*local_plt)
+              + sizeof (*local_got_tls_masks));
       local_got_refcounts = bfd_zalloc (abfd, size);
       if (local_got_refcounts == NULL)
-       return FALSE;
+       return NULL;
       elf_local_got_refcounts (abfd) = local_got_refcounts;
     }
 
-  local_got_refcounts[r_symndx] += 1;
-  local_got_tls_masks = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+  local_plt = (struct plt_entry **) (local_got_refcounts + symtab_hdr->sh_info);
+  local_got_tls_masks = (char *) (local_plt + symtab_hdr->sh_info);
   local_got_tls_masks[r_symndx] |= tls_type;
-  return TRUE;
+  if (tls_type != PLT_IFUNC)
+    local_got_refcounts[r_symndx] += 1;
+  return local_plt + r_symndx;
 }
 
 static bfd_boolean
@@ -3276,6 +3341,21 @@ find_plt_ent (struct plt_entry **plist, asection *sec, bfd_vma addend)
   return ent;
 }
 
+static bfd_boolean
+is_branch_reloc (enum elf_ppc_reloc_type r_type)
+{
+  return (r_type == R_PPC_PLTREL24
+         || r_type == R_PPC_LOCAL24PC
+         || r_type == R_PPC_REL24
+         || r_type == R_PPC_REL14
+         || r_type == R_PPC_REL14_BRTAKEN
+         || r_type == R_PPC_REL14_BRNTAKEN
+         || r_type == R_PPC_ADDR24
+         || r_type == R_PPC_ADDR14
+         || r_type == R_PPC_ADDR14_BRTAKEN
+         || r_type == R_PPC_ADDR14_BRNTAKEN);
+}
+
 static void
 bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
 {
@@ -3342,6 +3422,7 @@ ppc_elf_check_relocs (bfd *abfd,
       enum elf_ppc_reloc_type r_type;
       struct elf_link_hash_entry *h;
       int tls_type;
+      struct plt_entry **ifunc;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -3369,34 +3450,69 @@ ppc_elf_check_relocs (bfd *abfd,
        }
 
       tls_type = 0;
+      ifunc = NULL;
       r_type = ELF32_R_TYPE (rel->r_info);
-      if (h != NULL && h == tga)
-       switch (r_type)
-         {
-         default:
-           break;
+      if (!htab->is_vxworks && is_branch_reloc (r_type))
+       {
+         if (h != NULL && h == tga)
+           {
+             if (rel != relocs
+                 && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
+                     || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD))
+               /* We have a new-style __tls_get_addr call with a marker
+                  reloc.  */
+               ;
+             else
+               /* Mark this section as having an old-style call.  */
+               sec->has_tls_get_addr_call = 1;
+           }
 
-         case R_PPC_PLTREL24:
-         case R_PPC_LOCAL24PC:
-         case R_PPC_REL24:
-         case R_PPC_REL14:
-         case R_PPC_REL14_BRTAKEN:
-         case R_PPC_REL14_BRNTAKEN:
-         case R_PPC_ADDR24:
-         case R_PPC_ADDR14:
-         case R_PPC_ADDR14_BRTAKEN:
-         case R_PPC_ADDR14_BRNTAKEN:
-           if (rel != relocs
-               && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
-                   || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD))
-             /* We have a new-style __tls_get_addr call with a marker
-                reloc.  */
-             ;
-           else
-             /* Mark this section as having an old-style call.  */
-             sec->has_tls_get_addr_call = 1;
-           break;
-         }
+         /* STT_GNU_IFUNC symbols must have a PLT entry.  */
+         if (h != NULL)
+           {
+             if (h->type == STT_GNU_IFUNC)
+               {
+                 h->needs_plt = 1;
+                 ifunc = &h->plt.plist;
+               }
+           }
+         else
+           {
+             Elf_Internal_Sym *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)
+               {
+                 ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                                PLT_IFUNC);
+                 if (ifunc == NULL)
+                   return FALSE;
+               }
+           }
+         if (ifunc != NULL)
+           {
+             bfd_vma addend = 0;
+
+             if (r_type == R_PPC_PLTREL24)
+               {
+                 ppc_elf_tdata (abfd)->makes_plt_call = 1;
+                 addend = rel->r_addend;
+               }
+             if (!update_plt_info (abfd, ifunc,
+                                   addend < 32768 ? NULL : got2, addend))
+               return FALSE;
+
+             if (htab->glink == NULL)
+               {
+                 if (htab->elf.dynobj == NULL)
+                   htab->elf.dynobj = abfd;
+                 if (!ppc_elf_create_glink (htab->elf.dynobj, info))
+                   return FALSE;
+               }
+           }
+       }
 
       switch (r_type)
        {
@@ -3571,7 +3687,7 @@ ppc_elf_check_relocs (bfd *abfd,
          break;
 
        case R_PPC_PLTREL24:
-         if (h == NULL)
+         if (h == NULL || ifunc != NULL)
            break;
          /* Fall through */
        case R_PPC_PLT32:
@@ -3654,6 +3770,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_GLOB_DAT:
        case R_PPC_JMP_SLOT:
        case R_PPC_RELATIVE:
+       case R_PPC_IRELATIVE:
          break;
 
          /* These aren't handled yet.  We'll report an error later.  */
@@ -3782,7 +3899,9 @@ ppc_elf_check_relocs (bfd *abfd,
            {
              /* We may need a plt entry if the symbol turns out to be
                 a function defined in a dynamic object.  */
-             if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+             h->needs_plt = 1;
+             if (ifunc == NULL
+                 && !update_plt_info (abfd, &h->plt.plist, NULL, 0))
                return FALSE;
              break;
            }
@@ -4291,6 +4410,33 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
        }
 
       r_type = ELF32_R_TYPE (rel->r_info);
+      if (!htab->is_vxworks && is_branch_reloc (r_type))
+       {
+         struct plt_entry **ifunc = NULL;
+         if (h != NULL)
+           {
+             if (h->type == STT_GNU_IFUNC)
+               ifunc = &h->plt.plist;
+           }
+         else if (local_got_refcounts != NULL)
+           {
+             struct plt_entry **local_plt = (struct plt_entry **)
+               (local_got_refcounts + symtab_hdr->sh_info);
+             char *local_got_tls_masks = (char *)
+               (local_plt + symtab_hdr->sh_info);
+             if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
+               ifunc = local_plt + r_symndx;
+           }
+         if (ifunc != NULL)
+           {
+             bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
+             struct plt_entry *ent = find_plt_ent (ifunc, got2, addend);
+             if (ent->plt.refcount > 0)
+               ent->plt.refcount -= 1;
+             continue;
+           }
+       }
+
       switch (r_type)
        {
        case R_PPC_GOT_TLSLD16:
@@ -4405,17 +4551,7 @@ branch_reloc_hash_match (const bfd *ibfd,
   enum elf_ppc_reloc_type r_type = ELF32_R_TYPE (rel->r_info);
   unsigned int r_symndx = ELF32_R_SYM (rel->r_info);
 
-  if (r_symndx >= symtab_hdr->sh_info
-      && (r_type == R_PPC_PLTREL24
-         || r_type == R_PPC_LOCAL24PC
-         || r_type == R_PPC_REL14
-         || r_type == R_PPC_REL14_BRTAKEN
-         || r_type == R_PPC_REL14_BRNTAKEN
-         || r_type == R_PPC_REL24
-         || r_type == R_PPC_ADDR24
-         || r_type == R_PPC_ADDR14
-         || r_type == R_PPC_ADDR14_BRTAKEN
-         || r_type == R_PPC_ADDR14_BRNTAKEN))
+  if (r_symndx >= symtab_hdr->sh_info && is_branch_reloc (r_type))
     {
       struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
       struct elf_link_hash_entry *h;
@@ -4580,6 +4716,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                    {
                      Elf_Internal_Sym *sym;
                      bfd_signed_vma *lgot_refs;
+                     struct plt_entry **local_plt;
                      char *lgot_masks;
 
                      if (locsyms == NULL)
@@ -4600,7 +4737,9 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                      lgot_refs = elf_local_got_refcounts (ibfd);
                      if (lgot_refs == NULL)
                        abort ();
-                     lgot_masks = (char *) (lgot_refs + symtab_hdr->sh_info);
+                     local_plt = (struct plt_entry **)
+                       (lgot_refs + symtab_hdr->sh_info);
+                     lgot_masks = (char *) (local_plt + symtab_hdr->sh_info);
                      tls_mask = &lgot_masks[r_symndx];
                      got_count = &lgot_refs[r_symndx];
                    }
@@ -4690,6 +4829,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   /* Deal with function syms.  */
   if (h->type == STT_FUNC
+      || h->type == STT_GNU_IFUNC
       || h->needs_plt)
     {
       /* Clear procedure linkage table information for any symbol that
@@ -4699,9 +4839,10 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
        if (ent->plt.refcount > 0)
          break;
       if (ent == NULL
-         || SYMBOL_CALLS_LOCAL (info, h)
-         || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
-             && h->root.type == bfd_link_hash_undefweak))
+         || (h->type != STT_GNU_IFUNC
+             && (SYMBOL_CALLS_LOCAL (info, h)
+                 || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+                     && h->root.type == bfd_link_hash_undefweak))))
        {
          /* A PLT entry is not required/allowed when:
 
@@ -4937,29 +5078,37 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   htab = ppc_elf_hash_table (info);
-  if (htab->elf.dynamic_sections_created)
+  if (htab->elf.dynamic_sections_created
+      || h->type == STT_GNU_IFUNC)
     {
       struct plt_entry *ent;
       bfd_boolean doneone = FALSE;
       bfd_vma plt_offset = 0, glink_offset = 0;
+      bfd_boolean dyn;
 
       for (ent = h->plt.plist; ent != NULL; ent = ent->next)
        if (ent->plt.refcount > 0)
          {
            /* Make sure this symbol is output as a dynamic symbol.  */
            if (h->dynindx == -1
-               && !h->forced_local)
+               && !h->forced_local
+               && !h->def_regular
+               && htab->elf.dynamic_sections_created)
              {
                if (! bfd_elf_link_record_dynamic_symbol (info, h))
                  return FALSE;
              }
 
+           dyn = htab->elf.dynamic_sections_created;
            if (info->shared
-               || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+               || h->type == STT_GNU_IFUNC
+               || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
              {
                asection *s = htab->plt;
+               if (!dyn)
+                 s = htab->iplt;
 
-               if (htab->plt_type == PLT_NEW)
+               if (htab->plt_type == PLT_NEW || !dyn)
                  {
                    if (!doneone)
                      {
@@ -5037,29 +5186,35 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                /* We also need to make an entry in the .rela.plt section.  */
                if (!doneone)
                  {
-                   htab->relplt->size += sizeof (Elf32_External_Rela);
-
-                   if (htab->plt_type == PLT_VXWORKS)
+                   if (!htab->elf.dynamic_sections_created)
+                     htab->reliplt->size += sizeof (Elf32_External_Rela);
+                   else
                      {
-                       /* Allocate space for the unloaded relocations.  */
-                       if (!info->shared)
+                       htab->relplt->size += sizeof (Elf32_External_Rela);
+
+                       if (htab->plt_type == PLT_VXWORKS)
                          {
-                           if (ent->plt.offset
-                               == (bfd_vma) htab->plt_initial_entry_size)
+                           /* Allocate space for the unloaded relocations.  */
+                           if (!info->shared
+                               && htab->elf.dynamic_sections_created)
                              {
+                               if (ent->plt.offset
+                                   == (bfd_vma) htab->plt_initial_entry_size)
+                                 {
+                                   htab->srelplt2->size
+                                     += (sizeof (Elf32_External_Rela)
+                                         * VXWORKS_PLTRESOLVE_RELOCS);
+                                 }
+
                                htab->srelplt2->size
-                                 += sizeof (Elf32_External_Rela)
-                                     * VXWORKS_PLTRESOLVE_RELOCS;
+                                 += (sizeof (Elf32_External_Rela)
+                                     * VXWORKS_PLT_NON_JMP_SLOT_RELOCS);
                              }
 
-                           htab->srelplt2->size
-                             += sizeof (Elf32_External_Rela)
-                                 * VXWORKS_PLT_NON_JMP_SLOT_RELOCS;
+                           /* Every PLT entry has an associated GOT entry in
+                              .got.plt.  */
+                           htab->sgotplt->size += 4;
                          }
-
-                       /* Every PLT entry has an associated GOT entry in
-                          .got.plt.  */
-                       htab->sgotplt->size += 4;
                      }
                    doneone = TRUE;
                  }
@@ -5091,6 +5246,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       /* Make sure this symbol is output as a dynamic symbol.  */
       if (eh->elf.dynindx == -1
          && !eh->elf.forced_local
+         && !eh->elf.def_regular
          && htab->elf.dynamic_sections_created)
        {
          if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf))
@@ -5206,7 +5362,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          /* Make sure undefined weak symbols are output as a dynamic
             symbol in PIEs.  */
          else if (h->dynindx == -1
-                  && !h->forced_local)
+                  && !h->forced_local
+                  && !h->def_regular)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
@@ -5225,7 +5382,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
          if (h->dynindx == -1
-             && !h->forced_local)
+             && !h->forced_local
+             && !h->def_regular)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
                return FALSE;
@@ -5315,6 +5473,8 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
     {
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
+      struct plt_entry **local_plt;
+      struct plt_entry **end_local_plt;
       char *lgot_masks;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
@@ -5365,7 +5525,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       symtab_hdr = &elf_symtab_hdr (ibfd);
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
-      lgot_masks = (char *) end_local_got;
+      local_plt = (struct plt_entry **) end_local_got;
+      end_local_plt = local_plt + locsymcount;
+      lgot_masks = (char *) end_local_plt;
       for (; local_got < end_local_got; ++local_got, ++lgot_masks)
        if (*local_got > 0)
          {
@@ -5395,6 +5557,46 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          }
        else
          *local_got = (bfd_vma) -1;
+
+      if (htab->is_vxworks)
+       continue;
+
+      /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt.  */
+      for (; local_plt < end_local_plt; ++local_plt)
+       {
+         struct plt_entry *ent;
+         bfd_boolean doneone = FALSE;
+         bfd_vma plt_offset = 0, glink_offset = 0;
+
+         for (ent = *local_plt; ent != NULL; ent = ent->next)
+           if (ent->plt.refcount > 0)
+             {
+               asection *s = htab->iplt;
+
+               if (!doneone)
+                 {
+                   plt_offset = s->size;
+                   s->size += 4;
+                 }
+               ent->plt.offset = plt_offset;
+
+               s = htab->glink;
+               if (!doneone || info->shared || info->pie)
+                 {
+                   glink_offset = s->size;
+                   s->size += GLINK_ENTRY_SIZE;
+                 }
+               ent->glink_offset = glink_offset;
+
+               if (!doneone)
+                 {
+                   htab->reliplt += sizeof (Elf32_External_Rela);
+                   doneone = TRUE;
+                 }
+             }
+           else
+             ent->plt.offset = (bfd_vma) -1;
+       }
     }
 
   /* Allocate space for global sym dynamic relocs.  */
@@ -5428,7 +5630,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       htab->elf.hgot->root.u.def.value = g_o_t;
     }
 
-  if (htab->glink != NULL && htab->glink->size != 0)
+  if (htab->glink != NULL
+      && htab->glink->size != 0
+      && htab->elf.dynamic_sections_created)
     {
       htab->glink_pltresolve = htab->glink->size;
       /* Space for the branch table.  */
@@ -5484,22 +5688,23 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        continue;
 
       if (s == htab->plt
-         || s == htab->glink
-         || s == htab->got
-         || s == htab->sgotplt
-         || s == htab->sbss
-         || s == htab->dynbss
-         || s == htab->dynsbss)
+         || s == htab->got)
        {
          /* We'd like to strip these sections if they aren't needed, but if
             we've exported dynamic symbols from them we must leave them.
             It's too late to tell BFD to get rid of the symbols.  */
-         if ((s == htab->plt || s == htab->got) && htab->elf.hplt != NULL)
+         if (htab->elf.hplt != NULL)
            strip_section = FALSE;
          /* Strip this section if we don't need it; see the
             comment below.  */
        }
-      else if (s == htab->sdata[0].section
+      else if (s == htab->iplt
+              || s == htab->glink
+              || s == htab->sgotplt
+              || s == htab->sbss
+              || s == htab->dynbss
+              || s == htab->dynsbss
+              || s == htab->sdata[0].section
               || s == htab->sdata[1].section)
        {
          /* Strip these too.  */
@@ -6192,6 +6397,73 @@ elf_finish_pointer_linker_section (bfd *input_bfd,
   return relocation - linker_section_ptr->addend;
 }
 
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+static void
+write_glink_stub (struct plt_entry *ent, asection *plt_sec,
+                 struct bfd_link_info *info)
+{
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+  bfd *output_bfd = info->output_bfd;
+  bfd_vma plt;
+  unsigned char *p;
+
+  plt = ((ent->plt.offset & ~1)
+        + plt_sec->output_section->vma
+        + plt_sec->output_offset);
+  p = (unsigned char *) htab->glink->contents + ent->glink_offset;
+
+  if (info->shared || info->pie)
+    {
+      bfd_vma got = 0;
+
+      if (ent->addend >= 32768)
+       got = (ent->addend
+              + ent->sec->output_section->vma
+              + ent->sec->output_offset);
+      else if (htab->elf.hgot != NULL)
+       got = SYM_VAL (htab->elf.hgot);
+
+      plt -= got;
+
+      if (plt + 0x8000 < 0x10000)
+       {
+         bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
+         p += 4;
+         bfd_put_32 (output_bfd, MTCTR_11, p);
+         p += 4;
+         bfd_put_32 (output_bfd, BCTR, p);
+         p += 4;
+         bfd_put_32 (output_bfd, NOP, p);
+         p += 4;
+       }
+      else
+       {
+         bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
+         p += 4;
+         bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+         p += 4;
+         bfd_put_32 (output_bfd, MTCTR_11, p);
+         p += 4;
+         bfd_put_32 (output_bfd, BCTR, p);
+         p += 4;
+       }
+    }
+  else
+    {
+      bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
+      p += 4;
+      bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+      p += 4;
+      bfd_put_32 (output_bfd, MTCTR_11, p);
+      p += 4;
+      bfd_put_32 (output_bfd, BCTR, p);
+      p += 4;
+    }
+}
+
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -6285,6 +6557,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
       unsigned int tls_type, tls_mask, tls_gd;
+      struct plt_entry **ifunc;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       sym = NULL;
@@ -6349,8 +6622,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
        tls_mask = ((struct ppc_elf_link_hash_entry *) h)->tls_mask;
       else if (local_got_offsets != NULL)
        {
+         struct plt_entry **local_plt;
          char *lgot_masks;
-         lgot_masks = (char *) (local_got_offsets + symtab_hdr->sh_info);
+         local_plt
+           = (struct plt_entry **) (local_got_offsets + symtab_hdr->sh_info);
+         lgot_masks = (char *) (local_plt + symtab_hdr->sh_info);
          tls_mask = lgot_masks[r_symndx];
        }
 
@@ -6371,7 +6647,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_GOT_TPREL16:
        case R_PPC_GOT_TPREL16_LO:
-         if (tls_mask != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
              bfd_vma insn;
@@ -6385,7 +6661,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_TLS:
-         if (tls_mask != 0
+         if ((tls_mask & TLS_TLS) != 0
              && (tls_mask & TLS_TPREL) == 0)
            {
              bfd_vma insn, rtra;
@@ -6432,13 +6708,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_TLSGD16_HI:
        case R_PPC_GOT_TLSGD16_HA:
          tls_gd = TLS_TPRELGD;
-         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_gdld_hi;
          break;
 
        case R_PPC_GOT_TLSLD16_HI:
        case R_PPC_GOT_TLSLD16_HA:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
            tls_gdld_hi:
              if ((tls_mask & tls_gd) != 0)
@@ -6457,13 +6733,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GOT_TLSGD16:
        case R_PPC_GOT_TLSGD16_LO:
          tls_gd = TLS_TPRELGD;
-         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            goto tls_ldgd_opt;
          break;
 
        case R_PPC_GOT_TLSLD16:
        case R_PPC_GOT_TLSLD16_LO:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
              unsigned int insn1, insn2;
              bfd_vma offset;
@@ -6543,7 +6819,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_TLSGD:
-         if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
            {
              unsigned int insn2;
              bfd_vma offset = rel->r_offset;
@@ -6571,7 +6847,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_TLSLD:
-         if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+         if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
            {
              unsigned int insn2;
 
@@ -6635,6 +6911,64 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
        }
 
+      ifunc = NULL;
+      if (!htab->is_vxworks && is_branch_reloc (r_type))
+       {
+         if (h != NULL)
+           {
+             if (h->type == STT_GNU_IFUNC)
+               ifunc = &h->plt.plist;
+           }
+         else if (local_got_offsets != NULL)
+           {
+             if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+               {
+                 struct plt_entry **local_plt = (struct plt_entry **)
+                   (local_got_offsets + symtab_hdr->sh_info);
+
+                 ifunc = local_plt + r_symndx;
+               }
+           }
+         if (ifunc != NULL)
+           {
+             struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend);
+
+             if (h == NULL && (ent->plt.offset & 1) == 0)
+               {
+                 Elf_Internal_Rela rela;
+                 bfd_byte *loc;
+
+                 rela.r_offset = (htab->iplt->output_section->vma
+                                  + htab->iplt->output_offset
+                                  + ent->plt.offset);
+                 rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+                 rela.r_addend = relocation;
+                 loc = (htab->reliplt->contents
+                        + ent->plt.offset * sizeof (Elf32_External_Rela) / 4);
+                 bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+
+                 ent->plt.offset |= 1;
+               }
+             if (h == NULL && (ent->glink_offset & 1) == 0)
+               {
+                 write_glink_stub (ent, htab->iplt, info);
+                 ent->glink_offset |= 1;
+               }
+
+             unresolved_reloc = FALSE;
+             if (htab->plt_type == PLT_NEW
+                 || !htab->elf.dynamic_sections_created
+                 || h == NULL)
+               relocation = (htab->glink->output_section->vma
+                             + htab->glink->output_offset
+                             + (ent->glink_offset & ~1));
+             else
+               relocation = (htab->plt->output_section->vma
+                             + htab->plt->output_offset
+                             + ent->plt.offset);
+           }
+       }
+
       addend = rel->r_addend;
       tls_type = 0;
       howto = NULL;
@@ -7217,7 +7551,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_PLTREL24:
-         if (h == NULL)
+         if (h == NULL || ifunc != NULL)
            break;
          /* Relocation is to the entry for this symbol in the
             procedure linkage table.  */
@@ -7389,6 +7723,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_GLOB_DAT:
        case R_PPC_JMP_SLOT:
        case R_PPC_RELATIVE:
+       case R_PPC_IRELATIVE:
        case R_PPC_PLT32:
        case R_PPC_PLTREL32:
        case R_PPC_PLT16_LO:
@@ -7524,10 +7859,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
   return ret;
 }
 \f
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
-
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -7559,7 +7890,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
            bfd_byte *loc;
            bfd_vma reloc_index;
 
-           if (htab->plt_type == PLT_NEW)
+           if (htab->plt_type == PLT_NEW
+               || !htab->elf.dynamic_sections_created)
              reloc_index = ent->plt.offset / 4;
            else
              {
@@ -7572,7 +7904,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
            /* This symbol has an entry in the procedure linkage table.
               Set it up.  */
-           if (htab->plt_type == PLT_VXWORKS)
+           if (htab->plt_type == PLT_VXWORKS
+               && htab->elf.dynamic_sections_created)
              {
                bfd_vma got_offset;
                const bfd_vma *plt_entry;
@@ -7694,10 +8027,15 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
              }
            else
              {
-               rela.r_offset = (htab->plt->output_section->vma
-                                + htab->plt->output_offset
+               asection *splt = htab->plt;
+               if (!htab->elf.dynamic_sections_created)
+                 splt = htab->iplt;
+
+               rela.r_offset = (splt->output_section->vma
+                                + splt->output_offset
                                 + ent->plt.offset);
-               if (htab->plt_type == PLT_OLD)
+               if (htab->plt_type == PLT_OLD
+                   || !htab->elf.dynamic_sections_created)
                  {
                    /* We don't need to fill in the .plt.  The ppc dynamic
                       linker will fill it in.  */
@@ -7708,16 +8046,30 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                                   + htab->glink->output_section->vma
                                   + htab->glink->output_offset);
                    bfd_put_32 (output_bfd, val,
-                               htab->plt->contents + ent->plt.offset);
+                               splt->contents + ent->plt.offset);
                  }
              }
 
            /* Fill in the entry in the .rela.plt section.  */
-           rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
            rela.r_addend = 0;
+           if (!htab->elf.dynamic_sections_created
+               || h->dynindx == -1)
+             {
+               BFD_ASSERT (h->type == STT_GNU_IFUNC
+                           && h->def_regular
+                           && (h->root.type == bfd_link_hash_defined
+                               || h->root.type == bfd_link_hash_defweak));
+               rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+               rela.r_addend = SYM_VAL (h);
+             }
+           else
+             rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
 
-           loc = (htab->relplt->contents
-                  + reloc_index * sizeof (Elf32_External_Rela));
+           if (!htab->elf.dynamic_sections_created)
+             loc = htab->reliplt->contents;
+           else
+             loc = htab->relplt->contents;
+           loc += reloc_index * sizeof (Elf32_External_Rela);
            bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
 
            if (!h->def_regular)
@@ -7743,66 +8095,18 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
            doneone = TRUE;
          }
 
-       if (htab->plt_type == PLT_NEW)
+       if (htab->plt_type == PLT_NEW
+           || !htab->elf.dynamic_sections_created)
          {
-           bfd_vma plt;
-           unsigned char *p;
+           asection *splt = htab->plt;
+           if (!htab->elf.dynamic_sections_created)
+             splt = htab->iplt;
 
-           plt = (ent->plt.offset
-                  + htab->plt->output_section->vma
-                  + htab->plt->output_offset);
-           p = (unsigned char *) htab->glink->contents + ent->glink_offset;
+           write_glink_stub (ent, splt, info);
 
-           if (info->shared || info->pie)
-             {
-               bfd_vma got = 0;
-
-               if (ent->addend >= 32768)
-                 got = (ent->addend
-                        + ent->sec->output_section->vma
-                        + ent->sec->output_offset);
-               else if (htab->elf.hgot != NULL)
-                 got = SYM_VAL (htab->elf.hgot);
-
-               plt -= got;
-
-               if (plt + 0x8000 < 0x10000)
-                 {
-                   bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, MTCTR_11, p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, BCTR, p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, NOP, p);
-                   p += 4;
-                 }
-               else
-                 {
-                   bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, MTCTR_11, p);
-                   p += 4;
-                   bfd_put_32 (output_bfd, BCTR, p);
-                   p += 4;
-                 }
-             }
-           else
-             {
-               bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
-               p += 4;
-               bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
-               p += 4;
-               bfd_put_32 (output_bfd, MTCTR_11, p);
-               p += 4;
-               bfd_put_32 (output_bfd, BCTR, p);
-               p += 4;
-
-               /* We only need one non-PIC glink stub.  */
-               break;
-             }
+           if (!info->shared && !info->pie)
+             /* We only need one non-PIC glink stub.  */
+             break;
          }
        else
          break;
@@ -8073,7 +8377,9 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
        }
     }
 
-  if (htab->glink != NULL && htab->glink->contents != NULL)
+  if (htab->glink != NULL
+      && htab->glink->contents != NULL
+      && htab->elf.dynamic_sections_created)
     {
       unsigned char *p;
       unsigned char *endp;
@@ -8340,6 +8646,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_plt_sym_val                        ppc_elf_plt_sym_val
 #define elf_backend_action_discarded           ppc_elf_action_discarded
 #define elf_backend_init_index_section         _bfd_elf_init_1_index_section
+#define elf_backend_post_process_headers       _bfd_elf_set_osabi
 
 #include "elf32-target.h"
 
@@ -8449,5 +8756,6 @@ ppc_elf_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
 
 #undef elf32_bed
 #define elf32_bed                              ppc_elf_vxworks_bed
+#undef elf_backend_post_process_headers
 
 #include "elf32-target.h"
index c3b2115..d744990 100644 (file)
@@ -112,6 +112,7 @@ static bfd_vma opd_entry_value
 #define elf_backend_finish_dynamic_sections   ppc64_elf_finish_dynamic_sections
 #define elf_backend_link_output_symbol_hook   ppc64_elf_output_symbol_hook
 #define elf_backend_special_sections         ppc64_elf_special_sections
+#define elf_backend_post_process_headers      _bfd_elf_set_osabi
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -1873,6 +1874,20 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  HOWTO (R_PPC64_IRELATIVE,    /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0=byte, 1=short, 2=long, 4=64 bits) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC64_IRELATIVE",   /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        ONES (64),             /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_PPC64_GNU_VTINHERIT,        /* type */
         0,                     /* rightshift */
@@ -3116,7 +3131,11 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
 
              p = relplt->relocation;
              for (i = 0; i < plt_count; i++, p++)
-               size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+               {
+                 size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+                 if (p->addend != 0)
+                   size += sizeof ("+0x") - 1 + 16;
+               }
            }
        }
 
@@ -3236,6 +3255,13 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
              len = strlen ((*p->sym_ptr_ptr)->name);
              memcpy (names, (*p->sym_ptr_ptr)->name, len);
              names += len;
+             if (p->addend != 0)
+               {
+                 memcpy (names, "+0x", sizeof ("+0x") - 1);
+                 names += sizeof ("+0x") - 1;
+                 bfd_sprintf_vma (abfd, names, p->addend);
+                 names += strlen (names);
+               }
              memcpy (names, "@plt", sizeof ("@plt"));
              names += sizeof ("@plt");
              s++;
@@ -3518,6 +3544,7 @@ struct ppc_stub_hash_entry {
 
   /* The symbol table entry, if any, that this was derived from.  */
   struct ppc_link_hash_entry *h;
+  struct plt_entry *plt_ent;
 
   /* And the reloc addend that this was derived from.  */
   bfd_vma addend;
@@ -3586,6 +3613,7 @@ struct ppc_link_hash_entry
 #define TLS_TLS                16      /* Any TLS reloc.  */
 #define TLS_EXPLICIT   32      /* Marks TOC section TLS relocs. */
 #define TLS_TPRELGD    64      /* TPREL reloc resulting from GD->IE. */
+#define PLT_IFUNC      128     /* STT_GNU_IFUNC.  */
   char tls_mask;
 };
 
@@ -3638,6 +3666,8 @@ struct ppc_link_hash_table
   asection *got;
   asection *plt;
   asection *relplt;
+  asection *iplt;
+  asection *reliplt;
   asection *dynbss;
   asection *relbss;
   asection *glink;
@@ -4062,6 +4092,21 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
       || ! bfd_set_section_alignment (dynobj, htab->glink, 3))
     return FALSE;
 
+  flags = SEC_ALLOC | SEC_LINKER_CREATED;
+  htab->iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags);
+  if (htab->iplt == NULL
+      || ! bfd_set_section_alignment (dynobj, htab->iplt, 3))
+    return FALSE;
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+          | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  htab->reliplt = bfd_make_section_anyway_with_flags (dynobj,
+                                                     ".rela.iplt",
+                                                     flags);
+  if (htab->reliplt == NULL
+      || ! bfd_set_section_alignment (dynobj, htab->reliplt, 3))
+    return FALSE;
+
   /* Create branch lookup table for plt_branch stubs.  */
   flags = (SEC_ALLOC | SEC_LOAD
           | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
@@ -4079,7 +4124,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
   htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
                                                      ".rela.branch_lt",
                                                      flags);
-  if (!htab->relbrlt
+  if (htab->relbrlt == NULL
       || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
     return FALSE;
 
@@ -4367,15 +4412,19 @@ make_fdh (struct bfd_link_info *info,
 
 static bfd_boolean
 ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED,
-                          struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                          struct bfd_link_info *info,
                           Elf_Internal_Sym *isym,
                           const char **name ATTRIBUTE_UNUSED,
                           flagword *flags ATTRIBUTE_UNUSED,
                           asection **sec,
                           bfd_vma *value ATTRIBUTE_UNUSED)
 {
-  if (*sec != NULL
-      && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0)
+  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+  else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
+    ;
+  else if (*sec != NULL
+          && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0)
     isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
 
   return TRUE;
@@ -4533,25 +4582,28 @@ ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
-static bfd_boolean
+static struct plt_entry **
 update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
                       unsigned long r_symndx, bfd_vma r_addend, int tls_type)
 {
   struct got_entry **local_got_ents = elf_local_got_ents (abfd);
+  struct plt_entry **local_plt;
   char *local_got_tls_masks;
 
   if (local_got_ents == NULL)
     {
       bfd_size_type size = symtab_hdr->sh_info;
 
-      size *= sizeof (*local_got_ents) + sizeof (*local_got_tls_masks);
+      size *= (sizeof (*local_got_ents)
+              + sizeof (*local_plt)
+              + sizeof (*local_got_tls_masks));
       local_got_ents = bfd_zalloc (abfd, size);
       if (local_got_ents == NULL)
-       return FALSE;
+       return NULL;
       elf_local_got_ents (abfd) = local_got_ents;
     }
 
-  if ((tls_type & TLS_EXPLICIT) == 0)
+  if ((tls_type & (PLT_IFUNC | TLS_EXPLICIT)) == 0)
     {
       struct got_entry *ent;
 
@@ -4576,17 +4628,19 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
       ent->got.refcount += 1;
     }
 
-  local_got_tls_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
+  local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info);
+  local_got_tls_masks = (char *) (local_plt + symtab_hdr->sh_info);
   local_got_tls_masks[r_symndx] |= tls_type;
-  return TRUE;
+
+  return local_plt + r_symndx;
 }
 
 static bfd_boolean
-update_plt_info (bfd *abfd, struct ppc_link_hash_entry *eh, bfd_vma addend)
+update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend)
 {
   struct plt_entry *ent;
 
-  for (ent = eh->elf.plt.plist; ent != NULL; ent = ent->next)
+  for (ent = *plist; ent != NULL; ent = ent->next)
     if (ent->addend == addend)
       break;
   if (ent == NULL)
@@ -4595,19 +4649,28 @@ update_plt_info (bfd *abfd, struct ppc_link_hash_entry *eh, bfd_vma addend)
       ent = bfd_alloc (abfd, amt);
       if (ent == NULL)
        return FALSE;
-      ent->next = eh->elf.plt.plist;
+      ent->next = *plist;
       ent->addend = addend;
       ent->plt.refcount = 0;
-      eh->elf.plt.plist = ent;
+      *plist = ent;
     }
   ent->plt.refcount += 1;
-  eh->elf.needs_plt = 1;
-  if (eh->elf.root.root.string[0] == '.'
-      && eh->elf.root.root.string[1] != '\0')
-    eh->is_func = 1;
   return TRUE;
 }
 
+static bfd_boolean
+is_branch_reloc (enum elf_ppc64_reloc_type r_type)
+{
+  return (r_type == R_PPC64_REL24
+         || r_type == R_PPC64_REL14
+         || r_type == R_PPC64_REL14_BRTAKEN
+         || r_type == R_PPC64_REL14_BRNTAKEN
+         || r_type == R_PPC64_ADDR24
+         || r_type == R_PPC64_ADDR14
+         || r_type == R_PPC64_ADDR14_BRTAKEN
+         || r_type == R_PPC64_ADDR14_BRNTAKEN);
+}
+
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure
    linkage table, and dynamic reloc sections.  */
@@ -4685,6 +4748,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       enum elf_ppc64_reloc_type r_type;
       int tls_type;
       struct _ppc64_elf_section_data *ppc64_sec;
+      struct plt_entry **ifunc;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -4698,32 +4762,51 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
 
       tls_type = 0;
+      ifunc = NULL;
       r_type = ELF64_R_TYPE (rel->r_info);
-      if (h != NULL && (h == tga || h == dottga))
-       switch (r_type)
-         {
-         default:
-           break;
+      if (is_branch_reloc (r_type))
+       {
+         if (h != NULL && (h == tga || h == dottga))
+           {
+             if (rel != relocs
+                 && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
+                     || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
+               /* We have a new-style __tls_get_addr call with a marker
+                  reloc.  */
+               ;
+             else
+               /* Mark this section as having an old-style call.  */
+               sec->has_tls_get_addr_call = 1;
+           }
 
-         case R_PPC64_REL24:
-         case R_PPC64_REL14:
-         case R_PPC64_REL14_BRTAKEN:
-         case R_PPC64_REL14_BRNTAKEN:
-         case R_PPC64_ADDR24:
-         case R_PPC64_ADDR14:
-         case R_PPC64_ADDR14_BRTAKEN:
-         case R_PPC64_ADDR14_BRNTAKEN:
-           if (rel != relocs
-               && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
-                   || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
-             /* We have a new-style __tls_get_addr call with a marker
-                reloc.  */
-             ;
-           else
-             /* Mark this section as having an old-style call.  */
-             sec->has_tls_get_addr_call = 1;
-           break;
-         }
+         /* STT_GNU_IFUNC symbols must have a PLT entry.  */
+         if (h != NULL)
+           {
+             if (h->type == STT_GNU_IFUNC)
+               {
+                 h->needs_plt = 1;
+                 ifunc = &h->plt.plist;
+               }
+           }
+         else
+           {
+             Elf_Internal_Sym *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)
+               {
+                 ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                                rel->r_addend, PLT_IFUNC);
+                 if (ifunc == NULL)
+                   return FALSE;
+               }
+           }
+         if (ifunc != NULL
+             && !update_plt_info (abfd, ifunc, rel->r_addend))
+           return FALSE;
+       }
 
       switch (r_type)
        {
@@ -4829,9 +4912,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              return FALSE;
            }
          else
-           if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h,
-                                 rel->r_addend))
-             return FALSE;
+           {
+             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+               return FALSE;
+             h->needs_plt = 1;
+             if (h->root.root.string[0] == '.'
+                 && h->root.root.string[1] != '\0')
+               ((struct ppc_link_hash_entry *) h)->is_func = 1;
+           }
          break;
 
          /* The following relocations don't need to propagate the
@@ -4914,13 +5002,16 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* Fall through.  */
 
        case R_PPC64_REL24:
-         if (h != NULL)
+         if (h != NULL && ifunc == NULL)
            {
              /* We may need a .plt entry if the function this reloc
                 refers to is in a shared lib.  */
-             if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h,
-                                   rel->r_addend))
+             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
                return FALSE;
+             h->needs_plt = 1;
+             if (h->root.root.string[0] == '.'
+                 && h->root.root.string[1] != '\0')
+               ((struct ppc_link_hash_entry *) h)->is_func = 1;
              if (h == tga || h == dottga)
                sec->has_tls_reloc = 1;
            }
@@ -5525,6 +5616,38 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
              }
        }
 
+      if (is_branch_reloc (r_type))
+       {
+         struct plt_entry **ifunc = NULL;
+         if (h != NULL)
+           {
+             if (h->type == STT_GNU_IFUNC)
+               ifunc = &h->plt.plist;
+           }
+         else if (local_got_ents != NULL)
+           {
+             struct plt_entry **local_plt = (struct plt_entry **)
+               (local_got_ents + symtab_hdr->sh_info);
+             char *local_got_tls_masks = (char *)
+               (local_plt + symtab_hdr->sh_info);
+             if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
+               ifunc = local_plt + r_symndx;
+           }
+         if (ifunc != NULL)
+           {
+             struct plt_entry *ent;
+
+             for (ent = *ifunc; ent != NULL; ent = ent->next)
+               if (ent->addend == rel->r_addend)
+                 break;
+             if (ent == NULL)
+               abort ();
+             if (ent->plt.refcount > 0)
+               ent->plt.refcount -= 1;
+             continue;
+           }
+       }
+
       switch (r_type)
        {
        case R_PPC64_GOT_TLSLD16:
@@ -6039,6 +6162,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   /* Deal with function syms.  */
   if (h->type == STT_FUNC
+      || h->type == STT_GNU_IFUNC
       || h->needs_plt)
     {
       /* Clear procedure linkage table information for any symbol that
@@ -6048,9 +6172,10 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
        if (ent->plt.refcount > 0)
          break;
       if (ent == NULL
-         || SYMBOL_CALLS_LOCAL (info, h)
-         || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
-             && h->root.type == bfd_link_hash_undefweak))
+         || (h->type != STT_GNU_IFUNC
+             && (SYMBOL_CALLS_LOCAL (info, h)
+                 || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+                     && h->root.type == bfd_link_hash_undefweak))))
        {
          h->plt.plist = NULL;
          h->needs_plt = 0;
@@ -6301,7 +6426,10 @@ get_sym_h (struct elf_link_hash_entry **hp,
          lgot_ents = elf_local_got_ents (ibfd);
          if (lgot_ents != NULL)
            {
-             char *lgot_masks = (char *) (lgot_ents + symtab_hdr->sh_info);
+             struct plt_entry **local_plt = (struct plt_entry **)
+               (lgot_ents + symtab_hdr->sh_info);
+             char *lgot_masks = (char *)
+               (local_plt + symtab_hdr->sh_info);
              tls_mask = &lgot_masks[r_symndx];
            }
          *tls_maskp = tls_mask;
@@ -6983,15 +7111,7 @@ branch_reloc_hash_match (const bfd *ibfd,
   enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
   unsigned int r_symndx = ELF64_R_SYM (rel->r_info);
 
-  if (r_symndx >= symtab_hdr->sh_info
-      && (r_type == R_PPC64_REL24
-         || r_type == R_PPC64_REL14
-         || r_type == R_PPC64_REL14_BRTAKEN
-         || r_type == R_PPC64_REL14_BRNTAKEN
-         || r_type == R_PPC64_ADDR24
-         || r_type == R_PPC64_ADDR14
-         || r_type == R_PPC64_ADDR14_BRTAKEN
-         || r_type == R_PPC64_ADDR14_BRNTAKEN))
+  if (r_symndx >= symtab_hdr->sh_info && is_branch_reloc (r_type))
     {
       struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
       struct elf_link_hash_entry *h;
@@ -7860,37 +7980,48 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   info = (struct bfd_link_info *) inf;
   htab = ppc_hash_table (info);
 
-  if (htab->elf.dynamic_sections_created
-      && h->dynindx != -1
-      && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h))
+  if ((htab->elf.dynamic_sections_created
+       && h->dynindx != -1
+       && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h))
+      || h->type == STT_GNU_IFUNC)
     {
       struct plt_entry *pent;
       bfd_boolean doneone = FALSE;
       for (pent = h->plt.plist; pent != NULL; pent = pent->next)
        if (pent->plt.refcount > 0)
          {
-           /* If this is the first .plt entry, make room for the special
-              first entry.  */
-           s = htab->plt;
-           if (s->size == 0)
-             s->size += PLT_INITIAL_ENTRY_SIZE;
-
-           pent->plt.offset = s->size;
-
-           /* Make room for this entry.  */
-           s->size += PLT_ENTRY_SIZE;
-
-           /* Make room for the .glink code.  */
-           s = htab->glink;
-           if (s->size == 0)
-             s->size += GLINK_CALL_STUB_SIZE;
-           /* We need bigger stubs past index 32767.  */
-           if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
-             s->size += 4;
-           s->size += 2*4;
-
-           /* We also need to make an entry in the .rela.plt section.  */
-           s = htab->relplt;
+           if (!htab->elf.dynamic_sections_created)
+             {
+               s = htab->iplt;
+               pent->plt.offset = s->size;
+               s->size += PLT_ENTRY_SIZE;
+               s = htab->reliplt;
+             }
+           else
+             {
+               /* If this is the first .plt entry, make room for the special
+                  first entry.  */
+               s = htab->plt;
+               if (s->size == 0)
+                 s->size += PLT_INITIAL_ENTRY_SIZE;
+
+               pent->plt.offset = s->size;
+
+               /* Make room for this entry.  */
+               s->size += PLT_ENTRY_SIZE;
+
+               /* Make room for the .glink code.  */
+               s = htab->glink;
+               if (s->size == 0)
+                 s->size += GLINK_CALL_STUB_SIZE;
+               /* We need bigger stubs past index 32767.  */
+               if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+                 s->size += 4;
+               s->size += 2*4;
+
+               /* We also need to make an entry in the .rela.plt section.  */
+               s = htab->relplt;
+             }
            s->size += sizeof (Elf64_External_Rela);
            doneone = TRUE;
          }
@@ -8133,6 +8264,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
     {
       struct got_entry **lgot_ents;
       struct got_entry **end_lgot_ents;
+      struct plt_entry **local_plt;
+      struct plt_entry **end_local_plt;
       char *lgot_masks;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
@@ -8172,7 +8305,9 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       symtab_hdr = &elf_symtab_hdr (ibfd);
       locsymcount = symtab_hdr->sh_info;
       end_lgot_ents = lgot_ents + locsymcount;
-      lgot_masks = (char *) end_lgot_ents;
+      local_plt = (struct plt_entry **) end_lgot_ents;
+      end_local_plt = local_plt + locsymcount;
+      lgot_masks = (char *) end_local_plt;
       s = ppc64_elf_tdata (ibfd)->got;
       srel = ppc64_elf_tdata (ibfd)->relgot;
       for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
@@ -8207,6 +8342,25 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
            else
              ent->got.offset = (bfd_vma) -1;
        }
+
+      /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt.  */
+      for (; local_plt < end_local_plt; ++local_plt)
+       {
+         struct plt_entry *ent;
+
+         for (ent = *local_plt; ent != NULL; ent = ent->next)
+           if (ent->plt.refcount > 0)
+             {
+               asection *s = htab->iplt;
+
+               ent->plt.offset = s->size;
+               s->size += PLT_ENTRY_SIZE;
+
+               htab->reliplt += sizeof (Elf64_External_Rela);
+             }
+           else
+             ent->plt.offset = (bfd_vma) -1;
+       }
     }
 
   /* Allocate global sym .plt and .got entries, and space for global
@@ -8246,6 +8400,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        continue;
       else if (s == htab->got
               || s == htab->plt
+              || s == htab->iplt
               || s == htab->glink
               || s == htab->dynbss)
        {
@@ -8396,6 +8551,7 @@ static inline enum ppc_stub_type
 ppc_type_of_stub (asection *input_sec,
                  const Elf_Internal_Rela *rel,
                  struct ppc_link_hash_entry **hash,
+                 struct plt_entry **plt_ent,
                  bfd_vma destination)
 {
   struct ppc_link_hash_entry *h = *hash;
@@ -8406,23 +8562,20 @@ ppc_type_of_stub (asection *input_sec,
 
   if (h != NULL)
     {
+      struct plt_entry *ent;
       struct ppc_link_hash_entry *fdh = h;
       if (fdh->oh != NULL
          && fdh->oh->is_func_descriptor)
        fdh = fdh->oh;
 
-      if (fdh->elf.dynindx != -1)
-       {
-         struct plt_entry *ent;
-
-         for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next)
-           if (ent->addend == rel->r_addend
-               && ent->plt.offset != (bfd_vma) -1)
-             {
-               *hash = fdh;
-               return ppc_stub_plt_call;
-             }
-       }
+      for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next)
+       if (ent->addend == rel->r_addend
+           && ent->plt.offset != (bfd_vma) -1)
+         {
+           *hash = fdh;
+           *plt_ent = ent;
+           return ppc_stub_plt_call;
+         }
 
       /* Here, we know we don't have a plt entry.  If we don't have a
         either a defined function descriptor or a defined entry symbol
@@ -8436,6 +8589,26 @@ ppc_type_of_stub (asection *input_sec,
               && h->elf.root.u.def.section->output_section != NULL))
        return ppc_stub_none;
     }
+  else if (elf_local_got_ents (input_sec->owner) != NULL)
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_sec->owner);
+      struct plt_entry **local_plt = (struct plt_entry **)
+       elf_local_got_ents (input_sec->owner) + symtab_hdr->sh_info;
+      unsigned long r_symndx = ELF64_R_SYM (rel->r_info);
+
+      if (local_plt[r_symndx] != NULL)
+       {
+         struct plt_entry *ent;
+
+         for (ent = local_plt[r_symndx]; ent != NULL; ent = ent->next)
+           if (ent->addend == rel->r_addend
+               && ent->plt.offset != (bfd_vma) -1)
+             {
+               *plt_ent = ent;
+               return ppc_stub_plt_call;
+             }
+       }
+    }
 
   /* Determine where the call point is.  */
   location = (input_sec->output_offset
@@ -8576,10 +8749,10 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_link_hash_table *htab;
   bfd_byte *loc;
   bfd_byte *p;
-  struct plt_entry *ent;
   bfd_vma dest, off;
   int size;
   Elf_Internal_Rela *r;
+  asection *plt;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -8835,7 +9008,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       /* Do the best we can for shared libraries built without
         exporting ".foo" for each "foo".  This can happen when symbol
         versioning scripts strip all bar a subset of symbols.  */
-      if (stub_entry->h->oh != NULL
+      if (stub_entry->h != NULL
+         && stub_entry->h->oh != NULL
          && stub_entry->h->oh->elf.root.type != bfd_link_hash_defined
          && stub_entry->h->oh->elf.root.type != bfd_link_hash_defweak)
        {
@@ -8850,29 +9024,51 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
 
       /* Now build the stub.  */
-      dest = (bfd_vma) -1;
-      for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next)
-       if (ent->addend == stub_entry->addend)
-         {
-           dest = ent->plt.offset;
-           break;
-         }
+      dest = stub_entry->plt_ent->plt.offset & ~1;
       if (dest >= (bfd_vma) -2)
        abort ();
 
-      dest &= ~ (bfd_vma) 1;
-      dest += (htab->plt->output_offset
-              + htab->plt->output_section->vma);
+      plt = htab->plt;
+      if (!htab->elf.dynamic_sections_created)
+       plt = htab->iplt;
+
+      dest += plt->output_offset + plt->output_section->vma;
+
+      if (stub_entry->h == NULL
+         && (stub_entry->plt_ent->plt.offset & 1) == 0)
+       {
+         Elf_Internal_Rela rela;
+         bfd_byte *rl;
+
+         rela.r_offset = dest;
+         rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+         rela.r_addend = (stub_entry->target_value
+                          + stub_entry->target_section->output_offset
+                          + stub_entry->target_section->output_section->vma);
+
+         if (!htab->elf.dynamic_sections_created)
+           rl = (htab->reliplt->contents
+                 + (stub_entry->plt_ent->plt.offset
+                    / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
+         else
+           rl = (htab->relplt->contents
+                 + ((stub_entry->plt_ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
+                    / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
+         bfd_elf32_swap_reloca_out (info->output_bfd, &rela, rl);
+         stub_entry->plt_ent->plt.offset |= 1;
+       }
 
       off = (dest
-            - elf_gp (htab->plt->output_section->owner)
+            - elf_gp (plt->output_section->owner)
             - htab->stub_group[stub_entry->id_sec->id].toc_off);
 
       if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
        {
          (*_bfd_error_handler)
            (_("linkage table error against `%s'"),
-            stub_entry->h->elf.root.root.string);
+            stub_entry->h != NULL
+            ? stub_entry->h->elf.root.root.string
+            : "<local sym>");
          bfd_set_error (bfd_error_bad_value);
          htab->stub_error = TRUE;
          return FALSE;
@@ -8961,19 +9157,16 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
   if (stub_entry->stub_type == ppc_stub_plt_call)
     {
-      struct plt_entry *ent;
-      off = (bfd_vma) -1;
-      for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next)
-       if (ent->addend == stub_entry->addend)
-         {
-           off = ent->plt.offset & ~(bfd_vma) 1;
-           break;
-         }
+      asection *plt;
+      off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
       if (off >= (bfd_vma) -2)
        abort ();
-      off += (htab->plt->output_offset
-             + htab->plt->output_section->vma
-             - elf_gp (htab->plt->output_section->owner)
+      plt = htab->plt;
+      if (!htab->elf.dynamic_sections_created)
+       plt = htab->iplt;
+      off += (plt->output_offset
+             + plt->output_section->vma
+             - elf_gp (plt->output_section->owner)
              - htab->stub_group[stub_entry->id_sec->id].toc_off);
 
       size = PLT_CALL_STUB_SIZE;
@@ -9647,7 +9840,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                  enum ppc_stub_type stub_type;
                  struct ppc_stub_hash_entry *stub_entry;
                  asection *sym_sec, *code_sec;
-                 bfd_vma sym_value;
+                 bfd_vma sym_value, code_value;
                  bfd_vma destination;
                  bfd_boolean ok_dest;
                  struct ppc_link_hash_entry *hash;
@@ -9657,6 +9850,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                  char *stub_name;
                  const asection *id_sec;
                  struct _opd_sec_data *opd;
+                 struct plt_entry *plt_ent;
 
                  r_type = ELF64_R_TYPE (irela->r_info);
                  r_indx = ELF64_R_SYM (irela->r_info);
@@ -9733,6 +9927,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                    }
 
                  code_sec = sym_sec;
+                 code_value = sym_value;
                  opd = get_opd_info (sym_sec);
                  if (opd != NULL)
                    {
@@ -9743,10 +9938,11 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                          long adjust = opd->adjust[sym_value / 8];
                          if (adjust == -1)
                            continue;
+                         code_value += adjust;
                          sym_value += adjust;
                        }
                      dest = opd_entry_value (sym_sec, sym_value,
-                                             &code_sec, &sym_value);
+                                             &code_sec, &code_value);
                      if (dest != (bfd_vma) -1)
                        {
                          destination = dest;
@@ -9756,14 +9952,15 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                                 entry.  */
                              hash->elf.root.type = bfd_link_hash_defweak;
                              hash->elf.root.u.def.section = code_sec;
-                             hash->elf.root.u.def.value = sym_value;
+                             hash->elf.root.u.def.value = code_value;
                            }
                        }
                    }
 
                  /* Determine what (if any) linker stub is needed.  */
+                 plt_ent = NULL;
                  stub_type = ppc_type_of_stub (section, irela, &hash,
-                                               destination);
+                                               &plt_ent, destination);
 
                  if (stub_type != ppc_stub_plt_call)
                    {
@@ -9836,9 +10033,18 @@ ppc64_elf_size_stubs (bfd *output_bfd,
                    }
 
                  stub_entry->stub_type = stub_type;
-                 stub_entry->target_value = sym_value;
-                 stub_entry->target_section = code_sec;
+                 if (stub_type != ppc_stub_plt_call)
+                   {
+                     stub_entry->target_value = code_value;
+                     stub_entry->target_section = code_sec;
+                   }
+                 else
+                   {
+                     stub_entry->target_value = sym_value;
+                     stub_entry->target_section = sym_sec;
+                   }
                  stub_entry->h = hash;
+                 stub_entry->plt_ent = plt_ent;
                  stub_entry->addend = irela->r_addend;
 
                  if (stub_entry->h != NULL)
@@ -9924,13 +10130,13 @@ ppc64_elf_toc (bfd *obfd)
   /* The TOC consists of sections .got, .toc, .tocbss, .plt in that
      order.  The TOC starts where the first of these sections starts.  */
   s = bfd_get_section_by_name (obfd, ".got");
-  if (s == NULL)
+  if (s == NULL || (s->flags & SEC_EXCLUDE) != 0)
     s = bfd_get_section_by_name (obfd, ".toc");
-  if (s == NULL)
+  if (s == NULL || (s->flags & SEC_EXCLUDE) != 0)
     s = bfd_get_section_by_name (obfd, ".tocbss");
-  if (s == NULL)
+  if (s == NULL || (s->flags & SEC_EXCLUDE) != 0)
     s = bfd_get_section_by_name (obfd, ".plt");
-  if (s == NULL)
+  if (s == NULL || (s->flags & SEC_EXCLUDE) != 0)
     {
       /* This may happen for
         o  references to TOC base (SYM@toc / TOC[tc0]) without a
@@ -9943,21 +10149,23 @@ ppc64_elf_toc (bfd *obfd)
       /* Look for a likely section.  We probably won't even be
         using TOCstart.  */
       for (s = obfd->sections; s != NULL; s = s->next)
-       if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY))
+       if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY
+                        | SEC_EXCLUDE))
            == (SEC_ALLOC | SEC_SMALL_DATA))
          break;
       if (s == NULL)
        for (s = obfd->sections; s != NULL; s = s->next)
-         if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA))
+         if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_EXCLUDE))
              == (SEC_ALLOC | SEC_SMALL_DATA))
            break;
       if (s == NULL)
        for (s = obfd->sections; s != NULL; s = s->next)
-         if ((s->flags & (SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
+         if ((s->flags & (SEC_ALLOC | SEC_READONLY | SEC_EXCLUDE))
+             == SEC_ALLOC)
            break;
       if (s == NULL)
        for (s = obfd->sections; s != NULL; s = s->next)
-         if ((s->flags & SEC_ALLOC) == SEC_ALLOC)
+         if ((s->flags & (SEC_ALLOC | SEC_EXCLUDE)) == SEC_ALLOC)
            break;
     }
 
@@ -10386,8 +10594,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        tls_mask = h->tls_mask;
       else if (local_got_ents != NULL)
        {
-         char *lgot_masks;
-         lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
+         struct plt_entry **local_plt = (struct plt_entry **)
+           (local_got_ents + symtab_hdr->sh_info);
+         char *lgot_masks = (char *)
+           (local_plt + symtab_hdr->sh_info);
          tls_mask = lgot_masks[r_symndx];
        }
       if (tls_mask == 0
@@ -11827,23 +12037,41 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
       {
        /* This symbol has an entry in the procedure linkage
           table.  Set it up.  */
+       asection *plt = htab->plt;
+       if (!htab->elf.dynamic_sections_created)
+         plt = htab->iplt;
 
-       if (htab->plt == NULL
-           || htab->relplt == NULL
-           || htab->glink == NULL)
-         abort ();
-
-       /* Create a JMP_SLOT reloc to inform the dynamic linker to
-          fill in the PLT entry.  */
-       rela.r_offset = (htab->plt->output_section->vma
-                        + htab->plt->output_offset
+       rela.r_offset = (plt->output_section->vma
+                        + plt->output_offset
                         + ent->plt.offset);
-       rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
-       rela.r_addend = ent->addend;
 
-       loc = htab->relplt->contents;
-       loc += ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE
-               * sizeof (Elf64_External_Rela));
+       if (!htab->elf.dynamic_sections_created
+           || h->dynindx == -1)
+         {
+           BFD_ASSERT (h->type == STT_GNU_IFUNC
+                       && h->def_regular
+                       && (h->root.type == bfd_link_hash_defined
+                           || h->root.type == bfd_link_hash_defweak));
+           rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+           rela.r_addend = (h->root.u.def.value
+                            + h->root.u.def.section->output_offset
+                            + h->root.u.def.section->output_section->vma
+                            + ent->addend);
+         }
+       else
+         {
+           rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
+           rela.r_addend = ent->addend;
+         }
+
+       if (!htab->elf.dynamic_sections_created)
+         loc = (htab->reliplt->contents
+                + (ent->plt.offset
+                   / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
+       else
+         loc = (htab->relplt->contents
+                + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
+                   / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
        bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
       }
 
index 640824e..3936b20 100644 (file)
@@ -1,3 +1,10 @@
+2009-07-10  Alan Modra  <amodra@bigpond.net.au>
+
+       * ppc.h (R_PPC_IRELATIVE): Add.
+       (R_PPC_RELAX32, R_PPC_RELAX32PC,
+       R_PPC_RELAX32_PLT, R_PPC_RELAX32PC_PLT): Renumber.
+       * ppc64.h (R_PPC64_IRELATIVE): Add.
+
 2009-07-03  Jakub Jelinek  <jakub@redhat.com>
 
        * dwarf2.h (enum dwarf_location_atom): Add DW_OP_implicit_value
index 8f4ed9e..14c643a 100644 (file)
@@ -71,6 +71,14 @@ START_RELOC_NUMBERS (elf_ppc_reloc_type)
   RELOC_NUMBER (R_PPC_SECTOFF_HA,       36)
   RELOC_NUMBER (R_PPC_ADDR30,           37)
 
+#ifndef RELOC_MACROS_GEN_FUNC
+/* Fake relocations for branch stubs, only used internally by ld.  */
+  RELOC_NUMBER (R_PPC_RELAX32,          48)
+  RELOC_NUMBER (R_PPC_RELAX32PC,        49)
+  RELOC_NUMBER (R_PPC_RELAX32_PLT,      50)
+  RELOC_NUMBER (R_PPC_RELAX32PC_PLT,    51)
+#endif
+
   /* Relocs added to support TLS.  */
   RELOC_NUMBER (R_PPC_TLS,              67)
   RELOC_NUMBER (R_PPC_DTPMOD32,                 68)
@@ -122,13 +130,8 @@ START_RELOC_NUMBERS (elf_ppc_reloc_type)
   RELOC_NUMBER (R_PPC_EMB_BIT_FLD,     115)
   RELOC_NUMBER (R_PPC_EMB_RELSDA,      116)
 
-#ifndef RELOC_MACROS_GEN_FUNC
-/* Fake relocations for branch stubs, only used internally by ld.  */
-  RELOC_NUMBER (R_PPC_RELAX32,         245)
-  RELOC_NUMBER (R_PPC_RELAX32PC,       246)
-  RELOC_NUMBER (R_PPC_RELAX32_PLT,     247)
-  RELOC_NUMBER (R_PPC_RELAX32PC_PLT,   248)
-#endif
+/* Support STT_GNU_IFUNC plt calls.  */
+  RELOC_NUMBER (R_PPC_IRELATIVE,       248)
 
 /* These are GNU extensions used in PIC code sequences.  */
   RELOC_NUMBER (R_PPC_REL16,           249)
index c768180..4644b75 100644 (file)
@@ -139,6 +139,9 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_type)
   RELOC_NUMBER (R_PPC64_TLSGD,            107)
   RELOC_NUMBER (R_PPC64_TLSLD,            108)
 
+/* Support STT_GNU_IFUNC plt calls.  */
+  RELOC_NUMBER (R_PPC64_IRELATIVE,        248)
+
   /* These are GNU extensions to enable C++ vtable garbage collection.  */
   RELOC_NUMBER (R_PPC64_GNU_VTINHERIT,    253)
   RELOC_NUMBER (R_PPC64_GNU_VTENTRY,      254)
index 551c5c1..5836c86 100644 (file)
@@ -1,3 +1,7 @@
+2009-07-10  Alan Modra  <amodra@bigpond.net.au>
+
+       * emulparams/elf32ppc.sh (GOTPLT, PLT): Handle .iplt.
+
 2009-07-06  Matthias Klose <doko@ubuntu.com>
 
         * ld.texinfo: Fix typo.
index 7349993..49cc950 100644 (file)
@@ -10,8 +10,9 @@ SDATA_GOT=
 SEPARATE_GOTPLT=0
 BSS_PLT=
 GOT=".got          ${RELOCATING-0} : SPECIAL { *(.got) }"
-PLT=".plt          ${RELOCATING-0} : SPECIAL { *(.plt) }"
-GOTPLT="${PLT}"
+GOTPLT=".plt          ${RELOCATING-0} : SPECIAL { *(.plt) }"
+PLT=".plt          ${RELOCATING-0} : SPECIAL { *(.plt) *(.iplt) }
+  .iplt         ${RELOCATING-0} : { *(.iplt) }"
 OTHER_TEXT_SECTIONS="*(.glink)"
 EXTRA_EM_FILE=ppc32elf
 if grep -q 'ld_elf32_spu_emulation' ldemul-list.h; then
index 5e4b710..be6e2e5 100644 (file)
@@ -1,3 +1,8 @@
+2009-07-10  Alan Modra  <amodra@bigpond.net.au>
+
+       * ld-ifunc/ifunc.exp: Run for powerpc.  Really generate static
+       executables, renaming the existing dynamic but local tests.
+
 2009-07-08  Alan Modra  <amodra@bigpond.net.au>
 
        * ld-selective/selective.exp: Remove check that $CC contains the
index 6840e4e..3320b82 100644 (file)
 # Written by Nick Clifton <nickc@redhat.com>
 
 
-# IFUNC support has only been implemented for the x86_64 and ix86 so far.
-if {! (  [istarget "x86_64-*-elf*"]
-      || [istarget "x86_64-*-linux*"]
-      || [istarget "i?86-*-elf*"]
-      || [istarget "i?86-*-linux*"]) } {
+# IFUNC support has only been implemented for the ix86, x86_64 and powerpc
+# so far.
+if {!(([istarget "i?86-*-*"]
+       || [istarget "x86_64-*-*"]
+       || [istarget "powerpc*-*-*"])
+      && ([istarget "*-*-elf*"]
+         || ([istarget "*-*-linux*"]
+             && ![istarget "*-*-*aout*"]
+             && ![istarget "*-*-*oldld*"]))) } {
     verbose "IFUNC tests not run - target does not support IFUNC"
     return
 }
@@ -195,11 +199,15 @@ if ![default_ld_link $ld "tmpdir/dynamic_prog" "-Ltmpdir tmpdir/shared_prog.o -B
     fail "Could not link a dynamic executable"
     set fails [expr $fails + 1]
 }
-if ![default_ld_link $ld "tmpdir/static_prog" "-Ltmpdir tmpdir/static_prog.o -lifunc"] {
+if ![default_ld_link $ld "tmpdir/local_prog" "-Ltmpdir tmpdir/static_prog.o -lifunc"] {
+    fail "Could not link a dynamic executable using local ifunc"
+    set fails [expr $fails + 1]
+}
+if ![default_ld_link $ld "tmpdir/static_prog" "-static -Ltmpdir tmpdir/static_prog.o -lifunc"] {
     fail "Could not link a static executable"
     set fails [expr $fails + 1]
 }
-if ![default_ld_link $ld "tmpdir/static_nonifunc_prog" "-Ltmpdir tmpdir/static_prog.o tmpdir/static_noifunc.o"] {
+if ![default_ld_link $ld "tmpdir/static_nonifunc_prog" "-static -Ltmpdir tmpdir/static_prog.o tmpdir/static_noifunc.o"] {
     fail "Could not link a non-ifunc using static executable"
     set fails [expr $fails + 1]
 }
@@ -221,6 +229,10 @@ if {! [check_osabi tmpdir/libshared_ifunc.so {UNIX - Linux}]} {
     fail "Shared libraries containing ifunc does not have an OS/ABI field of LINUX"
     set fails [expr $fails + 1]
 }
+if {! [check_osabi tmpdir/local_prog {UNIX - Linux}]} {
+    fail "Local ifunc-using executable does not have an OS/ABI field of LINUX"
+    set fails [expr $fails + 1]
+}
 if {! [check_osabi tmpdir/static_prog {UNIX - Linux}]} {
     fail "Static ifunc-using executable does not have an OS/ABI field of LINUX"
     set fails [expr $fails + 1]
@@ -242,6 +254,10 @@ if {[contains_ifunc_symbol tmpdir/libshared_ifunc.so] != 1} {
     fail "Shared libraries containing ifunc does not contain an IFUNC symbol"
     set fails [expr $fails + 1]
 }
+if {[contains_ifunc_symbol tmpdir/local_prog] != 1} {
+    fail "Local ifunc-using executable does not contain an IFUNC symbol"
+    set fails [expr $fails + 1]
+}
 if {[contains_ifunc_symbol tmpdir/static_prog] != 1} {
     fail "Static ifunc-using executable does not contain an IFUNC symbol"
     set fails [expr $fails + 1]
@@ -264,6 +280,10 @@ if {[contains_irelative_reloc tmpdir/libshared_ifunc.so] != 1} {
     fail "ifunc-using shared library does not contain R_*_IRELATIVE relocation"
     set fails [expr $fails + 1]
 }
+if {[contains_irelative_reloc tmpdir/local_prog] != 1} {
+    fail "Local ifunc-using executable does not contain R_*_IRELATIVE relocation"
+    set fails [expr $fails + 1]
+}
 if {[contains_irelative_reloc tmpdir/static_prog] != 1} {
     fail "Static ifunc-using executable does not contain R_*_IRELATIVE relocation"
     set fails [expr $fails + 1]
@@ -291,6 +311,7 @@ if { $verbose < 1 } {
     remote_file host delete "tmpdir/libshared_ifunc.so"
     remote_file host delete "tmpdir/libifunc.a"
     remote_file host delete "tmpdir/dynamic_prog"
+    remote_file host delete "tmpdir/local_prog"
     remote_file host delete "tmpdir/static_prog"
     remote_file host delete "tmpdir/static_nonifunc_prog"
 }