Automatic date update in version.in
[deliverable/binutils-gdb.git] / bfd / elf32-ppc.c
index d24b095a543b6935c13d54847b8f28e025b505c7..0438af4e2a741bd151b3c8c541c14a11215b838c 100644 (file)
@@ -818,6 +818,35 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0,                     /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  /* Marker relocs on inline plt call instructions.  */
+  HOWTO (R_PPC_PLTSEQ,
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_PLTSEQ",        /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_PPC_PLTCALL,
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_PLTCALL",       /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   /* Computes the load module index of the load module that contains the
      definition of its TLS sym.  */
   HOWTO (R_PPC_DTPMOD32,
@@ -3248,6 +3277,7 @@ struct ppc_elf_link_hash_entry
   /* The above field is also used to mark function symbols.  In which
      case TLS_TLS will be 0.  */
 #define PLT_IFUNC       2      /* STT_GNU_IFUNC.  */
+#define PLT_KEEP        4      /* inline plt call requires plt entry.  */
 #define NON_GOT        256     /* local symbol plt, not stored.  */
 
   /* Nonzero if we have seen a small data relocation referring to this
@@ -3277,6 +3307,8 @@ struct ppc_elf_link_hash_table
   elf_linker_section_t sdata[2];
   asection *sbss;
   asection *glink_eh_frame;
+  asection *pltlocal;
+  asection *relpltlocal;
 
   /* The (unloaded but important) .rela.plt.unloaded on VxWorks.  */
   asection *srelplt2;
@@ -3315,6 +3347,9 @@ struct ppc_elf_link_hash_table
   /* Set if tls optimization is enabled.  */
   unsigned int do_tls_opt:1;
 
+  /* Set if inline plt calls should be converted to direct calls.  */
+  unsigned int can_convert_all_inline_plt:1;
+
   /* The size of PLT entries.  */
   int plt_entry_size;
   /* The distance between adjacent PLT slots.  */
@@ -3335,6 +3370,9 @@ struct ppc_elf_link_hash_table
 /* Nonzero if this section has a call to __tls_get_addr.  */
 #define has_tls_get_addr_call sec_flg1
 
+  /* Flag set when PLTCALL relocs are detected.  */
+#define has_pltcall sec_flg2
+
 /* Get the PPC ELF linker hash table from a link_info structure.  */
 
 #define ppc_elf_hash_table(p) \
@@ -3526,6 +3564,26 @@ ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
       || ! bfd_set_section_alignment (abfd, s, 2))
     return FALSE;
 
+  /* Local plt entries.  */
+  flags = (SEC_ALLOC | SEC_LOAD
+          | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  htab->pltlocal = bfd_make_section_anyway_with_flags (abfd, ".branch_lt",
+                                                      flags);
+  if (htab->pltlocal == NULL
+      || ! bfd_set_section_alignment (abfd, htab->pltlocal, 2))
+    return FALSE;
+
+  if (bfd_link_pic (info))
+    {
+      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+              | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      htab->relpltlocal
+       = bfd_make_section_anyway_with_flags (abfd, ".rela.branch_lt", flags);
+      if (htab->relpltlocal == NULL
+         || ! bfd_set_section_alignment (abfd, htab->relpltlocal, 2))
+       return FALSE;
+    }
+
   if (!ppc_elf_create_linker_section (abfd, info, 0,
                                      &htab->sdata[0]))
     return FALSE;
@@ -3933,6 +3991,17 @@ is_branch_reloc (enum elf_ppc_reloc_type r_type)
          || r_type == R_PPC_VLE_REL24);
 }
 
