diff options
author | H.J. Lu <hjl.tools@gmail.com> | 2024-07-23 23:39:50 -0700 |
---|---|---|
committer | H.J. Lu <hjl.tools@gmail.com> | 2024-07-25 12:41:29 -0700 |
commit | 6f81a3cde4f7da6083dc623a327d61182a40c244 (patch) | |
tree | ff5732ec372a90aa5aed2f8bdb40e771e7df96ff | |
parent | 8ef606d2bb406d1a965bbbb8f36a01a96d015fc7 (diff) | |
download | binutils-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.c | 118 | ||||
-rw-r--r-- | bfd/elf64-x86-64.c | 133 | ||||
-rw-r--r-- | bfd/elfxx-x86.c | 85 | ||||
-rw-r--r-- | bfd/elfxx-x86.h | 17 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/i386.exp | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsgdesc1.d | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsgdesc1.s | 11 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsgdesc2.d | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsgdesc2.s | 11 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsie2.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsie3.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsie4.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/tlsie5.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsdesc3.d | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsdesc3.s | 13 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsdesc4.d | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsdesc4.s | 13 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsie2.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsie3.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsie5.d | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/tlsie5.s | 12 | ||||
-rw-r--r-- | ld/testsuite/ld-x86-64/x86-64.exp | 3 |
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" |