This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
ARM: Support for TLS relocations
- From: Daniel Jacobowitz <drow at false dot org>
- To: binutils at sources dot redhat dot com
- Date: Fri, 25 Mar 2005 11:53:08 -0500
- Subject: ARM: Support for TLS relocations
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 <dan@codesourcery.com>
Phil Blundell <philb@gnu.org>
* 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 <dan@codesourcery.com>
Phil Blundell <philb@gnu.org>
* 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 <dan@codesourcery.com>
* gas/arm/tls.s, gas/arm/tls.d: New files.
* gas/arm/arm.exp: Run TLS test.
2005-03-25 Daniel Jacobowitz <dan@codesourcery.com>
Phil Blundell <philb@gnu.org>
* elf/arm.h: Add TLS relocations.
2005-03-25 Daniel Jacobowitz <dan@codesourcery.com>
* 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 <foo>:
+ 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 <foo>:
+ 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 <main>:
+ 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.*) }