+/* Relocs on inline plt call sequence insns prior to the call.  */
+
+static bfd_boolean
+is_plt_seq_reloc (enum elf_ppc_reloc_type r_type)
+{
+  return (r_type == R_PPC_PLT16_HA
+         || r_type == R_PPC_PLT16_HI
+         || r_type == R_PPC_PLT16_LO
+         || r_type == R_PPC_PLTSEQ);
+}
+
 static void
 bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
 {
@@ -4008,6 +4077,8 @@ ppc_elf_check_relocs (bfd *abfd,
       struct elf_link_hash_entry *h;
       int tls_type;
       struct plt_entry **ifunc;
+      struct plt_entry **pltent;
+      bfd_vma addend;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -4061,7 +4132,7 @@ ppc_elf_check_relocs (bfd *abfd,
                  || r_type == R_PPC_PLT16_HI
                  || r_type == R_PPC_PLT16_HA)
                {
-                 bfd_vma addend = 0;
+                 addend = 0;
                  if (r_type == R_PPC_PLTREL24)
                    ppc_elf_tdata (abfd)->makes_plt_call = 1;
                  if (bfd_link_pic (info)
@@ -4106,6 +4177,9 @@ ppc_elf_check_relocs (bfd *abfd,
              return FALSE;
          break;
 
+       case R_PPC_PLTSEQ:
+         break;
+
        case R_PPC_GOT_TLSLD16:
        case R_PPC_GOT_TLSLD16_LO:
        case R_PPC_GOT_TLSLD16_HI:
@@ -4283,46 +4357,45 @@ ppc_elf_check_relocs (bfd *abfd,
          if (h == NULL)
            break;
          ppc_elf_tdata (abfd)->makes_plt_call = 1;
-         /* Fall through */
+         goto pltentry;
+
+       case R_PPC_PLTCALL:
+         sec->has_pltcall = 1;
+         /* Fall through.  */
 
        case R_PPC_PLT32:
        case R_PPC_PLTREL32:
        case R_PPC_PLT16_LO:
        case R_PPC_PLT16_HI:
        case R_PPC_PLT16_HA:
+       pltentry:
 #ifdef DEBUG
          fprintf (stderr, "Reloc requires a PLT entry\n");
 #endif
          /* This symbol requires a procedure linkage table entry.  */
          if (h == NULL)
            {
-             if (ifunc == NULL)
-               {
-                 /* It does not make sense to have a procedure linkage
-                    table entry for a non-ifunc local symbol.  */
-                 info->callbacks->einfo
-                   /* xgettext:c-format */
-                   (_("%H: %s reloc against local symbol\n"),
-                    abfd, sec, rel->r_offset,
-                    ppc_elf_howto_table[r_type]->name);
-                 bfd_set_error (bfd_error_bad_value);
-                 return FALSE;
-               }
+             pltent = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+                                             NON_GOT | PLT_KEEP);
+             if (pltent == NULL)
+               return FALSE;
            }
          else
            {
-             bfd_vma addend = 0;
-
-             if (bfd_link_pic (info)
-                 && (r_type == R_PPC_PLTREL24
-                     || r_type == R_PPC_PLT16_LO
-                     || r_type == R_PPC_PLT16_HI
-                     || r_type == R_PPC_PLT16_HA))
-               addend = rel->r_addend;
+             if (r_type != R_PPC_PLTREL24)
+               ppc_elf_hash_entry (h)->tls_mask |= PLT_KEEP;
              h->needs_plt = 1;
-             if (!update_plt_info (abfd, &h->plt.plist, got2, addend))
-               return FALSE;
+             pltent = &h->plt.plist;
            }
+         addend = 0;
+         if (bfd_link_pic (info)
+             && (r_type == R_PPC_PLTREL24
+                 || r_type == R_PPC_PLT16_LO
+                 || r_type == R_PPC_PLT16_HI
+                 || r_type == R_PPC_PLT16_HA))
+           addend = rel->r_addend;
+         if (!update_plt_info (abfd, pltent, got2, addend))
+           return FALSE;
          break;
 
          /* The following relocations don't need to propagate the
@@ -5173,6 +5246,141 @@ get_sym_h (struct elf_link_hash_entry **hp,
   return TRUE;
 }
 \f
+/* Analyze inline PLT call relocations to see whether calls to locally
+   defined functions can be converted to direct calls.  */
+
+bfd_boolean
+ppc_elf_inline_plt (struct bfd_link_info *info)
+{
+  struct ppc_elf_link_hash_table *htab;
+  bfd *ibfd;
+  asection *sec;
+  bfd_vma low_vma, high_vma, limit;
+
+  htab = ppc_elf_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  /* A bl insn can reach -0x2000000 to 0x1fffffc.  The limit is
+     reduced somewhat to cater for possible stubs that might be added
+     between the call and its destination.  */
+  limit = 0x1e00000;
+  low_vma = -1;
+  high_vma = 0;
+  for (sec = info->output_bfd->sections; sec != NULL; sec = sec->next)
+    if ((sec->flags & (SEC_ALLOC | SEC_CODE)) == (SEC_ALLOC | SEC_CODE))
+      {
+       if (low_vma > sec->vma)
+         low_vma = sec->vma;
+       if (high_vma < sec->vma + sec->size)
+         high_vma = sec->vma + sec->size;
+      }
+
+  /* If a "bl" can reach anywhere in local code sections, then we can
+     convert all inline PLT sequences to direct calls when the symbol
+     is local.  */
+  if (high_vma - low_vma < limit)
+    {
+      htab->can_convert_all_inline_plt = 1;
+      return TRUE;
+    }
+
+  /* Otherwise, go looking through relocs for cases where a direct
+     call won't reach.  Mark the symbol on any such reloc to disable
+     the optimization and keep the PLT entry as it seems likely that
+     this will be better than creating trampolines.  Note that this
+     will disable the optimization for all inline PLT calls to a
+     particular symbol, not just those that won't reach.  The
+     difficulty in doing a more precise optimization is that the
+     linker needs to make a decision depending on whether a
+     particular R_PPC_PLTCALL insn can be turned into a direct
+     call, for each of the R_PPC_PLTSEQ and R_PPC_PLT16* insns in
+     the sequence, and there is nothing that ties those relocs
+     together except their symbol.  */
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Sym *local_syms;
+
+      if (!is_ppc_elf (ibfd))
+       continue;
+
+      local_syms = NULL;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       if (sec->has_pltcall
+           && !bfd_is_abs_section (sec->output_section))
+         {
+           Elf_Internal_Rela *relstart, *rel, *relend;
+
+           /* Read the relocations.  */
+           relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                                 info->keep_memory);
+           if (relstart == NULL)
+             return FALSE;
+
+           relend = relstart + sec->reloc_count;
+           for (rel = relstart; rel < relend; )
+             {
+               enum elf_ppc_reloc_type r_type;
+               unsigned long r_symndx;
+               asection *sym_sec;
+               struct elf_link_hash_entry *h;
+               Elf_Internal_Sym *sym;
+               unsigned char *tls_maskp;
+
+               r_type = ELF32_R_TYPE (rel->r_info);
+               if (r_type != R_PPC_PLTCALL)
+                 continue;
+
+               r_symndx = ELF32_R_SYM (rel->r_info);
+               if (!get_sym_h (&h, &sym, &sym_sec, &tls_maskp, &local_syms,
+                               r_symndx, ibfd))
+                 {
+                   if (elf_section_data (sec)->relocs != relstart)
+                     free (relstart);
+                   if (local_syms != NULL
+                       && symtab_hdr->contents != (unsigned char *) local_syms)
+                     free (local_syms);
+                   return FALSE;
+                 }
+
+               if (sym_sec != NULL && sym_sec->output_section != NULL)
+                 {
+                   bfd_vma from, to;
+                   if (h != NULL)
+                     to = h->root.u.def.value;
+                   else
+                     to = sym->st_value;
+                   to += (rel->r_addend
+                          + sym_sec->output_offset
+                          + sym_sec->output_section->vma);
+                   from = (rel->r_offset
+                           + sec->output_offset
+                           + sec->output_section->vma);
+                   if (to - from + limit < 2 * limit)
+                     *tls_maskp &= ~PLT_KEEP;
+                 }
+             }
+           if (elf_section_data (sec)->relocs != relstart)
+             free (relstart);
+         }
+
+      if (local_syms != NULL
+         && symtab_hdr->contents != (unsigned char *) local_syms)
+       {
+         if (!info->keep_memory)
+           free (local_syms);
+         else
+           symtab_hdr->contents = (unsigned char *) local_syms;
+       }
+    }
+
+  return TRUE;
+}
+
 /* Set plt output section type, htab->tls_get_addr, and call the
    generic ELF tls_setup function.  */
 
@@ -5418,6 +5626,40 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
 
                    case R_PPC_TLSGD:
                    case R_PPC_TLSLD:
+                     if (rel + 1 < relend
+                         && is_plt_seq_reloc (ELF32_R_TYPE (rel[1].r_info)))
+                       {
+                         if (pass != 0
+                             && ELF32_R_TYPE (rel[1].r_info) != R_PPC_PLTSEQ)
+                           {
+                             r_type = ELF32_R_TYPE (rel[1].r_info);
+                             r_symndx = ELF32_R_SYM (rel[1].r_info);
+                             if (r_symndx >= symtab_hdr->sh_info)
+                               {
+                                 struct elf_link_hash_entry **sym_hashes;
+
+                                 sym_hashes = elf_sym_hashes (ibfd);
+                                 h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+                                 while (h->root.type == bfd_link_hash_indirect
+                                        || h->root.type == bfd_link_hash_warning)
+                                   h = (struct elf_link_hash_entry *) h->root.u.i.link;
+                                 if (h != NULL)
+                                   {
+                                     struct plt_entry *ent = NULL;
+                                     bfd_vma addend = 0;
+
+                                     if (bfd_link_pic (info))
+                                       addend = rel->r_addend;
+                                     ent = find_plt_ent (&h->plt.plist,
+                                                         got2, addend);
+                                     if (ent != NULL
+                                         && ent->plt.refcount > 0)
+                                       ent->plt.refcount -= 1;
+                                   }
+                               }
+                           }
+                         continue;
+                       }
                      expecting_tls_get_addr = 2;
                      tls_set = 0;
                      tls_clear = 0;
@@ -5430,8 +5672,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                  if (pass == 0)
                    {
                      if (!expecting_tls_get_addr
-                         || (expecting_tls_get_addr == 1
-                             && !sec->has_tls_get_addr_call))
+                         || !sec->has_tls_get_addr_call)
                        continue;
 
                      if (rel + 1 < relend
@@ -5492,7 +5733,8 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                      bfd_vma addend = 0;
 
                      if (bfd_link_pic (info)
-                         && ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTREL24)
+                         && (ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTREL24
+                             || ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTCALL))
                        addend = rel[1].r_addend;
                      ent = find_plt_ent (&htab->tls_get_addr->plt.plist,
                                          got2, addend);
@@ -5617,7 +5859,11 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
        if (ent->plt.refcount > 0)
          break;
       if (ent == NULL
-         || (h->type != STT_GNU_IFUNC && local))
+         || (h->type != STT_GNU_IFUNC
+             && local
+             && (htab->can_convert_all_inline_plt
+                 || (ppc_elf_hash_entry (h)->tls_mask
+                     & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)))
        {
          /* A PLT entry is not required/allowed when:
 
@@ -6103,13 +6349,26 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       sreloc->size += p->count * sizeof (Elf32_External_Rela);
     }
 
-  /* Handle PLT relocs.  Done last, after dynindx has settled.  */
+  /* Handle PLT relocs.  Done last, after dynindx has settled.
+     We might need a PLT entry when the symbol
+     a) is dynamic, or
+     b) is an ifunc, or
+     c) has plt16 relocs and has been processed by adjust_dynamic_symbol, or
+     d) has plt16 relocs and we are linking statically.  */
   dyn = htab->elf.dynamic_sections_created && h->dynindx != -1;
-  if (dyn || h->type == STT_GNU_IFUNC)
+  if (dyn
+      || h->type == STT_GNU_IFUNC
+      || (h->needs_plt && h->dynamic_adjusted)
+      || (h->needs_plt
+         && h->def_regular
+         && !htab->elf.dynamic_sections_created
+         && !htab->can_convert_all_inline_plt
+         && (ppc_elf_hash_entry (h)->tls_mask
+             & (TLS_TLS | PLT_KEEP)) == PLT_KEEP))
     {
       struct plt_entry *ent;
       bfd_boolean doneone = FALSE;
-      bfd_vma plt_offset = 0, glink_offset = 0;
+      bfd_vma plt_offset = 0, glink_offset = (bfd_vma) -1;
 
       for (ent = h->plt.plist; ent != NULL; ent = ent->next)
        if (ent->plt.refcount > 0)
@@ -6117,7 +6376,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            asection *s = htab->elf.splt;
 
            if (!dyn)
-             s = htab->elf.iplt;
+             {
+               if (h->type == STT_GNU_IFUNC)
+                 s = htab->elf.iplt;
+               else
+                 s = htab->pltlocal;
+             }
 
            if (htab->plt_type == PLT_NEW || !dyn)
              {
@@ -6128,25 +6392,30 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                  }
                ent->plt.offset = plt_offset;
 
-               s = htab->glink;
-               if (!doneone || bfd_link_pic (info))
-                 {
-                   glink_offset = s->size;
-                   s->size += GLINK_ENTRY_SIZE (htab, h);
-                 }
-               if (!doneone
-                   && !bfd_link_pic (info)
-                   && h->def_dynamic
-                   && !h->def_regular)
+               if (s == htab->pltlocal)
+                 ent->glink_offset = glink_offset;
+               else
                  {
-                   h->root.u.def.section = s;
-                   h->root.u.def.value = glink_offset;
-                 }
-               ent->glink_offset = glink_offset;
+                   s = htab->glink;
+                   if (!doneone || bfd_link_pic (info))
+                     {
+                       glink_offset = s->size;
+                       s->size += GLINK_ENTRY_SIZE (htab, h);
+                     }
+                   if (!doneone
+                       && !bfd_link_pic (info)
+                       && h->def_dynamic
+                       && !h->def_regular)
+                     {
+                       h->root.u.def.section = s;
+                       h->root.u.def.value = glink_offset;
+                     }
+                   ent->glink_offset = glink_offset;
 
-               if (htab->params->emit_stub_syms
-                   && !add_stub_sym (ent, h, info))
-                 return FALSE;
+                   if (htab->params->emit_stub_syms
+                       && !add_stub_sym (ent, h, info))
+                     return FALSE;
+                 }
              }
            else
              {
@@ -6199,7 +6468,18 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            if (!doneone)
              {
                if (!dyn)
-                 htab->elf.irelplt->size += sizeof (Elf32_External_Rela);
+                 {
+                   if (h->type == STT_GNU_IFUNC)
+                     {
+                       s = htab->elf.irelplt;
+                       s->size += sizeof (Elf32_External_Rela);
+                     }
+                   else if (bfd_link_pic (info))
+                     {
+                       s = htab->relpltlocal;
+                       s->size += sizeof (Elf32_External_Rela);
+                     }
+                 }
                else
                  {
                    htab->elf.srelplt->size += sizeof (Elf32_External_Rela);
@@ -6425,16 +6705,26 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd,
        continue;
 
       /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt.  */
-      for (; local_plt < end_local_plt; ++local_plt)
+      lgot_masks = (char *) end_local_plt;
+      for (; local_plt < end_local_plt; ++local_plt, ++lgot_masks)
        {
          struct plt_entry *ent;
          bfd_boolean doneone = FALSE;
-         bfd_vma plt_offset = 0, glink_offset = 0;
+         bfd_vma plt_offset = 0, glink_offset = (bfd_vma) -1;
 
          for (ent = *local_plt; ent != NULL; ent = ent->next)
            if (ent->plt.refcount > 0)
              {
-               s = htab->elf.iplt;
+               if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
+                 s = htab->elf.iplt;
+               else if (htab->can_convert_all_inline_plt
+                        || (*lgot_masks & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)
+                 {
+                   ent->plt.offset = (bfd_vma) -1;
+                   continue;
+                 }
+               else
+                 s = htab->pltlocal;
 
                if (!doneone)
                  {
@@ -6443,9 +6733,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd,
                  }
                ent->plt.offset = plt_offset;
 
-               s = htab->glink;
-               if (!doneone || bfd_link_pic (info))
+               if (s != htab->pltlocal && (!doneone || bfd_link_pic (info)))
                  {
+                   s = htab->glink;
                    glink_offset = s->size;
                    s->size += GLINK_ENTRY_SIZE (htab, NULL);
                  }
@@ -6453,7 +6743,16 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd,
 
                if (!doneone)
                  {
-                   htab->elf.irelplt->size += sizeof (Elf32_External_Rela);
+                   if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
+                     {
+                       s = htab->elf.irelplt;
+                       s->size += sizeof (Elf32_External_Rela);
+                     }
+                   else if (bfd_link_pic (info))
+                     {
+                       s = htab->relpltlocal;
+                       s->size += sizeof (Elf32_External_Rela);
+                     }
                    doneone = TRUE;
                  }
              }
@@ -6599,6 +6898,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd,
             comment below.  */
        }
       else if (s == htab->elf.iplt
+              || s == htab->pltlocal
               || s == htab->glink
               || s == htab->glink_eh_frame
               || s == htab->elf.sgotplt
@@ -6988,6 +7288,7 @@ ppc_elf_relax_section (bfd *abfd,
            case R_PPC_REL24:
            case R_PPC_LOCAL24PC:
            case R_PPC_PLTREL24:
+           case R_PPC_PLTCALL:
              max_branch_offset = 1 << 25;
              break;
 
@@ -7816,7 +8117,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
       unsigned long r_symndx;
       bfd_vma relocation;
       bfd_vma branch_bit, from;
-      bfd_boolean unresolved_reloc;
+      bfd_boolean unresolved_reloc, save_unresolved_reloc;
       bfd_boolean warned;
       unsigned int tls_type, tls_mask, tls_gd;
       struct plt_entry **ifunc, **plt_list;
@@ -8086,6 +8387,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
              unsigned int insn2;
              bfd_vma offset = rel->r_offset;
 
+             if (is_plt_seq_reloc (ELF32_R_TYPE (rel[1].r_info)))
+               {
+                 bfd_put_32 (input_bfd, NOP, contents + offset);
+                 rel[1].r_info = ELF32_R_INFO (STN_UNDEF, R_PPC_NONE);
+                 break;
+               }
+
              if ((tls_mask & TLS_TPRELGD) != 0)
                {
                  /* IE */
@@ -8113,6 +8421,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
            {
              unsigned int insn2;
 
+             if (is_plt_seq_reloc (ELF32_R_TYPE (rel[1].r_info)))
+               {
+                 bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
+                 rel[1].r_info = ELF32_R_INFO (STN_UNDEF, R_PPC_NONE);
+                 break;
+               }
+
              for (r_symndx = 0;
                   r_symndx < symtab_hdr->sh_info;
                   r_symndx++)
@@ -8156,7 +8471,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_ADDR14_BRNTAKEN:
        case R_PPC_REL14_BRNTAKEN:
          {
-           bfd_vma insn;
+           unsigned int insn;
 
            insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
            insn &= ~BRANCH_PREDICT_BIT;
@@ -8390,6 +8705,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        }
 
       addend = rel->r_addend;
+      save_unresolved_reloc = unresolved_reloc;
       howto = NULL;
       if (r_type < R_PPC_max)
        howto = ppc_elf_howto_table[r_type];
@@ -9170,12 +9486,23 @@ ppc_elf_relocate_section (bfd *output_bfd,
          addend = 0;
          break;
 
+       case R_PPC_PLTSEQ:
+       case R_PPC_PLTCALL:
        case R_PPC_PLT16_LO:
        case R_PPC_PLT16_HI:
        case R_PPC_PLT16_HA:
-         plt_list = ifunc;
+         plt_list = NULL;
          if (h != NULL)
            plt_list = &h->plt.plist;
+         else if (ifunc != NULL)
+           plt_list = ifunc;
+         else if (local_got_offsets != NULL)
+           {
+             struct plt_entry **local_plt;
+             local_plt = (struct plt_entry **) (local_got_offsets
+                                                + symtab_hdr->sh_info);
+             plt_list = local_plt + r_symndx;
+           }
          unresolved_reloc = TRUE;
          if (plt_list != NULL)
            {
@@ -9183,11 +9510,23 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
              ent = find_plt_ent (plt_list, got2,
                                  bfd_link_pic (info) ? addend : 0);
-             if (ent != NULL)
+             if (ent != NULL && ent->plt.offset != (bfd_vma) -1)
                {
+                 asection *plt;
+
                  unresolved_reloc = FALSE;
-                 relocation = (htab->elf.splt->output_section->vma
-                               + htab->elf.splt->output_offset
+                 plt = htab->elf.splt;
+                 if (!htab->elf.dynamic_sections_created
+                     || h == NULL
+                     || h->dynindx == -1)
+                   {
+                     if (ifunc != NULL)
+                       plt = htab->elf.iplt;
+                     else
+                       plt = htab->pltlocal;
+                   }
+                 relocation = (plt->output_section->vma
+                               + plt->output_offset
                                + ent->plt.offset);
                  if (bfd_link_pic (info))
                    {
@@ -9549,6 +9888,48 @@ ppc_elf_relocate_section (bfd *output_bfd,
          goto copy_reloc;
        }
 
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case R_PPC_PLTCALL:
+         if (unresolved_reloc)
+           {
+             bfd_byte *p = contents + rel->r_offset;
+             unsigned int insn = bfd_get_32 (input_bfd, p);
+             insn &= 1;
+             bfd_put_32 (input_bfd, B | insn, p);
+             unresolved_reloc = save_unresolved_reloc;
+             r_type = R_PPC_REL24;
+             howto = ppc_elf_howto_table[r_type];
+           }
+         else if (htab->plt_type != PLT_NEW)
+           info->callbacks->einfo
+             (_("%P: %H: %s relocation unsupported for bss-plt\n"),
+              input_bfd, input_section, rel->r_offset,
+              howto->name);
+         break;
+
+       case R_PPC_PLTSEQ:
+       case R_PPC_PLT16_HA:
+       case R_PPC_PLT16_LO:
+         if (unresolved_reloc)
+           {
+             bfd_byte *p = contents + (rel->r_offset & ~3);
+             bfd_put_32 (input_bfd, NOP, p);
+             unresolved_reloc = FALSE;
+             r_type = R_PPC_NONE;
+             howto = ppc_elf_howto_table[r_type];
+           }
+         else if (htab->plt_type != PLT_NEW)
+           info->callbacks->einfo
+             (_("%P: %H: %s relocation unsupported for bss-plt\n"),
+              input_bfd, input_section, rel->r_offset,
+              howto->name);
+         break;
+       }
+
       /* Do any further special processing.  */
       switch (r_type)
        {
@@ -10085,6 +10466,8 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
            Elf_Internal_Rela rela;
            bfd_byte *loc;
            bfd_vma reloc_index;
+           asection *plt = htab->elf.splt;
+           asection *relplt = htab->elf.srelplt;
 
            if (htab->plt_type == PLT_NEW
                || !htab->elf.dynamic_sections_created
@@ -10120,10 +10503,10 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                  {
                    bfd_put_32 (info->output_bfd,
                                plt_entry[0] | PPC_HA (got_offset),
-                               htab->elf.splt->contents + ent->plt.offset + 0);
+                               plt->contents + ent->plt.offset + 0);
                    bfd_put_32 (info->output_bfd,
                                plt_entry[1] | PPC_LO (got_offset),
-                               htab->elf.splt->contents + ent->plt.offset + 4);
+                               plt->contents + ent->plt.offset + 4);
                  }
                else
                  {
@@ -10131,16 +10514,16 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
 
                    bfd_put_32 (info->output_bfd,
                                plt_entry[0] | PPC_HA (got_loc),
-                               htab->elf.splt->contents + ent->plt.offset + 0);
+                               plt->contents + ent->plt.offset + 0);
                    bfd_put_32 (info->output_bfd,
                                plt_entry[1] | PPC_LO (got_loc),
-                               htab->elf.splt->contents + ent->plt.offset + 4);
+                               plt->contents + ent->plt.offset + 4);
                  }
 
                bfd_put_32 (info->output_bfd, plt_entry[2],
-                           htab->elf.splt->contents + ent->plt.offset + 8);
+                           plt->contents + ent->plt.offset + 8);
                bfd_put_32 (info->output_bfd, plt_entry[3],
-                           htab->elf.splt->contents + ent->plt.offset + 12);
+                           plt->contents + ent->plt.offset + 12);
 
                /* This instruction is an immediate load.  The value loaded is
                   the byte offset of the R_PPC_JMP_SLOT relocation from the
@@ -10150,7 +10533,7 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                   prescaled offset.  */
                bfd_put_32 (info->output_bfd,
                            plt_entry[4] | reloc_index,
-                           htab->elf.splt->contents + ent->plt.offset + 16);
+                           plt->contents + ent->plt.offset + 16);
                /* This instruction is a PC-relative branch whose target is
                   the start of the PLT section.  The address of this branch
                   instruction is 20 bytes beyond the start of this PLT entry.
@@ -10160,19 +10543,18 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                bfd_put_32 (info->output_bfd,
                            (plt_entry[5]
                             | (-(ent->plt.offset + 20) & 0x03fffffc)),
-                           htab->elf.splt->contents + ent->plt.offset + 20);
+                           plt->contents + ent->plt.offset + 20);
                bfd_put_32 (info->output_bfd, plt_entry[6],
-                           htab->elf.splt->contents + ent->plt.offset + 24);
+                           plt->contents + ent->plt.offset + 24);
                bfd_put_32 (info->output_bfd, plt_entry[7],
-                           htab->elf.splt->contents + ent->plt.offset + 28);
+                           plt->contents + ent->plt.offset + 28);
 
                /* Fill in the GOT entry corresponding to this PLT slot with
                   the address immediately after the "bctr" instruction
                   in this PLT entry.  */
-               bfd_put_32 (info->output_bfd,
-                           (htab->elf.splt->output_section->vma
-                            + htab->elf.splt->output_offset
-                            + ent->plt.offset + 16),
+               bfd_put_32 (info->output_bfd, (plt->output_section->vma
+                                              + plt->output_offset
+                                              + ent->plt.offset + 16),
                            htab->elf.sgotplt->contents + got_offset);
 
                if (!bfd_link_pic (info))
@@ -10184,8 +10566,8 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                         * sizeof (Elf32_External_Rela));
 
                    /* Provide the @ha relocation for the first instruction.  */
-                   rela.r_offset = (htab->elf.splt->output_section->vma
-                                    + htab->elf.splt->output_offset
+                   rela.r_offset = (plt->output_section->vma
+                                    + plt->output_offset
                                     + ent->plt.offset + 2);
                    rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
                                                R_PPC_ADDR16_HA);
@@ -10194,8 +10576,8 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                    loc += sizeof (Elf32_External_Rela);
 
                    /* Provide the @l relocation for the second instruction.  */
-                   rela.r_offset = (htab->elf.splt->output_section->vma
-                                    + htab->elf.splt->output_offset
+                   rela.r_offset = (plt->output_section->vma
+                                    + plt->output_offset
                                     + ent->plt.offset + 6);
                    rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
                                                R_PPC_ADDR16_LO);
@@ -10222,66 +10604,83 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
                rela.r_offset = (htab->elf.sgotplt->output_section->vma
                                 + htab->elf.sgotplt->output_offset
                                 + got_offset);
-
+               rela.r_addend = 0;
              }
            else
              {
-               asection *splt = htab->elf.splt;
+               rela.r_addend = 0;
                if (!htab->elf.dynamic_sections_created
                    || h->dynindx == -1)
-                 splt = htab->elf.iplt;
+                 {
+                   if (h->type == STT_GNU_IFUNC)
+                     {
+                       plt = htab->elf.iplt;
+                       relplt = htab->elf.irelplt;
+                     }
+                   else
+                     {
+                       plt = htab->pltlocal;
+                       relplt = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+                     }
+                   if (h->def_regular
+                       && (h->root.type == bfd_link_hash_defined
+                           || h->root.type == bfd_link_hash_defweak))
+                     rela.r_addend = SYM_VAL (h);
+                 }
 
-               rela.r_offset = (splt->output_section->vma
-                                + splt->output_offset
-                                + ent->plt.offset);
-               if (htab->plt_type == PLT_OLD
-                   || !htab->elf.dynamic_sections_created
-                   || h->dynindx == -1)
+               if (relplt == NULL)
                  {
-                   /* We don't need to fill in the .plt.  The ppc dynamic
-                      linker will fill it in.  */
+                   loc = plt->contents + ent->plt.offset;
+                   bfd_put_32 (info->output_bfd, rela.r_addend, loc);
                  }
                else
                  {
-                   bfd_vma val = (htab->glink_pltresolve + ent->plt.offset
-                                  + htab->glink->output_section->vma
-                                  + htab->glink->output_offset);
-                   bfd_put_32 (info->output_bfd, val,
-                               splt->contents + ent->plt.offset);
-                 }
-             }
+                   rela.r_offset = (plt->output_section->vma
+                                    + plt->output_offset
+                                    + ent->plt.offset);
 
-           /* Fill in the entry in the .rela.plt section.  */
-           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);
+                   if (htab->plt_type == PLT_OLD
+                       || !htab->elf.dynamic_sections_created
+                       || h->dynindx == -1)
+                     {
+                       /* We don't need to fill in the .plt.  The ppc dynamic
+                          linker will fill it in.  */
+                     }
+                   else
+                     {
+                       bfd_vma val = (htab->glink_pltresolve + ent->plt.offset
+                                      + htab->glink->output_section->vma
+                                      + htab->glink->output_offset);
+                       bfd_put_32 (info->output_bfd, val,
+                                   plt->contents + ent->plt.offset);
+                     }
+                 }
              }
-           else
-             rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
 
-           if (!htab->elf.dynamic_sections_created
-               || h->dynindx == -1)
+           if (relplt != NULL)
              {
-               loc = (htab->elf.irelplt->contents
-                      + (htab->elf.irelplt->reloc_count++
-                         * sizeof (Elf32_External_Rela)));
-               htab->local_ifunc_resolver = 1;
-             }
-           else
-             {
-               loc = (htab->elf.srelplt->contents
-                      + reloc_index * sizeof (Elf32_External_Rela));
-               if (h->type == STT_GNU_IFUNC && is_static_defined (h))
-                 htab->maybe_local_ifunc_resolver = 1;
+               /* Fill in the entry in the .rela.plt section.  */
+               if (!htab->elf.dynamic_sections_created
+                   || h->dynindx == -1)
+                 {
+                   if (h->type == STT_GNU_IFUNC)
+                     rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+                   else
+                     rela.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
+                   loc = relplt->contents + (relplt->reloc_count++
+                                             * sizeof (Elf32_External_Rela));
+                   htab->local_ifunc_resolver = 1;
+                 }
+               else
+                 {
+                   rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
+                   loc = relplt->contents + (reloc_index
+                                             * sizeof (Elf32_External_Rela));
+                   if (h->type == STT_GNU_IFUNC && is_static_defined (h))
+                     htab->maybe_local_ifunc_resolver = 1;
+                 }
+               bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc);
              }
-           bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc);
            doneone = TRUE;
          }
 
@@ -10294,7 +10693,12 @@ write_global_sym_plt (struct elf_link_hash_entry *h, void *inf)
 
            if (!htab->elf.dynamic_sections_created
                || h->dynindx == -1)
-             plt = htab->elf.iplt;
+             {
+               if (h->type == STT_GNU_IFUNC)
+                 plt = htab->elf.iplt;
+               else
+                 break;
+             }
 
            p = (unsigned char *) htab->glink->contents + ent->glink_offset;
            write_glink_stub (h, ent, plt, p, info);
@@ -10368,16 +10772,32 @@ ppc_finish_symbols (struct bfd_link_info *info)
                if (sym_sec != NULL && sym_sec->output_section != NULL)
                  val += sym_sec->output_offset + sym_sec->output_section->vma;
 
-               BFD_ASSERT (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
-
-               htab->local_ifunc_resolver = 1;
-               plt = htab->elf.iplt;
-               relplt = htab->elf.irelplt;
+               if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                 {
+                   htab->local_ifunc_resolver = 1;
+                   plt = htab->elf.iplt;
+                   relplt = htab->elf.irelplt;
+                   rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+                 }
+               else
+                 {
+                   plt = htab->pltlocal;
+                   if (bfd_link_pic (info))
+                     {
+                       relplt = htab->relpltlocal;
+                       rela.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
+                     }
+                   else
+                     {
+                       loc = plt->contents + ent->plt.offset;
+                       bfd_put_32 (info->output_bfd, val, loc);
+                       continue;
+                     }
+                 }
 
                rela.r_offset = (ent->plt.offset
                                 + plt->output_offset
                                 + plt->output_section->vma);
-               rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
                rela.r_addend = val;
                loc = relplt->contents + (relplt->reloc_count++
                                          * sizeof (Elf32_External_Rela));
@@ -10969,14 +11389,9 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define ELF_ARCH               bfd_arch_powerpc
 #define ELF_TARGET_ID          PPC32_ELF_DATA
 #define ELF_MACHINE_CODE       EM_PPC
-#ifdef __QNXTARGET__
-#define ELF_MAXPAGESIZE                0x1000
-#define ELF_COMMONPAGESIZE     0x1000
-#else
 #define ELF_MAXPAGESIZE                0x10000
-#define ELF_COMMONPAGESIZE     0x10000
-#endif
-#define ELF_MINPAGESIZE                0x1000
+#define ELF_COMMONPAGESIZE     0x1000
+#define ELF_RELROPAGESIZE      ELF_MAXPAGESIZE
 #define elf_info_to_howto      ppc_elf_info_to_howto
 
 #ifdef  EM_CYGNUS_POWERPC
This page took 0.037841 seconds and 4 git commands to generate.