* elf32-ppc.c (ppc_elf_check_relocs): Always add a plt ref count
authorAlan Modra <amodra@gmail.com>
Mon, 3 Aug 2009 10:23:18 +0000 (10:23 +0000)
committerAlan Modra <amodra@gmail.com>
Mon, 3 Aug 2009 10:23:18 +0000 (10:23 +0000)
for local ifunc symbols in non-pie executables, regardless of
reloc type.  Don't specially create ifunc dyn relocs.  Tidy ifunc
code so that it's obvious that we only do anything special for
local ifunc syms.
(ppc_elf_gc_sweep_hook): Adjust to suit check_relocs changes.
(allocate_dynrelocs): Correct comment for syms defined in plt.
Don't specially allocate ifunc dyn relocs.
(ppc_elf_relax_section): Relax branches to ifunc plt entries too.
(ppc_elf_relocate_section): Set "relocation" value for ifunc
syms in non-pie executables.  No specially allocated dyn relocs
for ifunc to write.  Allow for local sym on R_PPC_RELAX32_PLT.
(ppc_elf_finish_dynamic_symbol): Set value of ifunc symbols in
a non-pie executable.

bfd/ChangeLog
bfd/elf32-ppc.c

index 51ae9e6e9b6de5e0abc31502ff4c720e0b6b0caf..4280a0ebe6c98c73819324d8cf9ce6af2da89f01 100644 (file)
@@ -1,3 +1,20 @@
+2009-08-03  Alan Modra  <amodra@bigpond.net.au>
+
+       * elf32-ppc.c (ppc_elf_check_relocs): Always add a plt ref count
+       for local ifunc symbols in non-pie executables, regardless of
+       reloc type.  Don't specially create ifunc dyn relocs.  Tidy ifunc
+       code so that it's obvious that we only do anything special for
+       local ifunc syms.
+       (ppc_elf_gc_sweep_hook): Adjust to suit check_relocs changes.
+       (allocate_dynrelocs): Correct comment for syms defined in plt.
+       Don't specially allocate ifunc dyn relocs.
+       (ppc_elf_relax_section): Relax branches to ifunc plt entries too.
+       (ppc_elf_relocate_section): Set "relocation" value for ifunc
+       syms in non-pie executables.  No specially allocated dyn relocs
+       for ifunc to write.  Allow for local sym on R_PPC_RELAX32_PLT.
+       (ppc_elf_finish_dynamic_symbol): Set value of ifunc symbols in
+       a non-pie executable.
+
 2009-08-02  H.J. Lu  <hongjiu.lu@intel.com>
            Jakub Jelinek  <jakub@redhat.com>
 
index d1e2cfced561857e41a31cf7e672c69b6d823673..2fbf01b34e5fd8671f3e128455c566899aaf7f6e 100644 (file)
@@ -3458,15 +3458,13 @@ ppc_elf_check_relocs (bfd *abfd,
 
       tls_type = 0;
       ifunc = NULL;
+      r_type = ELF32_R_TYPE (rel->r_info);
       if (!htab->is_vxworks)
        {
          if (h != NULL)
            {
              if (h->type == STT_GNU_IFUNC)
-               {
-                 h->needs_plt = 1;
-                 ifunc = &h->plt.plist;
-               }
+               ifunc = &h->plt.plist;
            }
          else
            {
@@ -3475,46 +3473,47 @@ ppc_elf_check_relocs (bfd *abfd,
              if (isym == NULL)
                return FALSE;
 
-             if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+             if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+                 && (!info->shared
+                     || is_branch_reloc (r_type)))
                {
+                 bfd_vma addend;
+
                  ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                                 PLT_IFUNC);
                  if (ifunc == NULL)
                    return FALSE;
+
+                 /* STT_GNU_IFUNC symbols must have a PLT entry;
+                    In a non-pie executable even when there are
+                    no plt calls.  */
+                 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;
                }
            }
        }
 
-      r_type = ELF32_R_TYPE (rel->r_info);
-      if (!htab->is_vxworks && is_branch_reloc (r_type))
+      if (!htab->is_vxworks
+         && is_branch_reloc (r_type)
+         && h != NULL
+         && h == tga)
        {
-         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;
-           }
-
-         /* STT_GNU_IFUNC symbols must have a PLT entry.  */
-         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 (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;
        }
 
       switch (r_type)
