This is the mail archive of the binutils@sourceware.org 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]

[PATCH 12/12] Add microblaze TLS support


From: Gopi <gopi@linux69.(none)>

Add relocations for TLS.

Add support for handling TLS symbol suffixes and generating
TLS relocs for General Dynamic and Local Dynamic models.

Create a got_symbol even when the symbol is directly used
(not just when a GOT reloc is seen)

Checkin generated files that have changed as a result of new TLS Relocs

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Signed-off-by: David Holsgrove <david.holsgrove@xilinx.com>
---
 bfd/ChangeLog              |    7 +
 bfd/bfd-in2.h              |   31 ++
 bfd/elf32-microblaze.c     |  736 +++++++++++++++++++++++++++++++++++---------
 bfd/libbfd.h               |    8 +
 bfd/reloc.c                |   39 +++
 gas/ChangeLog              |    4 +
 gas/config/tc-microblaze.c |  193 +++++++++---
 include/ChangeLog          |    4 +
 include/elf/microblaze.h   |    8 +
 9 files changed, 846 insertions(+), 184 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 75d40c9..36b69eb 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,10 @@
+2012-10-10  David Holsgrove  <david.holsgrove@xilinx.com>
+
+	* elf32-microblaze.c: Add TLS relocations support
+	* reloc.c: Update with microblaze relocation enums
+	* bfd-in2.h: Add microblaze TLS relocation
+	* libbfd.h: Generated file
+
 2012-10-10  Edgar E. Iglesias  <edgar.iglesias@gmail.com>
 
 	* elf32-microblaze.c: Add microblaze little endian support
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index e496083..1e47573 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5068,6 +5068,37 @@ value in a word.  The relocation is relative offset from  */
 the dynamic object into the runtime process image.  */
   BFD_RELOC_MICROBLAZE_COPY,
 
+/* Unused Reloc  */
+  BFD_RELOC_MICROBLAZE_64_TLS,
+
+/* This is a 64 bit reloc that stores the 32 bit GOT relative value
+of the GOT TLS GD info entry in two words (with an imm instruction). The
+relocation is GOT offset.  */
+  BFD_RELOC_MICROBLAZE_64_TLSGD,
+
+/* This is a 64 bit reloc that stores the 32 bit GOT relative value
+of the GOT TLS LD info entry in two words (with an imm instruction). The
+relocation is GOT offset.  */
+  BFD_RELOC_MICROBLAZE_64_TLSLD,
+
+/* This is a 32 bit reloc that stores the Module ID to GOT(n).  */
+  BFD_RELOC_MICROBLAZE_32_TLSDTPMOD,
+
+/* This is a 32 bit reloc that stores TLS offset to GOT(n+1).  */
+  BFD_RELOC_MICROBLAZE_32_TLSDTPREL,
+
+/* This is a 32 bit reloc for storing TLS offset to two words (uses imm
+instruction)  */
+  BFD_RELOC_MICROBLAZE_64_TLSDTPREL,
+
+/* This is a 64 bit reloc that stores 32-bit thread pointer relative offset
+to two words (uses imm instruction).  */
+  BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL,
+
+/* This is a 64 bit reloc that stores 32-bit thread pointer relative offset
+to two words (uses imm instruction).  */
+  BFD_RELOC_MICROBLAZE_64_TLSTPREL,
+
 /* AArch64 ADD immediate instruction, holding bits 0 to 11 of the address.
 Used in conjunction with BFD_RELOC_AARCH64_ADR_HI21_PCREL.  */
   BFD_RELOC_AARCH64_ADD_LO12,
diff --git a/bfd/elf32-microblaze.c b/bfd/elf32-microblaze.c
index b0f6cec..0507ecf 100644
--- a/bfd/elf32-microblaze.c
+++ b/bfd/elf32-microblaze.c
@@ -370,6 +370,132 @@ static reloc_howto_type microblaze_elf_howto_raw[] =
           0,			/* Source Mask.  */
           0x0000ffff,		/* Dest Mask.  */
           FALSE), 		/* PC relative offset?  */
+
+  /* Marker relocs for TLS.  */
+  HOWTO (R_MICROBLAZE_TLS,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_MICROBLAZE_TLS",		/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,			/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MICROBLAZE_TLSGD,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSGD",		/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,			/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MICROBLAZE_TLSLD,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSLD",		/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Computes the load module index of the load module that contains the
+     definition of its TLS sym.  */
+  HOWTO (R_MICROBLAZE_TLSDTPMOD32,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSDTPMOD32",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Computes a dtv-relative displacement, the difference between the value
+     of sym+add and the base address of the thread-local storage block that
+     contains the definition of sym, minus 0x8000.  Used for initializing GOT */
+  HOWTO (R_MICROBLAZE_TLSDTPREL32,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSDTPREL32",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Computes a dtv-relative displacement, the difference between the value
+     of sym+add and the base address of the thread-local storage block that
+     contains the definition of sym, minus 0x8000.  */
+  HOWTO (R_MICROBLAZE_TLSDTPREL64,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSDTPREL64",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Computes a tp-relative displacement, the difference between the value of
+     sym+add and the value of the thread pointer (r13).  */
+  HOWTO (R_MICROBLAZE_TLSGOTTPREL32,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSGOTTPREL32",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Computes a tp-relative displacement, the difference between the value of
+     sym+add and the value of the thread pointer (r13).  */
+  HOWTO (R_MICROBLAZE_TLSTPREL32,
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MICROBLAZE_TLSTPREL32",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
 };
 
 #ifndef NUM_ELEM
@@ -461,6 +587,27 @@ microblaze_elf_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_MICROBLAZE_32_GOTOFF:
       microblaze_reloc = R_MICROBLAZE_GOTOFF_32;
       break;
+    case BFD_RELOC_MICROBLAZE_64_TLSGD:
+      microblaze_reloc = R_MICROBLAZE_TLSGD;
+      break;
+    case BFD_RELOC_MICROBLAZE_64_TLSLD:
+      microblaze_reloc = R_MICROBLAZE_TLSLD;
+      break;
+    case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+      microblaze_reloc = R_MICROBLAZE_TLSDTPREL32;
+      break;
+    case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+      microblaze_reloc = R_MICROBLAZE_TLSDTPREL64;
+      break;
+    case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+      microblaze_reloc = R_MICROBLAZE_TLSDTPMOD32;
+      break;
+    case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+      microblaze_reloc = R_MICROBLAZE_TLSGOTTPREL32;
+      break;
+    case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
+      microblaze_reloc = R_MICROBLAZE_TLSTPREL32;
+      break;
     case BFD_RELOC_MICROBLAZE_COPY:
       microblaze_reloc = R_MICROBLAZE_COPY;
       break;
@@ -550,8 +697,21 @@ struct elf32_mb_link_hash_entry
   /* Track dynamic relocs copied for this symbol.  */
   struct elf32_mb_dyn_relocs *dyn_relocs;
 
+  /* TLS Reference Types for the symbol; Updated by check_relocs */
+#define TLS_GD		 1	/* GD reloc. */
+#define TLS_LD		 2	/* LD reloc. */
+#define TLS_TPREL	 4	/* TPREL reloc, => IE. */
+#define TLS_DTPREL	 8	/* DTPREL reloc, => LD. */
+#define TLS_TLS		16	/* Any TLS reloc.  */
+  unsigned char tls_mask;
+
 };
 
+#define IS_TLS_GD(x)   (x == (TLS_TLS | TLS_GD))
+#define IS_TLS_LD(x)   (x == (TLS_TLS | TLS_LD))
+#define IS_TLS_DTPREL(x)   (x == (TLS_TLS | TLS_DTPREL))
+#define IS_TLS_NONE(x) (x == 0)
+
 #define elf32_mb_hash_entry(ent) ((struct elf32_mb_link_hash_entry *)(ent))
 
 /* ELF linker hash table.  */
@@ -571,8 +731,17 @@ struct elf32_mb_link_hash_table
 
   /* Small local sym to section mapping cache.  */
   struct sym_cache sym_sec;
+
+  /* TLS Local Dynamic GOT Entry */
+  union {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+  } tlsld_got;
 };
 
+/* Nonzero if this section has TLS related relocations.  */
+#define has_tls_reloc sec_flg0
+
 /* Get the ELF linker hash table from a link_info structure.  */
 
 #define elf32_mb_hash_table(p)				\
@@ -604,6 +773,7 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
 
       eh = (struct elf32_mb_link_hash_entry *) entry;
       eh->dyn_relocs = NULL;
+      eh->tls_mask = 0;
     }
 
   return entry;
@@ -654,6 +824,79 @@ microblaze_elf_final_sdp (struct bfd_link_info *info)
                              + h->u.def.section->output_offset);
 }
 
