public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* ARM: Support for TLS relocations
@ 2005-03-26 12:16 Daniel Jacobowitz
  2005-03-29 16:38 ` Richard Earnshaw
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Jacobowitz @ 2005-03-26 12:16 UTC (permalink / raw)
  To: binutils

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

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: ARM: Support for TLS relocations
  2005-03-26 12:16 ARM: Support for TLS relocations Daniel Jacobowitz
@ 2005-03-29 16:38 ` Richard Earnshaw
  2005-03-29 21:11   ` Daniel Jacobowitz
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Earnshaw @ 2005-03-29 16:38 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: binutils

On Fri, 2005-03-25 at 16:53, Daniel Jacobowitz wrote:
> 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.]

I assume this has completed testing by now... and since you've said
nothing further that there weren't any problems.

This is OK provided it doesn't cause any failures for coff formats.

R.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: ARM: Support for TLS relocations
  2005-03-29 16:38 ` Richard Earnshaw
@ 2005-03-29 21:11   ` Daniel Jacobowitz
  0 siblings, 0 replies; 3+ messages in thread
From: Daniel Jacobowitz @ 2005-03-29 21:11 UTC (permalink / raw)
  To: binutils

On Tue, Mar 29, 2005 at 04:37:51PM +0100, Richard Earnshaw wrote:
> I assume this has completed testing by now... and since you've said
> nothing further that there weren't any problems.

Correct.

> This is OK provided it doesn't cause any failures for coff formats.

Thanks.  I ran before and after test runs on arm-coff, and there were
no changes.

-- 
Daniel Jacobowitz
CodeSourcery, LLC

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-03-29 16:54 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-03-26 12:16 ARM: Support for TLS relocations Daniel Jacobowitz
2005-03-29 16:38 ` Richard Earnshaw
2005-03-29 21:11   ` Daniel Jacobowitz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).