[v2] x86: Add tls check in gas

Message ID 20240905085750.2339224-1-lili.cui@intel.com
State New
Headers
Series [v2] x86: Add tls check in gas |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Cui, Lili Sept. 5, 2024, 8:57 a.m. UTC
  Sorry, added v2 to the patch name

---
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. 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.

---
V2:
1. Add format checks for each operator. And add a function to report errors.
2. Replace DEFAULT_TLS_CHECK with DEFAULT_X86_TLS_CHECK, and all related names.
3. Add more invalid test cases.
4. Remove the handling of "call *x@tlsdesc(%rax)", which is now illegal.
5. Add new INSTANCE RegDI
6. Use option drw for objdump.
---

gas/ChangeLog:

        PR gas/32022
        * config.in: Regenerate.
        * config/tc-i386.c (_BFD_MAKE_TABLE_bfd_reloc_code_real): New.
        (x86_check_tls_relocation): Added a new function to check tls relocation.
        (i386_assemble): Handle x86_check_tls_relocation.
        (OPTION_MTLS_CHECK): Added a new option to contrl tls check.
        (struct option): Ditto.
        (md_parse_option): Ditto.
        (md_show_usage): Ditto.
        * configure: Added a new option to check tls relocation by default.
        * configure.ac: Ditto.
        * doc/c-i386.texi: Ditto.
        * 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/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/tls.d: New test.
        * testsuite/gas/i386/tls.s: Ditto.
        * testsuite/gas/i386/x86-64-tls.d: Ditto.
        * testsuite/gas/i386/x86-64-tls.s: Ditto.

opcodes/ChangeLog:

	PR gas/32022
	* i386-gen.c: Added new INSTANCE RegDI.
	* i386-opc.h (enum operand_instance): Ditto.
	* i386-reg.tbl: Added RegDI to rdi.

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.
---
 gas/config.in                             |   3 +
 gas/config/tc-i386.c                      | 348 ++++++++++++++++++++--
 gas/configure                             |  26 +-
 gas/configure.ac                          |  18 ++
 gas/doc/c-i386.texi                       |  10 +
 gas/testsuite/gas/i386/i386.exp           |   3 +-
 gas/testsuite/gas/i386/ilp32/ilp32.exp    |   2 +-
 gas/testsuite/gas/i386/ilp32/reloc64.d    |   2 +-
 gas/testsuite/gas/i386/inval-tls.l        |  28 ++
 gas/testsuite/gas/i386/inval-tls.s        |  42 +++
 gas/testsuite/gas/i386/reloc32.d          |   2 +-
 gas/testsuite/gas/i386/reloc64.d          |   2 +-
 gas/testsuite/gas/i386/tls.d              |  25 ++
 gas/testsuite/gas/i386/tls.s              |  31 ++
 gas/testsuite/gas/i386/x86-64-inval-tls.l |  17 ++
 gas/testsuite/gas/i386/x86-64-inval-tls.s |  26 ++
 gas/testsuite/gas/i386/x86-64-tls.d       |  29 ++
 gas/testsuite/gas/i386/x86-64-tls.s       |  31 ++
 gas/testsuite/gas/i386/x86-64.exp         |   3 +-
 ld/testsuite/ld-i386/tlsgdesc1.d          |   2 +-
 ld/testsuite/ld-i386/tlsgdesc2.d          |   2 +-
 ld/testsuite/ld-i386/tlsgdesc3.d          |   2 +-
 ld/testsuite/ld-i386/tlsie2.d             |   2 +-
 ld/testsuite/ld-i386/tlsie3.d             |   2 +-
 ld/testsuite/ld-i386/tlsie4.d             |   2 +-
 ld/testsuite/ld-i386/tlsie5.d             |   2 +-
 ld/testsuite/ld-x86-64/tlsdesc3.d         |   2 +-
 ld/testsuite/ld-x86-64/tlsdesc4.d         |   2 +-
 ld/testsuite/ld-x86-64/tlsdesc5.d         |   2 +-
 ld/testsuite/ld-x86-64/tlsie2.d           |   2 +-
 ld/testsuite/ld-x86-64/tlsie3.d           |   2 +-
 ld/testsuite/ld-x86-64/tlsie5.d           |   2 +-
 opcodes/i386-gen.c                        |   1 +
 opcodes/i386-opc.h                        |   1 +
 opcodes/i386-reg.tbl                      |   2 +-
 35 files changed, 634 insertions(+), 44 deletions(-)
 create mode 100644 gas/testsuite/gas/i386/tls.d
 create mode 100644 gas/testsuite/gas/i386/tls.s
 create mode 100644 gas/testsuite/gas/i386/x86-64-tls.d
 create mode 100644 gas/testsuite/gas/i386/x86-64-tls.s
  

Comments

