x86: Add tls check in gas

Assembler shouldn't accept invalid TLS instructions, TLS relocations
can only be used with specific instructions as specified in TLS psABI
and linker issues an error when TLS relocations are used with wrong
instructions or format. Since it is inconvenient for gcc to rely on
linker to report errors, adding TLS check in the assembler stage so
that gcc can know TLS errors earlier.

gas/ChangeLog:

        PR gas/32022
        * config.in: Regenerate.
        * config/tc-i386.c
        *(enum x86_tls_error_type): New.
        *(struct _i386_insn): Added has_gotrel to indicate whether TLS
	relocations need to be checked.
        (x86_check_tls_relocation): Added a new function to check TLS
	relocation.
        (x86_report_tls_error): Created a new function to report TLS error.
        (i386_assemble): Handle x86_check_tls_relocation.
        (lex_got): Set i.has_gotrel.
        (OPTION_MTLS_CHECK): Added a new option to contrl TLS check.
        (struct option): Ditto.
        (md_parse_option): Ditto.
        (md_show_usage): Ditto.
        * configure.ac: Added a new option to check TLS relocation by
	default.
        * configure: Regenerated.
        * doc/c-i386.texi: Document -mtls-check=.
        * testsuite/gas/i386/i386.exp: Added new tests.
        * testsuite/gas/i386/ilp32/ilp32.exp: Ditto.
        * testsuite/gas/i386/ilp32/reloc64.d: Disable TLS check for it.
        * testsuite/gas/i386/ilp32/x32-tls.d: Ditto.
        * testsuite/gas/i386/inval-tls.l: Added more test cases.
        * testsuite/gas/i386/inval-tls.s: Ditto.
        * testsuite/gas/i386/reloc32.d: Disable TLS check for it.
        * testsuite/gas/i386/reloc64.d: Ditto.
        * testsuite/gas/i386/x86-64-inval-tls.l: Added more test cases.
        * testsuite/gas/i386/x86-64-inval-tls.s: Ditto.
        * testsuite/gas/i386/x86-64.exp: Added new tests.
        * testsuite/gas/i386/ilp32/x32-inval-tls.l: New test.
        * testsuite/gas/i386/ilp32/x32-inval-tls.s: Ditto.
        * testsuite/gas/i386/ilp32/x86-64-tls.d: Ditto.
        * testsuite/gas/i386/tls.d: Ditto.
        * testsuite/gas/i386/tls.s: Ditto.
        * testsuite/gas/i386/x86-64-tls.d: Ditto.
        * testsuite/gas/i386/x86-64-tls.s: Ditto.

ld/ChangeLog:

        PR gas/32022
        * testsuite/ld-i386/tlsgdesc1.d: Disable TLS check for it.
        * testsuite/ld-i386/tlsgdesc2.d: Ditto.
        * testsuite/ld-i386/tlsie2.d: Ditto.
        * testsuite/ld-i386/tlsie3.d: Ditto.
        * testsuite/ld-i386/tlsie4.d: Ditto.
        * testsuite/ld-i386/tlsie5.d: Ditto.
        * testsuite/ld-i386/tlsgdesc3.d: Ditto.
        * testsuite/ld-x86-64/tlsdesc3.d: Ditto.
        * testsuite/ld-x86-64/tlsdesc4.d: Ditto.
        * testsuite/ld-x86-64/tlsie2.d: Ditto.
        * testsuite/ld-x86-64/tlsie3.d: Ditto.
        * testsuite/ld-x86-64/tlsie5.d: Ditto.
        * testsuite/ld-x86-64/tlsdesc5.d: Ditto.
diff --git a/gas/config.in b/gas/config.in
index a1f8349..c32b46b 100644
--- a/gas/config.in
+++ b/gas/config.in
@@ -60,6 +60,9 @@
 /* Define default value for RISC-V -mpriv-spec */
 #undef DEFAULT_RISCV_PRIV_SPEC
 
+/* Define to 1 if you want to check x86 TLS relocation by default. */
+#undef DEFAULT_X86_TLS_CHECK
+
 /* Define to 1 if you want to generate GNU x86 used ISA and feature properties
    by default. */
 #undef DEFAULT_X86_USED_NOTE
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 72de448..11565ac 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -274,6 +274,30 @@
     internal_error,
   };
 
+enum x86_tls_error_type
+{
+  x86_tls_error_continue,
+  x86_tls_error_none,
+  x86_tls_error_insn,
+  x86_tls_error_opcode,
+  x86_tls_error_sib,
+  x86_tls_error_no_base_reg,
+  x86_tls_error_require_no_base_index_reg,
+  x86_tls_error_base_reg,
+  x86_tls_error_index_ebx,
+  x86_tls_error_eax,
+  x86_tls_error_RegA,
+  x86_tls_error_ebx,
+  x86_tls_error_rip,
+  x86_tls_error_dest_eax,
+  x86_tls_error_dest_rdi,
+  x86_tls_error_scale_factor,
+  x86_tls_error_base_reg_size,
+  x86_tls_error_dest_32bit_reg_size,
+  x86_tls_error_dest_64bit_reg_size,
+  x86_tls_error_dest_32bit_or_64bit_reg_size
+};
+
 struct _i386_insn
   {
     /* TM holds the template for the insn were currently assembling.  */
@@ -365,6 +389,9 @@
     /* Has GOTPC or TLS relocation.  */
     bool has_gotpc_tls_reloc;
 
+    /* Has relocation entry from the gotrel array.  */
+    bool has_gotrel;
+
     /* RM and SIB are the modrm byte and the sib byte where the
        addressing modes of this insn are encoded.  */
     modrm_byte rm;
@@ -717,6 +744,9 @@
 static int generate_relax_relocations
   = DEFAULT_GENERATE_X86_RELAX_RELOCATIONS;
 
+/* 1 if the assembler should check tls relocation.  */
+static bool tls_check = DEFAULT_X86_TLS_CHECK;
+
 static enum check_kind
   {
     check_none = 0,
@@ -6358,6 +6388,356 @@
 	       && (t->base_opcode | 8) == 0x2c);
 }
 
