From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24554 invoked by alias); 25 Mar 2005 16:52:53 -0000 Mailing-List: contact binutils-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: binutils-owner@sources.redhat.com Received: (qmail 24126 invoked from network); 25 Mar 2005 16:52:33 -0000 Received: from unknown (HELO nevyn.them.org) (66.93.172.17) by sourceware.org with SMTP; 25 Mar 2005 16:52:33 -0000 Received: from drow by nevyn.them.org with local (Exim 4.50 #1 (Debian)) id 1DEs3Y-0004jq-2a for ; Fri, 25 Mar 2005 11:53:08 -0500 Date: Sat, 26 Mar 2005 12:16:00 -0000 From: Daniel Jacobowitz To: binutils@sources.redhat.com Subject: ARM: Support for TLS relocations Message-ID: <20050325165308.GA17318@nevyn.them.org> Mail-Followup-To: binutils@sources.redhat.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.6+20040907i X-SW-Source: 2005-03/txt/msg00781.txt.bz2 This patch implements the ARM-documented TLS relocations, except for the twelve-bit instruction relocations. The first version was implemented by Phil, although I'm afraid he won't recognize much of it; mistakes all mine. The implemented relocations are, for the assembler: .word foo(tlsgd) .word foo(tlsldm) .word foo(tlsldo) .word foo(gottpoff) .word foo(tpoff) They support trailing addends, e.g. a typical usage: .word foo(tlsgd) + (. - .L2 + 8) No linker relaxations are supported; the compiler's choice is final. Dynamic relocations are avoided when unneeded, though. Tested on arm-linux, including NPTL with appropriate gcc/glibc patches. Is this OK for HEAD? [Full disclosure - I actually tested a different version of this patch, which used a different name for the IE32 relocation operator. After changing it to gottpoff I've started a new test cycle which is running now.] -- Daniel Jacobowitz CodeSourcery, LLC 2005-03-25 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-25 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-25 Daniel Jacobowitz * gas/arm/tls.s, gas/arm/tls.d: New files. * gas/arm/arm.exp: Run TLS test. 2005-03-25 Daniel Jacobowitz Phil Blundell * elf/arm.h: Add TLS relocations. 2005-03-25 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. Index: binutils/bfd/bfd-in2.h =================================================================== --- binutils.orig/bfd/bfd-in2.h 2005-03-25 11:15:11.809447416 -0500 +++ binutils/bfd/bfd-in2.h 2005-03-25 11:17:27.933909131 -0500 @@ -2688,6 +2688,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. */ Index: binutils/bfd/libbfd.h =================================================================== --- binutils.orig/bfd/libbfd.h 2005-03-25 11:15:11.809447416 -0500 +++ binutils/bfd/libbfd.h 2005-03-25 11:17:27.933909131 -0500 @@ -1177,6 +1177,14 @@ static const char *const bfd_reloc_code_ "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", Index: binutils/bfd/reloc.c =================================================================== --- binutils.orig/bfd/reloc.c 2005-03-25 11:15:11.809447416 -0500 +++ binutils/bfd/reloc.c 2005-03-25 11:17:27.935908652 -0500 @@ -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. Index: binutils/gas/config/tc-arm.c =================================================================== --- binutils.orig/gas/config/tc-arm.c 2005-03-25 11:17:24.678687612 -0500 +++ binutils/gas/config/tc-arm.c 2005-03-25 11:17:27.941907218 -0500 @@ -4873,6 +4873,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 }; @@ -12209,6 +12214,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: @@ -12532,6 +12545,18 @@ tc_gen_reloc (asection * section ATTRIBU 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 @@ -13828,6 +13853,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; @@ -13883,8 +13913,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 == '(' @@ -13898,9 +13932,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); } Index: binutils/include/elf/arm.h =================================================================== --- binutils.orig/include/elf/arm.h 2005-03-25 11:15:11.829442638 -0500 +++ binutils/include/elf/arm.h 2005-03-25 11:17:27.941907218 -0500 @@ -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 */ Index: binutils/bfd/elf32-arm.c =================================================================== --- binutils.orig/bfd/elf32-arm.c 2005-03-25 11:17:23.581949891 -0500 +++ binutils/bfd/elf32-arm.c 2005-03-25 11:17:27.945906261 -0500 @@ -294,49 +294,49 @@ static reloc_howto_type elf32_arm_howto_ 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_ 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 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 {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_ 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 st 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 st 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 *a 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_li #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_how 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 * 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 * 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 * 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 * 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 * /* 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 * #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 * } 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 * 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, struc #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, struc #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, struc *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 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 { 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 { 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 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 * o 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 * o 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 * o } } + 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 * o } } - 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; @@ -5752,6 +6343,8 @@ const struct elf_size_info elf32_arm_siz #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 Index: binutils/gas/testsuite/gas/arm/tls.s =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/gas/testsuite/gas/arm/tls.s 2005-03-25 11:17:27.945906261 -0500 @@ -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) Index: binutils/ld/testsuite/ld-arm/tls-lib.s =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-lib.s 2005-03-25 11:17:27.946906022 -0500 @@ -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 Index: binutils/gas/testsuite/gas/arm/arm.exp =================================================================== --- binutils.orig/gas/testsuite/gas/arm/arm.exp 2005-03-25 11:15:11.829442638 -0500 +++ binutils/gas/testsuite/gas/arm/arm.exp 2005-03-25 11:17:27.946906022 -0500 @@ -72,6 +72,8 @@ if {[istarget *arm*-*-*] || [istarget "x 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 { Index: binutils/ld/testsuite/ld-arm/tls-app.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-app.d 2005-03-25 11:17:27.946906022 -0500 @@ -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 Index: binutils/ld/testsuite/ld-arm/tls-app.r =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-app.r 2005-03-25 11:17:27.946906022 -0500 @@ -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 + + Index: binutils/ld/testsuite/ld-arm/tls-app.s =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-app.s 2005-03-25 11:17:27.947905783 -0500 @@ -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 Index: binutils/ld/testsuite/ld-arm/arm-elf.exp =================================================================== --- binutils.orig/ld/testsuite/ld-arm/arm-elf.exp 2005-03-25 11:15:11.846438578 -0500 +++ binutils/ld/testsuite/ld-arm/arm-elf.exp 2005-03-25 11:17:27.947905783 -0500 @@ -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 Index: binutils/ld/testsuite/ld-arm/tls-lib.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-lib.d 2005-03-25 11:17:27.947905783 -0500 @@ -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 Index: binutils/ld/testsuite/ld-arm/tls-lib.r =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/ld/testsuite/ld-arm/tls-lib.r 2005-03-25 11:17:27.947905783 -0500 @@ -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 + + Index: binutils/gas/testsuite/gas/arm/tls.d =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ binutils/gas/testsuite/gas/arm/tls.d 2005-03-23 16:17:18.000000000 -0500 @@ -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 Index: binutils/ld/testsuite/ld-arm/arm-lib.ld =================================================================== --- binutils.orig/ld/testsuite/ld-arm/arm-lib.ld 2005-03-25 11:17:07.096891954 -0500 +++ binutils/ld/testsuite/ld-arm/arm-lib.ld 2005-03-25 11:17:53.659756201 -0500 @@ -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.*) } Index: binutils/ld/testsuite/ld-arm/arm-dyn.ld =================================================================== --- binutils.orig/ld/testsuite/ld-arm/arm-dyn.ld 2005-03-25 11:17:07.070898170 -0500 +++ binutils/ld/testsuite/ld-arm/arm-dyn.ld 2005-03-25 11:17:53.658756440 -0500 @@ -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.*) }