[PATCH v3 9/9] RISC-V: Introduce TLSDESC relaxation.
Tatsuyuki Ishi
ishitatsuyuki@gmail.com
Tue Nov 28 08:51:09 GMT 2023
For now, only the 2 instruction (long) forms. This allows static binaries
to correctly execute when TLSDESC is used.
bfd/
* elfnn-riscv.c (riscv_elf_tls_type_from_hi_reloc): Decide TLS type
based on relaxation eligibility as well.
(riscv_elf_check_relocs): Pass the required eligibility information to
riscv_elf_tls_type_from_hi_reloc.
(perform_relocation): Handle encoding for TLSDESC relaxation
relocations.
(riscv_elf_relocate_section): Emit relaxation instruction sequence.
(_bfd_riscv_relax_tlsdesc): Added for handling of relaxable TLSDESC
relocs.
(_bfd_riscv_relax_section): Call _bfd_riscv_relax_tlsdesc when
eligible.
* elfxx-riscv.c (howto_table_internal): Add internal relocations
for TLSDESC -> LE / IE relaxation.
include/
elf/riscv.h: Add internal relocations, same as above.
opcode/riscv.h: Add X_A0 for use in TLSDESC relaxation sequence.
ld/
* testsuite/ld-riscv-elf/tlsbin.d: Remove TLSDESC relocs from
expectation to now that we have relaxation.
---
bfd/elfnn-riscv.c | 146 ++++++++++++++++++++++++++++-
bfd/elfxx-riscv.c | 55 +++++++++++
include/elf/riscv.h | 16 ++--
include/opcode/riscv.h | 1 +
ld/testsuite/ld-riscv-elf/tlsbin.d | 5 -
5 files changed, 209 insertions(+), 14 deletions(-)
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index b98264319d8..61bfa763799 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -663,8 +663,12 @@ riscv_elf_copy_indirect_symbol (struct bfd_link_info *info,
}
static char
-riscv_elf_tls_type_from_hi_reloc (unsigned int r_type)
+riscv_elf_tls_type_from_hi_reloc (struct bfd_link_info *info,
+ unsigned int r_type,
+ struct elf_link_hash_entry *h,
+ bool can_relax)
{
+ bool local_exec = SYMBOL_REFERENCES_LOCAL (info, h);
switch (r_type)
{
case R_RISCV_TLS_GD_HI20:
@@ -672,7 +676,10 @@ riscv_elf_tls_type_from_hi_reloc (unsigned int r_type)
case R_RISCV_TLS_GOT_HI20:
return GOT_TLS_IE;
case R_RISCV_TLSDESC_HI20:
- return GOT_TLSDESC;
+ if (!can_relax || !bfd_link_executable (info))
+ return GOT_TLSDESC;
+ else
+ return local_exec ? GOT_TLS_LE : GOT_TLS_IE;
case R_RISCV_TPREL_HI20:
return GOT_TLS_LE;
default:
@@ -866,7 +873,10 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_RISCV_TLSDESC_HI20:
case R_RISCV_TPREL_HI20:
{
- char tls_type = riscv_elf_tls_type_from_hi_reloc (r_type);
+ bool can_relax = rel != relocs + sec->reloc_count - 1
+ && ELFNN_R_TYPE ((rel + 1)->r_info) == R_RISCV_RELAX
+ && rel->r_offset == (rel + 1)->r_offset;
+ char tls_type = riscv_elf_tls_type_from_hi_reloc (info, r_type, h, can_relax);
/* Local exec is only allowed for executables. */
if (tls_type == GOT_TLS_LE && !bfd_link_executable (info))
@@ -1826,6 +1836,8 @@ perform_relocation (const reloc_howto_type *howto,
case R_RISCV_TLS_GOT_HI20:
case R_RISCV_TLS_GD_HI20:
case R_RISCV_TLSDESC_HI20:
+ case R_RISCV_TLSDESC_LE_HI:
+ case R_RISCV_TLSDESC_IE_HI:
if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)))
return bfd_reloc_overflow;
value = ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
@@ -1838,6 +1850,8 @@ perform_relocation (const reloc_howto_type *howto,
case R_RISCV_PCREL_LO12_I:
case R_RISCV_TLSDESC_LOAD_LO12:
case R_RISCV_TLSDESC_ADD_LO12:
+ case R_RISCV_TLSDESC_LE_LO:
+ case R_RISCV_TLSDESC_IE_LO:
value = ENCODE_ITYPE_IMM (value);
break;
@@ -2818,6 +2832,35 @@ riscv_elf_relocate_section (bfd *output_bfd,
r = bfd_reloc_overflow;
break;
+ case R_RISCV_TLSDESC_IE_HI:
+ {
+ bfd_vma insn = MATCH_AUIPC | (X_A0 << OP_SH_RD);
+ relocation = dtpoff (info, relocation);
+ bfd_putl32 (insn, contents + rel->r_offset);
+ break;
+ }
+ case R_RISCV_TLSDESC_IE_LO:
+ {
+ bfd_vma insn = MATCH_LREG | (X_A0 << OP_SH_RD) | (X_A0 << OP_SH_RS1);
+ relocation = dtpoff (info, relocation);
+ bfd_putl32 (insn, contents + rel->r_offset);
+ break;
+ }
+ case R_RISCV_TLSDESC_LE_HI:
+ {
+ bfd_vma insn = MATCH_LUI | (X_A0 << OP_SH_RD);
+ relocation = tpoff (info, relocation);
+ bfd_putl32 (insn, contents + rel->r_offset);
+ break;
+ }
+ case R_RISCV_TLSDESC_LE_LO:
+ {
+ bfd_vma insn = MATCH_ADDI | (X_A0 << OP_SH_RD) | (X_A0 << OP_SH_RS1);
+ relocation = tpoff (info, relocation);
+ bfd_putl32 (insn, contents + rel->r_offset);
+ break;
+ }
+
case R_RISCV_GPREL_I:
case R_RISCV_GPREL_S:
{
@@ -4855,6 +4898,97 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
}
}
+/* Relax TLSDESC (global-dynamic) references to TLS IE or LE references. */
+
+static bool
+_bfd_riscv_relax_tlsdesc (bfd *abfd,
+ asection *sec,
+ asection *sym_sec,
+ struct bfd_link_info *link_info,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Rela *rel,
+ bfd_vma symval,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
+ bool *again,
+ riscv_pcgp_relocs *pcgp_relocs,
+ bool undefined_weak)
+{
+ BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+ BFD_ASSERT (bfd_link_executable (link_info));
+ riscv_pcgp_hi_reloc *hi = NULL;
+ bool local_exec;
+ unsigned sym;
+
+ /* Chain the _LO relocs to their corresponding _HI reloc to compute the
+ actual target address. */
+ switch (ELFNN_R_TYPE (rel->r_info)) {
+ case R_RISCV_TLSDESC_HI20: {
+ /* If the corresponding lo relocation has already been seen then it's not
+ safe to relax this relocation. */
+ if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset))
+ return true;
+ riscv_record_pcgp_hi_reloc (pcgp_relocs,
+ rel->r_offset,
+ rel->r_addend,
+ symval,
+ ELFNN_R_SYM (rel->r_info),
+ sym_sec,
+ h,
+ undefined_weak);
+ sym = ELFNN_R_SYM (rel->r_info);
+ break;
+ }
+
+ case R_RISCV_TLSDESC_LOAD_LO12:
+ case R_RISCV_TLSDESC_ADD_LO12:
+ case R_RISCV_TLSDESC_CALL: {
+ /* _LO addends are not allowed in the psABI, but handle them similarly to
+ PC_LO for now. */
+ bfd_vma hi_sec_off = symval - sec_addr (sym_sec) - rel->r_addend;
+ hi = riscv_find_pcgp_hi_reloc (pcgp_relocs, hi_sec_off);
+ if (hi == NULL) {
+ riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
+ return true;
+ }
+ sym = hi->hi_sym;
+ symval = hi->hi_addr;
+ sym_sec = hi->sym_sec;
+ h = hi->h;
+ break;
+ }
+ default:
+ abort ();
+ }
+
+ local_exec = SYMBOL_REFERENCES_LOCAL (link_info, h);
+
+ switch (ELFNN_R_TYPE (rel->r_info)) {
+ case R_RISCV_TLSDESC_HI20:
+ *again = true;
+ riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+ pcgp_relocs, rel);
+ break;
+ case R_RISCV_TLSDESC_LOAD_LO12:
+ riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+ pcgp_relocs, rel);
+ break;
+ case R_RISCV_TLSDESC_ADD_LO12:
+ rel->r_info = ELFNN_R_INFO (sym, local_exec ? R_RISCV_TLSDESC_LE_HI : R_RISCV_TLSDESC_IE_HI);
+ rel->r_addend += hi->hi_addend;
+ break;
+ case R_RISCV_TLSDESC_CALL:
+ rel->r_info = ELFNN_R_INFO (sym, local_exec ? R_RISCV_TLSDESC_LE_LO : R_RISCV_TLSDESC_IE_LO);
+ rel->r_addend += hi->hi_addend;
+ break;
+ default:
+ abort ();
+ }
+
+ return true;
+}
+
+
/* Implement R_RISCV_ALIGN by deleting excess alignment NOPs.
Once we've handled an R_RISCV_ALIGN, we can't relax anything else. */
@@ -5159,6 +5293,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
|| type == R_RISCV_TPREL_LO12_I
|| type == R_RISCV_TPREL_LO12_S)
relax_func = _bfd_riscv_relax_tls_le;
+ else if (bfd_link_executable (info)
+ && (type == R_RISCV_TLSDESC_HI20
+ || type == R_RISCV_TLSDESC_LOAD_LO12
+ || type == R_RISCV_TLSDESC_ADD_LO12
+ || type == R_RISCV_TLSDESC_CALL))
+ relax_func = _bfd_riscv_relax_tlsdesc;
else if (!bfd_link_pic (info)
&& (type == R_RISCV_PCREL_HI20
|| type == R_RISCV_PCREL_LO12_I
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 65d676b06b1..dbca69a89d1 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -958,6 +958,61 @@ static reloc_howto_type howto_table_internal[] =
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
false), /* pcrel_offset */
+
+ /* TLSDESC relaxed to Initial Exec. */
+ HOWTO (R_RISCV_TLSDESC_IE_HI, /* type */
+ 0, /* rightshift */
+ 4, /* size */
+ 32, /* bitsize */
+ true, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLSDESC_IE_HI", /* name */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ ENCODE_UTYPE_IMM (-1U), /* dst_mask */
+ false), /* pcrel_offset */
+ HOWTO (R_RISCV_TLSDESC_IE_LO, /* type */
+ 0, /* rightshift */
+ 4, /* size */
+ 32, /* bitsize */
+ true, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLSDESC_IE_LO", /* name */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ ENCODE_ITYPE_IMM (-1U), /* dst_mask */
+ false), /* pcrel_offset */
+ /* TLSDESC relaxed to Local Exec. */
+ HOWTO (R_RISCV_TLSDESC_LE_HI, /* type */
+ 0, /* rightshift */
+ 4, /* size */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLSDESC_LE_HI", /* name */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ ENCODE_UTYPE_IMM (-1U), /* dst_mask */
+ false), /* pcrel_offset */
+ HOWTO (R_RISCV_TLSDESC_LE_LO, /* type */
+ 0, /* rightshift */
+ 4, /* size */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLSDESC_LE_LO", /* name */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ ENCODE_ITYPE_IMM (-1U), /* dst_mask */
+ false), /* pcrel_offset */
};
/* A mapping from BFD reloc types to RISC-V ELF reloc types. */
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 7f5c0407ac8..903c7645d68 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -98,12 +98,16 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
END_RELOC_NUMBERS (R_RISCV_max)
/* Internal relocations used exclusively by the relaxation pass. */
-#define R_RISCV_DELETE (R_RISCV_max)
-#define R_RISCV_RVC_LUI (R_RISCV_max + 1)
-#define R_RISCV_GPREL_I (R_RISCV_max + 2)
-#define R_RISCV_GPREL_S (R_RISCV_max + 3)
-#define R_RISCV_TPREL_I (R_RISCV_max + 4)
-#define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_DELETE (R_RISCV_max)
+#define R_RISCV_RVC_LUI (R_RISCV_max + 1)
+#define R_RISCV_GPREL_I (R_RISCV_max + 2)
+#define R_RISCV_GPREL_S (R_RISCV_max + 3)
+#define R_RISCV_TPREL_I (R_RISCV_max + 4)
+#define R_RISCV_TPREL_S (R_RISCV_max + 5)
+#define R_RISCV_TLSDESC_IE_HI (R_RISCV_max + 6)
+#define R_RISCV_TLSDESC_IE_LO (R_RISCV_max + 7)
+#define R_RISCV_TLSDESC_LE_HI (R_RISCV_max + 8)
+#define R_RISCV_TLSDESC_LE_LO (R_RISCV_max + 9)
/* Processor specific flags for the ELF header e_flags field. */
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 25486869606..e60e1ba9502 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -340,6 +340,7 @@ static inline unsigned int riscv_insn_length (insn_t insn)
#define X_T0 5
#define X_T1 6
#define X_T2 7
+#define X_A0 10
#define X_T3 28
#define NGPR 32
diff --git a/ld/testsuite/ld-riscv-elf/tlsbin.d b/ld/testsuite/ld-riscv-elf/tlsbin.d
index 79b7ade405e..cdcd51a9199 100644
--- a/ld/testsuite/ld-riscv-elf/tlsbin.d
+++ b/ld/testsuite/ld-riscv-elf/tlsbin.d
@@ -2,11 +2,6 @@
#ld: -no-pie tmpdir/tlslib.so
#readelf: -Wr
-Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
- +Offset +Info +Type +Symbol's Value +Symbol's Name \+ Addend
-[0-9a-f]+ +[0-9a-f]+ R_RISCV_TLSDESC +4
-[0-9a-f]+ +[0-9a-f]+ R_RISCV_TLSDESC +0
-
Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
+Offset +Info +Type +Symbol's Value +Symbol's Name \+ Addend
[0-9a-f]+ +[0-9a-f]+ R_RISCV_JUMP_SLOT +[0-9a-f]+ __tls_get_addr \+ 0
--
2.43.0
More information about the Binutils
mailing list