diff options
author | Kaz Kojima <kkojima@rr.iij4u.or.jp> | 2002-10-11 14:32:03 +0000 |
---|---|---|
committer | Kaz Kojima <kkojima@rr.iij4u.or.jp> | 2002-10-11 14:32:03 +0000 |
commit | 3376eaf57788bb1487e6d96bd74c647612dd91c9 (patch) | |
tree | ed21040f9814662bc28babc6ad4addfe505602a9 /bfd/elf32-sh.c | |
parent | 5dc976556d5fa7518147ddb609b3c274df56ab79 (diff) | |
download | gdb-3376eaf57788bb1487e6d96bd74c647612dd91c9.zip gdb-3376eaf57788bb1487e6d96bd74c647612dd91c9.tar.gz gdb-3376eaf57788bb1487e6d96bd74c647612dd91c9.tar.bz2 |
* elf32-sh.c (sh_elf_optimized_tls_reloc, sh_elf_mkobject,
sh_elf_object_p, dtpoff_base): New functions.
(sh_elf_howto_table): Add TLS relocs.
(sh_reloc_map): Likewise.
(sh_elf_info_to_howto): Support TLS relocs.
(elf_sh_link_hash_entry): Add tls_type and tls_tpoff32.
(sh_elf_hash_entry, sh_elf_tdata, sh_elf_local_got_tls_type):
New macros.
(sh_elf_obj_tdata): New.
(elf_sh_link_hash_table): Add tls_ldm_got.
(sh_elf_link_hash_table_create): Clear refcount of tls_ldm_got.
(allocate_dynrelocs): Support TLS relocs.
(sh_elf_size_dynamic_sections): Likewise.
(sh_elf_relocate_section): Support TLS relocs. Don't try to find
.rela.got section when found already. Return false after printing
error about unresolvable relocation.
(sh_elf_gc_sweep_hook): Support TLS relocs.
(sh_elf_check_relocs): Likewise.
(sh_elf_finish_dynamic_symbol): Likewise.
(bfd_elf32_mkobject, elf_backend_object_p): Define for TLS case.
* reloc.c: Add SH TLS relocs.
* libbfd.h: Regenerate.
Diffstat (limited to 'bfd/elf32-sh.c')
-rw-r--r-- | bfd/elf32-sh.c | 961 |
1 files changed, 935 insertions, 26 deletions
diff --git a/bfd/elf32-sh.c b/bfd/elf32-sh.c index 5fe0f58..53d2841 100644 --- a/bfd/elf32-sh.c +++ b/bfd/elf32-sh.c @@ -59,6 +59,12 @@ static bfd_byte *sh_elf_get_relocated_section_contents static void sh_elf_copy_indirect_symbol PARAMS ((struct elf_backend_data *, struct elf_link_hash_entry *, struct elf_link_hash_entry *)); +static int sh_elf_optimized_tls_reloc + PARAMS ((struct bfd_link_info *, int, int)); +static boolean sh_elf_mkobject + PARAMS((bfd *)); +static boolean sh_elf_object_p + PARAMS((bfd *)); static boolean sh_elf_check_relocs PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *)); @@ -82,6 +88,8 @@ static boolean create_got_section PARAMS((bfd *, struct bfd_link_info *)); static boolean sh_elf_create_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *)); +static bfd_vma dtpoff_base + PARAMS ((struct bfd_link_info *)); static asection * sh_elf_gc_mark_hook PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, struct elf_link_hash_entry *, Elf_Internal_Sym *)); @@ -713,14 +721,119 @@ static reloc_howto_type sh_elf_howto_table[] = EMPTY_HOWTO (141), EMPTY_HOWTO (142), EMPTY_HOWTO (143), - EMPTY_HOWTO (144), - EMPTY_HOWTO (145), - EMPTY_HOWTO (146), - EMPTY_HOWTO (147), - EMPTY_HOWTO (148), - EMPTY_HOWTO (149), - EMPTY_HOWTO (150), - EMPTY_HOWTO (151), + + HOWTO (R_SH_TLS_GD_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_GD_32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_LD_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_LD_32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_LDO_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_LDO_32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_IE_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_IE_32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_LE_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_LE_32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_DTPMOD32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_DTPMOD32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_DTPOFF32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_DTPOFF32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_SH_TLS_TPOFF32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* */ + "R_SH_TLS_TPOFF32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + EMPTY_HOWTO (152), EMPTY_HOWTO (153), EMPTY_HOWTO (154), @@ -1776,6 +1889,14 @@ static const struct elf_reloc_map sh_reloc_map[] = { BFD_RELOC_VTABLE_ENTRY, R_SH_GNU_VTENTRY }, { BFD_RELOC_SH_LOOP_START, R_SH_LOOP_START }, { BFD_RELOC_SH_LOOP_END, R_SH_LOOP_END }, + { BFD_RELOC_SH_TLS_GD_32, R_SH_TLS_GD_32 }, + { BFD_RELOC_SH_TLS_LD_32, R_SH_TLS_LD_32 }, + { BFD_RELOC_SH_TLS_LDO_32, R_SH_TLS_LDO_32 }, + { BFD_RELOC_SH_TLS_IE_32, R_SH_TLS_IE_32 }, + { BFD_RELOC_SH_TLS_LE_32, R_SH_TLS_LE_32 }, + { BFD_RELOC_SH_TLS_DTPMOD32, R_SH_TLS_DTPMOD32 }, + { BFD_RELOC_SH_TLS_DTPOFF32, R_SH_TLS_DTPOFF32 }, + { BFD_RELOC_SH_TLS_TPOFF32, R_SH_TLS_TPOFF32 }, { BFD_RELOC_32_GOT_PCREL, R_SH_GOT32 }, { BFD_RELOC_32_PLT_PCREL, R_SH_PLT32 }, { BFD_RELOC_SH_COPY, R_SH_COPY }, @@ -1874,6 +1995,7 @@ sh_elf_info_to_howto (abfd, cache_ptr, dst) BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_2 || r > R_SH_LAST_INVALID_RELOC_2); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_3 || r > R_SH_LAST_INVALID_RELOC_3); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_4 || r > R_SH_LAST_INVALID_RELOC_4); + BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_5 || r > R_SH_LAST_INVALID_RELOC_5); cache_ptr->howto = &sh_elf_howto_table[r]; } @@ -3390,8 +3512,45 @@ struct elf_sh_link_hash_entry struct elf_sh_dyn_relocs *dyn_relocs; bfd_signed_vma gotplt_refcount; + + enum { + GOT_UNKNOWN = 0, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE + } tls_type; + + /* If true, R_SH_TLS_TPOFF32 relocation is generated. */ + boolean tls_tpoff32; +}; + +#define sh_elf_hash_entry(ent) ((struct elf_sh_link_hash_entry *)(ent)) + +struct sh_elf_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; }; +#define sh_elf_tdata(abfd) \ + ((struct sh_elf_obj_tdata *) (abfd)->tdata.any) + +#define sh_elf_local_got_tls_type(abfd) \ + (sh_elf_tdata (abfd)->local_got_tls_type) + +/* Override the generic function because we need to store sh_elf_obj_tdata + as the specific tdata. */ + +static boolean +sh_elf_mkobject (abfd) + bfd *abfd; +{ + bfd_size_type amt = sizeof (struct sh_elf_obj_tdata); + abfd->tdata.any = bfd_zalloc (abfd, amt); + if (abfd->tdata.any == NULL) + return false; + return true; +} + /* sh ELF linker hash table. */ struct elf_sh_link_hash_table @@ -3409,6 +3568,13 @@ struct elf_sh_link_hash_table /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; + + /* A counter or offset to track a TLS got entry. */ + union + { + bfd_signed_vma refcount; + bfd_vma offset; + } tls_ldm_got; }; /* Traverse an sh ELF linker hash table. */ @@ -3458,6 +3624,8 @@ sh_elf_link_hash_newfunc (entry, table, string) #ifdef INCLUDE_SHMEDIA ret->datalabel_got_offset = (bfd_vma) -1; #endif + ret->tls_type = GOT_UNKNOWN; + ret->tls_tpoff32 = false; } return (struct bfd_hash_entry *) ret; @@ -3491,6 +3659,7 @@ sh_elf_link_hash_table_create (abfd) ret->sdynbss = NULL; ret->srelbss = NULL; ret->sym_sec.abfd = NULL; + ret->tls_ldm_got.refcount = 0; return &ret->root.root; } @@ -3946,6 +4115,7 @@ allocate_dynrelocs (h, inf) { asection *s; boolean dyn; + int tls_type = sh_elf_hash_entry (h)->tls_type; /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ @@ -3972,8 +4142,18 @@ allocate_dynrelocs (h, inf) h->got.offset = s->_raw_size; #endif s->_raw_size += 4; + /* R_SH_TLS_GD needs 2 consecutive GOT slots. */ + if (tls_type == GOT_TLS_GD) + s->_raw_size += 4; dyn = htab->root.dynamic_sections_created; - if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)) + /* R_SH_TLS_IE_32 needs one dynamic relocation, + R_SH_TLS_GD needs one if local symbol and two if global. */ + if ((tls_type == GOT_TLS_GD && h->dynindx == -1) + || tls_type == GOT_TLS_IE) + htab->srelgot->_raw_size += sizeof (Elf32_External_Rela); + else if (tls_type == GOT_TLS_GD) + htab->srelgot->_raw_size += 2 * sizeof (Elf32_External_Rela); + else if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)) htab->srelgot->_raw_size += sizeof (Elf32_External_Rela); } else @@ -4009,6 +4189,9 @@ allocate_dynrelocs (h, inf) } else { + if (sh_elf_hash_entry (h)->tls_tpoff32) + goto keep; + /* For the non-shared case, discard space for relocs against symbols which turn out to need copy relocs or are not dynamic. */ @@ -4116,6 +4299,7 @@ sh_elf_size_dynamic_sections (output_bfd, info) { bfd_signed_vma *local_got; bfd_signed_vma *end_local_got; + char *local_tls_type; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; @@ -4161,6 +4345,7 @@ sh_elf_size_dynamic_sections (output_bfd, info) locsymcount *= 2; #endif end_local_got = local_got + locsymcount; + local_tls_type = sh_elf_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; for (; local_got < end_local_got; ++local_got) @@ -4169,14 +4354,28 @@ sh_elf_size_dynamic_sections (output_bfd, info) { *local_got = s->_raw_size; s->_raw_size += 4; + if (*local_tls_type == GOT_TLS_GD) + s->_raw_size += 4; if (info->shared) srel->_raw_size += sizeof (Elf32_External_Rela); } else *local_got = (bfd_vma) -1; + ++local_tls_type; } } + if (htab->tls_ldm_got.refcount > 0) + { + /* Allocate 2 got entries and 1 dynamic reloc for R_SH_TLS_LD_32 + relocs. */ + htab->tls_ldm_got.offset = htab->sgot->_raw_size; + htab->sgot->_raw_size += 8; + htab->srelgot->_raw_size += sizeof (Elf32_External_Rela); + } + else + htab->tls_ldm_got.offset = -1; + /* Allocate global sym .plt and .got entries, and space for global sym dynamic relocs. */ elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (PTR) info); @@ -4312,6 +4511,7 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, asection *sgotplt; asection *splt; asection *sreloc; + asection *srelgot; htab = sh_elf_hash_table (info); symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; @@ -4323,6 +4523,7 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, sgotplt = htab->sgotplt; splt = htab->splt; sreloc = NULL; + srelgot = NULL; rel = relocs; relend = relocs + input_section->reloc_count; @@ -4338,6 +4539,8 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, bfd_vma addend = (bfd_vma) 0; bfd_reloc_status_type r; int seen_stt_datalabel = 0; + bfd_vma off; + int tls_type; r_symndx = ELF32_R_SYM (rel->r_info); @@ -4359,6 +4562,8 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, && r_type <= (int) R_SH_LAST_INVALID_RELOC_3) || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_4 && r_type <= (int) R_SH_LAST_INVALID_RELOC_4) + || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_5 + && r_type <= (int) R_SH_LAST_INVALID_RELOC_5) || (r_type >= (int) R_SH_FIRST_INVALID_RELOC_2 && r_type <= (int) R_SH_LAST_INVALID_RELOC_2)) { @@ -4529,15 +4734,18 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, with them here. */ || ((input_section->flags & SEC_DEBUGGING) != 0 && (h->elf_link_hash_flags - & ELF_LINK_HASH_DEF_DYNAMIC) != 0)))) + & ELF_LINK_HASH_DEF_DYNAMIC) != 0))) + || (sec->output_section == NULL + && (sh_elf_hash_entry (h)->tls_type == GOT_TLS_IE + || sh_elf_hash_entry (h)->tls_type == GOT_TLS_GD))) relocation = 0; else if (sec->output_section == NULL) { (*_bfd_error_handler) - (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"), + (_("%s: unresolvable relocation against symbol `%s' from %s section"), bfd_archive_filename (input_bfd), h->root.root.string, bfd_get_section_name (input_bfd, input_section)); - relocation = 0; + return false; } else relocation = ((h->root.u.def.value @@ -4782,7 +4990,6 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, if (h != NULL) { - bfd_vma off; boolean dyn; off = h->got.offset; @@ -4840,8 +5047,6 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, } else { - bfd_vma off; - #ifdef INCLUDE_SHMEDIA if (rel->r_addend) { @@ -4875,11 +5080,14 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, if (info->shared) { - asection *srelgot; Elf_Internal_Rela outrel; - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (srelgot != NULL); + if (srelgot == NULL) + { + srelgot = bfd_get_section_by_name (dynobj, + ".rela.got"); + BFD_ASSERT (srelgot != NULL); + } outrel.r_offset = (sgot->output_section->vma + sgot->output_offset @@ -5013,6 +5221,450 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, rel->r_offset, sec, start, end); break; } + + case R_SH_TLS_GD_32: + case R_SH_TLS_IE_32: + r_type = sh_elf_optimized_tls_reloc (info, r_type, h == NULL); + tls_type = GOT_UNKNOWN; + if (h == NULL && local_got_offsets) + tls_type = sh_elf_local_got_tls_type (input_bfd) [r_symndx]; + else if (h != NULL) + { + tls_type = sh_elf_hash_entry (h)->tls_type; + if (! info->shared + && (h->dynindx == -1 + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)) + && (tls_type == GOT_TLS_IE + || sh_elf_hash_entry (h)->tls_tpoff32)) + r_type = R_SH_TLS_LE_32; + } + + if (r_type == R_SH_TLS_GD_32 && tls_type == GOT_TLS_IE) + r_type = R_SH_TLS_IE_32; + + if (r_type == R_SH_TLS_LE_32) + { + bfd_vma offset; + unsigned short insn; + int indx; + Elf_Internal_Rela outrel; + + if (ELF32_R_TYPE (rel->r_info) == R_SH_TLS_GD_32) + { + /* GD->LE transition: + mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1; + jsr @r1; add r12,r4; bra 3f; nop; .align 2; + 1: .long x$TLSGD; 2: .long __tls_get_addr@PLT; 3: + We change it into: + mov.l 1f,r4; stc gbr,r0; add r4,r0; nop; + nop; nop; ... + 1: .long x@TPOFF; 2: .long __tls_get_addr@PLT; 3:. */ + + offset = rel->r_offset; + BFD_ASSERT (offset >= 16); + /* Size of GD instructions is 16 or 18. */ + offset -= 16; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + if ((insn & 0xff00) == 0xc700) + { + BFD_ASSERT (offset >= 2); + offset -= 2; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + } + + BFD_ASSERT ((insn & 0xff00) == 0xd400); + insn = bfd_get_16 (input_bfd, contents + offset + 2); + BFD_ASSERT ((insn & 0xff00) == 0xc700); + insn = bfd_get_16 (input_bfd, contents + offset + 4); + BFD_ASSERT ((insn & 0xff00) == 0xd100); + insn = bfd_get_16 (input_bfd, contents + offset + 6); + BFD_ASSERT (insn == 0x310c); + insn = bfd_get_16 (input_bfd, contents + offset + 8); + BFD_ASSERT (insn == 0x410b); + insn = bfd_get_16 (input_bfd, contents + offset + 10); + BFD_ASSERT (insn == 0x34cc); + + bfd_put_16 (output_bfd, 0x0012, contents + offset + 2); + bfd_put_16 (output_bfd, 0x304c, contents + offset + 4); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 6); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 8); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 10); + } + else + { + int index; + + /* IE->LE transition: + mov.l 1f,r0; stc gbr,rN; mov.l @(r0,r12),rM; + bra 2f; add ...; .align 2; 1: x@GOTTPOFF; 2: + We change it into: + mov.l .Ln,rM; stc gbr,rN; nop; ...; + 1: x@TPOFF; 2:. */ + + offset = rel->r_offset; + BFD_ASSERT (offset >= 16); + /* Size of IE instructions is 10 or 12. */ + offset -= 10; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + if ((insn & 0xf0ff) == 0x0012) + { + BFD_ASSERT (offset >= 2); + offset -= 2; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + } + + BFD_ASSERT ((insn & 0xff00) == 0xd000); + index = insn & 0x00ff; + insn = bfd_get_16 (input_bfd, contents + offset + 2); + BFD_ASSERT ((insn & 0xf0ff) == 0x0012); + insn = bfd_get_16 (input_bfd, contents + offset + 4); + BFD_ASSERT ((insn & 0xf0ff) == 0x00ce); + insn = 0xd000 | (insn & 0x0f00) | index; + bfd_put_16 (output_bfd, insn, contents + offset + 0); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 4); + } + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + indx = (h && h->dynindx != -1) ? h->dynindx : 0; + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset + + rel->r_offset); + outrel.r_info = ELF32_R_INFO (indx, R_SH_TLS_TPOFF32); + if (indx == 0) + outrel.r_addend = relocation - dtpoff_base (info); + else + outrel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + (((Elf32_External_Rela *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + continue; + } + + sgot = htab->sgot; + if (sgot == NULL) + abort (); + + if (h != NULL) + off = h->got.offset; + else + { + if (local_got_offsets == NULL) + abort (); + + off = local_got_offsets[r_symndx]; + } + + if ((off & 1) != 0) + off &= ~1; + else + { + Elf_Internal_Rela outrel; + Elf32_External_Rela *loc; + int dr_type, indx; + + if (srelgot == NULL) + { + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + } + + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + off); + + indx = (h && h->dynindx != -1) ? h->dynindx : 0; + dr_type = (r_type == R_SH_TLS_GD_32 ? R_SH_TLS_DTPMOD32 : + R_SH_TLS_TPOFF32); + if (dr_type == R_SH_TLS_TPOFF32 && indx == 0) + outrel.r_addend = relocation - dtpoff_base (info); + else + outrel.r_addend = 0; + outrel.r_info = ELF32_R_INFO (indx, dr_type); + loc = (Elf32_External_Rela *) srelgot->contents; + loc += srelgot->reloc_count++; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + + if (r_type == R_SH_TLS_GD_32) + { + if (indx == 0) + { + bfd_put_32 (output_bfd, + relocation - dtpoff_base (info), + sgot->contents + off + 4); + } + else + { + outrel.r_info = ELF32_R_INFO (indx, + R_SH_TLS_DTPOFF32); + outrel.r_offset += 4; + outrel.r_addend = 0; + srelgot->reloc_count++; + loc++; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + } + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; + } + + if (off >= (bfd_vma) -2) + abort (); + + if (r_type == (int) ELF32_R_TYPE (rel->r_info)) + relocation = sgot->output_offset + off; + else + { + bfd_vma offset; + unsigned short insn; + + /* GD->IE transition: + mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1; + jsr @r1; add r12,r4; bra 3f; nop; .align 2; + 1: .long x$TLSGD; 2: .long __tls_get_addr@PLT; 3: + We change it into: + mov.l 1f,r0; stc gbr,r4; mov.l @(r0,r12),r0; add r4,r0; + nop; nop; bra 3f; nop; .align 2; + 1: .long x@TPOFF; 2:...; 3:. */ + + offset = rel->r_offset; + BFD_ASSERT (offset >= 16); + /* Size of GD instructions is 16 or 18. */ + offset -= 16; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + if ((insn & 0xff00) == 0xc700) + { + BFD_ASSERT (offset >= 2); + offset -= 2; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + } + + BFD_ASSERT ((insn & 0xff00) == 0xd400); + + /* Replace mov.l 1f,R4 with mov.l 1f,r0. */ + bfd_put_16 (output_bfd, insn & 0xf0ff, contents + offset); + + insn = bfd_get_16 (input_bfd, contents + offset + 2); + BFD_ASSERT ((insn & 0xff00) == 0xc700); + insn = bfd_get_16 (input_bfd, contents + offset + 4); + BFD_ASSERT ((insn & 0xff00) == 0xd100); + insn = bfd_get_16 (input_bfd, contents + offset + 6); + BFD_ASSERT (insn == 0x310c); + insn = bfd_get_16 (input_bfd, contents + offset + 8); + BFD_ASSERT (insn == 0x410b); + insn = bfd_get_16 (input_bfd, contents + offset + 10); + BFD_ASSERT (insn == 0x34cc); + + bfd_put_16 (output_bfd, 0x0412, contents + offset + 2); + bfd_put_16 (output_bfd, 0x00ce, contents + offset + 4); + bfd_put_16 (output_bfd, 0x304c, contents + offset + 6); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 8); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 10); + + bfd_put_32 (output_bfd, sgot->output_offset + off, + contents + rel->r_offset); + + continue; + } + + addend = rel->r_addend; + + goto final_link_relocate; + + case R_SH_TLS_LD_32: + if (! info->shared) + { + bfd_vma offset; + unsigned short insn; + + /* LD->LE transition: + mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1; + jsr @r1; add r12,r4; bra 3f; nop; .align 2; + 1: .long x$TLSLD; 2: .long __tls_get_addr@PLT; 3: + We change it into: + stc gbr,r0; nop; nop; nop; + nop; nop; bra 3f; ...; 3:. */ + + offset = rel->r_offset; + BFD_ASSERT (offset >= 16); + /* Size of LD instructions is 16 or 18. */ + offset -= 16; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + if ((insn & 0xff00) == 0xc700) + { + BFD_ASSERT (offset >= 2); + offset -= 2; + insn = bfd_get_16 (input_bfd, contents + offset + 0); + } + + BFD_ASSERT ((insn & 0xff00) == 0xd400); + insn = bfd_get_16 (input_bfd, contents + offset + 2); + BFD_ASSERT ((insn & 0xff00) == 0xc700); + insn = bfd_get_16 (input_bfd, contents + offset + 4); + BFD_ASSERT ((insn & 0xff00) == 0xd100); + insn = bfd_get_16 (input_bfd, contents + offset + 6); + BFD_ASSERT (insn == 0x310c); + insn = bfd_get_16 (input_bfd, contents + offset + 8); + BFD_ASSERT (insn == 0x410b); + insn = bfd_get_16 (input_bfd, contents + offset + 10); + BFD_ASSERT (insn == 0x34cc); + + bfd_put_16 (output_bfd, 0x0012, contents + offset + 0); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 2); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 4); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 6); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 8); + bfd_put_16 (output_bfd, 0x0009, contents + offset + 10); + + continue; + } + + sgot = htab->sgot; + if (sgot == NULL) + abort (); + + off = htab->tls_ldm_got.offset; + if (off & 1) + off &= ~1; + else + { + Elf_Internal_Rela outrel; + Elf32_External_Rela *loc; + + srelgot = htab->srelgot; + if (srelgot == NULL) + abort (); + + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + off); + outrel.r_addend = 0; + outrel.r_info = ELF32_R_INFO (0, R_SH_TLS_DTPMOD32); + loc = (Elf32_External_Rela *) srelgot->contents; + loc += srelgot->reloc_count++; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + htab->tls_ldm_got.offset |= 1; + } + + relocation = sgot->output_offset + off; + addend = rel->r_addend; + + goto final_link_relocate; + + case R_SH_TLS_LDO_32: + if (! info->shared) + { + int indx; + Elf_Internal_Rela outrel; + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + indx = (h && h->dynindx != -1) ? h->dynindx : 0; + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset + + rel->r_offset); + outrel.r_info = ELF32_R_INFO (indx, R_SH_TLS_TPOFF32); + if (indx == 0) + outrel.r_addend = relocation - dtpoff_base (info); + else + outrel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + (((Elf32_External_Rela *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + continue; + } + else + relocation -= dtpoff_base (info); + + addend = rel->r_addend; + goto final_link_relocate; + + case R_SH_TLS_LE_32: + { + int indx; + Elf_Internal_Rela outrel; + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + indx = (h && h->dynindx != -1) ? h->dynindx : 0; + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset + + rel->r_offset); + outrel.r_info = ELF32_R_INFO (indx, R_SH_TLS_TPOFF32); + if (indx == 0) + outrel.r_addend = relocation - dtpoff_base (info); + else + outrel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + (((Elf32_External_Rela *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + continue; + } } relocation_done: @@ -5160,6 +5812,20 @@ sh_elf_get_relocated_section_contents (output_bfd, link_info, link_order, return NULL; } +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtpoff relocation. + This is PT_TLS segment p_vaddr. */ + +static bfd_vma +dtpoff_base (info) + struct bfd_link_info *info; +{ + /* If tls_segment is NULL, we should have signalled an error already. */ + if (elf_hash_table (info)->tls_segment == NULL) + return 0; + return elf_hash_table (info)->tls_segment->start; +} + static asection * sh_elf_gc_mark_hook (sec, info, rel, h, sym) asection *sec; @@ -5227,8 +5893,15 @@ sh_elf_gc_sweep_hook (abfd, info, sec, relocs) relend = relocs + sec->reloc_count; for (rel = relocs; rel < relend; rel++) - switch (ELF32_R_TYPE (rel->r_info)) + switch (sh_elf_optimized_tls_reloc (info, ELF32_R_TYPE (rel->r_info), + ELF32_R_SYM (rel->r_info) + >= symtab_hdr->sh_info)) { + case R_SH_TLS_LD_32: + if (sh_elf_hash_table (info)->tls_ldm_got.refcount > 0) + sh_elf_hash_table (info)->tls_ldm_got.refcount -= 1; + break; + case R_SH_GOT32: case R_SH_GOTOFF: case R_SH_GOTPC: @@ -5248,6 +5921,8 @@ sh_elf_gc_sweep_hook (abfd, info, sec, relocs) case R_SH_GOTPC_MEDHI16: case R_SH_GOTPC_HI16: #endif + case R_SH_TLS_GD_32: + case R_SH_TLS_IE_32: r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx >= symtab_hdr->sh_info) { @@ -5391,9 +6066,39 @@ sh_elf_copy_indirect_symbol (bed, dir, ind) edir->gotplt_refcount = eind->gotplt_refcount; eind->gotplt_refcount = 0; + if (ind->root.type == bfd_link_hash_indirect + && dir->got.refcount <= 0) + { + edir->tls_type = eind->tls_type; + eind->tls_type = GOT_UNKNOWN; + } + _bfd_elf_link_hash_copy_indirect (bed, dir, ind); } +static int +sh_elf_optimized_tls_reloc (info, r_type, is_local) + struct bfd_link_info *info; + int r_type; + int is_local; +{ + if (info->shared) + return r_type; + + switch (r_type) + { + case R_SH_TLS_GD_32: + case R_SH_TLS_IE_32: + if (is_local) + return R_SH_TLS_LE_32; + return R_SH_TLS_IE_32; + case R_SH_TLS_LD_32: + return R_SH_TLS_LE_32; + } + + return r_type; +} + /* Look through the relocs for a section during the first phase. Since we don't do .gots or .plts, we just need to consider the virtual table relocs for gc. */ @@ -5415,6 +6120,8 @@ sh_elf_check_relocs (abfd, info, sec, relocs) asection *sgot; asection *srelgot; asection *sreloc; + unsigned int r_type; + int tls_type, old_tls_type; sgot = NULL; srelgot = NULL; @@ -5440,15 +6147,27 @@ sh_elf_check_relocs (abfd, info, sec, relocs) unsigned long r_symndx; r_symndx = ELF32_R_SYM (rel->r_info); + r_type = ELF32_R_TYPE (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) h = NULL; else h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + r_type = sh_elf_optimized_tls_reloc (info, r_type, h == NULL); + if (! info->shared + && r_type == R_SH_TLS_IE_32 + && h != NULL + && h->root.type != bfd_link_hash_undefined + && h->root.type != bfd_link_hash_undefweak + && (h->dynindx == -1 + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))) + r_type = R_SH_TLS_LE_32; + /* Some relocs require a global offset table. */ if (htab->sgot == NULL) { - switch (ELF32_R_TYPE (rel->r_info)) + switch (r_type) { case R_SH_GOTPLT32: case R_SH_GOT32: @@ -5476,6 +6195,9 @@ sh_elf_check_relocs (abfd, info, sec, relocs) case R_SH_GOTPC_MEDHI16: case R_SH_GOTPC_HI16: #endif + case R_SH_TLS_GD_32: + case R_SH_TLS_LD_32: + case R_SH_TLS_IE_32: if (dynobj == NULL) htab->root.dynobj = dynobj = abfd; if (! create_got_section (dynobj, info)) @@ -5487,7 +6209,7 @@ sh_elf_check_relocs (abfd, info, sec, relocs) } } - switch (ELF32_R_TYPE (rel->r_info)) + switch (r_type) { /* This relocation describes the C++ object vtable hierarchy. Reconstruct it for later use during GC. */ @@ -5504,6 +6226,12 @@ sh_elf_check_relocs (abfd, info, sec, relocs) break; force_got: + case R_SH_TLS_IE_32: + if (info->shared) + info->flags |= DF_STATIC_TLS; + + /* FALLTHROUGH */ + case R_SH_TLS_GD_32: case R_SH_GOT32: #ifdef INCLUDE_SHMEDIA case R_SH_GOT_LOW16: @@ -5513,8 +6241,24 @@ sh_elf_check_relocs (abfd, info, sec, relocs) case R_SH_GOT10BY4: case R_SH_GOT10BY8: #endif + switch (r_type) + { + default: + tls_type = GOT_NORMAL; + break; + case R_SH_TLS_GD_32: + tls_type = GOT_TLS_GD; + break; + case R_SH_TLS_IE_32: + tls_type = GOT_TLS_IE; + break; + } + if (h != NULL) - h->got.refcount += 1; + { + h->got.refcount += 1; + old_tls_type = sh_elf_hash_entry (h)->tls_type; + } else { bfd_signed_vma *local_got_refcounts; @@ -5533,14 +6277,54 @@ sh_elf_check_relocs (abfd, info, sec, relocs) codelabel local GOT offsets. */ size *= 2; #endif + size += symtab_hdr->sh_info; local_got_refcounts = ((bfd_signed_vma *) bfd_zalloc (abfd, size)); if (local_got_refcounts == NULL) return false; elf_local_got_refcounts (abfd) = local_got_refcounts; +#ifdef INCLUDE_SHMEDIA + /* Take care of both the datalabel and codelabel local + GOT offsets. */ + sh_elf_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info); +#else + sh_elf_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + symtab_hdr->sh_info); +#endif } local_got_refcounts[r_symndx] += 1; + old_tls_type = sh_elf_local_got_tls_type (abfd) [r_symndx]; + } + + /* If a TLS symbol is accessed using IE at least once, + there is no point to use dynamic model for it. */ + if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN + && (old_tls_type != GOT_TLS_GD || tls_type != GOT_TLS_IE)) + { + if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD) + tls_type = GOT_TLS_IE; + else + { + (*_bfd_error_handler) + (_("%s: `%s' accessed both as normal and thread local symbol"), + bfd_archive_filename (abfd), h->root.root.string); + return false; + } + } + + if (old_tls_type != tls_type) + { + if (h != NULL) + sh_elf_hash_entry (h)->tls_type = tls_type; + else + sh_elf_local_got_tls_type (abfd) [r_symndx] = tls_type; } + + break; + + case R_SH_TLS_LD_32: + sh_elf_hash_table(info)->tls_ldm_got.refcount += 1; break; case R_SH_GOTPLT32: @@ -5623,7 +6407,7 @@ sh_elf_check_relocs (abfd, info, sec, relocs) symbol. */ if ((info->shared && (sec->flags & SEC_ALLOC) != 0 - && (ELF32_R_TYPE (rel->r_info) != R_SH_REL32 + && (r_type != R_SH_REL32 || (h != NULL && (! info->symbolic || h->root.type == bfd_link_hash_defweak @@ -5713,11 +6497,110 @@ sh_elf_check_relocs (abfd, info, sec, relocs) } p->count += 1; - if (ELF32_R_TYPE (rel->r_info) == R_SH_REL32) + if (r_type == R_SH_REL32) p->pc_count += 1; } break; + + case R_SH_TLS_LE_32: + if (info->shared) + { + (*_bfd_error_handler) (_("%s: TLS local exec code cannot be linked into shared objects"), + bfd_archive_filename (abfd)); + return false; + } + + if (ELF32_R_TYPE (rel->r_info) == R_SH_TLS_LD_32) + break; + + /* FALLTHROUGH */ + case R_SH_TLS_LDO_32: + /* We make a R_SH_TLS_TPOFF32 relocation. Count it as a + copy relocation. */ + if (! info->shared) + { + struct elf_sh_dyn_relocs *p; + struct elf_sh_dyn_relocs **head; + + if (dynobj == NULL) + htab->root.dynobj = dynobj = abfd; + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (abfd, + elf_elfheader (abfd)->e_shstrndx, + elf_section_data (sec)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (abfd, sec), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + if (sreloc == NULL) + { + flagword flags; + + sreloc = bfd_make_section (dynobj, name); + flags = (SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if ((sec->flags & SEC_ALLOC) != 0) + flags |= SEC_ALLOC | SEC_LOAD; + if (sreloc == NULL + || ! bfd_set_section_flags (dynobj, sreloc, flags) + || ! bfd_set_section_alignment (dynobj, sreloc, 2)) + return false; + } + elf_section_data (sec)->sreloc = sreloc; + if (sec->flags & SEC_READONLY) + info->flags |= DF_TEXTREL; + } + + /* If this is a global symbol, we count the number of + relocations we need for this symbol. */ + if (h != NULL) + head = &((struct elf_sh_link_hash_entry *) h)->dyn_relocs; + else + { + asection *s; + + /* Track dynamic relocs needed for local syms too. */ + s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, + sec, r_symndx); + if (s == NULL) + return false; + + head = ((struct elf_sh_dyn_relocs **) + &elf_section_data (s)->local_dynrel); + } + + p = *head; + if (p == NULL || p->sec != sec) + { + bfd_size_type amt = sizeof (*p); + p = ((struct elf_sh_dyn_relocs *) bfd_alloc (dynobj, amt)); + if (p == NULL) + return false; + p->next = *head; + *head = p; + p->sec = sec; + p->count = 0; + p->pc_count = 0; + } + + p->count += 1; + if (h) + sh_elf_hash_entry (h)->tls_tpoff32 = true; + } + break; + + default: + break; } } @@ -5838,6 +6721,29 @@ sh_elf_merge_private_data (ibfd, obfd) } #endif /* not sh_elf_merge_private_data */ +/* Override the generic function because we need to store sh_elf_obj_tdata + as the specific tdata. We set also the machine architecture from flags + here. */ + +static boolean +sh_elf_object_p (abfd) + bfd *abfd; +{ + struct sh_elf_obj_tdata *new_tdata; + bfd_size_type amt = sizeof (struct sh_elf_obj_tdata); + + if (sh_elf_set_mach_from_flags (abfd) == false) + return false; + + /* Allocate our special target data. */ + new_tdata = bfd_zalloc (abfd, amt); + if (new_tdata == NULL) + return false; + new_tdata->root = *abfd->tdata.elf_obj_data; + abfd->tdata.any = new_tdata; + return true; +} + /* Finish up dynamic symbol handling. We set the contents of various dynamic sections here. */ @@ -5994,7 +6900,9 @@ sh_elf_finish_dynamic_symbol (output_bfd, info, h, sym) } } - if (h->got.offset != (bfd_vma) -1) + if (h->got.offset != (bfd_vma) -1 + && sh_elf_hash_entry (h)->tls_type != GOT_TLS_GD + && sh_elf_hash_entry (h)->tls_type != GOT_TLS_IE) { asection *sgot; asection *srel; @@ -6283,7 +7191,8 @@ sh_elf_reloc_type_class (rela) #define elf_backend_relocate_section sh_elf_relocate_section #define bfd_elf32_bfd_get_relocated_section_contents \ sh_elf_get_relocated_section_contents -#define elf_backend_object_p sh_elf_set_mach_from_flags +#define bfd_elf32_mkobject sh_elf_mkobject +#define elf_backend_object_p sh_elf_object_p #define bfd_elf32_bfd_set_private_bfd_flags \ sh_elf_set_private_flags #define bfd_elf32_bfd_copy_private_bfd_data \ |