+static enum x86_tls_error_type
+x86_check_tls_relocation (enum bfd_reloc_code_real r_type)
+{
+  switch (r_type)
+    {
+    case BFD_RELOC_386_TLS_GOTDESC:
+      /* Check GDesc access model:
+
+	 leal x@tlsdesc(%ebx), %reg32 --> Memory reg must be %ebx and
+					  SIB is not supported.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      if (i.index_reg)
+	return x86_tls_error_sib;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_type.bitfield.instance != RegB)
+	return x86_tls_error_ebx;
+      if (!i.op[1].regs->reg_type.bitfield.dword)
+	return x86_tls_error_dest_32bit_reg_size;
+      break;
+
+    case BFD_RELOC_386_TLS_GD:
+      /* Check GD access model:
+
+	 leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
+	 leal foo@tlsgd(%reg32), %eax    --> Dest reg must be '%eax'
+					     Memory reg can't be %eax.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      if (i.op[1].regs->reg_type.bitfield.instance != Accum)
+	return x86_tls_error_dest_eax;
+      if (!i.op[1].regs->reg_type.bitfield.dword)
+	return x86_tls_error_dest_32bit_reg_size;
+      if (i.index_reg)
+	{
+	  if (i.base_reg)
+	    return x86_tls_error_base_reg;
+	  if (i.index_reg->reg_type.bitfield.instance != RegB)
+	    return x86_tls_error_index_ebx;
+	  if (i.log2_scale_factor)
+	    return x86_tls_error_scale_factor;
+	}
+      else
+	{
+	  if (!i.base_reg)
+	    return x86_tls_error_no_base_reg;
+	  if (i.base_reg->reg_type.bitfield.instance == Accum)
+	    return x86_tls_error_eax;
+	}
+      break;
+
+    case BFD_RELOC_386_TLS_LDM:
+      /*  Check LDM access model:
+
+	  leal foo@tlsldm(%reg32), %eax --> Dest reg must be '%eax'
+				            Memory reg can't be %eax and SIB
+					    is not supported.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      if (i.index_reg)
+	return x86_tls_error_sib;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_type.bitfield.instance == Accum)
+	return x86_tls_error_eax;
+      if (i.op[1].regs->reg_type.bitfield.instance != Accum)
+	return x86_tls_error_dest_eax;
+      if (!i.op[1].regs->reg_type.bitfield.dword)
+	return x86_tls_error_dest_32bit_reg_size;
+      break;
+
+    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
+      /* Check GOTPC32 TLSDESC access model:
+
+	 --- LP64 mode ---
+	 leaq x@tlsdesc(%rip), %reg64 --> Memory reg must be %rip.
+
+	 --- X32 mode ---
+	 rex/rex2 leal x@tlsdesc(%rip), %reg32 --> Memory reg must be %rip.
+
+	 In X32 mode, gas will add rex/rex2 for it later, no need to check
+	 here.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_num != RegIP
+	  || !i.base_reg->reg_type.bitfield.qword)
+	return x86_tls_error_rip;
+      if (x86_elf_abi == X86_64_ABI)
+	{
+	  if (!i.op[1].regs->reg_type.bitfield.qword)
+	    return x86_tls_error_dest_64bit_reg_size;
+	}
+      else if (!i.op[1].regs->reg_type.bitfield.dword
+	       && !i.op[1].regs->reg_type.bitfield.qword)
+	return x86_tls_error_dest_32bit_or_64bit_reg_size;
+	  break;
+
+    case BFD_RELOC_X86_64_TLSGD:
+      /* Check GD access model:
+
+	 leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
+       */
+    case BFD_RELOC_X86_64_TLSLD:
+      /* Check LD access model:
+
+	 leaq foo@tlsld(%rip), %rdi --> Only this fixed format is supported.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_num != RegIP
+	  || !i.base_reg->reg_type.bitfield.qword)
+	return x86_tls_error_rip;
+      if (!i.op[1].regs->reg_type.bitfield.qword
+	  || i.op[1].regs->reg_num != EDI_REG_NUM
+	  || i.op[1].regs->reg_flags)
+	return x86_tls_error_dest_rdi;
+      break;
+
+    case BFD_RELOC_386_TLS_GOTIE:
+      /* Check GOTIE access model:
+
+	 subl foo@gotntpoff(%reg1), %reg2
+	 movl foo@gotntpoff(%reg1), %reg2
+	 addl foo@gotntpoff(%reg1), %reg2
+
+	 Memory operand: SIB is not supported.
+       */
+    case BFD_RELOC_386_TLS_IE_32:
+      /* Check IE_32 access model:
+
+	 subl foo@gottpoff(%reg1), %reg2
+	 movl foo@gottpoff(%reg1), %reg2
+	 addl foo@gottpoff(%reg1), %reg2
+
+	 Memory operand: SIB is not supported.
+       */
+      if (i.tm.mnem_off != MN_sub
+	  && i.tm.mnem_off != MN_add
+	  && i.tm.mnem_off != MN_mov)
+	return x86_tls_error_insn;
+      if (i.op[1].regs->reg_type.bitfield.class != Reg
+	  || i.op[0].regs->reg_type.bitfield.class
+	  || i.imm_operands)
+	return x86_tls_error_opcode;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.index_reg)
+	return x86_tls_error_sib;
+      if (!i.base_reg->reg_type.bitfield.dword)
+	return x86_tls_error_base_reg_size;
+      if (!i.op[1].regs->reg_type.bitfield.dword)
+	return x86_tls_error_dest_32bit_reg_size;
+      break;
+
+    case BFD_RELOC_386_TLS_IE:
+      /* Check IE access model:
+
+	 movl foo@indntpoff, %reg32 --> Mod == 00 && r/m == 5
+	 addl foo@indntpoff, %reg32 --> Mod == 00 && r/m == 5
+       */
+      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
+	return x86_tls_error_insn;
+      if (i.op[1].regs->reg_type.bitfield.class != Reg
+	  || i.op[0].regs->reg_type.bitfield.class
+	  || i.imm_operands)
+	return x86_tls_error_opcode;
+      if (i.base_reg || i.index_reg)
+	return x86_tls_error_require_no_base_index_reg;
+      if (!i.op[1].regs->reg_type.bitfield.dword)
+	return x86_tls_error_dest_32bit_reg_size;
+      break;
+
+    case BFD_RELOC_X86_64_GOTTPOFF:
+      /* Check GOTTPOFF access model:
+
+	 mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
+	 add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
+	 add %reg1, foo@gottpoff(%rip), %reg2 --> Memory Reg must be %rip.
+	 add foo@gottpoff(%rip), %reg1, %reg2 --> Memory Reg must be %rip.
+       */
+      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
+	return x86_tls_error_insn;
+      if (i.op[i.operands - 1].regs->reg_type.bitfield.class != Reg
+	  || (i.op[0].regs->reg_type.bitfield.class
+	      && i.tm.opcode_modifier.vexvvvv != VexVVVV_DST)
+	  || i.imm_operands)
+	return x86_tls_error_opcode;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_num != RegIP
+	  || !i.base_reg->reg_type.bitfield.qword)
+	return x86_tls_error_rip;
+      if (x86_elf_abi == X86_64_ABI)
+	{
+	  if (!i.op[i.operands - 1].regs->reg_type.bitfield.qword)
+	    return x86_tls_error_dest_64bit_reg_size;
+	}
+      else if (!i.op[i.operands - 1].regs->reg_type.bitfield.dword
+	       && !i.op[i.operands - 1].regs->reg_type.bitfield.qword)
+	return x86_tls_error_dest_32bit_or_64bit_reg_size;
+      break;
+
+    case BFD_RELOC_386_TLS_DESC_CALL:
+      /* Check GDesc access model:
+
+	 call *x@tlscall(%eax) --> Memory reg must be %eax and
+				   SIB is not supported.
+       */
+    case BFD_RELOC_X86_64_TLSDESC_CALL:
+      /* Check GDesc access model:
+
+	 call *x@tlscall(%rax) <--- LP64 mode.
+	 call *x@tlscall(%eax) <--- X32 mode.
+
+	 Only these fixed formats are supported.
+       */
+      if (i.tm.mnem_off != MN_call)
+	return x86_tls_error_insn;
+      if (i.index_reg)
+	return x86_tls_error_sib;
+      if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      if (i.base_reg->reg_type.bitfield.instance != Accum)
+	return x86_tls_error_RegA;
+      break;
+
+    case BFD_RELOC_NONE:
+      /* This isn't a relocation.  */
+      return x86_tls_error_continue;
+
+    default:
+      break;
+    }
+
+  /* This relocation is OK.  */
+  return x86_tls_error_none;
+}
+
+static void
+x86_report_tls_error (enum x86_tls_error_type tls_error,
+		      enum bfd_reloc_code_real r_type)
+{
+  unsigned int k;
+  for (k = 0; k < ARRAY_SIZE (gotrel); k++)
+    if (gotrel[k].rel[object_64bit] == r_type)
+      break;
+
+  switch (tls_error)
+    {
+    case x86_tls_error_insn:
+      as_bad (_("@%s operator cannot be used with `%s'"),
+	      gotrel[k].str, insn_name (&i.tm));
+      return;
+
+    case x86_tls_error_opcode:
+      as_bad (_("@%s operator can be used with `%s', but format is wrong"),
+	      gotrel[k].str, insn_name (&i.tm));
+      return;
+
+    case x86_tls_error_sib:
+      as_bad (_("@%s operator requires no SIB"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_no_base_reg:
+      as_bad (_("@%s operator requires base register"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_require_no_base_index_reg:
+      as_bad (_("@%s operator requires no base/index register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_base_reg:
+      as_bad (_("@%s operator requires no base register"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_index_ebx:
+      as_bad (_("@%s operator requires `%sebx' as index register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_eax:
+      as_bad (_("@%s operator requires `%seax' as base register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_RegA:
+      as_bad (_("@%s operator requires `%seax/%srax' as base register"),
+	      gotrel[k].str, register_prefix, register_prefix);
+      return;
+
+    case x86_tls_error_ebx:
+      as_bad (_("@%s operator requires `%sebx' as base register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_rip:
+      as_bad (_("@%s operator requires `%srip' as base register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_dest_eax:
+      as_bad (_("@%s operator requires `%seax' as dest register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_dest_rdi:
+      as_bad (_("@%s operator requires `%srdi' as dest register"),
+	      gotrel[k].str, register_prefix);
+      return;
+
+    case x86_tls_error_scale_factor:
+      as_bad (_("@%s operator requires scale factor of 1"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_base_reg_size:
+      as_bad (_("@%s operator requires 32-bit base register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_dest_32bit_reg_size:
+      as_bad (_("@%s operator requires 32-bit dest register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_dest_64bit_reg_size:
+      as_bad (_("@%s operator requires 64-bit dest register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_dest_32bit_or_64bit_reg_size:
+      as_bad (_("@%s operator requires 32-bit or 64-bit dest register"),
+	      gotrel[k].str);
+      return;
+
+    default:
+      abort ();
+    }
+}
+
 /* This is the guts of the machine-dependent assembler.  LINE points to a
    machine dependent instruction.  This function is supposed to emit
    the frags/bytes it assembles to.  */
@@ -6696,6 +7076,21 @@
 	i.prefix[LOCK_PREFIX] = 0;
     }
 
+  if (i.has_gotrel && tls_check)
+    {
+      enum x86_tls_error_type tls_error;
+      for (j = 0; j < i.operands; ++j)
+	{
+	  tls_error = x86_check_tls_relocation (i.reloc[j]);
+	  if (tls_error == x86_tls_error_continue)
+	    continue;
+
+	  if (tls_error != x86_tls_error_none)
+	    x86_report_tls_error (tls_error, i.reloc[j]);
+	  break;
+	}
+    }
+
   if ((is_any_vex_encoding (&i.tm) && i.tm.opcode_space != SPACE_EVEXMAP4)
       || i.tm.operand_types[i.imm_operands].bitfield.class >= RegMMX
       || i.tm.operand_types[i.imm_operands + 1].bitfield.class >= RegMMX)
@@ -6706,28 +7101,6 @@
 	  as_bad (_("data size prefix invalid with `%s'"), insn_name (&i.tm));
 	  return;
 	}
-
-      /* Don't allow e.g. KMOV in TLS code sequences which will trigger
-	 linker error later.  */
-      for (j = i.imm_operands; j < i.operands; ++j)
-	switch (i.reloc[j])
-	  {
-	  case BFD_RELOC_X86_64_GOTTPOFF:
-	  case BFD_RELOC_386_TLS_GOTIE:
-	  case BFD_RELOC_X86_64_TLSLD:
-	    for (unsigned int k = 0; k < ARRAY_SIZE (gotrel); k++)
-	      {
-		if (gotrel[k].rel[object_64bit] == i.reloc[j])
-		  {
-		    as_bad (_("@%s operator cannot be used with `%s'"),
-			  gotrel[k].str, insn_name (&i.tm));
-		    return;
-		  }
-	      }
-	    abort ();
-	  default:
-	    break;
-	  }
     }
 
   /* Check if HLE prefix is OK.  */
@@ -12497,6 +12870,7 @@
 	      int first, second;
 	      char *tmpbuf, *past_reloc;
 
+	      i.has_gotrel = true;
 	      *rel = gotrel[j].rel[object_64bit];
 
 	      if (types)
@@ -16230,6 +16604,7 @@
 #define OPTION_MLFENCE_BEFORE_INDIRECT_BRANCH (OPTION_MD_BASE + 32)
 #define OPTION_MLFENCE_BEFORE_RET (OPTION_MD_BASE + 33)
 #define OPTION_MUSE_UNALIGNED_VECTOR_MOVE (OPTION_MD_BASE + 34)
+#define OPTION_MTLS_CHECK (OPTION_MD_BASE + 35)
 
 struct option md_longopts[] =
 {
@@ -16276,6 +16651,7 @@
   {"mlfence-before-ret", required_argument, NULL, OPTION_MLFENCE_BEFORE_RET},
   {"mamd64", no_argument, NULL, OPTION_MAMD64},
   {"mintel64", no_argument, NULL, OPTION_MINTEL64},
+  {"mtls-check", required_argument, NULL, OPTION_MTLS_CHECK},
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -16832,6 +17208,14 @@
 	  optimize_for_space = 0;
 	}
       break;
+    case OPTION_MTLS_CHECK:
+      if (strcasecmp (arg, "yes") == 0)
+	tls_check = true;
+      else if (strcasecmp (arg, "no") == 0)
+	tls_check = false;
+      else
+	as_fatal (_("invalid -mtls-check= option: `%s'"), arg);
+      break;
 
     default:
       return 0;
@@ -17074,6 +17458,16 @@
     fprintf (stream, _("(default: no)\n"));
   fprintf (stream, _("\
                           generate relax relocations\n"));
+
+  fprintf (stream, _("\
+  -mtls-check=[no|yes] "));
+  if (DEFAULT_X86_TLS_CHECK)
+    fprintf (stream, _("(default: yes)\n"));
+  else
+    fprintf (stream, _("(default: no)\n"));
+  fprintf (stream, _("\
+                          check TLS relocation\n"));
+
   fprintf (stream, _("\
   -malign-branch-boundary=NUM (default: 0)\n\
                           align branches within NUM byte boundary\n"));
diff --git a/gas/configure b/gas/configure
index 6b96d3a..be37f31 100755
--- a/gas/configure
+++ b/gas/configure
@@ -818,6 +818,7 @@
 enable_compressed_debug_sections
 enable_default_compressed_debug_sections_algorithm
 enable_x86_relax_relocations
+enable_x86_tls_check
 enable_elf_stt_common
 enable_generate_build_notes
 enable_mips_fix_loongson3_llsc
@@ -1493,6 +1494,7 @@
                           --enable-compressed-debug-sections.
   --enable-x86-relax-relocations
                           generate x86 relax relocations by default
+  --enable-x86-tls-check  check x86 TLS relocation by default
   --enable-elf-stt-common generate ELF common symbols with STT_COMMON type by
                           default
   --enable-generate-build-notes
@@ -10775,7 +10777,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10778 "configure"
+#line 10780 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10881,7 +10883,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10884 "configure"
+#line 10886 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11575,6 +11577,17 @@
 esac
 fi
 
+# PR gas/32022
+# Decide if x86 assembler should check TLS relocation.
+ac_default_x86_tls_check=unset
+# Provide a configure time option to override our default.
+# Check whether --enable-x86_tls_check was given.
+if test "${enable_x86_tls_check+set}" = set; then :
+  enableval=$enable_x86_tls_check; case "${enableval}" in
+  no)  ac_default_x86_tls_check=0 ;;
+esac
+fi
+
 # Decide if ELF assembler should generate common symbols with the
 # STT_COMMON type.
 ac_default_elf_stt_common=unset
@@ -12698,6 +12711,15 @@
 _ACEOF
 
 
+if test ${ac_default_x86_tls_check} = unset; then
+  ac_default_x86_tls_check=1
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_X86_TLS_CHECK $ac_default_x86_tls_check
+_ACEOF
+
+
 if test ${ac_default_elf_stt_common} = unset; then
   ac_default_elf_stt_common=0
 fi
diff --git a/gas/configure.ac b/gas/configure.ac
index 6b978aa..bf3f9b5 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -95,6 +95,17 @@
   no)  ac_default_x86_relax_relocations=0 ;;
 esac])dnl
 
+# PR gas/32022
+# Decide if x86 assembler should check TLS relocation.
+ac_default_x86_tls_check=unset
+# Provide a configure time option to override our default.
+AC_ARG_ENABLE(x86_tls_check,
+	      AS_HELP_STRING([--enable-x86-tls-check],
+	      [check x86 TLS relocation by default]),
+[case "${enableval}" in
+  no)  ac_default_x86_tls_check=0 ;;
+esac])dnl
+
 # Decide if ELF assembler should generate common symbols with the
 # STT_COMMON type.
 ac_default_elf_stt_common=unset
@@ -737,6 +748,13 @@
   $ac_default_x86_relax_relocations,
   [Define to 1 if you want to generate x86 relax relocations by default.])
 
+if test ${ac_default_x86_tls_check} = unset; then
+  ac_default_x86_tls_check=1
+fi
+AC_DEFINE_UNQUOTED(DEFAULT_X86_TLS_CHECK,
+  $ac_default_x86_tls_check,
+  [Define to 1 if you want to check x86 TLS relocation by default.])
+
 if test ${ac_default_elf_stt_common} = unset; then
   ac_default_elf_stt_common=0
 fi
diff --git a/gas/doc/c-i386.texi b/gas/doc/c-i386.texi
index a9e4356..9667061 100644
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -467,6 +467,16 @@
 relocations.  The default can be controlled by a configure option
 @option{--enable-x86-relax-relocations}.
 
+@cindex @samp{-mtls-check=} option, i386
+@cindex @samp{-mtls-check=} option, x86-64
+@item -mtls-check=@var{no}
+@itemx -mtls-check=@var{yes}
+These options control whether the assembler check tls relocation.
+@option{-mtls-check=@var{yes}} will check tls relocation.
+@option{-mtls-check=@var{no}} will not check tls relocation
+The default can be controlled by a configure option
+@option{--enable-x86-tls-check}.
+
 @cindex @samp{-malign-branch-boundary=} option, i386
 @cindex @samp{-malign-branch-boundary=} option, x86-64
 @item -malign-branch-boundary=@var{NUM}
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index 75ad061..6710a56 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -699,10 +699,11 @@
 	run_dump_test "tlsd"
 	run_dump_test "tlspic"
 	run_dump_test "tlsnopic"
+	run_dump_test "tls"
 	run_list_test "inval-tls"
 	run_dump_test "bss"
 	run_dump_test "reloc32"
-	run_list_test "reloc32" "--defsym _bad_=1"
+	run_list_test "reloc32" "--defsym _bad_=1 -mtls-check=no"
 	run_dump_test "intel-got32"
 	run_dump_test "intel-movs32"
 	run_dump_test "intel-movs16"
diff --git a/gas/testsuite/gas/i386/ilp32/ilp32.exp b/gas/testsuite/gas/i386/ilp32/ilp32.exp
index a301738..18befcc 100644
--- a/gas/testsuite/gas/i386/ilp32/ilp32.exp
+++ b/gas/testsuite/gas/i386/ilp32/ilp32.exp
@@ -37,8 +37,9 @@
 	}
     }
 
-    run_list_test "reloc64" "--defsym _bad_=1"
+    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
     run_list_test "reloc-2"
+    run_list_test "x32-inval-tls" "-I${srcdir}/$subdir"
 
     set ASFLAGS "$old_ASFLAGS"
 }
diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.d b/gas/testsuite/gas/i386/ilp32/reloc64.d
index e2c461f..84b6aac 100644
--- a/gas/testsuite/gas/i386/ilp32/reloc64.d
+++ b/gas/testsuite/gas/i386/ilp32/reloc64.d
@@ -1,4 +1,4 @@
-#as: -mx86-used-note=no --generate-missing-build-notes=no
+#as: -mx86-used-note=no --generate-missing-build-notes=no -mtls-check=no
 #objdump: -Drw
 #name: x86-64 (ILP32) relocs
 
diff --git a/gas/testsuite/gas/i386/ilp32/x32-inval-tls.l b/gas/testsuite/gas/i386/ilp32/x32-inval-tls.l
new file mode 100644
index 0000000..f3807c8
--- /dev/null
+++ b/gas/testsuite/gas/i386/ilp32/x32-inval-tls.l
@@ -0,0 +1,38 @@
+.*: Assembler messages:
+.*:3: Error: @GOTTPOFF operator cannot be used with `kmovq'
+.*:4: Error: @TLSLD operator cannot be used with `kmovq'
+.*:7: Error: @TLSGD operator cannot be used with `add'
+.*:8: Error: @TLSGD operator requires `%rdi' as dest register
+.*:9: Error: @TLSGD operator requires `%rip' as base register
+.*:10: Error: @TLSGD operator requires base register
+.*:11: Error: @TLSGD operator requires `%rip' as base register
+.*:12: Error: @TLSGD operator requires `%rdi' as dest register
+.*:15: Error: @TLSLD operator cannot be used with `add'
+.*:16: Error: @TLSLD operator requires `%rdi' as dest register
+.*:17: Error: @TLSLD operator requires `%rip' as base register
+.*:18: Error: @TLSLD operator requires base register
+.*:19: Error: @TLSLD operator requires `%rip' as base register
+.*:20: Error: @TLSLD operator requires `%rdi' as dest register
+.*:23: Error: @TLSDESC operator cannot be used with `add'
+.*:24: Error: @TLSDESC operator requires `%rip' as base register
+.*:25: Error: @TLSDESC operator requires `%rip' as base register
+.*:27: Error: @TLSDESC operator requires 32-bit or 64-bit dest register
+.*:30: Error: @GOTTPOFF operator cannot be used with `sub'
+.*:31: Error: @GOTTPOFF operator cannot be used with `xor'
+.*:32: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:33: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:34: Error: @GOTTPOFF operator requires 32-bit or 64-bit dest register
+.*:35: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:36: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:37: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:38: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:39: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:40: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:41: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:42: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:43: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:44: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:48: Error: @TLSCALL operator cannot be used with `lea'
+.*:49: Error: @TLSCALL operator requires `%eax/%rax' as base register
+.*:49: Error: 0-byte relocation cannot be applied to 4-byte field
+.*:50: Error: `\*foo@tlscall\(%ax\)' is not a valid base/index expression
diff --git a/gas/testsuite/gas/i386/ilp32/x32-inval-tls.s b/gas/testsuite/gas/i386/ilp32/x32-inval-tls.s
new file mode 100644
index 0000000..b1d967f
--- /dev/null
+++ b/gas/testsuite/gas/i386/ilp32/x32-inval-tls.s
@@ -0,0 +1 @@
+.include "../x86-64-inval-tls.s"
diff --git a/gas/testsuite/gas/i386/ilp32/x32-tls.d b/gas/testsuite/gas/i386/ilp32/x32-tls.d
index ab4da5c..ac7d136 100644
--- a/gas/testsuite/gas/i386/ilp32/x32-tls.d
+++ b/gas/testsuite/gas/i386/ilp32/x32-tls.d
@@ -1,3 +1,4 @@
+#as: -mtls-check=no
 #objdump: -dw
 #name: x86-64 (ILP32) TLS
 
diff --git a/gas/testsuite/gas/i386/ilp32/x86-64-tls.d b/gas/testsuite/gas/i386/ilp32/x86-64-tls.d
new file mode 100644
index 0000000..a2261f5
--- /dev/null
+++ b/gas/testsuite/gas/i386/ilp32/x86-64-tls.d
@@ -0,0 +1,4 @@
+#source: ../x86-64-tls.s
+#objdump: -drw
+#name: x86-64 (ILP32) TLS
+#dump: ../x86-64-tls.d
diff --git a/gas/testsuite/gas/i386/inval-tls.l b/gas/testsuite/gas/i386/inval-tls.l
index 98f7a29..e20ba7a 100644
--- a/gas/testsuite/gas/i386/inval-tls.l
+++ b/gas/testsuite/gas/i386/inval-tls.l
@@ -1,2 +1,70 @@
-.*: Assembler messages:
-.*:3: Error: @GOTNTPOFF operator cannot be used with `kmovd'
+.*ssembler messages:
+.* Error: @GOTNTPOFF operator cannot be used with `kmovd'
+.* Error: @TLSGD operator cannot be used with `add'
+.* Error: @TLSGD operator requires `%ebx' as index register
+.* Error: @TLSGD operator requires scale factor of 1
+.* Error: @TLSGD operator requires no base register
+.*: Error: @TLSGD operator requires `%eax' as dest register
+.*: Error: @TLSGD operator requires `%eax' as dest register
+.*: Error: @TLSGD operator requires `%eax' as base register
+.*: Error: @TLSGD operator requires 32-bit dest register
+.*: Error: @TLSLDM operator cannot be used with `add'
+.*: Error: @TLSLDM operator requires `%eax' as dest register
+.*: Error: @TLSLDM operator requires `%eax' as base register
+.*: Error: @TLSLDM operator requires no SIB
+.*: Error: @TLSLDM operator requires 32-bit dest register
+.*: Error: @TLSDESC operator cannot be used with `add'
+.*: Error: @TLSDESC operator requires `%ebx' as base register
+.*: Error: @TLSDESC operator requires no SIB
+.*: Error: @TLSDESC operator requires 32-bit dest register
+.*: Error: @INDNTPOFF operator cannot be used with `sub'
+.*: Error: @INDNTPOFF operator requires no base/index register
+.*: Error: @INDNTPOFF operator requires no base/index register
+.*: Error: @INDNTPOFF operator requires 32-bit dest register
+.*: Error: @INDNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @INDNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTNTPOFF operator cannot be used with `lea'
+.*: Error: @GOTNTPOFF operator requires base register
+.*: Error: @GOTNTPOFF operator cannot be used with `lea'
+.*: Error: @GOTNTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTNTPOFF operator requires base register
+.*: Error: @GOTNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTNTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTTPOFF operator cannot be used with `lea'
+.*: Error: @GOTTPOFF operator requires base register
+.*: Error: @GOTTPOFF operator requires 32-bit dest register
+.*: Error: @GOTTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `sub', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*: Error: @GOTTPOFF operator requires base register
+.*: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*: Error: @TLSCALL operator cannot be used with `lea'
+.*: Error: @TLSCALL operator requires `%eax/%rax' as base register
+.*: Error: @TLSCALL operator requires no SIB
+.*: Error: 0-byte relocation cannot be applied to 4-byte field
+.*: Error: @TLSCALL operator requires `%eax/%rax' as base register
diff --git a/gas/testsuite/gas/i386/inval-tls.s b/gas/testsuite/gas/i386/inval-tls.s
index ba0e1b8..067e9b4 100644
--- a/gas/testsuite/gas/i386/inval-tls.s
+++ b/gas/testsuite/gas/i386/inval-tls.s
@@ -1,3 +1,85 @@
 	.text
 # All the following should be illegal
 	kmovd	foo@gotntpoff(%eax), %k0
+
+	/* Invalid testcase for R_386_TLS_GD.  */
+	addl foo@tlsgd(,%ebx,1), %eax
+	leal foo@tlsgd(,%ecx,1), %eax
+	leal foo@tlsgd(,%ebx,2), %eax
+	leal foo@tlsgd(%ecx,%ebx,1), %eax
+	leal foo@tlsgd(,%ebx,1), %ecx
+	leal foo@tlsgd(%ebx), %ecx
+	leal foo@tlsgd(%eax), %eax
+	lea foo@tlsgd(%ebx), %ax
+
+	/* Invalid testcase for R_386_TLS_LDM.  */
+	addl foo@tlsldm(%ebx), %eax
+	leal foo@tlsldm(%ebx), %ecx
+	leal foo@tlsldm(%eax), %eax
+	leal foo@tlsldm(,%ebx,1), %eax
+	lea foo@tlsldm(%ebx), %ax
+
+	/* Invalid testcase for R_386_TLS_GOTDESC.  */
+	addl x@tlsdesc(%ebx), %eax
+	leal x@tlsdesc(%ecx), %eax
+	leal x@tlsdesc(,%ecx,1), %eax
+	lea  x@tlsdesc(%ebx), %ax
+
+	/* Invalid testcase for R_386_TLS_IE.  */
+	subl foo@indntpoff, %ecx
+	addl foo@indntpoff(%ebx), %ecx
+	movl foo@indntpoff(%ebx), %ecx
+	add foo@indntpoff, %cx
+	addl $foo@indntpoff, %eax
+	addl %ecx, foo@indntpoff
+	addl $0x9090,foo@indntpoff
+	addl $0x90909090,foo@indntpoff
+	movl foo@indntpoff,%eax
+	movl %edx,foo@indntpoff(%eax)
+	movw %ss,foo@indntpoff(%eax)
+	movw foo@indntpoff(%eax),%ss
+	movl $0x90909090,foo@indntpoff(%eax)
+	movl $foo@indntpoff, %eax
+
+	/* Invalid testcase for R_386_TLS_GOTIE.  */
+	leal foo@gotntpoff(%ebx), %ecx
+	subl foo@gotntpoff(,%ebx,1), %ecx
+	lea foo@gotntpoff(%ebx), %cx
+	subl %ecx, foo@gotntpoff(%ebx)
+	subl $0x9090,foo@gotntpoff(%ebx)
+	subl $0x90909090,foo@gotntpoff(%eax)
+	subl $foo@gotntpoff, %eax
+	addl %ecx, foo@gotntpoff(%ebx)
+	addl $0x9090,foo@gotntpoff(%ebx)
+	addl $0x90909090,foo@gotntpoff(%eax)
+	addl $foo@gotntpoff, %eax
+	movl foo@gotntpoff,%eax
+	movl %edx,foo@gotntpoff(%eax)
+	movw %ss,foo@gotntpoff(%eax)
+	movw foo@gotntpoff(%eax),%ss
+	movl $0x90909090,foo@gotntpoff(%eax)
+	movl $foo@gotntpoff, %eax
+
+	/* Invalid testcase for R_386_TLS_IE_32.  */
+	leal foo@gottpoff(%ebx), %ecx
+	subl foo@gottpoff(,%ebx,1), %ecx
+	add foo@gottpoff(%ebx), %cx
+	subl %ecx, foo@gottpoff(%ebx)
+	subl $0x9090,foo@gottpoff(%ebx)
+	subl $0x90909090,foo@gottpoff(%eax)
+	subl $foo@gottpoff, %eax
+	addl %ecx, foo@gottpoff(%ebx)
+	addl $0x9090,foo@gottpoff(%ebx)
+	addl $0x90909090,foo@gottpoff(%eax)
+	movl foo@gottpoff,%eax
+	movl %edx,foo@gottpoff(%eax)
+	movw %ss,foo@gottpoff(%eax)
+	movw foo@gottpoff(%eax),%ss
+	movl $0x90909090,foo@gottpoff(%eax)
+	movl $foo@gottpoff, %eax
+
+	/* Invalid testcase for R_386_TLS_DESC_CALL.  */
+	leal foo@tlscall(%eax), %ebx
+	call *x@tlscall(%ebx)
+	call *x@tlscall(,%eax,1)
+	call *x@tlscall(%bx)
diff --git a/gas/testsuite/gas/i386/reloc32.d b/gas/testsuite/gas/i386/reloc32.d
index 263a742..7d1b1ba 100644
--- a/gas/testsuite/gas/i386/reloc32.d
+++ b/gas/testsuite/gas/i386/reloc32.d
@@ -1,4 +1,4 @@
-#as: -mrelax-relocations=yes
+#as: -mrelax-relocations=yes -mtls-check=no
 #objdump: -Drw
 #name: i386 relocs
 
diff --git a/gas/testsuite/gas/i386/reloc64.d b/gas/testsuite/gas/i386/reloc64.d
index a96072d..665ede6 100644
--- a/gas/testsuite/gas/i386/reloc64.d
+++ b/gas/testsuite/gas/i386/reloc64.d
@@ -1,4 +1,4 @@
-#as: -mx86-used-note=no --generate-missing-build-notes=no
+#as: -mx86-used-note=no --generate-missing-build-notes=no -mtls-check=no
 #objdump: -Drw
 #name: x86-64 relocs
 #notarget: *-*-solaris*
diff --git a/gas/testsuite/gas/i386/tls.d b/gas/testsuite/gas/i386/tls.d
new file mode 100644
index 0000000..adfe7ce
--- /dev/null
+++ b/gas/testsuite/gas/i386/tls.d
@@ -0,0 +1,25 @@
+#as:
+#objdump: -drw
+#name: Check tls relocation 32 bit-mode
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+00000000 <_start>:
+\s*[a-f0-9]+:\s*8d 04 1d 00 00 00 00[	 ]+lea    0x0\(,%ebx,1\),%eax	3: R_386_TLS_GD	foo
+\s*[a-f0-9]+:\s*8d 81 00 00 00 00[	 ]+lea    0x0\(%ecx\),%eax	9: R_386_TLS_GD	foo
+\s*[a-f0-9]+:\s*8d 83 00 00 00 00[	 ]+lea    0x0\(%ebx\),%eax	f: R_386_TLS_LDM	foo
+\s*[a-f0-9]+:\s*8d 83 00 00 00 00[	 ]+lea    0x0\(%ebx\),%eax	15: R_386_TLS_GOTDESC	x
+\s*[a-f0-9]+:\s*a1 00 00 00 00[	 ]+mov    0x0,%eax	1a: R_386_TLS_IE	foo
+\s*[a-f0-9]+:\s*8b 1d 00 00 00 00[	 ]+mov    0x0,%ebx	20: R_386_TLS_IE	foo
+\s*[a-f0-9]+:\s*03 15 00 00 00 00[	 ]+add    0x0,%edx	26: R_386_TLS_IE	foo
+\s*[a-f0-9]+:\s*2b 8b 00 00 00 00[	 ]+sub    0x0\(%ebx\),%ecx	2c: R_386_TLS_GOTIE	foo
+\s*[a-f0-9]+:\s*8b 8b 00 00 00 00[	 ]+mov    0x0\(%ebx\),%ecx	32: R_386_TLS_GOTIE	foo
+\s*[a-f0-9]+:\s*03 8b 00 00 00 00[	 ]+add    0x0\(%ebx\),%ecx	38: R_386_TLS_GOTIE	foo
+\s*[a-f0-9]+:\s*2b 8b 00 00 00 00[	 ]+sub    0x0\(%ebx\),%ecx	3e: R_386_TLS_IE_32	foo
+\s*[a-f0-9]+:\s*8b 8b 00 00 00 00[	 ]+mov    0x0\(%ebx\),%ecx	44: R_386_TLS_IE_32	foo
+\s*[a-f0-9]+:\s*03 8b 00 00 00 00[	 ]+add    0x0\(%ebx\),%ecx	4a: R_386_TLS_IE_32	foo
+\s*[a-f0-9]+:\s*ff 10[	 ]+call   \*\(%eax\)	4e: R_386_TLS_DESC_CALL	foo
+#pass
diff --git a/gas/testsuite/gas/i386/tls.s b/gas/testsuite/gas/i386/tls.s
new file mode 100644
index 0000000..6c077ee
--- /dev/null
+++ b/gas/testsuite/gas/i386/tls.s
@@ -0,0 +1,31 @@
+# Check tls relocation 32-bit mode
+
+	.text
+_start:
+	/* R_386_TLS_GD.  */
+	leal foo@tlsgd(,%ebx,1), %eax
+	leal foo@tlsgd(%ecx), %eax
+
+	/* R_386_TLS_LDM.  */
+	leal foo@tlsldm(%ebx), %eax
+
+	/* R_386_TLS_GOTDESC.  */
+	leal x@tlsdesc(%ebx), %eax
+
+	/* R_386_TLS_IE.  */
+	movl foo@indntpoff, %eax
+	movl foo@indntpoff, %ebx
+	addl foo@indntpoff, %edx
+
+	/* R_386_TLS_GOTIE.  */
+	subl foo@gotntpoff(%ebx), %ecx
+	movl foo@gotntpoff(%ebx), %ecx
+	addl foo@gotntpoff(%ebx), %ecx
+
+	/* R_386_TLS_IE_32.  */
+	subl foo@gottpoff(%ebx), %ecx
+	movl foo@gottpoff(%ebx), %ecx
+	addl foo@gottpoff(%ebx), %ecx
+
+	/* R_386_TLS_DESC_CALL.  */
+	call *foo@tlscall(%eax)
diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.l b/gas/testsuite/gas/i386/x86-64-inval-tls.l
index da8ac19..134d96b 100644
--- a/gas/testsuite/gas/i386/x86-64-inval-tls.l
+++ b/gas/testsuite/gas/i386/x86-64-inval-tls.l
@@ -1,3 +1,39 @@
 .*: Assembler messages:
 .*:3: Error: @GOTTPOFF operator cannot be used with `kmovq'
 .*:4: Error: @TLSLD operator cannot be used with `kmovq'
+.*:7: Error: @TLSGD operator cannot be used with `add'
+.*:8: Error: @TLSGD operator requires `%rdi' as dest register
+.*:9: Error: @TLSGD operator requires `%rip' as base register
+.*:10: Error: @TLSGD operator requires base register
+.*:11: Error: @TLSGD operator requires `%rip' as base register
+.*:12: Error: @TLSGD operator requires `%rdi' as dest register
+.*:15: Error: @TLSLD operator cannot be used with `add'
+.*:16: Error: @TLSLD operator requires `%rdi' as dest register
+.*:17: Error: @TLSLD operator requires `%rip' as base register
+.*:18: Error: @TLSLD operator requires base register
+.*:19: Error: @TLSLD operator requires `%rip' as base register
+.*:20: Error: @TLSLD operator requires `%rdi' as dest register
+.*:23: Error: @TLSDESC operator cannot be used with `add'
+.*:24: Error: @TLSDESC operator requires `%rip' as base register
+.*:25: Error: @TLSDESC operator requires `%rip' as base register
+.*:26: Error: @TLSDESC operator requires 64-bit dest register
+.*:27: Error: @TLSDESC operator requires 64-bit dest register
+.*:30: Error: @GOTTPOFF operator cannot be used with `sub'
+.*:31: Error: @GOTTPOFF operator cannot be used with `xor'
+.*:32: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:33: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:34: Error: @GOTTPOFF operator requires 64-bit dest register
+.*:35: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:36: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:37: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:38: Error: @GOTTPOFF operator can be used with `add', but format is wrong
+.*:39: Error: @GOTTPOFF operator requires `%rip' as base register
+.*:40: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:41: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:42: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:43: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:44: Error: @GOTTPOFF operator can be used with `mov', but format is wrong
+.*:48: Error: @TLSCALL operator cannot be used with `lea'
+.*:49: Error: @TLSCALL operator requires `%eax/%rax' as base register
+.*:49: Error: 0-byte relocation cannot be applied to 4-byte field
+.*:50: Error: `\*foo@tlscall\(%ax\)' is not a valid base/index expression
diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.s b/gas/testsuite/gas/i386/x86-64-inval-tls.s
index 71e1927..8783530 100644
--- a/gas/testsuite/gas/i386/x86-64-inval-tls.s
+++ b/gas/testsuite/gas/i386/x86-64-inval-tls.s
@@ -2,3 +2,49 @@
 # All the following should be illegal
 	kmovq	foo@gottpoff(%rip), %k0
 	kmovq	foo@tlsld(%rip), %k0
+
+	/* Invalid testcase for R_X86_64_TLSGD.  */
+	addq foo@tlsgd(%rip), %rdi
+	leaq foo@tlsgd(%rip), %rax
+	leaq foo@tlsgd(%rax), %rdi
+	leaq foo@tlsgd(,%rax,1), %rdi
+	leaq foo@tlsgd(%eip), %rdi
+	leal foo@tlsgd(%rip), %edi
+
+	/* Invalid testcase for R_X86_64_TLSLD.  */
+	addq foo@tlsld(%rip), %rdi
+	leaq foo@tlsld(%rip), %rax
+	leaq foo@tlsld(%rax), %rdi
+	leaq foo@tlsld(,%rax,1), %rdi
+	leaq foo@tlsld(%eip), %rdi
+	leal foo@tlsld(%rip), %edi
+
+	/* Invalid testcase for R_X86_64_GOTPC32_TLSDESC.  */
+	addq x@tlsdesc(%rip), %rax
+	leaq x@tlsdesc(%rbx), %rax
+	lea  x@tlsdesc(%eip), %rdi
+	lea  x@tlsdesc(%rip), %eax
+	lea  x@tlsdesc(%rip), %ax
+
+	/* Invalid testcase for R_X86_64_GOTTPOFF.  */
+	subq foo@gottpoff(%rip), %r12
+	xorq foo@gottpoff(%rip), %rax
+	addq foo@gottpoff(%rbx), %rax
+	addq foo@gottpoff(%eip), %rax
+	add  foo@gottpoff(%rip), %ax
+	addq %rax, foo@gottpoff(%rip)
+	addl $0x90909090, foo@gottpoff(%rip)
+	add  $0x90, foo@gottpoff(%rip), %rax
+	add  $0xffffffffffffffff, foo@gottpoff(%rip), %rax
+	movq foo@gottpoff(%rbx), %rax
+	movq %rax, foo@gottpoff(%rip)
+	mov  %ss,foo@gottpoff(%rip)
+	mov  foo@gottpoff(%rip),%ss
+	movl $0x90909090,foo@gottpoff(%rip)
+	mov  $foo@gottpoff, %rax
+
+
+	/* Invalid testcase for R_X86_64_TLSDESC_CALL.  */
+	leaq foo@tlscall(%rax), %rbx
+	call *foo@tlscall(%rip)
+	call *foo@tlscall(%ax)
diff --git a/gas/testsuite/gas/i386/x86-64-tls.d b/gas/testsuite/gas/i386/x86-64-tls.d
new file mode 100644
index 0000000..1c30496
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-tls.d
@@ -0,0 +1,25 @@
+#as:
+#objdump: -drw
+#name: Check tls relocation x86-64
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <_start>:
+ +[a-f0-9]+:	48 8d 3d 00 00 00 00 	lea    0x0\(%rip\),%rdi        # 7 <_start\+0x7>	3: R_X86_64_TLSGD	foo-0x4
+ +[a-f0-9]+:	48 8d 3d 00 00 00 00 	lea    0x0\(%rip\),%rdi        # e <_start\+0xe>	a: R_X86_64_TLSLD	foo-0x4
+ +[a-f0-9]+:	48 8d 05 00 00 00 00 	lea    0x0\(%rip\),%rax        # 15 <_start\+0x15>	11: R_X86_64_GOTPC32_TLSDESC	x-0x4
+ +[a-f0-9]+:	4c 03 25 00 00 00 00 	add    0x0\(%rip\),%r12        # 1c <_start\+0x1c>	18: R_X86_64_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	48 8b 05 00 00 00 00 	mov    0x0\(%rip\),%rax        # 23 <_start\+0x23>	1f: R_X86_64_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	d5 48 03 05 00 00 00 00 	add    0x0\(%rip\),%r16        # 2b <_start\+0x2b>	27: R_X86_64_CODE_4_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	d5 48 8b 25 00 00 00 00 	mov    0x0\(%rip\),%r20        # 33 <_start\+0x33>	2f: R_X86_64_CODE_4_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	62 64 bc 18 01 35 00 00 00 00 	add    %r30,0x0\(%rip\),%r8        # 3d <_start\+0x3d>	39: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	62 f4 dc 10 03 05 00 00 00 00 	add    0x0\(%rip\),%rax,%r20        # 47 <_start\+0x47>	43: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	62 e4 fc 0c 03 05 00 00 00 00 	\{nf\} add 0x0\(%rip\),%r16        # 51 <_start\+0x51>	4d: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	62 64 bc 1c 01 35 00 00 00 00 	\{nf\} add %r30,0x0\(%rip\),%r8        # 5b <_start\+0x5b>	57: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	62 f4 dc 14 03 05 00 00 00 00 	\{nf\} add 0x0\(%rip\),%rax,%r20        # 65 <_start\+0x65>	61: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+ +[a-f0-9]+:	ff 10                	call   \*\(%rax\)	65: R_X86_64_TLSDESC_CALL	x
+ +[a-f0-9]+:	67 ff 10             	call   \*\(%eax\)	67: R_X86_64_TLSDESC_CALL	x
+#pass
diff --git a/gas/testsuite/gas/i386/x86-64-tls.s b/gas/testsuite/gas/i386/x86-64-tls.s
new file mode 100644
index 0000000..7c3bd4a
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-tls.s
@@ -0,0 +1,27 @@
+# Check tls relocation 64-bit mode
+
+	.text
+_start:
+	/* R_X86_64_TLSGD.  */
+	leaq foo@tlsgd(%rip), %rdi
+
+	/* R_X86_64_TLSLD.  */
+	leaq foo@tlsld(%rip), %rdi
+
+	/* R_X86_64_GOTPC32_TLSDESC.  */
+	leaq     x@tlsdesc(%rip), %rax
+
+	/* R_X86_64_GOTTPOFF.  */
+	addq     foo@gottpoff(%rip), %r12
+	movq     foo@gottpoff(%rip), %rax
+	addq     foo@gottpoff(%rip), %r16
+	movq     foo@gottpoff(%rip), %r20
+	addq     %r30, foo@gottpoff(%rip), %r8
+	addq     foo@gottpoff(%rip), %rax, %r20
+	{nf} addq foo@gottpoff(%rip), %r16
+	{nf} addq %r30, foo@gottpoff(%rip), %r8
+	{nf} addq foo@gottpoff(%rip), %rax, %r20
+
+	/* R_X86_64_TLSDESC_CALL.  */
+	call *x@tlscall(%rax)
+	call *x@tlscall(%eax)
diff --git a/gas/testsuite/gas/i386/x86-64.exp b/gas/testsuite/gas/i386/x86-64.exp
index 86e7f4a..740f526 100644
--- a/gas/testsuite/gas/i386/x86-64.exp
+++ b/gas/testsuite/gas/i386/x86-64.exp
@@ -653,7 +653,8 @@
     run_dump_test "x86-64-unwind"
 
     run_dump_test "reloc64"
-    run_list_test "reloc64" "--defsym _bad_=1"
+    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
+    run_dump_test "x86-64-tls"
     run_list_test "x86-64-inval-tls"
     run_dump_test "mixed-mode-reloc64"
     run_dump_test "rela"
diff --git a/ld/testsuite/ld-i386/tlsgdesc1.d b/ld/testsuite/ld-i386/tlsgdesc1.d
index 2a70e81..0c853ab 100644
--- a/ld/testsuite/ld-i386/tlsgdesc1.d
+++ b/ld/testsuite/ld-i386/tlsgdesc1.d
@@ -1,4 +1,4 @@
 #name: TLS GDesc->LE transition check (LEA)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_GOTDESC against `foo' must be used in LEA only
diff --git a/ld/testsuite/ld-i386/tlsgdesc2.d b/ld/testsuite/ld-i386/tlsgdesc2.d
index bbf93be..99e1b18 100644
--- a/ld/testsuite/ld-i386/tlsgdesc2.d
+++ b/ld/testsuite/ld-i386/tlsgdesc2.d
@@ -1,4 +1,4 @@
 #name: TLS GDesc->LE transition check (indirect CALL)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_DESC_CALL against `foo' must be used in indirect CALL with EAX register only
diff --git a/ld/testsuite/ld-i386/tlsgdesc3.d b/ld/testsuite/ld-i386/tlsgdesc3.d
index f2c29d8..4bb99c4 100644
--- a/ld/testsuite/ld-i386/tlsgdesc3.d
+++ b/ld/testsuite/ld-i386/tlsgdesc3.d
@@ -1,5 +1,5 @@
 #source: tlsgdesc2.s
 #name: TLS GDesc call (indirect CALL)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -shared -melf_i386
 #error: .*: relocation R_386_TLS_DESC_CALL against `foo' must be used in indirect CALL with EAX register only
diff --git a/ld/testsuite/ld-i386/tlsie2.d b/ld/testsuite/ld-i386/tlsie2.d
index 9f9e630..4e7dc6e 100644
--- a/ld/testsuite/ld-i386/tlsie2.d
+++ b/ld/testsuite/ld-i386/tlsie2.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (R_386_TLS_GOTIE with %eax)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_GOTIE against `foo' must be used in ADD, SUB or MOV only
diff --git a/ld/testsuite/ld-i386/tlsie3.d b/ld/testsuite/ld-i386/tlsie3.d
index 506f1a0..6bfc78e 100644
--- a/ld/testsuite/ld-i386/tlsie3.d
+++ b/ld/testsuite/ld-i386/tlsie3.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (R_386_TLS_GOTIE)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_GOTIE against `foo' must be used in ADD, SUB or MOV only
diff --git a/ld/testsuite/ld-i386/tlsie4.d b/ld/testsuite/ld-i386/tlsie4.d
index a516d00..98293f4 100644
--- a/ld/testsuite/ld-i386/tlsie4.d
+++ b/ld/testsuite/ld-i386/tlsie4.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (R_386_TLS_IE with %eax)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_IE against `foo' must be used in ADD or MOV only
diff --git a/ld/testsuite/ld-i386/tlsie5.d b/ld/testsuite/ld-i386/tlsie5.d
index d344718..4e9c9a8 100644
--- a/ld/testsuite/ld-i386/tlsie5.d
+++ b/ld/testsuite/ld-i386/tlsie5.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (R_386_TLS_IE)
-#as: --32
+#as: --32 -mtls-check=no
 #ld: -melf_i386
 #error: .*: relocation R_386_TLS_IE against `foo' must be used in ADD or MOV only
diff --git a/ld/testsuite/ld-x86-64/tlsdesc3.d b/ld/testsuite/ld-x86-64/tlsdesc3.d
index bbf22eb..9558848 100644
--- a/ld/testsuite/ld-x86-64/tlsdesc3.d
+++ b/ld/testsuite/ld-x86-64/tlsdesc3.d
@@ -1,4 +1,4 @@
 #name: TLS GDesc->LE transition check (LEA)
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -melf_x86_64
 #error: .*: relocation R_X86_64_GOTPC32_TLSDESC against `foo' must be used in LEA only
diff --git a/ld/testsuite/ld-x86-64/tlsdesc4.d b/ld/testsuite/ld-x86-64/tlsdesc4.d
index c882c87..ccaa525 100644
--- a/ld/testsuite/ld-x86-64/tlsdesc4.d
+++ b/ld/testsuite/ld-x86-64/tlsdesc4.d
@@ -1,4 +1,4 @@
 #name: TLS GDesc->LE transition check (indirect CALL)
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -melf_x86_64
 #error: .*: relocation R_X86_64_TLSDESC_CALL against `foo' must be used in indirect CALL with RAX register only
diff --git a/ld/testsuite/ld-x86-64/tlsdesc5.d b/ld/testsuite/ld-x86-64/tlsdesc5.d
index 6a0158b..0876993 100644
--- a/ld/testsuite/ld-x86-64/tlsdesc5.d
+++ b/ld/testsuite/ld-x86-64/tlsdesc5.d
@@ -1,5 +1,5 @@
 #source: tlsdesc4.s
 #name: TLS GDesc call (indirect CALL)
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -shared -melf_x86_64
 #error: .*: relocation R_X86_64_TLSDESC_CALL against `foo' must be used in indirect CALL with RAX register only
diff --git a/ld/testsuite/ld-x86-64/tlsie2.d b/ld/testsuite/ld-x86-64/tlsie2.d
index bf8a819..2e6d41c 100644
--- a/ld/testsuite/ld-x86-64/tlsie2.d
+++ b/ld/testsuite/ld-x86-64/tlsie2.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -melf_x86_64
 #error: .*: relocation R_X86_64_GOTTPOFF against `foo' must be used in ADD or MOV only
diff --git a/ld/testsuite/ld-x86-64/tlsie3.d b/ld/testsuite/ld-x86-64/tlsie3.d
index 49d8464..b59cc64 100644
--- a/ld/testsuite/ld-x86-64/tlsie3.d
+++ b/ld/testsuite/ld-x86-64/tlsie3.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (%r12)
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -melf_x86_64
 #error: .*: relocation R_X86_64_GOTTPOFF against `foo' must be used in ADD or MOV only
diff --git a/ld/testsuite/ld-x86-64/tlsie5.d b/ld/testsuite/ld-x86-64/tlsie5.d
index 29de1ce..d7ab5ab 100644
--- a/ld/testsuite/ld-x86-64/tlsie5.d
+++ b/ld/testsuite/ld-x86-64/tlsie5.d
@@ -1,4 +1,4 @@
 #name: TLS IE->LE transition check (APX)
-#as: --64
+#as: --64 -mtls-check=no
 #ld: -melf_x86_64
 #error: .*: relocation R_X86_64_CODE_6_GOTTPOFF against `foo' must be used in ADD only