From 133a1f6041bc131afcb39bf1a124d8e0fc5547ea Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 5 Sep 2019 10:06:42 +0930 Subject: [PATCH] PowerPC64 GOT_PCREL relocs 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 | 8 ++++++ bfd/elf64-ppc.c | 76 +++++++++++++++---------------------------------- 2 files changed, 31 insertions(+), 53 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 7f587df81af..8ed849d74d3 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2019-09-05 Alan Modra + + * 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 * elf64-ppc.c (xlate_pcrel_opt): Handle prefix loads and stores diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index e39b6faf17b..88ace7e6fe0 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -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; } -- 2.30.2