aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog9
-rw-r--r--bfd/elf64-ppc.c210
2 files changed, 216 insertions, 3 deletions
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 <amodra@gmail.com>
+
+ * 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 <hongjiu.lu@intel.com>
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'. */