[PATCH] x86-64: Fix TLSDESC relaxation for x32

H.J. Lu hjl.tools@gmail.com
Sat Jan 18 23:07:00 GMT 2020


For X32, TLSDESC sequences can be

8d 05 00 00 00 00	lea	x@TLSDESC(%rip), %eax
67 ff 10		call	*x@TLSCALL(%eax)

or the same sequence as LP64

48 8d 05 00 00 00 00	lea	foo@TLSDESC(%rip), %rax
ff 10			call	*foo@TLSCALL(%rax)

we need to support both sequences for x32.

8d 05 00 00 00 00	lea	x@TLSDESC(%rip), %eax

should relaxed to

c7 c0 fc ff ff ff	movl	$x@tpoff, %eax

and

67 ff 10		call	*x@TLSCALL(%eax)

should relaxed to

0f 1f 00		nopl	(%rax)

bfd/

	PR ld/25416
	* elf64-x86-64.c (elf_x86_64_check_tls_transition): Support
	"leal x@tlsdesc(%rip), %eax" and "call *x@tlsdesc(%eax)" in
	X32 mode.
	(elf_x86_64_relocate_section): Relax "leal x@tlsdesc(%rip), %eax"
	to "movl $x@tpoff, %eax" and "call *(%eax)" to "nopl (%rax)" in
	X32 mode.

ld/

	PR ld/25416
	* testsuite/ld-x86-64/pr25416-1.d: New file.
	* testsuite/ld-x86-64/pr25416-1.s: Likewise.
	* testsuite/ld-x86-64/pr25416-1.s: Likewise.
	* testsuite/ld-x86-64/pr25416-2.d: Likewise.
	* testsuite/ld-x86-64/pr25416-2.s: Likewise.
	* testsuite/ld-x86-64/pr25416-3a.c: Likewise.
	* testsuite/ld-x86-64/pr25416-3b.s: Likewise.
	* testsuite/ld-x86-64/pr25416-3c.s: Likewise.
	* testsuite/ld-x86-64/x86-64.exp: Run PR ld/25416 tests.
---
 bfd/elf64-x86-64.c                  | 84 ++++++++++++++++++++++-------
 ld/testsuite/ld-x86-64/pr25416-1.d  | 13 +++++
 ld/testsuite/ld-x86-64/pr25416-1.s  | 13 +++++
 ld/testsuite/ld-x86-64/pr25416-2.d  | 13 +++++
 ld/testsuite/ld-x86-64/pr25416-2.s  | 13 +++++
 ld/testsuite/ld-x86-64/pr25416-3a.c | 27 ++++++++++
 ld/testsuite/ld-x86-64/pr25416-3b.s | 50 +++++++++++++++++
 ld/testsuite/ld-x86-64/pr25416-3c.s | 54 +++++++++++++++++++
 ld/testsuite/ld-x86-64/x86-64.exp   | 69 ++++++++++++++++++++++++
 9 files changed, 317 insertions(+), 19 deletions(-)
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-1.d
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-1.s
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-2.d
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-2.s
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-3a.c
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-3b.s
 create mode 100644 ld/testsuite/ld-x86-64/pr25416-3c.s

diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 79e68ff4767..9fe8f163e51 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1030,6 +1030,7 @@ elf_x86_64_check_tls_transition (bfd *abfd,
   bfd_boolean largepic = FALSE;
   struct elf_link_hash_entry *h;
   bfd_vma offset;
+  unsigned int prefix;
   struct elf_x86_link_hash_table *htab;
   bfd_byte *call;
   bfd_boolean indirect_call;
@@ -1223,18 +1224,24 @@ elf_x86_64_check_tls_transition (bfd *abfd,
 
     case R_X86_64_GOTPC32_TLSDESC:
       /* Check transition from GDesc access model:
-		leaq x@tlsdesc(%rip), %rax
+		leaq x@tlsdesc(%rip), %rax <--- LP64 mode.
+		leal x@tlsdesc(%rip), %eax <--- X32 mode.
 
 	 Make sure it's a leaq adding rip to a 32-bit offset
 	 into any register, although it's probably almost always
 	 going to be rax.  */
 
-      if (offset < 3 || (offset + 4) > sec->size)
+      prefix = ABI_64_P (abfd) ? 1 : 0;
+      if (offset < (2 + prefix)
+	  || (offset + (3 + prefix)) > sec->size)
 	return FALSE;
 
-      val = bfd_get_8 (abfd, contents + offset - 3);
-      if ((val & 0xfb) != 0x48)
-	return FALSE;
+      if (prefix)
+	{
+	  val = bfd_get_8 (abfd, contents + offset - 3);
+	  if ((val & 0xfb) != 0x48)
+	    return FALSE;
+	}
 
       if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d)
 	return FALSE;
@@ -1244,13 +1251,25 @@ elf_x86_64_check_tls_transition (bfd *abfd,
 
     case R_X86_64_TLSDESC_CALL:
       /* Check transition from GDesc access model:
-		call *x@tlsdesc(%rax)
+		call *x@tlsdesc(%rax) <--- LP64 mode.
+		call *x@tlsdesc(%eax) <--- X32 mode.
        */
       if (offset + 2 <= sec->size)
 	{
-	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
 	  call = contents + offset;
-	  return call[0] == 0xff && call[1] == 0x10;
+	  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 FALSE;
+		}
+	    }
+	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
+	  return call[prefix] == 0xff && call[1 + prefix] == 0x10;
 	}
 
       return FALSE;
