summaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorMatthew Malcomson <hardenedapple@gmail.com>2022-08-02 12:10:01 +0100
committerMatthew Malcomson <hardenedapple@gmail.com>2022-08-02 12:10:01 +0100
commit976f16630b1f7421d6693011333cf0f51417c498 (patch)
treed364d4f02f24f64f63d7421370d8a7f4c4bd202e /bfd
parentb90e0b2f1b516ea32789ae3877830b518cb8ed60 (diff)
ld: aarch64: Adjust TLS relaxation condition
In aarch64_tls_transition_without_check and elfNN_aarch64_tls_relax we choose whether to perform a relaxation to an IE access model or an LE access model based on whether the symbol itself is marked as local (i.e. `h == NULL`). This is problematic in two ways. The first is that sometimes a global dynamic access can be relaxed to an initial exec access when creating a shared library, and if that happens on a local symbol then we currently relax it to a local exec access instead. This usually does not happen since we only relax an access if aarch64_can_relax_tls returns true and aarch64_can_relax_tls does not have the same problem. However, it can happen when we have seen both an IE and GD access on the same symbol. This case is exercised in the newly added testcase tls-relax-gd-ie-2. The second problem is that deciding based on whether the symbol is local misses the case when the symbol is global but is still non-interposable and known to be located in the executable. This happens on all global symbols in executables. This case is exercised in the newly added testcase tls-relax-ie-le-4. Here we adjust the condition we base our relaxation on so that we relax to local-exec if we are creating an executable and the relevant symbol we're accessing is stored inside that executable. -- Updating tests for new relaxation criteria Many of the tests added to check our relaxation to IE were implemented by taking advantage of the fact that we did not relax a global symbol defined in an executable. Since a global symbol defined in an executable is still not interposable, we know that a TLS version of such a symbol will be in the main TLS block. This means that we can perform a stronger relaxation on such symbols and relax their accesses to a local-exec access. Hence we have to update all tests that relied on the older suboptimal decision making. The two cases when we still would want to relax a general dynamic access to an initial exec one are: 1) When in a shared library and accessing a symbol which we have already seen accessed with an initial exec access sequence. 2) When in an executable and accessing a symbol defined in a shared library. Both of these require shared library support, which means that these tests are now only available on targets with that. I have chosen to switch the existing testcases from a plain executable to one dynamically linked to a shared object as that doesn't require changing the testcases quite so much (just requires accessing a different variable rather than requiring adding another code sequence). The tls-relax-all testcase was an outlier to the above approach, since it included a general dynamic access to both a local and global symbol and inspected for the difference accordingly.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/elfnn-aarch64.c70
1 files changed, 37 insertions, 33 deletions
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index cf4db84928..ac48a17572 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -4898,60 +4898,62 @@ aarch64_calculate_got_entry_vma (struct elf_link_hash_entry *h,
static bfd_reloc_code_real_type
aarch64_tls_transition_without_check (bfd_reloc_code_real_type r_type,
- struct elf_link_hash_entry *h)
+ struct elf_link_hash_entry *h,
+ struct bfd_link_info *info)
{
- bool is_local = h == NULL;
+ bool local_exec = bfd_link_executable (info)
+ && SYMBOL_REFERENCES_LOCAL (info, h);
switch (r_type)
{
case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1
: BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21);
case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC
: r_type);
case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1
: BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19);
case BFD_RELOC_AARCH64_TLSDESC_LDR:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC
: BFD_RELOC_AARCH64_NONE);
case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC
: BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC);
case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2
: BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1);
case BFD_RELOC_AARCH64_TLSDESC_LDNN_LO12_NC:
case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC
: BFD_RELOC_AARCH64_TLSIE_LDNN_GOTTPREL_LO12_NC);
case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
- return is_local ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1 : r_type;
+ return local_exec ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1 : r_type;
case BFD_RELOC_AARCH64_TLSIE_LDNN_GOTTPREL_LO12_NC:
- return is_local ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC : r_type;
+ return local_exec ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC : r_type;
case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
return r_type;
case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
- return (is_local
+ return (local_exec
? BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12
: BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19);
@@ -4964,16 +4966,16 @@ aarch64_tls_transition_without_check (bfd_reloc_code_real_type r_type,
case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC:
case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21:
case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21:
- return is_local ? BFD_RELOC_AARCH64_NONE : r_type;
+ return local_exec ? BFD_RELOC_AARCH64_NONE : r_type;
#if ARCH_SIZE == 64
case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC:
- return is_local
+ return local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC
: BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC;
case BFD_RELOC_AARCH64_TLSGD_MOVW_G1:
- return is_local
+ return local_exec
? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2
: BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1;
#endif
@@ -5082,7 +5084,7 @@ aarch64_tls_transition (bfd *input_bfd,
if (! aarch64_can_relax_tls (input_bfd, info, bfd_r_type, h, r_symndx))
return bfd_r_type;
- return aarch64_tls_transition_without_check (bfd_r_type, h);
+ return aarch64_tls_transition_without_check (bfd_r_type, h, info);
}
/* Return the base VMA address which should be subtracted from real addresses
@@ -6321,9 +6323,11 @@ static bfd_reloc_status_type
elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
bfd *input_bfd, asection *input_section,
bfd_byte *contents, Elf_Internal_Rela *rel,
- struct elf_link_hash_entry *h)
+ struct elf_link_hash_entry *h,
+ struct bfd_link_info *info)
{
- bool is_local = h == NULL;
+ bool local_exec = bfd_link_executable (info)
+ && SYMBOL_REFERENCES_LOCAL (info, h);
unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
unsigned long insn;
@@ -6333,7 +6337,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
{
case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
- if (is_local)
+ if (local_exec)
{
/* GD->LE relaxation:
adrp x0, :tlsgd:var => movz R0, :tprel_g1:var
@@ -6362,7 +6366,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
break;
case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19:
- if (is_local)
+ if (local_exec)
{
/* Tiny TLSDESC->LE relaxation:
ldr x1, :tlsdesc:var => movz R0, #:tprel_g1:var
@@ -6404,7 +6408,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
}
case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
- if (is_local)
+ if (local_exec)
{
/* Tiny GD->LE relaxation:
adr x0, :tlsgd:var => mrs x1, tpidr_el0
@@ -6456,7 +6460,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
BFD_ASSERT (rel->r_offset + 12 == rel[2].r_offset);
BFD_ASSERT (ELFNN_R_TYPE (rel[2].r_info) == AARCH64_R (CALL26));
- if (is_local)
+ if (local_exec)
{
/* Large GD->LE relaxation:
movz x0, #:tlsgd_g1:var => movz x0, #:tprel_g2:var, lsl #32
@@ -6500,7 +6504,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
return bfd_reloc_continue;
case BFD_RELOC_AARCH64_TLSDESC_LDNN_LO12_NC:
- if (is_local)
+ if (local_exec)
{
/* GD->LE relaxation:
ldr xd, [x0, #:tlsdesc_lo12:var] => movk x0, :tprel_g0_nc:var
@@ -6521,7 +6525,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
}
case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
- if (is_local)
+ if (local_exec)
{
/* GD->LE relaxation
add x0, #:tlsgd_lo12:var => movk R0, :tprel_g0_nc:var
@@ -6574,7 +6578,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
return bfd_reloc_ok;
case BFD_RELOC_AARCH64_TLSDESC_LDR:
- if (is_local)
+ if (local_exec)
{
/* GD->LE relaxation:
ldr xd, [gp, xn] => movk R0, #:tprel_g0_nc:var
@@ -6601,12 +6605,12 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
movk xd, #:tlsdesc_off_g0_nc:var => movk Rd, #:gottprel_g0_nc:var
Where R is x for lp64 mode, and w for ILP32 mode. */
- if (is_local)
+ if (local_exec)
bfd_putl32 (ldr_hw_R0, contents + rel->r_offset);
return bfd_reloc_continue;
case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
- if (is_local)
+ if (local_exec)
{
/* GD->LE relaxation:
movz xd, #:tlsdesc_off_g1:var => movz R0, #:tprel_g2:var, lsl #32
@@ -6631,7 +6635,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
adrp xd, :gottprel:var => movz Rd, :tprel_g1:var
Where R is x for lp64 mode, and w for ILP32 mode. */
- if (is_local)
+ if (local_exec)
{
insn = bfd_getl32 (contents + rel->r_offset);
bfd_putl32 (movz_R0 | (insn & 0x1f), contents + rel->r_offset);
@@ -6646,7 +6650,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
ldr xd, [xm, #:gottprel_lo12:var] => movk Rd, :tprel_g0_nc:var
Where R is x for lp64 mode, and w for ILP32 mode. */
- if (is_local)
+ if (local_exec)
{
insn = bfd_getl32 (contents + rel->r_offset);
bfd_putl32 (movk_R0 | (insn & 0x1f), contents + rel->r_offset);
@@ -6659,7 +6663,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
bl __tls_get_addr => add R0, R0, TCB_SIZE
Where R is x for lp64 mode, and w for ilp32 mode. */
- if (is_local)
+ if (local_exec)
{
BFD_ASSERT (rel->r_offset + 4 == rel[1].r_offset);
BFD_ASSERT (ELFNN_R_TYPE (rel[1].r_info) == AARCH64_R (CALL26));
@@ -6676,7 +6680,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
/* LD->LE relaxation (small):
adrp x0, :tlsldm:x => mrs x0, tpidr_el0
*/
- if (is_local)
+ if (local_exec)
{
bfd_putl32 (0xd53bd040, contents + rel->r_offset);
return bfd_reloc_ok;
@@ -6689,7 +6693,7 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
bl __tls_get_addr => nop
Where R is x for lp64 mode, and w for ilp32 mode. */
- if (is_local)
+ if (local_exec)
{
BFD_ASSERT (rel->r_offset + 4 == rel[1].r_offset);
BFD_ASSERT (ELFNN_R_TYPE (rel[1].r_info) == AARCH64_R (CALL26));
@@ -6863,7 +6867,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
BFD_ASSERT (howto != NULL);
r_type = howto->type;
r = elfNN_aarch64_tls_relax (globals, input_bfd, input_section,
- contents, rel, h);
+ contents, rel, h, info);
unresolved_reloc = 0;
}
else