This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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.*) }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]