PowerPC64 GOT_PCREL relocs
authorAlan Modra <amodra@gmail.com>
Thu, 5 Sep 2019 00:36:42 +0000 (10:06 +0930)
committerAlan Modra <amodra@gmail.com>
Thu, 5 Sep 2019 05:55:51 +0000 (15:25 +0930)
PC-relative relocs typically use the addend in adjusting what they are
relative to.  For example:
bcl 20,31,1f
1: mflr 12
addi 12,12,xxx-1b
generates "R_PPC64_REL16 xxx+0x4" for the addi (when little-endian).
The addend reflects the fact that you want the offset relative to the
previous insn not the current one in this case.

So the question is, will we ever want to do something like that for an
instruction using R_PPC64_GOT_PCREL34?  I thought so at the time I
first implemented support in ld but at the time I think the hardware
was possibly going to support pcrel+offset+reg addressing.  In which
case you might want something like:
load_big_offset_into_r2
pld 3,sym-big_offset@got@pcrel(2)
which would be a way of supporting more than 8G offsets from code to
the GOT.  We could do the same with
load_big_offset_into_r2
pla 9,sym-big_offset@got@pcrel
ldx 3,9,2
However, this is really a poor version of TOC-pointer relative code.

So let's go with an addend on R_PPC64_GOT_PCREL34 meaning that
sym+addend should be put in a GOT entry, and the relocation calculate
the pc-relative offset to that GOT entry.

Note that this is an extension to the ABI, which says (by the
expression given for GOT relocs) that non-zero addends on GOT and PLT
relocs are ignored.  This is true for all GOT/PLT relocs, not just the
pcrel ones.

* elf64-ppc.c (ppc64_elf_check_relocs): Interpret an addend in
GOT_PCREL and PLT_PCREL relocs as affecting the value stored
in the GOT/PLT entry rather than affecting the offset to that
GOI/PLT entry.
(ppc64_elf_edit_toc, ppc64_elf_relocate_section): Likewise.

bfd/ChangeLog
bfd/elf64-ppc.c

index 7f587df81afa16bd641c3a41a1ee7261bb660618..8ed849d74d3143b1f46a03f9577a708ef15e326d 100644 (file)
@@ -1,3 +1,11 @@
+2019-09-05  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (ppc64_elf_check_relocs): Interpret an addend in
+       GOT_PCREL and PLT_PCREL relocs as affecting the value stored
+       in the GOT/PLT entry rather than affecting the offset to that
+       GOI/PLT entry.
+       (ppc64_elf_edit_toc, ppc64_elf_relocate_section): Likewise.
+
 2019-09-05  Alan Modra  <amodra@gmail.com>
 
        * elf64-ppc.c (xlate_pcrel_opt): Handle prefix loads and stores
index e39b6faf17b9bba73ae05b6fb5647317aa789750..88ace7e6fe0f0e03fee50c5644851ef974fb3863 100644 (file)
@@ -4524,7 +4524,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int tls_type;
       struct _ppc64_elf_section_data *ppc64_sec;
       struct plt_entry **ifunc, **plt_list;
-      bfd_vma sym_addend;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -4550,18 +4549,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_D28:
        case R_PPC64_TPREL34:
        case R_PPC64_DTPREL34:
-         htab->powerxx_stubs = 1;
-         /* Fall through.  */
-       default:
-         /* Somewhat foolishly, because the ABIs don't specifically
-            allow it, ppc64 gas and ld support GOT and PLT relocs
-            with non-zero addends where the addend results in
-            sym+addend being stored in the GOT or PLT entry.  This
-            can't be supported for pcrel relocs because the addend is
-            used to specify the pcrel offset.  */
-         sym_addend = rel->r_addend;
-         break;
-
        case R_PPC64_PCREL34:
        case R_PPC64_GOT_PCREL34:
        case R_PPC64_GOT_TLSGD34:
@@ -4572,7 +4559,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_PCREL28:
          htab->powerxx_stubs = 1;
-         sym_addend = 0;
+         break;
+       default:
          break;
        }
 
@@ -4621,7 +4609,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                            sym_addend,
+                                            rel->r_addend,
                                             NON_GOT | PLT_IFUNC);
              if (ifunc == NULL)
                return FALSE;
@@ -4638,7 +4626,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            ((struct ppc_link_hash_entry *) h)->tls_mask |= TLS_TLS | TLS_MARK;
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend,
+                                       rel->r_addend,
                                        NON_GOT | TLS_TLS | TLS_MARK))
              return FALSE;
          sec->has_tls_reloc = 1;
@@ -4712,7 +4700,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
              eh = (struct ppc_link_hash_entry *) h;
              for (ent = eh->elf.got.glist; ent != NULL; ent = ent->next)
-               if (ent->addend == sym_addend
+               if (ent->addend == rel->r_addend
                    && ent->owner == abfd
                    && ent->tls_type == tls_type)
                  break;
@@ -4723,7 +4711,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  if (ent == NULL)
                    return FALSE;
                  ent->next = eh->elf.got.glist;
-                 ent->addend = sym_addend;
+                 ent->addend = rel->r_addend;
                  ent->owner = abfd;
                  ent->tls_type = tls_type;
                  ent->is_indirect = FALSE;
@@ -4736,14 +4724,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          else
            /* This is a global offset table entry for a local symbol.  */
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend, tls_type))
+                                       rel->r_addend, tls_type))
              return FALSE;
 
          /* We may also need a plt entry if the symbol turns out to be
             an ifunc.  */
          if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
            {
-             if (!update_plt_info (abfd, &h->plt.plist, sym_addend))
+             if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
                return FALSE;
            }
          break;
@@ -4769,9 +4757,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
          if (plt_list == NULL)
            plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                             sym_addend,
