diff options
author | Daniel Jacobowitz <drow@false.org> | 2005-03-29 16:54:22 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2005-03-29 16:54:22 +0000 |
commit | ba93b8aced8f39a02c47731e89278fe6f217d929 (patch) | |
tree | 10d828c846f4224226a06d2d2bd4e31283b9d289 /bfd/elf32-arm.c | |
parent | * elf32-arm.c (elf32_arm_check_relocs): Increment count for all (diff) | |
download | binutils-gdb-ba93b8aced8f39a02c47731e89278fe6f217d929.tar.gz binutils-gdb-ba93b8aced8f39a02c47731e89278fe6f217d929.tar.bz2 binutils-gdb-ba93b8aced8f39a02c47731e89278fe6f217d929.zip |
bfd/
* bfd-in2.h, libbfd.h: Regenerated.
* reloc.c: Add ARM TLS relocations.
* elf32-arm.c (elf32_arm_howto_table): Add dynamic TLS
relocations.
(elf32_arm_tls_gd32_howto, elf32_arm_tls_ldo32_howto)
(elf32_arm_tls_ldm32_howto, elf32_arm_tls_le32_howto)
(elf32_arm_tls_ie32_howto): New.
(elf32_arm_howto_from_type): Support TLS relocations.
(elf32_arm_reloc_map): Likewise.
(elf32_arm_reloc_type_lookup): Likewise.
(TCB_SIZE): Define.
(struct elf32_arm_obj_tdata): New.
(elf32_arm_tdata, elf32_arm_local_got_tls_type): Define.
(elf32_arm_mkobject): New function.
(struct elf32_arm_relocs_copied): Add pc_count.
(elf32_arm_hash_entry, GOT_UNKNOWN, GOT_NORMAL, GOT_TLS_GD)
(GOT_TLS_IE): Define.
(struct elf32_arm_link_hash_table): Add tls_ldm_got.
(elf32_arm_link_hash_newfunc): Initialize tls_type.
(elf32_arm_copy_indirect_symbol): Copy pc_count and tls_type.
(elf32_arm_link_hash_table_create): Initialize tls_ldm_got.
(dtpoff_base, tpoff): New functions.
(elf32_arm_final_link_relocate): Handle TLS relocations.
(IS_ARM_TLS_RELOC): Define.
(elf32_arm_relocate_section): Warn about TLS mismatches.
(elf32_arm_gc_sweep_hook): Handle TLS relocations and pc_count.
(elf32_arm_check_relocs): Detect invalid symbol indexes. Handle
TLS relocations and pc_count.
(elf32_arm_adjust_dynamic_symbol): Check non_got_ref.
(allocate_dynrelocs): Handle TLS. Bind REL32 relocs to local
calls.
(elf32_arm_size_dynamic_sections): Handle TLS.
(elf32_arm_finish_dynamic_symbol): Likewise.
(bfd_elf32_mkobject): Define.
gas/
* config/tc-arm.c (arm_parse_reloc): Add TLS relocations.
(md_apply_fix3): Mark TLS symbols.
(tc_gen_reloc): Handle TLS relocations.
(arm_fix_adjustable): Ignore TLS relocations.
(s_arm_elf_cons): Support expressions after decorated symbols.
gas/testuite/
* gas/arm/tls.s, gas/arm/tls.d: New files.
* gas/arm/arm.exp: Run TLS test.
include/elf/
* arm.h: Add TLS relocations.
ld/testsuite/
* ld-arm/tls-lib.s, ld-arm/tls-lib.d, ld-arm/tls-lib.r,
ld-arm/tls-app.s, ld-arm/tls-app.d, ld-arm/tls-app.r: New files.
* ld-arm/arm-lib.ld, ld-arm/arm-dyn.ld: Increase data segment
alignment.
* ld-arm/arm-elf.exp: Run TLS tests.
Diffstat (limited to 'bfd/elf32-arm.c')
-rw-r--r-- | bfd/elf32-arm.c | 753 |
1 files changed, 673 insertions, 80 deletions
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c index 2e1f1d35a85..7464bbe0b26 100644 --- a/bfd/elf32-arm.c +++ b/bfd/elf32-arm.c @@ -294,49 +294,49 @@ static reloc_howto_type elf32_arm_howto_table[] = 0x07ff07ff, /* dst_mask */ TRUE), /* pcrel_offset */ - /* These next three relocs are not defined, but we need to fill the space. */ + /* Dynamic TLS relocations. */ - HOWTO (R_ARM_NONE, /* type */ - 0, /* rightshift */ - 0, /* size (0 = byte, 1 = short, 2 = long) */ - 0, /* bitsize */ - FALSE, /* pc_relative */ - 0, /* bitpos */ - complain_overflow_dont,/* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ - "R_ARM_unknown_17", /* name */ - FALSE, /* partial_inplace */ - 0, /* src_mask */ - 0, /* dst_mask */ - FALSE), /* pcrel_offset */ + HOWTO (R_ARM_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, /* special_function */ + "R_ARM_TLS_DTPMOD32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - HOWTO (R_ARM_NONE, /* type */ - 0, /* rightshift */ - 0, /* size (0 = byte, 1 = short, 2 = long) */ - 0, /* bitsize */ - FALSE, /* pc_relative */ - 0, /* bitpos */ - complain_overflow_dont,/* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ - "R_ARM_unknown_18", /* name */ - FALSE, /* partial_inplace */ - 0, /* src_mask */ - 0, /* dst_mask */ - FALSE), /* pcrel_offset */ + HOWTO (R_ARM_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, /* special_function */ + "R_ARM_TLS_DTPOFF32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - HOWTO (R_ARM_NONE, /* type */ - 0, /* rightshift */ - 0, /* size (0 = byte, 1 = short, 2 = long) */ - 0, /* bitsize */ - FALSE, /* pc_relative */ - 0, /* bitpos */ - complain_overflow_dont,/* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ - "R_ARM_unknown_19", /* name */ - FALSE, /* partial_inplace */ - 0, /* src_mask */ - 0, /* dst_mask */ - FALSE), /* pcrel_offset */ + HOWTO (R_ARM_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, /* special_function */ + "R_ARM_TLS_TPOFF32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* Relocs used in ARM Linux */ @@ -663,6 +663,81 @@ static reloc_howto_type elf32_arm_howto_table[] = TRUE), /* pcrel_offset */ }; +static reloc_howto_type elf32_arm_tls_gd32_howto = + HOWTO (R_ARM_TLS_GD32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + NULL, /* special_function */ + "R_ARM_TLS_GD32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE); /* pcrel_offset */ + +static reloc_howto_type elf32_arm_tls_ldo32_howto = + HOWTO (R_ARM_TLS_LDO32, /* 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, /* special_function */ + "R_ARM_TLS_LDO32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE); /* pcrel_offset */ + +static reloc_howto_type elf32_arm_tls_ldm32_howto = + HOWTO (R_ARM_TLS_LDM32, /* 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, /* special_function */ + "R_ARM_TLS_LDM32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE); /* pcrel_offset */ + +static reloc_howto_type elf32_arm_tls_le32_howto = + HOWTO (R_ARM_TLS_LE32, /* 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, /* special_function */ + "R_ARM_TLS_LE32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE); /* pcrel_offset */ + +static reloc_howto_type elf32_arm_tls_ie32_howto = + HOWTO (R_ARM_TLS_IE32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + NULL, /* special_function */ + "R_ARM_TLS_IE32", /* name */ + TRUE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE); /* pcrel_offset */ + /* GNU extension to record C++ vtable hierarchy */ static reloc_howto_type elf32_arm_vtinherit_howto = HOWTO (R_ARM_GNU_VTINHERIT, /* type */ @@ -825,6 +900,26 @@ elf32_arm_howto_from_type (unsigned int r_type) case R_ARM_THM_PC9: return &elf32_arm_thm_pc9_howto; + + case R_ARM_TLS_GD32: + return &elf32_arm_tls_gd32_howto; + break; + + case R_ARM_TLS_LDO32: + return &elf32_arm_tls_ldo32_howto; + break; + + case R_ARM_TLS_LDM32: + return &elf32_arm_tls_ldm32_howto; + break; + + case R_ARM_TLS_IE32: + return &elf32_arm_tls_ie32_howto; + break; + + case R_ARM_TLS_LE32: + return &elf32_arm_tls_le32_howto; + break; case R_ARM_RREL32: case R_ARM_RABS32: @@ -879,7 +974,16 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] = {BFD_RELOC_ARM_ROSEGREL32, R_ARM_ROSEGREL32}, {BFD_RELOC_ARM_SBREL32, R_ARM_SBREL32}, {BFD_RELOC_ARM_PREL31, R_ARM_PREL31}, - {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2} + {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2}, + {BFD_RELOC_ARM_PLT32, R_ARM_PLT32}, + {BFD_RELOC_ARM_TLS_GD32, R_ARM_TLS_GD32}, + {BFD_RELOC_ARM_TLS_LDO32, R_ARM_TLS_LDO32}, + {BFD_RELOC_ARM_TLS_LDM32, R_ARM_TLS_LDM32}, + {BFD_RELOC_ARM_TLS_DTPMOD32, R_ARM_TLS_DTPMOD32}, + {BFD_RELOC_ARM_TLS_DTPOFF32, R_ARM_TLS_DTPOFF32}, + {BFD_RELOC_ARM_TLS_TPOFF32, R_ARM_TLS_TPOFF32}, + {BFD_RELOC_ARM_TLS_IE32, R_ARM_TLS_IE32}, + {BFD_RELOC_ARM_TLS_LE32, R_ARM_TLS_LE32}, }; static reloc_howto_type * @@ -903,6 +1007,21 @@ elf32_arm_reloc_type_lookup (abfd, code) case BFD_RELOC_THUMB_PCREL_BRANCH9: return & elf32_arm_thm_pc9_howto; + case BFD_RELOC_ARM_TLS_GD32: + return & elf32_arm_tls_gd32_howto; + + case BFD_RELOC_ARM_TLS_LDO32: + return & elf32_arm_tls_ldo32_howto; + + case BFD_RELOC_ARM_TLS_LDM32: + return & elf32_arm_tls_ldm32_howto; + + case BFD_RELOC_ARM_TLS_IE32: + return & elf32_arm_tls_ie32_howto; + + case BFD_RELOC_ARM_TLS_LE32: + return & elf32_arm_tls_le32_howto; + default: for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++) if (elf32_arm_reloc_map[i].bfd_reloc_val == code) @@ -1093,14 +1212,41 @@ struct _arm_elf_section_data #define elf32_arm_section_data(sec) \ ((struct _arm_elf_section_data *) elf_section_data (sec)) +/* The size of the thread control block. */ +#define TCB_SIZE 8 + +struct elf32_arm_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; +}; + +#define elf32_arm_tdata(abfd) \ + ((struct elf32_arm_obj_tdata *) (abfd)->tdata.any) + +#define elf32_arm_local_got_tls_type(abfd) \ + (elf32_arm_tdata (abfd)->local_got_tls_type) + +static bfd_boolean +elf32_arm_mkobject (bfd *abfd) +{ + bfd_size_type amt = sizeof (struct elf32_arm_obj_tdata); + abfd->tdata.any = bfd_zalloc (abfd, amt); + if (abfd->tdata.any == NULL) + return FALSE; + return TRUE; +} + /* The ARM linker needs to keep track of the number of relocs that it decides to copy in check_relocs for each symbol. This is so that it can discard PC relative relocs if it doesn't need them when linking with -Bsymbolic. We store the information in a field extending the regular ELF linker hash table. */ -/* This structure keeps track of the number of PC relative relocs we - have copied for a given symbol. */ +/* This structure keeps track of the number of relocs we have copied + for a given symbol. */ struct elf32_arm_relocs_copied { /* Next section. */ @@ -1109,8 +1255,12 @@ struct elf32_arm_relocs_copied asection * section; /* Number of relocs copied in this section. */ bfd_size_type count; + /* Number of PC-relative relocs copied in this section. */ + bfd_size_type pc_count; }; +#define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent)) + /* Arm ELF linker hash entry. */ struct elf32_arm_link_hash_entry { @@ -1127,6 +1277,12 @@ struct elf32_arm_link_hash_entry used, we need to record the index into .got.plt instead of recomputing it from the PLT offset. */ bfd_signed_vma plt_got_offset; + +#define GOT_UNKNOWN 0 +#define GOT_NORMAL 1 +#define GOT_TLS_GD 2 +#define GOT_TLS_IE 4 + unsigned char tls_type; }; /* Traverse an arm ELF linker hash table. */ @@ -1189,6 +1345,12 @@ struct elf32_arm_link_hash_table asection *sdynbss; asection *srelbss; + /* Data for R_ARM_TLS_LDM32 relocations. */ + union { + bfd_signed_vma refcount; + bfd_vma offset; + } tls_ldm_got; + /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; @@ -1220,6 +1382,7 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry, if (ret != NULL) { ret->relocs_copied = NULL; + ret->tls_type = GOT_UNKNOWN; ret->plt_thumb_refcount = 0; ret->plt_got_offset = -1; } @@ -1321,6 +1484,7 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed, for (q = edir->relocs_copied; q != NULL; q = q->next) if (q->section == p->section) { + q->pc_count += p->pc_count; q->count += p->count; *pp = p->next; break; @@ -1346,6 +1510,13 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed, else BFD_ASSERT (eind->plt_thumb_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); } @@ -1392,6 +1563,7 @@ elf32_arm_link_hash_table_create (bfd *abfd) ret->use_rel = 1; ret->sym_sec.abfd = NULL; ret->obfd = abfd; + ret->tls_ldm_got.refcount = 0; return &ret->root.root; } @@ -2228,6 +2400,35 @@ arm_real_reloc_type (struct elf32_arm_link_hash_table * globals, #endif /* OLD_ARM_ABI */ +/* 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 (struct bfd_link_info *info) +{ + /* If tls_sec is NULL, we should have signalled an error already. */ + if (elf_hash_table (info)->tls_sec == NULL) + return 0; + return elf_hash_table (info)->tls_sec->vma; +} + +/* Return the relocation value for @tpoff relocation + if STT_TLS virtual address is ADDRESS. */ + +static bfd_vma +tpoff (struct bfd_link_info *info, bfd_vma address) +{ + struct elf_link_hash_table *htab = elf_hash_table (info); + bfd_vma base; + + /* If tls_sec is NULL, we should have signalled an error already. */ + if (htab->tls_sec == NULL) + return 0; + base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power); + return address - htab->tls_sec->vma + base; +} + /* Perform a relocation as part of a final link. */ static bfd_reloc_status_type @@ -2979,6 +3180,222 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto, contents, rel->r_offset, value, (bfd_vma) 0); + case R_ARM_TLS_LDO32: + value = value - dtpoff_base (info); + + return _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, value, (bfd_vma) 0); + + case R_ARM_TLS_LDM32: + { + bfd_vma off; + + if (globals->sgot == NULL) + abort (); + + off = globals->tls_ldm_got.offset; + + if ((off & 1) != 0) + off &= ~1; + else + { + /* If we don't know the module number, create a relocation + for it. */ + if (info->shared) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + + if (globals->srelgot == NULL) + abort (); + + outrel.r_offset = (globals->sgot->output_section->vma + + globals->sgot->output_offset + off); + outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32); + + bfd_put_32 (output_bfd, 0, globals->sgot->contents + off); + + loc = globals->srelgot->contents; + loc += globals->srelgot->reloc_count++ * sizeof (Elf32_External_Rel); + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + } + else + bfd_put_32 (output_bfd, 1, globals->sgot->contents + off); + + globals->tls_ldm_got.offset |= 1; + } + + value = globals->sgot->output_section->vma + globals->sgot->output_offset + off + - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); + + return _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, value, + (bfd_vma) 0); + } + + case R_ARM_TLS_GD32: + case R_ARM_TLS_IE32: + { + bfd_vma off; + int indx; + char tls_type; + + if (globals->sgot == NULL) + abort (); + + indx = 0; + if (h != NULL) + { + bfd_boolean dyn; + dyn = globals->root.dynamic_sections_created; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + { + *unresolved_reloc_p = FALSE; + indx = h->dynindx; + } + off = h->got.offset; + tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type; + } + else + { + if (local_got_offsets == NULL) + abort (); + off = local_got_offsets[r_symndx]; + tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx]; + } + + if (tls_type == GOT_UNKNOWN) + abort (); + + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_boolean need_relocs = FALSE; + Elf_Internal_Rela outrel; + bfd_byte *loc = NULL; + int cur_off = off; + + /* The GOT entries have not been initialized yet. Do it + now, and emit any relocations. If both an IE GOT and a + GD GOT are necessary, we emit the GD first. */ + + if ((info->shared || indx != 0) + && (h == NULL + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + need_relocs = TRUE; + if (globals->srelgot == NULL) + abort (); + loc = globals->srelgot->contents; + loc += globals->srelgot->reloc_count * sizeof (Elf32_External_Rel); + } + + if (tls_type & GOT_TLS_GD) + { + if (need_relocs) + { + outrel.r_offset = (globals->sgot->output_section->vma + + globals->sgot->output_offset + cur_off); + outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32); + bfd_put_32 (output_bfd, 0, globals->sgot->contents + cur_off); + + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + globals->srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rel); + + if (indx == 0) + bfd_put_32 (output_bfd, value - dtpoff_base (info), + globals->sgot->contents + cur_off + 4); + else + { + bfd_put_32 (output_bfd, 0, + globals->sgot->contents + cur_off + 4); + + outrel.r_info = ELF32_R_INFO (indx, + R_ARM_TLS_DTPOFF32); + outrel.r_offset += 4; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + globals->srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rel); + } + } + else + { + /* If we are not emitting relocations for a + general dynamic reference, then we must be in a + static link or an executable link with the + symbol binding locally. Mark it as belonging + to module 1, the executable. */ + bfd_put_32 (output_bfd, 1, + globals->sgot->contents + cur_off); + bfd_put_32 (output_bfd, value - dtpoff_base (info), + globals->sgot->contents + cur_off + 4); + } + + cur_off += 8; + } + + if (tls_type & GOT_TLS_IE) + { + if (need_relocs) + { + outrel.r_offset = (globals->sgot->output_section->vma + + globals->sgot->output_offset + + cur_off); + outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32); + + if (indx == 0) + bfd_put_32 (output_bfd, value - dtpoff_base (info), + globals->sgot->contents + cur_off); + else + bfd_put_32 (output_bfd, 0, + globals->sgot->contents + cur_off); + + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + globals->srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rel); + } + else + bfd_put_32 (output_bfd, tpoff (info, value), + globals->sgot->contents + cur_off); + cur_off += 4; + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; + } + + if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32) + off += 8; + value = globals->sgot->output_section->vma + globals->sgot->output_offset + off + - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); + + return _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, value, + (bfd_vma) 0); + } + + case R_ARM_TLS_LE32: + if (info->shared) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"), + input_bfd, input_section, + (long) rel->r_offset, howto->name); + return FALSE; + } + else + value = tpoff (info, value); + + return _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, value, (bfd_vma) 0); + case R_ARM_SBREL32: return bfd_reloc_notsupported; @@ -3098,6 +3515,16 @@ arm_add_to_rel (bfd * abfd, } } +#define IS_ARM_TLS_RELOC(R_TYPE) \ + ((R_TYPE) == R_ARM_TLS_GD32 \ + || (R_TYPE) == R_ARM_TLS_LDO32 \ + || (R_TYPE) == R_ARM_TLS_LDM32 \ + || (R_TYPE) == R_ARM_TLS_DTPOFF32 \ + || (R_TYPE) == R_ARM_TLS_DTPMOD32 \ + || (R_TYPE) == R_ARM_TLS_TPOFF32 \ + || (R_TYPE) == R_ARM_TLS_LE32 \ + || (R_TYPE) == R_ARM_TLS_IE32) + /* Relocate an ARM ELF section. */ static bfd_boolean elf32_arm_relocate_section (bfd * output_bfd, @@ -3136,6 +3563,7 @@ elf32_arm_relocate_section (bfd * output_bfd, bfd_vma relocation; bfd_reloc_status_type r; arelent bfd_reloc; + char sym_type; bfd_boolean unresolved_reloc = FALSE; r_symndx = ELF32_R_SYM (rel->r_info); @@ -3179,6 +3607,7 @@ elf32_arm_relocate_section (bfd * output_bfd, if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; + sym_type = ELF32_ST_TYPE (sym->st_info); sec = local_sections[r_symndx]; if (globals->use_rel) { @@ -3232,6 +3661,8 @@ elf32_arm_relocate_section (bfd * output_bfd, r_symndx, symtab_hdr, sym_hashes, h, sec, relocation, unresolved_reloc, warned); + + sym_type = h->type; } if (h != NULL) @@ -3244,6 +3675,24 @@ elf32_arm_relocate_section (bfd * output_bfd, name = bfd_section_name (input_bfd, sec); } + if (r_symndx != 0 + && r_type != R_ARM_NONE + && (h == NULL + || h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && IS_ARM_TLS_RELOC (r_type) != (sym_type == STT_TLS)) + { + (*_bfd_error_handler) + ((sym_type == STT_TLS + ? _("%B(%A+0x%lx): %s used with TLS symbol %s") + : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")), + input_bfd, + input_section, + (long) rel->r_offset, + howto->name, + name); + } + r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd, input_section, contents, rel, relocation, info, sec, name, @@ -3823,10 +4272,10 @@ elf32_arm_gc_mark_hook (asection * sec, /* Update the got entry reference counts for the section being removed. */ static bfd_boolean -elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED, - struct bfd_link_info * info ATTRIBUTE_UNUSED, - asection * sec ATTRIBUTE_UNUSED, - const Elf_Internal_Rela * relocs ATTRIBUTE_UNUSED) +elf32_arm_gc_sweep_hook (bfd * abfd, + struct bfd_link_info * info, + asection * sec, + const Elf_Internal_Rela * relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; @@ -3868,6 +4317,8 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED, #ifndef OLD_ARM_ABI case R_ARM_GOT_PREL: #endif + case R_ARM_TLS_GD32: + case R_ARM_TLS_IE32: if (h != NULL) { if (h->got.refcount > 0) @@ -3880,6 +4331,10 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED, } break; + case R_ARM_TLS_LDM32: + elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1; + break; + case R_ARM_ABS32: case R_ARM_REL32: case R_ARM_PC24: @@ -3915,6 +4370,8 @@ elf32_arm_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED, if (p->section == sec) { p->count -= 1; + if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32) + p->pc_count -= 1; if (p->count == 0) *pp = p->next; break; @@ -3986,6 +4443,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info, #ifndef OLD_ARM_ABI r_type = arm_real_reloc_type (htab, r_type); #endif + + if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) + { + (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd, + r_symndx); + return FALSE; + } + if (r_symndx < symtab_hdr->sh_info) h = NULL; else @@ -3999,33 +4464,69 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info, #ifndef OLD_ARM_ABI case R_ARM_GOT_PREL: #endif + case R_ARM_TLS_GD32: + case R_ARM_TLS_IE32: /* This symbol requires a global offset table entry. */ - if (h != NULL) - { - h->got.refcount++; - } - else - { - bfd_signed_vma *local_got_refcounts; + { + int tls_type, old_tls_type; - /* This is a global offset table entry for a local symbol. */ - local_got_refcounts = elf_local_got_refcounts (abfd); - if (local_got_refcounts == NULL) - { - bfd_size_type size; + switch (r_type) + { + case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break; + case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break; + default: tls_type = GOT_NORMAL; break; + } - size = symtab_hdr->sh_info; - size *= (sizeof (bfd_signed_vma) + sizeof (char)); - local_got_refcounts = bfd_zalloc (abfd, size); - if (local_got_refcounts == NULL) - return FALSE; - elf_local_got_refcounts (abfd) = local_got_refcounts; - } - local_got_refcounts[r_symndx] += 1; - } - if (r_type == R_ARM_GOT32) - break; - /* Fall through. */ + if (h != NULL) + { + h->got.refcount++; + old_tls_type = elf32_arm_hash_entry (h)->tls_type; + } + else + { + bfd_signed_vma *local_got_refcounts; + + /* This is a global offset table entry for a local symbol. */ + local_got_refcounts = elf_local_got_refcounts (abfd); + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= (sizeof (bfd_signed_vma) + sizeof(char)); + local_got_refcounts = bfd_zalloc (abfd, size); + if (local_got_refcounts == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = local_got_refcounts; + elf32_arm_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + symtab_hdr->sh_info); + } + local_got_refcounts[r_symndx] += 1; + old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx]; + } + + /* We will already have issued an error message if there is a + TLS / non-TLS mismatch, based on the symbol type. We don't + support any linker relaxations. So just combine any TLS + types needed. */ + if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL + && tls_type != GOT_NORMAL) + tls_type |= old_tls_type; + + if (old_tls_type != tls_type) + { + if (h != NULL) + elf32_arm_hash_entry (h)->tls_type = tls_type; + else + elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type; + } + } + /* Fall through */ + + case R_ARM_TLS_LDM32: + if (r_type == R_ARM_TLS_LDM32) + htab->tls_ldm_got.refcount++; + /* Fall through */ case R_ARM_GOTOFF: case R_ARM_GOTPC: @@ -4176,8 +4677,11 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info, *head = p; p->section = sec; p->count = 0; + p->pc_count = 0; } + if (r_type == R_ARM_REL32) + p->pc_count += 1; p->count += 1; } break; @@ -4404,6 +4908,11 @@ elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info, return TRUE; } + /* If there are no non-GOT references, we do not need a copy + relocation. */ + if (!h->non_got_ref) + return TRUE; + /* This is a reference to a symbol defined by a dynamic object which is not a function. */ @@ -4571,6 +5080,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) { asection *s; bfd_boolean dyn; + int tls_type = elf32_arm_hash_entry (h)->tls_type; + int indx; /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ @@ -4585,12 +5096,49 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) { s = htab->sgot; h->got.offset = s->size; - s->size += 4; + + if (tls_type == GOT_UNKNOWN) + abort (); + + if (tls_type == GOT_NORMAL) + /* Non-TLS symbols need one GOT slot. */ + s->size += 4; + else + { + if (tls_type & GOT_TLS_GD) + /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. */ + s->size += 8; + if (tls_type & GOT_TLS_IE) + /* R_ARM_TLS_IE32 needs one GOT slot. */ + s->size += 4; + } + dyn = htab->root.dynamic_sections_created; - if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT - || h->root.type != bfd_link_hash_undefweak) - && (info->shared - || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) + + indx = 0; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + indx = h->dynindx; + + if (tls_type != GOT_NORMAL + && (info->shared || indx != 0) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + if (tls_type & GOT_TLS_IE) + htab->srelgot->size += sizeof (Elf32_External_Rel); + + if (tls_type & GOT_TLS_GD) + htab->srelgot->size += sizeof (Elf32_External_Rel); + + if ((tls_type & GOT_TLS_GD) && indx != 0) + htab->srelgot->size += sizeof (Elf32_External_Rel); + } + else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak) + && (info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) htab->srelgot->size += sizeof (Elf32_External_Rel); } } @@ -4608,7 +5156,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) if (info->shared || htab->root.is_relocatable_executable) { - /* Discard relocs on undefined weak syms with non-default + /* The only reloc that uses pc_count is R_ARM_REL32, which will + appear on something like ".long foo - .". We want calls to + protected symbols to resolve directly to the function rather + than going via the plt. If people want function pointer + comparisons to work as expected then they should avoid + writing assembly like ".long foo - .". */ + if (SYMBOL_CALLS_LOCAL (info, h)) + { + struct elf32_arm_relocs_copied **pp; + + for (pp = &eh->relocs_copied; (p = *pp) != NULL; ) + { + p->count -= p->pc_count; + p->pc_count = 0; + if (p->count == 0) + *pp = p->next; + else + pp = &p->next; + } + } + + /* Also discard relocs on undefined weak syms with non-default visibility. */ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT && h->root.type == bfd_link_hash_undefweak) @@ -4773,6 +5342,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED, symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; + local_tls_type = elf32_arm_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; for (; local_got < end_local_got; ++local_got, ++local_tls_type) @@ -4780,8 +5350,15 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED, if (*local_got > 0) { *local_got = s->size; - s->size += 4; - if (info->shared) + if (*local_tls_type & GOT_TLS_GD) + /* TLS_GD relocs need an 8-byte structure in the GOT. */ + s->size += 8; + if (*local_tls_type & GOT_TLS_IE) + s->size += 4; + if (*local_tls_type == GOT_NORMAL) + s->size += 4; + + if (info->shared || *local_tls_type == GOT_TLS_GD) srel->size += sizeof (Elf32_External_Rel); } else @@ -4789,6 +5366,18 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED, } } + if (htab->tls_ldm_got.refcount > 0) + { + /* Allocate two GOT entries and one dynamic relocation (if necessary) + for R_ARM_TLS_LDM32 relocations. */ + htab->tls_ldm_got.offset = htab->sgot->size; + htab->sgot->size += 8; + if (info->shared) + htab->srelgot->size += sizeof (Elf32_External_Rel); + } + 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, info); @@ -5059,7 +5648,9 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info, } } - if (h->got.offset != (bfd_vma) -1) + if (h->got.offset != (bfd_vma) -1 + && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 + && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) { asection * sgot; asection * srel; @@ -5754,6 +6345,8 @@ const struct elf_size_info elf32_arm_size_info = { #endif #define ELF_MINPAGESIZE 0x1000 +#define bfd_elf32_mkobject elf32_arm_mkobject + #define bfd_elf32_bfd_copy_private_bfd_data elf32_arm_copy_private_bfd_data #define bfd_elf32_bfd_merge_private_bfd_data elf32_arm_merge_private_bfd_data #define bfd_elf32_bfd_set_private_flags elf32_arm_set_private_flags |