H.J. Lu Sept. 6, 2024, 12:34 p.m. UTC | #1
On Thu, Sep 5, 2024 at 1:57 AM Cui, Lili <lili.cui@intel.com> wrote:
>
> Sorry, added v2 to the patch name
>
> ---
> 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. 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.
>
> ---
> V2:
> 1. Add format checks for each operator. And add a function to report errors.
> 2. Replace DEFAULT_TLS_CHECK with DEFAULT_X86_TLS_CHECK, and all related names.
> 3. Add more invalid test cases.
> 4. Remove the handling of "call *x@tlsdesc(%rax)", which is now illegal.
> 5. Add new INSTANCE RegDI
> 6. Use option drw for objdump.
> ---
>
> gas/ChangeLog:
>
>         PR gas/32022
>         * config.in: Regenerate.
>         * config/tc-i386.c (_BFD_MAKE_TABLE_bfd_reloc_code_real): New.
>         (x86_check_tls_relocation): Added a new function to check tls relocation.
>         (i386_assemble): Handle x86_check_tls_relocation.
>         (OPTION_MTLS_CHECK): Added a new option to contrl tls check.
>         (struct option): Ditto.
>         (md_parse_option): Ditto.
>         (md_show_usage): Ditto.
>         * configure: Added a new option to check tls relocation by default.
>         * configure.ac: Ditto.
>         * doc/c-i386.texi: Ditto.
>         * 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/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/tls.d: New test.
>         * testsuite/gas/i386/tls.s: Ditto.
>         * testsuite/gas/i386/x86-64-tls.d: Ditto.
>         * testsuite/gas/i386/x86-64-tls.s: Ditto.
>
> opcodes/ChangeLog:
>
>         PR gas/32022
>         * i386-gen.c: Added new INSTANCE RegDI.
>         * i386-opc.h (enum operand_instance): Ditto.
>         * i386-reg.tbl: Added RegDI to rdi.
>
> 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.
> ---
>  gas/config.in                             |   3 +
>  gas/config/tc-i386.c                      | 348 ++++++++++++++++++++--
>  gas/configure                             |  26 +-
>  gas/configure.ac                          |  18 ++
>  gas/doc/c-i386.texi                       |  10 +
>  gas/testsuite/gas/i386/i386.exp           |   3 +-
>  gas/testsuite/gas/i386/ilp32/ilp32.exp    |   2 +-
>  gas/testsuite/gas/i386/ilp32/reloc64.d    |   2 +-
>  gas/testsuite/gas/i386/inval-tls.l        |  28 ++
>  gas/testsuite/gas/i386/inval-tls.s        |  42 +++
>  gas/testsuite/gas/i386/reloc32.d          |   2 +-
>  gas/testsuite/gas/i386/reloc64.d          |   2 +-
>  gas/testsuite/gas/i386/tls.d              |  25 ++
>  gas/testsuite/gas/i386/tls.s              |  31 ++
>  gas/testsuite/gas/i386/x86-64-inval-tls.l |  17 ++
>  gas/testsuite/gas/i386/x86-64-inval-tls.s |  26 ++
>  gas/testsuite/gas/i386/x86-64-tls.d       |  29 ++
>  gas/testsuite/gas/i386/x86-64-tls.s       |  31 ++
>  gas/testsuite/gas/i386/x86-64.exp         |   3 +-
>  ld/testsuite/ld-i386/tlsgdesc1.d          |   2 +-
>  ld/testsuite/ld-i386/tlsgdesc2.d          |   2 +-
>  ld/testsuite/ld-i386/tlsgdesc3.d          |   2 +-
>  ld/testsuite/ld-i386/tlsie2.d             |   2 +-
>  ld/testsuite/ld-i386/tlsie3.d             |   2 +-
>  ld/testsuite/ld-i386/tlsie4.d             |   2 +-
>  ld/testsuite/ld-i386/tlsie5.d             |   2 +-
>  ld/testsuite/ld-x86-64/tlsdesc3.d         |   2 +-
>  ld/testsuite/ld-x86-64/tlsdesc4.d         |   2 +-
>  ld/testsuite/ld-x86-64/tlsdesc5.d         |   2 +-
>  ld/testsuite/ld-x86-64/tlsie2.d           |   2 +-
>  ld/testsuite/ld-x86-64/tlsie3.d           |   2 +-
>  ld/testsuite/ld-x86-64/tlsie5.d           |   2 +-
>  opcodes/i386-gen.c                        |   1 +
>  opcodes/i386-opc.h                        |   1 +
>  opcodes/i386-reg.tbl                      |   2 +-
>  35 files changed, 634 insertions(+), 44 deletions(-)
>  create mode 100644 gas/testsuite/gas/i386/tls.d
>  create mode 100644 gas/testsuite/gas/i386/tls.s
>  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.d
>  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.s
>
> diff --git a/gas/config.in b/gas/config.in
> index a1f83499332..3f199d5b4a6 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 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 a9d3536429b..a0222580106 100644
> --- a/gas/config/tc-i386.c
> +++ b/gas/config/tc-i386.c
> @@ -274,6 +274,23 @@ enum i386_error
>      internal_error,
>    };
>
> +enum x86_tls_error_type
> +{
> +  x86_tls_error_none,
> +  x86_tls_error_insn,
> +  x86_tls_error_sib,
> +  x86_tls_error_no_base_reg,
> +  x86_tls_error_require_no_base_index_reg,
> +  x86_tls_error_base_reg_name,
> +  x86_tls_error_index_RegB,
> +  x86_tls_error_RegA,
> +  x86_tls_error_RegB,
> +  x86_tls_error_RegIP,
> +  x86_tls_error_dest_RegA,
> +  x86_tls_error_dest_RegDI,
> +  x86_tls_error_scale_factor
> +};
> +
>  struct _i386_insn
>    {
>      /* TM holds the template for the insn were currently assembling.  */
> @@ -717,6 +734,9 @@ lfence_before_ret;
>  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,
> @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const insn_template *t)
>                && (t->base_opcode | 8) == 0x2c);
>  }
>
> +static enum x86_tls_error_type
> +x86_check_tls_relocation (unsigned int r_type)
> +{
> +  switch (r_type)
> +    {
> +    case BFD_RELOC_386_TLS_GOTDESC:
> +      /* Check transition from GDesc access model:
> +
> +        leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> +                                       SIB is not supported.
> +       */
> +      if (i.tm.mnem_off != MN_lea)
> +       return x86_tls_error_insn;
> +      else if (i.index_reg)
> +       return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> +       return x86_tls_error_RegB;
> +      break;
> +
> +    case BFD_RELOC_386_TLS_GD:
> +      /* Check transition from GD access model:
> +
> +        leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
> +        leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> +                                            Memory reg can't be %eax.
> +       */
> +      if (i.tm.mnem_off != MN_lea)
> +       return x86_tls_error_insn;
> +      else if (i.index_reg)
> +       {
> +         if (i.base_reg)
> +           return x86_tls_error_base_reg_name;
> +         if (i.index_reg->reg_type.bitfield.instance != RegB)
> +           return x86_tls_error_index_RegB;
> +         else if (i.types[1].bitfield.instance != Accum)
> +           return x86_tls_error_dest_RegA;
> +         else 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_RegA;
> +         else if (i.types[1].bitfield.instance != Accum)
> +           return x86_tls_error_dest_RegA;
> +       }
> +      break;
> +
> +    case BFD_RELOC_386_TLS_LDM:
> +      /*  Check transition from LDM access model:
> +
> +         leal foo@tlsldm(%reg), %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;
> +      else if (i.index_reg)
> +       return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> +       return x86_tls_error_RegA;
> +      else if (i.types[1].bitfield.instance != Accum)
> +       return x86_tls_error_dest_RegA;
> +      break;
> +
> +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> +      /* Check transition from GOTPC32 TLSDESC access model:
> +
> +        --- LP64 mode ---
> +        leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> +
> +        --- X32 mode ---
> +        rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> +
> +       */
> +      if (i.tm.mnem_off != MN_lea)
> +       return x86_tls_error_insn;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +       return x86_tls_error_RegIP;
> +      break;
> +
> +    case BFD_RELOC_X86_64_TLSGD:
> +      /* Check transition from GD access model:
> +
> +        leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> +       */
> +    case BFD_RELOC_X86_64_TLSLD:
> +      /* Check transition from 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;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +       return x86_tls_error_RegIP;
> +      else if (i.types[1].bitfield.instance != RegDI)
> +       return x86_tls_error_dest_RegDI;
> +      break;
> +
> +    case BFD_RELOC_386_TLS_GOTIE:
> +      /* Check transition from 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 transition from 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;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.index_reg)
> +       return x86_tls_error_sib;
> +      break;
> +
> +    case BFD_RELOC_386_TLS_IE:
> +      /* Check transition from IE access model:
> +
> +        movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> +        addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> +       */
> +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> +       return x86_tls_error_insn;
> +      else if (i.base_reg || i.index_reg)
> +       return x86_tls_error_require_no_base_index_reg;
> +      break;
> +
> +    case BFD_RELOC_X86_64_GOTTPOFF:
> +      /* Check transition from GOTTPOFF access model:
> +
> +        mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> +        add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> +       */
> +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> +       return x86_tls_error_insn;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +       return x86_tls_error_RegIP;
> +      break;
> +
> +    case BFD_RELOC_386_TLS_DESC_CALL:
> +      /* Check transition from GDesc access model:
> +
> +        call *x@tlscall(%eax) --> Memory reg must be %eax and
> +                                  SIB is not supported.
> +       */
> +      if (i.tm.mnem_off != MN_call)
> +       return x86_tls_error_insn;
> +      else if (i.index_reg)
> +       return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> +       return x86_tls_error_RegA;
> +      break;
> +
> +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> +      /* Check transition from 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;
> +      else if (i.index_reg)
> +       return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +       return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> +       return x86_tls_error_RegA;
> +      break;
> +
> +    default:
> +      return x86_tls_error_none;
> +    }
> +
> +  return x86_tls_error_none;
> +}
> +
> +static void
> +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int 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_sib:
> +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_no_base_reg:
> +      as_bad (_("@%s operator must have base regster"), gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_require_no_base_index_reg:
> +      as_bad (_("@%s operator should not have base/index register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_base_reg_name:
> +      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_index_RegB:
> +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_RegA:
> +      as_bad (_("@%s operator only supports eax/rax as base register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_RegB:
> +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_RegIP:
> +      as_bad (_("@%s operator only supports rip as base register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_dest_RegA:
> +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_dest_RegDI:
> +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> +             gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_scale_factor:
> +      as_bad (_("@%s operator only supports scale factor of 00"),
> +             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.  */
> @@ -6656,6 +6954,14 @@ i386_assemble (char *line)
>         i.prefix[LOCK_PREFIX] = 0;
>      }
>
> +  if (tls_check)
> +    {
> +      enum x86_tls_error_type tls_error;
> +      for (j = 0; j < i.operands; ++j)
> +       if ((tls_error = x86_check_tls_relocation (i.reloc[j])) != x86_tls_error_none)
> +         x86_report_tls_error (tls_error, i.reloc[j]);
> +    }
> +
>    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)
> @@ -6666,28 +6972,6 @@ i386_assemble (char *line)
>           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.  */
> @@ -16184,6 +16468,7 @@ const char *md_shortopts = "qnO::";
>  #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[] =
>  {
> @@ -16230,6 +16515,7 @@ struct option md_longopts[] =
>    {"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);
> @@ -16786,6 +17072,14 @@ md_parse_option (int c, const char *arg)
>           optimize_for_space = 0;
>         }
>        break;
> +    case OPTION_MTLS_CHECK:
> +      if (strcasecmp (arg, "yes") == 0)
> +       tls_check = 1;
> +      else if (strcasecmp (arg, "no") == 0)
> +       tls_check = 0;
> +      else
> +       as_fatal (_("invalid -mtls-check= option: `%s'"), arg);
> +      break;
>
>      default:
>        return 0;
> @@ -17028,6 +17322,16 @@ md_show_usage (FILE *stream)
>      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 6b96d3a4e0c..a4cdeb80900 100755
> --- a/gas/configure
> +++ b/gas/configure
> @@ -818,6 +818,7 @@ enable_checking
>  enable_compressed_debug_sections
>  enable_default_compressed_debug_sections_algorithm
>  enable_x86_relax_relocations
> +enable_tls_check
>  enable_elf_stt_common
>  enable_generate_build_notes
>  enable_mips_fix_loongson3_llsc
> @@ -1493,6 +1494,7 @@ Optional Features:
>                            --enable-compressed-debug-sections.
>    --enable-x86-relax-relocations
>                            generate x86 relax relocations by default
> +  --enable-tls-check      check 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 @@ else
>    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 @@ else
>    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 @@ if test "${enable_x86_relax_relocations+set}" = set; then :
>  esac
>  fi
>
> +# PR gas/32022
> +# Decide if check tls relocation.
> +ac_default_x86_tls_check=unset
> +# Provide a configure time option to override our default.
> +# Check whether --enable-tls_check was given.
> +if test "${enable_tls_check+set}" = set; then :
> +  enableval=$enable_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 @@ cat >>confdefs.h <<_ACEOF
>  _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 6b978aae3f7..5fe57e52746 100644
> --- a/gas/configure.ac
> +++ b/gas/configure.ac
> @@ -95,6 +95,17 @@ AC_ARG_ENABLE(x86_relax_relocations,
>    no)  ac_default_x86_relax_relocations=0 ;;
>  esac])dnl
>
> +# PR gas/32022
> +# Decide if check tls relocation.
> +ac_default_x86_tls_check=unset
> +# Provide a configure time option to override our default.
> +AC_ARG_ENABLE(tls_check,
> +             AS_HELP_STRING([--enable-tls-check],
> +             [check 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_DEFINE_UNQUOTED(DEFAULT_GENERATE_X86_RELAX_RELOCATIONS,
>    $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 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 a9e43560aea..5da673a0287 100644
> --- a/gas/doc/c-i386.texi
> +++ b/gas/doc/c-i386.texi
> @@ -467,6 +467,16 @@ R_X86_64_REX_GOTPCRELX, in 64-bit mode.
>  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-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 75ad061b32c..6710a56d64a 100644
> --- a/gas/testsuite/gas/i386/i386.exp
> +++ b/gas/testsuite/gas/i386/i386.exp
> @@ -699,10 +699,11 @@ if [gas_32_check] then {
>         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 a3017388934..b32da8725b5 100644
> --- a/gas/testsuite/gas/i386/ilp32/ilp32.exp
> +++ b/gas/testsuite/gas/i386/ilp32/ilp32.exp
> @@ -37,7 +37,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_x32_check] &
>         }
>      }
>
> -    run_list_test "reloc64" "--defsym _bad_=1"
> +    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
>      run_list_test "reloc-2"
>
>      set ASFLAGS "$old_ASFLAGS"
> diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.d b/gas/testsuite/gas/i386/ilp32/reloc64.d
> index e2c461f24e8..84b6aaccc01 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/inval-tls.l b/gas/testsuite/gas/i386/inval-tls.l
> index 98f7a29f1ab..40443bf68a2 100644
> --- a/gas/testsuite/gas/i386/inval-tls.l
> +++ b/gas/testsuite/gas/i386/inval-tls.l
> @@ -1,2 +1,30 @@
>  .*: Assembler messages:
>  .*:3: Error: @GOTNTPOFF operator cannot be used with `kmovd'
> +.*:6: Error: @TLSGD operator cannot be used with `add'
> +.*:7: Error: @TLSGD operator only supports ebx/rbx as index register
> +.*:8: Error: @TLSGD operator only supports scale factor of 00
> +.*:9: Error: @TLSGD operator should not have base register
> +.*:10: Error: @TLSGD operator only supports eax/rax as dest register
> +.*:11: Error: @TLSGD operator only supports eax/rax as dest register
> +.*:12: Error: @TLSGD operator only supports eax/rax as base register
> +.*:13: Error: 4-byte relocation cannot be applied to 2-byte field
> +.*:16: Error: @TLSLDM operator cannot be used with `add'
> +.*:17: Error: @TLSLDM operator only supports eax/rax as dest register
> +.*:18: Error: @TLSLDM operator only supports eax/rax as base register
> +.*:19: Error: @TLSLDM operator do not support SIB
> +.*:20: Error: 4-byte relocation cannot be applied to 2-byte field
> +.*:23: Error: @TLSDESC operator cannot be used with `add'
> +.*:24: Error: @TLSDESC operator only supports ebx/rbx as base register
> +.*:25: Error: @TLSDESC operator do not support SIB
> +.*:28: Error: @INDNTPOFF operator cannot be used with `sub'
> +.*:29: Error: @INDNTPOFF operator should not have base/index register
> +.*:30: Error: @INDNTPOFF operator should not have base/index register
> +.*:34: Error: @GOTNTPOFF operator cannot be used with `lea'
> +.*:35: Error: @GOTNTPOFF operator must have base regster
> +.*:38: Error: @GOTTPOFF operator cannot be used with `lea'
> +.*:39: Error: @GOTTPOFF operator must have base regster
> +.*:40: Error: 4-byte relocation cannot be applied to 2-byte field
> +.*:43: Error: @TLSCALL operator cannot be used with `lea'
> +.*:44: Error: @TLSCALL operator only supports eax/rax as base register
> +.*:45: Error: @TLSCALL operator do not support SIB
> +.*:45: Error: 0-byte relocation cannot be applied to 4-byte field
> diff --git a/gas/testsuite/gas/i386/inval-tls.s b/gas/testsuite/gas/i386/inval-tls.s
> index ba0e1b81dc9..d00dbfa3008 100644
> --- a/gas/testsuite/gas/i386/inval-tls.s
> +++ b/gas/testsuite/gas/i386/inval-tls.s
> @@ -1,3 +1,45 @@
>         .text
>  # All the following should be illegal
>         kmovd   foo@gotntpoff(%eax), %k0
> +
> +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> +
> +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> +
> +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTDESC.  */
> +       addl x@tlsdesc(%ebx), %eax
> +       leal x@tlsdesc(%ecx), %eax
> +       leal x@tlsdesc(,%ecx,1), %eax
> +
> +       /* Invalid testcase for BFD_RELOC_386_TLS_IE.  */
> +       subl foo@indntpoff, %ecx
> +       addl foo@indntpoff(%ebx), %ecx
> +       movl foo@indntpoff(%ebx), %ecx
> +       add foo@indntpoff, %cx
> +
> +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTIE.  */
> +       leal foo@gotntpoff(%ebx), %ecx
> +       subl foo@gotntpoff(,%ebx,1), %ecx
> +
> +       /* Invalid testcase for BFD_RELOC_386_TLS_IE_32.  */
> +       leal foo@gottpoff(%ebx), %ecx
> +       subl foo@gottpoff(,%ebx,1), %ecx
> +       add foo@gottpoff(%bx), %cx
> +
> +       /* Invalid testcase for BFD_RELOC_386_TLS_DESC_CALL.  */
> +       leal foo@tlscall(%eax), %ebx
> +       call *x@tlscall(%ebx)
> +       call *x@tlscall(,%eax,1)
> diff --git a/gas/testsuite/gas/i386/reloc32.d b/gas/testsuite/gas/i386/reloc32.d
> index 263a742022e..7d1b1ba2ae0 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 540a9b77d35..f4e44a627c5 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 00000000000..adfe7ce9551
> --- /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 00000000000..7a0a83147e4
> --- /dev/null
> +++ b/gas/testsuite/gas/i386/tls.s
> @@ -0,0 +1,31 @@
> +# Check tls relocation 32-bit mode
> +
> +       .text
> +_start:
> +       /* BFD_RELOC_386_TLS_GD.  */
> +       leal foo@tlsgd(,%ebx,1), %eax
> +       leal foo@tlsgd(%ecx), %eax
> +
> +       /* BFD_RELOC_386_TLS_LDM.  */
> +       leal foo@tlsldm(%ebx), %eax
> +
> +       /* BFD_RELOC_386_TLS_GOTDESC.  */
> +       leal x@tlsdesc(%ebx), %eax
> +
> +       /* BFD_RELOC_386_TLS_IE.  */
> +       movl foo@indntpoff, %eax
> +       movl foo@indntpoff, %ebx
> +       addl foo@indntpoff, %edx
> +
> +       /* BFD_RELOC_386_TLS_GOTIE.  */
> +       subl foo@gotntpoff(%ebx), %ecx
> +       movl foo@gotntpoff(%ebx), %ecx
> +       addl foo@gotntpoff(%ebx), %ecx
> +
> +       /* BFD_RELOC_386_TLS_IE_32.  */
> +       subl foo@gottpoff(%ebx), %ecx
> +       movl foo@gottpoff(%ebx), %ecx
> +       addl foo@gottpoff(%ebx), %ecx
> +
> +       /* BFD_RELOC_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 da8ac19f869..b9a53693c4f 100644
> --- a/gas/testsuite/gas/i386/x86-64-inval-tls.l
> +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.l
> @@ -1,3 +1,20 @@
>  .*: 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 only supports eax/rax as dest register
> +.*:9: Error: @TLSGD operator only supports rip as base register
> +.*:10: Error: @TLSGD operator must have base regster
> +.*:13: Error: @TLSLD operator cannot be used with `add'
> +.*:14: Error: @TLSLD operator only supports eax/rax as dest register
> +.*:15: Error: @TLSLD operator only supports rip as base register
> +.*:16: Error: @TLSLD operator must have base regster
> +.*:19: Error: @TLSDESC operator cannot be used with `add'
> +.*:20: Error: @TLSDESC operator only supports rip as base register
> +.*:23: Error: @GOTTPOFF operator cannot be used with `sub'
> +.*:24: Error: @GOTTPOFF operator cannot be used with `xor'
> +.*:25: Error: @GOTTPOFF operator only supports rip as base register
> +.*:26: Error: @GOTTPOFF operator only supports rip as base register
> +.*:29: Error: @TLSCALL operator cannot be used with `lea'
> +.*:30: Error: @TLSCALL operator only supports eax/rax as base register
> +.*:30: Error: 0-byte relocation cannot be applied to 4-byte field
> diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.s b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> index 71e19272ba9..ccefeb780b6 100644
> --- a/gas/testsuite/gas/i386/x86-64-inval-tls.s
> +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> @@ -2,3 +2,29 @@
>  # All the following should be illegal
>         kmovq   foo@gottpoff(%rip), %k0
>         kmovq   foo@tlsld(%rip), %k0
> +
> +       /* Invalid testcase for BFD_RELOC_X86_64_TLSGD.  */
> +       addq foo@tlsgd(%rip), %rdi
> +       leaq foo@tlsgd(%rip), %rax
> +       leaq foo@tlsgd(%rax), %rdi
> +       leaq foo@tlsgd(,%rax,1), %rdi
> +
> +       /* Invalid testcase for BFD_RELOC_X86_64_TLSLD.  */
> +       addq foo@tlsld(%rip), %rdi
> +       leaq foo@tlsld(%rip), %rax
> +       leaq foo@tlsld(%rax), %rdi
> +       leaq foo@tlsld(,%rax,1), %rdi
> +
> +       /* Invalid testcase for BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> +       addq x@tlsdesc(%rip), %rax
> +       leaq x@tlsdesc(%rbx), %rax
> +
> +       /* Invalid testcase for BFD_RELOC_X86_64_GOTTPOFF.  */
> +       subq foo@gottpoff(%rip), %r12
> +       xorq foo@gottpoff(%rip), %rax
> +       movq foo@gottpoff(%rbx), %rax
> +       addq foo@gottpoff(%rbx), %rax
> +
> +       /* Invalid testcase for BFD_RELOC_X86_64_TLSDESC_CALL.  */
> +       leaq foo@tlscall(%rax), %rbx
> +       call *foo@tlscall(%rip)
> 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 00000000000..c4e5a8a4b29
> --- /dev/null
> +++ b/gas/testsuite/gas/i386/x86-64-tls.d
> @@ -0,0 +1,29 @@
> +#as:
> +#objdump: -drw
> +#name: Check tls relocation x86-64
> +
> +.*: +file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0000000000000000 <_start>:
> +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # 7 <_start\+0x7>        3: R_X86_64_TLSGD       foo-0x4
> +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # e <_start\+0xe>        a: R_X86_64_TLSLD       foo-0x4
> +\s*[a-f0-9]+:\s*48 8d 05 00 00 00 00   lea    0x0\(%rip\),%rax        # 15 <_start\+0x15>      11: R_X86_64_GOTPC32_TLSDESC    x-0x4
> +\s*[a-f0-9]+:\s*40 8d 05 00 00 00 00   rex lea 0x0\(%rip\),%eax        # 1c <_start\+0x1c>     18: R_X86_64_GOTPC32_TLSDESC    x-0x4
> +\s*[a-f0-9]+:\s*d5 40 8d 05 00 00 00 00        lea    0x0\(%rip\),%r16d        # 24 <_start\+0x24>     20: R_X86_64_CODE_4_GOTPC32_TLSDESC     x-0x4
> +\s*[a-f0-9]+:\s*4c 03 25 00 00 00 00   add    0x0\(%rip\),%r12        # 2b <_start\+0x2b>      27: R_X86_64_GOTTPOFF   foo-0x4
> +\s*[a-f0-9]+:\s*48 8b 05 00 00 00 00   mov    0x0\(%rip\),%rax        # 32 <_start\+0x32>      2e: R_X86_64_GOTTPOFF   foo-0x4
> +\s*[a-f0-9]+:\s*44 03 25 00 00 00 00   add    0x0\(%rip\),%r12d        # 39 <_start\+0x39>     35: R_X86_64_GOTTPOFF   foo-0x4
> +\s*[a-f0-9]+:\s*8b 05 00 00 00 00      mov    0x0\(%rip\),%eax        # 3f <_start\+0x3f>      3b: R_X86_64_GOTTPOFF   foo-0x4
> +\s*[a-f0-9]+:\s*d5 48 03 05 00 00 00 00        add    0x0\(%rip\),%r16        # 47 <_start\+0x47>      43: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*d5 48 8b 25 00 00 00 00        mov    0x0\(%rip\),%r20        # 4f <_start\+0x4f>      4b: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*62 64 bc 18 01 35 00 00 00 00  add    %r30,0x0\(%rip\),%r8        # 59 <_start\+0x59>  55: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*62 f4 dc 10 03 05 00 00 00 00  add    0x0\(%rip\),%rax,%r20        # 63 <_start\+0x63> 5f: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*62 e4 fc 0c 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%r16        # 6d <_start\+0x6d>    69: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*62 64 bc 1c 01 35 00 00 00 00  {nf} add %r30,0x0\(%rip\),%r8        # 77 <_start\+0x77>        73: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*62 f4 dc 14 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%rax,%r20        # 81 <_start\+0x81>       7d: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> +\s*[a-f0-9]+:\s*ff 10                  call   \*\(%rax\)       81: R_X86_64_TLSDESC_CALL       x
> +\s*[a-f0-9]+:\s*67 ff 10               call   \*\(%eax\)       83: 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 00000000000..4c573922773
> --- /dev/null
> +++ b/gas/testsuite/gas/i386/x86-64-tls.s
> @@ -0,0 +1,31 @@
> +# Check tls relocation 64-bit mode
> +
> +       .text
> +_start:
> +       /* BFD_RELOC_X86_64_TLSGD.  */
> +       leaq foo@tlsgd(%rip), %rdi
> +
> +       /* BFD_RELOC_X86_64_TLSLD.  */
> +       leaq foo@tlsld(%rip), %rdi
> +
> +       /* BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> +       leaq     x@tlsdesc(%rip), %rax
> +       rex leal x@tlsdesc(%rip), %eax
> +       leal     x@tlsdesc(%rip), %r16d
> +
> +       /* BFD_RELOC_X86_64_GOTTPOFF.  */
> +       addq     foo@gottpoff(%rip), %r12
> +       movq     foo@gottpoff(%rip), %rax
> +       addl     foo@gottpoff(%rip), %r12d
> +       movl     foo@gottpoff(%rip), %eax
> +       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
> +
> +       /* BFD_RELOC_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 86e7f4a75b3..740f5268de3 100644
> --- a/gas/testsuite/gas/i386/x86-64.exp
> +++ b/gas/testsuite/gas/i386/x86-64.exp
> @@ -653,7 +653,8 @@ if [is_elf_format] then {
>      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 2a70e81c444..0c853ab0a67 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 bbf93bef767..99e1b18b1ea 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 f2c29d880f2..4bb99c4422b 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 9f9e63029d6..4e7dc6ea56e 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 506f1a02605..6bfc78e0b49 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 a516d002660..98293f4b36a 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 d3447182e19..4e9c9a8f74a 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 bbf22ebeafe..955884885d7 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 c882c877ae3..ccaa525c74b 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 6a0158b44b7..0876993bca3 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 bf8a8198b5b..2e6d41ccf61 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 49d8464fbaf..b59cc6429de 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 29de1cebf8e..d7ab5ab7b32 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
> diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c
> index 565aae722f8..dd5e8744efe 100644
> --- a/opcodes/i386-gen.c
> +++ b/opcodes/i386-gen.c
> @@ -526,6 +526,7 @@ static const struct {
>      INSTANCE (RegC),
>      INSTANCE (RegD),
>      INSTANCE (RegB),
> +    INSTANCE (RegDI),
>  };
>
>  #undef INSTANCE
> diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
> index c0d5e44d461..608e238fa18 100644
> --- a/opcodes/i386-opc.h
> +++ b/opcodes/i386-opc.h
> @@ -843,6 +843,7 @@ enum operand_instance
>    RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
>    RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
>    RegB,  /* %bl / %bx / %ebx / %rbx */
> +  RegDI, /* %rdx */
>  };
>
>  /* Position of operand_type bits.  */
> diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
> index e3146456133..028f9523b52 100644
> --- a/opcodes/i386-reg.tbl
> +++ b/opcodes/i386-reg.tbl
> @@ -135,7 +135,7 @@ rbx, Class=Reg|Instance=RegB|Qword|BaseIndex, 0, 3, Dw2Inval, 3
>  rsp, Class=Reg|Qword, 0, 4, Dw2Inval, 7
>  rbp, Class=Reg|Qword|BaseIndex, 0, 5, Dw2Inval, 6
>  rsi, Class=Reg|Qword|BaseIndex, 0, 6, Dw2Inval, 4
> -rdi, Class=Reg|Qword|BaseIndex, 0, 7, Dw2Inval, 5
> +rdi, Class=Reg|Instance=RegDI|Qword|BaseIndex, 0, 7, Dw2Inval, 5
>  r8, Class=Reg|Qword|BaseIndex, RegRex, 0, Dw2Inval, 8
>  r9, Class=Reg|Qword|BaseIndex, RegRex, 1, Dw2Inval, 9
>  r10, Class=Reg|Qword|BaseIndex, RegRex, 2, Dw2Inval, 10
> --
> 2.34.1
>

This failed with GCC 14:

FAIL: Check tls relocation x86-64
FAIL: Build size-8

gcc  -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
-I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
-O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
-o tmpdir/size-8b.o
Executing on host: sh -c {gcc
-B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
-I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
-O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
-o tmpdir/size-8b.o 2>&1}  /dev/null ld.tmp (timeout = 300)
spawn [open ...]^M
/tmp/cc6aqbbv.s: Assembler messages:
/tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
dest register
/tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
dest register

UNSUPPORTED: Build libsize-8.so

FAIL: Run size-2
FAIL: Run size-8
FAIL: Run size-10
FAIL: Build libtls-1b.so
FAIL: Build libtls-1bn.so
FAIL: TLS GD/LD -> LE transition without PLT (dynamic)
FAIL: TLS GD/LD -> LE transition without PLT (dynamic, -z now)
FAIL: TLS GD/LD -> LE transition without PLT (PIE)
FAIL: TLS GD/LD -> LE transition without PLT (PIE, -z now)
FAIL: TLS GD/LD -> LE transition without PLT (static)
FAIL: TLS GD/LD -> IE transition without PLT
FAIL: TLS GD/LD -> IE transition without PLT (-z now)
FAIL: TLS without PLT (1)
FAIL: TLS without PLT (2)
FAIL: TLS without PLT (3)
FAIL: TLS without PLT (4)
FAIL: TLS -fpic and -fno-pic exec transitions
FAIL: TLS -fpic and -fno-pic exec transitions without PLT
FAIL: TLS with global dynamic and descriptors
FAIL: TLS GD->LE transition
FAIL: TLS LD->LE transition
FAIL: TLS X32 GD->LE transition
FAIL: TLS GD->IE transition
FAIL: TLS GD->IE transition without PLT
FAIL: TLS X32 GD->IE transition
FAIL: TLS X32 GD->IE transition without PLT
FAIL: TLS X32 LD->LE transition
FAIL: TLS -mcmodel=large GD->LE transition
FAIL: TLS -mcmodel=large LD->LE transition
FAIL: TLS -mcmodel=large LD->LE transition with r15 as GOT base
FAIL: TLS LD->LE transition without PLT
FAIL: TLS X32 LD->LE transition without PLT
FAIL: TLS -mcmodel=large GD->IE transition
FAIL: TLS -mcmodel=large GD->LE transition with r15 as GOT base
FAIL: TLS -mcmodel=large GD->IE transition with r15 as GOT base
FAIL: TLS GD->LE transition without PLT
FAIL: TLS X32 GD->LE transition without PLT
FAIL: TLS -fpic -shared transitions
FAIL: TLS -fpic -shared transitions with r15 as GOT base
FAIL: TLS GD->IE transition check
FAIL: TLS GD->LE transition check
FAIL: TLS GD->IE transition check without PLT
FAIL: TLS GD->LE transition check without PLT
FAIL: TLS with PIE (1 x86_64)
FAIL: ld-x86-64/tlspie2a
FAIL: ld-x86-64/tlspie2b
FAIL: ld-x86-64/tlspie2c
FAIL: ld-x86-64/align-branch-1
  
H.J. Lu Sept. 6, 2024, 1:07 p.m. UTC | #2
On Fri, Sep 6, 2024 at 5:34 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Thu, Sep 5, 2024 at 1:57 AM Cui, Lili <lili.cui@intel.com> wrote:
> >
> > Sorry, added v2 to the patch name
> >
> > ---
> > 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. 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.
> >
> > ---
> > V2:
> > 1. Add format checks for each operator. And add a function to report errors.
> > 2. Replace DEFAULT_TLS_CHECK with DEFAULT_X86_TLS_CHECK, and all related names.
> > 3. Add more invalid test cases.
> > 4. Remove the handling of "call *x@tlsdesc(%rax)", which is now illegal.
> > 5. Add new INSTANCE RegDI
> > 6. Use option drw for objdump.
> > ---
> >
> > gas/ChangeLog:
> >
> >         PR gas/32022
> >         * config.in: Regenerate.
> >         * config/tc-i386.c (_BFD_MAKE_TABLE_bfd_reloc_code_real): New.
> >         (x86_check_tls_relocation): Added a new function to check tls relocation.
> >         (i386_assemble): Handle x86_check_tls_relocation.
> >         (OPTION_MTLS_CHECK): Added a new option to contrl tls check.
> >         (struct option): Ditto.
> >         (md_parse_option): Ditto.
> >         (md_show_usage): Ditto.
> >         * configure: Added a new option to check tls relocation by default.
> >         * configure.ac: Ditto.
> >         * doc/c-i386.texi: Ditto.
> >         * 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/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/tls.d: New test.
> >         * testsuite/gas/i386/tls.s: Ditto.
> >         * testsuite/gas/i386/x86-64-tls.d: Ditto.
> >         * testsuite/gas/i386/x86-64-tls.s: Ditto.
> >
> > opcodes/ChangeLog:
> >
> >         PR gas/32022
> >         * i386-gen.c: Added new INSTANCE RegDI.
> >         * i386-opc.h (enum operand_instance): Ditto.
> >         * i386-reg.tbl: Added RegDI to rdi.
> >
> > 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.
> > ---
> >  gas/config.in                             |   3 +
> >  gas/config/tc-i386.c                      | 348 ++++++++++++++++++++--
> >  gas/configure                             |  26 +-
> >  gas/configure.ac                          |  18 ++
> >  gas/doc/c-i386.texi                       |  10 +
> >  gas/testsuite/gas/i386/i386.exp           |   3 +-
> >  gas/testsuite/gas/i386/ilp32/ilp32.exp    |   2 +-
> >  gas/testsuite/gas/i386/ilp32/reloc64.d    |   2 +-
> >  gas/testsuite/gas/i386/inval-tls.l        |  28 ++
> >  gas/testsuite/gas/i386/inval-tls.s        |  42 +++
> >  gas/testsuite/gas/i386/reloc32.d          |   2 +-
> >  gas/testsuite/gas/i386/reloc64.d          |   2 +-
> >  gas/testsuite/gas/i386/tls.d              |  25 ++
> >  gas/testsuite/gas/i386/tls.s              |  31 ++
> >  gas/testsuite/gas/i386/x86-64-inval-tls.l |  17 ++
> >  gas/testsuite/gas/i386/x86-64-inval-tls.s |  26 ++
> >  gas/testsuite/gas/i386/x86-64-tls.d       |  29 ++
> >  gas/testsuite/gas/i386/x86-64-tls.s       |  31 ++
> >  gas/testsuite/gas/i386/x86-64.exp         |   3 +-
> >  ld/testsuite/ld-i386/tlsgdesc1.d          |   2 +-
> >  ld/testsuite/ld-i386/tlsgdesc2.d          |   2 +-
> >  ld/testsuite/ld-i386/tlsgdesc3.d          |   2 +-
> >  ld/testsuite/ld-i386/tlsie2.d             |   2 +-
> >  ld/testsuite/ld-i386/tlsie3.d             |   2 +-
> >  ld/testsuite/ld-i386/tlsie4.d             |   2 +-
> >  ld/testsuite/ld-i386/tlsie5.d             |   2 +-
> >  ld/testsuite/ld-x86-64/tlsdesc3.d         |   2 +-
> >  ld/testsuite/ld-x86-64/tlsdesc4.d         |   2 +-
> >  ld/testsuite/ld-x86-64/tlsdesc5.d         |   2 +-
> >  ld/testsuite/ld-x86-64/tlsie2.d           |   2 +-
> >  ld/testsuite/ld-x86-64/tlsie3.d           |   2 +-
> >  ld/testsuite/ld-x86-64/tlsie5.d           |   2 +-
> >  opcodes/i386-gen.c                        |   1 +
> >  opcodes/i386-opc.h                        |   1 +
> >  opcodes/i386-reg.tbl                      |   2 +-
> >  35 files changed, 634 insertions(+), 44 deletions(-)
> >  create mode 100644 gas/testsuite/gas/i386/tls.d
> >  create mode 100644 gas/testsuite/gas/i386/tls.s
> >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.d
> >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.s
> >
> > diff --git a/gas/config.in b/gas/config.in
> > index a1f83499332..3f199d5b4a6 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 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 a9d3536429b..a0222580106 100644
> > --- a/gas/config/tc-i386.c
> > +++ b/gas/config/tc-i386.c
> > @@ -274,6 +274,23 @@ enum i386_error
> >      internal_error,
> >    };
> >
> > +enum x86_tls_error_type
> > +{
> > +  x86_tls_error_none,
> > +  x86_tls_error_insn,
> > +  x86_tls_error_sib,
> > +  x86_tls_error_no_base_reg,
> > +  x86_tls_error_require_no_base_index_reg,
> > +  x86_tls_error_base_reg_name,
> > +  x86_tls_error_index_RegB,
> > +  x86_tls_error_RegA,
> > +  x86_tls_error_RegB,
> > +  x86_tls_error_RegIP,
> > +  x86_tls_error_dest_RegA,
> > +  x86_tls_error_dest_RegDI,
> > +  x86_tls_error_scale_factor
> > +};
> > +
> >  struct _i386_insn
> >    {
> >      /* TM holds the template for the insn were currently assembling.  */
> > @@ -717,6 +734,9 @@ lfence_before_ret;
> >  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,
> > @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const insn_template *t)
> >                && (t->base_opcode | 8) == 0x2c);
> >  }
> >
> > +static enum x86_tls_error_type
> > +x86_check_tls_relocation (unsigned int r_type)

Please use enum bfd_reloc_code_real r_type.