+                                             rel->r_addend,
                                              NON_GOT | PLT_KEEP);
-         if (!update_plt_info (abfd, plt_list, sym_addend))
+         if (!update_plt_info (abfd, plt_list, rel->r_addend))
            return FALSE;
          break;
 
@@ -4929,7 +4917,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* We may need a .plt entry if the function this reloc
             refers to is in a shared lib.  */
          if (plt_list
-             && !update_plt_info (abfd, plt_list, sym_addend))
+             && !update_plt_info (abfd, plt_list, rel->r_addend))
            return FALSE;
          break;
 
@@ -4973,7 +4961,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
          else
            if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
-                                       sym_addend, tls_type))
+                                       rel->r_addend, tls_type))
              return FALSE;
 
          ppc64_sec = ppc64_elf_section_data (sec);
@@ -4995,7 +4983,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
          BFD_ASSERT (rel->r_offset % 8 == 0);
          ppc64_sec->u.toc.symndx[rel->r_offset / 8] = r_symndx;
-         ppc64_sec->u.toc.add[rel->r_offset / 8] = sym_addend;
+         ppc64_sec->u.toc.add[rel->r_offset / 8] = rel->r_addend;
 
          /* Mark the second slot of a GD or LD entry.
             -1 to indicate GD and -2 to indicate LD.  */
@@ -9044,7 +9032,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              asection *sym_sec;
              struct elf_link_hash_entry *h;
              struct got_entry *ent;
-             bfd_vma sym_addend, val, pc;
+             bfd_vma val, pc;
              unsigned char buf[8];
              unsigned int insn;
              enum {no_check, check_lo, check_ha} insn_check;
@@ -9119,11 +9107,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
                case R_PPC64_GOT16_HA:
                case R_PPC64_GOT16_LO_DS:
-                 sym_addend = rel->r_addend;
-                 break;
-
                case R_PPC64_GOT_PCREL34:
-                 sym_addend = 0;
                  break;
                }
 
@@ -9144,7 +9128,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                val = h->root.u.def.value;
              else
                val = sym->st_value;
-             val += sym_addend;
+             val += rel->r_addend;
              val += sym_sec->output_section->vma + sym_sec->output_offset;
 
 /* Fudge factor to allow for the fact that the preliminary layout
@@ -9209,7 +9193,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  ent = local_got_ents[r_symndx];
                }
              for (; ent != NULL; ent = ent->next)
-               if (ent->addend == sym_addend
+               if (ent->addend == rel->r_addend
                    && ent->owner == ibfd
                    && ent->tls_type == 0)
                  break;
@@ -15535,14 +15519,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            bfd_vma off;
            unsigned long indx = 0;
            struct got_entry *ent;
-           bfd_vma sym_addend = orig_rel.r_addend;
-
-           if (r_type == R_PPC64_GOT_PCREL34
-               || r_type == R_PPC64_GOT_TLSGD34
-               || r_type == R_PPC64_GOT_TLSLD34
-               || r_type == R_PPC64_GOT_TPREL34
-               || r_type == R_PPC64_GOT_DTPREL34)
-             sym_addend = 0;
 
            if (tls_type == (TLS_TLS | TLS_LD)
                && (h == NULL
@@ -15576,7 +15552,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  }
 
                for (; ent != NULL; ent = ent->next)
-                 if (ent->addend == sym_addend
+                 if (ent->addend == orig_rel.r_addend
                      && ent->owner == input_bfd
                      && ent->tls_type == tls_type)
                    break;
@@ -15633,7 +15609,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    outrel.r_offset = (got->output_section->vma
                                       + got->output_offset
                                       + off);
-                   outrel.r_addend = sym_addend;
+                   outrel.r_addend = orig_rel.r_addend;
                    if (tls_type & (TLS_LD | TLS_GD))
                      {
                        outrel.r_addend = 0;
@@ -15646,7 +15622,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                            bfd_elf64_swap_reloca_out (output_bfd,
                                                       &outrel, loc);
                            outrel.r_offset += 8;
-                           outrel.r_addend = sym_addend;
+                           outrel.r_addend = orig_rel.r_addend;
                            outrel.r_info
                              = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
                          }
@@ -15692,7 +15668,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   emitting a reloc.  */
                else
                  {
-                   relocation += sym_addend;
+                   relocation += orig_rel.r_addend;
                    if (tls_type != 0)
                      {
                        if (htab->elf.tls_sec == NULL)
@@ -15723,6 +15699,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              abort ();
 
            relocation = got->output_section->vma + got->output_offset + off;
+           addend = 0;
            if (!(r_type == R_PPC64_GOT_PCREL34
                  || r_type == R_PPC64_GOT_TLSGD34
                  || r_type == R_PPC64_GOT_TLSLD34
@@ -15760,15 +15737,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            if (plt_list)
              {
                struct plt_entry *ent;
-               bfd_vma sym_addend = orig_rel.r_addend;
-
-               if (r_type == R_PPC64_PLT_PCREL34
-                   || r_type == R_PPC64_PLT_PCREL34_NOTOC)
-                 sym_addend = 0;
 
                for (ent = *plt_list; ent != NULL; ent = ent->next)
                  if (ent->plt.offset != (bfd_vma) -1
-                     && ent->addend == sym_addend)
+                     && ent->addend == orig_rel.r_addend)
                    {
                      asection *plt;
                      bfd_vma got;
@@ -15797,9 +15769,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                                 + htab->sec_info[input_section->id].toc_off);
                          relocation -= got;
                        }
-                     if (r_type != R_PPC64_PLT_PCREL34
-                         && r_type != R_PPC64_PLT_PCREL34_NOTOC)
-                       addend = 0;
+                     addend = 0;
                      unresolved_reloc = FALSE;
                      break;
                    }