aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c280
1 files changed, 231 insertions, 49 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 703a2b3..1873728 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -1235,7 +1235,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
0xfffc, /* dst_mask */
FALSE), /* pcrel_offset */
- /* Marker reloc for TLS. */
+ /* Marker relocs for TLS. */
HOWTO (R_PPC64_TLS,
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -1250,6 +1250,34 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
0, /* dst_mask */
FALSE), /* pcrel_offset */
+ HOWTO (R_PPC64_TLSGD,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_TLSGD", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_PPC64_TLSLD,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_TLSLD", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* Computes the load module index of the load module that contains the
definition of its TLS sym. */
HOWTO (R_PPC64_DTPMOD64,
@@ -2031,6 +2059,10 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
break;
case BFD_RELOC_PPC_TLS: r = R_PPC64_TLS;
break;
+ case BFD_RELOC_PPC_TLSGD: r = R_PPC64_TLSGD;
+ break;
+ case BFD_RELOC_PPC_TLSLD: r = R_PPC64_TLSLD;
+ break;
case BFD_RELOC_PPC_DTPMOD: r = R_PPC64_DTPMOD64;
break;
case BFD_RELOC_PPC_TPREL16: r = R_PPC64_TPREL16;
@@ -4644,7 +4676,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
unsigned long r_symndx;
struct elf_link_hash_entry *h;
enum elf_ppc64_reloc_type r_type;
- int tls_type = 0;
+ int tls_type;
struct _ppc64_elf_section_data *ppc64_sec;
r_symndx = ELF64_R_SYM (rel->r_info);
@@ -4658,9 +4690,42 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
+ tls_type = 0;
r_type = ELF64_R_TYPE (rel->r_info);
+ if (h != NULL && (h == tga || h == dottga))
+ switch (r_type)
+ {
+ default:
+ break;
+
+ case R_PPC64_REL24:
+ case R_PPC64_REL14:
+ case R_PPC64_REL14_BRTAKEN:
+ case R_PPC64_REL14_BRNTAKEN:
+ case R_PPC64_ADDR24:
+ case R_PPC64_ADDR14:
+ case R_PPC64_ADDR14_BRTAKEN:
+ case R_PPC64_ADDR14_BRNTAKEN:
+ if (rel != relocs
+ && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
+ || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
+ /* We have a new-style __tls_get_addr call with a marker
+ reloc. */
+ ;
+ else
+ /* Mark this section as having an old-style call. */
+ sec->has_tls_get_addr_call = 1;
+ break;
+ }
+
switch (r_type)
{
+ case R_PPC64_TLSGD:
+ case R_PPC64_TLSLD:
+ /* These special tls relocs tie a call to __tls_get_addr with
+ its parameter symbol. */
+ break;
+
case R_PPC64_GOT_TLSLD16:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_GOT_TLSLD16_HI:
@@ -7072,6 +7137,8 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
case R_PPC64_TOC16:
case R_PPC64_TOC16_LO:
case R_PPC64_TLS:
+ case R_PPC64_TLSGD:
+ case R_PPC64_TLSLD:
if (sym_sec == NULL || sym_sec != toc)
continue;
@@ -7092,7 +7159,9 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
value += rel->r_addend;
BFD_ASSERT (value < toc->size && value % 8 == 0);
toc_ref_index = value / 8;
- if (r_type == R_PPC64_TLS)
+ if (r_type == R_PPC64_TLS
+ || r_type == R_PPC64_TLSGD
+ || r_type == R_PPC64_TLSLD)
{
toc_ref[toc_ref_index] = 1;
continue;
@@ -7157,7 +7226,8 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
if (pass == 0)
{
- if (!expecting_tls_get_addr)
+ if (!expecting_tls_get_addr
+ || !sec->has_tls_get_addr_call)
continue;
if (rel + 1 < relend
@@ -7276,18 +7346,18 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info)
free (relstart);
}
- if (toc_ref != NULL)
- free (toc_ref);
+ if (toc_ref != NULL)
+ free (toc_ref);
- if (locsyms != NULL
- && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms))
- {
- if (!info->keep_memory)
- free (locsyms);
- else
- elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms;
- }
- }
+ if (locsyms != NULL
+ && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms))
+ {
+ if (!info->keep_memory)
+ free (locsyms);
+ else
+ elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms;
+ }
+ }
return TRUE;
}
@@ -10288,28 +10358,28 @@ ppc64_elf_relocate_section (bfd *output_bfd,
tls_mask = 0;
tls_gd = 0;
toc_symndx = 0;
- if (IS_PPC64_TLS_RELOC (r_type))
+ if (h != NULL)
+ tls_mask = h->tls_mask;
+ else if (local_got_ents != NULL)
{
- if (h != NULL)
- tls_mask = h->tls_mask;
- else if (local_got_ents != NULL)
- {
- char *lgot_masks;
- lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
- tls_mask = lgot_masks[r_symndx];
- }
- if (tls_mask == 0 && r_type == R_PPC64_TLS)
- {
- /* Check for toc tls entries. */
- char *toc_tls;
+ char *lgot_masks;
+ lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
+ tls_mask = lgot_masks[r_symndx];
+ }
+ if (tls_mask == 0
+ && (r_type == R_PPC64_TLS
+ || r_type == R_PPC64_TLSGD
+ || r_type == R_PPC64_TLSLD))
+ {
+ /* Check for toc tls entries. */
+ char *toc_tls;
- if (!get_tls_mask (&toc_tls, &toc_symndx, &toc_addend,
- &local_syms, rel, input_bfd))
- return FALSE;
+ if (!get_tls_mask (&toc_tls, &toc_symndx, &toc_addend,
+ &local_syms, rel, input_bfd))
+ return FALSE;
- if (toc_tls)
- tls_mask = *toc_tls;
- }
+ if (toc_tls)
+ tls_mask = *toc_tls;
}
/* Check that tls relocs are used with tls syms, and non-tls
@@ -10324,7 +10394,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|| (sym_type == STT_SECTION
&& (sec->flags & SEC_THREAD_LOCAL) != 0))))
{
- if (r_type == R_PPC64_TLS && tls_mask != 0)
+ if (tls_mask != 0
+ && (r_type == R_PPC64_TLS
+ || r_type == R_PPC64_TLSGD
+ || r_type == R_PPC64_TLSLD))
/* R_PPC64_TLS is OK against a symbol in the TOC. */
;
else
@@ -10520,9 +10593,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
bfd_vma offset;
tls_ldgd_opt:
- /* We know that the next reloc is on a tls_get_addr
- call, since ppc64_elf_tls_optimize checks this. */
- offset = rel[1].r_offset;
+ offset = (bfd_vma) -1;
+ /* If not using the newer R_PPC64_TLSGD/LD to mark
+ __tls_get_addr calls, we must trust that the call
+ stays with its arg setup insns, ie. that the next
+ reloc is the __tls_get_addr call associated with
+ the current reloc. Edit both insns. */
+ if (input_section->has_tls_get_addr_call
+ && rel + 1 < relend
+ && branch_reloc_hash_match (input_bfd, rel + 1,
+ htab->tls_get_addr,
+ htab->tls_get_addr_fd))
+ offset = rel[1].r_offset;
if ((tls_mask & tls_gd) != 0)
{
/* IE */
@@ -10531,8 +10613,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
insn1 &= (1 << 26) - (1 << 2);
insn1 |= 58 << 26; /* ld */
insn2 = 0x7c636a14; /* add 3,3,13 */
- rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
- R_PPC64_NONE);
+ if (offset != (bfd_vma) -1)
+ rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
+ R_PPC64_NONE);
if ((tls_mask & TLS_EXPLICIT) == 0)
r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3)
+ R_PPC64_GOT_TPREL16_DS);
@@ -10570,34 +10653,131 @@ ppc64_elf_relocate_section (bfd *output_bfd,
}
r_type = R_PPC64_TPREL16_HA;
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
- rel[1].r_info = ELF64_R_INFO (r_symndx,
- R_PPC64_TPREL16_LO);
- rel[1].r_offset += d_offset;
- rel[1].r_addend = rel->r_addend;
+ if (offset != (bfd_vma) -1)
+ {
+ rel[1].r_info = ELF64_R_INFO (r_symndx,
+ R_PPC64_TPREL16_LO);
+ rel[1].r_offset = offset + d_offset;
+ rel[1].r_addend = rel->r_addend;
+ }
}
bfd_put_32 (output_bfd, insn1,
contents + rel->r_offset - d_offset);
+ if (offset != (bfd_vma) -1)
+ {
+ insn3 = bfd_get_32 (output_bfd,
+ contents + offset + 4);
+ if (insn3 == NOP
+ || insn3 == CROR_151515 || insn3 == CROR_313131)
+ {
+ rel[1].r_offset += 4;
+ bfd_put_32 (output_bfd, insn2, contents + offset + 4);
+ insn2 = NOP;
+ }
+ bfd_put_32 (output_bfd, insn2, contents + offset);
+ }
+ if ((tls_mask & tls_gd) == 0
+ && (tls_gd == 0 || toc_symndx != 0))
+ {
+ /* We changed the symbol. Start over in order
+ to get h, sym, sec etc. right. */
+ rel--;
+ continue;
+ }
+ }
+ break;
+
+ case R_PPC64_TLSGD:
+ if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ {
+ unsigned int insn2, insn3;
+ bfd_vma offset = rel->r_offset;
+
+ if ((tls_mask & TLS_TPRELGD) != 0)
+ {
+ /* IE */
+ r_type = R_PPC64_NONE;
+ insn2 = 0x7c636a14; /* add 3,3,13 */
+ }
+ else
+ {
+ /* LE */
+ if (toc_symndx != 0)
+ {
+ r_symndx = toc_symndx;
+ rel->r_addend = toc_addend;
+ }
+ r_type = R_PPC64_TPREL16_LO;
+ rel->r_offset = offset + d_offset;
+ insn2 = 0x38630000; /* addi 3,3,0 */
+ }
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ /* Zap the reloc on the _tls_get_addr call too. */
+ BFD_ASSERT (offset == rel[1].r_offset);
+ rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
+ R_PPC64_NONE);
insn3 = bfd_get_32 (output_bfd,
contents + offset + 4);
if (insn3 == NOP
|| insn3 == CROR_151515 || insn3 == CROR_313131)
{
- rel[1].r_offset += 4;
+ rel->r_offset += 4;
bfd_put_32 (output_bfd, insn2, contents + offset + 4);
insn2 = NOP;
}
bfd_put_32 (output_bfd, insn2, contents + offset);
- if ((tls_mask & tls_gd) == 0
- && (tls_gd == 0 || toc_symndx != 0))
+ if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
{
- /* We changed the symbol. Start over in order
- to get h, sym, sec etc. right. */
rel--;
continue;
}
}
break;
+ case R_PPC64_TLSLD:
+ if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ {
+ unsigned int insn2, insn3;
+ bfd_vma offset = rel->r_offset;
+
+ if (toc_symndx)
+ sec = local_sections[toc_symndx];
+ for (r_symndx = 0;
+ r_symndx < symtab_hdr->sh_info;
+ r_symndx++)
+ if (local_sections[r_symndx] == sec)
+ break;
+ if (r_symndx >= symtab_hdr->sh_info)
+ r_symndx = 0;
+ rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (r_symndx != 0)
+ rel->r_addend -= (local_syms[r_symndx].st_value
+ + sec->output_offset
+ + sec->output_section->vma);
+
+ r_type = R_PPC64_TPREL16_LO;
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ rel->r_offset = offset + d_offset;
+ /* Zap the reloc on the _tls_get_addr call too. */
+ BFD_ASSERT (offset == rel[1].r_offset);
+ rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info),
+ R_PPC64_NONE);
+ insn2 = 0x38630000; /* addi 3,3,0 */
+ insn3 = bfd_get_32 (output_bfd,
+ contents + offset + 4);
+ if (insn3 == NOP
+ || insn3 == CROR_151515 || insn3 == CROR_313131)
+ {
+ rel->r_offset += 4;
+ bfd_put_32 (output_bfd, insn2, contents + offset + 4);
+ insn2 = NOP;
+ }
+ bfd_put_32 (output_bfd, insn2, contents + offset);
+ rel--;
+ continue;
+ }
+ break;
+
case R_PPC64_DTPMOD64:
if (rel + 1 < relend
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPREL64)
@@ -10851,6 +11031,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_NONE:
case R_PPC64_TLS:
+ case R_PPC64_TLSGD:
+ case R_PPC64_TLSLD:
case R_PPC64_GNU_VTINHERIT:
case R_PPC64_GNU_VTENTRY:
continue;