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


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

[PATCH] Add TLS support to binutils (ia32 and ia64 for now)


Hi!

Now that the branch has been cut, here is a patch for TLS (thread local
storage) support.
The aim is that one can write:
int __thread i = 24;
in C code to make i an per-thread variable. This needs binutils support (as
below), glibc support (partly done in current glibc 2.3 CVS) and gcc support
(AFAIK not yet started).
For details see Ulrich's paper http://people.redhat.com/drepper/tls.pdf
For SPARC, Sun defined the ABI, so there is no need to invent anything,
just to add support for this in sparc arch files, but for the rest of
architectures it would be good if port maintainers could come up with their
TLS ABIs.
Ok to commit, or do you have any suggestions on what should be cleaned up?

2002-02-12  Jakub Jelinek  <jakub@redhat.com>

	* elf.c (_bfd_elf_make_section_from_shdr): Set SEC_FLAG2_THREAD_LOCAL
	for symbols from SHF_TLS section.
	(_bfd_elf_print_private_bfd_data): Add PT_TLS.
	(elf_fake_sections): Set SHF_TLS for SEC_FLAG2_THREAD_LOCAL sections.
	(map_sections_to_segments): Build PT_TLS segment if necessary.
	(assign_file_positions_for_segments): Likewise.
	(get_program_header_size): Account for PT_TLS segment.
	(swap_out_syms): Set type of BSF_THREAD_LOCAL symbols and symbols from
	SEC_FLAG2_THREAD_LOCAL sections to STT_TLS.
	* reloc.c: Add 386 and IA-64 TLS relocs.
	* section.c (struct sec): Add flags2.
	(SEC_FLAG2_THREAD_LOCAL): Define.
	(STD_SECTION): Adjust for added flags2 field.
	* elflink.h (elf_link_add_object_symbols): Support .tcommon.
	(size_dynamic_sections): If DF_STATIC_TLS, set DF_FLAGS
	unconditionally.
	(struct elf_final_link_info): Add first_tls_sec.
	(elf_bfd_final_link): Set first_tls_sec.
	(elf_link_output_extsym): Handle STT_TLS symbols.
	(elf_link_input_bfd): Likewise.
	* syms.c (BSF_THREAD_LOCAL): Define.
	* bfd-in2.h: Rebuilt.
	* libbfd.h: Rebuilt.
	* elf32-i386.c (elf_i386_tls_transition, dtpoff_base, tpoff): New
	functions.
	(elf_howto_table): Add TLS relocs.
	(elf_i386_reloc_type_lookup): Support TLS relocs.
	(elf_i386_info_to_howto_rel): Likewise.
	(struct elf_i386_link_hash_entry): Add tls_type.
	(elf_i386_hash_entry, local_got_tls_type): New macros.
	(struct elf_i386_link_hash_table): Add tls_ldm_got.
	(link_hash_newfunc): Clear tls_type.
	(elf_i386_check_relocs): Support TLS relocs.
	(elf_i386_gc_sweep_hook): Likewise.
	(allocate_dynrelocs): Likewise.
	(elf_i386_size_dynamic_sections): Likewise.
	(elf_i386_relocate_section): Likewise.
	(elf_i386_finish_dynamic_symbol): Likewise.
	* elfxx-ia64.c (struct elfNN_ia64_dyn_sym_info): Add tprel_offset,
	dtpmod_offset, dtprel_offset, tprel_done, dtpmod_done, dtprel_done,
	want_tprel, want_dtpmod, want_dtprel.
	(elfNN_ia64_tprel_base, elfNN_ia64_dtprel_base): New functions.
	(ia64_howto_table): Add TLS relocs, rename R_IA64_LTOFF_TP22 to
	R_IA64_LTOFF_TPREL22.
	(elf_code_to_howto_index): Add TLS relocs.
	(elfNN_ia64_check_relocs): Support TLS relocs.
	(allocate_global_data_got): Account for TLS .got data.
	(allocate_dynrel_entries): Account for TLS dynamic relocations.
	(elfNN_ia64_install_value): Supprt TLS relocs.
	(set_got_entry): Support TLS relocs.
	(elfNN_ia64_relocate_section): Likewise.

	* config/obj-elf.c (elf_common): Renamed from obj_elf_common.
	(obj_elf_common): Call elf_common.
	(obj_elf_tls_common): New function.
	(elf_pseudo_tab): Support .tls_common.
	(special_sections): Add .tdata and .tbss.
	(obj_elf_change_section): Set SEC_FLAG2_THREAD_LOCAL for SHF_TLS
	sections.
	(obj_elf_parse_section_letters): Support T in section flags (SHF_TLS).
	(obj_elf_parse_section_letters): Include T in error message.
	* config/tc-ppc.c (ppc_section_letter): Likewise.
	* config/tc-alpha.c (alpha_elf_section_letter): Likewise.
	(tc_gen_reloc): Handle SEC_FLAG2_THREAD_LOCAL the same way as
	SEC_MERGE.
	* config/tc-sparc.c (md_apply_fix3): Likewise.
	* config/tc-i386.c (tc_i386_fix_adjustable): Add TLS relocs.
	Define them if not BFD_ASSEMBLER.
	(lex_got): Support @TLSGD, @TLSLDM, @GOTTPOFF, @TPOFF and @DTPOFF.
	(md_apply_fix3): Add TLS relocs.
	* config/tc-ia64.c (enum reloc_func): Add FUNC_DTP_MODULE,
	FUNC_DTP_RELATIVE, FUNC_TP_RELATIVE, FUNC_LT_DTP_MODULE,
	FUNC_LT_DTP_RELATIVE, FUNC_LT_TP_RELATIVE.
	(pseudo_func): Support @dtpmod(), @dtprel() and @tprel().
	(ia64_elf_section_letter): Include T in error message.
	(md_begin): Support TLS operators.
	(md_operand): Likewise.
	(ia64_gen_real_reloc_type): Support TLS relocs.
	* testsuite/gas/i386/tlspic.s: New file.
	* testsuite/gas/i386/tlsd.s: New file.
	* testsuite/gas/i386/tlsnopic.s: New file.
	* testsuite/gas/i386/tlsd.d: New file.
	* testsuite/gas/i386/tlsnopic.d: New file.
	* testsuite/gas/i386/tlspic.d: New file.
	* testsuite/gas/i386/i386.exp: Add tlsd, tlsnopic and tlspic tests.
	* testsuite/gas/ia64/tls.s: New file.
	* testsuite/gas/ia64/tls.d: New file.
	* testsuite/gas/ia64/ia64.exp: Add tls test.
	* write.c (adjust_reloc_syms): Don't change symbols in
	SEC_FLAG2_THREAD_LOCAL sections to STT_SECTION + addend.

	* elf/common.h (PT_TLS, SHF_TLS, STT_TLS, DF_STATIC_TLS): Define.
	* elf/ia64.h (R_IA64_LTOFF_TPREL22): Renamed from R_IA64_LTOFF_TP22.
	* elf/i386.h: Add TLS relocs.

	* scripttempl/elf.sc: Add .rel{,a}.t{bss,data}, .tdata and .tbss.
	* ldlang.c (lang_add_section): Set SEC_FLAG2_THREAD_LOCAL for
	output section if necessary.
	Handle .tbss.
	(lang_size_sections): Clear _raw_size for .tbss section
	(it allocates space in PT_TLS segment only).
	* ldwrite.c (build_link_order): Build link order for .tbss too.

	* readelf.c (get_segment_type): Add PT_TLS.
	(get_elf_section_flags): Add SHF_TLS.
	(get_dynamic_flags): Optimize.  Add DF_STATIC_TLS.
	(process_dynamic_segment): Use puts instead of printf.
	(get_symbol_type): Support STT_TLS.
	* objcopy.c (setup_section): Copy flags2.

--- binutils/readelf.c.jj	Mon Jan 28 14:42:33 2002
+++ binutils/readelf.c	Mon Jan 28 18:48:03 2002
@@ -1906,6 +1906,7 @@ get_segment_type (p_type)
     case PT_NOTE:       return "NOTE";
     case PT_SHLIB:      return "SHLIB";
     case PT_PHDR:       return "PHDR";
+    case PT_TLS:	return "TLS";
 
     case PT_GNU_EH_FRAME:
 			return "GNU_EH_FRAME";
@@ -3116,6 +3117,7 @@ get_elf_section_flags (sh_flags)
 	case SHF_LINK_ORDER:       strcat (buff, "L"); break;
 	case SHF_OS_NONCONFORMING: strcat (buff, "O"); break;
 	case SHF_GROUP:            strcat (buff, "G"); break;
+	case SHF_TLS:		   strcat (buff, "T"); break;
 
 	default:
 	  if (flag & SHF_MASKOS)
@@ -4154,7 +4156,10 @@ static const char *
 get_dynamic_flags (flags)
      bfd_vma flags;
 {
-  static char buff [64];
+  static char buff [128];
+  char *p = buff;
+
+  *p = '\0';
   while (flags)
     {
       bfd_vma flag;
@@ -4162,14 +4167,20 @@ get_dynamic_flags (flags)
       flag = flags & - flags;
       flags &= ~ flag;
 
+      if (p != buff)
+	*p++ = ' ';
+
       switch (flag)
 	{
-	case DF_ORIGIN:   strcat (buff, "ORIGIN "); break;
-	case DF_SYMBOLIC: strcat (buff, "SYMBOLIC "); break;
-	case DF_TEXTREL:  strcat (buff, "TEXTREL "); break;
-	case DF_BIND_NOW: strcat (buff, "BIND_NOW "); break;
-	default:          strcat (buff, "unknown "); break;
+	case DF_ORIGIN:   strcpy (p, "ORIGIN"); break;
+	case DF_SYMBOLIC: strcpy (p, "SYMBOLIC"); break;
+	case DF_TEXTREL:  strcpy (p, "TEXTREL"); break;
+	case DF_BIND_NOW: strcpy (p, "BIND_NOW"); break;
+	case DF_STATIC_TLS: strcpy (p, "STATIC_TLS"); break;
+	default:          strcpy (p, "unknown"); break;
 	}
+
+      p = strchr (p, '\0');
     }
   return buff;
 }
