PowerPC64 --emit-relocs support for notoc stubs
authorAlan Modra <amodra@gmail.com>
Wed, 29 Aug 2018 13:01:25 +0000 (22:31 +0930)
committerAlan Modra <amodra@gmail.com>
Fri, 31 Aug 2018 12:45:05 +0000 (22:15 +0930)
This patch uses the newly defined high-part REL16 relocs to emit
relocations on the notoc stubs as we already do for other stubs.

* elf64-ppc.c (num_relocs_for_offset): New function.
(emit_relocs_for_offset): New function.
(use_global_in_relocs): New function, split out from..
(ppc_build_one_stub): ..here.  Output relocations for notoc stubs.
(ppc_size_one_stub): Calculate reloc count for notoc stubs.
(ppc64_elf_size_stubs): Don't count undefined syms in stub_globals.

bfd/ChangeLog
bfd/elf64-ppc.c

index 8538bcd943f5a2c3ace4066140f548e374ce283b..fcbfa96625aea539a4639f453757afada9f35be1 100644 (file)
@@ -1,3 +1,12 @@
+2018-08-31  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (num_relocs_for_offset): New function.
+       (emit_relocs_for_offset): New function.
+       (use_global_in_relocs): New function, split out from..
+       (ppc_build_one_stub): ..here.  Output relocations for notoc stubs.
+       (ppc_size_one_stub): Calculate reloc count for notoc stubs.
+       (ppc64_elf_size_stubs): Don't count undefined syms in stub_globals.
+
 2018-08-31  Alan Modra  <amodra@gmail.com>
 
        * reloc.c (BFD_RELOC_PPC64_REL16_HIGH, BFD_RELOC_PPC64_REL16_HIGHA),
index 5826c2288aa988b57a183007e71d095267a1dc78..eadde17615154606d87ab0ec814fa4b64fe10c6b 100644 (file)
@@ -9626,6 +9626,86 @@ size_offset (bfd_vma off)
   return size + 16;
 }
 
+static unsigned int
+num_relocs_for_offset (bfd_vma off)
+{
+  unsigned int num_rel;
+  if (off + 0x8000 < 0x10000)
+    num_rel = 1;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    num_rel = 2;
+  else
+    {
+      num_rel = 1;
+      if (off + 0x800000000000ULL >= 0x1000000000000ULL
+         && ((off >> 32) & 0xffff) != 0)
+       num_rel += 1;
+      if (PPC_HI (off) != 0)
+       num_rel += 1;
+      if (PPC_LO (off) != 0)
+       num_rel += 1;
+    }
+  return num_rel;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
+                       bfd_vma roff, bfd_vma targ, bfd_vma off)
+{
+  bfd_vma relative_targ = targ - (roff - 8);
+  if (bfd_big_endian (info->output_bfd))
+    roff += 2;
+  r->r_offset = roff;
+  r->r_addend = relative_targ + roff;
+  if (off + 0x8000 < 0x10000)
+    r->r_info = ELF64_R_INFO (0, R_PPC64_REL16);
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HA);
+      ++r;
+      roff += 4;
+      r->r_offset = roff;
+      r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+      r->r_addend = relative_targ + roff;
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+      else
+       {
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHEST);
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             ++r;
+             roff += 4;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+             r->r_addend = relative_targ + roff;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       roff += 4;
+      if (PPC_HI (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGH);
+         r->r_addend = relative_targ + roff;
+       }
+      if (PPC_LO (off) != 0)
+       {
+         ++r;
+         roff += 4;
+         r->r_offset = roff;
+         r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+         r->r_addend = relative_targ + roff;
+       }
+    }
+  return r;
+}
+
 /* Emit .eh_frame opcode to advance pc by DELTA.  */
 
 static bfd_byte *
@@ -10049,6 +10129,65 @@ get_relocs (asection *sec, int count)
   return relocs;
 }
 
+/* Convert the relocs R[0] thru R[-NUM_REL+1], which are all no-symbol
+   forms, to the equivalent relocs against the global symbol given by
+   STUB_ENTRY->H.  */
+
+static bfd_boolean
+use_global_in_relocs (struct ppc_link_hash_table *htab,
+                     struct ppc_stub_hash_entry *stub_entry,
+                     Elf_Internal_Rela *r, unsigned int num_rel)
+{
+  struct elf_link_hash_entry **hashes;
+  unsigned long symndx;
+  struct ppc_link_hash_entry *h;
+  bfd_vma symval;
+
+  /* Relocs are always against symbols in their own object file.  Fake
+     up global sym hashes for the stub bfd (which has no symbols).  */
+  hashes = elf_sym_hashes (htab->params->stub_bfd);
+  if (hashes == NULL)
+    {
+      bfd_size_type hsize;
+
+      /* When called the first time, stub_globals will contain the
+        total number of symbols seen during stub sizing.  After
+        allocating, stub_globals is used as an index to fill the
+        hashes array.  */
+      hsize = (htab->stub_globals + 1) * sizeof (*hashes);
+      hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
+      if (hashes == NULL)
+       return FALSE;
+      elf_sym_hashes (htab->params->stub_bfd) = hashes;
+      htab->stub_globals = 1;
+    }
+  symndx = htab->stub_globals++;
+  h = stub_entry->h;
+  hashes[symndx] = &h->elf;
+  if (h->oh != NULL && h->oh->is_func)
+    h = ppc_follow_link (h->oh);
+  BFD_ASSERT (h->elf.root.type == bfd_link_hash_defined
+             || h->elf.root.type == bfd_link_hash_defweak);
+  symval = (h->elf.root.u.def.value
+           + h->elf.root.u.def.section->output_offset
+           + h->elf.root.u.def.section->output_section->vma);
+  while (num_rel-- != 0)
+    {
+      r->r_info = ELF64_R_INFO (symndx, ELF64_R_TYPE (r->r_info));
+      if (h->elf.root.u.def.section != stub_entry->target_section)
+       {
+         /* H is an opd symbol.  The addend must be zero, and the
+            branch reloc is the only one we can convert.  */
+         r->r_addend = 0;
+         break;
+       }
+      else
+       r->r_addend -= symval;
+      --r;
+    }
+  return TRUE;
+}
+
 static bfd_vma
 get_r2off (struct bfd_link_info *info,
           struct ppc_stub_hash_entry *stub_entry)