> > +{
> > +  switch (r_type)
> > +    {
> > +    case BFD_RELOC_386_TLS_GOTDESC:
> > +      /* Check transition from GDesc access model:
> > +
> > +        leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> > +                                       SIB is not supported.
> > +       */
> > +      if (i.tm.mnem_off != MN_lea)
> > +       return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +       return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> > +       return x86_tls_error_RegB;
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_GD:
> > +      /* Check transition from GD access model:
> > +
> > +        leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
> > +        leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> > +                                            Memory reg can't be %eax.
> > +       */
> > +      if (i.tm.mnem_off != MN_lea)
> > +       return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +       {
> > +         if (i.base_reg)
> > +           return x86_tls_error_base_reg_name;
> > +         if (i.index_reg->reg_type.bitfield.instance != RegB)
> > +           return x86_tls_error_index_RegB;
> > +         else if (i.types[1].bitfield.instance != Accum)
> > +           return x86_tls_error_dest_RegA;
> > +         else 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_RegA;
> > +         else if (i.types[1].bitfield.instance != Accum)
> > +           return x86_tls_error_dest_RegA;
> > +       }
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_LDM:
> > +      /*  Check transition from LDM access model:
> > +
> > +         leal foo@tlsldm(%reg), %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;
> > +      else if (i.index_reg)
> > +       return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> > +       return x86_tls_error_RegA;
> > +      else if (i.types[1].bitfield.instance != Accum)
> > +       return x86_tls_error_dest_RegA;
> > +      break;
> > +
> > +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> > +      /* Check transition from GOTPC32 TLSDESC access model:
> > +
> > +        --- LP64 mode ---
> > +        leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> > +
> > +        --- X32 mode ---
> > +        rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> > +
> > +       */
> > +      if (i.tm.mnem_off != MN_lea)
> > +       return x86_tls_error_insn;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +       return x86_tls_error_RegIP;
> > +      break;
> > +
> > +    case BFD_RELOC_X86_64_TLSGD:
> > +      /* Check transition from GD access model:
> > +
> > +        leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> > +       */
> > +    case BFD_RELOC_X86_64_TLSLD:
> > +      /* Check transition from 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;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +       return x86_tls_error_RegIP;
> > +      else if (i.types[1].bitfield.instance != RegDI)

Use

      else if (i.types[1].bitfield.class != Reg
               || !i.op[1].regs->reg_type.bitfield.qword
               || i.op[1].regs->reg_num != EDI_REG_NUM)
        return x86_tls_error_dest_RegDI;

> > +       return x86_tls_error_dest_RegDI;
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_GOTIE:
> > +      /* Check transition from 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 transition from 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;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.index_reg)
> > +       return x86_tls_error_sib;
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_IE:
> > +      /* Check transition from IE access model:
> > +
> > +        movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > +        addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > +       */
> > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > +       return x86_tls_error_insn;
> > +      else if (i.base_reg || i.index_reg)
> > +       return x86_tls_error_require_no_base_index_reg;
> > +      break;
> > +
> > +    case BFD_RELOC_X86_64_GOTTPOFF:
> > +      /* Check transition from GOTTPOFF access model:
> > +
> > +        mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > +        add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > +       */
> > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > +       return x86_tls_error_insn;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +       return x86_tls_error_RegIP;
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_DESC_CALL:
> > +      /* Check transition from GDesc access model:
> > +
> > +        call *x@tlscall(%eax) --> Memory reg must be %eax and
> > +                                  SIB is not supported.
> > +       */
> > +      if (i.tm.mnem_off != MN_call)
> > +       return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +       return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > +       return x86_tls_error_RegA;
> > +      break;
> > +
> > +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> > +      /* Check transition from 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;
> > +      else if (i.index_reg)
> > +       return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +       return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > +       return x86_tls_error_RegA;
> > +      break;
> > +
> > +    default:
> > +      return x86_tls_error_none;
> > +    }
> > +
> > +  return x86_tls_error_none;
> > +}
> > +
> > +static void
> > +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int 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_sib:
> > +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_no_base_reg:
> > +      as_bad (_("@%s operator must have base regster"), gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_require_no_base_index_reg:
> > +      as_bad (_("@%s operator should not have base/index register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_base_reg_name:
> > +      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_index_RegB:
> > +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_RegA:
> > +      as_bad (_("@%s operator only supports eax/rax as base register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_RegB:
> > +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_RegIP:
> > +      as_bad (_("@%s operator only supports rip as base register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_dest_RegA:
> > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_dest_RegDI:
> > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > +             gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_scale_factor:
> > +      as_bad (_("@%s operator only supports scale factor of 00"),
> > +             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.  */
> > @@ -6656,6 +6954,14 @@ i386_assemble (char *line)
> >         i.prefix[LOCK_PREFIX] = 0;
> >      }
> >
> > +  if (tls_check)
> > +    {
> > +      enum x86_tls_error_type tls_error;
> > +      for (j = 0; j < i.operands; ++j)
> > +       if ((tls_error = x86_check_tls_relocation (i.reloc[j])) != x86_tls_error_none)
> > +         x86_report_tls_error (tls_error, i.reloc[j]);
> > +    }
> > +
> >    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)
> > @@ -6666,28 +6972,6 @@ i386_assemble (char *line)
> >           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.  */
> > @@ -16184,6 +16468,7 @@ const char *md_shortopts = "qnO::";
> >  #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[] =
> >  {
> > @@ -16230,6 +16515,7 @@ struct option md_longopts[] =
> >    {"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);
> > @@ -16786,6 +17072,14 @@ md_parse_option (int c, const char *arg)
> >           optimize_for_space = 0;
> >         }
> >        break;
> > +    case OPTION_MTLS_CHECK:
> > +      if (strcasecmp (arg, "yes") == 0)
> > +       tls_check = 1;
> > +      else if (strcasecmp (arg, "no") == 0)
> > +       tls_check = 0;
> > +      else
> > +       as_fatal (_("invalid -mtls-check= option: `%s'"), arg);
> > +      break;
> >
> >      default:
> >        return 0;
> > @@ -17028,6 +17322,16 @@ md_show_usage (FILE *stream)
> >      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 6b96d3a4e0c..a4cdeb80900 100755
> > --- a/gas/configure
> > +++ b/gas/configure
> > @@ -818,6 +818,7 @@ enable_checking
> >  enable_compressed_debug_sections
> >  enable_default_compressed_debug_sections_algorithm
> >  enable_x86_relax_relocations
> > +enable_tls_check
> >  enable_elf_stt_common
> >  enable_generate_build_notes
> >  enable_mips_fix_loongson3_llsc
> > @@ -1493,6 +1494,7 @@ Optional Features:
> >                            --enable-compressed-debug-sections.
> >    --enable-x86-relax-relocations
> >                            generate x86 relax relocations by default
> > +  --enable-tls-check      check 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 @@ else
> >    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 @@ else
> >    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 @@ if test "${enable_x86_relax_relocations+set}" = set; then :
> >  esac
> >  fi
> >
> > +# PR gas/32022
> > +# Decide if check tls relocation.
> > +ac_default_x86_tls_check=unset
> > +# Provide a configure time option to override our default.
> > +# Check whether --enable-tls_check was given.
> > +if test "${enable_tls_check+set}" = set; then :
> > +  enableval=$enable_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 @@ cat >>confdefs.h <<_ACEOF
> >  _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 6b978aae3f7..5fe57e52746 100644
> > --- a/gas/configure.ac
> > +++ b/gas/configure.ac
> > @@ -95,6 +95,17 @@ AC_ARG_ENABLE(x86_relax_relocations,
> >    no)  ac_default_x86_relax_relocations=0 ;;
> >  esac])dnl
> >
> > +# PR gas/32022
> > +# Decide if check tls relocation.
> > +ac_default_x86_tls_check=unset
> > +# Provide a configure time option to override our default.
> > +AC_ARG_ENABLE(tls_check,
> > +             AS_HELP_STRING([--enable-tls-check],
> > +             [check 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_DEFINE_UNQUOTED(DEFAULT_GENERATE_X86_RELAX_RELOCATIONS,
> >    $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 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 a9e43560aea..5da673a0287 100644
> > --- a/gas/doc/c-i386.texi
> > +++ b/gas/doc/c-i386.texi
> > @@ -467,6 +467,16 @@ R_X86_64_REX_GOTPCRELX, in 64-bit mode.
> >  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-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 75ad061b32c..6710a56d64a 100644
> > --- a/gas/testsuite/gas/i386/i386.exp
> > +++ b/gas/testsuite/gas/i386/i386.exp
> > @@ -699,10 +699,11 @@ if [gas_32_check] then {
> >         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 a3017388934..b32da8725b5 100644
> > --- a/gas/testsuite/gas/i386/ilp32/ilp32.exp
> > +++ b/gas/testsuite/gas/i386/ilp32/ilp32.exp
> > @@ -37,7 +37,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_x32_check] &
> >         }
> >      }
> >
> > -    run_list_test "reloc64" "--defsym _bad_=1"
> > +    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
> >      run_list_test "reloc-2"
> >
> >      set ASFLAGS "$old_ASFLAGS"
> > diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.d b/gas/testsuite/gas/i386/ilp32/reloc64.d
> > index e2c461f24e8..84b6aaccc01 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/inval-tls.l b/gas/testsuite/gas/i386/inval-tls.l
> > index 98f7a29f1ab..40443bf68a2 100644
> > --- a/gas/testsuite/gas/i386/inval-tls.l
> > +++ b/gas/testsuite/gas/i386/inval-tls.l
> > @@ -1,2 +1,30 @@
> >  .*: Assembler messages:
> >  .*:3: Error: @GOTNTPOFF operator cannot be used with `kmovd'
> > +.*:6: Error: @TLSGD operator cannot be used with `add'
> > +.*:7: Error: @TLSGD operator only supports ebx/rbx as index register
> > +.*:8: Error: @TLSGD operator only supports scale factor of 00
> > +.*:9: Error: @TLSGD operator should not have base register
> > +.*:10: Error: @TLSGD operator only supports eax/rax as dest register
> > +.*:11: Error: @TLSGD operator only supports eax/rax as dest register
> > +.*:12: Error: @TLSGD operator only supports eax/rax as base register
> > +.*:13: Error: 4-byte relocation cannot be applied to 2-byte field
> > +.*:16: Error: @TLSLDM operator cannot be used with `add'
> > +.*:17: Error: @TLSLDM operator only supports eax/rax as dest register
> > +.*:18: Error: @TLSLDM operator only supports eax/rax as base register
> > +.*:19: Error: @TLSLDM operator do not support SIB
> > +.*:20: Error: 4-byte relocation cannot be applied to 2-byte field
> > +.*:23: Error: @TLSDESC operator cannot be used with `add'
> > +.*:24: Error: @TLSDESC operator only supports ebx/rbx as base register
> > +.*:25: Error: @TLSDESC operator do not support SIB
> > +.*:28: Error: @INDNTPOFF operator cannot be used with `sub'
> > +.*:29: Error: @INDNTPOFF operator should not have base/index register
> > +.*:30: Error: @INDNTPOFF operator should not have base/index register
> > +.*:34: Error: @GOTNTPOFF operator cannot be used with `lea'
> > +.*:35: Error: @GOTNTPOFF operator must have base regster
> > +.*:38: Error: @GOTTPOFF operator cannot be used with `lea'
> > +.*:39: Error: @GOTTPOFF operator must have base regster
> > +.*:40: Error: 4-byte relocation cannot be applied to 2-byte field
> > +.*:43: Error: @TLSCALL operator cannot be used with `lea'
> > +.*:44: Error: @TLSCALL operator only supports eax/rax as base register
> > +.*:45: Error: @TLSCALL operator do not support SIB
> > +.*:45: Error: 0-byte relocation cannot be applied to 4-byte field
> > diff --git a/gas/testsuite/gas/i386/inval-tls.s b/gas/testsuite/gas/i386/inval-tls.s
> > index ba0e1b81dc9..d00dbfa3008 100644
> > --- a/gas/testsuite/gas/i386/inval-tls.s
> > +++ b/gas/testsuite/gas/i386/inval-tls.s
> > @@ -1,3 +1,45 @@
> >         .text
> >  # All the following should be illegal
> >         kmovd   foo@gotntpoff(%eax), %k0
> > +
> > +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> > +
> > +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> > +
> > +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTDESC.  */
> > +       addl x@tlsdesc(%ebx), %eax
> > +       leal x@tlsdesc(%ecx), %eax
> > +       leal x@tlsdesc(,%ecx,1), %eax
> > +
> > +       /* Invalid testcase for BFD_RELOC_386_TLS_IE.  */
> > +       subl foo@indntpoff, %ecx
> > +       addl foo@indntpoff(%ebx), %ecx
> > +       movl foo@indntpoff(%ebx), %ecx
> > +       add foo@indntpoff, %cx
> > +
> > +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTIE.  */
> > +       leal foo@gotntpoff(%ebx), %ecx
> > +       subl foo@gotntpoff(,%ebx,1), %ecx
> > +
> > +       /* Invalid testcase for BFD_RELOC_386_TLS_IE_32.  */
> > +       leal foo@gottpoff(%ebx), %ecx
> > +       subl foo@gottpoff(,%ebx,1), %ecx
> > +       add foo@gottpoff(%bx), %cx
> > +
> > +       /* Invalid testcase for BFD_RELOC_386_TLS_DESC_CALL.  */
> > +       leal foo@tlscall(%eax), %ebx
> > +       call *x@tlscall(%ebx)
> > +       call *x@tlscall(,%eax,1)
> > diff --git a/gas/testsuite/gas/i386/reloc32.d b/gas/testsuite/gas/i386/reloc32.d
> > index 263a742022e..7d1b1ba2ae0 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 540a9b77d35..f4e44a627c5 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 00000000000..adfe7ce9551
> > --- /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 00000000000..7a0a83147e4
> > --- /dev/null
> > +++ b/gas/testsuite/gas/i386/tls.s
> > @@ -0,0 +1,31 @@
> > +# Check tls relocation 32-bit mode
> > +
> > +       .text
> > +_start:
> > +       /* BFD_RELOC_386_TLS_GD.  */
> > +       leal foo@tlsgd(,%ebx,1), %eax
> > +       leal foo@tlsgd(%ecx), %eax
> > +
> > +       /* BFD_RELOC_386_TLS_LDM.  */
> > +       leal foo@tlsldm(%ebx), %eax
> > +
> > +       /* BFD_RELOC_386_TLS_GOTDESC.  */
> > +       leal x@tlsdesc(%ebx), %eax
> > +
> > +       /* BFD_RELOC_386_TLS_IE.  */
> > +       movl foo@indntpoff, %eax
> > +       movl foo@indntpoff, %ebx
> > +       addl foo@indntpoff, %edx
> > +
> > +       /* BFD_RELOC_386_TLS_GOTIE.  */
> > +       subl foo@gotntpoff(%ebx), %ecx
> > +       movl foo@gotntpoff(%ebx), %ecx
> > +       addl foo@gotntpoff(%ebx), %ecx
> > +
> > +       /* BFD_RELOC_386_TLS_IE_32.  */
> > +       subl foo@gottpoff(%ebx), %ecx
> > +       movl foo@gottpoff(%ebx), %ecx
> > +       addl foo@gottpoff(%ebx), %ecx
> > +
> > +       /* BFD_RELOC_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 da8ac19f869..b9a53693c4f 100644
> > --- a/gas/testsuite/gas/i386/x86-64-inval-tls.l
> > +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.l
> > @@ -1,3 +1,20 @@
> >  .*: 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 only supports eax/rax as dest register
> > +.*:9: Error: @TLSGD operator only supports rip as base register
> > +.*:10: Error: @TLSGD operator must have base regster
> > +.*:13: Error: @TLSLD operator cannot be used with `add'
> > +.*:14: Error: @TLSLD operator only supports eax/rax as dest register
> > +.*:15: Error: @TLSLD operator only supports rip as base register
> > +.*:16: Error: @TLSLD operator must have base regster
> > +.*:19: Error: @TLSDESC operator cannot be used with `add'
> > +.*:20: Error: @TLSDESC operator only supports rip as base register
> > +.*:23: Error: @GOTTPOFF operator cannot be used with `sub'
> > +.*:24: Error: @GOTTPOFF operator cannot be used with `xor'
> > +.*:25: Error: @GOTTPOFF operator only supports rip as base register
> > +.*:26: Error: @GOTTPOFF operator only supports rip as base register
> > +.*:29: Error: @TLSCALL operator cannot be used with `lea'
> > +.*:30: Error: @TLSCALL operator only supports eax/rax as base register
> > +.*:30: Error: 0-byte relocation cannot be applied to 4-byte field
> > diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.s b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > index 71e19272ba9..ccefeb780b6 100644
> > --- a/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > @@ -2,3 +2,29 @@
> >  # All the following should be illegal
> >         kmovq   foo@gottpoff(%rip), %k0
> >         kmovq   foo@tlsld(%rip), %k0
> > +
> > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSGD.  */
> > +       addq foo@tlsgd(%rip), %rdi
> > +       leaq foo@tlsgd(%rip), %rax
> > +       leaq foo@tlsgd(%rax), %rdi
> > +       leaq foo@tlsgd(,%rax,1), %rdi
> > +
> > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSLD.  */
> > +       addq foo@tlsld(%rip), %rdi
> > +       leaq foo@tlsld(%rip), %rax
> > +       leaq foo@tlsld(%rax), %rdi
> > +       leaq foo@tlsld(,%rax,1), %rdi
> > +
> > +       /* Invalid testcase for BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> > +       addq x@tlsdesc(%rip), %rax
> > +       leaq x@tlsdesc(%rbx), %rax
> > +
> > +       /* Invalid testcase for BFD_RELOC_X86_64_GOTTPOFF.  */
> > +       subq foo@gottpoff(%rip), %r12
> > +       xorq foo@gottpoff(%rip), %rax
> > +       movq foo@gottpoff(%rbx), %rax
> > +       addq foo@gottpoff(%rbx), %rax
> > +
> > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSDESC_CALL.  */
> > +       leaq foo@tlscall(%rax), %rbx
> > +       call *foo@tlscall(%rip)
> > 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 00000000000..c4e5a8a4b29
> > --- /dev/null
> > +++ b/gas/testsuite/gas/i386/x86-64-tls.d
> > @@ -0,0 +1,29 @@
> > +#as:
> > +#objdump: -drw
> > +#name: Check tls relocation x86-64
> > +
> > +.*: +file format .*
> > +
> > +
> > +Disassembly of section .text:
> > +
> > +0000000000000000 <_start>:
> > +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # 7 <_start\+0x7>        3: R_X86_64_TLSGD       foo-0x4
> > +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # e <_start\+0xe>        a: R_X86_64_TLSLD       foo-0x4
> > +\s*[a-f0-9]+:\s*48 8d 05 00 00 00 00   lea    0x0\(%rip\),%rax        # 15 <_start\+0x15>      11: R_X86_64_GOTPC32_TLSDESC    x-0x4
> > +\s*[a-f0-9]+:\s*40 8d 05 00 00 00 00   rex lea 0x0\(%rip\),%eax        # 1c <_start\+0x1c>     18: R_X86_64_GOTPC32_TLSDESC    x-0x4
> > +\s*[a-f0-9]+:\s*d5 40 8d 05 00 00 00 00        lea    0x0\(%rip\),%r16d        # 24 <_start\+0x24>     20: R_X86_64_CODE_4_GOTPC32_TLSDESC     x-0x4
> > +\s*[a-f0-9]+:\s*4c 03 25 00 00 00 00   add    0x0\(%rip\),%r12        # 2b <_start\+0x2b>      27: R_X86_64_GOTTPOFF   foo-0x4
> > +\s*[a-f0-9]+:\s*48 8b 05 00 00 00 00   mov    0x0\(%rip\),%rax        # 32 <_start\+0x32>      2e: R_X86_64_GOTTPOFF   foo-0x4
> > +\s*[a-f0-9]+:\s*44 03 25 00 00 00 00   add    0x0\(%rip\),%r12d        # 39 <_start\+0x39>     35: R_X86_64_GOTTPOFF   foo-0x4
> > +\s*[a-f0-9]+:\s*8b 05 00 00 00 00      mov    0x0\(%rip\),%eax        # 3f <_start\+0x3f>      3b: R_X86_64_GOTTPOFF   foo-0x4
> > +\s*[a-f0-9]+:\s*d5 48 03 05 00 00 00 00        add    0x0\(%rip\),%r16        # 47 <_start\+0x47>      43: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*d5 48 8b 25 00 00 00 00        mov    0x0\(%rip\),%r20        # 4f <_start\+0x4f>      4b: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*62 64 bc 18 01 35 00 00 00 00  add    %r30,0x0\(%rip\),%r8        # 59 <_start\+0x59>  55: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*62 f4 dc 10 03 05 00 00 00 00  add    0x0\(%rip\),%rax,%r20        # 63 <_start\+0x63> 5f: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*62 e4 fc 0c 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%r16        # 6d <_start\+0x6d>    69: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*62 64 bc 1c 01 35 00 00 00 00  {nf} add %r30,0x0\(%rip\),%r8        # 77 <_start\+0x77>        73: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*62 f4 dc 14 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%rax,%r20        # 81 <_start\+0x81>       7d: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > +\s*[a-f0-9]+:\s*ff 10                  call   \*\(%rax\)       81: R_X86_64_TLSDESC_CALL       x
> > +\s*[a-f0-9]+:\s*67 ff 10               call   \*\(%eax\)       83: 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 00000000000..4c573922773
> > --- /dev/null
> > +++ b/gas/testsuite/gas/i386/x86-64-tls.s
> > @@ -0,0 +1,31 @@
> > +# Check tls relocation 64-bit mode
> > +
> > +       .text
> > +_start:
> > +       /* BFD_RELOC_X86_64_TLSGD.  */
> > +       leaq foo@tlsgd(%rip), %rdi
> > +
> > +       /* BFD_RELOC_X86_64_TLSLD.  */
> > +       leaq foo@tlsld(%rip), %rdi
> > +
> > +       /* BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> > +       leaq     x@tlsdesc(%rip), %rax
> > +       rex leal x@tlsdesc(%rip), %eax
> > +       leal     x@tlsdesc(%rip), %r16d
> > +
> > +       /* BFD_RELOC_X86_64_GOTTPOFF.  */
> > +       addq     foo@gottpoff(%rip), %r12
> > +       movq     foo@gottpoff(%rip), %rax
> > +       addl     foo@gottpoff(%rip), %r12d
> > +       movl     foo@gottpoff(%rip), %eax
> > +       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
> > +
> > +       /* BFD_RELOC_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 86e7f4a75b3..740f5268de3 100644
> > --- a/gas/testsuite/gas/i386/x86-64.exp
> > +++ b/gas/testsuite/gas/i386/x86-64.exp
> > @@ -653,7 +653,8 @@ if [is_elf_format] then {
> >      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 2a70e81c444..0c853ab0a67 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 bbf93bef767..99e1b18b1ea 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 f2c29d880f2..4bb99c4422b 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 9f9e63029d6..4e7dc6ea56e 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 506f1a02605..6bfc78e0b49 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 a516d002660..98293f4b36a 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 d3447182e19..4e9c9a8f74a 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 bbf22ebeafe..955884885d7 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 c882c877ae3..ccaa525c74b 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 6a0158b44b7..0876993bca3 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 bf8a8198b5b..2e6d41ccf61 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 49d8464fbaf..b59cc6429de 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 29de1cebf8e..d7ab5ab7b32 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
> > diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c
> > index 565aae722f8..dd5e8744efe 100644
> > --- a/opcodes/i386-gen.c
> > +++ b/opcodes/i386-gen.c
> > @@ -526,6 +526,7 @@ static const struct {
> >      INSTANCE (RegC),
> >      INSTANCE (RegD),
> >      INSTANCE (RegB),
> > +    INSTANCE (RegDI),
> >  };
> >
> >  #undef INSTANCE
> > diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
> > index c0d5e44d461..608e238fa18 100644
> > --- a/opcodes/i386-opc.h
> > +++ b/opcodes/i386-opc.h
> > @@ -843,6 +843,7 @@ enum operand_instance
> >    RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
> >    RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
> >    RegB,  /* %bl / %bx / %ebx / %rbx */
> > +  RegDI, /* %rdx */
> >  };
> >
> >  /* Position of operand_type bits.  */
> > diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
> > index e3146456133..028f9523b52 100644
> > --- a/opcodes/i386-reg.tbl
> > +++ b/opcodes/i386-reg.tbl
> > @@ -135,7 +135,7 @@ rbx, Class=Reg|Instance=RegB|Qword|BaseIndex, 0, 3, Dw2Inval, 3
> >  rsp, Class=Reg|Qword, 0, 4, Dw2Inval, 7
> >  rbp, Class=Reg|Qword|BaseIndex, 0, 5, Dw2Inval, 6
> >  rsi, Class=Reg|Qword|BaseIndex, 0, 6, Dw2Inval, 4
> > -rdi, Class=Reg|Qword|BaseIndex, 0, 7, Dw2Inval, 5
> > +rdi, Class=Reg|Instance=RegDI|Qword|BaseIndex, 0, 7, Dw2Inval, 5

Remove it.  Unused and not needed.

> >  r8, Class=Reg|Qword|BaseIndex, RegRex, 0, Dw2Inval, 8
> >  r9, Class=Reg|Qword|BaseIndex, RegRex, 1, Dw2Inval, 9
> >  r10, Class=Reg|Qword|BaseIndex, RegRex, 2, Dw2Inval, 10
> > --
> > 2.34.1
> >
>
> This failed with GCC 14:
>
> FAIL: Check tls relocation x86-64
> FAIL: Build size-8
>
> gcc  -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
> -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> -o tmpdir/size-8b.o
> Executing on host: sh -c {gcc
> -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
> -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> -o tmpdir/size-8b.o 2>&1}  /dev/null ld.tmp (timeout = 300)
> spawn [open ...]^M
> /tmp/cc6aqbbv.s: Assembler messages:
> /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> dest register
> /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> dest register
>
> UNSUPPORTED: Build libsize-8.so
>
> FAIL: Run size-2
> FAIL: Run size-8
> FAIL: Run size-10
> FAIL: Build libtls-1b.so
> FAIL: Build libtls-1bn.so
> FAIL: TLS GD/LD -> LE transition without PLT (dynamic)
> FAIL: TLS GD/LD -> LE transition without PLT (dynamic, -z now)
> FAIL: TLS GD/LD -> LE transition without PLT (PIE)
> FAIL: TLS GD/LD -> LE transition without PLT (PIE, -z now)
> FAIL: TLS GD/LD -> LE transition without PLT (static)
> FAIL: TLS GD/LD -> IE transition without PLT
> FAIL: TLS GD/LD -> IE transition without PLT (-z now)
> FAIL: TLS without PLT (1)
> FAIL: TLS without PLT (2)
> FAIL: TLS without PLT (3)
> FAIL: TLS without PLT (4)
> FAIL: TLS -fpic and -fno-pic exec transitions
> FAIL: TLS -fpic and -fno-pic exec transitions without PLT
> FAIL: TLS with global dynamic and descriptors
> FAIL: TLS GD->LE transition
> FAIL: TLS LD->LE transition
> FAIL: TLS X32 GD->LE transition
> FAIL: TLS GD->IE transition
> FAIL: TLS GD->IE transition without PLT
> FAIL: TLS X32 GD->IE transition
> FAIL: TLS X32 GD->IE transition without PLT
> FAIL: TLS X32 LD->LE transition
> FAIL: TLS -mcmodel=large GD->LE transition
> FAIL: TLS -mcmodel=large LD->LE transition
> FAIL: TLS -mcmodel=large LD->LE transition with r15 as GOT base
> FAIL: TLS LD->LE transition without PLT
> FAIL: TLS X32 LD->LE transition without PLT
> FAIL: TLS -mcmodel=large GD->IE transition
> FAIL: TLS -mcmodel=large GD->LE transition with r15 as GOT base
> FAIL: TLS -mcmodel=large GD->IE transition with r15 as GOT base
> FAIL: TLS GD->LE transition without PLT
> FAIL: TLS X32 GD->LE transition without PLT
> FAIL: TLS -fpic -shared transitions
> FAIL: TLS -fpic -shared transitions with r15 as GOT base
> FAIL: TLS GD->IE transition check
> FAIL: TLS GD->LE transition check
> FAIL: TLS GD->IE transition check without PLT
> FAIL: TLS GD->LE transition check without PLT
> FAIL: TLS with PIE (1 x86_64)
> FAIL: ld-x86-64/tlspie2a
> FAIL: ld-x86-64/tlspie2b
> FAIL: ld-x86-64/tlspie2c
> FAIL: ld-x86-64/align-branch-1
>
>
> --
> H.J.
  
H.J. Lu Sept. 6, 2024, 1:51 p.m. UTC | #3
On Fri, Sep 6, 2024 at 6:07 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Fri, Sep 6, 2024 at 5:34 AM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > On Thu, Sep 5, 2024 at 1:57 AM Cui, Lili <lili.cui@intel.com> wrote:
> > >
> > > Sorry, added v2 to the patch name
> > >
> > > ---
> > > 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. 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.
> > >
> > > ---
> > > V2:
> > > 1. Add format checks for each operator. And add a function to report errors.
> > > 2. Replace DEFAULT_TLS_CHECK with DEFAULT_X86_TLS_CHECK, and all related names.
> > > 3. Add more invalid test cases.
> > > 4. Remove the handling of "call *x@tlsdesc(%rax)", which is now illegal.
> > > 5. Add new INSTANCE RegDI
> > > 6. Use option drw for objdump.
> > > ---
> > >
> > > gas/ChangeLog:
> > >
> > >         PR gas/32022
> > >         * config.in: Regenerate.
> > >         * config/tc-i386.c (_BFD_MAKE_TABLE_bfd_reloc_code_real): New.
> > >         (x86_check_tls_relocation): Added a new function to check tls relocation.
> > >         (i386_assemble): Handle x86_check_tls_relocation.
> > >         (OPTION_MTLS_CHECK): Added a new option to contrl tls check.
> > >         (struct option): Ditto.
> > >         (md_parse_option): Ditto.
> > >         (md_show_usage): Ditto.
> > >         * configure: Added a new option to check tls relocation by default.
> > >         * configure.ac: Ditto.
> > >         * doc/c-i386.texi: Ditto.
> > >         * 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/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/tls.d: New test.
> > >         * testsuite/gas/i386/tls.s: Ditto.
> > >         * testsuite/gas/i386/x86-64-tls.d: Ditto.
> > >         * testsuite/gas/i386/x86-64-tls.s: Ditto.
> > >
> > > opcodes/ChangeLog:
> > >
> > >         PR gas/32022
> > >         * i386-gen.c: Added new INSTANCE RegDI.
> > >         * i386-opc.h (enum operand_instance): Ditto.
> > >         * i386-reg.tbl: Added RegDI to rdi.
> > >
> > > 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.
> > > ---
> > >  gas/config.in                             |   3 +
> > >  gas/config/tc-i386.c                      | 348 ++++++++++++++++++++--
> > >  gas/configure                             |  26 +-
> > >  gas/configure.ac                          |  18 ++
> > >  gas/doc/c-i386.texi                       |  10 +
> > >  gas/testsuite/gas/i386/i386.exp           |   3 +-
> > >  gas/testsuite/gas/i386/ilp32/ilp32.exp    |   2 +-
> > >  gas/testsuite/gas/i386/ilp32/reloc64.d    |   2 +-
> > >  gas/testsuite/gas/i386/inval-tls.l        |  28 ++
> > >  gas/testsuite/gas/i386/inval-tls.s        |  42 +++
> > >  gas/testsuite/gas/i386/reloc32.d          |   2 +-
> > >  gas/testsuite/gas/i386/reloc64.d          |   2 +-
> > >  gas/testsuite/gas/i386/tls.d              |  25 ++
> > >  gas/testsuite/gas/i386/tls.s              |  31 ++
> > >  gas/testsuite/gas/i386/x86-64-inval-tls.l |  17 ++
> > >  gas/testsuite/gas/i386/x86-64-inval-tls.s |  26 ++
> > >  gas/testsuite/gas/i386/x86-64-tls.d       |  29 ++
> > >  gas/testsuite/gas/i386/x86-64-tls.s       |  31 ++
> > >  gas/testsuite/gas/i386/x86-64.exp         |   3 +-
> > >  ld/testsuite/ld-i386/tlsgdesc1.d          |   2 +-
> > >  ld/testsuite/ld-i386/tlsgdesc2.d          |   2 +-
> > >  ld/testsuite/ld-i386/tlsgdesc3.d          |   2 +-
> > >  ld/testsuite/ld-i386/tlsie2.d             |   2 +-
> > >  ld/testsuite/ld-i386/tlsie3.d             |   2 +-
> > >  ld/testsuite/ld-i386/tlsie4.d             |   2 +-
> > >  ld/testsuite/ld-i386/tlsie5.d             |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsdesc3.d         |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsdesc4.d         |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsdesc5.d         |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsie2.d           |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsie3.d           |   2 +-
> > >  ld/testsuite/ld-x86-64/tlsie5.d           |   2 +-
> > >  opcodes/i386-gen.c                        |   1 +
> > >  opcodes/i386-opc.h                        |   1 +
> > >  opcodes/i386-reg.tbl                      |   2 +-
> > >  35 files changed, 634 insertions(+), 44 deletions(-)
> > >  create mode 100644 gas/testsuite/gas/i386/tls.d
> > >  create mode 100644 gas/testsuite/gas/i386/tls.s
> > >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.d
> > >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.s
> > >
> > > diff --git a/gas/config.in b/gas/config.in
> > > index a1f83499332..3f199d5b4a6 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 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 a9d3536429b..a0222580106 100644
> > > --- a/gas/config/tc-i386.c
> > > +++ b/gas/config/tc-i386.c
> > > @@ -274,6 +274,23 @@ enum i386_error
> > >      internal_error,
> > >    };
> > >
> > > +enum x86_tls_error_type
> > > +{
> > > +  x86_tls_error_none,

Add x86_tls_error_continue.

> > > +  x86_tls_error_insn,
> > > +  x86_tls_error_sib,
> > > +  x86_tls_error_no_base_reg,
> > > +  x86_tls_error_require_no_base_index_reg,
> > > +  x86_tls_error_base_reg_name,
> > > +  x86_tls_error_index_RegB,
> > > +  x86_tls_error_RegA,
> > > +  x86_tls_error_RegB,
> > > +  x86_tls_error_RegIP,
> > > +  x86_tls_error_dest_RegA,
> > > +  x86_tls_error_dest_RegDI,
> > > +  x86_tls_error_scale_factor
> > > +};
> > > +
> > >  struct _i386_insn
> > >    {

Add

bool has_gotrel;

and set it to true for relocation from the gotrel array.

> > >      /* TM holds the template for the insn were currently assembling.  */
> > > @@ -717,6 +734,9 @@ lfence_before_ret;
> > >  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,
> > > @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const insn_template *t)
> > >                && (t->base_opcode | 8) == 0x2c);
> > >  }
> > >
> > > +static enum x86_tls_error_type
> > > +x86_check_tls_relocation (unsigned int r_type)
>
> Please use enum bfd_reloc_code_real r_type.
>
> > > +{
> > > +  switch (r_type)
> > > +    {
> > > +    case BFD_RELOC_386_TLS_GOTDESC:
> > > +      /* Check transition from GDesc access model:
> > > +
> > > +        leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> > > +                                       SIB is not supported.
> > > +       */
> > > +      if (i.tm.mnem_off != MN_lea)
> > > +       return x86_tls_error_insn;
> > > +      else if (i.index_reg)
> > > +       return x86_tls_error_sib;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> > > +       return x86_tls_error_RegB;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_386_TLS_GD:
> > > +      /* Check transition from GD access model:
> > > +
> > > +        leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
> > > +        leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> > > +                                            Memory reg can't be %eax.
> > > +       */
> > > +      if (i.tm.mnem_off != MN_lea)
> > > +       return x86_tls_error_insn;
> > > +      else if (i.index_reg)
> > > +       {
> > > +         if (i.base_reg)
> > > +           return x86_tls_error_base_reg_name;
> > > +         if (i.index_reg->reg_type.bitfield.instance != RegB)
> > > +           return x86_tls_error_index_RegB;
> > > +         else if (i.types[1].bitfield.instance != Accum)
> > > +           return x86_tls_error_dest_RegA;
> > > +         else 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_RegA;
> > > +         else if (i.types[1].bitfield.instance != Accum)
> > > +           return x86_tls_error_dest_RegA;
> > > +       }
> > > +      break;
> > > +
> > > +    case BFD_RELOC_386_TLS_LDM:
> > > +      /*  Check transition from LDM access model:
> > > +
> > > +         leal foo@tlsldm(%reg), %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;
> > > +      else if (i.index_reg)
> > > +       return x86_tls_error_sib;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> > > +       return x86_tls_error_RegA;
> > > +      else if (i.types[1].bitfield.instance != Accum)
> > > +       return x86_tls_error_dest_RegA;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> > > +      /* Check transition from GOTPC32 TLSDESC access model:
> > > +
> > > +        --- LP64 mode ---
> > > +        leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> > > +
> > > +        --- X32 mode ---
> > > +        rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> > > +
> > > +       */
> > > +      if (i.tm.mnem_off != MN_lea)
> > > +       return x86_tls_error_insn;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_num != RegIP)
> > > +       return x86_tls_error_RegIP;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_X86_64_TLSGD:
> > > +      /* Check transition from GD access model:
> > > +
> > > +        leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> > > +       */
> > > +    case BFD_RELOC_X86_64_TLSLD:
> > > +      /* Check transition from 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;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_num != RegIP)
> > > +       return x86_tls_error_RegIP;
> > > +      else if (i.types[1].bitfield.instance != RegDI)
>
> Use
>
>       else if (i.types[1].bitfield.class != Reg
>                || !i.op[1].regs->reg_type.bitfield.qword
>                || i.op[1].regs->reg_num != EDI_REG_NUM)
>         return x86_tls_error_dest_RegDI;
>
> > > +       return x86_tls_error_dest_RegDI;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_386_TLS_GOTIE:
> > > +      /* Check transition from 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 transition from 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;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.index_reg)
> > > +       return x86_tls_error_sib;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_386_TLS_IE:
> > > +      /* Check transition from IE access model:
> > > +
> > > +        movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > > +        addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > > +       */
> > > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > > +       return x86_tls_error_insn;
> > > +      else if (i.base_reg || i.index_reg)
> > > +       return x86_tls_error_require_no_base_index_reg;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_X86_64_GOTTPOFF:
> > > +      /* Check transition from GOTTPOFF access model:
> > > +
> > > +        mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > > +        add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > > +       */
> > > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > > +       return x86_tls_error_insn;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_num != RegIP)
> > > +       return x86_tls_error_RegIP;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_386_TLS_DESC_CALL:
> > > +      /* Check transition from GDesc access model:
> > > +
> > > +        call *x@tlscall(%eax) --> Memory reg must be %eax and
> > > +                                  SIB is not supported.
> > > +       */
> > > +      if (i.tm.mnem_off != MN_call)
> > > +       return x86_tls_error_insn;
> > > +      else if (i.index_reg)
> > > +       return x86_tls_error_sib;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > > +       return x86_tls_error_RegA;
> > > +      break;
> > > +
> > > +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> > > +      /* Check transition from 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;
> > > +      else if (i.index_reg)
> > > +       return x86_tls_error_sib;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > > +       return x86_tls_error_RegA;
> > > +      break;
> > > +

  case BFD_RELOC_NONE:
      return x86_tls_error_continue;

> > > +    default:
> > > +      return x86_tls_error_none;
                 break;
> > > +    }
> > > +
> > > +  return x86_tls_error_none;
> > > +}
> > > +
> > > +static void
> > > +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int r_type)

Use 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;
> > > +    }

Drop {}.

> > > +
> > > +  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_sib:
> > > +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_no_base_reg:
> > > +      as_bad (_("@%s operator must have base regster"), gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_require_no_base_index_reg:
> > > +      as_bad (_("@%s operator should not have base/index register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_base_reg_name:
> > > +      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_index_RegB:
> > > +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_RegA:
> > > +      as_bad (_("@%s operator only supports eax/rax as base register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_RegB:
> > > +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_RegIP:
> > > +      as_bad (_("@%s operator only supports rip as base register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_dest_RegA:
> > > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_dest_RegDI:
> > > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > > +             gotrel[k].str);
> > > +      return;
> > > +
> > > +    case x86_tls_error_scale_factor:
> > > +      as_bad (_("@%s operator only supports scale factor of 00"),
> > > +             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.  */
> > > @@ -6656,6 +6954,14 @@ i386_assemble (char *line)
> > >         i.prefix[LOCK_PREFIX] = 0;
> > >      }
> > >
> > > +  if (tls_check)

