/* 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,
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;
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
{
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)
&& !(indirect_call
&& (call[1] & 0xf8) == 0x90
&& (call[1] & 0x7) == reg))
- return false;
+ return elf_x86_tls_error_yes;
}
}
else
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)
&& !(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:
*/
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:
*/
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:
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:
{
/* 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 ();
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;
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;
}
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. */
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
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)
/* 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,
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)
{
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
|| 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;
}
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
|| 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;
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:
if (offset < 4
|| (offset + 4) > sec->size
|| contents[offset - 4] != 0xd5)
- return false;
+ return elf_x86_tls_error_yes;
goto check_gottpoff;
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:
{
/* 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:
if (offset < 4
|| (offset + 4) > sec->size
|| contents[offset - 4] != 0xd5)
- return false;
+ return elf_x86_tls_error_yes;
goto check_tlsdesc;
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:
{
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 ();
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;
/* 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;
}
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);
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;
}
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. */
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)
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)
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
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)
(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 \
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"
--- /dev/null
+#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
--- /dev/null
+ .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
--- /dev/null
+#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
--- /dev/null
+ .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
#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
#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
#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
#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
--- /dev/null
+#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
--- /dev/null
+ .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
--- /dev/null
+#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
--- /dev/null
+ .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
#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
#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
--- /dev/null
+#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
--- /dev/null
+ .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
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"