@@ -4355,7 +4366,7 @@ process_dynamic_segment (file)
 	{
 	case DT_FLAGS:
 	  if (do_dynamic)
-	    printf ("%s", get_dynamic_flags (entry->d_un.d_val));
+	    puts (get_dynamic_flags (entry->d_un.d_val));
 	  break;
 
 	case DT_AUXILIARY:
@@ -5164,6 +5175,7 @@ get_symbol_type (type)
     case STT_SECTION:  return "SECTION";
     case STT_FILE:     return "FILE";
     case STT_COMMON:   return "COMMON";
+    case STT_TLS:      return "TLS";
     default:
       if (type >= STT_LOPROC && type <= STT_HIPROC)
 	{
--- binutils/objcopy.c.jj	Mon Jan 28 14:42:33 2002
+++ binutils/objcopy.c	Mon Jan 28 16:02:38 2002
@@ -1610,6 +1610,8 @@ setup_section (ibfd, isection, obfdarg)
       goto loser;
     }
 
+  /* Copy second set of flags.  */
+  osection->flags2 = isection->flags2;
   /* Copy merge entity size.  */
   osection->entsize = isection->entsize;
 
--- bfd/elf.c.jj	Mon Jan 28 14:42:17 2002
+++ bfd/elf.c	Thu Feb  7 16:31:41 2002
@@ -544,7 +544,7 @@ _bfd_elf_make_section_from_shdr (abfd, h
      const char *name;
 {
   asection *newsect;
-  flagword flags;
+  flagword flags, flags2;
   struct elf_backend_data *bed;
 
   if (hdr->bfd_section != NULL)
@@ -567,6 +567,7 @@ _bfd_elf_make_section_from_shdr (abfd, h
     return false;
 
   flags = SEC_NO_FLAGS;
+  flags2 = 0;
   if (hdr->sh_type != SHT_NOBITS)
     flags |= SEC_HAS_CONTENTS;
   if (hdr->sh_type == SHT_GROUP)
@@ -593,6 +594,8 @@ _bfd_elf_make_section_from_shdr (abfd, h
   if (hdr->sh_flags & SHF_GROUP)
     if (!setup_group (abfd, hdr, newsect))
       return false;
+  if ((hdr->sh_flags & SHF_TLS) != 0)
+    flags2 |= SEC_FLAG2_THREAD_LOCAL;
 
   /* The debugging sections appear to be recognized only by name, not
      any sort of flag.  */
@@ -631,6 +634,8 @@ _bfd_elf_make_section_from_shdr (abfd, h
   if (! bfd_set_section_flags (abfd, newsect, flags))
     return false;
 
+  newsect->flags2 = flags2;
+
   if ((flags & SEC_ALLOC) != 0)
     {
       Elf_Internal_Phdr *phdr;
@@ -855,6 +860,7 @@ _bfd_elf_print_private_bfd_data (abfd, f
 	    case PT_NOTE: pt = "NOTE"; break;
 	    case PT_SHLIB: pt = "SHLIB"; break;
 	    case PT_PHDR: pt = "PHDR"; break;
+	    case PT_TLS: pt = "TLS"; break;
 	    case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
 	    default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break;
 	    }
@@ -2236,6 +2242,8 @@ elf_fake_sections (abfd, asect, failedpt
     }
   if (elf_group_name (asect) != NULL)
     this_hdr->sh_flags |= SHF_GROUP;
+  if ((asect->flags2 & SEC_FLAG2_THREAD_LOCAL) != 0)
+    this_hdr->sh_flags |= SHF_TLS;
 
   /* Check for processor-specific section types.  */
   if (bed->elf_backend_fake_sections
@@ -2918,6 +2926,8 @@ map_sections_to_segments (abfd)
   asection **hdrpp;
   boolean phdr_in_segment = true;
   boolean writable;
+  int tls_count = 0;
+  asection *first_tls = NULL;
   asection *dynsec, *eh_frame_hdr;
   bfd_size_type amt;
 
@@ -3156,6 +3166,39 @@ map_sections_to_segments (abfd)
 	  *pm = m;
 	  pm = &m->next;
 	}
+      if (s->flags2 & SEC_FLAG2_THREAD_LOCAL)
+	{
+	  if (! tls_count)
+	    first_tls = s;
+	  tls_count++;
+	}
+    }
+
+  /* If there are any SHF_TLS output sections, add PT_TLS segment.  */
+  if (tls_count > 0)
+    {
+      int i;
+
+      amt = sizeof (struct elf_segment_map);
+      amt += (tls_count - 1) * sizeof (asection *);
+      m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
+      if (m == NULL)
+	goto error_return;
+      m->next = NULL;
+      m->p_type = PT_TLS;
+      m->count = tls_count;
+      /* Mandated PF_R.  */
+      m->p_flags = PF_R;
+      m->p_flags_valid = 1;
+      for (i = 0; i < tls_count; ++i)
+	{
+	  BFD_ASSERT (first_tls->flags2 & SEC_FLAG2_THREAD_LOCAL);
+	  m->sections[i] = first_tls;
+	  first_tls = first_tls->next;
+	}
+
+      *pm = m;
+      pm = &m->next;
     }
 
   /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
@@ -3580,6 +3623,20 @@ Error: First section in segment (%s) sta
 	      if ((flags & SEC_LOAD) != 0)
 		p->p_filesz += sec->_raw_size;
 
+	      if (p->p_type == PT_TLS
+		  && sec->_raw_size == 0
+		  && (sec->flags & SEC_HAS_CONTENTS) == 0)
+		{
+		  struct bfd_link_order *o;
+		  bfd_vma tbss_size = 0;
+
+		  for (o = sec->link_order_head; o != NULL; o = o->next)
+		    if (tbss_size < o->offset + o->size)
+		      tbss_size = o->offset + o->size;
+
+		  p->p_memsz += tbss_size;
+		}
+
 	      if (align > p->p_align
 		  && (p->p_type != PT_LOAD || (abfd->flags & D_PAGED) == 0))
 		p->p_align = align;
@@ -3714,6 +3771,16 @@ get_program_header_size (abfd)
 	}
     }
 
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if (s->flags2 & SEC_FLAG2_THREAD_LOCAL)
+	{
+	  /* We need a PT_TLS segment.  */
+	  ++segs;
+	  break;
+	}
+    }
+
   /* Let the backend count up any program headers it might need.  */
   if (bed->elf_backend_additional_program_headers)
     {
@@ -5014,13 +5081,18 @@ swap_out_syms (abfd, sttp, relocatable_p
 	  sym.st_shndx = shndx;
 	}
 
-      if ((flags & BSF_FUNCTION) != 0)
+      if ((flags & BSF_THREAD_LOCAL) != 0)
+	type = STT_TLS;
+      else if ((flags & BSF_FUNCTION) != 0)
 	type = STT_FUNC;
       else if ((flags & BSF_OBJECT) != 0)
 	type = STT_OBJECT;
       else
 	type = STT_NOTYPE;
 
+      if (syms[idx]->section->flags2 & SEC_FLAG2_THREAD_LOCAL)
+	type = STT_TLS;
+
       /* Processor-specific types */
       if (type_ptr != NULL
 	  && bed->elf_backend_get_symbol_type)
--- bfd/bfd-in2.h.jj	Mon Jan 14 17:02:42 2002
+++ bfd/bfd-in2.h	Wed Feb  6 19:03:22 2002
@@ -1179,6 +1179,11 @@ typedef struct sec
 
   /*  End of section flags.  */
 
+  flagword flags2;
+
+  /* The section contains thread local data.  */
+#define SEC_FLAG2_THREAD_LOCAL 1
+
   /* Some internal packed boolean fields.  */
 
   /* See the vma field.  */
@@ -2193,6 +2198,14 @@ to compensate for the borrow when the lo
   BFD_RELOC_386_RELATIVE,
   BFD_RELOC_386_GOTOFF,
   BFD_RELOC_386_GOTPC,
+  BFD_RELOC_386_TLS_GD,
+  BFD_RELOC_386_TLS_LDM,
+  BFD_RELOC_386_TLS_LDO_32,
+  BFD_RELOC_386_TLS_IE_32,
+  BFD_RELOC_386_TLS_LE_32,
+  BFD_RELOC_386_TLS_DTPMOD32,
+  BFD_RELOC_386_TLS_DTPOFF32,
+  BFD_RELOC_386_TLS_TPOFF32,
 
 /* x86-64/elf relocations */
   BFD_RELOC_X86_64_GOT32,
@@ -2855,12 +2868,25 @@ this offset in the reloc's section offse
   BFD_RELOC_IA64_IPLTMSB,
   BFD_RELOC_IA64_IPLTLSB,
   BFD_RELOC_IA64_COPY,
+  BFD_RELOC_IA64_LTOFF22X,
+  BFD_RELOC_IA64_LDXMOV,
+  BFD_RELOC_IA64_TPREL14,
   BFD_RELOC_IA64_TPREL22,
+  BFD_RELOC_IA64_TPREL64I,
   BFD_RELOC_IA64_TPREL64MSB,
   BFD_RELOC_IA64_TPREL64LSB,
-  BFD_RELOC_IA64_LTOFF_TP22,
-  BFD_RELOC_IA64_LTOFF22X,
-  BFD_RELOC_IA64_LDXMOV,
+  BFD_RELOC_IA64_LTOFF_TPREL22,
+  BFD_RELOC_IA64_DTPMOD64MSB,
+  BFD_RELOC_IA64_DTPMOD64LSB,
+  BFD_RELOC_IA64_LTOFF_DTPMOD22,
+  BFD_RELOC_IA64_DTPREL14,
+  BFD_RELOC_IA64_DTPREL22,
+  BFD_RELOC_IA64_DTPREL64I,
+  BFD_RELOC_IA64_DTPREL32MSB,
+  BFD_RELOC_IA64_DTPREL32LSB,
+  BFD_RELOC_IA64_DTPREL64MSB,
+  BFD_RELOC_IA64_DTPREL64LSB,
+  BFD_RELOC_IA64_LTOFF_DTPREL22,
 
 /* Motorola 68HC11 reloc.
 This is the 8 bits high part of an absolute address. */
@@ -3074,6 +3100,9 @@ typedef struct symbol_cache_entry
           as well.  */
 #define BSF_DEBUGGING_RELOC 0x20000
 
+       /* This symbol is thread local.  Used in ELF.  */
+#define BSF_THREAD_LOCAL  0x40000
+
   flagword flags;
 
        /* A pointer to the section to which this symbol is
--- bfd/elf32-i386.c.jj	Sat Dec 22 00:48:25 2001
+++ bfd/elf32-i386.c	Sat Feb  9 22:32:22 2002
@@ -46,6 +46,9 @@ static boolean elf_i386_create_dynamic_s
   PARAMS((bfd *, struct bfd_link_info *));
 static void elf_i386_copy_indirect_symbol
   PARAMS ((struct elf_link_hash_entry *, struct elf_link_hash_entry *));
+static int elf_i386_tls_transition
+  PARAMS ((struct bfd_link_info *, int, int));
+
 static boolean elf_i386_check_relocs
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   const Elf_Internal_Rela *));
@@ -65,6 +68,10 @@ static boolean elf_i386_fake_sections
   PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *));
 static boolean elf_i386_size_dynamic_sections
   PARAMS ((bfd *, struct bfd_link_info *));
+static bfd_vma dtpoff_base
+  PARAMS ((bfd *));
+static bfd_vma tpoff
+  PARAMS ((bfd *, bfd_vma));
 static boolean elf_i386_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
 	   Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -121,9 +128,15 @@ static reloc_howto_type elf_howto_table[
      R_386_ext_offset is the value to subtract from a reloc type of
      R_386_16 thru R_386_PC8 to form an index into this table.  */
 #define R_386_standard ((unsigned int) R_386_GOTPC + 1)
-#define R_386_ext_offset ((unsigned int) R_386_16 - R_386_standard)
+#define R_386_ext_offset ((unsigned int) R_386_TLS_GD - R_386_standard)
 
   /* The remaining relocs are a GNU extension.  */
+  HOWTO(R_386_TLS_GD, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_GD",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_LDM, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_LDM",
+	true, 0xffffffff, 0xffffffff, false),
   HOWTO(R_386_16, 0, 1, 16, false, 0, complain_overflow_bitfield,
 	bfd_elf_generic_reloc, "R_386_16",
 	true, 0xffff, 0xffff, false),
@@ -137,9 +150,31 @@ static reloc_howto_type elf_howto_table[
 	bfd_elf_generic_reloc, "R_386_PC8",
 	true, 0xff, 0xff, true),
 
-  /* Another gap.  */
 #define R_386_ext ((unsigned int) R_386_PC8 + 1 - R_386_ext_offset)
-#define R_386_vt_offset ((unsigned int) R_386_GNU_VTINHERIT - R_386_ext)
+#define R_386_tls_offset ((unsigned int) R_386_TLS_LDO_32 - R_386_ext)
+  /* These are common with Solaris TLS implementation.  */
+  HOWTO(R_386_TLS_LDO_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_LDO_32",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_IE_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_IE_32",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_LE_32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_LE_32",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_DTPMOD32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_DTPMOD32",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_DTPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_DTPOFF32",
+	true, 0xffffffff, 0xffffffff, false),
+  HOWTO(R_386_TLS_TPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_386_TLS_TPOFF32",
+	true, 0xffffffff, 0xffffffff, false),
+
+  /* Another gap.  */
+#define R_386_tls ((unsigned int) R_386_TLS_TPOFF32 + 1 - R_386_tls_offset)
+#define R_386_vt_offset ((unsigned int) R_386_GNU_VTINHERIT - R_386_tls)
 
 /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_386_GNU_VTINHERIT,	/* type */
@@ -237,6 +272,14 @@ elf_i386_reloc_type_lookup (abfd, code)
       return &elf_howto_table[(unsigned int) R_386_GOTPC ];
 
       /* The remaining relocs are a GNU extension.  */
+    case BFD_RELOC_386_TLS_GD:
+      TRACE ("BFD_RELOC_386_TLS_GD");
+      return &elf_howto_table[(unsigned int) R_386_TLS_GD - R_386_ext_offset];
+
+    case BFD_RELOC_386_TLS_LDM:
+      TRACE ("BFD_RELOC_386_TLS_LDM");
+      return &elf_howto_table[(unsigned int) R_386_TLS_LDM - R_386_ext_offset];
+
     case BFD_RELOC_16:
       TRACE ("BFD_RELOC_16");
       return &elf_howto_table[(unsigned int) R_386_16 - R_386_ext_offset];
@@ -253,6 +296,31 @@ elf_i386_reloc_type_lookup (abfd, code)
       TRACE ("BFD_RELOC_8_PCREL");
       return &elf_howto_table[(unsigned int) R_386_PC8 - R_386_ext_offset];
 
+    /* Common with Sun TLS implementation.  */
+    case BFD_RELOC_386_TLS_LDO_32:
+      TRACE ("BFD_RELOC_386_TLS_LDO_32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_LDO_32 - R_386_tls_offset];
+
+    case BFD_RELOC_386_TLS_IE_32:
+      TRACE ("BFD_RELOC_386_TLS_IE_32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_IE_32 - R_386_tls_offset];
+
+    case BFD_RELOC_386_TLS_LE_32:
+      TRACE ("BFD_RELOC_386_TLS_LE_32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_LE_32 - R_386_tls_offset];
+
+    case BFD_RELOC_386_TLS_DTPMOD32:
+      TRACE ("BFD_RELOC_386_TLS_DTPMOD32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_DTPMOD32 - R_386_tls_offset];
+
+    case BFD_RELOC_386_TLS_DTPOFF32:
+      TRACE ("BFD_RELOC_386_TLS_DTPOFF32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_DTPOFF32 - R_386_tls_offset];
+
+    case BFD_RELOC_386_TLS_TPOFF32:
+      TRACE ("BFD_RELOC_386_TLS_TPOFF32");
+      return &elf_howto_table[(unsigned int) R_386_TLS_TPOFF32 - R_386_tls_offset];
+
     case BFD_RELOC_VTABLE_INHERIT:
       TRACE ("BFD_RELOC_VTABLE_INHERIT");
       return &elf_howto_table[(unsigned int) R_386_GNU_VTINHERIT
@@ -292,8 +360,10 @@ elf_i386_info_to_howto_rel (abfd, cache_
   if ((indx = r_type) >= R_386_standard
       && ((indx = r_type - R_386_ext_offset) - R_386_standard
 	  >= R_386_ext - R_386_standard)
-      && ((indx = r_type - R_386_vt_offset) - R_386_ext
-	  >= R_386_vt - R_386_ext))
+      && ((indx = r_type - R_386_tls_offset) - R_386_ext
+	  >= R_386_tls - R_386_ext)
+      && ((indx = r_type - R_386_vt_offset) - R_386_tls
+	  >= R_386_vt - R_386_tls))
     {
       (*_bfd_error_handler) (_("%s: invalid relocation type %d"),
 			     bfd_archive_filename (abfd), (int) r_type);
@@ -481,8 +551,17 @@ struct elf_i386_link_hash_entry
 
   /* Track dynamic relocs copied for this symbol.  */
   struct elf_i386_dyn_relocs *dyn_relocs;
+
+  enum {
+    GOT_UNKNOWN = 0, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE
+  } tls_type;
 };
 
+#define elf_i386_hash_entry(ent) ((struct elf_i386_link_hash_entry *)(ent))
+
+#define local_got_tls_type(abfd, symsize, n) \
+  ((char *)(elf_local_got_refcounts (abfd) + (symsize)))[n]
+
 /* i386 ELF linker hash table.  */
 
 struct elf_i386_link_hash_table
@@ -498,6 +577,11 @@ struct elf_i386_link_hash_table
   asection *sdynbss;
   asection *srelbss;
 
+  union {
+    bfd_signed_vma refcount;
+    bfd_vma offset;
+  } tls_ldm_got;
+
   /* Small local sym to section mapping cache.  */
   struct sym_sec_cache sym_sec;
 };
@@ -533,6 +617,7 @@ link_hash_newfunc (entry, table, string)
 
       eh = (struct elf_i386_link_hash_entry *) entry;
       eh->dyn_relocs = NULL;
+      eh->tls_type = GOT_UNKNOWN;
     }
 
   return entry;
@@ -678,6 +763,29 @@ elf_i386_copy_indirect_symbol (dir, ind)
   _bfd_elf_link_hash_copy_indirect (dir, ind);
 }
 
+static int
+elf_i386_tls_transition (info, r_type, is_local)
+     struct bfd_link_info *info;
+     int r_type;
+     int is_local;
+{
+  if (info->shared)
+    return r_type;
+
+  switch (r_type)
+    {
+    case R_386_TLS_GD:
+    case R_386_TLS_IE_32:
+      if (is_local)
+	return R_386_TLS_LE_32;
+      return R_386_TLS_IE_32;
+    case R_386_TLS_LDM:
+      return R_386_TLS_LE_32;
+    }
+
+  return r_type;
+}
+
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure linkage
    table, and dynamic reloc sections.  */
@@ -708,10 +816,12 @@ elf_i386_check_relocs (abfd, info, sec, 
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
+      unsigned int r_type;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
+      r_type = ELF32_R_TYPE (rel->r_info);
 
       if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
 	{
@@ -726,38 +836,86 @@ elf_i386_check_relocs (abfd, info, sec, 
       else
 	h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-      switch (ELF32_R_TYPE (rel->r_info))
+      r_type = elf_i386_tls_transition (info, r_type, h == NULL);
+
+      switch (r_type)
 	{
+	case R_386_TLS_IE_32:
+	  if (info->shared)
+	    info->flags |= DF_STATIC_TLS;
+	  /* FALLTHROUGH */
 	case R_386_GOT32:
+	case R_386_TLS_GD:
 	  /* This symbol requires a global offset table entry.  */
-	  if (h != NULL)
-	    {
-	      h->got.refcount += 1;
-	    }
-	  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)
+	      {
+	      default:
+	      case R_386_GOT32: tls_type = GOT_NORMAL; break;
+	      case R_386_TLS_GD: tls_type = GOT_TLS_GD; break;
+	      case R_386_TLS_IE_32: tls_type = GOT_TLS_IE; break;
+	      }
+
+	    if (h != NULL)
+	      {
+		h->got.refcount += 1;
+		old_tls_type = elf_i386_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);
-		  local_got_refcounts = ((bfd_signed_vma *)
-					 bfd_zalloc (abfd, size));
-		  if (local_got_refcounts == NULL)
+		    size = symtab_hdr->sh_info;
+		    size *= (sizeof (bfd_signed_vma) + sizeof(char));
+		    local_got_refcounts = ((bfd_signed_vma *)
+					   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;
+		old_tls_type
+		  = local_got_tls_type (abfd, symtab_hdr->sh_info, r_symndx);
+	      }
+
+	    /* If a TLS symbol is accessed using IE at least once,
+	       there is no point to use dynamic model for it.  */
+	    if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
+		&& (old_tls_type != GOT_TLS_GD || tls_type != GOT_TLS_IE))
+	      {
+		if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD)
+		  tls_type = GOT_TLS_IE;
+		else
+		  {
+		    (*_bfd_error_handler)
+		      (_("%s: `%s' accessed both as normal and thread local symbol"),
+		       bfd_archive_filename (abfd), h->root.root.string);
 		    return false;
-		  elf_local_got_refcounts (abfd) = local_got_refcounts;
-		}
-	      local_got_refcounts[r_symndx] += 1;
-	    }
+		  }
+	      }
+
+	    if (old_tls_type != tls_type)
+	      {
+		if (h != NULL)
+		  elf_i386_hash_entry(h)->tls_type = tls_type;
+		else
+		  local_got_tls_type (abfd, symtab_hdr->sh_info, r_symndx)
+		    = tls_type;
+	      }
+	  }
 	  /* Fall through */
 
 	case R_386_GOTOFF:
 	case R_386_GOTPC:
+	create_got:
 	  if (htab->sgot == NULL)
 	    {
 	      if (htab->elf.dynobj == NULL)
@@ -767,6 +925,10 @@ elf_i386_check_relocs (abfd, info, sec, 
 	    }
 	  break;
 
+	case R_386_TLS_LDM:
+	  htab->tls_ldm_got.refcount += 1;
+	  goto create_got;
+
 	case R_386_PLT32:
 	  /* This symbol requires a procedure linkage table entry.  We
 	     actually build the entry in adjust_dynamic_symbol,
@@ -824,7 +986,7 @@ elf_i386_check_relocs (abfd, info, sec, 
 	     symbol.  */
 	  if ((info->shared
 	       && (sec->flags & SEC_ALLOC) != 0
-	       && (ELF32_R_TYPE (rel->r_info) != R_386_PC32
+	       && (r_type != R_386_PC32
 		   || (h != NULL
 		       && (! info->symbolic
 			   || h->root.type == bfd_link_hash_defweak
@@ -924,7 +1086,7 @@ elf_i386_check_relocs (abfd, info, sec, 
 		}
 
 	      p->count += 1;
-	      if (ELF32_R_TYPE (rel->r_info) == R_386_PC32)
+	      if (r_type == R_386_PC32)
 		p->pc_count += 1;
 	    }
 	  break;
@@ -943,6 +1105,15 @@ elf_i386_check_relocs (abfd, info, sec, 
 	    return false;
 	  break;
 
+	case R_386_TLS_LE_32:
+	  if (info->shared)
+	    {
+	      (*_bfd_error_handler) (_("%s: TLS local exec code cannot be linked into shared objects"),
+				     bfd_archive_filename (abfd));
+	      return false;
+	    }
+	  break;
+
 	default:
 	  break;
 	}
@@ -1017,11 +1188,18 @@ elf_i386_gc_sweep_hook (abfd, info, sec,
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
-    switch (ELF32_R_TYPE (rel->r_info))
+    switch (elf_i386_tls_transition (info, ELF32_R_TYPE (rel->r_info),
+				     ELF32_R_SYM (rel->r_info)
+				     >= symtab_hdr->sh_info))
       {
+      case R_386_TLS_LDM:
+	if (elf_i386_hash_table (info)->tls_ldm_got.refcount > 0)
+	  elf_i386_hash_table (info)->tls_ldm_got.refcount -= 1;
+	break;
+
+      case R_386_TLS_GD:
+      case R_386_TLS_IE_32:
       case R_386_GOT32:
-      case R_386_GOTOFF:
-      case R_386_GOTPC:
 	r_symndx = ELF32_R_SYM (rel->r_info);
 	if (r_symndx >= symtab_hdr->sh_info)
 	  {
@@ -1313,10 +1491,18 @@ allocate_dynrelocs (h, inf)
       h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
     }
 
-  if (h->got.refcount > 0)
+  /* If R_386_TLS_IE_32 symbol is now local to the binary,
+     make it a R_386_TLS_LE_32 requiring no TLS entry.  */
+  if (h->got.refcount > 0
+      && !info->shared
+      && h->dynindx == -1
+      && elf_i386_hash_entry(h)->tls_type == GOT_TLS_IE)
+    h->got.offset = (bfd_vma) -1;    
+  else if (h->got.refcount > 0)
     {
       asection *s;
       boolean dyn;
+      int tls_type = elf_i386_hash_entry(h)->tls_type;
 
       /* Make sure this symbol is output as a dynamic symbol.
 	 Undefined weak syms won't yet be marked as dynamic.  */
@@ -1330,8 +1516,18 @@ allocate_dynrelocs (h, inf)
       s = htab->sgot;
       h->got.offset = s->_raw_size;
       s->_raw_size += 4;
+      /* R_386_TLS_GD needs 2 consecutive GOT slots.  */
+      if (tls_type == GOT_TLS_GD)
+	s->_raw_size += 4;
       dyn = htab->elf.dynamic_sections_created;
-      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h))
+      /* R_386_TLS_IE_32 needs one dynamic relocation,
+	 R_386_TLS_GD needs one if local symbol and two if global.  */
+      if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
+	  || tls_type == GOT_TLS_IE)
+	htab->srelgot->_raw_size += sizeof (Elf32_External_Rel);
+      else if (tls_type == GOT_TLS_GD)
+	htab->srelgot->_raw_size += 2 * sizeof (Elf32_External_Rel);
+      else if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h))
 	htab->srelgot->_raw_size += sizeof (Elf32_External_Rel);
     }
   else
@@ -1474,6 +1670,7 @@ elf_i386_size_dynamic_sections (output_b
     {
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
+      char *local_tls_type;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
@@ -1513,15 +1710,20 @@ elf_i386_size_dynamic_sections (output_b
       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
+      local_tls_type = &local_got_tls_type (ibfd, symtab_hdr->sh_info, 0);
       s = htab->sgot;
       srel = htab->srelgot;
-      for (; local_got < end_local_got; ++local_got)
+      for (; local_got < end_local_got; ++local_got, ++local_tls_type)
 	{
 	  if (*local_got > 0)
 	    {
 	      *local_got = s->_raw_size;
 	      s->_raw_size += 4;
-	      if (info->shared)
+	      if (*local_tls_type == GOT_TLS_GD)
+		s->_raw_size += 4;
+	      if (info->shared
+		  || *local_tls_type == GOT_TLS_GD
+		  || *local_tls_type == GOT_TLS_IE)
 		srel->_raw_size += sizeof (Elf32_External_Rel);
 	    }
 	  else
@@ -1529,6 +1731,17 @@ elf_i386_size_dynamic_sections (output_b
 	}
     }
 
+  if (htab->tls_ldm_got.refcount > 0)
+    {
+      /* Allocate 2 got entries and 1 dynamic reloc for R_386_TLS_LDM
+	 relocs.  */
+      htab->tls_ldm_got.offset = htab->sgot->_raw_size;
+      htab->sgot->_raw_size += 8;
+      htab->srelgot->_raw_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->elf, allocate_dynrelocs, (PTR) info);
@@ -1672,6 +1885,62 @@ elf_i386_fake_sections (abfd, hdr, sec)
   return true;
 }
 
+/* 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 (abfd)
+     bfd *abfd;
+{
+  asection *sec;
+
+  for (sec = abfd->sections; sec; sec = sec->next)
+    if (sec->flags2 & SEC_FLAG2_THREAD_LOCAL)
+      return sec->vma;
+  BFD_FAIL ();
+  return 0;
+}
+
+/* Return the relocation value for @tpoff relocation
+   if STT_TLS virtual address is ADDRESS.  */
+
+static bfd_vma
+tpoff (abfd, address)
+     bfd *abfd;
+     bfd_vma address;
+{
+  asection *sec;
+  bfd_vma align = 0, base = 0, end = 0;
+
+  for (sec = abfd->sections; sec; sec = sec->next)
+    if (sec->flags2 & SEC_FLAG2_THREAD_LOCAL)
+      {
+	base = sec->vma;
+	break;
+      }
+  BFD_ASSERT (sec);
+  for (; sec && (sec->flags2 & SEC_FLAG2_THREAD_LOCAL); sec = sec->next)
+    {
+      bfd_vma size = sec->_raw_size;
+
+      if (bfd_get_section_alignment (abfd, sec) > align)
+	align = bfd_get_section_alignment (abfd, sec);
+      if (sec->_raw_size == 0 && (sec->flags & SEC_HAS_CONTENTS) == 0)
+	{
+	  struct bfd_link_order *o;
+
+	  size = 0;
+	  for (o = sec->link_order_head; o != NULL; o = o->next)
+	    if (size < o->offset + o->size)
+	      size = o->offset + o->size;
+	}
+      end = sec->vma + size;
+    }
+
+  return align_power (end - base, align) + base - address;
+}
+
 /* Relocate an i386 ELF section.  */
 
 static boolean
@@ -1702,7 +1971,7 @@ elf_i386_relocate_section (output_bfd, i
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
+      unsigned int r_type;
       reloc_howto_type *howto;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
@@ -1713,6 +1982,7 @@ elf_i386_relocate_section (output_bfd, i
       boolean unresolved_reloc;
       bfd_reloc_status_type r;
       unsigned int indx;
+      int tls_type;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == (int) R_386_GNU_VTINHERIT
@@ -1720,8 +1990,10 @@ elf_i386_relocate_section (output_bfd, i
 	continue;
 
       if ((indx = (unsigned) r_type) >= R_386_standard
-	  && ((indx = (unsigned) r_type - R_386_ext_offset) - R_386_standard
-	      >= R_386_ext - R_386_standard))
+	  && ((indx = r_type - R_386_ext_offset) - R_386_standard
+	      >= R_386_ext - R_386_standard)
+	  && ((indx = r_type - R_386_tls_offset) - R_386_ext
+	      >= R_386_tls - R_386_ext))
 	{
 	  bfd_set_error (bfd_error_bad_value);
 	  return false;
@@ -2050,6 +2322,278 @@ elf_i386_relocate_section (output_bfd, i
 	    }
 	  break;
 
+	case R_386_TLS_GD:
+	case R_386_TLS_IE_32:
+	  r_type = elf_i386_tls_transition (info, r_type, h == NULL);
+	  tls_type = GOT_UNKNOWN;
+	  if (h == NULL && local_got_offsets)
+	    tls_type
+	      = local_got_tls_type (input_bfd,
+				    elf_tdata (input_bfd)->symtab_hdr.sh_info,
+				    r_symndx);
+	  else if (h != NULL)
+	    {
+	      tls_type = elf_i386_hash_entry(h)->tls_type;
+	      if (!info->shared && h->dynindx == -1 && tls_type == GOT_TLS_IE)
+		r_type = R_386_TLS_LE_32;
+	    }
+	  if (r_type == R_386_TLS_GD && tls_type == GOT_TLS_IE)
+	    r_type = R_386_TLS_IE_32;
+
+	  if (r_type == R_386_TLS_LE_32)
+	    {
+	      BFD_ASSERT (unresolved_reloc == false);
+	      if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GD)
+		{
+		  unsigned int val;
+
+		  /* GD->LE transition:
+		     Ensure it is:
+		     leal foo(%reg), %eax; call ___tls_get_addr.
+		     We change it into:
+		     movl %gs:0, %eax; subl $foo@tpoff, %eax.  */
+		  BFD_ASSERT (rel->r_offset >= 2);
+		  BFD_ASSERT (bfd_get_8 (input_bfd,
+					 contents + rel->r_offset - 2)
+			      == 0x8d);
+		  val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
+		  BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
+		  BFD_ASSERT (rel->r_offset + 9 <= input_section->_raw_size);
+		  BFD_ASSERT (bfd_get_8 (input_bfd,
+					 contents + rel->r_offset + 4)
+			      == 0xe8);
+		  BFD_ASSERT (rel + 1 < relend);
+		  BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
+		  memcpy (contents + rel->r_offset - 2,
+			  "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
+		  bfd_put_32 (output_bfd, tpoff (output_bfd, relocation),
+			      contents + rel->r_offset + 5);
+		  /* Skip R_386_PLT32.  */
+		  rel++;
+		  continue;
+		}
+	      else
+		{
+		  unsigned int val, type;
+
+		  /* IE->LE transition:
+		     Originally it can be either:
+		     subl foo(%reg1), %reg2
+		     or
+		     movl foo(%reg1), %reg2
+		     We change it into:
+		     subl $foo, %reg2
+		     or
+		     movl $foo, %reg2; nop  */
+		  BFD_ASSERT (rel->r_offset >= 2);
+		  type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
+		  val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
+		  BFD_ASSERT (rel->r_offset + 4 <= input_section->_raw_size);
+		  if (type == 0x8b)
+		    {
+		      /* movl */
+		      BFD_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4);
+		      bfd_put_8 (output_bfd, 0xb8 | ((val >> 3) & 7),
+				 contents + rel->r_offset - 2);
+		      bfd_put_32 (output_bfd, tpoff (output_bfd, relocation),
+				  contents + rel->r_offset - 1);
+		      bfd_put_8 (output_bfd, 0x90,
+				 contents + rel->r_offset + 3);
+		    }
+		  else if (type == 0x2b)
+		    {
+		      /* subl */
+		      BFD_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4);
+		      bfd_put_8 (output_bfd, 0x81,
+				 contents + rel->r_offset - 2);
+		      bfd_put_8 (output_bfd, 0xe8 | ((val >> 3) & 7),
+				 contents + rel->r_offset - 1);
+		      bfd_put_32 (output_bfd, tpoff (output_bfd, relocation),
+				  contents + rel->r_offset);
+		    }
+		  else
+		    BFD_FAIL ();
+		  continue;
+		}
+	    }
+
+	  if (htab->sgot == NULL)
+	    abort ();
+
+	  if (h != NULL)
+	    off = h->got.offset;
+	  else
+	    {
+	      if (local_got_offsets == NULL)
+		abort ();
+
+	      off = local_got_offsets[r_symndx];
+	    }
+
+	  if ((off & 1) != 0)
+	    off &= ~1;
+          else
+	    {
+	      Elf_Internal_Rel outrel;
+	      Elf32_External_Rel *loc;
+	      int dr_type, indx;
+
+	      if (htab->srelgot == NULL)
+		abort ();
+
+	      outrel.r_offset = (htab->sgot->output_section->vma
+				 + htab->sgot->output_offset + off);
+
+	      bfd_put_32 (output_bfd, 0,
+			  htab->sgot->contents + off);
+	      indx = h && h->dynindx != -1 ? h->dynindx : 0;
+	      if (r_type == R_386_TLS_GD)
+		dr_type = R_386_TLS_DTPMOD32;
+	      else
+		dr_type = R_386_TLS_TPOFF32;
+	      outrel.r_info = ELF32_R_INFO (indx, dr_type);
+	      loc = (Elf32_External_Rel *) htab->srelgot->contents;
+	      loc += htab->srelgot->reloc_count++;
+	      bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+
+	      if (r_type == R_386_TLS_GD)
+		{
+		  if (indx == 0)
+		    {
+	    	      BFD_ASSERT (unresolved_reloc == false);
+		      bfd_put_32 (output_bfd,
+				  relocation - dtpoff_base (output_bfd),
+				  htab->sgot->contents + off + 4);
+		    }
+		  else
+		    {
+		      bfd_put_32 (output_bfd, 0,
+				  htab->sgot->contents + off + 4);
+		      outrel.r_info = ELF32_R_INFO (indx,
+						    R_386_TLS_DTPOFF32);
+		      outrel.r_offset += 4;
+		      htab->srelgot->reloc_count++;
+		      loc++;
+		      bfd_elf32_swap_reloc_out (output_bfd, &outrel,
+						loc);
+		    }
+		}
+
+	      if (h != NULL)
+		h->got.offset |= 1;
+	      else
+		local_got_offsets[r_symndx] |= 1;
+	    }
+
+	  if (off >= (bfd_vma) -2)
+	    abort ();
+	  if (r_type == ELF32_R_TYPE (rel->r_info))
+	    {
+	      relocation = htab->sgot->output_offset + off;
+	      unresolved_reloc = false;
+	    }
+	  else
+	    {
+	      unsigned int val;
+
+	      /* GD->IE transition:
+		 Ensure it is:
+		 leal foo(%reg), %eax; call ___tls_get_addr; nop.
+		 We change it into:
+		 movl %gs:0, %eax; subl foo@gottpoff(%reg), %eax.  */
+	      BFD_ASSERT (rel->r_offset >= 2);
+	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2)
+			  == 0x8d);
+	      val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
+	      BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
+	      BFD_ASSERT (rel->r_offset + 10 <= input_section->_raw_size);
+	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4)
+			  == 0xe8);
+	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 9)
+			  == 0x90);
+	      BFD_ASSERT (rel + 1 < relend);
+	      BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
+	      memcpy (contents + rel->r_offset - 2,
+		      "\x65\xa1\0\0\0\0\x2b\x80\0\0\0", 12);
+	      contents[rel->r_offset + 5] = 0x80 | (val & 7);
+	      bfd_put_32 (output_bfd, htab->sgot->output_offset + off,
+			  contents + rel->r_offset + 6);
+	      /* Skip R_386_PLT32.  */
+	      rel++;
+	      continue;
+	    }
+	  break;
+
+	case R_386_TLS_LDM:
+	  if (! info->shared)
+	    {
+	      unsigned int val;
+
+	      /* LD->LE transition:
+		 Ensure it is:
+		 leal foo(%reg), %eax; call ___tls_get_addr.
+		 We change it into:
+		 movl %gs:0, %eax; nop; leal 0(%esi,1), %esi.  */
+	      BFD_ASSERT (rel->r_offset >= 2);
+	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2)
+			  == 0x8d);
+	      val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
+	      BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
+	      BFD_ASSERT (rel->r_offset + 9 <= input_section->_raw_size);
+	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4)
+			  == 0xe8);
+	      BFD_ASSERT (rel + 1 < relend);
+	      BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
+	      memcpy (contents + rel->r_offset - 2,
+		      "\x65\xa1\0\0\0\0\x90\x8d\x74\x26", 11);
+	      /* Skip R_386_PLT32.  */
+	      rel++;
+	      continue;
+	    }
+
+	  if (htab->sgot == NULL)
+	    abort ();
+
+	  off = htab->tls_ldm_got.offset;
+	  if (off & 1)
+	    off &= ~1;
+	  else
+	    {
+	      Elf_Internal_Rel outrel;
+	      Elf32_External_Rel *loc;
+
+	      if (htab->srelgot == NULL)
+		abort ();
+
+	      outrel.r_offset = (htab->sgot->output_section->vma
+				 + htab->sgot->output_offset + off);
+
+	      bfd_put_32 (output_bfd, 0,
+			  htab->sgot->contents + off);
+	      bfd_put_32 (output_bfd, 0,
+			  htab->sgot->contents + off + 4);
+	      outrel.r_info = ELF32_R_INFO (0, R_386_TLS_DTPMOD32);
+	      loc = (Elf32_External_Rel *) htab->srelgot->contents;
+	      loc += htab->srelgot->reloc_count++;
+	      bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+	      htab->tls_ldm_got.offset |= 1;
+	    }
+	  relocation = htab->sgot->output_offset + off;
+	  unresolved_reloc = false;
+	  break;
+
+	case R_386_TLS_LDO_32:
+	  if (info->shared)
+	    relocation -= dtpoff_base (output_bfd);
+	  else
+	    /* When converting LDO to LE, we must negate.  */
+	    relocation = -tpoff (output_bfd, relocation);
+	  break;
+
+	case R_386_TLS_LE_32:
+	  relocation = tpoff (output_bfd, relocation);
+	  break;
+
 	default:
 	  break;
 	}
