aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2024-08-29 08:47:00 -0700
committerH.J. Lu <hjl.tools@gmail.com>2024-08-30 05:18:10 -0700
commit67e30b15212adc1502b898a1ca224fdf65dc110d (patch)
treeb5a4bd115a06aa54c301944401772e58d0ab0122
parentca6f0a61e78dd3b7f26dcffa3ec21eb81b6c525c (diff)
downloadbinutils-67e30b15212adc1502b898a1ca224fdf65dc110d.zip
binutils-67e30b15212adc1502b898a1ca224fdf65dc110d.tar.gz
binutils-67e30b15212adc1502b898a1ca224fdf65dc110d.tar.bz2
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>
-rw-r--r--bfd/elf32-i386.c44
-rw-r--r--bfd/elf64-x86-64.c71
-rw-r--r--ld/testsuite/ld-i386/i386.exp1
-rw-r--r--ld/testsuite/ld-i386/tlsgdesc3.d5
-rw-r--r--ld/testsuite/ld-x86-64/tlsdesc5.d5
-rw-r--r--ld/testsuite/ld-x86-64/x86-64.exp1
6 files changed, 86 insertions, 41 deletions
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_i386_check_tls_transition (asection *sec,
: 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 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
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 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
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_64_check_tls_transition (bfd *abfd,
: 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 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
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 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
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 "pr31868b"
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 "pr31868c-x32"
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"