x86: Check invalid TLS descriptor call

TLS descriptor call,

call *x@tlsdesc(%rax)

or

call *x@tlsdesc(%eax)

calls _dl_tlsdesc_return which expects that RAX/EAX points to the TLS
descriptor.  Update x86 linker to issue an error with or without TLS
transition.

bfd/

	PR ld/32123
	* elf32-i386.c (elf_i386_check_tls_transition): Move
	R_386_TLS_DESC_CALL to ...
	(elf_i386_tls_transition): Here.
	* elf64-x86-64.c (elf_x86_64_check_tls_transition): Move.
	R_X86_64_TLSDESC_CALL check to ...
	(elf_x86_64_tls_transition): Here.

ld/

	PR ld/32123
	* testsuite/ld-i386/i386.exp: Run tlsgdesc3.
	* testsuite/ld-i386/tlsgdesc3.d: New file.
	* testsuite/ld-x86-64/tlsdesc5.d: Likewise.
	* testsuite/ld-x86-64/x86-64.exp: Run tlsdesc5.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 7d573e7..b1d997b 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1039,19 +1039,8 @@
 	      : elf_x86_tls_error_yes);
 
     case R_386_TLS_DESC_CALL:
-      /* Check transition from GDesc access model:
-		call *x@tlsdesc(%eax)
-       */
-      if (offset + 2 <= sec->size)
-	{
-	  /* Make sure that it's a call *x@tlsdesc(%eax).  */
-	  call = contents + offset;
-	  return (call[0] == 0xff && call[1] == 0x10
-		  ? elf_x86_tls_error_none
-		  : elf_x86_tls_error_indirect_call);
-	}
-
-      return elf_x86_tls_error_yes;
+      /* It has been checked in elf_i386_tls_transition.  */
+      return elf_x86_tls_error_none;
 
     default:
       abort ();
@@ -1077,6 +1066,8 @@
   unsigned int to_type = from_type;
   bool check = true;
   unsigned int to_le_type, to_ie_type;