@@ -2206,7 +2750,9 @@ elf_i386_finish_dynamic_symbol (output_b
 	}
     }
 
-  if (h->got.offset != (bfd_vma) -1)
+  if (h->got.offset != (bfd_vma) -1
+      && elf_i386_hash_entry(h)->tls_type != GOT_TLS_GD
+      && elf_i386_hash_entry(h)->tls_type != GOT_TLS_IE)
     {
       Elf_Internal_Rel rel;
       Elf32_External_Rel *loc;
--- bfd/reloc.c.jj	Sat Dec 22 00:50:46 2001
+++ bfd/reloc.c	Mon Jan 28 16:02:38 2002
@@ -2071,6 +2071,22 @@ ENUMX
   BFD_RELOC_386_GOTOFF
 ENUMX
   BFD_RELOC_386_GOTPC
+ENUMX
+  BFD_RELOC_386_TLS_GD
+ENUMX
+  BFD_RELOC_386_TLS_LDM
+ENUMX
+  BFD_RELOC_386_TLS_LDO_32
+ENUMX
+  BFD_RELOC_386_TLS_IE_32
+ENUMX
+  BFD_RELOC_386_TLS_LE_32
+ENUMX
+  BFD_RELOC_386_TLS_DTPMOD32
+ENUMX
+  BFD_RELOC_386_TLS_DTPOFF32
+ENUMX
+  BFD_RELOC_386_TLS_TPOFF32
 ENUMDOC
   i386/elf relocations
 
@@ -3086,17 +3102,43 @@ ENUMX
 ENUMX
   BFD_RELOC_IA64_COPY
 ENUMX
+  BFD_RELOC_IA64_LTOFF22X
+ENUMX
+  BFD_RELOC_IA64_LDXMOV
+ENUMX
+  BFD_RELOC_IA64_TPREL14
+ENUMX
   BFD_RELOC_IA64_TPREL22
 ENUMX
+  BFD_RELOC_IA64_TPREL64I
+ENUMX
   BFD_RELOC_IA64_TPREL64MSB
 ENUMX
   BFD_RELOC_IA64_TPREL64LSB
 ENUMX
-  BFD_RELOC_IA64_LTOFF_TP22
+  BFD_RELOC_IA64_LTOFF_TPREL22
 ENUMX
-  BFD_RELOC_IA64_LTOFF22X
+  BFD_RELOC_IA64_DTPMOD64MSB
 ENUMX
-  BFD_RELOC_IA64_LDXMOV
+  BFD_RELOC_IA64_DTPMOD64LSB
+ENUMX
+  BFD_RELOC_IA64_LTOFF_DTPMOD22
+ENUMX
+  BFD_RELOC_IA64_DTPREL14
+ENUMX
+  BFD_RELOC_IA64_DTPREL22
+ENUMX
+  BFD_RELOC_IA64_DTPREL64I
+ENUMX
+  BFD_RELOC_IA64_DTPREL32MSB
+ENUMX
+  BFD_RELOC_IA64_DTPREL32LSB
+ENUMX
+  BFD_RELOC_IA64_DTPREL64MSB
+ENUMX
+  BFD_RELOC_IA64_DTPREL64LSB
+ENUMX
+  BFD_RELOC_IA64_LTOFF_DTPREL22
 ENUMDOC
   Intel IA64 Relocations.
 
--- bfd/libbfd.h.jj	Mon Jan 14 17:50:28 2002
+++ bfd/libbfd.h	Mon Jan 28 16:02:38 2002
@@ -760,6 +760,14 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_386_RELATIVE",
   "BFD_RELOC_386_GOTOFF",
   "BFD_RELOC_386_GOTPC",
+  "BFD_RELOC_386_TLS_GD",
+  "BFD_RELOC_386_TLS_LDM",
+  "BFD_RELOC_386_TLS_LDO_32",
+  "BFD_RELOC_386_TLS_IE_32",
+  "BFD_RELOC_386_TLS_LE_32",
+  "BFD_RELOC_386_TLS_DTPMOD32",
+  "BFD_RELOC_386_TLS_DTPOFF32",
+  "BFD_RELOC_386_TLS_TPOFF32",
   "BFD_RELOC_X86_64_GOT32",
   "BFD_RELOC_X86_64_PLT32",
   "BFD_RELOC_X86_64_COPY",
@@ -1082,12 +1090,25 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_IA64_IPLTMSB",
   "BFD_RELOC_IA64_IPLTLSB",
   "BFD_RELOC_IA64_COPY",
+  "BFD_RELOC_IA64_LTOFF22X",
+  "BFD_RELOC_IA64_LDXMOV",
+  "BFD_RELOC_IA64_TPREL14",
   "BFD_RELOC_IA64_TPREL22",
+  "BFD_RELOC_IA64_TPREL64I",
   "BFD_RELOC_IA64_TPREL64MSB",
   "BFD_RELOC_IA64_TPREL64LSB",
-  "BFD_RELOC_IA64_LTOFF_TP22",
-  "BFD_RELOC_IA64_LTOFF22X",
-  "BFD_RELOC_IA64_LDXMOV",
+  "BFD_RELOC_IA64_LTOFF_TPREL22",
+  "BFD_RELOC_IA64_DTPMOD64MSB",
+  "BFD_RELOC_IA64_DTPMOD64LSB",
+  "BFD_RELOC_IA64_LTOFF_DTPMOD22",
+  "BFD_RELOC_IA64_DTPREL14",
+  "BFD_RELOC_IA64_DTPREL22",
+  "BFD_RELOC_IA64_DTPREL64I",
+  "BFD_RELOC_IA64_DTPREL32MSB",
+  "BFD_RELOC_IA64_DTPREL32LSB",
+  "BFD_RELOC_IA64_DTPREL64MSB",
+  "BFD_RELOC_IA64_DTPREL64LSB",
+  "BFD_RELOC_IA64_LTOFF_DTPREL22",
   "BFD_RELOC_M68HC11_HI8",
   "BFD_RELOC_M68HC11_LO8",
   "BFD_RELOC_M68HC11_3B",
--- bfd/elfxx-ia64.c.jj	Mon Jan 28 14:42:26 2002
+++ bfd/elfxx-ia64.c	Mon Jan 28 18:26:37 2002
@@ -79,6 +79,9 @@ struct elfNN_ia64_dyn_sym_info
   bfd_vma pltoff_offset;
   bfd_vma plt_offset;
   bfd_vma plt2_offset;
+  bfd_vma tprel_offset;
+  bfd_vma dtpmod_offset;
+  bfd_vma dtprel_offset;
 
   /* The symbol table entry, if any, that this was derrived from.  */
   struct elf_link_hash_entry *h;
@@ -97,6 +100,9 @@ struct elfNN_ia64_dyn_sym_info
   unsigned got_done : 1;
   unsigned fptr_done : 1;
   unsigned pltoff_done : 1;
+  unsigned tprel_done : 1;
+  unsigned dtpmod_done : 1;
+  unsigned dtprel_done : 1;
 
   /* True for the different kinds of linker data we want created.  */
   unsigned want_got : 1;
@@ -105,6 +111,9 @@ struct elfNN_ia64_dyn_sym_info
   unsigned want_plt : 1;
   unsigned want_plt2 : 1;
   unsigned want_pltoff : 1;
+  unsigned want_tprel : 1;
+  unsigned want_dtpmod : 1;
+  unsigned want_dtprel : 1;
 };
 
 struct elfNN_ia64_local_hash_entry