@@ -3690,7 +3689,7 @@ ppc_elf_check_relocs (bfd *abfd,
          break;
 
        case R_PPC_PLTREL24:
-         if (h == NULL || ifunc != NULL)
+         if (h == NULL)
            break;
          /* Fall through */
        case R_PPC_PLT32:
@@ -3903,8 +3902,7 @@ 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.  */
              h->needs_plt = 1;
-             if (ifunc == NULL
-                 && !update_plt_info (abfd, &h->plt.plist, NULL, 0))
+             if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
                return FALSE;
              break;
            }
@@ -3941,9 +3939,7 @@ ppc_elf_check_relocs (bfd *abfd,
                  && !info->shared
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
-                     || !h->def_regular))
-             || (!info->shared
-                 && ifunc != NULL))
+                     || !h->def_regular)))
            {
              struct ppc_elf_dyn_relocs *p;
              struct ppc_elf_dyn_relocs **head;
@@ -4415,25 +4411,19 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
        }
 
       r_type = ELF32_R_TYPE (rel->r_info);
-      if (!htab->is_vxworks && is_branch_reloc (r_type))
+      if (!htab->is_vxworks
+         && h == NULL
+         && local_got_refcounts != NULL
+         && (!info->shared
+             || 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)
+         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)
            {
+             struct plt_entry **ifunc = local_plt + r_symndx;
              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)
@@ -5166,8 +5156,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
                        /* If this symbol is not defined in a regular
                           file, and we are not generating a shared
-                          library, then set the symbol to this location
-                          in the .plt.  This is required to make
+                          library, then set the symbol to this location 
+                          in the .plt.  This is to avoid text
+                          relocations, and is required to make
                           function pointers compare as equal between
                           the normal executable and the shared library.  */
                        if (! info->shared
@@ -5313,8 +5304,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     eh->elf.got.offset = (bfd_vma) -1;
 
   if (eh->dyn_relocs == NULL
-      || (!htab->elf.dynamic_sections_created
-         && h->type != STT_GNU_IFUNC))
+      || !htab->elf.dynamic_sections_created)
     return TRUE;
 
   /* In the shared -Bsymbolic case, discard space allocated for
@@ -5385,11 +5375,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            }
        }
     }
-  else if (h->type == STT_GNU_IFUNC)
-    {
-      if (!h->non_got_ref)
-       eh->dyn_relocs = NULL;
-    }
   else if (ELIMINATE_COPY_RELOCS)
     {
       /* For the non-shared case, discard space for relocs against
@@ -5938,6 +5923,7 @@ ppc_elf_relax_section (bfd *abfd,
       bfd_vma max_branch_offset, val;
       bfd_byte *hit_addr;
       unsigned long t0;
+      struct elf_link_hash_entry *h;
       unsigned char sym_type;
 
       switch (r_type)
@@ -5959,6 +5945,7 @@ ppc_elf_relax_section (bfd *abfd,
        }
 
       /* Get the value of the symbol referred to by the reloc.  */