@@ -10092,10 +10231,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
   bfd_byte *loc;
-  bfd_byte *p;
+  bfd_byte *p, *relp;
   bfd_vma targ, off;
   Elf_Internal_Rela *r;
   asection *plt;
+  int num_rel;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -10171,41 +10311,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
          r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
          r->r_addend = targ;
-         if (stub_entry->h != NULL)
-           {
-             struct elf_link_hash_entry **hashes;
-             unsigned long symndx;
-             struct ppc_link_hash_entry *h;
-
-             hashes = elf_sym_hashes (htab->params->stub_bfd);
-             if (hashes == NULL)
-               {
-                 bfd_size_type hsize;
-
-                 hsize = (htab->stub_globals + 1) * sizeof (*hashes);
-                 hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
-                 if (hashes == NULL)
-                   return FALSE;
-                 elf_sym_hashes (htab->params->stub_bfd) = hashes;
-                 htab->stub_globals = 1;
-               }
-             symndx = htab->stub_globals++;
-             h = stub_entry->h;
-             hashes[symndx] = &h->elf;
-             r->r_info = ELF64_R_INFO (symndx, R_PPC64_REL24);
-             if (h->oh != NULL && h->oh->is_func)
-               h = ppc_follow_link (h->oh);
-             if (h->elf.root.u.def.section != stub_entry->target_section)
-               /* H is an opd symbol.  The addend must be zero.  */
-               r->r_addend = 0;
-             else
-               {
-                 off = (h->elf.root.u.def.value
-                        + h->elf.root.u.def.section->output_offset
-                        + h->elf.root.u.def.section->output_section->vma);
-                 r->r_addend -= off;
-               }
-           }
+         if (stub_entry->h != NULL
+             && !use_global_in_relocs (htab, stub_entry, r, 1))
+           return FALSE;
        }
       break;
 
@@ -10408,6 +10516,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                + stub_entry->target_section->output_section->vma);
       off = targ - off;
 
+      relp = p;
+      num_rel = 0;
       /* The notoc stubs calculate their target (either a PLT entry or
         the global entry point of a function) relative to the PC
         returned by the "bcl" two instructions past the start of the
@@ -10419,6 +10529,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (stub_entry->stub_type <= ppc_stub_long_branch_both)
        {
          bfd_vma from;
+         num_rel = 1;
          from = (stub_entry->stub_offset
                  + stub_entry->group->stub_sec->output_offset
                  + stub_entry->group->stub_sec->output_section->vma
@@ -10434,6 +10545,29 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
       p += 4;
 
+      if (info->emitrelocations)
+       {
+         bfd_vma roff;
+         num_rel += num_relocs_for_offset (off);
+         r = get_relocs (stub_entry->group->stub_sec, num_rel);
+         if (r == NULL)
+           return FALSE;
+         roff = relp + 16 - stub_entry->group->stub_sec->contents;
+         r = emit_relocs_for_offset (info, r, roff, targ, off);
+         if (stub_entry->stub_type == ppc_stub_long_branch_notoc
+             || stub_entry->stub_type == ppc_stub_long_branch_both)
+           {
+             ++r;
+             roff = p - 4 - stub_entry->group->stub_sec->contents;
+             r->r_offset = roff;
+             r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
+             r->r_addend = targ;
+             if (stub_entry->h != NULL
+                 && !use_global_in_relocs (htab, stub_entry, r, num_rel))
+               return FALSE;
+           }
+       }
+
       if (htab->glink_eh_frame != NULL
        && htab->glink_eh_frame->size != 0)
        {
@@ -10759,6 +10893,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              + stub_entry->target_section->output_section->vma);
       off = targ - off;
 
+      if (info->emitrelocations)
+       {
+         stub_entry->group->stub_sec->reloc_count
+           += num_relocs_for_offset (off);
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
       extra = size_offset (off - 8);
       /* Include branch insn plus those in the offset sequence.  */
       size += 4 + extra;
@@ -10786,6 +10927,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                                    - ppc_stub_long_branch_notoc);
          size += 4;
        }
+      else if (info->emitrelocations)
+       stub_entry->group->stub_sec->reloc_count +=1;
       break;
 
     case ppc_stub_plt_call_notoc:
@@ -10821,6 +10964,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          off -= pad;
        }
 
+      if (info->emitrelocations)
+       {
+         stub_entry->group->stub_sec->reloc_count
+           += num_relocs_for_offset (off - 8);
+         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+       }
+
       size = plt_stub_size (htab, stub_entry, off);
 
       /* After the bcl, lr has been modified so we need to emit
@@ -12184,7 +12334,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                    = hash ? hash->elf.type : ELF_ST_TYPE (sym->st_info);
                  stub_entry->other = hash ? hash->elf.other : sym->st_other;
 
-                 if (stub_entry->h != NULL)
+                 if (hash != NULL
+                     && (hash->elf.root.type == bfd_link_hash_defined
+                         || hash->elf.root.type == bfd_link_hash_defweak))
                    htab->stub_globals += 1;
                }