if (i.has_gotrel && tls_check)

> > > +    {
> > > +      enum x86_tls_error_type tls_error;
> > > +      for (j = 0; j < i.operands; ++j)
> > > +       if ((tls_error = x86_check_tls_relocation (i.reloc[j])) != x86_tls_error_none)
> > > +         x86_report_tls_error (tls_error, i.reloc[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)
> > > @@ -6666,28 +6972,6 @@ i386_assemble (char *line)
> > >           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.  */
> > > @@ -16184,6 +16468,7 @@ const char *md_shortopts = "qnO::";
> > >  #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[] =
> > >  {
> > > @@ -16230,6 +16515,7 @@ struct option md_longopts[] =
> > >    {"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);
> > > @@ -16786,6 +17072,14 @@ md_parse_option (int c, const char *arg)
> > >           optimize_for_space = 0;
> > >         }
> > >        break;
> > > +    case OPTION_MTLS_CHECK:
> > > +      if (strcasecmp (arg, "yes") == 0)
> > > +       tls_check = 1;
> > > +      else if (strcasecmp (arg, "no") == 0)
> > > +       tls_check = 0;
> > > +      else
> > > +       as_fatal (_("invalid -mtls-check= option: `%s'"), arg);
> > > +      break;
> > >
> > >      default:
> > >        return 0;
> > > @@ -17028,6 +17322,16 @@ md_show_usage (FILE *stream)
> > >      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 6b96d3a4e0c..a4cdeb80900 100755
> > > --- a/gas/configure
> > > +++ b/gas/configure
> > > @@ -818,6 +818,7 @@ enable_checking
> > >  enable_compressed_debug_sections
> > >  enable_default_compressed_debug_sections_algorithm
> > >  enable_x86_relax_relocations
> > > +enable_tls_check
> > >  enable_elf_stt_common
> > >  enable_generate_build_notes
> > >  enable_mips_fix_loongson3_llsc
> > > @@ -1493,6 +1494,7 @@ Optional Features:
> > >                            --enable-compressed-debug-sections.
> > >    --enable-x86-relax-relocations
> > >                            generate x86 relax relocations by default
> > > +  --enable-tls-check      check 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 @@ else
> > >    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 @@ else
> > >    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 @@ if test "${enable_x86_relax_relocations+set}" = set; then :
> > >  esac
> > >  fi
> > >
> > > +# PR gas/32022
> > > +# Decide if check tls relocation.
> > > +ac_default_x86_tls_check=unset
> > > +# Provide a configure time option to override our default.
> > > +# Check whether --enable-tls_check was given.
> > > +if test "${enable_tls_check+set}" = set; then :
> > > +  enableval=$enable_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 @@ cat >>confdefs.h <<_ACEOF
> > >  _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 6b978aae3f7..5fe57e52746 100644
> > > --- a/gas/configure.ac
> > > +++ b/gas/configure.ac
> > > @@ -95,6 +95,17 @@ AC_ARG_ENABLE(x86_relax_relocations,
> > >    no)  ac_default_x86_relax_relocations=0 ;;
> > >  esac])dnl
> > >
> > > +# PR gas/32022
> > > +# Decide if check tls relocation.
> > > +ac_default_x86_tls_check=unset
> > > +# Provide a configure time option to override our default.
> > > +AC_ARG_ENABLE(tls_check,
> > > +             AS_HELP_STRING([--enable-tls-check],
> > > +             [check 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_DEFINE_UNQUOTED(DEFAULT_GENERATE_X86_RELAX_RELOCATIONS,
> > >    $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 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 a9e43560aea..5da673a0287 100644
> > > --- a/gas/doc/c-i386.texi
> > > +++ b/gas/doc/c-i386.texi
> > > @@ -467,6 +467,16 @@ R_X86_64_REX_GOTPCRELX, in 64-bit mode.
> > >  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-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 75ad061b32c..6710a56d64a 100644
> > > --- a/gas/testsuite/gas/i386/i386.exp
> > > +++ b/gas/testsuite/gas/i386/i386.exp
> > > @@ -699,10 +699,11 @@ if [gas_32_check] then {
> > >         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 a3017388934..b32da8725b5 100644
> > > --- a/gas/testsuite/gas/i386/ilp32/ilp32.exp
> > > +++ b/gas/testsuite/gas/i386/ilp32/ilp32.exp
> > > @@ -37,7 +37,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_x32_check] &
> > >         }
> > >      }
> > >
> > > -    run_list_test "reloc64" "--defsym _bad_=1"
> > > +    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
> > >      run_list_test "reloc-2"
> > >
> > >      set ASFLAGS "$old_ASFLAGS"
> > > diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.d b/gas/testsuite/gas/i386/ilp32/reloc64.d
> > > index e2c461f24e8..84b6aaccc01 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/inval-tls.l b/gas/testsuite/gas/i386/inval-tls.l
> > > index 98f7a29f1ab..40443bf68a2 100644
> > > --- a/gas/testsuite/gas/i386/inval-tls.l
> > > +++ b/gas/testsuite/gas/i386/inval-tls.l
> > > @@ -1,2 +1,30 @@
> > >  .*: Assembler messages:
> > >  .*:3: Error: @GOTNTPOFF operator cannot be used with `kmovd'
> > > +.*:6: Error: @TLSGD operator cannot be used with `add'
> > > +.*:7: Error: @TLSGD operator only supports ebx/rbx as index register
> > > +.*:8: Error: @TLSGD operator only supports scale factor of 00
> > > +.*:9: Error: @TLSGD operator should not have base register
> > > +.*:10: Error: @TLSGD operator only supports eax/rax as dest register
> > > +.*:11: Error: @TLSGD operator only supports eax/rax as dest register
> > > +.*:12: Error: @TLSGD operator only supports eax/rax as base register
> > > +.*:13: Error: 4-byte relocation cannot be applied to 2-byte field
> > > +.*:16: Error: @TLSLDM operator cannot be used with `add'
> > > +.*:17: Error: @TLSLDM operator only supports eax/rax as dest register
> > > +.*:18: Error: @TLSLDM operator only supports eax/rax as base register
> > > +.*:19: Error: @TLSLDM operator do not support SIB
> > > +.*:20: Error: 4-byte relocation cannot be applied to 2-byte field
> > > +.*:23: Error: @TLSDESC operator cannot be used with `add'
> > > +.*:24: Error: @TLSDESC operator only supports ebx/rbx as base register
> > > +.*:25: Error: @TLSDESC operator do not support SIB
> > > +.*:28: Error: @INDNTPOFF operator cannot be used with `sub'
> > > +.*:29: Error: @INDNTPOFF operator should not have base/index register
> > > +.*:30: Error: @INDNTPOFF operator should not have base/index register
> > > +.*:34: Error: @GOTNTPOFF operator cannot be used with `lea'
> > > +.*:35: Error: @GOTNTPOFF operator must have base regster
> > > +.*:38: Error: @GOTTPOFF operator cannot be used with `lea'
> > > +.*:39: Error: @GOTTPOFF operator must have base regster
> > > +.*:40: Error: 4-byte relocation cannot be applied to 2-byte field
> > > +.*:43: Error: @TLSCALL operator cannot be used with `lea'
> > > +.*:44: Error: @TLSCALL operator only supports eax/rax as base register
> > > +.*:45: Error: @TLSCALL operator do not support SIB
> > > +.*:45: Error: 0-byte relocation cannot be applied to 4-byte field
> > > diff --git a/gas/testsuite/gas/i386/inval-tls.s b/gas/testsuite/gas/i386/inval-tls.s
> > > index ba0e1b81dc9..d00dbfa3008 100644
> > > --- a/gas/testsuite/gas/i386/inval-tls.s
> > > +++ b/gas/testsuite/gas/i386/inval-tls.s
> > > @@ -1,3 +1,45 @@
> > >         .text
> > >  # All the following should be illegal
> > >         kmovd   foo@gotntpoff(%eax), %k0
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_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(%bx), %ax
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTDESC.  */
> > > +       addl x@tlsdesc(%ebx), %eax
> > > +       leal x@tlsdesc(%ecx), %eax
> > > +       leal x@tlsdesc(,%ecx,1), %eax
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_386_TLS_IE.  */
> > > +       subl foo@indntpoff, %ecx
> > > +       addl foo@indntpoff(%ebx), %ecx
> > > +       movl foo@indntpoff(%ebx), %ecx
> > > +       add foo@indntpoff, %cx
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_386_TLS_GOTIE.  */
> > > +       leal foo@gotntpoff(%ebx), %ecx
> > > +       subl foo@gotntpoff(,%ebx,1), %ecx
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_386_TLS_IE_32.  */
> > > +       leal foo@gottpoff(%ebx), %ecx
> > > +       subl foo@gottpoff(,%ebx,1), %ecx
> > > +       add foo@gottpoff(%bx), %cx
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_386_TLS_DESC_CALL.  */
> > > +       leal foo@tlscall(%eax), %ebx
> > > +       call *x@tlscall(%ebx)
> > > +       call *x@tlscall(,%eax,1)
> > > diff --git a/gas/testsuite/gas/i386/reloc32.d b/gas/testsuite/gas/i386/reloc32.d
> > > index 263a742022e..7d1b1ba2ae0 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 540a9b77d35..f4e44a627c5 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 00000000000..adfe7ce9551
> > > --- /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 00000000000..7a0a83147e4
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/i386/tls.s
> > > @@ -0,0 +1,31 @@
> > > +# Check tls relocation 32-bit mode
> > > +
> > > +       .text
> > > +_start:
> > > +       /* BFD_RELOC_386_TLS_GD.  */
> > > +       leal foo@tlsgd(,%ebx,1), %eax
> > > +       leal foo@tlsgd(%ecx), %eax
> > > +
> > > +       /* BFD_RELOC_386_TLS_LDM.  */
> > > +       leal foo@tlsldm(%ebx), %eax
> > > +
> > > +       /* BFD_RELOC_386_TLS_GOTDESC.  */
> > > +       leal x@tlsdesc(%ebx), %eax
> > > +
> > > +       /* BFD_RELOC_386_TLS_IE.  */
> > > +       movl foo@indntpoff, %eax
> > > +       movl foo@indntpoff, %ebx
> > > +       addl foo@indntpoff, %edx
> > > +
> > > +       /* BFD_RELOC_386_TLS_GOTIE.  */
> > > +       subl foo@gotntpoff(%ebx), %ecx
> > > +       movl foo@gotntpoff(%ebx), %ecx
> > > +       addl foo@gotntpoff(%ebx), %ecx
> > > +
> > > +       /* BFD_RELOC_386_TLS_IE_32.  */
> > > +       subl foo@gottpoff(%ebx), %ecx
> > > +       movl foo@gottpoff(%ebx), %ecx
> > > +       addl foo@gottpoff(%ebx), %ecx
> > > +
> > > +       /* BFD_RELOC_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 da8ac19f869..b9a53693c4f 100644
> > > --- a/gas/testsuite/gas/i386/x86-64-inval-tls.l
> > > +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.l
> > > @@ -1,3 +1,20 @@
> > >  .*: 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 only supports eax/rax as dest register
> > > +.*:9: Error: @TLSGD operator only supports rip as base register
> > > +.*:10: Error: @TLSGD operator must have base regster
> > > +.*:13: Error: @TLSLD operator cannot be used with `add'
> > > +.*:14: Error: @TLSLD operator only supports eax/rax as dest register
> > > +.*:15: Error: @TLSLD operator only supports rip as base register
> > > +.*:16: Error: @TLSLD operator must have base regster
> > > +.*:19: Error: @TLSDESC operator cannot be used with `add'
> > > +.*:20: Error: @TLSDESC operator only supports rip as base register
> > > +.*:23: Error: @GOTTPOFF operator cannot be used with `sub'
> > > +.*:24: Error: @GOTTPOFF operator cannot be used with `xor'
> > > +.*:25: Error: @GOTTPOFF operator only supports rip as base register
> > > +.*:26: Error: @GOTTPOFF operator only supports rip as base register
> > > +.*:29: Error: @TLSCALL operator cannot be used with `lea'
> > > +.*:30: Error: @TLSCALL operator only supports eax/rax as base register
> > > +.*:30: Error: 0-byte relocation cannot be applied to 4-byte field
> > > diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.s b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > > index 71e19272ba9..ccefeb780b6 100644
> > > --- a/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > > +++ b/gas/testsuite/gas/i386/x86-64-inval-tls.s
> > > @@ -2,3 +2,29 @@
> > >  # All the following should be illegal
> > >         kmovq   foo@gottpoff(%rip), %k0
> > >         kmovq   foo@tlsld(%rip), %k0
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSGD.  */
> > > +       addq foo@tlsgd(%rip), %rdi
> > > +       leaq foo@tlsgd(%rip), %rax
> > > +       leaq foo@tlsgd(%rax), %rdi
> > > +       leaq foo@tlsgd(,%rax,1), %rdi
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSLD.  */
> > > +       addq foo@tlsld(%rip), %rdi
> > > +       leaq foo@tlsld(%rip), %rax
> > > +       leaq foo@tlsld(%rax), %rdi
> > > +       leaq foo@tlsld(,%rax,1), %rdi
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> > > +       addq x@tlsdesc(%rip), %rax
> > > +       leaq x@tlsdesc(%rbx), %rax
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_X86_64_GOTTPOFF.  */
> > > +       subq foo@gottpoff(%rip), %r12
> > > +       xorq foo@gottpoff(%rip), %rax
> > > +       movq foo@gottpoff(%rbx), %rax
> > > +       addq foo@gottpoff(%rbx), %rax
> > > +
> > > +       /* Invalid testcase for BFD_RELOC_X86_64_TLSDESC_CALL.  */
> > > +       leaq foo@tlscall(%rax), %rbx
> > > +       call *foo@tlscall(%rip)
> > > 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 00000000000..c4e5a8a4b29
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/i386/x86-64-tls.d
> > > @@ -0,0 +1,29 @@
> > > +#as:
> > > +#objdump: -drw
> > > +#name: Check tls relocation x86-64
> > > +
> > > +.*: +file format .*
> > > +
> > > +
> > > +Disassembly of section .text:
> > > +
> > > +0000000000000000 <_start>:
> > > +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # 7 <_start\+0x7>        3: R_X86_64_TLSGD       foo-0x4
> > > +\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00   lea    0x0\(%rip\),%rdi        # e <_start\+0xe>        a: R_X86_64_TLSLD       foo-0x4
> > > +\s*[a-f0-9]+:\s*48 8d 05 00 00 00 00   lea    0x0\(%rip\),%rax        # 15 <_start\+0x15>      11: R_X86_64_GOTPC32_TLSDESC    x-0x4
> > > +\s*[a-f0-9]+:\s*40 8d 05 00 00 00 00   rex lea 0x0\(%rip\),%eax        # 1c <_start\+0x1c>     18: R_X86_64_GOTPC32_TLSDESC    x-0x4
> > > +\s*[a-f0-9]+:\s*d5 40 8d 05 00 00 00 00        lea    0x0\(%rip\),%r16d        # 24 <_start\+0x24>     20: R_X86_64_CODE_4_GOTPC32_TLSDESC     x-0x4
> > > +\s*[a-f0-9]+:\s*4c 03 25 00 00 00 00   add    0x0\(%rip\),%r12        # 2b <_start\+0x2b>      27: R_X86_64_GOTTPOFF   foo-0x4
> > > +\s*[a-f0-9]+:\s*48 8b 05 00 00 00 00   mov    0x0\(%rip\),%rax        # 32 <_start\+0x32>      2e: R_X86_64_GOTTPOFF   foo-0x4
> > > +\s*[a-f0-9]+:\s*44 03 25 00 00 00 00   add    0x0\(%rip\),%r12d        # 39 <_start\+0x39>     35: R_X86_64_GOTTPOFF   foo-0x4
> > > +\s*[a-f0-9]+:\s*8b 05 00 00 00 00      mov    0x0\(%rip\),%eax        # 3f <_start\+0x3f>      3b: R_X86_64_GOTTPOFF   foo-0x4
> > > +\s*[a-f0-9]+:\s*d5 48 03 05 00 00 00 00        add    0x0\(%rip\),%r16        # 47 <_start\+0x47>      43: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*d5 48 8b 25 00 00 00 00        mov    0x0\(%rip\),%r20        # 4f <_start\+0x4f>      4b: R_X86_64_CODE_4_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*62 64 bc 18 01 35 00 00 00 00  add    %r30,0x0\(%rip\),%r8        # 59 <_start\+0x59>  55: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*62 f4 dc 10 03 05 00 00 00 00  add    0x0\(%rip\),%rax,%r20        # 63 <_start\+0x63> 5f: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*62 e4 fc 0c 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%r16        # 6d <_start\+0x6d>    69: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*62 64 bc 1c 01 35 00 00 00 00  {nf} add %r30,0x0\(%rip\),%r8        # 77 <_start\+0x77>        73: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*62 f4 dc 14 03 05 00 00 00 00  {nf} add 0x0\(%rip\),%rax,%r20        # 81 <_start\+0x81>       7d: R_X86_64_CODE_6_GOTTPOFF    foo-0x4
> > > +\s*[a-f0-9]+:\s*ff 10                  call   \*\(%rax\)       81: R_X86_64_TLSDESC_CALL       x
> > > +\s*[a-f0-9]+:\s*67 ff 10               call   \*\(%eax\)       83: 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 00000000000..4c573922773
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/i386/x86-64-tls.s
> > > @@ -0,0 +1,31 @@
> > > +# Check tls relocation 64-bit mode
> > > +
> > > +       .text
> > > +_start:
> > > +       /* BFD_RELOC_X86_64_TLSGD.  */
> > > +       leaq foo@tlsgd(%rip), %rdi
> > > +
> > > +       /* BFD_RELOC_X86_64_TLSLD.  */
> > > +       leaq foo@tlsld(%rip), %rdi
> > > +
> > > +       /* BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
> > > +       leaq     x@tlsdesc(%rip), %rax
> > > +       rex leal x@tlsdesc(%rip), %eax
> > > +       leal     x@tlsdesc(%rip), %r16d
> > > +
> > > +       /* BFD_RELOC_X86_64_GOTTPOFF.  */
> > > +       addq     foo@gottpoff(%rip), %r12
> > > +       movq     foo@gottpoff(%rip), %rax
> > > +       addl     foo@gottpoff(%rip), %r12d
> > > +       movl     foo@gottpoff(%rip), %eax
> > > +       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
> > > +
> > > +       /* BFD_RELOC_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 86e7f4a75b3..740f5268de3 100644
> > > --- a/gas/testsuite/gas/i386/x86-64.exp
> > > +++ b/gas/testsuite/gas/i386/x86-64.exp
> > > @@ -653,7 +653,8 @@ if [is_elf_format] then {
> > >      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 2a70e81c444..0c853ab0a67 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 bbf93bef767..99e1b18b1ea 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 f2c29d880f2..4bb99c4422b 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 9f9e63029d6..4e7dc6ea56e 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 506f1a02605..6bfc78e0b49 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 a516d002660..98293f4b36a 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 d3447182e19..4e9c9a8f74a 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 bbf22ebeafe..955884885d7 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 c882c877ae3..ccaa525c74b 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 6a0158b44b7..0876993bca3 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 bf8a8198b5b..2e6d41ccf61 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 49d8464fbaf..b59cc6429de 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 29de1cebf8e..d7ab5ab7b32 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
> > > diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c
> > > index 565aae722f8..dd5e8744efe 100644
> > > --- a/opcodes/i386-gen.c
> > > +++ b/opcodes/i386-gen.c
> > > @@ -526,6 +526,7 @@ static const struct {
> > >      INSTANCE (RegC),
> > >      INSTANCE (RegD),
> > >      INSTANCE (RegB),
> > > +    INSTANCE (RegDI),
> > >  };
> > >
> > >  #undef INSTANCE
> > > diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
> > > index c0d5e44d461..608e238fa18 100644
> > > --- a/opcodes/i386-opc.h
> > > +++ b/opcodes/i386-opc.h
> > > @@ -843,6 +843,7 @@ enum operand_instance
> > >    RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
> > >    RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
> > >    RegB,  /* %bl / %bx / %ebx / %rbx */
> > > +  RegDI, /* %rdx */
> > >  };
> > >
> > >  /* Position of operand_type bits.  */
> > > diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
> > > index e3146456133..028f9523b52 100644
> > > --- a/opcodes/i386-reg.tbl
> > > +++ b/opcodes/i386-reg.tbl
> > > @@ -135,7 +135,7 @@ rbx, Class=Reg|Instance=RegB|Qword|BaseIndex, 0, 3, Dw2Inval, 3
> > >  rsp, Class=Reg|Qword, 0, 4, Dw2Inval, 7
> > >  rbp, Class=Reg|Qword|BaseIndex, 0, 5, Dw2Inval, 6
> > >  rsi, Class=Reg|Qword|BaseIndex, 0, 6, Dw2Inval, 4
> > > -rdi, Class=Reg|Qword|BaseIndex, 0, 7, Dw2Inval, 5
> > > +rdi, Class=Reg|Instance=RegDI|Qword|BaseIndex, 0, 7, Dw2Inval, 5
>
> Remove it.  Unused and not needed.
>
> > >  r8, Class=Reg|Qword|BaseIndex, RegRex, 0, Dw2Inval, 8
> > >  r9, Class=Reg|Qword|BaseIndex, RegRex, 1, Dw2Inval, 9
> > >  r10, Class=Reg|Qword|BaseIndex, RegRex, 2, Dw2Inval, 10
> > > --
> > > 2.34.1
> > >
> >
> > This failed with GCC 14:
> >
> > FAIL: Check tls relocation x86-64
> > FAIL: Build size-8
> >
> > gcc  -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
> > -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> > -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> > /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> > -o tmpdir/size-8b.o
> > Executing on host: sh -c {gcc
> > -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-linux/ld/tmpdir/ld/
> > -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> > -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> > /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> > -o tmpdir/size-8b.o 2>&1}  /dev/null ld.tmp (timeout = 300)
> > spawn [open ...]^M
> > /tmp/cc6aqbbv.s: Assembler messages:
> > /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> > dest register
> > /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> > dest register
> >
> > UNSUPPORTED: Build libsize-8.so
> >
> > FAIL: Run size-2
> > FAIL: Run size-8
> > FAIL: Run size-10
> > FAIL: Build libtls-1b.so
> > FAIL: Build libtls-1bn.so
> > FAIL: TLS GD/LD -> LE transition without PLT (dynamic)
> > FAIL: TLS GD/LD -> LE transition without PLT (dynamic, -z now)
> > FAIL: TLS GD/LD -> LE transition without PLT (PIE)
> > FAIL: TLS GD/LD -> LE transition without PLT (PIE, -z now)
> > FAIL: TLS GD/LD -> LE transition without PLT (static)
> > FAIL: TLS GD/LD -> IE transition without PLT
> > FAIL: TLS GD/LD -> IE transition without PLT (-z now)
> > FAIL: TLS without PLT (1)
> > FAIL: TLS without PLT (2)
> > FAIL: TLS without PLT (3)
> > FAIL: TLS without PLT (4)
> > FAIL: TLS -fpic and -fno-pic exec transitions
> > FAIL: TLS -fpic and -fno-pic exec transitions without PLT
> > FAIL: TLS with global dynamic and descriptors
> > FAIL: TLS GD->LE transition
> > FAIL: TLS LD->LE transition
> > FAIL: TLS X32 GD->LE transition
> > FAIL: TLS GD->IE transition
> > FAIL: TLS GD->IE transition without PLT
> > FAIL: TLS X32 GD->IE transition
> > FAIL: TLS X32 GD->IE transition without PLT
> > FAIL: TLS X32 LD->LE transition
> > FAIL: TLS -mcmodel=large GD->LE transition
> > FAIL: TLS -mcmodel=large LD->LE transition
> > FAIL: TLS -mcmodel=large LD->LE transition with r15 as GOT base
> > FAIL: TLS LD->LE transition without PLT
> > FAIL: TLS X32 LD->LE transition without PLT
> > FAIL: TLS -mcmodel=large GD->IE transition
> > FAIL: TLS -mcmodel=large GD->LE transition with r15 as GOT base
> > FAIL: TLS -mcmodel=large GD->IE transition with r15 as GOT base
> > FAIL: TLS GD->LE transition without PLT
> > FAIL: TLS X32 GD->LE transition without PLT
> > FAIL: TLS -fpic -shared transitions
> > FAIL: TLS -fpic -shared transitions with r15 as GOT base
> > FAIL: TLS GD->IE transition check
> > FAIL: TLS GD->LE transition check
> > FAIL: TLS GD->IE transition check without PLT
> > FAIL: TLS GD->LE transition check without PLT
> > FAIL: TLS with PIE (1 x86_64)
> > FAIL: ld-x86-64/tlspie2a
> > FAIL: ld-x86-64/tlspie2b
> > FAIL: ld-x86-64/tlspie2c
> > FAIL: ld-x86-64/align-branch-1
> >
> >
> > --
> > H.J.
>
>
>
> --
> H.J.
  
H.J. Lu Sept. 6, 2024, 2:06 p.m. UTC | #4
On Fri, Sep 6, 2024 at 6:51 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Fri, Sep 6, 2024 at 6:07 AM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > On Fri, Sep 6, 2024 at 5:34 AM H.J. Lu <hjl.tools@gmail.com> wrote:
> > >
> > > On Thu, Sep 5, 2024 at 1:57 AM Cui, Lili <lili.cui@intel.com> wrote:
> > > >
> > > > Sorry, added v2 to the patch name
> > > >
> > > > ---
> > > > 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. 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.
> > > >
> > > > ---
> > > > V2:
> > > > 1. Add format checks for each operator. And add a function to report errors.
> > > > 2. Replace DEFAULT_TLS_CHECK with DEFAULT_X86_TLS_CHECK, and all related names.
> > > > 3. Add more invalid test cases.
> > > > 4. Remove the handling of "call *x@tlsdesc(%rax)", which is now illegal.
> > > > 5. Add new INSTANCE RegDI
> > > > 6. Use option drw for objdump.
> > > > ---
> > > >
> > > > gas/ChangeLog:
> > > >
> > > >         PR gas/32022
> > > >         * config.in: Regenerate.
> > > >         * config/tc-i386.c (_BFD_MAKE_TABLE_bfd_reloc_code_real): New.
> > > >         (x86_check_tls_relocation): Added a new function to check tls relocation.
> > > >         (i386_assemble): Handle x86_check_tls_relocation.
> > > >         (OPTION_MTLS_CHECK): Added a new option to contrl tls check.
> > > >         (struct option): Ditto.
> > > >         (md_parse_option): Ditto.
> > > >         (md_show_usage): Ditto.
> > > >         * configure: Added a new option to check tls relocation by default.
> > > >         * configure.ac: Ditto.
> > > >         * doc/c-i386.texi: Ditto.
> > > >         * 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/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/tls.d: New test.
> > > >         * testsuite/gas/i386/tls.s: Ditto.
> > > >         * testsuite/gas/i386/x86-64-tls.d: Ditto.
> > > >         * testsuite/gas/i386/x86-64-tls.s: Ditto.
> > > >
> > > > opcodes/ChangeLog:
> > > >
> > > >         PR gas/32022
> > > >         * i386-gen.c: Added new INSTANCE RegDI.
> > > >         * i386-opc.h (enum operand_instance): Ditto.
> > > >         * i386-reg.tbl: Added RegDI to rdi.
> > > >
> > > > 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.
> > > > ---
> > > >  gas/config.in                             |   3 +
> > > >  gas/config/tc-i386.c                      | 348 ++++++++++++++++++++--
> > > >  gas/configure                             |  26 +-
> > > >  gas/configure.ac                          |  18 ++
> > > >  gas/doc/c-i386.texi                       |  10 +
> > > >  gas/testsuite/gas/i386/i386.exp           |   3 +-
> > > >  gas/testsuite/gas/i386/ilp32/ilp32.exp    |   2 +-
> > > >  gas/testsuite/gas/i386/ilp32/reloc64.d    |   2 +-
> > > >  gas/testsuite/gas/i386/inval-tls.l        |  28 ++
> > > >  gas/testsuite/gas/i386/inval-tls.s        |  42 +++
> > > >  gas/testsuite/gas/i386/reloc32.d          |   2 +-
> > > >  gas/testsuite/gas/i386/reloc64.d          |   2 +-
> > > >  gas/testsuite/gas/i386/tls.d              |  25 ++
> > > >  gas/testsuite/gas/i386/tls.s              |  31 ++
> > > >  gas/testsuite/gas/i386/x86-64-inval-tls.l |  17 ++
> > > >  gas/testsuite/gas/i386/x86-64-inval-tls.s |  26 ++
> > > >  gas/testsuite/gas/i386/x86-64-tls.d       |  29 ++
> > > >  gas/testsuite/gas/i386/x86-64-tls.s       |  31 ++
> > > >  gas/testsuite/gas/i386/x86-64.exp         |   3 +-
> > > >  ld/testsuite/ld-i386/tlsgdesc1.d          |   2 +-
> > > >  ld/testsuite/ld-i386/tlsgdesc2.d          |   2 +-
> > > >  ld/testsuite/ld-i386/tlsgdesc3.d          |   2 +-
> > > >  ld/testsuite/ld-i386/tlsie2.d             |   2 +-
> > > >  ld/testsuite/ld-i386/tlsie3.d             |   2 +-
> > > >  ld/testsuite/ld-i386/tlsie4.d             |   2 +-
> > > >  ld/testsuite/ld-i386/tlsie5.d             |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsdesc3.d         |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsdesc4.d         |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsdesc5.d         |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsie2.d           |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsie3.d           |   2 +-
> > > >  ld/testsuite/ld-x86-64/tlsie5.d           |   2 +-
> > > >  opcodes/i386-gen.c                        |   1 +
> > > >  opcodes/i386-opc.h                        |   1 +
> > > >  opcodes/i386-reg.tbl                      |   2 +-
> > > >  35 files changed, 634 insertions(+), 44 deletions(-)
> > > >  create mode 100644 gas/testsuite/gas/i386/tls.d
> > > >  create mode 100644 gas/testsuite/gas/i386/tls.s
> > > >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.d
> > > >  create mode 100644 gas/testsuite/gas/i386/x86-64-tls.s
> > > >
> > > > diff --git a/gas/config.in b/gas/config.in
> > > > index a1f83499332..3f199d5b4a6 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 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 a9d3536429b..a0222580106 100644
> > > > --- a/gas/config/tc-i386.c
> > > > +++ b/gas/config/tc-i386.c
> > > > @@ -274,6 +274,23 @@ enum i386_error
> > > >      internal_error,
> > > >    };
> > > >
> > > > +enum x86_tls_error_type
> > > > +{
> > > > +  x86_tls_error_none,
>
> Add x86_tls_error_continue.
>
> > > > +  x86_tls_error_insn,
> > > > +  x86_tls_error_sib,
> > > > +  x86_tls_error_no_base_reg,
> > > > +  x86_tls_error_require_no_base_index_reg,
> > > > +  x86_tls_error_base_reg_name,
> > > > +  x86_tls_error_index_RegB,
> > > > +  x86_tls_error_RegA,
> > > > +  x86_tls_error_RegB,
> > > > +  x86_tls_error_RegIP,
> > > > +  x86_tls_error_dest_RegA,
> > > > +  x86_tls_error_dest_RegDI,
> > > > +  x86_tls_error_scale_factor
> > > > +};
> > > > +
> > > >  struct _i386_insn
> > > >    {
>
> Add
>
> bool has_gotrel;
>
> and set it to true for relocation from the gotrel array.
>
> > > >      /* TM holds the template for the insn were currently assembling.  */
> > > > @@ -717,6 +734,9 @@ lfence_before_ret;
> > > >  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,
> > > > @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const insn_template *t)
> > > >                && (t->base_opcode | 8) == 0x2c);
> > > >  }
> > > >
> > > > +static enum x86_tls_error_type
> > > > +x86_check_tls_relocation (unsigned int r_type)
> >
> > Please use enum bfd_reloc_code_real r_type.
> >
> > > > +{
> > > > +  switch (r_type)
> > > > +    {
> > > > +    case BFD_RELOC_386_TLS_GOTDESC:
> > > > +      /* Check transition from GDesc access model:
> > > > +
> > > > +        leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> > > > +                                       SIB is not supported.
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_lea)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (i.index_reg)
> > > > +       return x86_tls_error_sib;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> > > > +       return x86_tls_error_RegB;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_386_TLS_GD:
> > > > +      /* Check transition from GD access model:
> > > > +
> > > > +        leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
> > > > +        leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> > > > +                                            Memory reg can't be %eax.
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_lea)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (i.index_reg)
> > > > +       {
> > > > +         if (i.base_reg)
> > > > +           return x86_tls_error_base_reg_name;
> > > > +         if (i.index_reg->reg_type.bitfield.instance != RegB)
> > > > +           return x86_tls_error_index_RegB;
> > > > +         else if (i.types[1].bitfield.instance != Accum)
> > > > +           return x86_tls_error_dest_RegA;
> > > > +         else 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_RegA;
> > > > +         else if (i.types[1].bitfield.instance != Accum)
> > > > +           return x86_tls_error_dest_RegA;
> > > > +       }
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_386_TLS_LDM:
> > > > +      /*  Check transition from LDM access model:
> > > > +
> > > > +         leal foo@tlsldm(%reg), %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;
> > > > +      else if (i.index_reg)
> > > > +       return x86_tls_error_sib;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> > > > +       return x86_tls_error_RegA;
> > > > +      else if (i.types[1].bitfield.instance != Accum)
> > > > +       return x86_tls_error_dest_RegA;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> > > > +      /* Check transition from GOTPC32 TLSDESC access model:
> > > > +
> > > > +        --- LP64 mode ---
> > > > +        leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> > > > +
> > > > +        --- X32 mode ---
> > > > +        rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> > > > +
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_lea)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_num != RegIP)
> > > > +       return x86_tls_error_RegIP;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_X86_64_TLSGD:
> > > > +      /* Check transition from GD access model:
> > > > +
> > > > +        leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> > > > +       */
> > > > +    case BFD_RELOC_X86_64_TLSLD:
> > > > +      /* Check transition from 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;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_num != RegIP)
> > > > +       return x86_tls_error_RegIP;
> > > > +      else if (i.types[1].bitfield.instance != RegDI)
> >
> > Use
> >
> >       else if (i.types[1].bitfield.class != Reg
> >                || !i.op[1].regs->reg_type.bitfield.qword
> >                || i.op[1].regs->reg_num != EDI_REG_NUM)
> >         return x86_tls_error_dest_RegDI;
> >
> > > > +       return x86_tls_error_dest_RegDI;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_386_TLS_GOTIE:
> > > > +      /* Check transition from 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 transition from 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;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.index_reg)
> > > > +       return x86_tls_error_sib;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_386_TLS_IE:
> > > > +      /* Check transition from IE access model:
> > > > +
> > > > +        movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > > > +        addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (i.base_reg || i.index_reg)
> > > > +       return x86_tls_error_require_no_base_index_reg;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_X86_64_GOTTPOFF:
> > > > +      /* Check transition from GOTTPOFF access model:
> > > > +
> > > > +        mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > > > +        add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_num != RegIP)
> > > > +       return x86_tls_error_RegIP;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_386_TLS_DESC_CALL:
> > > > +      /* Check transition from GDesc access model:
> > > > +
> > > > +        call *x@tlscall(%eax) --> Memory reg must be %eax and
> > > > +                                  SIB is not supported.
> > > > +       */
> > > > +      if (i.tm.mnem_off != MN_call)
> > > > +       return x86_tls_error_insn;
> > > > +      else if (i.index_reg)
> > > > +       return x86_tls_error_sib;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > > > +       return x86_tls_error_RegA;
> > > > +      break;
> > > > +
> > > > +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> > > > +      /* Check transition from 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;
> > > > +      else if (i.index_reg)
> > > > +       return x86_tls_error_sib;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > > > +       return x86_tls_error_RegA;
> > > > +      break;
> > > > +
>
>   case BFD_RELOC_NONE:
>       return x86_tls_error_continue;
>
> > > > +    default:
> > > > +      return x86_tls_error_none;
>                  break;
> > > > +    }
> > > > +
> > > > +  return x86_tls_error_none;
> > > > +}
> > > > +
> > > > +static void
> > > > +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int r_type)
>
> Use 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;
> > > > +    }
>
> Drop {}.
>
> > > > +
> > > > +  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_sib:
> > > > +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_no_base_reg:
> > > > +      as_bad (_("@%s operator must have base regster"), gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_require_no_base_index_reg:
> > > > +      as_bad (_("@%s operator should not have base/index register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_base_reg_name:
> > > > +      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_index_RegB:
> > > > +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_RegA:
> > > > +      as_bad (_("@%s operator only supports eax/rax as base register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_RegB:
> > > > +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_RegIP:
> > > > +      as_bad (_("@%s operator only supports rip as base register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_dest_RegA:
> > > > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_dest_RegDI:
> > > > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > > > +             gotrel[k].str);
> > > > +      return;
> > > > +
> > > > +    case x86_tls_error_scale_factor:
> > > > +      as_bad (_("@%s operator only supports scale factor of 00"),
> > > > +             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.  */
> > > > @@ -6656,6 +6954,14 @@ i386_assemble (char *line)
> > > >         i.prefix[LOCK_PREFIX] = 0;
> > > >      }
> > > >
> > > > +  if (tls_check)
>
> if (i.has_gotrel && tls_check)
>
> > > > +    {
> > > > +      enum x86_tls_error_type tls_error;
> > > > +      for (j = 0; j < i.operands; ++j)
> > > > +       if ((tls_error = x86_check_tls_relocation (i.reloc[j])) != x86_tls_error_none)
> > > > +         x86_report_tls_error (tls_error, i.reloc[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;
>         }
>

This patch is on top of your patch.
  
Cui, Lili Sept. 9, 2024, 8:15 a.m. UTC | #5
> > if (i.has_gotrel && tls_check)
> >
> > > > > +    {
> > > > > +      enum x86_tls_error_type tls_error;
> > > > > +      for (j = 0; j < i.operands; ++j)
> > > > > +       if ((tls_error = x86_check_tls_relocation (i.reloc[j])) !=
> x86_tls_error_none)
> > > > > +         x86_report_tls_error (tls_error, i.reloc[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;
> >         }
> >
> 
> This patch is on top of your patch.
> 

Applied this patch, thanks! Only one place that I think there is a bug. 

> > > +    case BFD_RELOC_X86_64_TLSLD:
> > > +      /* Check transition from 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;
> > > +      else if (!i.base_reg)
> > > +       return x86_tls_error_no_base_reg;
> > > +      else if (i.base_reg->reg_num != RegIP)
> > > +       return x86_tls_error_RegIP;
> > > +      else if (i.types[1].bitfield.instance != RegDI)
> 
> Use
> 
>       else if (i.types[1].bitfield.class != Reg
>                || !i.op[1].regs->reg_type.bitfield.qword
>                || i.op[1].regs->reg_num != EDI_REG_NUM)
>         return x86_tls_error_dest_RegDI;
> 

If we use reg_num, the following illegal usage will also pass the test.

leaq        bar@tlsgd(%rip), %r15

> > This failed with GCC 14:
> >
> > FAIL: Check tls relocation x86-64
> > FAIL: Build size-8
> >
> > gcc  -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-
> linux/ld/tmpdir/ld/
> > -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> > -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> > /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> > -o tmpdir/size-8b.o
> > Executing on host: sh -c {gcc
> > -B/export/build/gnu/tools-build/binutils-gitlab/build-x86_64-
> linux/ld/tmpdir/ld/
> > -I/export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size -g
> > -O2 -fno-sanitize=all   -c -fno-lto -fPIC -c
> > /export/gnu/import/git/gitlab/x86-binutils/ld/testsuite/ld-size/size-8b.c
> > -o tmpdir/size-8b.o 2>&1}  /dev/null ld.tmp (timeout = 300)
> > spawn [open ...]^M
> > /tmp/cc6aqbbv.s: Assembler messages:
> > /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> > dest register
> > /tmp/cc6aqbbv.s:27: Error: @TLSGD operator only supports eax/rax as
> > dest register
> >
> > UNSUPPORTED: Build libsize-8.so
> >

H.J, I cannot reproduce these failures with gcc14,

I checked the test case size-8b.c (FAIL: Build size-8):

https://godbolt.org/z/YE1jo54ad
set_bar:
        pushq   %rbp
        movl    %esi, %ebp
        pushq   %rbx
        movslq  %edi, %rbx
        subq    $8, %rsp
        data16  leaq        bar@tlsgd(%rip), %rdi    --> It is right, my environment does not reproduce the error case.
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT
        movb    %bpl, (%rax,%rbx)
        addq    $8, %rsp
        popq    %rbx
        popq    %rbp
        ret
bar:
        .zero   10

My Binutils build option is : ../upstream/configure  --disable-nls --disable-gdb --enable-maintainer-mode --prefix=$TARGET/binary CFLAGS='-O0 -gdwarf-2 -fPIE' CXXFLAGS='-O0 -gdwarf-2 -fPIE'

Thanks,
Lili.
> --
> H.J.
  
Jan Beulich Sept. 9, 2024, 8:52 a.m. UTC | #6
On 05.09.2024 10:57, Cui, Lili wrote:
> @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const insn_template *t)
>  	       && (t->base_opcode | 8) == 0x2c);
>  }
>  
> +static enum x86_tls_error_type
> +x86_check_tls_relocation (unsigned int r_type)
> +{
> +  switch (r_type)
> +    {
> +    case BFD_RELOC_386_TLS_GOTDESC:
> +      /* Check transition from GDesc access model:
> +
> +	 leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> +					SIB is not supported.
> +       */

What does "check transition" here mean? Aiui it's the linker doing the
transitions (if/when possible).

> +      if (i.tm.mnem_off != MN_lea)
> +	return x86_tls_error_insn;
> +      else if (i.index_reg)
> +	return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> +	return x86_tls_error_RegB;

As mentioned on other occasions before: While I understand this is to a
fair degree a matter of style and hence personal preference, I view use
of "else if" in such situations as misleading. Ommiting the "else" also
leads to shorter code, i.e. (slightly) improved readability. (Note how
further down you sometimes omit the "else". Such inconsistency is imo
even worse than having the "else" everywhere.)

As to in particular the last check: 16-bit addressing using %bx is then
also okay?

> +      break;
> +
> +    case BFD_RELOC_386_TLS_GD:
> +      /* Check transition from GD access model:
> +
> +	 leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
> +	 leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> +					     Memory reg can't be %eax.
> +       */
> +      if (i.tm.mnem_off != MN_lea)
> +	return x86_tls_error_insn;
> +      else if (i.index_reg)
> +	{
> +	  if (i.base_reg)
> +	    return x86_tls_error_base_reg_name;
> +	  if (i.index_reg->reg_type.bitfield.instance != RegB)
> +	    return x86_tls_error_index_RegB;
> +	  else if (i.types[1].bitfield.instance != Accum)
> +	    return x86_tls_error_dest_RegA;
> +	  else 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_RegA;
> +	  else if (i.types[1].bitfield.instance != Accum)
> +	    return x86_tls_error_dest_RegA;

Using i.types[] here looks fragile to me. Please consider using
i.op[1].regs instead.

> +	}
> +      break;
> +
> +    case BFD_RELOC_386_TLS_LDM:
> +      /*  Check transition from LDM access model:
> +
> +	  leal foo@tlsldm(%reg), %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;
> +      else if (i.index_reg)
> +	return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> +	return x86_tls_error_RegA;
> +      else if (i.types[1].bitfield.instance != Accum)
> +	return x86_tls_error_dest_RegA;
> +      break;
> +
> +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> +      /* Check transition from GOTPC32 TLSDESC access model:
> +
> +	 --- LP64 mode ---
> +	 leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> +
> +	 --- X32 mode ---
> +	 rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> +
> +       */
> +      if (i.tm.mnem_off != MN_lea)
> +	return x86_tls_error_insn;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +	return x86_tls_error_RegIP;
> +      break;

And no check for the required REX prefix in x32 mode? Or is it gas that
automatically adds it (could do with a few words in the comment then)?

> +    case BFD_RELOC_X86_64_TLSGD:
> +      /* Check transition from GD access model:
> +
> +	 leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> +       */
> +    case BFD_RELOC_X86_64_TLSLD:
> +      /* Check transition from 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;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +	return x86_tls_error_RegIP;
> +      else if (i.types[1].bitfield.instance != RegDI)
> +	return x86_tls_error_dest_RegDI;

For this you don't need to invent RegDI. You can check the register's
number and flags.

> +      break;
> +
> +    case BFD_RELOC_386_TLS_GOTIE:
> +      /* Check transition from 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 transition from 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;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.index_reg)
> +	return x86_tls_error_sib;
> +      break;

And all MOV/ADD/SUB forms are permitted here? Plus what about operand
size (perhaps also already further up)?

> +    case BFD_RELOC_386_TLS_IE:
> +      /* Check transition from IE access model:
> +
> +	 movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> +	 addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> +       */
> +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> +	return x86_tls_error_insn;
> +      else if (i.base_reg || i.index_reg)
> +	return x86_tls_error_require_no_base_index_reg;
> +      break;

Wouldn't this allow RegEIZ to pass, when that shouldn't pass?

> +    case BFD_RELOC_X86_64_GOTTPOFF:
> +      /* Check transition from GOTTPOFF access model:
> +
> +	 mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> +	 add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> +       */
> +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> +	return x86_tls_error_insn;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_num != RegIP)
> +	return x86_tls_error_RegIP;
> +      break;
> +
> +    case BFD_RELOC_386_TLS_DESC_CALL:
> +      /* Check transition from GDesc access model:
> +
> +	 call *x@tlscall(%eax) --> Memory reg must be %eax and
> +				   SIB is not supported.
> +       */
> +      if (i.tm.mnem_off != MN_call)
> +	return x86_tls_error_insn;
> +      else if (i.index_reg)
> +	return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> +	return x86_tls_error_RegA;
> +      break;

While the commentary for this is different from that for ...

> +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> +      /* Check transition from 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;
> +      else if (i.index_reg)
> +	return x86_tls_error_sib;
> +      else if (!i.base_reg)
> +	return x86_tls_error_no_base_reg;
> +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> +	return x86_tls_error_RegA;
> +      break;

... this, the code is all the same. Fold both?

> +    default:
> +      return x86_tls_error_none;
> +    }
> +
> +  return x86_tls_error_none;
> +}
> +
> +static void
> +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int 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_sib:
> +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_no_base_reg:
> +      as_bad (_("@%s operator must have base regster"), gotrel[k].str);

Nit: "register"

> +      return;
> +
> +    case x86_tls_error_require_no_base_index_reg:
> +      as_bad (_("@%s operator should not have base/index register"),

Nit: Odd wording. The operator never has any register. Perhaps better
"... requires no base/index register"?

> +	      gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_base_reg_name:
> +      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_index_RegB:
> +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> +	      gotrel[k].str);

Please properly print register names: With % prefix when such is required,
and only the one register size that's actually permitted.

> +      return;
> +
> +    case x86_tls_error_RegA:
> +      as_bad (_("@%s operator only supports eax/rax as base register"),
> +	      gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_RegB:
> +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> +	      gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_RegIP:
> +      as_bad (_("@%s operator only supports rip as base register"),
> +	      gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_dest_RegA:
> +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> +	      gotrel[k].str);
> +      return;
> +
> +    case x86_tls_error_dest_RegDI:
> +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> +	      gotrel[k].str);

Surely not again eax/rax?

> +      return;
> +
> +    case x86_tls_error_scale_factor:
> +      as_bad (_("@%s operator only supports scale factor of 00"),
> +	      gotrel[k].str);

The scale _factor_ can never be 0; you mean 1 here.

> @@ -16786,6 +17072,14 @@ md_parse_option (int c, const char *arg)
>  	  optimize_for_space = 0;
>  	}
>        break;
> +    case OPTION_MTLS_CHECK:
> +      if (strcasecmp (arg, "yes") == 0)
> +	tls_check = 1;
> +      else if (strcasecmp (arg, "no") == 0)
> +	tls_check = 0;

Nit: The variable has type bool - please use true/false in such cases.

> --- a/opcodes/i386-gen.c
> +++ b/opcodes/i386-gen.c
> @@ -526,6 +526,7 @@ static const struct {
>      INSTANCE (RegC),
>      INSTANCE (RegD),
>      INSTANCE (RegB),
> +    INSTANCE (RegDI),
>  };

In addition to what H.J. said here and below: If this was really needed (and
then actually used somewhere in the opcode table), it would surely need some
explanation in the description. Plus ...

> --- a/opcodes/i386-opc.h
> +++ b/opcodes/i386-opc.h
> @@ -843,6 +843,7 @@ enum operand_instance
>    RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
>    RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
>    RegB,  /* %bl / %bx / %ebx / %rbx */
> +  RegDI, /* %rdx */
>  };

... the comment would then look wrong here. Yet as already indicated by H.J.,
this likely all wants dropping again anyway.

Jan
  
Cui, Lili Sept. 10, 2024, 1:28 a.m. UTC | #7
> -----Original Message-----
> From: Cui, Lili <lili.cui@intel.com>
> Sent: Monday, September 9, 2024 4:16 PM
> To: H.J. Lu <hjl.tools@gmail.com>
> Cc: Beulich, Jan <JBeulich@suse.com>; binutils@sourceware.org
> Subject: RE: [PATCH v2] x86: Add tls check in gas
> 
> 
> > > if (i.has_gotrel && tls_check)
> > >
> > > > > > +    {
> > > > > > +      enum x86_tls_error_type tls_error;
> > > > > > +      for (j = 0; j < i.operands; ++j)
> > > > > > +       if ((tls_error = x86_check_tls_relocation
> > > > > > + (i.reloc[j])) !=
> > x86_tls_error_none)
> > > > > > +         x86_report_tls_error (tls_error, i.reloc[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;
> > >         }
> > >
> >
> > This patch is on top of your patch.
> >
> 
> Applied this patch, thanks! Only one place that I think there is a bug.
> 
> > > > +    case BFD_RELOC_X86_64_TLSLD:
> > > > +      /* Check transition from 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;
> > > > +      else if (!i.base_reg)
> > > > +       return x86_tls_error_no_base_reg;
> > > > +      else if (i.base_reg->reg_num != RegIP)
> > > > +       return x86_tls_error_RegIP;
> > > > +      else if (i.types[1].bitfield.instance != RegDI)
> >
> > Use
> >
> >       else if (i.types[1].bitfield.class != Reg
> >                || !i.op[1].regs->reg_type.bitfield.qword
> >                || i.op[1].regs->reg_num != EDI_REG_NUM)
> >         return x86_tls_error_dest_RegDI;
> >
> 
> If we use reg_num, the following illegal usage will also pass the test.
> 
> leaq        bar@tlsgd(%rip), %r15
> 

Fixed , added "i.op[1].regs->reg_flags" check.

Thanks,
Lili.
  
Cui, Lili Sept. 10, 2024, 7:12 a.m. UTC | #8
> On 05.09.2024 10:57, Cui, Lili wrote:
> > @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const
> insn_template *t)
> >  	       && (t->base_opcode | 8) == 0x2c);  }
> >
> > +static enum x86_tls_error_type
> > +x86_check_tls_relocation (unsigned int r_type) {
> > +  switch (r_type)
> > +    {
> > +    case BFD_RELOC_386_TLS_GOTDESC:
> > +      /* Check transition from GDesc access model:
> > +
> > +	 leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
> > +					SIB is not supported.
> > +       */
> 
> What does "check transition" here mean? Aiui it's the linker doing the
> transitions (if/when possible).
> 
Removed "transition from".

> > +      if (i.tm.mnem_off != MN_lea)
> > +	return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +	return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> > +	return x86_tls_error_RegB;
> 
> As mentioned on other occasions before: While I understand this is to a fair
> degree a matter of style and hence personal preference, I view use of "else if"
> in such situations as misleading. Ommiting the "else" also leads to shorter
> code, i.e. (slightly) improved readability. (Note how further down you
> sometimes omit the "else". Such inconsistency is imo even worse than having
> the "else" everywhere.)
>

Jan, do you mean to use "if " instead "else if"? 

> As to in particular the last check: 16-bit addressing using %bx is then also
> okay?
> 
I removed the register size check by mistake, reloc() only checks the memory registers, I will add the register part check in this function.

> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_GD:
> > +      /* Check transition from GD access model:
> > +
> > +	 leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is
> supported.
> > +	 leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
> > +					     Memory reg can't be %eax.
> > +       */
> > +      if (i.tm.mnem_off != MN_lea)
> > +	return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +	{
> > +	  if (i.base_reg)
> > +	    return x86_tls_error_base_reg_name;
> > +	  if (i.index_reg->reg_type.bitfield.instance != RegB)
> > +	    return x86_tls_error_index_RegB;
> > +	  else if (i.types[1].bitfield.instance != Accum)
> > +	    return x86_tls_error_dest_RegA;
> > +	  else 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_RegA;
> > +	  else if (i.types[1].bitfield.instance != Accum)
> > +	    return x86_tls_error_dest_RegA;
> 
> Using i.types[] here looks fragile to me. Please consider using i.op[1].regs
> instead.
>
Ok.

> 
> > +	}
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_LDM:
> > +      /*  Check transition from LDM access model:
> > +
> > +	  leal foo@tlsldm(%reg), %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;
> > +      else if (i.index_reg)
> > +	return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> > +	return x86_tls_error_RegA;
> > +      else if (i.types[1].bitfield.instance != Accum)
> > +	return x86_tls_error_dest_RegA;
> > +      break;
> > +
> > +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> > +      /* Check transition from GOTPC32 TLSDESC access model:
> > +
> > +	 --- LP64 mode ---
> > +	 leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> > +
> > +	 --- X32 mode ---
> > +	 rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> > +
> > +       */
> > +      if (i.tm.mnem_off != MN_lea)
> > +	return x86_tls_error_insn;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +	return x86_tls_error_RegIP;
> > +      break;
> 
> And no check for the required REX prefix in x32 mode? Or is it gas that
> automatically adds it (could do with a few words in the comment then)?
> 

For Egpr gas will automatically add rex2 for it. 
For Gpr we need check REX prefix here.

Added REX check for Gpr and notes.

> > +    case BFD_RELOC_X86_64_TLSGD:
> > +      /* Check transition from GD access model:
> > +
> > +	 leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
> > +       */
> > +    case BFD_RELOC_X86_64_TLSLD:
> > +      /* Check transition from 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;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +	return x86_tls_error_RegIP;
> > +      else if (i.types[1].bitfield.instance != RegDI)
> > +	return x86_tls_error_dest_RegDI;
> 
> For this you don't need to invent RegDI. You can check the register's number
> and flags.
> 
Done.

> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_GOTIE:
> > +      /* Check transition from 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 transition from 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;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.index_reg)
> > +	return x86_tls_error_sib;
> > +      break;
> 
> And all MOV/ADD/SUB forms are permitted here? Plus what about operand
> size (perhaps also already further up)?
> 
Added a dest register size check here, and  reloc() will check memory register.

> > +    case BFD_RELOC_386_TLS_IE:
> > +      /* Check transition from IE access model:
> > +
> > +	 movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > +	 addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
> > +       */
> > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > +	return x86_tls_error_insn;
> > +      else if (i.base_reg || i.index_reg)
> > +	return x86_tls_error_require_no_base_index_reg;
> > +      break;
> 
> Wouldn't this allow RegEIZ to pass, when that shouldn't pass?
> 

Input:
addl foo@indntpoff(,%eiz,1), %eax    ---> i.index_reg  !=0
addl foo@indntpoff(%eiz), %eax

output:
Error: @INDNTPOFF operator should not have base/index register
Error: `foo@indntpoff(%eiz)' is not a valid base/index expression

Added dest register size check here.

> > +    case BFD_RELOC_X86_64_GOTTPOFF:
> > +      /* Check transition from GOTTPOFF access model:
> > +
> > +	 mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > +	 add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
> > +       */
> > +      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
> > +	return x86_tls_error_insn;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_num != RegIP)
> > +	return x86_tls_error_RegIP;
> > +      break;
> > +
> > +    case BFD_RELOC_386_TLS_DESC_CALL:
> > +      /* Check transition from GDesc access model:
> > +
> > +	 call *x@tlscall(%eax) --> Memory reg must be %eax and
> > +				   SIB is not supported.
> > +       */
> > +      if (i.tm.mnem_off != MN_call)
> > +	return x86_tls_error_insn;
> > +      else if (i.index_reg)
> > +	return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > +	return x86_tls_error_RegA;
> > +      break;
> 
> While the commentary for this is different from that for ...
> 
> > +    case BFD_RELOC_X86_64_TLSDESC_CALL:
> > +      /* Check transition from 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;
> > +      else if (i.index_reg)
> > +	return x86_tls_error_sib;
> > +      else if (!i.base_reg)
> > +	return x86_tls_error_no_base_reg;
> > +      else if (i.base_reg->reg_type.bitfield.instance != Accum)
> > +	return x86_tls_error_RegA;
> > +      break;
> 
> ... this, the code is all the same. Fold both?
>
Done.

> 
> > +    default:
> > +      return x86_tls_error_none;
> > +    }
> > +
> > +  return x86_tls_error_none;
> > +}
> > +
> > +static void
> > +x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int
> > +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_sib:
> > +      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_no_base_reg:
> > +      as_bad (_("@%s operator must have base regster"),
> > + gotrel[k].str);
> 
> Nit: "register"
> 
Done.

> > +      return;
> > +
> > +    case x86_tls_error_require_no_base_index_reg:
> > +      as_bad (_("@%s operator should not have base/index register"),
> 
> Nit: Odd wording. The operator never has any register. Perhaps better "...
> requires no base/index register"?
> 
Done.

> > +	      gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_base_reg_name:
> > +      as_bad (_("@%s operator should not have base register"),
> gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_index_RegB:
> > +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> > +	      gotrel[k].str);
> 
> Please properly print register names: With % prefix when such is required,
> and only the one register size that's actually permitted.
> 
Removed "rbx". Considering Intel format, I did not add % prefix here.

> > +      return;
> > +
> > +    case x86_tls_error_RegA:
> > +      as_bad (_("@%s operator only supports eax/rax as base register"),
> > +	      gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_RegB:
> > +      as_bad (_("@%s operator only supports ebx/rbx as base register"),
> > +	      gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_RegIP:
> > +      as_bad (_("@%s operator only supports rip as base register"),
> > +	      gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_dest_RegA:
> > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > +	      gotrel[k].str);
> > +      return;
> > +
> > +    case x86_tls_error_dest_RegDI:
> > +      as_bad (_("@%s operator only supports eax/rax as dest register"),
> > +	      gotrel[k].str);
> 
> Surely not again eax/rax?
> 
Done.

> > +      return;
> > +
> > +    case x86_tls_error_scale_factor:
> > +      as_bad (_("@%s operator only supports scale factor of 00"),
> > +	      gotrel[k].str);
> 
> The scale _factor_ can never be 0; you mean 1 here.
> 
Done.

> > @@ -16786,6 +17072,14 @@ md_parse_option (int c, const char *arg)
> >  	  optimize_for_space = 0;
> >  	}
> >        break;
> > +    case OPTION_MTLS_CHECK:
> > +      if (strcasecmp (arg, "yes") == 0)
> > +	tls_check = 1;
> > +      else if (strcasecmp (arg, "no") == 0)
> > +	tls_check = 0;
> 
> Nit: The variable has type bool - please use true/false in such cases.
> 
Done.

> > --- a/opcodes/i386-gen.c
> > +++ b/opcodes/i386-gen.c
> > @@ -526,6 +526,7 @@ static const struct {
> >      INSTANCE (RegC),
> >      INSTANCE (RegD),
> >      INSTANCE (RegB),
> > +    INSTANCE (RegDI),
> >  };
> 
> In addition to what H.J. said here and below: If this was really needed (and
> then actually used somewhere in the opcode table), it would surely need
> some explanation in the description. Plus ...
> 
Removed.

> > --- a/opcodes/i386-opc.h
> > +++ b/opcodes/i386-opc.h
> > @@ -843,6 +843,7 @@ enum operand_instance
> >    RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
> >    RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
> >    RegB,  /* %bl / %bx / %ebx / %rbx */
> > +  RegDI, /* %rdx */
> >  };
> 
> ... the comment would then look wrong here. Yet as already indicated by H.J.,
> this likely all wants dropping again anyway.
> 
Removed.

Thanks,
Lili.

> Jan
  
Jan Beulich Sept. 10, 2024, 8:42 a.m. UTC | #9
On 10.09.2024 09:12, Cui, Lili wrote:
>> On 05.09.2024 10:57, Cui, Lili wrote:
>>> @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const
>>> +      if (i.tm.mnem_off != MN_lea)
>>> +	return x86_tls_error_insn;
>>> +      else if (i.index_reg)
>>> +	return x86_tls_error_sib;
>>> +      else if (!i.base_reg)
>>> +	return x86_tls_error_no_base_reg;
>>> +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
>>> +	return x86_tls_error_RegB;
>>
>> As mentioned on other occasions before: While I understand this is to a fair
>> degree a matter of style and hence personal preference, I view use of "else if"
>> in such situations as misleading. Ommiting the "else" also leads to shorter
>> code, i.e. (slightly) improved readability. (Note how further down you
>> sometimes omit the "else". Such inconsistency is imo even worse than having
>> the "else" everywhere.)
> 
> Jan, do you mean to use "if " instead "else if"? 

Yes.

>>> +    case BFD_RELOC_386_TLS_LDM:
>>> +      /*  Check transition from LDM access model:
>>> +
>>> +	  leal foo@tlsldm(%reg), %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;
>>> +      else if (i.index_reg)
>>> +	return x86_tls_error_sib;
>>> +      else if (!i.base_reg)
>>> +	return x86_tls_error_no_base_reg;
>>> +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
>>> +	return x86_tls_error_RegA;
>>> +      else if (i.types[1].bitfield.instance != Accum)
>>> +	return x86_tls_error_dest_RegA;
>>> +      break;
>>> +
>>> +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
>>> +      /* Check transition from GOTPC32 TLSDESC access model:
>>> +
>>> +	 --- LP64 mode ---
>>> +	 leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
>>> +
>>> +	 --- X32 mode ---
>>> +	 rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
>>> +
>>> +       */
>>> +      if (i.tm.mnem_off != MN_lea)
>>> +	return x86_tls_error_insn;
>>> +      else if (!i.base_reg)
>>> +	return x86_tls_error_no_base_reg;
>>> +      else if (i.base_reg->reg_num != RegIP)
>>> +	return x86_tls_error_RegIP;
>>> +      break;
>>
>> And no check for the required REX prefix in x32 mode? Or is it gas that
>> automatically adds it (could do with a few words in the comment then)?
> 
> For Egpr gas will automatically add rex2 for it. 

And REX2 is permitted there? If so, imo the comment wants updating to
reflect that.

>>> +    case x86_tls_error_base_reg_name:
>>> +      as_bad (_("@%s operator should not have base register"),
>> gotrel[k].str);
>>> +      return;
>>> +
>>> +    case x86_tls_error_index_RegB:
>>> +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
>>> +	      gotrel[k].str);
>>
>> Please properly print register names: With % prefix when such is required,
>> and only the one register size that's actually permitted.
>>
> Removed "rbx". Considering Intel format, I did not add % prefix here.

I didn't ask to add an unconditional %. Instead I asked to use
register_prefix, like we do in other diagnostics. I merely assumed you
knew what's meant; I'm sorry if that was wrong.

Further you having removed rbx (and the messages being possible outside
of 64-bit mode only), I'd like to ask that the enumerators (i.e. also
the other one concerning EBX) be renamed from having RegB to having EBX.

Thinking of the involved addressing: Is use of any of these in 16-bit
code permitted by ld (i.e. when there's an address size prefix there)?
If not, I expect this also wants diagnosing.

Jan
  
Cui, Lili Sept. 11, 2024, 2:42 a.m. UTC | #10
> On 10.09.2024 09:12, Cui, Lili wrote:
> >> On 05.09.2024 10:57, Cui, Lili wrote:
> >>> @@ -6318,6 +6338,284 @@ static INLINE bool may_need_pass2 (const
> >>> +      if (i.tm.mnem_off != MN_lea)
> >>> +	return x86_tls_error_insn;
> >>> +      else if (i.index_reg)
> >>> +	return x86_tls_error_sib;
> >>> +      else if (!i.base_reg)
> >>> +	return x86_tls_error_no_base_reg;
> >>> +      else if (i.base_reg->reg_type.bitfield.instance != RegB)
> >>> +	return x86_tls_error_RegB;
> >>
> >> As mentioned on other occasions before: While I understand this is to
> >> a fair degree a matter of style and hence personal preference, I view use of
> "else if"
> >> in such situations as misleading. Ommiting the "else" also leads to
> >> shorter code, i.e. (slightly) improved readability. (Note how further
> >> down you sometimes omit the "else". Such inconsistency is imo even
> >> worse than having the "else" everywhere.)
> >
> > Jan, do you mean to use "if " instead "else if"?
> 
> Yes.
> 
Done.

> >>> +    case BFD_RELOC_386_TLS_LDM:
> >>> +      /*  Check transition from LDM access model:
> >>> +
> >>> +	  leal foo@tlsldm(%reg), %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;
> >>> +      else if (i.index_reg)
> >>> +	return x86_tls_error_sib;
> >>> +      else if (!i.base_reg)
> >>> +	return x86_tls_error_no_base_reg;
> >>> +      else if (i.base_reg->reg_type.bitfield.instance == Accum)
> >>> +	return x86_tls_error_RegA;
> >>> +      else if (i.types[1].bitfield.instance != Accum)
> >>> +	return x86_tls_error_dest_RegA;
> >>> +      break;
> >>> +
> >>> +    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
> >>> +      /* Check transition from GOTPC32 TLSDESC access model:
> >>> +
> >>> +	 --- LP64 mode ---
> >>> +	 leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
> >>> +
> >>> +	 --- X32 mode ---
> >>> +	 rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
> >>> +
> >>> +       */
> >>> +      if (i.tm.mnem_off != MN_lea)
> >>> +	return x86_tls_error_insn;
> >>> +      else if (!i.base_reg)
> >>> +	return x86_tls_error_no_base_reg;
> >>> +      else if (i.base_reg->reg_num != RegIP)
> >>> +	return x86_tls_error_RegIP;
> >>> +      break;
> >>
> >> And no check for the required REX prefix in x32 mode? Or is it gas
> >> that automatically adds it (could do with a few words in the comment
> then)?
> >
> > For Egpr gas will automatically add rex2 for it.
> 
> And REX2 is permitted there? If so, imo the comment wants updating to
> reflect that.
> 
REX2 is permitted , Added. 

> >>> +    case x86_tls_error_base_reg_name:
> >>> +      as_bad (_("@%s operator should not have base register"),
> >> gotrel[k].str);
> >>> +      return;
> >>> +
> >>> +    case x86_tls_error_index_RegB:
> >>> +      as_bad (_("@%s operator only supports ebx/rbx as index register"),
> >>> +	      gotrel[k].str);
> >>
> >> Please properly print register names: With % prefix when such is
> >> required, and only the one register size that's actually permitted.
> >>
> > Removed "rbx". Considering Intel format, I did not add % prefix here.
> 
> I didn't ask to add an unconditional %. Instead I asked to use register_prefix,
> like we do in other diagnostics. I merely assumed you knew what's meant; I'm
> sorry if that was wrong.
> 
Oh, sorry, I misunderstood.  Done.

> Further you having removed rbx (and the messages being possible outside of
> 64-bit mode only), I'd like to ask that the enumerators (i.e. also the other one
> concerning EBX) be renamed from having RegB to having EBX.
> 
Done.

> Thinking of the involved addressing: Is use of any of these in 16-bit code
> permitted by ld (i.e. when there's an address size prefix there)?
> If not, I expect this also wants diagnosing.
> 
I found in TLS sequences, 0x66 is often used as a placeholder, so data16 should be ok.


Thanks,
Lili.

> Jan
  

Patch

diff --git a/gas/config.in b/gas/config.in
index a1f83499332..3f199d5b4a6 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 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 a9d3536429b..a0222580106 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -274,6 +274,23 @@  enum i386_error
     internal_error,
   };
 
+enum x86_tls_error_type
+{
+  x86_tls_error_none,
+  x86_tls_error_insn,
+  x86_tls_error_sib,
+  x86_tls_error_no_base_reg,
+  x86_tls_error_require_no_base_index_reg,
+  x86_tls_error_base_reg_name,
+  x86_tls_error_index_RegB,
+  x86_tls_error_RegA,
+  x86_tls_error_RegB,
+  x86_tls_error_RegIP,
+  x86_tls_error_dest_RegA,
+  x86_tls_error_dest_RegDI,
+  x86_tls_error_scale_factor
+};
+
 struct _i386_insn
   {
     /* TM holds the template for the insn were currently assembling.  */
@@ -717,6 +734,9 @@  lfence_before_ret;
 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,
@@ -6318,6 +6338,284 @@  static INLINE bool may_need_pass2 (const insn_template *t)
 	       && (t->base_opcode | 8) == 0x2c);
 }
 
+static enum x86_tls_error_type
+x86_check_tls_relocation (unsigned int r_type)
+{
+  switch (r_type)
+    {
+    case BFD_RELOC_386_TLS_GOTDESC:
+      /* Check transition from GDesc access model:
+
+	 leal x@tlsdesc(%ebx), %reg --> Memory reg must be %ebx and
+					SIB is not supported.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      else if (i.index_reg)
+	return x86_tls_error_sib;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_type.bitfield.instance != RegB)
+	return x86_tls_error_RegB;
+      break;
+
+    case BFD_RELOC_386_TLS_GD:
+      /* Check transition from GD access model:
+
+	 leal foo@tlsgd(,%ebx,1), %eax   --> Only this fixed format is supported.
+	 leal foo@tlsgd(%reg), %eax      --> Dest reg must be '%eax'
+					     Memory reg can't be %eax.
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      else if (i.index_reg)
+	{
+	  if (i.base_reg)
+	    return x86_tls_error_base_reg_name;
+	  if (i.index_reg->reg_type.bitfield.instance != RegB)
+	    return x86_tls_error_index_RegB;
+	  else if (i.types[1].bitfield.instance != Accum)
+	    return x86_tls_error_dest_RegA;
+	  else 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_RegA;
+	  else if (i.types[1].bitfield.instance != Accum)
+	    return x86_tls_error_dest_RegA;
+	}
+      break;
+
+    case BFD_RELOC_386_TLS_LDM:
+      /*  Check transition from LDM access model:
+
+	  leal foo@tlsldm(%reg), %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;
+      else if (i.index_reg)
+	return x86_tls_error_sib;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_type.bitfield.instance == Accum)
+	return x86_tls_error_RegA;
+      else if (i.types[1].bitfield.instance != Accum)
+	return x86_tls_error_dest_RegA;
+      break;
+
+    case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
+      /* Check transition from GOTPC32 TLSDESC access model:
+
+	 --- LP64 mode ---
+	 leaq x@tlsdesc(%rip), %rax --> Memory reg must be %rip.
+
+	 --- X32 mode ---
+	 rex leal x@tlsdesc(%rip), %eax --> Memory reg must be %rip.
+
+       */
+      if (i.tm.mnem_off != MN_lea)
+	return x86_tls_error_insn;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_num != RegIP)
+	return x86_tls_error_RegIP;
+      break;
+
+    case BFD_RELOC_X86_64_TLSGD:
+      /* Check transition from GD access model:
+
+	 leaq foo@tlsgd(%rip), %rdi --> Only this fixed format is supported.
+       */
+    case BFD_RELOC_X86_64_TLSLD:
+      /* Check transition from 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;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_num != RegIP)
+	return x86_tls_error_RegIP;
+      else if (i.types[1].bitfield.instance != RegDI)
+	return x86_tls_error_dest_RegDI;
+      break;
+
+    case BFD_RELOC_386_TLS_GOTIE:
+      /* Check transition from 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 transition from 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;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.index_reg)
+	return x86_tls_error_sib;
+      break;
+
+    case BFD_RELOC_386_TLS_IE:
+      /* Check transition from IE access model:
+
+	 movl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
+	 addl foo@indntpoff, %reg --> Mod == 00 && r/m == 5
+       */
+      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
+	return x86_tls_error_insn;
+      else if (i.base_reg || i.index_reg)
+	return x86_tls_error_require_no_base_index_reg;
+      break;
+
+    case BFD_RELOC_X86_64_GOTTPOFF:
+      /* Check transition from GOTTPOFF access model:
+
+	 mov foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
+	 add foo@gottpoff(%rip), %reg --> Memory Reg must be %rip.
+       */
+      if (i.tm.mnem_off != MN_add && i.tm.mnem_off != MN_mov)
+	return x86_tls_error_insn;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_num != RegIP)
+	return x86_tls_error_RegIP;
+      break;
+
+    case BFD_RELOC_386_TLS_DESC_CALL:
+      /* Check transition from GDesc access model:
+
+	 call *x@tlscall(%eax) --> Memory reg must be %eax and
+				   SIB is not supported.
+       */
+      if (i.tm.mnem_off != MN_call)
+	return x86_tls_error_insn;
+      else if (i.index_reg)
+	return x86_tls_error_sib;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_type.bitfield.instance != Accum)
+	return x86_tls_error_RegA;
+      break;
+
+    case BFD_RELOC_X86_64_TLSDESC_CALL:
+      /* Check transition from 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;
+      else if (i.index_reg)
+	return x86_tls_error_sib;
+      else if (!i.base_reg)
+	return x86_tls_error_no_base_reg;
+      else if (i.base_reg->reg_type.bitfield.instance != Accum)
+	return x86_tls_error_RegA;
+      break;
+
+    default:
+      return x86_tls_error_none;
+    }
+
+  return x86_tls_error_none;
+}
+
+static void
+x86_report_tls_error (enum x86_tls_error_type tls_error, unsigned int 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_sib:
+      as_bad (_("@%s operator do not support SIB"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_no_base_reg:
+      as_bad (_("@%s operator must have base regster"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_require_no_base_index_reg:
+      as_bad (_("@%s operator should not have base/index register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_base_reg_name:
+      as_bad (_("@%s operator should not have base register"), gotrel[k].str);
+      return;
+
+    case x86_tls_error_index_RegB:
+      as_bad (_("@%s operator only supports ebx/rbx as index register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_RegA:
+      as_bad (_("@%s operator only supports eax/rax as base register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_RegB:
+      as_bad (_("@%s operator only supports ebx/rbx as base register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_RegIP:
+      as_bad (_("@%s operator only supports rip as base register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_dest_RegA:
+      as_bad (_("@%s operator only supports eax/rax as dest register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_dest_RegDI:
+      as_bad (_("@%s operator only supports eax/rax as dest register"),
+	      gotrel[k].str);
+      return;
+
+    case x86_tls_error_scale_factor:
+      as_bad (_("@%s operator only supports scale factor of 00"),
+	      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.  */
@@ -6656,6 +6954,14 @@  i386_assemble (char *line)
 	i.prefix[LOCK_PREFIX] = 0;
     }
 
+  if (tls_check)
+    {
+      enum x86_tls_error_type tls_error;
+      for (j = 0; j < i.operands; ++j)
+	if ((tls_error = x86_check_tls_relocation (i.reloc[j])) != x86_tls_error_none)
+	  x86_report_tls_error (tls_error, i.reloc[j]);
+    }
+
   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)
@@ -6666,28 +6972,6 @@  i386_assemble (char *line)
 	  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.  */
@@ -16184,6 +16468,7 @@  const char *md_shortopts = "qnO::";
 #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[] =
 {
@@ -16230,6 +16515,7 @@  struct option md_longopts[] =
   {"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);
@@ -16786,6 +17072,14 @@  md_parse_option (int c, const char *arg)
 	  optimize_for_space = 0;
 	}
       break;
+    case OPTION_MTLS_CHECK:
+      if (strcasecmp (arg, "yes") == 0)
+	tls_check = 1;
+      else if (strcasecmp (arg, "no") == 0)
+	tls_check = 0;
+      else
+	as_fatal (_("invalid -mtls-check= option: `%s'"), arg);
+      break;
 
     default:
       return 0;
@@ -17028,6 +17322,16 @@  md_show_usage (FILE *stream)
     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 6b96d3a4e0c..a4cdeb80900 100755
--- a/gas/configure
+++ b/gas/configure
@@ -818,6 +818,7 @@  enable_checking
 enable_compressed_debug_sections
 enable_default_compressed_debug_sections_algorithm
 enable_x86_relax_relocations
+enable_tls_check
 enable_elf_stt_common
 enable_generate_build_notes
 enable_mips_fix_loongson3_llsc
@@ -1493,6 +1494,7 @@  Optional Features:
                           --enable-compressed-debug-sections.
   --enable-x86-relax-relocations
                           generate x86 relax relocations by default
+  --enable-tls-check      check 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 @@  else
   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 @@  else
   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 @@  if test "${enable_x86_relax_relocations+set}" = set; then :
 esac
 fi
 
+# PR gas/32022
+# Decide if check tls relocation.
+ac_default_x86_tls_check=unset
+# Provide a configure time option to override our default.
+# Check whether --enable-tls_check was given.
+if test "${enable_tls_check+set}" = set; then :
+  enableval=$enable_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 @@  cat >>confdefs.h <<_ACEOF
 _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 6b978aae3f7..5fe57e52746 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -95,6 +95,17 @@  AC_ARG_ENABLE(x86_relax_relocations,
   no)  ac_default_x86_relax_relocations=0 ;;
 esac])dnl
 
+# PR gas/32022
+# Decide if check tls relocation.
+ac_default_x86_tls_check=unset
+# Provide a configure time option to override our default.
+AC_ARG_ENABLE(tls_check,
+	      AS_HELP_STRING([--enable-tls-check],
+	      [check 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_DEFINE_UNQUOTED(DEFAULT_GENERATE_X86_RELAX_RELOCATIONS,
   $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 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 a9e43560aea..5da673a0287 100644
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -467,6 +467,16 @@  R_X86_64_REX_GOTPCRELX, in 64-bit mode.
 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-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 75ad061b32c..6710a56d64a 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -699,10 +699,11 @@  if [gas_32_check] then {
 	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 a3017388934..b32da8725b5 100644
--- a/gas/testsuite/gas/i386/ilp32/ilp32.exp
+++ b/gas/testsuite/gas/i386/ilp32/ilp32.exp
@@ -37,7 +37,7 @@  if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_x32_check] &
 	}
     }
 
-    run_list_test "reloc64" "--defsym _bad_=1"
+    run_list_test "reloc64" "--defsym _bad_=1 -mtls-check=no"
     run_list_test "reloc-2"
 
     set ASFLAGS "$old_ASFLAGS"
diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.d b/gas/testsuite/gas/i386/ilp32/reloc64.d
index e2c461f24e8..84b6aaccc01 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/inval-tls.l b/gas/testsuite/gas/i386/inval-tls.l
index 98f7a29f1ab..40443bf68a2 100644
--- a/gas/testsuite/gas/i386/inval-tls.l
+++ b/gas/testsuite/gas/i386/inval-tls.l
@@ -1,2 +1,30 @@ 
 .*: Assembler messages:
 .*:3: Error: @GOTNTPOFF operator cannot be used with `kmovd'
+.*:6: Error: @TLSGD operator cannot be used with `add'
+.*:7: Error: @TLSGD operator only supports ebx/rbx as index register
+.*:8: Error: @TLSGD operator only supports scale factor of 00
+.*:9: Error: @TLSGD operator should not have base register
+.*:10: Error: @TLSGD operator only supports eax/rax as dest register
+.*:11: Error: @TLSGD operator only supports eax/rax as dest register
+.*:12: Error: @TLSGD operator only supports eax/rax as base register
+.*:13: Error: 4-byte relocation cannot be applied to 2-byte field
+.*:16: Error: @TLSLDM operator cannot be used with `add'
+.*:17: Error: @TLSLDM operator only supports eax/rax as dest register
+.*:18: Error: @TLSLDM operator only supports eax/rax as base register
+.*:19: Error: @TLSLDM operator do not support SIB
+.*:20: Error: 4-byte relocation cannot be applied to 2-byte field
+.*:23: Error: @TLSDESC operator cannot be used with `add'
+.*:24: Error: @TLSDESC operator only supports ebx/rbx as base register
+.*:25: Error: @TLSDESC operator do not support SIB
+.*:28: Error: @INDNTPOFF operator cannot be used with `sub'
+.*:29: Error: @INDNTPOFF operator should not have base/index register
+.*:30: Error: @INDNTPOFF operator should not have base/index register
+.*:34: Error: @GOTNTPOFF operator cannot be used with `lea'
+.*:35: Error: @GOTNTPOFF operator must have base regster
+.*:38: Error: @GOTTPOFF operator cannot be used with `lea'
+.*:39: Error: @GOTTPOFF operator must have base regster
+.*:40: Error: 4-byte relocation cannot be applied to 2-byte field
+.*:43: Error: @TLSCALL operator cannot be used with `lea'
+.*:44: Error: @TLSCALL operator only supports eax/rax as base register
+.*:45: Error: @TLSCALL operator do not support SIB
+.*:45: Error: 0-byte relocation cannot be applied to 4-byte field
diff --git a/gas/testsuite/gas/i386/inval-tls.s b/gas/testsuite/gas/i386/inval-tls.s
index ba0e1b81dc9..d00dbfa3008 100644
--- a/gas/testsuite/gas/i386/inval-tls.s
+++ b/gas/testsuite/gas/i386/inval-tls.s
@@ -1,3 +1,45 @@ 
 	.text
 # All the following should be illegal
 	kmovd	foo@gotntpoff(%eax), %k0
+
+	/* Invalid testcase for BFD_RELOC_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(%bx), %ax
+
+	/* Invalid testcase for BFD_RELOC_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(%bx), %ax
+
+	/* Invalid testcase for BFD_RELOC_386_TLS_GOTDESC.  */
+	addl x@tlsdesc(%ebx), %eax
+	leal x@tlsdesc(%ecx), %eax
+	leal x@tlsdesc(,%ecx,1), %eax
+
+	/* Invalid testcase for BFD_RELOC_386_TLS_IE.  */
+	subl foo@indntpoff, %ecx
+	addl foo@indntpoff(%ebx), %ecx
+	movl foo@indntpoff(%ebx), %ecx
+	add foo@indntpoff, %cx
+
+	/* Invalid testcase for BFD_RELOC_386_TLS_GOTIE.  */
+	leal foo@gotntpoff(%ebx), %ecx
+	subl foo@gotntpoff(,%ebx,1), %ecx
+
+	/* Invalid testcase for BFD_RELOC_386_TLS_IE_32.  */
+	leal foo@gottpoff(%ebx), %ecx
+	subl foo@gottpoff(,%ebx,1), %ecx
+	add foo@gottpoff(%bx), %cx
+
+	/* Invalid testcase for BFD_RELOC_386_TLS_DESC_CALL.  */
+	leal foo@tlscall(%eax), %ebx
+	call *x@tlscall(%ebx)
+	call *x@tlscall(,%eax,1)
diff --git a/gas/testsuite/gas/i386/reloc32.d b/gas/testsuite/gas/i386/reloc32.d
index 263a742022e..7d1b1ba2ae0 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 540a9b77d35..f4e44a627c5 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 00000000000..adfe7ce9551
--- /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 00000000000..7a0a83147e4
--- /dev/null
+++ b/gas/testsuite/gas/i386/tls.s
@@ -0,0 +1,31 @@ 
+# Check tls relocation 32-bit mode
+
+	.text
+_start:
+	/* BFD_RELOC_386_TLS_GD.  */
+	leal foo@tlsgd(,%ebx,1), %eax
+	leal foo@tlsgd(%ecx), %eax
+
+	/* BFD_RELOC_386_TLS_LDM.  */
+	leal foo@tlsldm(%ebx), %eax
+
+	/* BFD_RELOC_386_TLS_GOTDESC.  */
+	leal x@tlsdesc(%ebx), %eax
+
+	/* BFD_RELOC_386_TLS_IE.  */
+	movl foo@indntpoff, %eax
+	movl foo@indntpoff, %ebx
+	addl foo@indntpoff, %edx
+
+	/* BFD_RELOC_386_TLS_GOTIE.  */
+	subl foo@gotntpoff(%ebx), %ecx
+	movl foo@gotntpoff(%ebx), %ecx
+	addl foo@gotntpoff(%ebx), %ecx
+
+	/* BFD_RELOC_386_TLS_IE_32.  */
+	subl foo@gottpoff(%ebx), %ecx
+	movl foo@gottpoff(%ebx), %ecx
+	addl foo@gottpoff(%ebx), %ecx
+
+	/* BFD_RELOC_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 da8ac19f869..b9a53693c4f 100644
--- a/gas/testsuite/gas/i386/x86-64-inval-tls.l
+++ b/gas/testsuite/gas/i386/x86-64-inval-tls.l
@@ -1,3 +1,20 @@ 
 .*: 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 only supports eax/rax as dest register
+.*:9: Error: @TLSGD operator only supports rip as base register
+.*:10: Error: @TLSGD operator must have base regster
+.*:13: Error: @TLSLD operator cannot be used with `add'
+.*:14: Error: @TLSLD operator only supports eax/rax as dest register
+.*:15: Error: @TLSLD operator only supports rip as base register
+.*:16: Error: @TLSLD operator must have base regster
+.*:19: Error: @TLSDESC operator cannot be used with `add'
+.*:20: Error: @TLSDESC operator only supports rip as base register
+.*:23: Error: @GOTTPOFF operator cannot be used with `sub'
+.*:24: Error: @GOTTPOFF operator cannot be used with `xor'
+.*:25: Error: @GOTTPOFF operator only supports rip as base register
+.*:26: Error: @GOTTPOFF operator only supports rip as base register
+.*:29: Error: @TLSCALL operator cannot be used with `lea'
+.*:30: Error: @TLSCALL operator only supports eax/rax as base register
+.*:30: Error: 0-byte relocation cannot be applied to 4-byte field
diff --git a/gas/testsuite/gas/i386/x86-64-inval-tls.s b/gas/testsuite/gas/i386/x86-64-inval-tls.s
index 71e19272ba9..ccefeb780b6 100644
--- a/gas/testsuite/gas/i386/x86-64-inval-tls.s
+++ b/gas/testsuite/gas/i386/x86-64-inval-tls.s
@@ -2,3 +2,29 @@ 
 # All the following should be illegal
 	kmovq	foo@gottpoff(%rip), %k0
 	kmovq	foo@tlsld(%rip), %k0
+
+	/* Invalid testcase for BFD_RELOC_X86_64_TLSGD.  */
+	addq foo@tlsgd(%rip), %rdi
+	leaq foo@tlsgd(%rip), %rax
+	leaq foo@tlsgd(%rax), %rdi
+	leaq foo@tlsgd(,%rax,1), %rdi
+
+	/* Invalid testcase for BFD_RELOC_X86_64_TLSLD.  */
+	addq foo@tlsld(%rip), %rdi
+	leaq foo@tlsld(%rip), %rax
+	leaq foo@tlsld(%rax), %rdi
+	leaq foo@tlsld(,%rax,1), %rdi
+
+	/* Invalid testcase for BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
+	addq x@tlsdesc(%rip), %rax
+	leaq x@tlsdesc(%rbx), %rax
+
+	/* Invalid testcase for BFD_RELOC_X86_64_GOTTPOFF.  */
+	subq foo@gottpoff(%rip), %r12
+	xorq foo@gottpoff(%rip), %rax
+	movq foo@gottpoff(%rbx), %rax
+	addq foo@gottpoff(%rbx), %rax
+
+	/* Invalid testcase for BFD_RELOC_X86_64_TLSDESC_CALL.  */
+	leaq foo@tlscall(%rax), %rbx
+	call *foo@tlscall(%rip)
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 00000000000..c4e5a8a4b29
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-tls.d
@@ -0,0 +1,29 @@ 
+#as:
+#objdump: -drw
+#name: Check tls relocation x86-64
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0000000000000000 <_start>:
+\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00 	lea    0x0\(%rip\),%rdi        # 7 <_start\+0x7>	3: R_X86_64_TLSGD	foo-0x4
+\s*[a-f0-9]+:\s*48 8d 3d 00 00 00 00 	lea    0x0\(%rip\),%rdi        # e <_start\+0xe>	a: R_X86_64_TLSLD	foo-0x4
+\s*[a-f0-9]+:\s*48 8d 05 00 00 00 00 	lea    0x0\(%rip\),%rax        # 15 <_start\+0x15>	11: R_X86_64_GOTPC32_TLSDESC	x-0x4
+\s*[a-f0-9]+:\s*40 8d 05 00 00 00 00 	rex lea 0x0\(%rip\),%eax        # 1c <_start\+0x1c>	18: R_X86_64_GOTPC32_TLSDESC	x-0x4
+\s*[a-f0-9]+:\s*d5 40 8d 05 00 00 00 00 	lea    0x0\(%rip\),%r16d        # 24 <_start\+0x24>	20: R_X86_64_CODE_4_GOTPC32_TLSDESC	x-0x4
+\s*[a-f0-9]+:\s*4c 03 25 00 00 00 00 	add    0x0\(%rip\),%r12        # 2b <_start\+0x2b>	27: R_X86_64_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*48 8b 05 00 00 00 00 	mov    0x0\(%rip\),%rax        # 32 <_start\+0x32>	2e: R_X86_64_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*44 03 25 00 00 00 00 	add    0x0\(%rip\),%r12d        # 39 <_start\+0x39>	35: R_X86_64_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*8b 05 00 00 00 00    	mov    0x0\(%rip\),%eax        # 3f <_start\+0x3f>	3b: R_X86_64_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*d5 48 03 05 00 00 00 00 	add    0x0\(%rip\),%r16        # 47 <_start\+0x47>	43: R_X86_64_CODE_4_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*d5 48 8b 25 00 00 00 00 	mov    0x0\(%rip\),%r20        # 4f <_start\+0x4f>	4b: R_X86_64_CODE_4_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*62 64 bc 18 01 35 00 00 00 00 	add    %r30,0x0\(%rip\),%r8        # 59 <_start\+0x59>	55: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*62 f4 dc 10 03 05 00 00 00 00 	add    0x0\(%rip\),%rax,%r20        # 63 <_start\+0x63>	5f: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*62 e4 fc 0c 03 05 00 00 00 00 	{nf} add 0x0\(%rip\),%r16        # 6d <_start\+0x6d>	69: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*62 64 bc 1c 01 35 00 00 00 00 	{nf} add %r30,0x0\(%rip\),%r8        # 77 <_start\+0x77>	73: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*62 f4 dc 14 03 05 00 00 00 00 	{nf} add 0x0\(%rip\),%rax,%r20        # 81 <_start\+0x81>	7d: R_X86_64_CODE_6_GOTTPOFF	foo-0x4
+\s*[a-f0-9]+:\s*ff 10                	call   \*\(%rax\)	81: R_X86_64_TLSDESC_CALL	x
+\s*[a-f0-9]+:\s*67 ff 10             	call   \*\(%eax\)	83: 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 00000000000..4c573922773
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-tls.s
@@ -0,0 +1,31 @@ 
+# Check tls relocation 64-bit mode
+
+	.text
+_start:
+	/* BFD_RELOC_X86_64_TLSGD.  */
+	leaq foo@tlsgd(%rip), %rdi
+
+	/* BFD_RELOC_X86_64_TLSLD.  */
+	leaq foo@tlsld(%rip), %rdi
+
+	/* BFD_RELOC_X86_64_GOTPC32_TLSDESC.  */
+	leaq     x@tlsdesc(%rip), %rax
+	rex leal x@tlsdesc(%rip), %eax
+	leal     x@tlsdesc(%rip), %r16d
+
+	/* BFD_RELOC_X86_64_GOTTPOFF.  */
+	addq     foo@gottpoff(%rip), %r12
+	movq     foo@gottpoff(%rip), %rax
+	addl     foo@gottpoff(%rip), %r12d
+	movl     foo@gottpoff(%rip), %eax
+	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
+
+	/* BFD_RELOC_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 86e7f4a75b3..740f5268de3 100644
--- a/gas/testsuite/gas/i386/x86-64.exp
+++ b/gas/testsuite/gas/i386/x86-64.exp
@@ -653,7 +653,8 @@  if [is_elf_format] then {
     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 2a70e81c444..0c853ab0a67 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 bbf93bef767..99e1b18b1ea 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 f2c29d880f2..4bb99c4422b 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 9f9e63029d6..4e7dc6ea56e 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 506f1a02605..6bfc78e0b49 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 a516d002660..98293f4b36a 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 d3447182e19..4e9c9a8f74a 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 bbf22ebeafe..955884885d7 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 c882c877ae3..ccaa525c74b 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 6a0158b44b7..0876993bca3 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 bf8a8198b5b..2e6d41ccf61 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 49d8464fbaf..b59cc6429de 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 29de1cebf8e..d7ab5ab7b32 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
diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c
index 565aae722f8..dd5e8744efe 100644
--- a/opcodes/i386-gen.c
+++ b/opcodes/i386-gen.c
@@ -526,6 +526,7 @@  static const struct {
     INSTANCE (RegC),
     INSTANCE (RegD),
     INSTANCE (RegB),
+    INSTANCE (RegDI),
 };
 
 #undef INSTANCE
diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
index c0d5e44d461..608e238fa18 100644
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -843,6 +843,7 @@  enum operand_instance
   RegC,  /* %cl / %cx / %ecx / %rcx, e.g. register to hold shift count */
   RegD,  /* %dl / %dx / %edx / %rdx, e.g. register to hold I/O port addr */
   RegB,  /* %bl / %bx / %ebx / %rbx */
+  RegDI, /* %rdx */
 };
 
 /* Position of operand_type bits.  */
diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
index e3146456133..028f9523b52 100644
--- a/opcodes/i386-reg.tbl
+++ b/opcodes/i386-reg.tbl
@@ -135,7 +135,7 @@  rbx, Class=Reg|Instance=RegB|Qword|BaseIndex, 0, 3, Dw2Inval, 3
 rsp, Class=Reg|Qword, 0, 4, Dw2Inval, 7
 rbp, Class=Reg|Qword|BaseIndex, 0, 5, Dw2Inval, 6
 rsi, Class=Reg|Qword|BaseIndex, 0, 6, Dw2Inval, 4
-rdi, Class=Reg|Qword|BaseIndex, 0, 7, Dw2Inval, 5
+rdi, Class=Reg|Instance=RegDI|Qword|BaseIndex, 0, 7, Dw2Inval, 5
 r8, Class=Reg|Qword|BaseIndex, RegRex, 0, Dw2Inval, 8
 r9, Class=Reg|Qword|BaseIndex, RegRex, 1, Dw2Inval, 9
 r10, Class=Reg|Qword|BaseIndex, RegRex, 2, Dw2Inval, 10