+      h = NULL;
       if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
        {
          /* A local symbol.  */
@@ -5992,7 +5979,6 @@ ppc_elf_relax_section (bfd *abfd,
        {
          /* Global symbol handling.  */
          unsigned long indx;
-         struct elf_link_hash_entry *h;
 
          indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
          h = elf_sym_hashes (abfd)[indx];
@@ -6003,26 +5989,6 @@ ppc_elf_relax_section (bfd *abfd,
 
          tsec = NULL;
          toff = 0;
-         if (r_type == R_PPC_PLTREL24
-             && htab->plt != NULL)
-           {
-             struct plt_entry *ent = find_plt_ent (&h->plt.plist,
-                                                   got2, irel->r_addend);
-
-             if (ent != NULL)
-               {
-                 if (htab->plt_type == PLT_NEW)
-                   {
-                     tsec = htab->glink;
-                     toff = ent->glink_offset;
-                   }
-                 else
-                   {
-                     tsec = htab->plt;
-                     toff = ent->plt.offset;
-                   }
-               }
-           }
          if (tsec != NULL)
            ;
          else if (h->root.type == bfd_link_hash_defined
@@ -6043,6 +6009,46 @@ ppc_elf_relax_section (bfd *abfd,
          sym_type = h->type;
        }
 
+      if (is_branch_reloc (r_type))
+       {
+         struct plt_entry **plist = NULL;
+
+         if (h != NULL)
+           plist = &h->plt.plist;
+         else if (sym_type == STT_GNU_IFUNC)
+           {
+             bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
+             struct plt_entry **local_plt = (struct plt_entry **)
+               (local_got_offsets + symtab_hdr->sh_info);
+             plist = local_plt + ELF32_R_SYM (irel->r_info);
+           }
+         if (plist != NULL)
+           {
+             bfd_vma addend = 0;
+             struct plt_entry *ent;
+
+             if (r_type == R_PPC_PLTREL24)
+               addend = irel->r_addend;
+             ent = find_plt_ent (plist, got2, addend);
+             if (ent != NULL)
+               {
+                 if (htab->plt_type == PLT_NEW
+                     || h == NULL
+                     || !htab->elf.dynamic_sections_created
+                     || h->dynindx == -1)
+                   {
+                     tsec = htab->glink;
+                     toff = ent->glink_offset;
+                   }
+                 else
+                   {
+                     tsec = htab->plt;
+                     toff = ent->plt.offset;
+                   }
+               }
+           }
+       }
+
       /* If the branch and target are in the same section, you have
         no hope of adding stubs.  We'll error out later should the
         branch overflow.  */
@@ -6940,25 +6946,35 @@ ppc_elf_relocate_section (bfd *output_bfd,
       ifunc = NULL;
       if (!htab->is_vxworks)
        {
+         struct plt_entry *ent;
+
          if (h != NULL)
            {
              if (h->type == STT_GNU_IFUNC)
                ifunc = &h->plt.plist;
            }
-         else if (local_got_offsets != NULL)
+         else if (local_got_offsets != NULL
+                  && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
            {
-             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);
+             struct plt_entry **local_plt;
 
-                 ifunc = local_plt + r_symndx;
-               }
+             local_plt = (struct plt_entry **) (local_got_offsets
+                                                + symtab_hdr->sh_info);
+             ifunc = local_plt + r_symndx;
            }
-         if (ifunc != NULL && is_branch_reloc (r_type))
-           {
-             struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend);
 
+         ent = NULL;
+         if (ifunc != NULL
+             && (!info->shared
+                 || is_branch_reloc (r_type)))
+           {
+             addend = 0;
+             if (r_type == R_PPC_PLTREL24)
+               addend = rel->r_addend;
+             ent = find_plt_ent (ifunc, got2, addend);
+           }
+         if (ent != NULL)
+           {
              if (h == NULL && (ent->plt.offset & 1) == 0)
                {
                  Elf_Internal_Rela rela;
@@ -7385,9 +7401,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  && h != NULL
                  && h->dynindx != -1
                  && !h->non_got_ref
-                 && !h->def_regular)
-             || (!info->shared
-                 && ifunc != NULL))
+                 && !h->def_regular))
            {
              int skip;
 
@@ -7526,18 +7540,19 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_RELAX32PC_PLT:
        case R_PPC_RELAX32_PLT:
-         {
-           struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2, addend);
-
-           if (htab->plt_type == PLT_NEW)
-             relocation = (htab->glink->output_section->vma
-                           + htab->glink->output_offset
-                           + ent->glink_offset);
-           else
-             relocation = (htab->plt->output_section->vma
-                           + htab->plt->output_offset
-                           + ent->plt.offset);
-         }
+         if (h != NULL)
+           {
+             struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
+                                                   addend);
+             if (htab->plt_type == PLT_NEW)
+               relocation = (htab->glink->output_section->vma
+                             + htab->glink->output_offset
+                             + ent->glink_offset);
+             else
+               relocation = (htab->plt->output_section->vma
+                             + htab->plt->output_offset
+                             + ent->plt.offset);
+           }
          if (r_type == R_PPC_RELAX32_PLT)
            goto relax32;
          /* Fall thru */
@@ -8164,6 +8179,22 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                    sym->st_value = 0;
                  }
              }
+           else if (h->type == STT_GNU_IFUNC
+                    && !info->shared)
+             {
+               /* Set the value of ifunc symbols in a non-pie
+                  executable to the glink entry.  This is to avoid
+                  text relocations.  We can't do this for ifunc in
+                  allocate_dynrelocs, as we do for normal dynamic
+                  function symbols with plt entries, because we need
+                  to keep the original value around for the ifunc
+                  relocation.  */
+               sym->st_shndx = (_bfd_elf_section_from_bfd_section
+                                (output_bfd, htab->glink->output_section));
+               sym->st_value = (ent->glink_offset +
+                                htab->glink->output_offset
+                                + htab->glink->output_section->vma);
+             }
            doneone = TRUE;
          }