+static bfd_vma
+dtprel_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;
+}
+
+/* The size of the thread control block.  */
+#define TCB_SIZE	8
+
+#if 0
+static bfd_vma
+tprel_base (struct bfd_link_info *info)
+{
+  bfd_vma base;
+
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  base = align_power ((bfd_vma) TCB_SIZE, elf_hash_table(info)->tls_sec->alignment_power);
+
+  return elf_hash_table (info)->tls_sec->vma - base;
+}
+#endif
+
+static void
+check_unique_offset(bfd_vma offset, int tls_type, bfd_vma v)
+{
+  static bfd_vma offsets[1000];
+  static bfd_vma values[1000];
+
+  static int noffsets = 0;
+  int scan1;
+
+  return;
+
+  for (scan1 = 0; scan1 < noffsets; scan1++)
+  {
+	if (offsets[scan1] == offset)
+	  {
+		fprintf (stderr, "Duplicate Offset: %lx type: %x Old: %lx New: %lx \n", offset, tls_type, values[scan1], v);
+	  }
+  }
+  fprintf (stderr, "%d Registered Offset: %lx Value: %lx type: %x\n", noffsets, offset, v, tls_type);
+  offsets[noffsets] = offset;
+  values[noffsets++] = v;
+}
+
+/* Output a simple dynamic relocation into SRELOC.  */
+
+static void
+microblaze_elf_output_dynamic_relocation (bfd *output_bfd,
+				    asection *sreloc,
+				    unsigned long reloc_index,
+				    unsigned long indx,
+				    int r_type,
+				    bfd_vma offset,
+				    bfd_vma addend
+                                    )
+{
+
+  Elf_Internal_Rela rel;
+
+  rel.r_info = ELF32_R_INFO (indx, r_type);
+  rel.r_offset = offset;
+  rel.r_addend = addend;
+
+  bfd_elf32_swap_reloca_out (output_bfd, &rel,
+              (sreloc->contents + reloc_index * sizeof (Elf32_External_Rela)));
+}
+
 /* This code is taken from elf32-m32r.c
    There is some attempt to make this function usable for many architectures,
    both USE_REL and USE_RELA ['twould be nice if such a critter existed],
@@ -707,6 +950,7 @@ microblaze_elf_relocate_section (bfd *output_bfd,
   bfd_boolean ret = TRUE;
   asection *sreloc;
   bfd_vma *local_got_offsets;
+  unsigned int tls_type;
 
   if (!microblaze_elf_howto_table[R_MICROBLAZE_max-1])
     microblaze_elf_howto_init ();
@@ -738,6 +982,8 @@ microblaze_elf_relocate_section (bfd *output_bfd,
 
       h = NULL;
       r_type = ELF32_R_TYPE (rel->r_info);
+      tls_type = 0;
+
       if (r_type < 0 || r_type >= (int) R_MICROBLAZE_max)
 	{
 	  (*_bfd_error_handler) (_("%s: unknown relocation type %d"),
@@ -971,70 +1217,208 @@ microblaze_elf_relocate_section (bfd *output_bfd,
 		break;
 	      }
 
+	    case (int) R_MICROBLAZE_TLSGD:
+		tls_type = (TLS_TLS | TLS_GD);
+		goto dogot;
+	    case (int) R_MICROBLAZE_TLSLD:
+		tls_type = (TLS_TLS | TLS_LD);
+		dogot:
 	    case (int) R_MICROBLAZE_GOT_64:
 	      {
+	        bfd_vma *offp;
+                bfd_vma off, off2;
+	        unsigned long indx;
+		bfd_vma static_value;
+
+		bfd_boolean need_relocs = FALSE;
+
 		if (htab->sgot == NULL)
 		  abort ();
-		if (h == NULL)
-		  {
-		    bfd_vma off;
+
+	        indx = 0;
+	        offp = NULL;
+
+                /* 1. Identify GOT Offset;
+                   2. Compute Static Values
+                   3. Process Module Id, Process Offset
+                   4. Fixup Relocation with GOT offset value
+                 */
+
+                /* 1. Determine GOT Offset to use : TLS_LD, global, local */
+	        if ( IS_TLS_LD (tls_type) )
+	          offp = &htab->tlsld_got.offset;
+                else if (h != NULL)
+	          {
+		    if (htab->sgotplt != NULL && h->got.offset != (bfd_vma) -1)
+		       offp = &h->got.offset;
+                    else
+		      abort ();
+	          }
+	        else
+	          {
 		    if (local_got_offsets == NULL)
 		      abort ();
-		    off = local_got_offsets[r_symndx];
-		    /* The LSB indicates whether we've already
-		       created relocation.  */
-		    if (off & 1)
-		      off &= ~1;
-		    else
-		      {
-			bfd_put_32 (output_bfd, relocation + addend,
-				    htab->sgot->contents + off);
+		    offp = &local_got_offsets[r_symndx];
+	          }
+
+                if ( !offp ) abort();
+
+                off = (*offp) & ~1;
+                off2 = off;
+
+                if (IS_TLS_LD(tls_type) || IS_TLS_GD(tls_type))
+                  off2 = off + 4;
+
+                /* Symbol index to use for relocs */
+                if (h != NULL)
+                  {
+                    bfd_boolean dyn =
+                      elf_hash_table (info)->dynamic_sections_created;
+
+                    if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+	                && (!info->shared ||
+                            !SYMBOL_REFERENCES_LOCAL (info, h)))
+	              indx = h->dynindx;
+                  }
+
+                /* Need to generate relocs ? */
+                if ((info->shared || indx != 0)
+                    && (h == NULL
+	                || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+	                || h->root.type != bfd_link_hash_undefweak))
+                  need_relocs = TRUE;
+
+		/* 2. Compute/Emit Static value of r-expression */
+		static_value = relocation + addend;
+
+		/* 3. Process module-id and offset */
+                if (! ((*offp) & 1) )
+                  {
+		     bfd_vma got_offset;
+
+		     got_offset = (htab->sgot->output_section->vma
+				       + htab->sgot->output_offset
+				       + off);
+
+                    /* Process module-id */
+                    if (IS_TLS_LD(tls_type))
+                      {
+
+                        if ( ! info->shared )
+                          {
+                            check_unique_offset( off, tls_type, 1);
+
+		            bfd_put_32 (output_bfd, 1,
+			         htab->sgot->contents + off);
+                          }
+                        else
+                          {
+                            check_unique_offset( off, tls_type, 0xDDDDDDDD);
+
+                            microblaze_elf_output_dynamic_relocation (
+                                  output_bfd, htab->srelgot,
+			      htab->srelgot->reloc_count++,
+			      /* symindex= */ 0,
+			      R_MICROBLAZE_TLSDTPMOD32,
+			      got_offset, 0);
+                          }
+                      }
+                    else if (IS_TLS_GD(tls_type))
+                      {
+                        if ( ! need_relocs )
+                          {
+                            check_unique_offset( off, tls_type, 1);
+
+		            bfd_put_32 (output_bfd, 1,
+			         htab->sgot->contents + off);
+                          }
+                        else
+                          {
+                            check_unique_offset( off, tls_type, 0);
+
+                            microblaze_elf_output_dynamic_relocation (
+                                  output_bfd, htab->srelgot,
+			      htab->srelgot->reloc_count++,
+			      /* symindex= */ indx,
+			      R_MICROBLAZE_TLSDTPMOD32,
+			      got_offset, indx ? 0 : static_value);
+                          }
+                      }
+
+                    /* Process Offset */
+		    if (htab->srelgot == NULL)
+		      abort ();
 
-			if (info->shared)
-			  {
-			    Elf_Internal_Rela outrel;
-			    bfd_byte *loc;
-			    if (htab->srelgot == NULL)
-			      abort ();
-			    outrel.r_offset = (htab->sgot->output_section->vma
+		    got_offset = (htab->sgot->output_section->vma
 					       + htab->sgot->output_offset
-					       + off);
-			    outrel.r_info = ELF32_R_INFO (0, R_MICROBLAZE_REL);
-			    outrel.r_addend = relocation + addend;
-			    loc = htab->srelgot->contents;
-			    loc += htab->srelgot->reloc_count++
-			      * sizeof (Elf32_External_Rela);
-			    bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
-			  }
-			local_got_offsets[r_symndx] |= 1;
-		      }
-		    relocation = htab->sgot->output_section->vma
-		      + htab->sgot->output_offset + off
-		      - htab->sgotplt->output_section->vma
-		      - htab->sgotplt->output_offset;
-		    unresolved_reloc = FALSE;
-		  }
-		else
-		  {
-		    if (htab->sgotplt != NULL && h != NULL
-			&& h->got.offset != (bfd_vma) -1)
-		      {
-			bfd_put_32 (output_bfd, relocation + addend,
-				    htab->sgot->contents + h->got.offset);
-			relocation = htab->sgot->output_section->vma
-			  + htab->sgot->output_offset
-			  + h->got.offset
-			  - htab->sgotplt->output_section->vma
-			  - htab->sgotplt->output_offset;
-			unresolved_reloc = FALSE;
-		      }
-		    else
-		      abort (); /* ??? */
-		  }
+					       + off2);
+                    if (IS_TLS_LD(tls_type))
+                      {
+                        /* For LD, offset should be 0 */
+                        *offp |= 1;
+		        bfd_put_32 (output_bfd, 0, htab->sgot->contents + off2);
+                      }
+                    else if (IS_TLS_GD(tls_type))
+                      {
+		        *offp |= 1;
+                        static_value -= dtprel_base(info);
+                        if (need_relocs)
+                          {
+                            check_unique_offset(off2, tls_type, indx ? 0 : static_value + 1);
+                            microblaze_elf_output_dynamic_relocation (
+                                  output_bfd, htab->srelgot,
+			      htab->srelgot->reloc_count++,
+			      /* symindex= */ indx,
+			      R_MICROBLAZE_TLSDTPREL32,
+			      got_offset, indx ? 0 : static_value);
+                          }
+                        else
+                          {
+                            check_unique_offset(off2, tls_type, static_value);
+
+		            bfd_put_32 (output_bfd, static_value,
+				    htab->sgot->contents + off2);
+                          }
+                      }
+                    else
+                      {
+		         bfd_put_32 (output_bfd, static_value,
+			    htab->sgot->contents + off2);
+
+                         /* Relocs for dyn symbols generated by
+                            finish_dynamic_symbols */
+                         if (info->shared && h == NULL)
+                           {
+		              *offp |= 1;
+
+                              check_unique_offset(off2, tls_type, static_value);
+
+                              microblaze_elf_output_dynamic_relocation (
+                                output_bfd, htab->srelgot,
+			        htab->srelgot->reloc_count++,
+			        /* symindex= */ indx,
+			        R_MICROBLAZE_REL,
+			        got_offset, static_value);
+                           }
+                      }
+                  }
+
+                /* 4. Fixup Relocation with GOT offset value */
+                /* Compute relative address of GOT entry for applying
+                         the current relocation */
+		relocation = htab->sgot->output_section->vma
+		    + htab->sgot->output_offset
+		    + off
+		    - htab->sgotplt->output_section->vma
+		    - htab->sgotplt->output_offset;
+
+                /* Apply Current Relocation */
 		bfd_put_16 (input_bfd, (relocation >> 16) & 0xffff,
 			    contents + offset + endian);
 		bfd_put_16 (input_bfd, relocation & 0xffff,
 			    contents + offset + endian + INST_WORD_SIZE);
