[PATCH][binutils][2_42 1/2] x86: Improve TLS transition error check

Sadineni, Harish Harish.Sadineni@windriver.com
Wed May 21 13:52:40 GMT 2025


Hi all,

Please ignore this thread.
I will correct the subject line and will send a v2.


thanks,
Harish
________________________________
From: Sadineni, Harish
Sent: Wednesday, May 21, 2025 7:09 PM
To: binutils@sourceware.org <binutils@sourceware.org>
Cc: Kokkonda, Sundeep <Sundeep.Kokkonda@windriver.com>; Dora, Sunil Kumar <SunilKumar.Dora@windriver.com>
Subject: [PATCH][binutils][2_42 1/2] x86: Improve TLS transition error check

From: "H.J. Lu" <hjl.tools@gmail.com>

Provide detailed TLS transition errors when unsupported instructions are
used.  Treat R_X86_64_CODE_4_GOTTPOFF and R_X86_64_CODE_6_GOTTPOFF as
R_X86_64_GOTTPOFF when performing TLS transition.

bfd/

        PR ld/32017
        * elf32-i386.c (elf_i386_check_tls_transition): Return different
        enums for different errors.
        (elf_i386_tls_transition): Change argument from r_symndx to sym.
        Call _bfd_x86_elf_link_report_tls_transition_error to report TLS
        transition errors.
        (elf_i386_scan_relocs): Pass isym instead of r_symndx to
        elf_i386_tls_transition.
        (elf_i386_relocate_section): Pass sym instead of r_symndx to
        elf_i386_tls_transition.
        * elf64-x86-64.c (elf_x86_64_check_tls_transition): Return
        different enums for different errors.
        (elf_x86_64_tls_transition): Change argument from r_symndx to sym.
        Treat R_X86_64_CODE_4_GOTTPOFF and R_X86_64_CODE_6_GOTTPOFF as
        R_X86_64_GOTTPOFF.  Call
        _bfd_x86_elf_link_report_tls_transition_error to report TLS
        transition errors.
        (elf_x86_64_scan_relocs): Pass isym instead of r_symndx to
        elf_x86_64_tls_transition.
        (elf_x86_64_relocate_section): Pass sym instead of r_symndx to
        elf_x86_64_tls_transition.
        * elfxx-x86.c (_bfd_x86_elf_link_report_tls_transition_error): New.
        * elfxx-x86.h (elf_x86_tls_error_type): Likewise.
        (_bfd_x86_elf_link_report_tls_transition_error): Likewise.

ld/

        PR ld/32017
        * testsuite/ld-i386/i386.exp: Run tlsgdesc1 and tlsgdesc2.
        * testsuite/ld-i386/tlsie2.d: Updated.
        * testsuite/ld-i386/tlsie3.d: Likewise.
        * testsuite/ld-i386/tlsie4.d: Likewise.
        * testsuite/ld-i386/tlsie5.d: Likewise.
        * testsuite/ld-x86-64/tlsie2.d: Likewise.
        * testsuite/ld-x86-64/tlsie3.d: Likewise.
        * testsuite/ld-i386/tlsgdesc1.d: New file.
        * testsuite/ld-i386/tlsgdesc1.s: Likewise.
        * testsuite/ld-i386/tlsgdesc2.d: Likewise.
        * testsuite/ld-i386/tlsgdesc2.s: Likewise.
        * testsuite/ld-x86-64/tlsdesc3.d: Likewise.
        * testsuite/ld-x86-64/tlsdesc3.s: Likewise.
        * testsuite/ld-x86-64/tlsdesc4.d: Likewise.
        * testsuite/ld-x86-64/tlsdesc4.s: Likewise.
        * testsuite/ld-x86-64/tlsie5.d: Likewise.
        * testsuite/ld-x86-64/tlsie5.s: Likewise.
        * testsuite/ld-x86-64/x86-64.exp: Run tlsie5, tlsdesc3 and
        tlsdesc4.

Backported this commit to fix dependency issue for CVE-2025-1179
(cherry picked from commit:1d68a49ac5d71b648304f69af978fce0f4413800)

