summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2024-07-23 23:39:50 -0700
committerH.J. Lu <hjl.tools@gmail.com>2024-07-25 12:41:29 -0700
commit6f81a3cde4f7da6083dc623a327d61182a40c244 (patch)
treeff5732ec372a90aa5aed2f8bdb40e771e7df96ff
parent8ef606d2bb406d1a965bbbb8f36a01a96d015fc7 (diff)
downloadbinutils-6f81a3cde4f7da6083dc623a327d61182a40c244.tar.gz
binutils-6f81a3cde4f7da6083dc623a327d61182a40c244.tar.bz2
binutils-6f81a3cde4f7da6083dc623a327d61182a40c244.zip
x86: Improve TLS transition error check
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. Signed-off-by: H.J. Lu <hjl.tools@gmail.com> (cherry picked from commit 1d68a49ac5d71b648304f69af978fce0f4413800)
-rw-r--r--bfd/elf32-i386.c118
-rw-r--r--bfd/elf64-x86-64.c133
-rw-r--r--bfd/elfxx-x86.c85
-rw-r--r--bfd/elfxx-x86.h17
-rw-r--r--ld/testsuite/ld-i386/i386.exp2
-rw-r--r--ld/testsuite/ld-i386/tlsgdesc1.d4
-rw-r--r--ld/testsuite/ld-i386/tlsgdesc1.s11
-rw-r--r--ld/testsuite/ld-i386/tlsgdesc2.d4
-rw-r--r--ld/testsuite/ld-i386/tlsgdesc2.s11
-rw-r--r--ld/testsuite/ld-i386/tlsie2.d2
-rw-r--r--ld/testsuite/ld-i386/tlsie3.d2
-rw-r--r--ld/testsuite/ld-i386/tlsie4.d2
-rw-r--r--ld/testsuite/ld-i386/tlsie5.d2
-rw-r--r--ld/testsuite/ld-x86-64/tlsdesc3.d4
-rw-r--r--ld/testsuite/ld-x86-64/tlsdesc3.s13
-rw-r--r--ld/testsuite/ld-x86-64/tlsdesc4.d4
-rw-r--r--ld/testsuite/ld-x86-64/tlsdesc4.s13
-rw-r--r--ld/testsuite/ld-x86-64/tlsie2.d2
-rw-r--r--ld/testsuite/ld-x86-64/tlsie3.d2
-rw-r--r--ld/testsuite/ld-x86-64/tlsie5.d4
-rw-r--r--ld/testsuite/ld-x86-64/tlsie5.s12
-rw-r--r--ld/testsuite/ld-x86-64/x86-64.exp3
22 files changed, 318 insertions, 132 deletions
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 6183d962a83..7d573e7e2c7 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. */
@@ -2874,7 +2868,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
@@ -3364,7 +3358,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 60aa1802552..83399ea8928 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. */
@@ -3647,7 +3652,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)
@@ -4307,7 +4312,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 00a5251ec74..044c36fbfab 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -3200,6 +3200,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 0f79d83e99c..5eef7b09152 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -767,6 +767,17 @@ 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)
@@ -915,6 +926,12 @@ 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 *) ATTRIBUTE_HIDDEN;
+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
#define bfd_elf32_mkobject \
diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp
index f96c514ccd4..ad6ad2e725e 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -544,6 +544,8 @@ run_dump_test "pr27998b"
run_dump_test "pr31868a"
run_dump_test "pr31868b"
run_dump_test "pr31868c"
+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 57a1cc44e2c..742823e6458 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -537,6 +537,9 @@ run_dump_test "pr31868b"
run_dump_test "pr31868b-x32"
run_dump_test "pr31868c"
run_dump_test "pr31868c-x32"
+run_dump_test "tlsie5"
+run_dump_test "tlsdesc3"
+run_dump_test "tlsdesc4"
if { ![skip_sframe_tests] } {
run_dump_test "sframe-simple-1"