@@ -285,6 +294,10 @@ static bfd_vma set_pltoff_entry
   PARAMS ((bfd *abfd, struct bfd_link_info *info,
 	   struct elfNN_ia64_dyn_sym_info *dyn_i,
 	   bfd_vma value, boolean));
+static bfd_vma elfNN_ia64_tprel_base
+  PARAMS ((bfd *abfd));
+static bfd_vma elfNN_ia64_dtprel_base
+  PARAMS ((bfd *abfd));
 static int elfNN_ia64_unwind_entry_compare
   PARAMS ((const PTR, const PTR));
 static boolean elfNN_ia64_final_link
@@ -423,10 +436,25 @@ static reloc_howto_type ia64_howto_table
     IA64_HOWTO (R_IA64_LTOFF22X,    "LTOFF22X",	   0, false, true),
     IA64_HOWTO (R_IA64_LDXMOV,	    "LDXMOV",	   0, false, true),
 
+    IA64_HOWTO (R_IA64_TPREL14,	    "TPREL14",	   0, false, false),
     IA64_HOWTO (R_IA64_TPREL22,	    "TPREL22",	   0, false, false),
+    IA64_HOWTO (R_IA64_TPREL64I,    "TPREL64I",	   0, false, false),
     IA64_HOWTO (R_IA64_TPREL64MSB,  "TPREL64MSB",  8, false, false),
     IA64_HOWTO (R_IA64_TPREL64LSB,  "TPREL64LSB",  8, false, false),
-    IA64_HOWTO (R_IA64_LTOFF_TP22,  "LTOFF_TP22",  0, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_TPREL22, "LTOFF_TPREL22",  0, false, false),
+
+    IA64_HOWTO (R_IA64_DTPMOD64MSB, "TPREL64MSB",  8, false, false),
+    IA64_HOWTO (R_IA64_DTPMOD64LSB, "TPREL64LSB",  8, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_DTPMOD22, "LTOFF_DTPMOD22", 0, false, false),
+
+    IA64_HOWTO (R_IA64_DTPREL14,    "DTPREL14",	   0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL22,    "DTPREL22",	   0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64I,   "DTPREL64I",   0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL32MSB, "DTPREL32MSB", 4, false, false),
+    IA64_HOWTO (R_IA64_DTPREL32LSB, "DTPREL32LSB", 4, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64MSB, "DTPREL64MSB", 8, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64LSB, "DTPREL64LSB", 8, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_DTPREL22, "LTOFF_DTPREL22", 0, false, false),
   };
 
 static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1];
@@ -541,10 +569,25 @@ elfNN_ia64_reloc_type_lookup (abfd, bfd_
     case BFD_RELOC_IA64_LTOFF22X:	rtype = R_IA64_LTOFF22X; break;
     case BFD_RELOC_IA64_LDXMOV:		rtype = R_IA64_LDXMOV; break;
 
+    case BFD_RELOC_IA64_TPREL14:	rtype = R_IA64_TPREL14; break;
     case BFD_RELOC_IA64_TPREL22:	rtype = R_IA64_TPREL22; break;
+    case BFD_RELOC_IA64_TPREL64I:	rtype = R_IA64_TPREL64I; break;
     case BFD_RELOC_IA64_TPREL64MSB:	rtype = R_IA64_TPREL64MSB; break;
     case BFD_RELOC_IA64_TPREL64LSB:	rtype = R_IA64_TPREL64LSB; break;
-    case BFD_RELOC_IA64_LTOFF_TP22:	rtype = R_IA64_LTOFF_TP22; break;
+    case BFD_RELOC_IA64_LTOFF_TPREL22:	rtype = R_IA64_LTOFF_TPREL22; break;
+
+    case BFD_RELOC_IA64_DTPMOD64MSB:	rtype = R_IA64_DTPMOD64MSB; break;
+    case BFD_RELOC_IA64_DTPMOD64LSB:	rtype = R_IA64_DTPMOD64LSB; break;
+    case BFD_RELOC_IA64_LTOFF_DTPMOD22:	rtype = R_IA64_LTOFF_DTPMOD22; break;
+
+    case BFD_RELOC_IA64_DTPREL14:	rtype = R_IA64_DTPREL14; break;
+    case BFD_RELOC_IA64_DTPREL22:	rtype = R_IA64_DTPREL22; break;
+    case BFD_RELOC_IA64_DTPREL64I:	rtype = R_IA64_DTPREL64I; break;
+    case BFD_RELOC_IA64_DTPREL32MSB:	rtype = R_IA64_DTPREL32MSB; break;
+    case BFD_RELOC_IA64_DTPREL32LSB:	rtype = R_IA64_DTPREL32LSB; break;
+    case BFD_RELOC_IA64_DTPREL64MSB:	rtype = R_IA64_DTPREL64MSB; break;
+    case BFD_RELOC_IA64_DTPREL64LSB:	rtype = R_IA64_DTPREL64LSB; break;
+    case BFD_RELOC_IA64_LTOFF_DTPREL22:	rtype = R_IA64_LTOFF_DTPREL22; break;
 
     default: return 0;
     }
@@ -2103,6 +2146,9 @@ elfNN_ia64_check_relocs (abfd, info, sec
 	NEED_FULL_PLT = 16,
 	NEED_DYNREL = 32,
 	NEED_LTOFF_FPTR = 64,
+	NEED_TPREL = 128,
+	NEED_DTPMOD = 256,
+	NEED_DTPREL = 512
       };
 
       struct elf_link_hash_entry *h = NULL;