Signed-off-by: Harish Sadineni <Harish.Sadineni@windriver.com>
---
 bfd/elf32-i386.c                  | 118 +++++++++++++-------------
 bfd/elf64-x86-64.c                | 133 ++++++++++++++++--------------
 bfd/elfxx-x86.c                   |  85 +++++++++++++++++++
 bfd/elfxx-x86.h                   |  18 ++++
 ld/testsuite/ld-i386/i386.exp     |   2 +
 ld/testsuite/ld-i386/tlsgdesc1.d  |   4 +
 ld/testsuite/ld-i386/tlsgdesc1.s  |  11 +++
 ld/testsuite/ld-i386/tlsgdesc2.d  |   4 +
 ld/testsuite/ld-i386/tlsgdesc2.s  |  11 +++
 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 |   4 +
 ld/testsuite/ld-x86-64/tlsdesc3.s |  13 +++
 ld/testsuite/ld-x86-64/tlsdesc4.d |   4 +
 ld/testsuite/ld-x86-64/tlsdesc4.s |  13 +++
 ld/testsuite/ld-x86-64/tlsie2.d   |   2 +-
 ld/testsuite/ld-x86-64/tlsie3.d   |   2 +-
 ld/testsuite/ld-x86-64/tlsie5.d   |   4 +
 ld/testsuite/ld-x86-64/tlsie5.s   |  12 +++
 ld/testsuite/ld-x86-64/x86-64.exp |   3 +
 22 files changed, 319 insertions(+), 132 deletions(-)
 create mode 100644 ld/testsuite/ld-i386/tlsgdesc1.d
 create mode 100644 ld/testsuite/ld-i386/tlsgdesc1.s
 create mode 100644 ld/testsuite/ld-i386/tlsgdesc2.d
 create mode 100644 ld/testsuite/ld-i386/tlsgdesc2.s
 create mode 100644 ld/testsuite/ld-x86-64/tlsdesc3.d
 create mode 100644 ld/testsuite/ld-x86-64/tlsdesc3.s
 create mode 100644 ld/testsuite/ld-x86-64/tlsdesc4.d
 create mode 100644 ld/testsuite/ld-x86-64/tlsdesc4.s
 create mode 100644 ld/testsuite/ld-x86-64/tlsie5.d
 create mode 100644 ld/testsuite/ld-x86-64/tlsie5.s

diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index e2f88a11487..18a28d2491c 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -839,7 +839,7 @@ static const struct elf_x86_non_lazy_plt_layout elf_i386_non_lazy_ibt_plt =
 /* Return TRUE if the TLS access code sequence support transition
    from R_TYPE.  */