@@ -3401,19 +3420,27 @@ corrupt_input:
 		{
 		  /* GDesc -> LE transition.
 		     It's originally something like:
-		     leaq x@tlsdesc(%rip), %rax
+		     leaq x@tlsdesc(%rip), %rax <--- LP64 mode.
+		     leal x@tlsdesc(%rip), %eax <--- X32 mode.
 
 		     Change it to:
-		     movl $x@tpoff, %rax.  */
+		     movq $x@tpoff, %rax <--- LP64 mode.
+		     movl $x@tpoff, %eax <--- X32 mode.
+		   */
 
-		  unsigned int val, type;
+		  unsigned int val, prefix;
 
-		  if (roff < 3)
+		  prefix = ABI_64_P (input_bfd) ? 1 : 0;
+		  if (roff < (2 + prefix))
 		    goto corrupt_input;
-		  type = bfd_get_8 (input_bfd, contents + roff - 3);
+		  if (prefix)
+		    {
+		      unsigned int type;
+		      type = bfd_get_8 (input_bfd, contents + roff - 3);
+		      bfd_put_8 (output_bfd, 0x48 | ((type >> 2) & 1),
+				 contents + roff - 3);
+		    }
 		  val = bfd_get_8 (input_bfd, contents + roff - 1);
-		  bfd_put_8 (output_bfd, 0x48 | ((type >> 2) & 1),
-			     contents + roff - 3);
 		  bfd_put_8 (output_bfd, 0xc7, contents + roff - 2);
 		  bfd_put_8 (output_bfd, 0xc0 | ((val >> 3) & 7),
 			     contents + roff - 1);
@@ -3426,11 +3453,30 @@ corrupt_input:
 		{
 		  /* GDesc -> LE transition.
 		     It's originally:
-		     call *(%rax)
+		     call *(%rax) <--- LP64 mode.
+		     call *(%eax) <--- X32 mode.
 		     Turn it into:
-		     xchg %ax,%ax.  */
-		  bfd_put_8 (output_bfd, 0x66, contents + roff);
-		  bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+		     xchg %ax,%ax <-- LP64 mode.
+		     nopl (%rax)  <-- X32 mode.
+		   */
+		  unsigned int prefix = 0;
+		  if (!ABI_64_P (input_bfd))
+		    {
+		      /* Check for call *x@tlsdesc(%eax).  */
+		      if (contents[roff] == 0x67)
+			prefix = 1;
+		    }
+		  if (prefix)
+		    {
+		      bfd_put_8 (output_bfd, 0x0f, contents + roff);
+		      bfd_put_8 (output_bfd, 0x1f, contents + roff + 1);
+		      bfd_put_8 (output_bfd, 0x00, contents + roff + 2);
+		    }
+		  else
+		    {
+		      bfd_put_8 (output_bfd, 0x66, contents + roff);
+		      bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+		    }
 		  continue;
 		}
 	      else if (r_type == R_X86_64_GOTTPOFF)
diff --git a/ld/testsuite/ld-x86-64/pr25416-1.d b/ld/testsuite/ld-x86-64/pr25416-1.d
new file mode 100644
index 00000000000..39854cd3510
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-1.d
@@ -0,0 +1,13 @@
+#as: --x32
+#ld: -melf32_x86_64
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+[a-f0-9]+ <_start>:
+ +[a-f0-9]+:	c7 c0 [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f][ \t]+mov    \$0x[a-f0-9]+,%eax
+ +[a-f0-9]+:	0f 1f 00             	nopl   \(%rax\)
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr25416-1.s b/ld/testsuite/ld-x86-64/pr25416-1.s
new file mode 100644
index 00000000000..66636c12981
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-1.s
@@ -0,0 +1,13 @@
+	.text
+	.globl	_start
+	.type	_start, @function
+_start:
+	lea	foo@TLSDESC(%rip), %eax
+	call	*foo@TLSCALL(%eax)
+	.section	.tdata,"awT",@progbits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.long	30
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/pr25416-2.d b/ld/testsuite/ld-x86-64/pr25416-2.d
new file mode 100644
index 00000000000..e60c8222474
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-2.d
@@ -0,0 +1,13 @@
+#as: --x32
+#ld: -melf32_x86_64
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+[a-f0-9]+ <_start>:
+ +[a-f0-9]+:	48 c7 c0 [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f][ \t]+mov    \$0x[a-f0-9]+,%rax
+ +[a-f0-9]+:	66 90                	xchg   %ax,%ax
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr25416-2.s b/ld/testsuite/ld-x86-64/pr25416-2.s
new file mode 100644
index 00000000000..b6dbb6d93ad
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-2.s
@@ -0,0 +1,13 @@
+	.text
+	.globl	_start
+	.type	_start, @function
+_start:
+	lea	foo@TLSDESC(%rip), %rax
+	call	*foo@TLSCALL(%rax)
+	.section	.tdata,"awT",@progbits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.long	30
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/pr25416-3a.c b/ld/testsuite/ld-x86-64/pr25416-3a.c
new file mode 100644
index 00000000000..521c13b38a8
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-3a.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+__thread int bar = 301;
+
+extern int *test1 (int);
+extern int *test2 (int);
+
+int
+main ()
+{
+  int *p;
+  p = test1 (30);
+  if (*p != 30)
+    abort ();
+  *p = 40;
+  test1 (40);
+  p = test2 (301);
+  if (*p != 301)
+    abort ();
+  if (p != &bar)
+    abort ();
+  *p = 40;
+  test2 (40);
+  puts ("PASS");
+  return 0;
+}
diff --git a/ld/testsuite/ld-x86-64/pr25416-3b.s b/ld/testsuite/ld-x86-64/pr25416-3b.s
new file mode 100644
index 00000000000..95a0226aa42
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-3b.s
@@ -0,0 +1,50 @@
+	.text
+	.p2align 4
+	.globl	test1
+	.type	test1, @function
+test1:
+	.cfi_startproc
+	subl	$8, %esp
+	.cfi_def_cfa_offset 16
+	lea	foo@TLSDESC(%rip), %eax
+	call	*foo@TLSCALL(%eax)
+	addl	%fs:0, %eax
+	cmpl	%edi, (%eax)
+	jne	.L5
+	addl	$8, %esp
+	.cfi_remember_state
+	.cfi_def_cfa_offset 8
+	ret
+.L5:
+	.cfi_restore_state
+	call	abort@PLT
+	.cfi_endproc
+	.size	test1, .-test1
+	.p2align 4
+	.globl	test2
+	.type	test2, @function
+test2:
+	.cfi_startproc
+	subl	$8, %esp
+	.cfi_def_cfa_offset 16
+	lea	bar@TLSDESC(%rip), %eax
+	call	*bar@TLSCALL(%eax)
+	addl	%fs:0, %eax
+	cmpl	%edi, (%eax)
+	jne	.L9
+	addl	$8, %esp
+	.cfi_remember_state
+	.cfi_def_cfa_offset 8
+	ret
+.L9:
+	.cfi_restore_state
+	call	abort@PLT
+	.cfi_endproc
+	.size	test2, .-test2
+	.section	.tdata,"awT",@progbits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.long	30
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/pr25416-3c.s b/ld/testsuite/ld-x86-64/pr25416-3c.s
new file mode 100644
index 00000000000..94a64bad9ea
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr25416-3c.s
@@ -0,0 +1,54 @@
+	.text
+	.p2align 4
+	.globl	test1
+	.type	test1, @function
+test1:
+	.cfi_startproc
+	subq	$8, %rsp
+	.cfi_def_cfa_offset 16
+	lea	foo@TLSDESC(%rip), %rax
+	call	*foo@TLSCALL(%rax)
+	addl	%fs:0, %eax
+	cmpl	%edi, (%eax)
+	jne	.L5
+	movl	%eax, %r8d
+	addq	$8, %rsp
+	.cfi_remember_state
+	.cfi_def_cfa_offset 8
+	movq	%r8, %rax
+	ret
+.L5:
+	.cfi_restore_state
+	call	abort@PLT
+	.cfi_endproc
+	.size	test1, .-test1
+	.p2align 4
+	.globl	test2
+	.type	test2, @function
+test2:
+	.cfi_startproc
+	subq	$8, %rsp
+	.cfi_def_cfa_offset 16
+	lea	bar@TLSDESC(%rip), %rax
+	call	*bar@TLSCALL(%rax)
+	addl	%fs:0, %eax
+	cmpl	%edi, (%eax)
+	jne	.L9
+	movl	%eax, %r8d
+	addq	$8, %rsp
+	.cfi_remember_state
+	.cfi_def_cfa_offset 8
+	movq	%r8, %rax
+	ret
+.L9:
+	.cfi_restore_state
+	call	abort@PLT
+	.cfi_endproc
+	.size	test2, .-test2
+	.section	.tdata,"awT",@progbits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.long	30
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 88f75e0e431..535b65f7bdf 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -461,6 +461,8 @@ run_dump_test "pr24721-x32"
 run_dump_test "pr24905"
 run_dump_test "pr24905-x32"
 run_dump_test "align-branch-1"
+run_dump_test "pr25416-1"
+run_dump_test "pr25416-2"
 
 if { ![istarget "x86_64-*-linux*"] && ![istarget "x86_64-*-nacl*"]} {
     return
@@ -1302,6 +1304,37 @@ if { [isnative] && [check_compiler_available] } {
     ]
 
     if  {[istarget "x86_64-*-linux*-gnux32"]} {
+	run_cc_link_tests [list \
+	    [list \
+		"Build pr25416-3b.o" \
+		"" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3b.s } \
+	    ] \
+	    [list \
+		"Build pr25416-3b.so" \
+		"-shared" \
+		"-fPIC -Wa,-mx86-used-note=yes" \
+		{ pr25416-3b.s } \
+		{} \
+		"pr25416-3b.so" \
+	    ] \
+	    [list \
+		"Build pr25416-3c.o" \
+		"" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3c.s } \
+	    ] \
+	    [list \
+		"Build pr25416-3c.so" \
+		"-shared" \
+		"-fPIC -Wa,-mx86-used-note=yes" \
+		{ pr25416-3b.s } \
+		{} \
+		"pr25416-3c.so" \
+	    ] \
+	]
+
 	run_ld_link_exec_tests [list \
 	    [list \
 		"Run pr22001-1b" \
@@ -1321,6 +1354,42 @@ if { [isnative] && [check_compiler_available] } {
 		"pass.out" \
 		"$NOPIE_CFLAGS" \
 	    ] \
+	    [list \
+		"Run pr25416-3a" \
+		"$NOPIE_LDFLAGS -Wl,--no-as-needed tmpdir/pr25416-3b.so" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3a.c } \
+		"pr25416-3a" \
+		"pass.out" \
+		"$NOPIE_CFLAGS" \
+	    ] \
+	    [list \
+		"Run pr25416-3b" \
+		"$NOPIE_LDFLAGS tmpdir/pr25416-3b.o" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3a.c } \
+		"pr25416-3b" \
+		"pass.out" \
+		"$NOPIE_CFLAGS" \
+	    ] \
+	    [list \
+		"Run pr25416-3c" \
+		"$NOPIE_LDFLAGS -Wl,--no-as-needed tmpdir/pr25416-3c.so" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3a.c } \
+		"pr25416-3c" \
+		"pass.out" \
+		"$NOPIE_CFLAGS" \
+	    ] \
+	    [list \
+		"Run pr25416-3d" \
+		"$NOPIE_LDFLAGS tmpdir/pr25416-3c.o" \
+		"-Wa,-mx86-used-note=yes" \
+		{ pr25416-3a.c } \
+		"pr25416-3d" \
+		"pass.out" \
+		"$NOPIE_CFLAGS" \
+	    ] \
 	]
     } else {
 	run_cc_link_tests [list \
-- 
2.24.1



More information about the Binutils mailing list