+  bfd_vma offset;
+  bfd_byte *call;
 
   /* Skip TLS transition for functions.  */
   if (h != NULL
@@ -1098,9 +1089,34 @@
 
   switch (from_type)
     {
+    case R_386_TLS_DESC_CALL:
+      /* Check valid GDesc call:
+		call *x@tlsdesc(%eax)
+       */
+      offset = rel->r_offset;
+      call = NULL;
+      if (offset + 2 <= sec->size)
+	{
+	  /* Make sure that it's a call *x@tlsdesc(%eax).  */
+	  call = contents + offset;
+	  if (call[0] != 0xff || call[1] != 0x10)
+	    call = NULL;
+	}
+
+      if (call == NULL)
+	{
+	  _bfd_x86_elf_link_report_tls_transition_error
+	    (info, abfd, sec, symtab_hdr, h, sym, rel,
+	     "R_386_TLS_DESC_CALL", NULL,
+	     elf_x86_tls_error_indirect_call);
+
+	  return false;
+	}
+
+      /* Fall through.  */
+
     case R_386_TLS_GD:
     case R_386_TLS_GOTDESC:
-    case R_386_TLS_DESC_CALL:
     case R_386_TLS_IE_32:
     case R_386_TLS_IE:
     case R_386_TLS_GOTIE:
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 83399ea..cef5cbb 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1409,32 +1409,8 @@
 	      : elf_x86_tls_error_yes);
 
     case R_X86_64_TLSDESC_CALL:
-      /* Check transition from GDesc access model:
-		call *x@tlsdesc(%rax) <--- LP64 mode.
-		call *x@tlsdesc(%eax) <--- X32 mode.
-       */
-      if (offset + 2 <= sec->size)
-	{
-	  unsigned int prefix;
-	  call = contents + offset;
-	  prefix = 0;
-	  if (!ABI_64_P (abfd))
-	    {
-	      /* Check for call *x@tlsdesc(%eax).  */
-	      if (call[0] == 0x67)
-		{
-		  prefix = 1;
-		  if (offset + 3 > sec->size)
-		    return elf_x86_tls_error_yes;
-		}
-	    }
-	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
-	  return (call[prefix] == 0xff && call[1 + prefix] == 0x10
-		  ? elf_x86_tls_error_none
-		  : elf_x86_tls_error_indirect_call);
-	}
-
-      return elf_x86_tls_error_yes;
+      /* It has been checked in elf_x86_64_tls_transition.  */
+      return elf_x86_tls_error_none;
 
     default:
       abort ();
@@ -1459,6 +1435,8 @@
   unsigned int from_type = *r_type;
   unsigned int to_type = from_type;
   bool check = true;
+  bfd_vma offset;
+  bfd_byte *call;
 
   /* Skip TLS transition for functions.  */
   if (h != NULL
@@ -1468,10 +1446,49 @@
 
   switch (from_type)
     {
+    case R_X86_64_TLSDESC_CALL:
+      /* Check valid GDesc call:
+		call *x@tlsdesc(%rax) <--- LP64 mode.
+		call *x@tlsdesc(%eax) <--- X32 mode.
+       */
+      offset = rel->r_offset;
+      call = NULL;
+      if (offset + 2 <= sec->size)
+	{
+	  unsigned int prefix;
+	  call = contents + offset;
+	  prefix = 0;
+	  if (!ABI_64_P (abfd))
+	    {
+	      /* Check for call *x@tlsdesc(%eax).  */
+	      if (call[0] == 0x67)
+		{
+		  prefix = 1;
+		  if (offset + 3 > sec->size)
+		    call = NULL;
+		}
+	    }
+
+	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
+	  if (call != NULL
+	      && (call[prefix] != 0xff || call[1 + prefix] != 0x10))
+	    call = NULL;
+	}
+
+      if (call == NULL)
+	{
+	  _bfd_x86_elf_link_report_tls_transition_error
+	    (info, abfd, sec, symtab_hdr, h, sym, rel,
+	     "R_X86_64_TLSDESC_CALL", NULL,
+	     elf_x86_tls_error_indirect_call);
+	  return false;
+	}
+
+      /* Fall through.  */
+
     case R_X86_64_TLSGD:
     case R_X86_64_GOTPC32_TLSDESC:
     case R_X86_64_CODE_4_GOTPC32_TLSDESC:
-    case R_X86_64_TLSDESC_CALL:
     case R_X86_64_GOTTPOFF:
     case R_X86_64_CODE_4_GOTTPOFF:
     case R_X86_64_CODE_6_GOTTPOFF:
diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp
index 6f97f5d..a66d67a 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -547,6 +547,7 @@
 run_dump_test "pr31868c"
 run_dump_test "tlsgdesc1"
 run_dump_test "tlsgdesc2"
+run_dump_test "tlsgdesc3"
 
 proc undefined_weak {cflags ldflags} {
     set testname "Undefined weak symbol"
diff --git a/ld/testsuite/ld-i386/tlsgdesc3.d b/ld/testsuite/ld-i386/tlsgdesc3.d
new file mode 100644
index 0000000..f2c29d8
--- /dev/null
+++ b/ld/testsuite/ld-i386/tlsgdesc3.d
@@ -0,0 +1,5 @@
+#source: tlsgdesc2.s
+#name: TLS GDesc call (indirect CALL)
+#as: --32
+#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-x86-64/tlsdesc5.d b/ld/testsuite/ld-x86-64/tlsdesc5.d
new file mode 100644
index 0000000..6a0158b
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/tlsdesc5.d
@@ -0,0 +1,5 @@
+#source: tlsdesc4.s
+#name: TLS GDesc call (indirect CALL)
+#as: --64
+#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/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 2214e08..e729b69 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -540,6 +540,7 @@
 run_dump_test "tlsie5"
 run_dump_test "tlsdesc3"
 run_dump_test "tlsdesc4"
+run_dump_test "tlsdesc5"
 
 if { ![skip_sframe_tests] } {
     run_dump_test "sframe-simple-1"