From 066f4018ae7822d81cb6747fd9494e5dd63bfecf Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 30 Apr 2019 16:31:01 +0930 Subject: PowerPC64 GOT indirect to GOT relative optimisation This implements an optimisation that converts sequences like addis r9,r2,sym@got@ha ld r3,sym@got@l(r9) to addis r9,r2,sym@toc@ha addi r3,r9,sym@toc@l when "sym" is locally defined and can't be overridden. bfd/ * elf64-ppc.c (struct ppc64_elf_obj_tdata): Add has_gotrel. (struct _ppc64_elf_section_data): Likewise. (ppc64_elf_check_relocs): Set above fields. (ppc64_elf_edit_toc): Add a pass over GOT relocs. (ppc64_elf_relocate_section): Edit GOT indirect to GOT relative when possible. ld/ * testsuite/ld-powerpc/elfv2exe.d: Update. * testsuite/ld-powerpc/elfv2so.d: Update. * testsuite/ld-powerpc/tocopt.d: Update. * testsuite/ld-powerpc/tocopt.s: Update. * testsuite/ld-powerpc/tocopt5.d: Update. * testsuite/ld-powerpc/tocopt5.s: Update. * testsuite/ld-powerpc/tocopt7.d: Update. * testsuite/ld-powerpc/tocopt7.s: Update. * testsuite/ld-powerpc/tocopt8.d: Update. * testsuite/ld-powerpc/tocopt8.s: Update. --- bfd/ChangeLog | 9 +++ bfd/elf64-ppc.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 3 deletions(-) (limited to 'bfd') diff --git a/bfd/ChangeLog b/bfd/ChangeLog index b39197d..0b68dc0 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,12 @@ +2019-04-30 Alan Modra + + * elf64-ppc.c (struct ppc64_elf_obj_tdata): Add has_gotrel. + (struct _ppc64_elf_section_data): Likewise. + (ppc64_elf_check_relocs): Set above fields. + (ppc64_elf_edit_toc): Add a pass over GOT relocs. + (ppc64_elf_relocate_section): Edit GOT indirect to GOT relative + when possible. + 2019-04-26 H.J. Lu PR ld/24486 diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index a795ad1..4eb0153 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1594,6 +1594,9 @@ struct ppc64_elf_obj_tdata /* Set if toc/got ha relocs detected not using r2, or lo reloc instruction not one we handle. */ unsigned int unexpected_toc_insn : 1; + + /* Set if got relocs that can be optimised are present in this file. */ + unsigned int has_gotrel : 1; }; #define ppc64_elf_tdata(bfd) \ @@ -1793,6 +1796,9 @@ struct _ppc64_elf_section_data /* Flag set when PLTCALL relocs are detected. */ unsigned int has_pltcall:1; + + /* Flag set when section has GOT relocations that can be optimised. */ + unsigned int has_gotrel:1; }; #define ppc64_elf_section_data(sec) \ @@ -4383,14 +4389,19 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, tls_type = TLS_TLS | TLS_DTPREL; dogottls: sec->has_tls_reloc = 1; - /* Fall through */ + goto dogot; - case R_PPC64_GOT16: case R_PPC64_GOT16_DS: case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_LO_DS: + ppc64_elf_tdata (abfd)->has_gotrel = 1; + ppc64_elf_section_data (sec)->has_gotrel = 1; + /* Fall through. */ + + case R_PPC64_GOT16: case R_PPC64_GOT16_HI: case R_PPC64_GOT16_LO: - case R_PPC64_GOT16_LO_DS: + dogot: /* This symbol requires a global offset table entry. */ sec->has_toc_reloc = 1; if (r_type == R_PPC64_GOT_TLSLD16 @@ -8579,6 +8590,161 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) free (skip); } + /* Look for cases where we can change an indirect GOT access to + a GOT relative access, possibly reducing the number of GOT + entries. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next) + { + asection *sec; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *local_syms; + Elf_Internal_Rela *relstart, *rel; + bfd_vma got; + + if (!is_ppc64_elf (ibfd)) + continue; + + if (!ppc64_elf_tdata (ibfd)->has_gotrel) + continue; + + sec = ppc64_elf_tdata (ibfd)->got; + got = sec->output_section->vma + sec->output_offset + 0x8000; + + local_syms = NULL; + symtab_hdr = &elf_symtab_hdr (ibfd); + + for (sec = ibfd->sections; sec != NULL; sec = sec->next) + { + if (sec->reloc_count == 0 + || !ppc64_elf_section_data (sec)->has_gotrel + || discarded_section (sec)) + continue; + + relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + { + got_error_ret: + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + free (local_syms); + if (sec != NULL + && relstart != NULL + && elf_section_data (sec)->relocs != relstart) + free (relstart); + return FALSE; + } + + for (rel = relstart; rel < relstart + sec->reloc_count; ++rel) + { + enum elf_ppc64_reloc_type r_type; + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sym_sec; + struct elf_link_hash_entry *h; + struct got_entry *ent; + bfd_vma val; + unsigned char buf[4]; + unsigned int insn; + + r_type = ELF64_R_TYPE (rel->r_info); + switch (r_type) + { + default: + continue; + + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_HA: + case R_PPC64_GOT16_LO_DS: + break; + } + + r_symndx = ELF64_R_SYM (rel->r_info); + if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, + r_symndx, ibfd)) + goto got_error_ret; + + if (!SYMBOL_REFERENCES_LOCAL (info, h)) + continue; + + if (h != NULL) + val = h->root.u.def.value; + else + val = sym->st_value; + val += rel->r_addend; + val += sym_sec->output_section->vma + sym_sec->output_offset; + + switch (r_type) + { + default: + continue; + + case R_PPC64_GOT16_DS: + if (val - got + 0x8000 >= 0x10000) + continue; + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 4)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if ((insn & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */) + continue; + break; + + case R_PPC64_GOT16_HA: + if (val - got + 0x80008000ULL >= 0x100000000ULL) + continue; + + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 4)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if (((insn & ((0x3f << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + continue; + break; + + case R_PPC64_GOT16_LO_DS: + if (val - got + 0x80008000ULL >= 0x100000000ULL) + continue; + if (!bfd_get_section_contents (ibfd, sec, buf, + rel->r_offset & ~3, 4)) + goto got_error_ret; + insn = bfd_get_32 (ibfd, buf); + if ((insn & (0x3f << 26 | 0x3)) != 58u << 26 /* ld */) + continue; + break; + } + + if (h != NULL) + ent = h->got.glist; + else + { + struct got_entry **local_got_ents = elf_local_got_ents (ibfd); + ent = local_got_ents[r_symndx]; + } + for (; ent != NULL; ent = ent->next) + if (ent->addend == rel->r_addend + && ent->owner == ibfd + && ent->tls_type == 0) + break; + BFD_ASSERT (ent && ent->got.refcount > 0); + ent->got.refcount -= 1; + } + + 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; } @@ -14397,6 +14563,44 @@ ppc64_elf_relocate_section (bfd *output_bfd, goto copy_reloc; } break; + + case R_PPC64_GOT16_DS: + from = TOCstart + htab->sec_info[input_section->id].toc_off; + if (relocation + addend - from + 0x8000 < 0x10000 + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); + if ((insn & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */) + { + insn += (14u << 26) - (58u << 26); + bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3)); + r_type = R_PPC64_TOC16; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + } + break; + + case R_PPC64_GOT16_LO_DS: + case R_PPC64_GOT16_HA: + from = TOCstart + htab->sec_info[input_section->id].toc_off; + if (relocation + addend - from + 0x80008000ULL < 0x100000000ULL + && SYMBOL_REFERENCES_LOCAL (info, &h->elf)) + { + insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3)); + if ((insn & (0x3f << 26 | 0x3)) == 58u << 26 /* ld */) + { + insn += (14u << 26) - (58u << 26); + bfd_put_32 (input_bfd, insn, contents + (rel->r_offset & ~3)); + r_type = R_PPC64_TOC16_LO; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + else if ((insn & (0x3f << 26)) == 15u << 26 /* addis */) + { + r_type = R_PPC64_TOC16_HA; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + } + break; } /* Set `addend'. */ -- cgit v1.1