+
+		unresolved_reloc = FALSE;
 		break;
 	      }
 
@@ -1064,6 +1448,14 @@ microblaze_elf_relocate_section (bfd *output_bfd,
 		break;
 	      }
 
+	    case (int) R_MICROBLAZE_TLSDTPREL64:
+		 relocation += addend;
+                 relocation -= dtprel_base(info);
+		 bfd_put_16 (input_bfd, (relocation >> 16) & 0xffff,
+				    contents + offset + 2);
+		 bfd_put_16 (input_bfd, relocation & 0xffff,
+				    contents + offset + 2 + INST_WORD_SIZE);
+		break;
 	    case (int) R_MICROBLAZE_64_PCREL :
 	    case (int) R_MICROBLAZE_64:
 	    case (int) R_MICROBLAZE_32:
@@ -1082,6 +1474,7 @@ microblaze_elf_relocate_section (bfd *output_bfd,
 			  relocation -= (input_section->output_section->vma
 					 + input_section->output_offset
 					 + offset + INST_WORD_SIZE);
+
 			bfd_put_16 (input_bfd, (relocation >> 16) & 0xffff,
 				    contents + offset + endian);
 			bfd_put_16 (input_bfd, relocation & 0xffff,
@@ -1977,6 +2370,34 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   return TRUE;
 }
 
+static bfd_boolean
+update_local_sym_info (bfd *abfd,
+		       Elf_Internal_Shdr *symtab_hdr,
+		       unsigned long r_symndx,
+		       unsigned int tls_type)
+{
+  bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+  unsigned char *local_got_tls_masks;
+
+  if (local_got_refcounts == NULL)
+    {
+      bfd_size_type size = symtab_hdr->sh_info;
+
+      size *= (sizeof (*local_got_refcounts)
+	       + sizeof (*local_got_tls_masks));
+      local_got_refcounts = bfd_zalloc (abfd, size);
+      if (local_got_refcounts == NULL)
+	return FALSE;
+      elf_local_got_refcounts (abfd) = local_got_refcounts;
+    }
+
+  local_got_tls_masks =
+         (unsigned char *) (local_got_refcounts + symtab_hdr->sh_info);
+  local_got_tls_masks[r_symndx] |= tls_type;
+  local_got_refcounts[r_symndx] += 1;
+
+  return TRUE;
+}
 /* Look through the relocs for a section during the first phase.  */
 
 static bfd_boolean
@@ -2013,6 +2434,7 @@ microblaze_elf_check_relocs (bfd * abfd,
       unsigned int r_type;
       struct elf_link_hash_entry * h;
       unsigned long r_symndx;
+      unsigned char tls_type = 0;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       r_type = ELF32_R_TYPE (rel->r_info);
@@ -2048,6 +2470,13 @@ microblaze_elf_check_relocs (bfd * abfd,
           break;
 
 	  /* This relocation requires .got entry.  */
+        case R_MICROBLAZE_TLSGD:
+		tls_type |= (TLS_TLS | TLS_GD);
+		goto dogottls;
+        case R_MICROBLAZE_TLSLD:
+		tls_type |= (TLS_TLS | TLS_LD);
+	dogottls:
+	  sec->has_tls_reloc = 1;
         case R_MICROBLAZE_GOT_64:
           if (htab->sgot == NULL)
             {
@@ -2059,25 +2488,12 @@ microblaze_elf_check_relocs (bfd * abfd,
           if (h != NULL)
 	    {
 	      h->got.refcount += 1;
+	      elf32_mb_hash_entry (h)->tls_mask |= 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);
-		  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 (! update_local_sym_info(abfd, symtab_hdr, r_symndx, tls_type) )
+		return FALSE;
 	    }
           break;
 
@@ -2090,7 +2506,7 @@ microblaze_elf_check_relocs (bfd * abfd,
 		/* we may need a copy reloc.  */
 		h->non_got_ref = 1;
 
-		/* we may also need a .plt entry.  */
+	        /* we may also need a .plt entry.  */
 		h->plt.refcount += 1;
 		if (ELF32_R_TYPE (rel->r_info) != R_MICROBLAZE_64_PCREL)
 		  h->pointer_equality_needed = 1;
@@ -2141,45 +2557,17 @@ microblaze_elf_check_relocs (bfd * abfd,
 
 		if (sreloc == NULL)
 		  {
-		    const char *name;
 		    bfd *dynobj;
-		    unsigned int strndx = elf_elfheader (abfd)->e_shstrndx;
-		    unsigned int shnam = _bfd_elf_single_rel_hdr (sec)->sh_name;
-
-		    name = bfd_elf_string_from_elf_section (abfd, strndx, shnam);
-		    if (name == NULL)
-		      return FALSE;
-
-		    if (strncmp (name, ".rela", 5) != 0
-			|| strcmp (bfd_get_section_name (abfd, sec),
-				   name + 5) != 0)
-		      {
-			(*_bfd_error_handler)
-			  (_("%B: bad relocation section name `%s\'"),
-			   abfd, name);
-		      }
 
 		    if (htab->elf.dynobj == NULL)
 		      htab->elf.dynobj = abfd;
+
 		    dynobj = htab->elf.dynobj;
 
-		    sreloc = bfd_get_linker_section (dynobj, name);
+	            sreloc = _bfd_elf_make_dynamic_reloc_section
+		                     (sec, dynobj, 2, abfd, 1);
 		    if (sreloc == NULL)
-		      {
-			flagword flags;
-
-			flags = (SEC_HAS_CONTENTS | SEC_READONLY
-				 | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-			if ((sec->flags & SEC_ALLOC) != 0)
-			  flags |= SEC_ALLOC | SEC_LOAD;
-			sreloc = bfd_make_section_anyway_with_flags (dynobj,
-								     name,
-								     flags);
-			if (sreloc == NULL
-			    || ! bfd_set_section_alignment (dynobj, sreloc, 2))
-			  return FALSE;
-		      }
-		    elf_section_data (sec)->sreloc = sreloc;
+		      return FALSE;
 		  }
 
 		/* If this is a global symbol, we count the number of
@@ -2310,6 +2698,8 @@ microblaze_elf_copy_indirect_symbol (struct bfd_link_info *info,
       eind->dyn_relocs = NULL;
     }
 
+   edir->tls_mask |= eind->tls_mask;
+
   _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
@@ -2528,8 +2918,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * dat)
       h->needs_plt = 0;
     }
 
+  eh = (struct elf32_mb_link_hash_entry *) h;
   if (h->got.refcount > 0)
     {
+      unsigned int need;
       asection *s;
 
       /* Make sure this symbol is output as a dynamic symbol.
@@ -2540,16 +2932,43 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * dat)
           if (! bfd_elf_link_record_dynamic_symbol (info, h))
             return FALSE;
         }
-
-      s = htab->sgot;
-      h->got.offset = s->size;
-      s->size += 4;
-      htab->srelgot->size += sizeof (Elf32_External_Rela);
+      need = 0;
+      if ((eh->tls_mask & TLS_TLS) != 0)
+	{
+          /* Handle TLS Symbol */
+	  if ((eh->tls_mask & TLS_LD) != 0)
+	    {
+	      if (!eh->elf.def_dynamic)
+		/* We'll just use htab->tlsld_got.offset.  This should
+		   always be the case.  It's a little odd if we have
+		   a local dynamic reloc against a non-local symbol.  */
+		htab->tlsld_got.refcount += 1;
+	      else
+		need += 8;
+	    }
+	  if ((eh->tls_mask & TLS_GD) != 0)
+	    need += 8;
+	}
+      else
+	{
+          /* Regular (non-TLS) symbol */
+	  need += 4;
+	}
+      if (need == 0)
+        {
+          h->got.offset = (bfd_vma) -1;
+        }
+      else
+        {
+          s = htab->sgot;
+          h->got.offset = s->size;
+          s->size += need;
+          htab->srelgot->size += need * (sizeof (Elf32_External_Rela) / 4);
+        }
     }
   else
     h->got.offset = (bfd_vma) -1;
 
-  eh = (struct elf32_mb_link_hash_entry *) h;
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -2647,6 +3066,7 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       bfd_signed_vma *end_local_got;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
+      unsigned char *lgot_masks;
       asection *srel;
 
       if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
@@ -2686,17 +3106,36 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
+      lgot_masks = (unsigned char *) end_local_got;
       s = htab->sgot;
       srel = htab->srelgot;
 
-      for (; local_got < end_local_got; ++local_got)
+      for (; local_got < end_local_got; ++local_got, ++lgot_masks)
         {
           if (*local_got > 0)
             {
-              *local_got = s->size;
-              s->size += 4;
-              if (info->shared)
-                srel->size += sizeof (Elf32_External_Rela);
+	      unsigned int need = 0;
+	      if ((*lgot_masks & TLS_TLS) != 0)
+	        {
+		  if ((*lgot_masks & TLS_GD) != 0)
+		    need += 8;
+		  if ((*lgot_masks & TLS_LD) != 0)
+		    htab->tlsld_got.refcount += 1;
+	        }
+	      else
+	        need += 4;
+
+              if (need == 0)
+                {
+                  *local_got = (bfd_vma) -1;
+                }
+              else
+                {
+                  *local_got = s->size;
+                  s->size += need;
+                  if (info->shared)
+                    srel->size += need * ( sizeof (Elf32_External_Rela) / 4 );
+                }
             }
           else
             *local_got = (bfd_vma) -1;
@@ -2707,6 +3146,16 @@ microblaze_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
      sym dynamic relocs.  */
   elf_link_hash_traverse (elf_hash_table (info), allocate_dynrelocs, info);
 
+  if (htab->tlsld_got.refcount > 0)
+    {
+      htab->tlsld_got.offset = htab->sgot->size;
+      htab->sgot->size += 8;
+      if (info->shared)
+	htab->srelgot->size += sizeof (Elf32_External_Rela);
+    }
+  else
+    htab->tlsld_got.offset = (bfd_vma) -1;
+
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       /* Make space for the trailing nop in .plt.  */
@@ -2825,6 +3274,7 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
 				      Elf_Internal_Sym *sym)
 {
   struct elf32_mb_link_hash_table *htab;
+  struct elf32_mb_link_hash_entry *eh = elf32_mb_hash_entry(h);
 
   htab = elf32_mb_hash_table (info);
   if (htab == NULL)
@@ -2896,12 +3346,14 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
         }
     }
 
-  if (h->got.offset != (bfd_vma) -1)
+  /* h->got.refcount to be checked ? */
+  if (h->got.offset != (bfd_vma) -1 &&
+      ! ((h->got.offset & 1) ||
+        IS_TLS_LD(eh->tls_mask) || IS_TLS_GD(eh->tls_mask)))
     {
       asection *sgot;
       asection *srela;
-      Elf_Internal_Rela rela;
-      bfd_byte *loc;
+      bfd_vma offset;
 
       /* This symbol has an entry in the global offset table.  Set it
          up.  */
@@ -2910,36 +3362,36 @@ microblaze_elf_finish_dynamic_symbol (bfd *output_bfd,
       srela = htab->srelgot;
       BFD_ASSERT (sgot != NULL && srela != NULL);
 
-      rela.r_offset = (sgot->output_section->vma
-                       + sgot->output_offset
+      offset = (sgot->output_section->vma + sgot->output_offset
                        + (h->got.offset &~ (bfd_vma) 1));
 
-      /* If this is a -Bsymbolic link, and the symbol is defined
-         locally, we just want to emit a RELATIVE reloc.  Likewise if
-         the symbol was forced to be local because of a version file.
-         The entry in the global offset table will already have been
-         initialized in the relocate_section function.  */
-      if (info->shared
-          && (info->symbolic || h->dynindx == -1)
-          && h->def_regular)
-        {
-          asection *sec = h->root.u.def.section;
-          rela.r_info = ELF32_R_INFO (0, R_MICROBLAZE_REL);
-          rela.r_addend = (h->root.u.def.value
-                           + sec->output_section->vma
-                           + sec->output_offset);
-        }
-      else
-        {
-          rela.r_info = ELF32_R_INFO (h->dynindx, R_MICROBLAZE_GLOB_DAT);
-          rela.r_addend = 0;
-        }
+       {
+         /* If this is a -Bsymbolic link, and the symbol is defined
+            locally, we just want to emit a RELATIVE reloc.  Likewise if
+            the symbol was forced to be local because of a version file.
+            The entry in the global offset table will already have been
+            initialized in the relocate_section function.  */
+          if (info->shared && (info->symbolic || h->dynindx == -1)
+            && h->def_regular)
+          {
+            asection *sec = h->root.u.def.section;
+            microblaze_elf_output_dynamic_relocation ( output_bfd,
+              srela, srela->reloc_count++, /* symindex= */ 0,
+	      R_MICROBLAZE_REL, offset,
+              h->root.u.def.value + sec->output_section->vma
+              + sec->output_offset);
+          }
+        else
+          {
+            microblaze_elf_output_dynamic_relocation ( output_bfd,
+              srela, srela->reloc_count++,
+	      h->dynindx, R_MICROBLAZE_GLOB_DAT, offset, 0);
+          }
+       }
 
-      bfd_put_32 (output_bfd, (bfd_vma) 0,
+        bfd_put_32 (output_bfd, (bfd_vma) 0,
                   sgot->contents + (h->got.offset &~ (bfd_vma) 1));
-      loc = srela->contents;
-      loc += srela->reloc_count++ * sizeof (Elf32_External_Rela);
-      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+
     }
 
   if (h->needs_copy)
@@ -3109,7 +3561,7 @@ microblaze_elf_add_symbol_hook (bfd *abfd,
 #define ELF_TARGET_ID		MICROBLAZE_ELF_DATA
 #define ELF_MACHINE_CODE	EM_MICROBLAZE
 #define ELF_MACHINE_ALT1	EM_MICROBLAZE_OLD
-#define ELF_MAXPAGESIZE		0x4   		/* 4k, if we ever have 'em.  */
+#define ELF_MAXPAGESIZE		0x1000   		/* 4k, if we ever have 'em.  */
 #define elf_info_to_howto	microblaze_elf_info_to_howto
 #define elf_info_to_howto_rel	NULL
 
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 6c48d64..e20013a 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2418,6 +2418,14 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_MICROBLAZE_64_GOTOFF",
   "BFD_RELOC_MICROBLAZE_32_GOTOFF",
   "BFD_RELOC_MICROBLAZE_COPY",
+  "BFD_RELOC_MICROBLAZE_64_TLS",
+  "BFD_RELOC_MICROBLAZE_64_TLSGD",
+  "BFD_RELOC_MICROBLAZE_64_TLSLD",
+  "BFD_RELOC_MICROBLAZE_32_TLSDTPMOD",
+  "BFD_RELOC_MICROBLAZE_32_TLSDTPREL",
+  "BFD_RELOC_MICROBLAZE_64_TLSDTPREL",
+  "BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL",
+  "BFD_RELOC_MICROBLAZE_64_TLSTPREL",
   "BFD_RELOC_AARCH64_ADD_LO12",
   "BFD_RELOC_AARCH64_ADR_GOT_PAGE",
   "BFD_RELOC_AARCH64_ADR_HI21_PCREL",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 47d052d..76669fa 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5882,6 +5882,45 @@ ENUM
 ENUMDOC
   This is used to tell the dynamic linker to copy the value out of
   the dynamic object into the runtime process image.
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLS
+ENUMDOC
+  Unused Reloc
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLSGD
+ENUMDOC
+  This is a 64 bit reloc that stores the 32 bit GOT relative value
+  of the GOT TLS GD info entry in two words (with an imm instruction). The
+  relocation is GOT offset.
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLSLD
+ENUMDOC
+  This is a 64 bit reloc that stores the 32 bit GOT relative value
+  of the GOT TLS LD info entry in two words (with an imm instruction). The
+  relocation is GOT offset.
+ENUM
+  BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
+ENUMDOC
+  This is a 32 bit reloc that stores the Module ID to GOT(n).
+ENUM
+  BFD_RELOC_MICROBLAZE_32_TLSDTPREL
+ENUMDOC
+  This is a 32 bit reloc that stores TLS offset to GOT(n+1).
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLSDTPREL
+ENUMDOC
+  This is a 32 bit reloc for storing TLS offset to two words (uses imm
+  instruction)
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
+ENUMDOC
+  This is a 64 bit reloc that stores 32-bit thread pointer relative offset
+  to two words (uses imm instruction).
+ENUM
+  BFD_RELOC_MICROBLAZE_64_TLSTPREL
+ENUMDOC
+  This is a 64 bit reloc that stores 32-bit thread pointer relative offset
+  to two words (uses imm instruction).
 
 ENUM
   BFD_RELOC_AARCH64_ADD_LO12
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 29daf78..4aed1c5 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,5 +1,9 @@
 2012-10-10  David Holsgrove  <david.holsgrove@xilinx.com>
 
+	* config/tc-microblaze.c: Add microblaze TLS support
+
+2012-10-10  David Holsgrove  <david.holsgrove@xilinx.com>
+
 	* config/tc-microblaze.c: Add support for slr and shr insns
 
 2012-10-10  Nagaraju Mekala  <nagaraju.mekala@xilinx.com>
diff --git a/gas/config/tc-microblaze.c b/gas/config/tc-microblaze.c
index e9c7846..d77c0c0 100644
--- a/gas/config/tc-microblaze.c
+++ b/gas/config/tc-microblaze.c
@@ -81,6 +81,11 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define GOT_OFFSET           8
 #define PLT_OFFSET           9
 #define GOTOFF_OFFSET        10
+#define TLSGD_OFFSET         11
+#define TLSLD_OFFSET         12
+#define TLSDTPMOD_OFFSET     13
+#define TLSDTPREL_OFFSET     14
+#define TLSTPREL_OFFSET      15
 
 
 /* Initialize the relax table.  */
@@ -97,6 +102,12 @@ const relax_typeS md_relax_table[] =
   { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /*  8: GOT_OFFSET.  */
   { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /*  9: PLT_OFFSET.  */
   { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /* 10: GOTOFF_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /* 11: TLSGD_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /* 12: TLSLD_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*1, 0 },  /* 13: TLSDTPMOD_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /* 14: TLSDTPREL_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 },  /* 15: TLSGOTTPREL_OFFSET.  */
+  { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }  /* 16: TLSTPREL_OFFSET.  */
 };
 
 static struct hash_control * opcode_hash_control;	/* Opcode mnemonics.  */
@@ -599,9 +610,73 @@ parse_exp (char *s, expressionS *e)
 }
 
 /* Symbol modifiers (@GOT, @PLT, @GOTOFF).  */
+
+#define IMM_NONE    0
 #define IMM_GOT    1
 #define IMM_PLT    2
 #define IMM_GOTOFF 3
+#define IMM_TLSGD 4
+#define IMM_TLSLD 5
+#define IMM_TLSDTPMOD 6
+#define IMM_TLSDTPREL 7
+#define IMM_TLSTPREL 8
+#define IMM_MAX 9
+
+struct imm_type {
+	char *isuffix;	 /* Suffix String */
+	int itype;       /* Suffix Type */
+	int otype;       /* Offset Type */
+};
+
+/* These are NOT in assending order of type, GOTOFF is ahead to make
+   sure @GOTOFF does not get matched with @GOT  */
+static struct imm_type imm_types[] = {
+	{ "NONE", IMM_NONE , 0 },
+	{ "GOTOFF", IMM_GOTOFF , GOTOFF_OFFSET },
+	{ "GOT", IMM_GOT , GOT_OFFSET },
+	{ "PLT", IMM_PLT , PLT_OFFSET },
+	{ "TLSGD", IMM_TLSGD , TLSGD_OFFSET },
+	{ "TLSLDM", IMM_TLSLD, TLSLD_OFFSET },
+	{ "TLSDTPMOD", IMM_TLSDTPMOD, TLSDTPMOD_OFFSET },
+	{ "TLSDTPREL", IMM_TLSDTPREL, TLSDTPREL_OFFSET },
+	{ "TLSTPREL", IMM_TLSTPREL, TLSTPREL_OFFSET }
+};
+
+static int
+match_imm (const char *s, int *ilen)
+{
+  int i;
+  int slen;
+
+  /* Check for matching suffix */
+  for (i = 1; i < IMM_MAX; i++) {
+        slen = strlen (imm_types[i].isuffix);
+
+	if (strncmp (imm_types[i].isuffix, s, slen) == 0)
+	{
+	        *ilen = slen;
+		return imm_types[i].itype;
+	}
+  } /* for */
+  *ilen = 0;
+  return 0;
+}
+
+static int
+get_imm_otype (int itype)
+{
+  int i, otype;
+
+  otype = 0;
+  /* Check for matching itype */
+  for (i = 1; i < IMM_MAX; i++) {
+    if (imm_types[i].itype == itype) {
+      otype = imm_types[i].otype;
+      break;
+    }
+  }
+  return otype;
+}
 
 static symbolS * GOT_symbol;
 
@@ -612,6 +687,9 @@ parse_imm (char * s, expressionS * e, int min, int max)
 {
   char *new_pointer;
   char *atp;
+  int itype, ilen;
+
+  ilen = 0;
 
   /* Find the start of "@GOT" or "@PLT" suffix (if any) */
   for (atp = s; *atp != '@'; atp++)
@@ -620,25 +698,17 @@ parse_imm (char * s, expressionS * e, int min, int max)
 
   if (*atp == '@')
     {
-      if (strncmp (atp + 1, "GOTOFF", 5) == 0)
-	{
-	  *atp = 0;
-	  e->X_md = IMM_GOTOFF;
-	}
-      else if (strncmp (atp + 1, "GOT", 3) == 0)
-	{
-	  *atp = 0;
-	  e->X_md = IMM_GOT;
-	}
-      else if (strncmp (atp + 1, "PLT", 3) == 0)
-	{
-	  *atp = 0;
-	  e->X_md = IMM_PLT;
+      itype = match_imm (atp + 1, &ilen);
+      if (itype != 0)
+        {
+          *atp = 0;
+          e->X_md = itype;
 	}
       else
 	{
 	  atp = NULL;
 	  e->X_md = 0;
+          ilen = 0;
 	}
       *atp = 0;
     }
@@ -655,6 +725,11 @@ parse_imm (char * s, expressionS * e, int min, int max)
 
   new_pointer = parse_exp (s, e);
 
+  if (!GOT_symbol && ! strncmp (s, GOT_SYMBOL_NAME, 20))
+    {
+      GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+    }
+
   if (e->X_op == O_absent)
     ; /* An error message has already been emitted.  */
   else if ((e->X_op != O_constant && e->X_op != O_symbol) )
@@ -670,9 +745,7 @@ parse_imm (char * s, expressionS * e, int min, int max)
     {
       *atp = '@'; /* restore back (needed?)  */
       if (new_pointer >= atp)
-        new_pointer += (e->X_md == IMM_GOTOFF)?7:4;
-      /* sizeof("@GOTOFF", "@GOT" or "@PLT") */
-
+        new_pointer += ilen + 1; /* sizeof (imm_suffix) + 1 for '@' */
     }
   return new_pointer;
 }
@@ -792,7 +865,14 @@ tc_microblaze_fix_adjustable (struct fix *fixP)
   if (fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF
       || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF
       || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT
-      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT)
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGD
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSLD
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPREL
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSDTPREL
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
+      || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSTPREL)
     return 0;
 
   return 1;
@@ -940,15 +1020,12 @@ md_assemble (char * str)
 	    opc = str_microblaze_rw_anchor;
 	  else
 	    opc = NULL;
-	  if (exp.X_md == IMM_GOT)
-	    subtype = GOT_OFFSET;
-	  else if (exp.X_md == IMM_PLT)
-	    subtype = PLT_OFFSET;
-	  else if (exp.X_md == IMM_GOTOFF)
-	    subtype = GOTOFF_OFFSET;
-	  else
-	    subtype = opcode->inst_offset_type;
 
+          if (exp.X_md != 0) {
+             subtype = get_imm_otype(exp.X_md);
+          } else {
+	    subtype = opcode->inst_offset_type;
+          }
 	  output = frag_var (rs_machine_dependent,
 			     isize * 2, /* maxm of 2 words.  */
 			     isize,     /* minm of 1 word.  */
@@ -1436,12 +1513,11 @@ md_assemble (char * str)
           char *opc = NULL;
           relax_substateT subtype;
 
-	  if (exp.X_md == IMM_GOT)
-	    subtype = GOT_OFFSET;
-	  else if (exp.X_md == IMM_PLT)
-	    subtype = PLT_OFFSET;
-	  else
+          if (exp.X_md != 0) {
+             subtype = get_imm_otype(exp.X_md);
+          } else {
 	    subtype = opcode->inst_offset_type;
+          }
 	  output = frag_var (rs_machine_dependent,
 			     isize * 2, /* maxm of 2 words.  */
 			     isize,     /* minm of 1 word.  */
@@ -1503,12 +1579,11 @@ md_assemble (char * str)
           char *opc = NULL;
           relax_substateT subtype;
 
-          if (exp.X_md == IMM_GOT)
-            subtype = GOT_OFFSET;
-          else if (exp.X_md == IMM_PLT)
-            subtype = PLT_OFFSET;
-          else
+          if (exp.X_md != 0) {
+             subtype = get_imm_otype(exp.X_md);
+          } else {
 	    subtype = opcode->inst_offset_type;
+          }
           output = frag_var (rs_machine_dependent,
 			     isize * 2, /* maxm of 2 words.  */
 			     isize,     /* minm of 1 word.  */
@@ -1576,12 +1651,11 @@ md_assemble (char * str)
           char *opc = NULL;
           relax_substateT subtype;
 
-          if (exp.X_md == IMM_GOT)
-            subtype = GOT_OFFSET;
-          else if (exp.X_md == IMM_PLT)
-            subtype = PLT_OFFSET;
-          else
-            subtype = opcode->inst_offset_type;
+          if (exp.X_md != 0) {
+             subtype = get_imm_otype(exp.X_md);
+          } else {
+	    subtype = opcode->inst_offset_type;
+          }
           output = frag_var (rs_machine_dependent,
 			     isize * 2, /* maxm of 2 words.  */
 			     isize,     /* minm of 1 word.  */
@@ -1847,6 +1921,24 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
       fragP->fr_fix += INST_WORD_SIZE * 2;
       fragP->fr_var = 0;
       break;
+    case TLSGD_OFFSET:
+      fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+	       fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSGD);
+      fragP->fr_fix += INST_WORD_SIZE * 2;
+      fragP->fr_var = 0;
+      break;
+    case TLSLD_OFFSET:
+      fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+	       fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSLD);
+      fragP->fr_fix += INST_WORD_SIZE * 2;
+      fragP->fr_var = 0;
+      break;
+    case TLSDTPREL_OFFSET:
+      fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+	       fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSDTPREL);
+      fragP->fr_fix += INST_WORD_SIZE * 2;
+      fragP->fr_var = 0;
+      break;
 
     default:
       abort ();
@@ -2028,6 +2120,11 @@ md_apply_fix (fixS *   fixP,
 	}
       break;
 
+    case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+    case BFD_RELOC_MICROBLAZE_64_TLSGD:
+    case BFD_RELOC_MICROBLAZE_64_TLSLD:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+
     case BFD_RELOC_MICROBLAZE_64_GOTPC:
     case BFD_RELOC_MICROBLAZE_64_GOT:
     case BFD_RELOC_MICROBLAZE_64_PLT:
@@ -2206,11 +2303,16 @@ md_estimate_size_before_relax (fragS * fragP,
     case GOT_OFFSET:
     case PLT_OFFSET:
     case GOTOFF_OFFSET:
+    case TLSGD_OFFSET:
+    case TLSLD_OFFSET:
+    case TLSTPREL_OFFSET:
+    case TLSDTPREL_OFFSET:
       fragP->fr_var = INST_WORD_SIZE*2;
       break;
     case DEFINED_RO_SEGMENT:
     case DEFINED_RW_SEGMENT:
     case DEFINED_PC_OFFSET:
+    case TLSDTPMOD_OFFSET:
       fragP->fr_var = INST_WORD_SIZE;
       break;
     default:
@@ -2294,6 +2396,13 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
     case BFD_RELOC_MICROBLAZE_64_PLT:
     case BFD_RELOC_MICROBLAZE_64_GOTOFF:
     case BFD_RELOC_MICROBLAZE_32_GOTOFF:
+    case BFD_RELOC_MICROBLAZE_64_TLSGD:
+    case BFD_RELOC_MICROBLAZE_64_TLSLD:
+    case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+    case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+    case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+    case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+    case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
       code = fixp->fx_r_type;
       break;
 
diff --git a/include/ChangeLog b/include/ChangeLog
index 7b24264..51c446a 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,7 @@
+2012-10-10  David Holsgrove  <david.holsgrove@xilinx.com>
+
+	* elf/microblaze.h: Define microblaze TLS RELOC_NUMBER
+
 2012-08-28  Matthew Gretton-Dann  <matthew.gretton-dann@arm.com>
 
 	Apply mainline patches
diff --git a/include/elf/microblaze.h b/include/elf/microblaze.h
index d392231..0f73809 100644
--- a/include/elf/microblaze.h
+++ b/include/elf/microblaze.h
@@ -50,6 +50,14 @@ START_RELOC_NUMBERS (elf_microblaze_reloc_type)
   RELOC_NUMBER (R_MICROBLAZE_GOTOFF_64, 19) /* Offset relative to GOT.  */
   RELOC_NUMBER (R_MICROBLAZE_GOTOFF_32, 20) /* Offset relative to GOT.  */
   RELOC_NUMBER (R_MICROBLAZE_COPY, 21)      /* Runtime copy.  */
+  RELOC_NUMBER (R_MICROBLAZE_TLS, 22)       /* TLS Reloc */
+  RELOC_NUMBER (R_MICROBLAZE_TLSGD, 23)     /* TLS General Dynamic */
+  RELOC_NUMBER (R_MICROBLAZE_TLSLD, 24)     /* TLS Local Dynamic */
+  RELOC_NUMBER (R_MICROBLAZE_TLSDTPMOD32, 25)  /* TLS Module ID */
+  RELOC_NUMBER (R_MICROBLAZE_TLSDTPREL32, 26)  /* TLS Offset Within TLS Block */
+  RELOC_NUMBER (R_MICROBLAZE_TLSDTPREL64, 27)  /* TLS Offset Within TLS Block */
+  RELOC_NUMBER (R_MICROBLAZE_TLSGOTTPREL32, 28)   /* TLS Offset From Thread Pointer */
+  RELOC_NUMBER (R_MICROBLAZE_TLSTPREL32, 29)   /* TLS Offset From Thread Pointer */
    
 END_RELOC_NUMBERS (R_MICROBLAZE_max)
 
-- 
1.7.0.4


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