-static bool
+static enum elf_x86_tls_error_type
 elf_i386_check_tls_transition (asection *sec,
                                bfd_byte *contents,
                                Elf_Internal_Shdr *symtab_hdr,
@@ -861,7 +861,7 @@ elf_i386_check_tls_transition (asection *sec,
     case R_386_TLS_GD:
     case R_386_TLS_LDM:
       if (offset < 2 || (rel + 1) >= relend)
-       return false;
+       return elf_x86_tls_error_yes;

       indirect_call = false;
       call = contents + offset + 4;
@@ -884,19 +884,19 @@ elf_i386_check_tls_transition (asection *sec,
              can transit to different access model.  */
           if ((offset + 10) > sec->size
               || (type != 0x8d && type != 0x04))
-           return false;
+           return elf_x86_tls_error_yes;

           if (type == 0x04)
             {
               /* leal foo@tlsgd(,%ebx,1), %eax
                  call ___tls_get_addr@PLT  */
               if (offset < 3)
-               return false;
+               return elf_x86_tls_error_yes;

               if (*(call - 7) != 0x8d
                   || val != 0x1d
                   || call[0] != 0xe8)
-               return false;
+               return elf_x86_tls_error_yes;
             }
           else
             {
@@ -914,7 +914,7 @@ elf_i386_check_tls_transition (asection *sec,
                  is used to pass parameter to ___tls_get_addr.  */
               reg = val & 7;
               if ((val & 0xf8) != 0x80 || reg == 4 || reg == 0)
-               return false;
+               return elf_x86_tls_error_yes;

               indirect_call = call[0] == 0xff;
               if (!(reg == 3 && call[0] == 0xe8 && call[5] == 0x90)
@@ -922,7 +922,7 @@ elf_i386_check_tls_transition (asection *sec,
                   && !(indirect_call
                        && (call[1] & 0xf8) == 0x90
                        && (call[1] & 0x7) == reg))
-               return false;
+               return elf_x86_tls_error_yes;
             }
         }
       else
@@ -937,13 +937,13 @@ elf_i386_check_tls_transition (asection *sec,
                 addr32 call ___tls_get_addr
              can transit to different access model.  */
           if (type != 0x8d || (offset + 9) > sec->size)
-           return false;
+           return elf_x86_tls_error_yes;

           /* %eax can't be used as the GOT base register since it is
              used to pass parameter to ___tls_get_addr.  */
           reg = val & 7;
           if ((val & 0xf8) != 0x80 || reg == 4 || reg == 0)
-           return false;
+           return elf_x86_tls_error_yes;

           indirect_call = call[0] == 0xff;
           if (!(reg == 3 && call[0] == 0xe8)
@@ -951,23 +951,27 @@ elf_i386_check_tls_transition (asection *sec,
               && !(indirect_call
                    && (call[1] & 0xf8) == 0x90
                    && (call[1] & 0x7) == reg))
-           return false;
+           return elf_x86_tls_error_yes;
         }

       r_symndx = ELF32_R_SYM (rel[1].r_info);
       if (r_symndx < symtab_hdr->sh_info)
-       return false;
+       return elf_x86_tls_error_yes;

       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       if (h == NULL
           || !((struct elf_x86_link_hash_entry *) h)->tls_get_addr)
-       return false;
+       return elf_x86_tls_error_yes;
       else if (indirect_call)
-       return (ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32X
-               || ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32);
+       return ((ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32X
+                || ELF32_R_TYPE (rel[1].r_info) == R_386_GOT32)
+               ? elf_x86_tls_error_none
+               : elf_x86_tls_error_yes);
       else
-       return (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
-               || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
+       return ((ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
+               || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32)
+               ? elf_x86_tls_error_none
+               : elf_x86_tls_error_yes);

     case R_386_TLS_IE:
       /* Check transition from IE access model:
@@ -977,20 +981,23 @@ elf_i386_check_tls_transition (asection *sec,
        */

       if (offset < 1 || (offset + 4) > sec->size)
-       return false;
+       return elf_x86_tls_error_yes;

       /* Check "movl foo@tpoff(%rip), %eax" first.  */
       val = bfd_get_8 (abfd, contents + offset - 1);
       if (val == 0xa1)
-       return true;
+       return elf_x86_tls_error_none;

       if (offset < 2)
-       return false;
+       return elf_x86_tls_error_yes;

       /* Check movl|addl foo@tpoff(%rip), %reg.   */
       type = bfd_get_8 (abfd, contents + offset - 2);
-      return ((type == 0x8b || type == 0x03)
-             && (val & 0xc7) == 0x05);
+      if (type != 0x8b && type != 0x03)
+       return elf_x86_tls_error_add_mov;
+      return ((val & 0xc7) == 0x05
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_yes);

     case R_386_TLS_GOTIE:
     case R_386_TLS_IE_32:
@@ -1001,14 +1008,16 @@ elf_i386_check_tls_transition (asection *sec,
        */

       if (offset < 2 || (offset + 4) > sec->size)
-       return false;
+       return elf_x86_tls_error_yes;

       val = bfd_get_8 (abfd, contents + offset - 1);
       if ((val & 0xc0) != 0x80 || (val & 7) == 4)
-       return false;
+       return elf_x86_tls_error_yes;

       type = bfd_get_8 (abfd, contents + offset - 2);
-      return type == 0x8b || type == 0x2b || type == 0x03;
+      return (type == 0x8b || type == 0x2b || type == 0x03
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_add_sub_mov);

     case R_386_TLS_GOTDESC:
       /* Check transition from GDesc access model:
@@ -1019,13 +1028,15 @@ elf_i386_check_tls_transition (asection *sec,
          going to be eax.  */

       if (offset < 2 || (offset + 4) > sec->size)
-       return false;
+       return elf_x86_tls_error_yes;

       if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d)
-       return false;
+       return elf_x86_tls_error_lea;

       val = bfd_get_8 (abfd, contents + offset - 1);
-      return (val & 0xc7) == 0x83;
+      return ((val & 0xc7) == 0x83
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_yes);

     case R_386_TLS_DESC_CALL:
       /* Check transition from GDesc access model:
@@ -1035,10 +1046,12 @@ elf_i386_check_tls_transition (asection *sec,
         {
           /* Make sure that it's a call *x@tlsdesc(%eax).  */
           call = contents + offset;
-         return call[0] == 0xff && call[1] == 0x10;
+         return (call[0] == 0xff && call[1] == 0x10
+                 ? elf_x86_tls_error_none
+                 : elf_x86_tls_error_indirect_call);
         }

-      return false;
+      return elf_x86_tls_error_yes;

     default:
       abort ();
@@ -1057,7 +1070,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
                          const Elf_Internal_Rela *rel,
                          const Elf_Internal_Rela *relend,
                          struct elf_link_hash_entry *h,
-                        unsigned long r_symndx,
+                        Elf_Internal_Sym *sym,
                          bool from_relocate_section)
 {
   unsigned int from_type = *r_type;
@@ -1142,43 +1155,24 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
     return true;

   /* Check if the transition can be performed.  */
+  enum elf_x86_tls_error_type tls_error;
   if (check
-      && ! elf_i386_check_tls_transition (sec, contents,
-                                         symtab_hdr, sym_hashes,
-                                         from_type, rel, relend))
+      && ((tls_error = elf_i386_check_tls_transition (sec, contents,
+                                                     symtab_hdr,
+                                                     sym_hashes,
+                                                     from_type, rel,
+                                                     relend))
+         != elf_x86_tls_error_none))
     {
       reloc_howto_type *from, *to;
-      const char *name;

       from = elf_i386_rtype_to_howto (from_type);
       to = elf_i386_rtype_to_howto (to_type);

-      if (h)
-       name = h->root.root.string;
-      else
-       {
-         struct elf_x86_link_hash_table *htab;
-
-         htab = elf_x86_hash_table (info, I386_ELF_DATA);
-         if (htab == NULL)
-           name = "*unknown*";
-         else
-           {
-             Elf_Internal_Sym *isym;
-
-             isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
-                                           abfd, r_symndx);
-             name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
-           }
-       }
+      _bfd_x86_elf_link_report_tls_transition_error
+       (info, abfd, sec, symtab_hdr, h, sym, rel, from->name,
+        to->name, tls_error);

-      _bfd_error_handler
-       /* xgettext:c-format */
-       (_("%pB: TLS transition from %s to %s against `%s'"
-          " at %#" PRIx64 " in section `%pA' failed"),
-        abfd, from->name, to->name, name,
-        (uint64_t) rel->r_offset, sec);
-      bfd_set_error (bfd_error_bad_value);
       return false;
     }

@@ -1600,7 +1594,7 @@ elf_i386_scan_relocs (bfd *abfd,
       if (! elf_i386_tls_transition (info, abfd, sec, contents,
                                      symtab_hdr, sym_hashes,
                                      &r_type, GOT_UNKNOWN,
-                                    rel, rel_end, h, r_symndx, false))
+                                    rel, rel_end, h, isym, false))
         goto error_return;

       /* Check if _GLOBAL_OFFSET_TABLE_ is referenced.  */
@@ -2875,7 +2869,7 @@ elf_i386_relocate_section (bfd *output_bfd,
                                          input_section, contents,
                                          symtab_hdr, sym_hashes,
                                          &r_type_tls, tls_type, rel,
-                                        relend, h, r_symndx, true))
+                                        relend, h, sym, true))
             return false;

           expected_tls_le = htab->elf.target_os == is_solaris
