[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