aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r--bfd/elf32-ppc.c192
1 files changed, 169 insertions, 23 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index ac416ba..5db64b0 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -753,7 +753,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
- /* Marker reloc for TLS. */
+ /* Marker relocs for TLS. */
HOWTO (R_PPC_TLS,
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -768,6 +768,34 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
0, /* dst_mask */
FALSE), /* pcrel_offset */
+ HOWTO (R_PPC_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_PPC_TLSGD", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_PPC_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_PPC_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_PPC_DTPMOD32,
@@ -1531,6 +1559,8 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
case BFD_RELOC_CTOR: r = R_PPC_ADDR32; break;
case BFD_RELOC_PPC_TOC16: r = R_PPC_TOC16; break;
case BFD_RELOC_PPC_TLS: r = R_PPC_TLS; break;
+ case BFD_RELOC_PPC_TLSGD: r = R_PPC_TLSGD; break;
+ case BFD_RELOC_PPC_TLSLD: r = R_PPC_TLSLD; break;
case BFD_RELOC_PPC_DTPMOD: r = R_PPC_DTPMOD32; break;
case BFD_RELOC_PPC_TPREL16: r = R_PPC_TPREL16; break;
case BFD_RELOC_PPC_TPREL16_LO: r = R_PPC_TPREL16_LO; break;
@@ -3288,6 +3318,7 @@ ppc_elf_check_relocs (bfd *abfd,
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *got2, *sreloc;
+ struct elf_link_hash_entry *tga;
if (info->relocatable)
return TRUE;
@@ -3313,6 +3344,8 @@ ppc_elf_check_relocs (bfd *abfd,
ppc_elf_howto_init ();
htab = ppc_elf_hash_table (info);
+ tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
+ FALSE, FALSE, TRUE);
symtab_hdr = &elf_symtab_hdr (abfd);
sym_hashes = elf_sym_hashes (abfd);
got2 = bfd_get_section_by_name (abfd, ".got2");
@@ -3324,7 +3357,7 @@ ppc_elf_check_relocs (bfd *abfd,
unsigned long r_symndx;
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h;
- int tls_type = 0;
+ int tls_type;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
@@ -3351,9 +3384,44 @@ ppc_elf_check_relocs (bfd *abfd,
BFD_ASSERT (h == htab->elf.hgot);
}
+ tls_type = 0;
r_type = ELF32_R_TYPE (rel->r_info);
+ if (h != NULL && h == tga)
+ switch (r_type)
+ {
+ default:
+ break;
+
+ case R_PPC_PLTREL24:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_REL24:
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ case R_PPC_ADDR24:
+ case R_PPC_ADDR14:
+ case R_PPC_ADDR14_BRTAKEN:
+ case R_PPC_ADDR14_BRNTAKEN:
+ if (rel != relocs
+ && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
+ || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_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_PPC_TLSGD:
+ case R_PPC_TLSLD:
+ /* These special tls relocs tie a call to __tls_get_addr with
+ its parameter symbol. */
+ break;
+
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
case R_PPC_GOT_TLSLD16_HI:
@@ -3607,7 +3675,7 @@ ppc_elf_check_relocs (bfd *abfd,
/* This refers only to functions defined in the shared library. */
case R_PPC_LOCAL24PC:
- if (h && h == htab->elf.hgot && htab->plt_type == PLT_UNSET)
+ if (h != NULL && h == htab->elf.hgot && htab->plt_type == PLT_UNSET)
{
htab->plt_type = PLT_OLD;
htab->old_bfd = abfd;
@@ -4484,7 +4552,8 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
if (pass == 0)
{
- if (!expecting_tls_get_addr)
+ if (!expecting_tls_get_addr
+ || !sec->has_tls_get_addr_call)
continue;
if (rel + 1 < relend
@@ -6234,16 +6303,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
for the final instruction stream. */
tls_mask = 0;
tls_gd = 0;
- if (IS_PPC_TLS_RELOC (r_type))
+ if (h != NULL)
+ tls_mask = ((struct ppc_elf_link_hash_entry *) h)->tls_mask;
+ else if (local_got_offsets != NULL)
{
- if (h != NULL)
- tls_mask = ((struct ppc_elf_link_hash_entry *) h)->tls_mask;
- else if (local_got_offsets != NULL)
- {
- char *lgot_masks;
- lgot_masks = (char *) (local_got_offsets + symtab_hdr->sh_info);
- tls_mask = lgot_masks[r_symndx];
- }
+ char *lgot_masks;
+ lgot_masks = (char *) (local_got_offsets + symtab_hdr->sh_info);
+ tls_mask = lgot_masks[r_symndx];
}
/* Ensure reloc mapping code below stays sane. */
@@ -6361,7 +6427,17 @@ ppc_elf_relocate_section (bfd *output_bfd,
bfd_vma offset;
tls_ldgd_opt:
- offset = rel[1].r_offset;
+ offset = (bfd_vma) -1;
+ /* If not using the newer R_PPC_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))
+ offset = rel[1].r_offset;
if ((tls_mask & tls_gd) != 0)
{
/* IE */
@@ -6369,9 +6445,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
contents + rel->r_offset - d_offset);
insn1 &= (1 << 26) - 1;
insn1 |= 32 << 26; /* lwz */
- insn2 = 0x7c631214; /* add 3,3,2 */
- rel[1].r_info
- = ELF32_R_INFO (ELF32_R_SYM (rel[1].r_info), R_PPC_NONE);
+ if (offset != (bfd_vma) -1)
+ {
+ rel[1].r_info
+ = ELF32_R_INFO (ELF32_R_SYM (rel[1].r_info),
+ R_PPC_NONE);
+ insn2 = 0x7c631214; /* add 3,3,2 */
+ bfd_put_32 (output_bfd, insn2, contents + offset);
+ }
r_type = (((r_type - (R_PPC_GOT_TLSGD16 & 3)) & 3)
+ R_PPC_GOT_TPREL16);
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
@@ -6380,7 +6461,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
{
/* LE */
insn1 = 0x3c620000; /* addis 3,2,0 */
- insn2 = 0x38630000; /* addi 3,3,0 */
if (tls_gd == 0)
{
/* Was an LD reloc. */
@@ -6399,14 +6479,17 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
r_type = R_PPC_TPREL16_HA;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
- rel[1].r_info = ELF32_R_INFO (r_symndx,
- R_PPC_TPREL16_LO);
- rel[1].r_offset += d_offset;
- rel[1].r_addend = rel->r_addend;
+ if (offset != (bfd_vma) -1)
+ {
+ rel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_TPREL16_LO);
+ rel[1].r_offset = offset + d_offset;
+ rel[1].r_addend = rel->r_addend;
+ insn2 = 0x38630000; /* addi 3,3,0 */
+ bfd_put_32 (output_bfd, insn2, contents + offset);
+ }
}
bfd_put_32 (output_bfd, insn1,
contents + rel->r_offset - d_offset);
- bfd_put_32 (output_bfd, insn2, contents + offset);
if (tls_gd == 0)
{
/* We changed the symbol on an LD reloc. Start over
@@ -6416,6 +6499,66 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
}
break;
+
+ case R_PPC_TLSGD:
+ if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ {
+ unsigned int insn2;
+ bfd_vma offset = rel->r_offset;
+
+ if ((tls_mask & TLS_TPRELGD) != 0)
+ {
+ /* IE */
+ r_type = R_PPC_NONE;
+ insn2 = 0x7c631214; /* add 3,3,2 */
+ }
+ else
+ {
+ /* LE */
+ r_type = R_PPC_TPREL16_LO;
+ rel->r_offset += d_offset;
+ insn2 = 0x38630000; /* addi 3,3,0 */
+ }
+ rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+ bfd_put_32 (output_bfd, insn2, contents + offset);
+ /* Zap the reloc on the _tls_get_addr call too. */
+ BFD_ASSERT (offset == rel[1].r_offset);
+ rel[1].r_info = ELF32_R_INFO (ELF32_R_SYM (rel[1].r_info),
+ R_PPC_NONE);
+ }
+ break;
+
+ case R_PPC_TLSLD:
+ if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ {
+ unsigned int insn2;
+
+ 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);
+
+ rel->r_info = ELF32_R_INFO (r_symndx, R_PPC_TPREL16_LO);
+ rel->r_offset += d_offset;
+ insn2 = 0x38630000; /* addi 3,3,0 */
+ bfd_put_32 (output_bfd, insn2,
+ contents + rel->r_offset - d_offset);
+ /* Zap the reloc on the _tls_get_addr call too. */
+ BFD_ASSERT (rel->r_offset - d_offset == rel[1].r_offset);
+ rel[1].r_info = ELF32_R_INFO (ELF32_R_SYM (rel[1].r_info),
+ R_PPC_NONE);
+ rel--;
+ continue;
+ }
+ break;
}
/* Handle other relocations that tweak non-addend part of insn. */
@@ -6468,6 +6611,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_NONE:
case R_PPC_TLS:
+ case R_PPC_TLSGD:
+ case R_PPC_TLSLD:
case R_PPC_EMB_MRKREF:
case R_PPC_GNU_VTINHERIT:
case R_PPC_GNU_VTENTRY:
@@ -6509,6 +6654,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_GOT16_LO:
case R_PPC_GOT16_HI:
case R_PPC_GOT16_HA:
+ tls_mask = 0;
dogot:
{
/* Relocation is to the entry for this symbol in the global