@@ -3365,7 +3359,7 @@ elf_i386_relocate_section (bfd *output_bfd,
                                          input_section, contents,
                                          symtab_hdr, sym_hashes,
                                          &r_type, GOT_UNKNOWN, rel,
-                                        relend, h, r_symndx, true))
+                                        relend, h, sym, true))
             return false;

           if (r_type != R_386_TLS_LDM)
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 2ed120af780..f116e423f61 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1120,7 +1120,7 @@ elf32_x86_64_elf_object_p (bfd *abfd)
 /* Return TRUE if the TLS access code sequence support transition
    from R_TYPE.  */

-static bool
+static enum elf_x86_tls_error_type
 elf_x86_64_check_tls_transition (bfd *abfd,
                                  struct bfd_link_info *info,
                                  asection *sec,
@@ -1147,7 +1147,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
     case R_X86_64_TLSGD:
     case R_X86_64_TLSLD:
       if ((rel + 1) >= relend)
-       return false;
+       return elf_x86_tls_error_yes;

       if (r_type == R_X86_64_TLSGD)
         {
@@ -1184,7 +1184,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
           static const unsigned char leaq[] = { 0x66, 0x48, 0x8d, 0x3d };

           if ((offset + 12) > sec->size)
-           return false;
+           return elf_x86_tls_error_yes;

           call = contents + offset + 4;
           if (call[0] != 0x66
@@ -1208,20 +1208,20 @@ elf_x86_64_check_tls_transition (bfd *abfd,
                   || call[14] != 0xd0
                   || !((call[10] == 0x48 && call[12] == 0xd8)
                        || (call[10] == 0x4c && call[12] == 0xf8)))
-               return false;
+               return elf_x86_tls_error_yes;
               largepic = true;
             }
           else if (ABI_64_P (abfd))
             {
               if (offset < 4
                   || memcmp (contents + offset - 4, leaq, 4) != 0)
-               return false;
+               return elf_x86_tls_error_yes;
             }
           else
             {
               if (offset < 3
                   || memcmp (contents + offset - 3, leaq + 1, 3) != 0)
-               return false;
+               return elf_x86_tls_error_yes;
             }
           indirect_call = call[2] == 0xff;
         }
@@ -1250,10 +1250,10 @@ elf_x86_64_check_tls_transition (bfd *abfd,
           static const unsigned char lea[] = { 0x48, 0x8d, 0x3d };

           if (offset < 3 || (offset + 9) > sec->size)
-           return false;
+           return elf_x86_tls_error_yes;

           if (memcmp (contents + offset - 3, lea, 3) != 0)
-           return false;
+           return elf_x86_tls_error_yes;

           call = contents + offset + 4;
           if (!(call[0] == 0xe8
@@ -1268,7 +1268,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
                   || call[14] != 0xd0
                   || !((call[10] == 0x48 && call[12] == 0xd8)
                        || (call[10] == 0x4c && call[12] == 0xf8)))
-               return false;
+               return elf_x86_tls_error_yes;
               largepic = true;
             }
           indirect_call = call[0] == 0xff;
@@ -1276,22 +1276,30 @@ elf_x86_64_check_tls_transition (bfd *abfd,

       r_symndx = htab->r_sym (rel[1].r_info);
       if (r_symndx < symtab_hdr->sh_info)
-       return false;
+       return elf_x86_tls_error_yes;

       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
       if (h == NULL
           || !((struct elf_x86_link_hash_entry *) h)->tls_get_addr)
-       return false;
+       return elf_x86_tls_error_yes;
       else
         {
           r_type = (ELF32_R_TYPE (rel[1].r_info)
                     & ~R_X86_64_converted_reloc_bit);
           if (largepic)
-           return r_type == R_X86_64_PLTOFF64;
+           return (r_type == R_X86_64_PLTOFF64
+                   ? elf_x86_tls_error_none
+                   : elf_x86_tls_error_yes);
           else if (indirect_call)
-           return (r_type == R_X86_64_GOTPCRELX || r_type == R_X86_64_GOTPCREL);
+           return ((r_type == R_X86_64_GOTPCRELX
+                    || r_type == R_X86_64_GOTPCREL)
+                   ? elf_x86_tls_error_none
+                   : elf_x86_tls_error_yes);
           else
-           return (r_type == R_X86_64_PC32 || r_type == R_X86_64_PLT32);
+           return ((r_type == R_X86_64_PC32
+                    || r_type == R_X86_64_PLT32)
+                   ? elf_x86_tls_error_none
+                   : elf_x86_tls_error_yes);
         }

     case R_X86_64_CODE_4_GOTTPOFF:
@@ -1303,7 +1311,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
       if (offset < 4
           || (offset + 4) > sec->size
           || contents[offset - 4] != 0xd5)
-       return false;
+       return elf_x86_tls_error_yes;

       goto check_gottpoff;

@@ -1315,14 +1323,16 @@ elf_x86_64_check_tls_transition (bfd *abfd,
       if (offset < 6
           || (offset + 4) > sec->size
           || contents[offset - 6] != 0x62)
-       return false;
+       return elf_x86_tls_error_yes;

       val = bfd_get_8 (abfd, contents + offset - 2);
       if (val != 0x01 && val != 0x03)
-       return false;
+       return elf_x86_tls_error_add;

       val = bfd_get_8 (abfd, contents + offset - 1);
-      return (val & 0xc7) == 5;
+      return ((val & 0xc7) == 5
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_yes);

     case R_X86_64_GOTTPOFF:
       /* Check transition from IE access model:
@@ -1338,25 +1348,27 @@ elf_x86_64_check_tls_transition (bfd *abfd,
             {
               /* X32 may have 0x44 REX prefix or no REX prefix.  */
               if (ABI_64_P (abfd))
-               return false;
+               return elf_x86_tls_error_yes;
             }
         }
       else
         {
           /* X32 may not have any REX prefix.  */
           if (ABI_64_P (abfd))
-           return false;
+           return elf_x86_tls_error_yes;
           if (offset < 2 || (offset + 3) > sec->size)
-           return false;
+           return elf_x86_tls_error_yes;
         }

  check_gottpoff:
       val = bfd_get_8 (abfd, contents + offset - 2);
       if (val != 0x8b && val != 0x03)
-       return false;
+       return elf_x86_tls_error_add_mov;

       val = bfd_get_8 (abfd, contents + offset - 1);
-      return (val & 0xc7) == 5;
+      return ((val & 0xc7) == 5
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_yes);

     case R_X86_64_CODE_4_GOTPC32_TLSDESC:
       /* Check transition from GDesc access model:
@@ -1366,7 +1378,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
       if (offset < 4
           || (offset + 4) > sec->size
           || contents[offset - 4] != 0xd5)
-       return false;
+       return elf_x86_tls_error_yes;

       goto check_tlsdesc;

@@ -1380,19 +1392,21 @@ elf_x86_64_check_tls_transition (bfd *abfd,
          going to be rax.  */

       if (offset < 3 || (offset + 4) > sec->size)
-       return false;
+       return elf_x86_tls_error_yes;

       val = bfd_get_8 (abfd, contents + offset - 3);
       val &= 0xfb;
       if (val != 0x48 && (ABI_64_P (abfd) || val != 0x40))
-       return false;
+       return elf_x86_tls_error_yes;

  check_tlsdesc:
       if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d)
-       return false;
+       return elf_x86_tls_error_lea;

       val = bfd_get_8 (abfd, contents + offset - 1);
-      return (val & 0xc7) == 0x05;
+      return ((val & 0xc7) == 0x05
+             ? elf_x86_tls_error_none
+             : elf_x86_tls_error_yes);

     case R_X86_64_TLSDESC_CALL:
       /* Check transition from GDesc access model:
@@ -1411,14 +1425,16 @@ elf_x86_64_check_tls_transition (bfd *abfd,
                 {
                   prefix = 1;
                   if (offset + 3 > sec->size)
-                   return false;
+                   return elf_x86_tls_error_yes;
                 }
             }
           /* Make sure that it's a call *x@tlsdesc(%rax).  */
-         return call[prefix] == 0xff && call[1 + prefix] == 0x10;
+         return (call[prefix] == 0xff && call[1 + prefix] == 0x10
+                 ? elf_x86_tls_error_none
+                 : elf_x86_tls_error_indirect_call);
         }

