From ba93b8aced8f39a02c47731e89278fe6f217d929 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Tue, 29 Mar 2005 16:54:22 +0000 Subject: [PATCH] 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. --- bfd/ChangeLog | 38 ++ bfd/bfd-in2.h | 8 + bfd/elf32-arm.c | 753 ++++++++++++++++++++++++++++---- bfd/libbfd.h | 8 + bfd/reloc.c | 16 + gas/ChangeLog | 9 + gas/config/tc-arm.c | 51 ++- gas/testsuite/ChangeLog | 5 + gas/testsuite/gas/arm/arm.exp | 2 + gas/testsuite/gas/arm/tls.d | 21 + gas/testsuite/gas/arm/tls.s | 14 + include/elf/ChangeLog | 5 + include/elf/arm.h | 10 +- ld/testsuite/ChangeLog | 8 + ld/testsuite/ld-arm/arm-dyn.ld | 2 +- ld/testsuite/ld-arm/arm-elf.exp | 6 + ld/testsuite/ld-arm/arm-lib.ld | 2 +- ld/testsuite/ld-arm/tls-app.d | 18 + ld/testsuite/ld-arm/tls-app.r | 12 + ld/testsuite/ld-arm/tls-app.s | 34 ++ ld/testsuite/ld-arm/tls-lib.d | 15 + ld/testsuite/ld-arm/tls-lib.r | 10 + ld/testsuite/ld-arm/tls-lib.s | 22 + 23 files changed, 984 insertions(+), 85 deletions(-) create mode 100644 gas/testsuite/gas/arm/tls.d create mode 100644 gas/testsuite/gas/arm/tls.s create mode 100644 ld/testsuite/ld-arm/tls-app.d create mode 100644 ld/testsuite/ld-arm/tls-app.r create mode 100644 ld/testsuite/ld-arm/tls-app.s create mode 100644 ld/testsuite/ld-arm/tls-lib.d create mode 100644 ld/testsuite/ld-arm/tls-lib.r create mode 100644 ld/testsuite/ld-arm/tls-lib.s diff --git a/bfd/ChangeLog b/bfd/ChangeLog index c2629c585eb..c4b62ddd588 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,41 @@ +2005-03-29 Daniel Jacobowitz + Phil Blundell + + * 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. + 2005-03-29 Daniel Jacobowitz * elf32-arm.c (elf32_arm_check_relocs): Increment count for all diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 131bd833012..55492cb257f 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2691,6 +2691,14 @@ field in the instruction. */ BFD_RELOC_ARM_RELATIVE, BFD_RELOC_ARM_GOTOFF, BFD_RELOC_ARM_GOTPC, + BFD_RELOC_ARM_TLS_GD32, + BFD_RELOC_ARM_TLS_LDO32, + BFD_RELOC_ARM_TLS_LDM32, + BFD_RELOC_ARM_TLS_DTPOFF32, + BFD_RELOC_ARM_TLS_DTPMOD32, + BFD_RELOC_ARM_TLS_TPOFF32, + BFD_RELOC_ARM_TLS_IE32, + BFD_RELOC_ARM_TLS_LE32, /* Pc-relative or absolute relocation depending on target. Used for entries in .init_array sections. */ 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 diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 3cfed3a11d4..dadba59ef2d 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -1177,6 +1177,14 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_ARM_RELATIVE", "BFD_RELOC_ARM_GOTOFF", "BFD_RELOC_ARM_GOTPC", + "BFD_RELOC_ARM_TLS_GD32", + "BFD_RELOC_ARM_TLS_LDO32", + "BFD_RELOC_ARM_TLS_LDM32", + "BFD_RELOC_ARM_TLS_DTPOFF32", + "BFD_RELOC_ARM_TLS_DTPMOD32", + "BFD_RELOC_ARM_TLS_TPOFF32", + "BFD_RELOC_ARM_TLS_IE32", + "BFD_RELOC_ARM_TLS_LE32", "BFD_RELOC_ARM_TARGET1", "BFD_RELOC_ARM_ROSEGREL32", "BFD_RELOC_ARM_SBREL32", diff --git a/bfd/reloc.c b/bfd/reloc.c index d952d1b2485..17f8a45c114 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -2678,6 +2678,22 @@ ENUMX BFD_RELOC_ARM_GOTOFF ENUMX BFD_RELOC_ARM_GOTPC +ENUMX + BFD_RELOC_ARM_TLS_GD32 +ENUMX + BFD_RELOC_ARM_TLS_LDO32 +ENUMX + BFD_RELOC_ARM_TLS_LDM32 +ENUMX + BFD_RELOC_ARM_TLS_DTPOFF32 +ENUMX + BFD_RELOC_ARM_TLS_DTPMOD32 +ENUMX + BFD_RELOC_ARM_TLS_TPOFF32 +ENUMX + BFD_RELOC_ARM_TLS_IE32 +ENUMX + BFD_RELOC_ARM_TLS_LE32 ENUMDOC These relocs are only used within the ARM assembler. They are not (at present) written to any object files. diff --git a/gas/ChangeLog b/gas/ChangeLog index 2cc94453e18..d6263e6b434 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,12 @@ +2005-03-29 Daniel Jacobowitz + Phil Blundell + + * 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. + 2005-03-29 Julian Brown * config/tc-arm.c (marked_pr_dependency): New bitmap, bit N indicates diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 9baf888b68e..78d126c7493 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -4881,6 +4881,11 @@ arm_parse_reloc (void) MAP ("(target1)", BFD_RELOC_ARM_TARGET1), MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32), MAP ("(target2)", BFD_RELOC_ARM_TARGET2), + MAP ("(tlsgd)", BFD_RELOC_ARM_TLS_GD32), + MAP ("(tlsldm)", BFD_RELOC_ARM_TLS_LDM32), + MAP ("(tlsldo)", BFD_RELOC_ARM_TLS_LDO32), + MAP ("(gottpoff)", BFD_RELOC_ARM_TLS_IE32), + MAP ("(tpoff)", BFD_RELOC_ARM_TLS_LE32), { NULL, 0, BFD_RELOC_UNUSED } #undef MAP }; @@ -12224,6 +12229,14 @@ md_apply_fix3 (fixS * fixP, break; #ifdef OBJ_ELF + case BFD_RELOC_ARM_TLS_GD32: + case BFD_RELOC_ARM_TLS_LE32: + case BFD_RELOC_ARM_TLS_IE32: + case BFD_RELOC_ARM_TLS_LDM32: + case BFD_RELOC_ARM_TLS_LDO32: + S_SET_THREAD_LOCAL (fixP->fx_addsy); + /* fall through */ + case BFD_RELOC_ARM_GOT32: case BFD_RELOC_ARM_GOTOFF: case BFD_RELOC_ARM_TARGET2: @@ -12547,6 +12560,18 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, case BFD_RELOC_ARM_SBREL32: case BFD_RELOC_ARM_PREL31: case BFD_RELOC_ARM_TARGET2: + case BFD_RELOC_ARM_TLS_LE32: + case BFD_RELOC_ARM_TLS_LDO32: + code = fixp->fx_r_type; + break; + + case BFD_RELOC_ARM_TLS_GD32: + case BFD_RELOC_ARM_TLS_IE32: + case BFD_RELOC_ARM_TLS_LDM32: + /* BFD will include the symbol's address in the addend. + But we don't want that, so subtract it out again here. */ + if (!S_IS_COMMON (fixp->fx_addsy)) + reloc->addend -= (*reloc->sym_ptr_ptr)->value; code = fixp->fx_r_type; break; #endif @@ -13843,6 +13868,11 @@ arm_fix_adjustable (fixS * fixP) if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32 || fixP->fx_r_type == BFD_RELOC_ARM_GOT32 || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32 || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2) return 0; @@ -13898,8 +13928,12 @@ s_arm_elf_cons (int nbytes) do { bfd_reloc_code_real_type reloc; + char *sym_start; + int sym_len; + sym_start = input_line_pointer; expression (& exp); + sym_len = input_line_pointer - sym_start; if (exp.X_op == O_symbol && * input_line_pointer == '(' @@ -13913,9 +13947,22 @@ s_arm_elf_cons (int nbytes) howto->name, nbytes); else { - char *p = frag_more ((int) nbytes); + char *p; int offset = nbytes - size; - + char *saved_buf = alloca (sym_len), *saved_input; + + /* We've parsed an expression stopping at O_symbol. But there + may be more expression left now that we have parsed the + relocation marker. Parse it again. */ + saved_input = input_line_pointer - sym_len; + memcpy (saved_buf, saved_input, sym_len); + memmove (saved_input, sym_start, sym_len); + input_line_pointer = saved_input; + expression (& exp); + memcpy (saved_input, saved_buf, sym_len); + assert (input_line_pointer >= saved_input + sym_len); + + p = frag_more ((int) nbytes); fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc); } diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index bef8501a4f0..5559174aae8 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2005-03-29 Daniel Jacobowitz + + * gas/arm/tls.s, gas/arm/tls.d: New files. + * gas/arm/arm.exp: Run TLS test. + 2005-03-28 H.J. Lu PR 803 diff --git a/gas/testsuite/gas/arm/arm.exp b/gas/testsuite/gas/arm/arm.exp index 4ea64543182..7cd5630cffa 100644 --- a/gas/testsuite/gas/arm/arm.exp +++ b/gas/testsuite/gas/arm/arm.exp @@ -72,6 +72,8 @@ if {[istarget *arm*-*-*] || [istarget "xscale-*-*"]} then { run_dump_test "mapping" gas_test "bignum1.s" "" $stdoptlist "bignums" run_dump_test "unwind" + + run_dump_test "tls" } if {! [istarget arm*-*-aout] && ![istarget arm-*-pe]} then { diff --git a/gas/testsuite/gas/arm/tls.d b/gas/testsuite/gas/arm/tls.d new file mode 100644 index 00000000000..71a1a6f49dc --- /dev/null +++ b/gas/testsuite/gas/arm/tls.d @@ -0,0 +1,21 @@ +#objdump: -dr +#name: TLS + +# Test generation of TLS relocations + +.*: +file format .*arm.* + +Disassembly of section .text: + +00+0
: + 0: e1a00000 nop \(mov r0,r0\) + 4: e1a00000 nop \(mov r0,r0\) + 8: e1a0f00e mov pc, lr + c: 00000000 andeq r0, r0, r0 + c: R_ARM_TLS_GD32 a + 10: 00000004 andeq r0, r0, r4 + 10: R_ARM_TLS_LDM32 b + 14: 00000008 andeq r0, r0, r8 + 14: R_ARM_TLS_IE32 c + 18: 00000000 andeq r0, r0, r0 + 18: R_ARM_TLS_LE32 d diff --git a/gas/testsuite/gas/arm/tls.s b/gas/testsuite/gas/arm/tls.s new file mode 100644 index 00000000000..48722a42b3a --- /dev/null +++ b/gas/testsuite/gas/arm/tls.s @@ -0,0 +1,14 @@ + .text + .globl main + .type main, %function +main: + nop +.L2: + nop + mov pc, lr + +.Lpool: + .word a(tlsgd) + (. - .L2 - 8) + .word b(tlsldm) + (. - .L2 - 8) + .word c(gottpoff) + (. - .L2 - 8) + .word d(tpoff) diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index b273adba9a6..1d9aedb859f 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,8 @@ +2005-03-29 Daniel Jacobowitz + Phil Blundell + + * arm.h: Add TLS relocations. + 2005-03-23 Ben Elliston * dwarf.h: Merge with GCC's dwarf.h. diff --git a/include/elf/arm.h b/include/elf/arm.h index de3ed066441..81a8de1d701 100644 --- a/include/elf/arm.h +++ b/include/elf/arm.h @@ -114,6 +114,9 @@ START_RELOC_NUMBERS (elf_arm_reloc_type) RELOC_NUMBER (R_ARM_THM_SWI8, 14) RELOC_NUMBER (R_ARM_XPC25, 15) RELOC_NUMBER (R_ARM_THM_XPC22, 16) + RELOC_NUMBER (R_ARM_TLS_DTPMOD32, 17) + RELOC_NUMBER (R_ARM_TLS_DTPOFF32, 18) + RELOC_NUMBER (R_ARM_TLS_TPOFF32, 19) #endif /* not OLD_ARM_ABI */ RELOC_NUMBER (R_ARM_COPY, 20) /* Copy symbol at runtime. */ RELOC_NUMBER (R_ARM_GLOB_DAT, 21) /* Create GOT entry. */ @@ -153,7 +156,12 @@ START_RELOC_NUMBERS (elf_arm_reloc_type) RELOC_NUMBER (R_ARM_GNU_VTINHERIT, 101) RELOC_NUMBER (R_ARM_THM_PC11, 102) /* Cygnus extension to abi: Thumb unconditional branch. */ RELOC_NUMBER (R_ARM_THM_PC9, 103) /* Cygnus extension to abi: Thumb conditional branch. */ - FAKE_RELOC (FIRST_INVALID_RELOC3, 104) + RELOC_NUMBER (R_ARM_TLS_GD32, 104) + RELOC_NUMBER (R_ARM_TLS_LDM32, 105) + RELOC_NUMBER (R_ARM_TLS_LDO32, 106) + RELOC_NUMBER (R_ARM_TLS_IE32, 107) + RELOC_NUMBER (R_ARM_TLS_LE32, 108) + FAKE_RELOC (FIRST_INVALID_RELOC3, 109) FAKE_RELOC (LAST_INVALID_RELOC3, 248) RELOC_NUMBER (R_ARM_RXPC25, 249) #endif /* not OLD_ARM_ABI */ diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index 9ae7391003a..cd9ca6303cc 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2005-03-29 Daniel Jacobowitz + + * 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. + 2005-03-28 H.J. Lu PR 803 diff --git a/ld/testsuite/ld-arm/arm-dyn.ld b/ld/testsuite/ld-arm/arm-dyn.ld index 20b96132825..96bc10c0c4c 100644 --- a/ld/testsuite/ld-arm/arm-dyn.ld +++ b/ld/testsuite/ld-arm/arm-dyn.ld @@ -76,7 +76,7 @@ SECTIONS .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ - . = ALIGN(256) + (. & (256 - 1)); + . = ALIGN (0x8000) - ((0x8000 - .) & (0x8000 - 1)); . = DATA_SEGMENT_ALIGN (0x8000, 0x1000); /* Exception handling */ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp index 3e747cab83f..1a0e5b93b9a 100644 --- a/ld/testsuite/ld-arm/arm-elf.exp +++ b/ld/testsuite/ld-arm/arm-elf.exp @@ -75,6 +75,12 @@ set armelftests { {"arm-rel31" "-static -T arm.ld" "" {arm-rel31.s} {{objdump -s arm-rel31.d}} "arm-rel31"} + {"TLS shared library" "-shared -T arm-lib.ld" "" {tls-lib.s} + {{objdump -fdw tls-lib.d} {objdump -Rw tls-lib.r}} + "tls-lib.so"} + {"TLS dynamic application" "-T arm-dyn.ld tmpdir/tls-lib.so" "" {tls-app.s} + {{objdump -fdw tls-app.d} {objdump -Rw tls-app.r}} + "tls-app"} } run_ld_link_tests $armelftests diff --git a/ld/testsuite/ld-arm/arm-lib.ld b/ld/testsuite/ld-arm/arm-lib.ld index 075eaa45cd6..0415d20d8a6 100644 --- a/ld/testsuite/ld-arm/arm-lib.ld +++ b/ld/testsuite/ld-arm/arm-lib.ld @@ -75,7 +75,7 @@ SECTIONS .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ - . = ALIGN(256) + (. & (256 - 1)); + . = ALIGN (0x8000) - ((0x8000 - .) & (0x8000 - 1)); . = DATA_SEGMENT_ALIGN (0x8000, 0x1000); /* Exception handling */ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } diff --git a/ld/testsuite/ld-arm/tls-app.d b/ld/testsuite/ld-arm/tls-app.d new file mode 100644 index 00000000000..7f486efa7b6 --- /dev/null +++ b/ld/testsuite/ld-arm/tls-app.d @@ -0,0 +1,18 @@ + +.*: file format elf32-.*arm +architecture: arm, flags 0x00000112: +EXEC_P, HAS_SYMS, D_PAGED +start address 0x00008274 + +Disassembly of section .text: + +00008274 : + 8274: e1a00000 nop \(mov r0,r0\) + 8278: e1a00000 nop \(mov r0,r0\) + 827c: e1a0f00e mov pc, lr + 8280: 000080bc streqh r8, \[r0\], -ip + 8284: 000080b4 streqh r8, \[r0\], -r4 + 8288: 000080ac andeq r8, r0, ip, lsr #1 + 828c: 00000004 andeq r0, r0, r4 + 8290: 000080c4 andeq r8, r0, r4, asr #1 + 8294: 00000014 andeq r0, r0, r4, lsl r0 diff --git a/ld/testsuite/ld-arm/tls-app.r b/ld/testsuite/ld-arm/tls-app.r new file mode 100644 index 00000000000..3f023970838 --- /dev/null +++ b/ld/testsuite/ld-arm/tls-app.r @@ -0,0 +1,12 @@ + +.*: file format elf32-.*arm + +DYNAMIC RELOCATION RECORDS +OFFSET TYPE VALUE +00010334 R_ARM_TLS_DTPMOD32 app_gd +00010338 R_ARM_TLS_DTPOFF32 app_gd +0001033c R_ARM_TLS_DTPMOD32 lib_gd +00010340 R_ARM_TLS_DTPOFF32 lib_gd +00010344 R_ARM_TLS_TPOFF32 app_ie + + diff --git a/ld/testsuite/ld-arm/tls-app.s b/ld/testsuite/ld-arm/tls-app.s new file mode 100644 index 00000000000..d505295f09d --- /dev/null +++ b/ld/testsuite/ld-arm/tls-app.s @@ -0,0 +1,34 @@ + .text + .globl foo + .type foo, %function +foo: + nop +.L2: + nop + mov pc, lr + +.Lpool: + .word lib_gd(tlsgd) + (. - .L2 - 8) + .word app_gd(tlsgd) + (. - .L2 - 8) + .word app_ld(tlsldm) + (. - .L2 - 8) + .word app_ld(tlsldo) + .word app_ie(gottpoff) + (. - .L2 - 8) + .word app_le(tpoff) + + .section .tdata,"awT" + .global app_gd +app_gd: + .space 4 + + .global app_ld +app_ld: + .space 4 + + .section .tbss,"awT",%nobits + .global app_ie +app_ie: + .space 4 + + .global app_le +app_le: + .space 4 diff --git a/ld/testsuite/ld-arm/tls-lib.d b/ld/testsuite/ld-arm/tls-lib.d new file mode 100644 index 00000000000..0492877529c --- /dev/null +++ b/ld/testsuite/ld-arm/tls-lib.d @@ -0,0 +1,15 @@ + +.*: file format elf32-.*arm +architecture: arm, flags 0x00000150: +HAS_SYMS, DYNAMIC, D_PAGED +start address 0x.* + +Disassembly of section .text: + +00000328 : + 328: e1a00000 nop \(mov r0,r0\) + 32c: e1a00000 nop \(mov r0,r0\) + 330: e1a0f00e mov pc, lr + 334: 00008098 muleq r0, r8, r0 + 338: 0000808c andeq r8, r0, ip, lsl #1 + 33c: 00000004 andeq r0, r0, r4 diff --git a/ld/testsuite/ld-arm/tls-lib.r b/ld/testsuite/ld-arm/tls-lib.r new file mode 100644 index 00000000000..c0bf4bfa40b --- /dev/null +++ b/ld/testsuite/ld-arm/tls-lib.r @@ -0,0 +1,10 @@ + +.*: file format elf32-.*arm + +DYNAMIC RELOCATION RECORDS +OFFSET TYPE VALUE +000083c4 R_ARM_TLS_DTPMOD32 \*ABS\* +000083cc R_ARM_TLS_DTPMOD32 lib_gd +000083d0 R_ARM_TLS_DTPOFF32 lib_gd + + diff --git a/ld/testsuite/ld-arm/tls-lib.s b/ld/testsuite/ld-arm/tls-lib.s new file mode 100644 index 00000000000..fa928c087e8 --- /dev/null +++ b/ld/testsuite/ld-arm/tls-lib.s @@ -0,0 +1,22 @@ + .text + .globl foo + .type foo, %function +foo: + nop +.L2: + nop + mov pc, lr + +.Lpool: + .word lib_gd(tlsgd) + (. - .L2 - 8) + .word lib_ld(tlsldm) + (. - .L2 - 8) + .word lib_ld(tlsldo) + + .section .tdata,"awT" + .global lib_gd +lib_gd: + .space 4 + + .global lib_ld +lib_ld: + .space 4 -- 2.30.2