@@ -2140,11 +2186,42 @@ elfNN_ia64_check_relocs (abfd, info, sec
       need_entry = 0;
       switch (ELFNN_R_TYPE (rel->r_info))
 	{
-	case R_IA64_TPREL22:
 	case R_IA64_TPREL64MSB:
 	case R_IA64_TPREL64LSB:
-	case R_IA64_LTOFF_TP22:
-	  return false;
+	  if (info->shared || maybe_dynamic)
+	    need_entry = NEED_DYNREL;
+	  dynrel_type = R_IA64_TPREL64LSB;
+	  if (info->shared)
+	    info->flags |= DF_STATIC_TLS;
+	  break;
+
+	case R_IA64_LTOFF_TPREL22:
+	  need_entry = NEED_TPREL;
+	  if (info->shared)
+	    info->flags |= DF_STATIC_TLS;
+	  break;
+
+	case R_IA64_DTPREL64MSB:
+	case R_IA64_DTPREL64LSB:
+	  if (info->shared || maybe_dynamic)
+	    need_entry = NEED_DYNREL;
+	  dynrel_type = R_IA64_DTPREL64LSB;
+	  break;
+
+	case R_IA64_LTOFF_DTPREL22:
+	  need_entry = NEED_DTPREL;
+	  break;
+
+	case R_IA64_DTPMOD64MSB:
+	case R_IA64_DTPMOD64LSB:
+	  if (info->shared || maybe_dynamic)
+	    need_entry = NEED_DYNREL;
+	  dynrel_type = R_IA64_DTPMOD64LSB;
+	  break;
+
+	case R_IA64_LTOFF_DTPMOD22:
+	  need_entry = NEED_DTPMOD;
+	  break;
 
 	case R_IA64_LTOFF_FPTR22:
 	case R_IA64_LTOFF_FPTR64I:
@@ -2254,7 +2331,7 @@ elfNN_ia64_check_relocs (abfd, info, sec
       dyn_i->h = h;
 
       /* Create what's needed.  */
-      if (need_entry & NEED_GOT)
+      if (need_entry & (NEED_GOT | NEED_TPREL | NEED_DTPMOD | NEED_DTPREL))
 	{
 	  if (!got)
 	    {
@@ -2262,7 +2339,14 @@ elfNN_ia64_check_relocs (abfd, info, sec
 	      if (!got)
 		return false;
 	    }
-	  dyn_i->want_got = 1;
+	  if (need_entry & NEED_GOT)
+	    dyn_i->want_got = 1;
+	  if (need_entry & NEED_TPREL)
+	    dyn_i->want_tprel = 1;
+	  if (need_entry & NEED_DTPMOD)
+	    dyn_i->want_dtpmod = 1;
+	  if (need_entry & NEED_DTPREL)
+	    dyn_i->want_dtprel = 1;
 	}
       if (need_entry & NEED_FPTR)
 	{
@@ -2342,6 +2426,21 @@ allocate_global_data_got (dyn_i, data)
        dyn_i->got_offset = x->ofs;
        x->ofs += 8;
      }
+  if (dyn_i->want_tprel)
+    {
+      dyn_i->tprel_offset = x->ofs;
+      x->ofs += 8;
+    }
+  if (dyn_i->want_dtpmod)
+    {
+      dyn_i->dtpmod_offset = x->ofs;
+      x->ofs += 8;
+    }
+  if (dyn_i->want_dtprel)
+    {
+      dyn_i->dtprel_offset = x->ofs;
+      x->ofs += 8;
+    }
   return true;
 }
 
@@ -2588,6 +2687,10 @@ allocate_dynrel_entries (dyn_i, data)
 	  if (!dynamic_symbol)
 	    count *= 2;
 	  break;
+	case R_IA64_TPREL64LSB:
+	case R_IA64_DTPREL64LSB:
+	case R_IA64_DTPMOD64LSB:
+	  break;
 	default:
 	  abort ();
 	}
@@ -2599,6 +2702,12 @@ allocate_dynrel_entries (dyn_i, data)
   if (((dynamic_symbol || shared) && dyn_i->want_got)
       || (dyn_i->want_ltoff_fptr && dyn_i->h && dyn_i->h->dynindx != -1))
     ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if ((dynamic_symbol || shared) && dyn_i->want_tprel)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if ((dynamic_symbol || shared) && dyn_i->want_dtpmod)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if (dynamic_symbol && dyn_i->want_dtprel)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
 
   if (dyn_i->want_pltoff)
     {
@@ -2909,7 +3018,11 @@ elfNN_ia64_install_value (abfd, hit_addr
 
       /* Instruction relocations.  */
 
-    case R_IA64_IMM14:		opnd = IA64_OPND_IMM14; break;
+    case R_IA64_IMM14:
+    case R_IA64_TPREL14:
+    case R_IA64_DTPREL14:
+      opnd = IA64_OPND_IMM14;
+      break;
 
     case R_IA64_PCREL21F:	opnd = IA64_OPND_TGT25; break;
     case R_IA64_PCREL21M:	opnd = IA64_OPND_TGT25b; break;
@@ -2926,6 +3039,11 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_PLTOFF22:
     case R_IA64_PCREL22:
     case R_IA64_LTOFF_FPTR22:
+    case R_IA64_TPREL22:
+    case R_IA64_DTPREL22:
+    case R_IA64_LTOFF_TPREL22:
+    case R_IA64_LTOFF_DTPMOD22:
+    case R_IA64_LTOFF_DTPREL22:
       opnd = IA64_OPND_IMM22;
       break;
 
@@ -2936,6 +3054,8 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_PCREL64I:
     case R_IA64_FPTR64I:
     case R_IA64_LTOFF_FPTR64I:
+    case R_IA64_TPREL64I:
+    case R_IA64_DTPREL64I:
       opnd = IA64_OPND_IMMU64;
       break;
 
@@ -2949,6 +3069,7 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_SEGREL32MSB:
     case R_IA64_SECREL32MSB:
     case R_IA64_LTV32MSB:
+    case R_IA64_DTPREL32MSB:
       size = 4; bigendian = 1;
       break;
 
@@ -2960,6 +3081,7 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_SEGREL32LSB:
     case R_IA64_SECREL32LSB:
     case R_IA64_LTV32LSB:
+    case R_IA64_DTPREL32LSB:
       size = 4; bigendian = 0;
       break;
 
@@ -2972,6 +3094,9 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_SEGREL64MSB:
     case R_IA64_SECREL64MSB:
     case R_IA64_LTV64MSB:
+    case R_IA64_TPREL64MSB:
+    case R_IA64_DTPMOD64MSB:
+    case R_IA64_DTPREL64MSB:
       size = 8; bigendian = 1;
       break;
 
@@ -2984,6 +3109,9 @@ elfNN_ia64_install_value (abfd, hit_addr
     case R_IA64_SEGREL64LSB:
     case R_IA64_SECREL64LSB:
     case R_IA64_LTV64LSB:
+    case R_IA64_TPREL64LSB:
+    case R_IA64_DTPMOD64LSB:
+    case R_IA64_DTPREL64LSB:
       size = 8; bigendian = 0;
       break;
 
@@ -3138,26 +3266,53 @@ set_got_entry (abfd, info, dyn_i, dynind
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *got_sec;
+  boolean done;
+  bfd_vma got_offset;
 
   ia64_info = elfNN_ia64_hash_table (info);
   got_sec = ia64_info->got_sec;
 
-  BFD_ASSERT ((dyn_i->got_offset & 7) == 0);
-
-  if (! dyn_i->got_done)
+  switch (dyn_r_type)
     {
+    case R_IA64_TPREL64LSB:
+      done = dyn_i->tprel_done;
+      dyn_i->tprel_done = true;
+      got_offset = dyn_i->tprel_offset;
+      break;
+    case R_IA64_DTPMOD64LSB:
+      done = dyn_i->dtpmod_done;
+      dyn_i->dtpmod_done = true;
+      got_offset = dyn_i->dtpmod_offset;
+      break;
+    case R_IA64_DTPREL64LSB:
+      done = dyn_i->dtprel_done;
+      dyn_i->dtprel_done = true;
+      got_offset = dyn_i->dtprel_offset;
+      break;
+    default:
+      done = dyn_i->got_done;
       dyn_i->got_done = true;
+      got_offset = dyn_i->got_offset;
+      break;
+    }
+
+  BFD_ASSERT ((got_offset & 7) == 0);
 
+  if (! done)
+    {
       /* Store the target address in the linkage table entry.  */
-      bfd_put_64 (abfd, value, got_sec->contents + dyn_i->got_offset);
+      bfd_put_64 (abfd, value, got_sec->contents + got_offset);
 
       /* Install a dynamic relocation if needed.  */
-      if (info->shared
+      if ((info->shared && dyn_r_type != R_IA64_DTPREL64LSB)
           || elfNN_ia64_dynamic_symbol_p (dyn_i->h, info)
 	  || elfNN_ia64_aix_vec (abfd->xvec)
 	  || (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB))
 	{
-	  if (dynindx == -1)
+	  if (dynindx == -1
+	      && dyn_r_type != R_IA64_TPREL64LSB
+	      && dyn_r_type != R_IA64_DTPMOD64LSB
+	      && dyn_r_type != R_IA64_DTPREL64LSB)
 	    {
 	      dyn_r_type = R_IA64_REL64LSB;
 	      dynindx = 0;
@@ -3177,6 +3332,15 @@ set_got_entry (abfd, info, dyn_i, dynind
 		case R_IA64_FPTR64LSB:
 		  dyn_r_type = R_IA64_FPTR64MSB;
 		  break;
+		case R_IA64_TPREL64LSB:
+		  dyn_r_type = R_IA64_TPREL64MSB;
+		  break;
+		case R_IA64_DTPMOD64LSB:
+		  dyn_r_type = R_IA64_DTPMOD64MSB;
+		  break;
+		case R_IA64_DTPREL64LSB:
+		  dyn_r_type = R_IA64_DTPREL64MSB;
+		  break;
 		default:
 		  BFD_ASSERT (false);
 		  break;
@@ -3185,7 +3349,7 @@ set_got_entry (abfd, info, dyn_i, dynind
 
 	  elfNN_ia64_install_dyn_reloc (abfd, NULL, got_sec,
 					ia64_info->rel_got_sec,
-					dyn_i->got_offset, dyn_r_type,
+					got_offset, dyn_r_type,
 					dynindx, addend);
 	}
     }
@@ -3193,7 +3357,7 @@ set_got_entry (abfd, info, dyn_i, dynind
   /* Return the address of the linkage table entry.  */
   value = (got_sec->output_section->vma
 	   + got_sec->output_offset
-	   + dyn_i->got_offset);
+	   + got_offset);
 
   return value;
 }
@@ -3291,6 +3455,52 @@ set_pltoff_entry (abfd, info, dyn_i, val
   return value;
 }
 
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @tprel() relocation.
+   Main program TLS (whose template starts at PT_TLS p_vaddr)
+   is assigned offset round(16, PT_TLS p_align).  */
+
+static bfd_vma
+elfNN_ia64_tprel_base (abfd)
+     bfd *abfd;
+{
+  asection *sec;
+  bfd_vma align = 0;
+  bfd_vma base = 0;
+
+  for (sec = abfd->sections; sec; sec = sec->next)
+    if (sec->flags2 & SEC_FLAG2_THREAD_LOCAL)
+      {
+        base = sec->vma;
+        break;
+      }
+  BFD_ASSERT (sec);
+  for (; sec && (sec->flags2 & SEC_FLAG2_THREAD_LOCAL); sec = sec->next)
+    {
+      if (bfd_get_section_alignment (abfd, sec) > align)
+        align = bfd_get_section_alignment (abfd, sec);
+    }
+
+  return base - align_power ((bfd_vma) 16, align);
+}
+
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @dtprel() relocation.
+   This is PT_TLS segment p_vaddr.  */
+
+static bfd_vma
+elfNN_ia64_dtprel_base (abfd)
+     bfd *abfd;
+{
+  asection *sec;
+
+  for (sec = abfd->sections; sec; sec = sec->next)
+    if (sec->flags2 & SEC_FLAG2_THREAD_LOCAL)
+      return sec->vma;
+  BFD_FAIL ();
+  return 0;
+}
+
 /* Called through qsort to sort the .IA_64.unwind section during a
    non-relocatable link.  Set elfNN_ia64_unwind_entry_compare_bfd
    to the output bfd so we can do proper endianness frobbing.  */
@@ -4055,6 +4265,55 @@ elfNN_ia64_relocate_section (output_bfd,
 					r_type);
 	  break;
 
+	case R_IA64_TPREL14:
+	case R_IA64_TPREL22:
+	case R_IA64_TPREL64I:
+	  value -= elfNN_ia64_tprel_base (output_bfd);
+	  r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+	  break;
+
+	case R_IA64_DTPREL14:
+	case R_IA64_DTPREL22:
+	case R_IA64_DTPREL64I:
+	  value -= elfNN_ia64_dtprel_base (output_bfd);
+	  r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+	  break;
+
+	case R_IA64_LTOFF_TPREL22:
+	case R_IA64_LTOFF_DTPMOD22:
+	case R_IA64_LTOFF_DTPREL22:
+	  {
+	    int got_r_type;
+
+	    switch (r_type)
+	      {
+	      default:
+	      case R_IA64_LTOFF_TPREL22:
+		if (!dynamic_symbol_p && !info->shared)
+		  value -= elfNN_ia64_tprel_base (output_bfd);
+		got_r_type = R_IA64_TPREL64LSB;
+		break;
+	      case R_IA64_LTOFF_DTPMOD22:
+		if (!dynamic_symbol_p && !info->shared)
+		  value = 1;
+		got_r_type = R_IA64_DTPMOD64LSB;
+		break;
+	      case R_IA64_LTOFF_DTPREL22:
+		if (!dynamic_symbol_p)
+		  value -= elfNN_ia64_dtprel_base (output_bfd);
+		got_r_type = R_IA64_DTPREL64LSB;
+		break;
+	      }
+	    dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, false);
+	    value = set_got_entry (input_bfd, info, dyn_i,
+				   (h ? h->dynindx : -1), rel->r_addend,
+				   value, got_r_type);
+	    value -= gp_val;
+	    r = elfNN_ia64_install_value (output_bfd, hit_addr, value,
+					  r_type);
+	  }
+	  break;
+
 	default:
 	  r = bfd_reloc_notsupported;
 	  break;
--- bfd/section.c.jj	Mon Jan 14 17:02:48 2002
+++ bfd/section.c	Mon Jan 28 16:02:38 2002
@@ -367,6 +367,11 @@ CODE_FRAGMENT
 .
 .  {*  End of section flags.  *}
 .
+.  flagword flags2;
+.
+.  {* The section contains thread local data.  *}
+.#define SEC_FLAG2_THREAD_LOCAL	1
+.
 .  {* Some internal packed boolean fields.  *}
 .
 .  {* See the vma field.  *}
@@ -614,14 +619,14 @@ static const asymbol global_syms[] =
 #define STD_SECTION(SEC, FLAGS, SYM, NAME, IDX)				\
   const asymbol * const SYM = (asymbol *) &global_syms[IDX]; 		\
   const asection SEC = 							\
-    /* name, id,  index, next, flags, user_set_vma, reloc_done,      */	\
-    { NAME,  IDX, 0,     NULL, FLAGS, 0,            0,			\
+    /* name, id,  index, next, flags, flags2, user_set_vma, 	     */	\
+    { NAME,  IDX, 0,     NULL, FLAGS, 0,      0,			\
 									\
-    /* linker_mark, linker_has_input, gc_mark, segment_mark,         */	\
-       0,           0,                1,       0,			\
+    /* reloc_done, linker_mark, linker_has_input, gc_mark,	     */	\
+       0,          0,           0,                1,			\
 									\
-    /* vma, lma, _cooked_size, _raw_size,                            */	\
-       0,   0,   0,            0,					\
+    /* segment_mark, vma, lma, _cooked_size, _raw_size,              */	\
+       0,            0,   0,   0,            0,				\
 									\
     /* output_offset, output_section,      alignment_power,          */	\
        0,             (struct sec *) &SEC, 0,				\
--- bfd/elflink.h.jj	Mon Jan 28 14:42:25 2002
+++ bfd/elflink.h	Sat Feb  9 22:13:48 2002
@@ -1725,7 +1725,23 @@ elf_link_add_object_symbols (abfd, info)
       if (name == (const char *) NULL)
 	goto error_return;
 
-      if (add_symbol_hook)
+      if (sym.st_shndx == SHN_COMMON && ELF_ST_TYPE (sym.st_info) == STT_TLS)
+	{
+	  asection *tcomm = bfd_get_section_by_name (abfd, ".tcommon");
+
+	  if (tcomm == NULL)
+	    {
+	      tcomm = bfd_make_section (abfd, ".tcommon");
+	      if (tcomm == NULL
+		  || !bfd_set_section_flags (abfd, tcomm, (SEC_ALLOC
+							   | SEC_IS_COMMON
+							   | SEC_LINKER_CREATED)))
+		goto error_return;
+	      tcomm->flags2 |= SEC_FLAG2_THREAD_LOCAL;
+	    }
+	  sec = tcomm;
+	}
+      else if (add_symbol_hook)
 	{
 	  if (! (*add_symbol_hook) (abfd, info, &sym, &name, &flags, &sec,
 				    &value))
@@ -3413,7 +3429,7 @@ NAME(bfd_elf,size_dynamic_sections) (out
 	  elf_tdata (output_bfd)->cverdefs = cdefs;
 	}
 
-      if (info->new_dtags && info->flags)
+      if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS))
 	{
 	  if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS, info->flags))
 	    return false;
@@ -4414,6 +4430,8 @@ struct elf_final_link_info
   asection *hash_sec;
   /* symbol version section (.gnu.version).  */
   asection *symver_sec;
+  /* first SHF_TLS section (if any).  */
+  asection *first_tls_sec;
   /* Buffer large enough to hold contents of any section.  */
   bfd_byte *contents;
   /* Buffer large enough to hold external relocs of any section.  */
@@ -4893,6 +4911,14 @@ elf_bfd_final_link (abfd, info)
   finfo.symbuf = NULL;
   finfo.symshndxbuf = NULL;
   finfo.symbuf_count = 0;
+  finfo.first_tls_sec = NULL;
+  for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+    if ((o->flags2 & SEC_FLAG2_THREAD_LOCAL) != 0
+	&& (o->flags & SEC_LOAD) != 0)
+      {
+	finfo.first_tls_sec = o;
+	break;
+      }
 
   /* Count up the number of relocations we will output for each output
      section, so that we know the sizes of the reloc sections.  We
@@ -5990,7 +6016,16 @@ elf_link_output_extsym (h, data)
 	       addresses.  */
 	    sym.st_value = h->root.u.def.value + input_sec->output_offset;
 	    if (! finfo->info->relocateable)
-	      sym.st_value += input_sec->output_section->vma;
+	      {
+		sym.st_value += input_sec->output_section->vma;
+		if (h->type == STT_TLS)
+		  {
+		    /* STT_TLS symbols are relative to PT_TLS segment
+		       base.  */
+		    BFD_ASSERT (finfo->first_tls_sec != NULL);
+		    sym.st_value -= finfo->first_tls_sec->vma;
+		  }
+	      }
 	  }
 	else
 	  {
@@ -6451,7 +6486,15 @@ elf_link_input_bfd (finfo, input_bfd)
 	 these requirements.  */
       osym.st_value += isec->output_offset;
       if (! finfo->info->relocateable)
-	osym.st_value += isec->output_section->vma;
+	{
+	  osym.st_value += isec->output_section->vma;
+	  if (ELF_ST_TYPE (osym.st_info) == STT_TLS)
+	    {
+	      /* STT_TLS symbols are relative to PT_TLS segment base.  */
+	      BFD_ASSERT (finfo->first_tls_sec != NULL);
+	      osym.st_value -= finfo->first_tls_sec->vma;
+	    }
+	}
 
       if (! elf_link_output_sym (finfo, name, &osym, isec))
 	return false;
@@ -6776,7 +6819,16 @@ elf_link_input_bfd (finfo, input_bfd)
 
 			  isym->st_value += sec->output_offset;
 			  if (! finfo->info->relocateable)
-			    isym->st_value += osec->vma;
+			    {
+			      isym->st_value += osec->vma;
+			      if (ELF_ST_TYPE (isym->st_info) == STT_TLS)
+				{
+				  /* STT_TLS symbols are relative to PT_TLS
+				     segment base.  */
+				  BFD_ASSERT (finfo->first_tls_sec != NULL);
+				  isym->st_value -= finfo->first_tls_sec->vma;
+				}
+			    }
 
 			  finfo->indices[r_symndx]
 			    = bfd_get_symcount (output_bfd);
--- bfd/syms.c.jj	Mon Jan 14 17:02:52 2002
+++ bfd/syms.c	Wed Feb  6 19:03:40 2002
@@ -288,6 +288,9 @@ CODE_FRAGMENT
 .          as well.  *}
 .#define BSF_DEBUGGING_RELOC 0x20000
 .
+.       {* This symbol is thread local.  Used in ELF.  *}
+.#define BSF_THREAD_LOCAL  0x40000
+.
 .  flagword flags;
 .
 .	{* A pointer to the section to which this symbol is
--- gas/config/obj-elf.c.jj	Mon Jan 28 15:56:03 2002
+++ gas/config/obj-elf.c	Wed Feb  6 20:01:05 2002
@@ -61,6 +61,7 @@ static void adjust_stab_sections PARAMS 
 static void build_group_lists PARAMS ((bfd *, asection *, PTR));
 static int elf_separate_stab_sections PARAMS ((void));
 static void elf_init_stab_section PARAMS ((segT));
+static symbolS *elf_common PARAMS ((int));
 
 #ifdef NEED_ECOFF_DEBUG
 static boolean elf_get_extr PARAMS ((asymbol *, EXTR *));
@@ -84,6 +85,7 @@ static int obj_elf_section_type PARAMS (
 static void obj_elf_symver PARAMS ((int));
 static void obj_elf_subsection PARAMS ((int));
 static void obj_elf_popsection PARAMS ((int));
+static void obj_elf_tls_common PARAMS ((int));
 
 static const pseudo_typeS elf_pseudo_table[] =
 {
@@ -130,6 +132,8 @@ static const pseudo_typeS elf_pseudo_tab
   {"data", obj_elf_data, 0},
   {"text", obj_elf_text, 0},
 
+  {"tls_common", obj_elf_tls_common, 0},
+
   /* End sentinel.  */
   {NULL, NULL, 0},
 };
@@ -280,8 +284,8 @@ elf_file_symbol (s)
 #endif
 }
 
-void
-obj_elf_common (is_common)
+static symbolS *
+elf_common (is_common)
      int is_common;
 {
   char *name;
@@ -294,7 +298,7 @@ obj_elf_common (is_common)
   if (flag_mri && is_common)
     {
       s_mri_common (0);
-      return;
+      return NULL;
     }
 
   name = input_line_pointer;
@@ -307,14 +311,14 @@ obj_elf_common (is_common)
     {
       as_bad (_("expected comma after symbol-name"));
       ignore_rest_of_line ();
-      return;
+      return NULL;
     }
   input_line_pointer++;		/* skip ',' */
   if ((temp = get_absolute_expression ()) < 0)
     {
       as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
       ignore_rest_of_line ();
-      return;
+      return NULL;
     }
   size = temp;
   *p = 0;
@@ -324,7 +328,7 @@ obj_elf_common (is_common)
     {
       as_bad (_("symbol `%s' is already defined"), S_GET_NAME (symbolP));
       ignore_rest_of_line ();
-      return;
+      return NULL;
     }
   if (S_GET_VALUE (symbolP) != 0)
     {
@@ -374,7 +378,7 @@ obj_elf_common (is_common)
 		{
 		  as_bad (_("common alignment not a power of 2"));
 		  ignore_rest_of_line ();
-		  return;
+		  return NULL;
 		}
 	    }
 	  else
@@ -426,7 +430,7 @@ obj_elf_common (is_common)
   symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
 
   demand_empty_rest_of_line ();
-  return;
+  return symbolP;
 
   {
   bad_common_segment:
@@ -439,10 +443,27 @@ obj_elf_common (is_common)
     *p = c;
     input_line_pointer = p;
     ignore_rest_of_line ();
-    return;
+    return NULL;
   }
 }
 
+void
+obj_elf_common (is_common)
+     int is_common;
+{
+  elf_common (is_common);
+}
+
+static void
+obj_elf_tls_common (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  symbolS *symbolP = elf_common (0);
+
+  if (symbolP)
+    symbol_get_bfdsym (symbolP)->flags |= BSF_THREAD_LOCAL;
+}
+
 static void
 obj_elf_local (ignore)
      int ignore ATTRIBUTE_UNUSED;
@@ -594,6 +615,8 @@ static struct special_section const spec
   { ".note",	SHT_NOTE,	0				},
   { ".rodata",	SHT_PROGBITS,	SHF_ALLOC			},
   { ".rodata1",	SHT_PROGBITS,	SHF_ALLOC			},
+  { ".tbss",	SHT_NOBITS,	SHF_ALLOC + SHF_WRITE + SHF_TLS	},
+  { ".tdata",	SHT_PROGBITS,	SHF_ALLOC + SHF_WRITE + SHF_TLS	},
   { ".text",	SHT_PROGBITS,	SHF_ALLOC + SHF_EXECINSTR	},
 
 #ifdef ELF_TC_SPECIAL_SECTIONS
@@ -630,7 +653,7 @@ obj_elf_change_section (name, type, attr
 {
   asection *old_sec;
   segT sec;
-  flagword flags;
+  flagword flags, flags2;
   int i;
 
 #ifdef md_flush_pending_output
@@ -697,6 +720,7 @@ obj_elf_change_section (name, type, attr
 	   | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0)
 	   | ((attr & SHF_MERGE) ? SEC_MERGE : 0)
 	   | ((attr & SHF_STRINGS) ? SEC_STRINGS : 0));
+  flags2 = ((attr & SHF_TLS) ? SEC_FLAG2_THREAD_LOCAL : 0);
 #ifdef md_elf_section_flags
   flags = md_elf_section_flags (flags, attr, type);
 #endif
@@ -710,6 +734,7 @@ obj_elf_change_section (name, type, attr
         seg_info (sec)->bss = 1;
 
       bfd_set_section_flags (stdoutput, sec, flags);
+      sec->flags2 = flags2;
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
       elf_group_name (sec) = group_name;
@@ -726,9 +751,10 @@ obj_elf_change_section (name, type, attr
       /* If section attributes are specified the second time we see a
 	 particular section, then check that they are the same as we
 	 saw the first time.  */
-      if ((old_sec->flags ^ flags)
-	  & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
-	     | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS))
+      if (((old_sec->flags ^ flags)
+	   & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+	      | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS))
+	  || (old_sec->flags2 ^ flags2) & (SEC_FLAG2_THREAD_LOCAL))
 	as_warn (_("ignoring changed section attributes for %s"), name);
       else if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
 	as_warn (_("ignoring changed section entity size for %s"), name);
@@ -771,6 +797,9 @@ obj_elf_parse_section_letters (str, len)
 	case 'G':
 	  attr |= SHF_GROUP;
 	  break;
+	case 'T':
+	  attr |= SHF_TLS;
+	  break;
 	/* Compatibility.  */
 	case 'm':
 	  if (*(str - 1) == 'a')
@@ -785,7 +814,7 @@ obj_elf_parse_section_letters (str, len)
 	    }
 	default:
 	  {
-	    char *bad_msg = _("unrecognized .section attribute: want a,w,x,M,S,G");
+	    char *bad_msg = _("unrecognized .section attribute: want a,w,x,M,S,G,T");
 #ifdef md_elf_section_letter
 	    int md_attr = md_elf_section_letter (*str, &bad_msg);
 	    if (md_attr >= 0)
--- gas/config/tc-ia64.c.jj	Mon Jan 14 17:03:20 2002
+++ gas/config/tc-ia64.c	Mon Jan 28 16:02:39 2002
@@ -77,6 +77,8 @@ enum special_section
 
 enum reloc_func
   {
+    FUNC_DTP_MODULE,
+    FUNC_DTP_RELATIVE,
     FUNC_FPTR_RELATIVE,
     FUNC_GP_RELATIVE,
     FUNC_LT_RELATIVE,
@@ -84,8 +86,12 @@ enum reloc_func
     FUNC_PLT_RELATIVE,
     FUNC_SEC_RELATIVE,
     FUNC_SEG_RELATIVE,
+    FUNC_TP_RELATIVE,
     FUNC_LTV_RELATIVE,
     FUNC_LT_FPTR_RELATIVE,
+    FUNC_LT_DTP_MODULE,
+    FUNC_LT_DTP_RELATIVE,
+    FUNC_LT_TP_RELATIVE,
     FUNC_IPLT_RELOC,
   };
 
@@ -476,6 +482,8 @@ static struct
 pseudo_func[] =
   {
     /* reloc pseudo functions (these must come first!):  */
+    { "dtpmod",	PSEUDO_FUNC_RELOC, { 0 } },
+    { "dtprel",	PSEUDO_FUNC_RELOC, { 0 } },
     { "fptr",	PSEUDO_FUNC_RELOC, { 0 } },
     { "gprel",	PSEUDO_FUNC_RELOC, { 0 } },
     { "ltoff",	PSEUDO_FUNC_RELOC, { 0 } },
@@ -483,8 +491,12 @@ pseudo_func[] =
     { "pltoff",	PSEUDO_FUNC_RELOC, { 0 } },
     { "secrel",	PSEUDO_FUNC_RELOC, { 0 } },
     { "segrel",	PSEUDO_FUNC_RELOC, { 0 } },
+    { "tprel",	PSEUDO_FUNC_RELOC, { 0 } },
     { "ltv",	PSEUDO_FUNC_RELOC, { 0 } },
     { "", 0, { 0 } },	/* placeholder for FUNC_LT_FPTR_RELATIVE */
+    { "", 0, { 0 } },	/* placeholder for FUNC_LT_DTP_MODULE */
+    { "", 0, { 0 } },	/* placeholder for FUNC_LT_DTP_RELATIVE */
+    { "", 0, { 0 } },	/* placeholder for FUNC_LT_TP_RELATIVE */
     { "iplt",	PSEUDO_FUNC_RELOC, { 0 } },
 
     /* mbtype4 constants:  */
@@ -929,7 +941,7 @@ ia64_elf_section_letter (letter, ptr_msg
   if (letter == 's')
     return SHF_IA_64_SHORT;
 
-  *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S in string");
+  *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
   return 0;
 }
 
@@ -6405,6 +6417,14 @@ md_begin ()
   bfd_set_section_alignment (stdoutput, text_section, 4);
 
   target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+  pseudo_func[FUNC_DTP_MODULE].u.sym =
+    symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
+		&zero_address_frag);
+
+  pseudo_func[FUNC_DTP_RELATIVE].u.sym =
+    symbol_new (".<dtprel>", undefined_section, FUNC_DTP_RELATIVE,
+		&zero_address_frag);
+
   pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
     symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
 		&zero_address_frag);
@@ -6433,6 +6453,10 @@ md_begin ()
     symbol_new (".<segrel>", undefined_section, FUNC_SEG_RELATIVE,
 		&zero_address_frag);
 
+  pseudo_func[FUNC_TP_RELATIVE].u.sym =
+    symbol_new (".<tprel>", undefined_section, FUNC_TP_RELATIVE,
+		&zero_address_frag);
+
   pseudo_func[FUNC_LTV_RELATIVE].u.sym =
     symbol_new (".<ltv>", undefined_section, FUNC_LTV_RELATIVE,
 		&zero_address_frag);
@@ -6441,6 +6465,18 @@ md_begin ()
     symbol_new (".<ltoff.fptr>", undefined_section, FUNC_LT_FPTR_RELATIVE,
 		&zero_address_frag);
 
+  pseudo_func[FUNC_LT_DTP_MODULE].u.sym =
+    symbol_new (".<ltoff.dtpmod>", undefined_section, FUNC_LT_DTP_MODULE,
+		&zero_address_frag);
+
+  pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.dptrel>", undefined_section, FUNC_LT_DTP_RELATIVE,
+		&zero_address_frag);
+
+  pseudo_func[FUNC_LT_TP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.tprel>", undefined_section, FUNC_LT_TP_RELATIVE,
+		&zero_address_frag);
+
   pseudo_func[FUNC_IPLT_RELOC].u.sym =
     symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
 		&zero_address_frag);
@@ -9737,11 +9773,22 @@ md_operand (e)
 		  as_bad ("Not a symbolic expression");
 		  goto err;
 		}
-	      if (S_GET_VALUE (e->X_op_symbol) == FUNC_FPTR_RELATIVE
-		  && i == FUNC_LT_RELATIVE)
-		i = FUNC_LT_FPTR_RELATIVE;
-	      else
+	      if (i != FUNC_LT_RELATIVE)
+		{
+		  as_bad ("Illegal combination of relocation functions");
+		  goto err;
+		}
+	      switch (S_GET_VALUE (e->X_op_symbol))
 		{
+		case FUNC_FPTR_RELATIVE:
+		  i = FUNC_LT_FPTR_RELATIVE; break;
+		case FUNC_DTP_MODULE:
+		  i = FUNC_LT_DTP_MODULE; break;
+		case FUNC_DTP_RELATIVE:
+		  i = FUNC_LT_DTP_RELATIVE; break;
+		case FUNC_TP_RELATIVE:
+		  i = FUNC_LT_TP_RELATIVE; break;
+		default:
 		  as_bad ("Illegal combination of relocation functions");
 		  goto err;
 		}
@@ -10056,6 +10103,64 @@ ia64_gen_real_reloc_type (sym, r_type)
 	default:
 	  break;
 	}
+      break;
+
+    case FUNC_TP_RELATIVE:
+      switch (r_type)
+	{
+	case BFD_RELOC_IA64_IMM14:
+	  new = BFD_RELOC_IA64_TPREL14; break;
+	case BFD_RELOC_IA64_IMM22:
+	  new = BFD_RELOC_IA64_TPREL22; break;
+	case BFD_RELOC_IA64_IMM64:
+	  new = BFD_RELOC_IA64_TPREL64I; break;
+	default:
+	  break;
+	}
+      break;
+
+    case FUNC_LT_TP_RELATIVE:
+      switch (r_type)
+	{
+	case BFD_RELOC_IA64_IMM22:
+	  new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
+	default:
+	  break;
+	}
+      break;
+
+    case FUNC_LT_DTP_MODULE:
+      switch (r_type)
+	{
+	case BFD_RELOC_IA64_IMM22:
+	  new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
+	default:
+	  break;
+	}
+      break;
+
+    case FUNC_DTP_RELATIVE:
+      switch (r_type)
+	{
+	case BFD_RELOC_IA64_IMM14:
+	  new = BFD_RELOC_IA64_DTPREL14; break;
+	case BFD_RELOC_IA64_IMM22:
+	  new = BFD_RELOC_IA64_DTPREL22; break;
+	case BFD_RELOC_IA64_IMM64:
+	  new = BFD_RELOC_IA64_DTPREL64I; break;
+	default:
+	  break;
+	}
+      break;
+
+    case FUNC_LT_DTP_RELATIVE:
+      switch (r_type)
+	{
+	case BFD_RELOC_IA64_IMM22:
+	  new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
+	default:
+	  break;
+	}
       break;
 
     case  FUNC_IPLT_RELOC:
--- gas/config/tc-alpha.c.jj	Sat Dec 22 00:48:31 2001
+++ gas/config/tc-alpha.c	Mon Jan 28 16:02:39 2002
@@ -1508,7 +1508,8 @@ tc_gen_reloc (sec, fixp)
        * of thing, and as a result we need to fake it out here.
        */
       if ((S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)
-	   || (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE))
+	   || (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE)
+	   || (S_GET_SEGMENT (fixp->fx_addsy)->flags2 & SEC_FLAG2_THREAD_LOCAL))
 	  && !S_IS_COMMON (fixp->fx_addsy))
 	reloc->addend -= symbol_get_bfdsym (fixp->fx_addsy)->value;
 #endif
@@ -5407,7 +5408,7 @@ alpha_elf_section_letter (letter, ptr_ms
   if (letter == 's')
     return SHF_ALPHA_GPREL;
 
-  *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S in string");
+  *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
   return 0;
 }
 
--- gas/config/tc-ppc.c.jj	Thu Jan  3 18:31:42 2002
+++ gas/config/tc-ppc.c	Mon Jan 28 16:02:39 2002
@@ -2472,7 +2472,7 @@ ppc_section_letter (letter, ptr_msg)
   if (letter == 'e')
     return SHF_EXCLUDE;
 
-  *ptr_msg = _("Bad .section directive: want a,e,w,x,M,S in string");
+  *ptr_msg = _("Bad .section directive: want a,e,w,x,M,S,G,T in string");
   return 0;
 }
 
--- gas/config/tc-i386.c.jj	Fri Nov 16 21:50:03 2001
+++ gas/config/tc-i386.c	Mon Jan 28 16:02:39 2002
@@ -1181,6 +1181,11 @@ tc_i386_fix_adjustable (fixP)
   if (fixP->fx_r_type == BFD_RELOC_386_GOTOFF
       || fixP->fx_r_type == BFD_RELOC_386_PLT32
       || fixP->fx_r_type == BFD_RELOC_386_GOT32
+      || fixP->fx_r_type == BFD_RELOC_386_TLS_GD
+      || fixP->fx_r_type == BFD_RELOC_386_TLS_LDM
+      || fixP->fx_r_type == BFD_RELOC_386_TLS_LDO_32
+      || fixP->fx_r_type == BFD_RELOC_386_TLS_IE_32
+      || fixP->fx_r_type == BFD_RELOC_386_TLS_LE_32
       || fixP->fx_r_type == BFD_RELOC_X86_64_PLT32
       || fixP->fx_r_type == BFD_RELOC_X86_64_GOT32
       || fixP->fx_r_type == BFD_RELOC_X86_64_GOTPCREL
@@ -1198,6 +1203,11 @@ tc_i386_fix_adjustable (fixP)
 #define BFD_RELOC_386_PLT32		0
 #define BFD_RELOC_386_GOT32		0
 #define BFD_RELOC_386_GOTOFF		0
+#define BFD_RELOC_386_TLS_GD		0
+#define BFD_RELOC_386_TLS_LDM		0
+#define BFD_RELOC_386_TLS_LDO_32	0
+#define BFD_RELOC_386_TLS_IE_32		0
+#define BFD_RELOC_386_TLS_LE_32		0
 #define BFD_RELOC_X86_64_PLT32		0
 #define BFD_RELOC_X86_64_GOT32		0
 #define BFD_RELOC_X86_64_GOTPCREL	0
@@ -3159,10 +3169,15 @@ lex_got (reloc, adjust)
     const char *str;
     const RELOC_ENUM rel[NUM_FLAG_CODE];
   } gotrel[] = {
-    { "PLT",      { BFD_RELOC_386_PLT32,  0, BFD_RELOC_X86_64_PLT32    } },
-    { "GOTOFF",   { BFD_RELOC_386_GOTOFF, 0, 0                         } },
-    { "GOTPCREL", { 0,                    0, BFD_RELOC_X86_64_GOTPCREL } },
-    { "GOT",      { BFD_RELOC_386_GOT32,  0, BFD_RELOC_X86_64_GOT32    } }
+    { "PLT",      { BFD_RELOC_386_PLT32,      0, BFD_RELOC_X86_64_PLT32    } },
+    { "GOTOFF",   { BFD_RELOC_386_GOTOFF,     0, 0                         } },
+    { "GOTPCREL", { 0,                        0, BFD_RELOC_X86_64_GOTPCREL } },
+    { "TLSGD",    { BFD_RELOC_386_TLS_GD,     0, 0                         } },
+    { "TLSLDM",   { BFD_RELOC_386_TLS_LDM,    0, 0                         } },
+    { "GOTTPOFF", { BFD_RELOC_386_TLS_IE_32,  0, 0                         } },
+    { "TPOFF",    { BFD_RELOC_386_TLS_LE_32,  0, 0                         } },
+    { "DTPOFF",   { BFD_RELOC_386_TLS_LDO_32, 0, 0                         } },
+    { "GOT",      { BFD_RELOC_386_GOT32,      0, BFD_RELOC_X86_64_GOT32    } }
   };
   char *cp;
   unsigned int j;
@@ -4345,6 +4360,11 @@ md_apply_fix3 (fixP, valP, seg)
 	value -= 1;
 	break;
       case BFD_RELOC_386_GOT32:
+      case BFD_RELOC_386_TLS_GD:
+      case BFD_RELOC_386_TLS_LDM:
+      case BFD_RELOC_386_TLS_LDO_32:
+      case BFD_RELOC_386_TLS_IE_32:
+      case BFD_RELOC_386_TLS_LE_32:
       case BFD_RELOC_X86_64_GOT32:
 	value = 0; /* Fully resolved at runtime.  No addend.  */
 	break;
@@ -4811,6 +4831,11 @@ tc_gen_reloc (section, fixp)
     case BFD_RELOC_386_GOT32:
     case BFD_RELOC_386_GOTOFF:
     case BFD_RELOC_386_GOTPC:
+    case BFD_RELOC_386_TLS_GD:
+    case BFD_RELOC_386_TLS_LDM:
+    case BFD_RELOC_386_TLS_LDO_32:
+    case BFD_RELOC_386_TLS_IE_32:
+    case BFD_RELOC_386_TLS_LE_32:
     case BFD_RELOC_X86_64_32S:
     case BFD_RELOC_RVA:
     case BFD_RELOC_VTABLE_ENTRY:
--- gas/config/tc-sparc.c.jj	Sat Dec 22 00:50:46 2001
+++ gas/config/tc-sparc.c	Mon Jan 28 16:02:39 2002
@@ -2910,6 +2910,7 @@ md_apply_fix3 (fixP, valP, segment)
 	  && (S_IS_EXTERNAL (sym)
 	      || S_IS_WEAK (sym)
 	      || (seg->flags & SEC_MERGE)
+	      || (seg->flags2 & SEC_FLAG2_THREAD_LOCAL)
 	      || (sparc_pic_code && ! fixP->fx_pcrel)
 	      || (seg != segment
 		  && (((bfd_get_section_flags (stdoutput, seg) & SEC_LINK_ONCE) != 0)
--- gas/testsuite/gas/i386/tlspic.s.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlspic.s	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,27 @@
+	.section ".tdata", "awT", @progbits
+	.globl foo
+foo:	.long 25
+	.text
+	.globl	fn
+	.type	fn,@function
+fn:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+	pushl	%eax
+	call	1f
+1:	popl	%ebx
+	addl	$_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx
+
+	/* foo can be anywhere in the startup TLS  */
+	movl	%gs:0, %eax
+
+	/* Arbitrary instructions in between  */
+	leal	0(%esi, 1), %esi
+
+	subl	foo@GOTTPOFF(%ebx), %eax
+	/* %eax now contains &foo  */
+
+	movl    -4(%ebp), %ebx
+	leave
+	ret
--- gas/testsuite/gas/i386/tlsd.s.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlsd.s	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,43 @@
+	.section ".tdata", "awT", @progbits
+	.globl foo, baz
+	.hidden baz
+foo:	.long 25
+bar:	.long 27
+baz:	.long 29
+	.text
+	.globl	fn
+	.type	fn,@function
+fn:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+	pushl	%eax
+	call	1f
+1:	popl	%ebx
+	addl	$_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx
+
+	/* Dynamic TLS model, foo not known to be in the current object  */
+	leal	foo@TLSGD(%ebx), %eax
+	call	___tls_get_addr@PLT
+	nop
+	/* %eax now contains &foo  */
+
+	/* Dynamic TLS model, bar and baz known to be in the current object  */
+	leal	bar@TLSLDM(%ebx), %eax
+	call	___tls_get_addr@PLT
+
+	/* Just show that there can be arbitrary instructions here  */
+	leal	0(%edi, 1), %edi
+
+	leal	bar@DTPOFF(%eax), %edx
+	/* %edx now contains &bar  */
+
+	/* Again, arbitrary instructions  */
+	leal	0(%esi, 1), %esi
+
+	leal	baz@DTPOFF(%eax), %ecx
+	/* %ecx now contains &baz  */
+
+	movl    -4(%ebp), %ebx
+	leave
+	ret
--- gas/testsuite/gas/i386/tlsnopic.s.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlsnopic.s	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,31 @@
+	.section ".tdata", "awT", @progbits
+	.globl baz
+	.hidden baz
+bar:	.long 27
+baz:	.long 29
+	.text
+	.globl	fn
+	.type	fn,@function
+fn:
+	/* Main binary, no PIC  */
+1:	movl	1b, %edx
+	addl	$_GLOBAL_OFFSET_TABLE_+[.-1b], %edx
+
+	/* foo can be anywhere in startup TLS  */
+	movl	%gs:0, %eax
+	subl	foo@GOTTPOFF(%edx), %eax
+	/* %eax now contains &foo  */
+
+	/* bar only in the main program  */
+	movl	%gs:0, %eax
+	subl	$bar@TPOFF, %eax
+	/* %eax now contains &bar  */
+
+	/* baz only in the main program  */
+	movl	%gs:0, %ecx
+	/* Arbitrary instructions in between  */
+	nop
+	subl	$baz@TPOFF, %ecx
+	/* %ecx now contains &baz  */
+
+	ret
--- gas/testsuite/gas/i386/tlsd.d.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlsd.d	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,35 @@
+#objdump: -dr
+#name: i386 dynamic tls
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+000 <fn>:
+   0:	55 [ 	]*push   %ebp
+   1:	89 e5 [ 	]*mov    %esp,%ebp
+   3:	53 [ 	]*push   %ebx
+   4:	50 [ 	]*push   %eax
+   5:	e8 00 00 00 00 [ 	]*call   a <fn\+0xa>
+   a:	5b [ 	]*pop    %ebx
+   b:	81 c3 03 00 00 00 [ 	]*add    \$0x3,%ebx
+[ 	]+d: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
+  11:	8d 83 00 00 00 00 [ 	]*lea    0x0\(%ebx\),%eax
+[ 	]+13: R_386_TLS_GD	foo
+  17:	e8 fc ff ff ff [ 	]*call   18 <fn\+0x18>
+[ 	]+18: R_386_PLT32	___tls_get_addr
+  1c:	90 [ 	]*nop    
+  1d:	8d 83 00 00 00 00 [ 	]*lea    0x0\(%ebx\),%eax
+[ 	]+1f: R_386_TLS_LDM	bar
+  23:	e8 fc ff ff ff [ 	]*call   24 <fn\+0x24>
+[ 	]+24: R_386_PLT32	___tls_get_addr
+  28:	8d 7f 00 [ 	]*lea    0x0\(%edi\),%edi
+  2b:	8d 90 00 00 00 00 [ 	]*lea    0x0\(%eax\),%edx
+[ 	]+2d: R_386_TLS_LDO_32	bar
+  31:	8d 76 00 [ 	]*lea    0x0\(%esi\),%esi
+  34:	8d 88 00 00 00 00 [ 	]*lea    0x0\(%eax\),%ecx
+[ 	]+36: R_386_TLS_LDO_32	baz
+  3a:	8b 5d fc [ 	]*mov    0xfffffffc\(%ebp\),%ebx
+  3d:	c9 [ 	]*leave[ 	]*
+  3e:	c3 [ 	]*ret[ 	]*
+  3f:	90 [ 	]*nop[ 	]*
--- gas/testsuite/gas/i386/tlsnopic.d.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlsnopic.d	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,24 @@
+#objdump: -dr
+#name: i386 non-pic tls
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+000 <fn>:
+   0:	8b 15 00 00 00 00 [ 	]*mov    0x0,%edx
+[ 	]+2: R_386_32	.text
+   6:	81 c2 08 00 00 00 [ 	]*add    \$0x8,%edx
+[ 	]+8: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
+   c:	65 a1 00 00 00 00 [ 	]*mov    %gs:0x0,%eax
+  12:	2b 82 00 00 00 00 [ 	]*sub    0x0\(%edx\),%eax
+[ 	]+14: R_386_TLS_IE_32	foo
+  18:	65 a1 00 00 00 00 [ 	]*mov    %gs:0x0,%eax
+  1e:	2d 00 00 00 00 [ 	]*sub    \$0x0,%eax
+[ 	]+1f: R_386_TLS_LE_32	bar
+  23:	65 8b 0d 00 00 00 00 [ 	]*mov    %gs:0x0,%ecx
+  2a:	90 [ 	]*nop[ 	]*
+  2b:	81 e9 00 00 00 00 [ 	]*sub    \$0x0,%ecx
+[ 	]+2d: R_386_TLS_LE_32	baz
+  31:	c3 [ 	]*ret[ 	]*
+  32:	89 f6 [ 	]*mov    %esi,%esi
--- gas/testsuite/gas/i386/tlspic.d.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/i386/tlspic.d	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,24 @@
+#objdump: -dr
+#name: i386 non-pic tls
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+000 <fn>:
+   0:	55 [ 	]*push   %ebp
+   1:	89 e5 [ 	]*mov    %esp,%ebp
+   3:	53 [ 	]*push   %ebx
+   4:	50 [ 	]*push   %eax
+   5:	e8 00 00 00 00 [ 	]*call   a <fn\+0xa>
+   a:	5b [ 	]*pop    %ebx
+   b:	81 c3 03 00 00 00 [ 	]*add    \$0x3,%ebx
+[ 	]+d: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
+  11:	65 a1 00 00 00 00 [ 	]*mov    %gs:0x0,%eax
+  17:	8d 76 00 [ 	]*lea    0x0\(%esi\),%esi
+  1a:	2b 83 00 00 00 00 [ 	]*sub    0x0\(%ebx\),%eax
+[ 	]+1c: R_386_TLS_IE_32	foo
+  20:	8b 5d fc [ 	]*mov    0xfffffffc\(%ebp\),%ebx
+  23:	c9 [ 	]*leave[ 	]*
+  24:	c3 [ 	]*ret[ 	]*
+  25:	8d 76 00 [ 	]*lea    0x0\(%esi\),%esi
--- gas/testsuite/gas/i386/i386.exp.jj	Fri Oct  5 13:03:40 2001
+++ gas/testsuite/gas/i386/i386.exp	Mon Jan 28 16:02:39 2002
@@ -75,6 +75,9 @@ if [expr ([istarget "i*86-*-*"] ||  [ist
 	 && ![istarget *-*-linux*oldld*])
     } then {
 	run_dump_test "relax"
+	run_dump_test "tlsd"
+	run_dump_test "tlspic"
+	run_dump_test "tlsnopic"
     }
 
     set ASFLAGS "$old_ASFLAGS"
--- gas/testsuite/gas/ia64/tls.s.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/ia64/tls.s	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,64 @@
+	.section ".tdata", "awT", @progbits
+	.align 16
+	.global x#, y#, z#, a#, b#, c#
+	.protected a#, b#, c#
+	.type	x#,@object
+	.size	x#,4
+x:	data4	1
+	.type	y#,@object
+	.size	y#,4
+y:	data4	2
+	.type	z#,@object
+	.size	z#,4
+z:	data4	3
+	.align	8
+	.type	a#,@object
+	.size	a#,8
+a:	data8	4
+	.type	b#,@object
+	.size	b#,8
+b:	data8	5
+	.type	c#,@object
+	.size	c#,1
+c:	data1	6
+
+	.text
+	.align 16
+	.global foo#
+	.proc foo#
+foo:
+	.prologue
+	alloc r36 = ar.pfs, 0, 5, 3, 0
+	.body
+	addl	loc0 = @ltoff(@tprel(x)), gp;;
+	ld8	loc0 = [loc0];;
+	add	loc1 = loc0, r13;;
+
+	mov	r2 = r13;;
+	addl	loc1 = @tprel(y), r2;;
+
+	mov	loc0 = gp
+	addl	out0 = @ltoff(@dtpmod(z)), gp
+	addl	out1 = @ltoff(@dtprel(z)), gp;;
+	ld8	out0 = [out0]
+	ld8	out1 = [out1]
+	br.call.sptk.many	b0 = __tls_get_addr;;
+	mov	gp = loc0;;
+
+	addl	out0 = @ltoff(@dtpmod(a)), gp
+	addl	out1 = @dtprel(a), r0;;
+	ld8	out0 = [out0]
+	br.call.sptk.many	b0 = __tls_get_addr;;
+	mov	gp = loc0;;
+
+	addl	out0 = @ltoff(@dtpmod(b)), gp
+	mov	out1 = r0;;
+	ld8	out0 = [out0]
+	br.call.sptk.many	b0 = __tls_get_addr;;
+	mov	gp = loc0
+	mov	r2 = ret0;;
+	addl	loc1 = @dtprel(b), r2
+	addl	loc2 = @dtprel(c), r2
+
+	br.ret.sptk.many b0
+	.endp foo#
--- gas/testsuite/gas/ia64/tls.d.jj	Mon Jan 28 16:02:39 2002
+++ gas/testsuite/gas/ia64/tls.d	Mon Jan 28 16:02:39 2002
@@ -0,0 +1,53 @@
+#objdump: -dr
+#name: ia64 tls
+
+.*: +file format .*
+
+Disassembly of section \.text:
+
+0000000000000000 <foo>:
+   0:	0d 20 21 0a 80 05 	\[MFI\]       alloc r36=ar\.pfs,8,5,0
+			2: LTOFF_TPREL22	x
+   6:	00 00 00 02 00 00 	            nop\.f 0x0
+   c:	04 08 00 90       	            addl r32=0,r1;;
+  10:	0b 00 01 40 18 10 	\[MMI\]       ld8 r32=\[r32\];;
+  16:	10 02 35 00 40 00 	            add r33=r32,r13
+  1c:	00 00 04 00       	            nop\.i 0x0;;
+  20:	0b 10 00 1a 00 21 	\[MMI\]       mov r2=r13;;
+			21: TPREL22	y
+  26:	10 02 08 00 48 00 	            addl r33=0,r2
+  2c:	00 00 04 00       	            nop\.i 0x0;;
+  30:	01 00 01 02 00 21 	\[MII\]       mov r32=r1
+			31: LTOFF_DTPMOD22	z
+			32: LTOFF_DTPREL22	z
+  36:	50 02 04 00 48 c0 	            addl r37=0,r1
+  3c:	04 08 00 90       	            addl r38=0,r1;;
+  40:	19 28 01 4a 18 10 	\[MMB\]       ld8 r37=\[r37\]
+			42: PCREL21B	__tls_get_addr
+  46:	60 02 98 30 20 00 	            ld8 r38=\[r38\]
+  4c:	08 00 00 50       	            br\.call\.sptk\.many b0=40 <foo\+0x40>;;
+  50:	0b 08 00 40 00 21 	\[MMI\]       mov r1=r32;;
+			51: LTOFF_DTPMOD22	a
+			52: DTPREL22	a
+  56:	50 02 04 00 48 c0 	            addl r37=0,r1
+  5c:	04 00 00 90       	            mov r38=0;;
+  60:	1d 28 01 4a 18 10 	\[MFB\]       ld8 r37=\[r37\]
+			62: PCREL21B	__tls_get_addr
+  66:	00 00 00 02 00 00 	            nop\.f 0x0
+  6c:	08 00 00 50       	            br\.call\.sptk\.many b0=60 <foo\+0x60>;;
+  70:	0b 08 00 40 00 21 	\[MMI\]       mov r1=r32;;
+			71: LTOFF_DTPMOD22	b
+  76:	50 02 04 00 48 c0 	            addl r37=0,r1
+  7c:	04 00 00 84       	            mov r38=r0;;
+  80:	1d 28 01 4a 18 10 	\[MFB\]       ld8 r37=\[r37\]
+			82: PCREL21B	__tls_get_addr
+  86:	00 00 00 02 00 00 	            nop\.f 0x0
+  8c:	08 00 00 50       	            br\.call\.sptk\.many b0=80 <foo\+0x80>;;
+  90:	02 08 00 40 00 21 	\[MII\]       mov r1=r32
+			92: DTPREL22	b
+  96:	20 00 20 00 42 20 	            mov r2=r8;;
+  9c:	04 10 00 90       	            addl r33=0,r2
+  a0:	1d 10 01 04 00 24 	\[MFB\]       addl r34=0,r2
+			a0: DTPREL22	c
+  a6:	00 00 00 02 00 80 	            nop\.f 0x0
+  ac:	08 00 84 00       	            br\.ret\.sptk\.many b0;;
--- gas/testsuite/gas/ia64/ia64.exp.jj	Fri Apr 21 22:22:22 2000
+++ gas/testsuite/gas/ia64/ia64.exp	Mon Jan 28 16:02:39 2002
@@ -35,4 +35,5 @@ if [istarget "ia64-*"] then {
     run_dump_test "dv-mutex"
     run_dump_test "dv-safe"
     run_dump_test "dv-srlz"
+    run_dump_test "tls"
 }
--- gas/write.c.jj	Mon Jan 14 17:03:20 2002
+++ gas/write.c	Mon Jan 28 16:02:39 2002
@@ -881,6 +881,13 @@ adjust_reloc_syms (abfd, sec, xxx)
 	    symbol_mark_used_in_reloc (fixp->fx_addsy);
 	    goto done;
 	  }
+
+	/* Never adjust a reloc against TLS local symbol.  */
+	if (symsec->flags2 & SEC_FLAG2_THREAD_LOCAL)
+	  {
+	    symbol_mark_used_in_reloc (fixp->fx_addsy);
+	    goto done;
+	  }
 #endif
 
 	/* Is there some other reason we can't adjust this one?  (E.g.,
--- include/elf/common.h.jj	Mon Jan 14 17:05:19 2002
+++ include/elf/common.h	Mon Jan 28 16:02:39 2002
@@ -251,6 +251,7 @@ Foundation, Inc., 59 Temple Place - Suit
 #define PT_NOTE		4		/* Auxiliary information */
 #define PT_SHLIB	5		/* Reserved, unspecified semantics */
 #define PT_PHDR		6		/* Entry for header table itself */
+#define PT_TLS		7		/* Thread local storage segment */
 #define PT_LOOS         0x60000000	/* OS-specific */
 #define PT_HIOS         0x6fffffff	/* OS-specific */
 #define PT_LOPROC	0x70000000	/* Processor-specific */
@@ -319,6 +320,7 @@ Foundation, Inc., 59 Temple Place - Suit
 #define SHF_LINK_ORDER  (1 << 7)	/* Preserve section ordering when linking */
 #define SHF_OS_NONCONFORMING (1 << 8)	/* OS specific processing required */
 #define SHF_GROUP	(1 << 9)	/* Member of a section group */
+#define SHF_TLS		(1 << 10)	/* Thread local storage section */
 
 /* #define SHF_MASKOS	0x0F000000    *//* OS-specific semantics */
 #define SHF_MASKOS	0x0FF00000	/* New value, Oct 4, 1999 Draft */
@@ -399,6 +401,7 @@ Foundation, Inc., 59 Temple Place - Suit
 #define STT_SECTION	3		/* Symbol associated with a section */
 #define STT_FILE	4		/* Symbol gives a file name */
 #define STT_COMMON	5		/* An uninitialised common block */
+#define STT_TLS		6		/* Thread local data object */
 #define STT_LOOS        10		/* OS-specific semantics */
 #define STT_HIOS        12		/* OS-specific semantics */
 #define STT_LOPROC	13		/* Application-specific semantics */
@@ -567,6 +570,7 @@ Foundation, Inc., 59 Temple Place - Suit
 #define DF_SYMBOLIC	(1 << 1)
 #define DF_TEXTREL	(1 << 2)
 #define DF_BIND_NOW	(1 << 3)
+#define DF_STATIC_TLS	(1 << 4)
 
 /* These constants are used for the version number of a Elf32_Verdef
    structure.  */
--- include/elf/ia64.h.jj	Mon Jan 14 17:05:21 2002
+++ include/elf/ia64.h	Mon Jan 28 16:02:39 2002
@@ -192,7 +192,7 @@ START_RELOC_NUMBERS (elf_ia64_reloc_type
   RELOC_NUMBER (R_IA64_TPREL64MSB, 0x96) /* @tprel(sym+add), data8 MSB */
   RELOC_NUMBER (R_IA64_TPREL64LSB, 0x97) /* @tprel(sym+add), data8 LSB */
 
-  RELOC_NUMBER (R_IA64_LTOFF_TP22, 0x9a) /* @ltoff(@tprel(s+a)), add imm22 */
+  RELOC_NUMBER (R_IA64_LTOFF_TPREL22, 0x9a) /* @ltoff(@tprel(s+a)), add imm22 */
 
   RELOC_NUMBER (R_IA64_DTPMOD64MSB, 0xa6) /* @dtpmod(sym+add), data8 MSB */
   RELOC_NUMBER (R_IA64_DTPMOD64LSB, 0xa7) /* @dtpmod(sym+add), data8 LSB */
--- include/elf/i386.h.jj	Wed Mar 14 03:27:44 2001
+++ include/elf/i386.h	Mon Jan 28 16:02:39 2002
@@ -34,12 +34,30 @@ START_RELOC_NUMBERS (elf_i386_reloc_type
      RELOC_NUMBER (R_386_RELATIVE,  8)	/* Adjust by program base */
      RELOC_NUMBER (R_386_GOTOFF,    9)	/* 32 bit offset to GOT */
      RELOC_NUMBER (R_386_GOTPC,    10)	/* 32 bit PC relative offset to GOT */
-     FAKE_RELOC   (FIRST_INVALID_RELOC, 11)
-     FAKE_RELOC   (LAST_INVALID_RELOC,  19)
+     RELOC_NUMBER (R_386_32PLT,    11)	/* Used by Sun */
+     FAKE_RELOC   (FIRST_INVALID_RELOC, 12)
+     FAKE_RELOC   (LAST_INVALID_RELOC,  17)
+     RELOC_NUMBER (R_386_TLS_GD,   18)
+     RELOC_NUMBER (R_386_TLS_LDM,  19)
      RELOC_NUMBER (R_386_16,       20)
      RELOC_NUMBER (R_386_PC16,     21)
      RELOC_NUMBER (R_386_8,	   22)
      RELOC_NUMBER (R_386_PC8,      23)
+     RELOC_NUMBER (R_386_TLS_GD_32,    24)
+     RELOC_NUMBER (R_386_TLS_GD_PUSH,  25)
+     RELOC_NUMBER (R_386_TLS_GD_CALL,  26)
+     RELOC_NUMBER (R_386_TLS_GD_POP,   27)
+     RELOC_NUMBER (R_386_TLS_LDM_32,   28)
+     RELOC_NUMBER (R_386_TLS_LDM_PUSH, 29)
+     RELOC_NUMBER (R_386_TLS_LDM_CALL, 30)
+     RELOC_NUMBER (R_386_TLS_LDM_POP,  31)
+     RELOC_NUMBER (R_386_TLS_LDO_32,   32)
+     RELOC_NUMBER (R_386_TLS_IE_32,    33)
+     RELOC_NUMBER (R_386_TLS_LE_32,    34)
+     RELOC_NUMBER (R_386_TLS_DTPMOD32, 35)
+     RELOC_NUMBER (R_386_TLS_DTPOFF32, 36)
+     RELOC_NUMBER (R_386_TLS_TPOFF32,  37)
+
      /* These are GNU extensions to enable C++ vtable garbage collection.  */
      RELOC_NUMBER (R_386_GNU_VTINHERIT, 250)
      RELOC_NUMBER (R_386_GNU_VTENTRY, 251)
--- ld/scripttempl/elf.sc.jj	Thu Dec 13 13:22:35 2001
+++ ld/scripttempl/elf.sc	Fri Feb  8 16:36:32 2002
@@ -200,6 +205,10 @@ eval $COMBRELOCCAT <<EOF
   ${OTHER_READONLY_RELOC_SECTIONS}
   .rel.data     ${RELOCATING-0} : { *(.rel.data${RELOCATING+ .rel.data.* .rel.gnu.linkonce.d.*}) }
   .rela.data    ${RELOCATING-0} : { *(.rela.data${RELOCATING+ .rela.data.* .rela.gnu.linkonce.d.*}) }
+  .rel.tdata	${RELOCATING-0} : { *(.rel.tdata${RELOCATING+ .rel.tdata.*}) }
+  .rela.tdata	${RELOCATING-0} : { *(.rela.tdata${RELOCATING+ .rela.tdata.*}) }
+  .rel.tbss	${RELOCATING-0} : { *(.rel.tbss${RELOCATING+ .rel.tbss.*}) }
+  .rela.tbss	${RELOCATING-0} : { *(.rela.tbss${RELOCATING+ .rela.tbss.*}) }
   .rel.ctors    ${RELOCATING-0} : { *(.rel.ctors) }
   .rela.ctors   ${RELOCATING-0} : { *(.rela.ctors) }
   .rel.dtors    ${RELOCATING-0} : { *(.rel.dtors) }
@@ -279,6 +286,8 @@ cat <<EOF
     ${CONSTRUCTING+SORT(CONSTRUCTORS)}
   }
   .data1        ${RELOCATING-0} : { *(.data1) }
+  .tdata	${RELOCATING-0} : { *(.tdata .tdata.*) }
+  .tbss		${RELOCATING-0} : { *(.tbss .tbss.*) *(.tcommon) }
   .eh_frame     ${RELOCATING-0} : { KEEP (*(.eh_frame)) }
   .gcc_except_table ${RELOCATING-0} : { *(.gcc_except_table) }
   ${WRITABLE_RODATA+${RODATA}}
--- ld/ldlang.c.jj	Mon Jan 14 17:05:21 2002
+++ ld/ldlang.c	Fri Feb  8 15:25:04 2002
@@ -1150,7 +1153,7 @@ lang_add_section (ptr, section, output, 
     {
       boolean first;
       lang_input_section_type *new;
-      flagword flags;
+      flagword flags, flags2;
 
       if (output->bfd_section == NULL)
 	init_os (output);
@@ -1166,6 +1169,7 @@ lang_add_section (ptr, section, output, 
       section->output_section = output->bfd_section;
 
       flags = section->flags;
+      flags2 = section->flags2;
 
       /* We don't copy the SEC_NEVER_LOAD flag from an input section
 	 to an output section, because we want to be able to include a
@@ -1202,7 +1206,12 @@ lang_add_section (ptr, section, output, 
 	  flags &= ~ (SEC_MERGE | SEC_STRINGS);
 	}
 
+      /* For now make .tbss normal section.  */
+      if (flags2 & SEC_FLAG2_THREAD_LOCAL)
+	flags |= SEC_LOAD;
+
       section->output_section->flags |= flags;
+      section->output_section->flags2 |= flags2;
 
       if (flags & SEC_MERGE)
 	section->output_section->entsize = section->entsize;
@@ -2961,6 +2970,9 @@ lang_size_sections (s, output_section_st
 
 	    if (bfd_is_abs_section (os->bfd_section))
 	      ASSERT (after == os->bfd_section->vma);
+	    else if ((os->bfd_section->flags & SEC_HAS_CONTENTS) == 0
+		     && (os->bfd_section->flags2 & SEC_FLAG2_THREAD_LOCAL))
+	      os->bfd_section->_raw_size = 0;
 	    else
 	      os->bfd_section->_raw_size =
 		(after - os->bfd_section->vma) * opb;
--- ld/ldwrite.c.jj	Tue Mar 13 07:14:27 2001
+++ ld/ldwrite.c	Thu Feb  7 16:15:17 2002
@@ -233,7 +233,9 @@ build_link_order (statement)
 
 	  ASSERT (output_section->owner == output_bfd);
 
-	  if ((output_section->flags & SEC_HAS_CONTENTS) != 0)
+	  if ((output_section->flags & SEC_HAS_CONTENTS) != 0
+	      || ((output_section->flags & SEC_LOAD) != 0
+		  && (output_section->flags2 & SEC_FLAG2_THREAD_LOCAL)))
 	    {
 	      struct bfd_link_order *link_order;
 

	Jakub


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