-      return false;
+      return elf_x86_tls_error_yes;

     default:
       abort ();
@@ -1437,7 +1453,7 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
                            const Elf_Internal_Rela *rel,
                            const Elf_Internal_Rela *relend,
                            struct elf_link_hash_entry *h,
-                          unsigned long r_symndx,
+                          Elf_Internal_Sym *sym,
                            bool from_relocate_section)
 {
   unsigned int from_type = *r_type;
@@ -1488,7 +1504,12 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
           /* We checked the transition before when we were called from
              elf_x86_64_scan_relocs.  We only want to check the new
              transition which hasn't been checked before.  */
-         check = new_to_type != to_type && from_type == to_type;
+         check = (new_to_type != to_type
+                  && (from_type == to_type
+                      || (from_type == R_X86_64_CODE_4_GOTTPOFF
+                          && to_type == R_X86_64_GOTTPOFF)
+                      || (from_type == R_X86_64_CODE_6_GOTTPOFF
+                          && to_type == R_X86_64_GOTTPOFF)));
           to_type = new_to_type;
         }

@@ -1512,13 +1533,18 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
     return true;

   /* Check if the transition can be performed.  */
+  enum elf_x86_tls_error_type tls_error;
   if (check
-      && ! elf_x86_64_check_tls_transition (abfd, info, sec, contents,
-                                           symtab_hdr, sym_hashes,
-                                           from_type, rel, relend))
+      && ((tls_error = elf_x86_64_check_tls_transition (abfd, info, sec,
+                                                       contents,
+                                                       symtab_hdr,
+                                                       sym_hashes,
+                                                       from_type, rel,
+                                                       relend))
+         != elf_x86_tls_error_none))
+
     {
       reloc_howto_type *from, *to;
-      const char *name;

       from = elf_x86_64_rtype_to_howto (abfd, from_type);
       to = elf_x86_64_rtype_to_howto (abfd, to_type);
@@ -1526,31 +1552,10 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
       if (from == NULL || to == NULL)
         return false;

-      if (h)
-       name = h->root.root.string;
-      else
-       {
-         struct elf_x86_link_hash_table *htab;
-
-         htab = elf_x86_hash_table (info, X86_64_ELF_DATA);
-         if (htab == NULL)
-           name = "*unknown*";
-         else
-           {
-             Elf_Internal_Sym *isym;
+      _bfd_x86_elf_link_report_tls_transition_error
+       (info, abfd, sec, symtab_hdr, h, sym, rel, from->name,
+        to->name, tls_error);

-             isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
-                                           abfd, r_symndx);
-             name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
-           }
-       }
-
-      _bfd_error_handler
-       /* xgettext:c-format */
-       (_("%pB: TLS transition from %s to %s against `%s' at %#" PRIx64
-          " in section `%pA' failed"),
-        abfd, from->name, to->name, name, (uint64_t) rel->r_offset, sec);
-      bfd_set_error (bfd_error_bad_value);
       return false;
     }

@@ -2198,7 +2203,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
       if (! elf_x86_64_tls_transition (info, abfd, sec, contents,
                                        symtab_hdr, sym_hashes,
                                        &r_type, GOT_UNKNOWN,
-                                      rel, rel_end, h, r_symndx, false))
+                                      rel, rel_end, h, isym, false))
         goto error_return;

       /* Check if _GLOBAL_OFFSET_TABLE_ is referenced.  */
@@ -3648,7 +3653,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                                            input_section, contents,
                                            symtab_hdr, sym_hashes,
                                            &r_type_tls, tls_type, rel,
-                                          relend, h, r_symndx, true))
+                                          relend, h, sym, true))
             return false;

           if (r_type_tls == R_X86_64_TPOFF32)
@@ -4308,7 +4313,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
                                            input_section, contents,
                                            symtab_hdr, sym_hashes,
                                            &r_type, GOT_UNKNOWN, rel,
-                                          relend, h, r_symndx, true))
+                                          relend, h, sym, true))
             return false;

           if (r_type != R_X86_64_TLSLD)
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 508fd771da3..b17dad759c8 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -3202,6 +3202,91 @@ _bfd_x86_elf_link_report_relative_reloc
        asect, abfd);
 }

+/* Report TLS transition error.  */
+
+void
+_bfd_x86_elf_link_report_tls_transition_error
+  (struct bfd_link_info *info, bfd *abfd, asection *asect,
+   Elf_Internal_Shdr *symtab_hdr, struct elf_link_hash_entry *h,
+   Elf_Internal_Sym *sym, const Elf_Internal_Rela *rel,
+   const char *from_reloc_name, const char *to_reloc_name,
+   enum elf_x86_tls_error_type tls_error)
+{
+  const char *name;
+
+  if (h)
+    name = h->root.root.string;
+  else
+    {
+      const struct elf_backend_data *bed
+       = get_elf_backend_data (abfd);
+      struct elf_x86_link_hash_table *htab
+       = elf_x86_hash_table (info, bed->target_id);
+      if (htab == NULL)
+       name = "*unknown*";
+      else
+       name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+    }
+
+  switch (tls_error)
+    {
+    case elf_x86_tls_error_yes:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB: TLS transition from %s to %s against `%s' at 0x%v in "
+          "section `%pA' failed"),
+        abfd, from_reloc_name, to_reloc_name, name, rel->r_offset,
+        asect);
+      break;
+
+    case elf_x86_tls_error_add:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB(%pA+0x%v): relocation %s against `%s' must be used "
+          "in ADD only"),
+        abfd, asect, rel->r_offset, from_reloc_name, name);
+      break;
+
+    case elf_x86_tls_error_add_mov:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB(%pA+0x%v): relocation %s against `%s' must be used "
+          "in ADD or MOV only"),
+        abfd, asect, rel->r_offset, from_reloc_name, name);
+      break;
+
+    case elf_x86_tls_error_add_sub_mov:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB(%pA+0x%v): relocation %s against `%s' must be used "
+          "in ADD, SUB or MOV only"),
+        abfd, asect, rel->r_offset, from_reloc_name, name);
+      break;
+
+    case elf_x86_tls_error_indirect_call:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB(%pA+0x%v): relocation %s against `%s' must be used "
+          "in indirect CALL only"),
+        abfd, asect, rel->r_offset, from_reloc_name, name);
+      break;
+
+    case elf_x86_tls_error_lea:
+      info->callbacks->einfo
+       /* xgettext:c-format */
+       (_("%pB(%pA+0x%v): relocation %s against `%s' must be used "
+          "in LEA only"),
+        abfd, asect, rel->r_offset, from_reloc_name, name);
+      break;
+
+    default:
+      abort ();
+      break;
+    }
+
+  bfd_set_error (bfd_error_bad_value);
+}
+
 /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */

 bool
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 110bcb9ad71..02e2efa6c56 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -767,6 +767,18 @@ struct elf_x86_plt
   long count;
 };

+enum elf_x86_tls_error_type
+{
+  elf_x86_tls_error_none,
+  elf_x86_tls_error_add,
+  elf_x86_tls_error_add_mov,
+  elf_x86_tls_error_add_sub_mov,
+  elf_x86_tls_error_indirect_call,
+  elf_x86_tls_error_lea,
+  elf_x86_tls_error_yes
+};
+
+
 /* Set if a relocation is converted from a GOTPCREL relocation.  */
 #define R_X86_64_converted_reloc_bit (1 << 7)

@@ -908,6 +920,12 @@ extern void _bfd_x86_elf_link_fixup_ifunc_symbol
 extern void _bfd_x86_elf_link_report_relative_reloc
   (struct bfd_link_info *, asection *, struct elf_link_hash_entry *,
    Elf_Internal_Sym *, const char *, const void *);
+extern void _bfd_x86_elf_link_report_tls_transition_error
+  (struct bfd_link_info *, bfd *, asection *, Elf_Internal_Shdr *,
+   struct elf_link_hash_entry *, Elf_Internal_Sym *,
+   const Elf_Internal_Rela *, const char *, const char *,
+   enum elf_x86_tls_error_type);
+

 #define bfd_elf64_mkobject \
   _bfd_x86_elf_mkobject
diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp
index 18d1c9198ca..a8db2c713f3 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -541,6 +541,8 @@ run_dump_test "tlsdesc2"
 run_dump_test "report-reloc-1"
 run_dump_test "pr27998a"
 run_dump_test "pr27998b"
+run_dump_test "tlsgdesc1"
+run_dump_test "tlsgdesc2"

 proc undefined_weak {cflags ldflags} {
     set testname "Undefined weak symbol"
diff --git a/ld/testsuite/ld-i386/tlsgdesc1.d b/ld/testsuite/ld-i386/tlsgdesc1.d
new file mode 100644
index 00000000000..2a70e81c444
--- /dev/null
+++ b/ld/testsuite/ld-i386/tlsgdesc1.d
@@ -0,0 +1,4 @@
+#name: TLS GDesc->LE transition check (LEA)
+#as: --32
+#ld: -melf_i386
+#error: .*: relocation R_386_TLS_GOTDESC against `foo' must be used in LEA only
diff --git a/ld/testsuite/ld-i386/tlsgdesc1.s b/ld/testsuite/ld-i386/tlsgdesc1.s
new file mode 100644
index 00000000000..c30f7523462
--- /dev/null
+++ b/ld/testsuite/ld-i386/tlsgdesc1.s
@@ -0,0 +1,11 @@
+       .text
+       .globl _start
+_start:
+       movl    foo@tlsdesc(%ebx), %eax
+       call    *foo@tlscall(%eax)
+       .section        .tdata,"awT",@progbits
+       .align 4
+       .type   foo, @object
+       .size   foo, 4
+foo:
+       .long   100
diff --git a/ld/testsuite/ld-i386/tlsgdesc2.d b/ld/testsuite/ld-i386/tlsgdesc2.d
new file mode 100644
index 00000000000..2e6a66d372c
--- /dev/null
+++ b/ld/testsuite/ld-i386/tlsgdesc2.d
@@ -0,0 +1,4 @@
+#name: TLS GDesc->LE transition check (indirect CALL)
+#as: --32
+#ld: -melf_i386
+#error: .*: relocation R_386_TLS_DESC_CALL against `foo' must be used in indirect CALL only
diff --git a/ld/testsuite/ld-i386/tlsgdesc2.s b/ld/testsuite/ld-i386/tlsgdesc2.s
new file mode 100644
index 00000000000..7d9d556e2ab
--- /dev/null
+++ b/ld/testsuite/ld-i386/tlsgdesc2.s
@@ -0,0 +1,11 @@
+       .text
+       .globl _start
+_start:
+       leal    foo@tlsdesc(%ebx), %eax
+       jmp     *foo@tlscall(%eax)
+       .section        .tdata,"awT",@progbits
+       .align 4
+       .type   foo, @object
+       .size   foo, 4
+foo:
+       .long   100
diff --git a/ld/testsuite/ld-i386/tlsie2.d b/ld/testsuite/ld-i386/tlsie2.d
index ebb85fde7e7..9f9e63029d6 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
 #ld: -melf_i386
-#error: .*TLS transition from R_386_TLS_GOTIE to R_386_TLS_LE_32 against `foo'.*failed.*
+#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 d993f303c25..506f1a02605 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
 #ld: -melf_i386
-#error: .*TLS transition from R_386_TLS_GOTIE to R_386_TLS_LE_32 against `foo'.*failed.*
+#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 3ca8fddf5dd..a516d002660 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
 #ld: -melf_i386
-#error: .*TLS transition from R_386_TLS_IE to R_386_TLS_LE_32 against `foo'.*failed.*
+#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 3febeb159a9..d3447182e19 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
 #ld: -melf_i386
-#error: .*TLS transition from R_386_TLS_IE to R_386_TLS_LE_32 against `foo'.*failed.*
+#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
new file mode 100644
index 00000000000..bbf22ebeafe
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsdesc3.d
@@ -0,0 +1,4 @@
+#name: TLS GDesc->LE transition check (LEA)
+#as: --64
+#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/tlsdesc3.s b/ld/testsuite/ld-x86-64/tlsdesc3.s
new file mode 100644
index 00000000000..45310654ffc
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsdesc3.s
@@ -0,0 +1,13 @@
+       .text
+       .globl  _start
+       .type   _start,@function
+_start:
+       movq    foo@tlsdesc(%rip), %rax
+       call    *foo@tlscall(%rax)
+       .globl foo
+       .section        .tdata,"awT",@progbits
+       .align 8
+       .type   foo, @object
+       .size   foo, 8
+foo:
+       .quad   100
diff --git a/ld/testsuite/ld-x86-64/tlsdesc4.d b/ld/testsuite/ld-x86-64/tlsdesc4.d
new file mode 100644
index 00000000000..b50115c7178
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsdesc4.d
@@ -0,0 +1,4 @@
+#name: TLS GDesc->LE transition check (indirect CALL)
+#as: --64
+#ld: -melf_x86_64
+#error: .*: relocation R_X86_64_TLSDESC_CALL against `foo' must be used in indirect CALL only
diff --git a/ld/testsuite/ld-x86-64/tlsdesc4.s b/ld/testsuite/ld-x86-64/tlsdesc4.s
new file mode 100644
index 00000000000..b3d6c12d4fc
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsdesc4.s
@@ -0,0 +1,13 @@
+       .text
+       .globl  _start
+       .type   _start,@function
+_start:
+       leaq    foo@tlsdesc(%rip), %rax
+       jmp     *foo@tlscall(%rax)
+       .globl foo
+       .section        .tdata,"awT",@progbits
+       .align 8
+       .type   foo, @object
+       .size   foo, 8
+foo:
+       .quad   100
diff --git a/ld/testsuite/ld-x86-64/tlsie2.d b/ld/testsuite/ld-x86-64/tlsie2.d
index 97dcc288a3d..bf8a8198b5b 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
 #ld: -melf_x86_64
-#error: .*TLS transition from R_X86_64_GOTTPOFF to R_X86_64_TPOFF32 against `foo'.*failed.*
+#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 8c982a69838..49d8464fbaf 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
 #ld: -melf_x86_64
-#error: .*TLS transition from R_X86_64_GOTTPOFF to R_X86_64_TPOFF32 against `foo'.*failed.*
+#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
new file mode 100644
index 00000000000..29de1cebf8e
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsie5.d
@@ -0,0 +1,4 @@
+#name: TLS IE->LE transition check (APX)
+#as: --64
+#ld: -melf_x86_64
+#error: .*: relocation R_X86_64_CODE_6_GOTTPOFF against `foo' must be used in ADD only
diff --git a/ld/testsuite/ld-x86-64/tlsie5.s b/ld/testsuite/ld-x86-64/tlsie5.s
new file mode 100644
index 00000000000..c39e46fd97b
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsie5.s
@@ -0,0 +1,12 @@
+       .text
+       .globl _start
+_start:
+       xorq    %rax, foo@GOTTPOFF(%rip), %rax
+       movq    (%rax), %rax
+       .globl  foo
+       .section        .tdata,"awT",@progbits
+       .align 4
+       .type   foo, @object
+       .size   foo, 4
+foo:
+       .long   100
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 2a40f0b095b..811813466f8 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -741,6 +741,9 @@ run_dump_test "pr27016b"
 run_dump_test "report-reloc-1"
 run_dump_test "report-reloc-1-x32"
 run_dump_test "pr29820"
+run_dump_test "tlsie5"
+run_dump_test "tlsdesc3"
+run_dump_test "tlsdesc4"

 proc undefined_weak {cflags ldflags} {
     set testname "Undefined weak symbol"
--
2.49.0

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/binutils/attachments/20250521/f4bbe66a/attachment-0001.htm>


More information about the Binutils mailing list