[PATCH] RISC-V: Implement TLS Descriptors.
Tatsuyuki Ishi
ishitatsuyuki@gmail.com
Thu Aug 17 18:12:26 GMT 2023
This is mostly based off AArch64 implementation, with some adaptations
to different TLS DTV offsets and calling conventions.
---
No regression in binutils and gcc tests for rv64gc, tested alongside the
gcc and binutils implementation (posted at the same time).
This contribution is made on behalf of Blue Whale Systems, which has
copyright assignment on file with the FSF.
sysdeps/riscv/Makefile | 8 ++
sysdeps/riscv/dl-lookupcfg.h | 27 +++++
sysdeps/riscv/dl-machine.h | 27 +++++
sysdeps/riscv/dl-tlsdesc.S | 204 +++++++++++++++++++++++++++++++++++
sysdeps/riscv/dl-tlsdesc.h | 49 +++++++++
sysdeps/riscv/linkmap.h | 1 +
sysdeps/riscv/tlsdesc.c | 38 +++++++
sysdeps/riscv/tlsdesc.sym | 19 ++++
8 files changed, 373 insertions(+)
create mode 100644 sysdeps/riscv/dl-lookupcfg.h
create mode 100644 sysdeps/riscv/dl-tlsdesc.S
create mode 100644 sysdeps/riscv/dl-tlsdesc.h
create mode 100644 sysdeps/riscv/tlsdesc.c
create mode 100644 sysdeps/riscv/tlsdesc.sym
diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
index 8fb10b164f..bb4bcd29b2 100644
--- a/sysdeps/riscv/Makefile
+++ b/sysdeps/riscv/Makefile
@@ -2,6 +2,14 @@ ifeq ($(subdir),misc)
sysdep_headers += sys/asm.h
endif
+ifeq ($(subdir),elf)
+sysdep-dl-routines += tlsdesc dl-tlsdesc
+endif
+
+ifeq ($(subdir),csu)
+gen-as-const-headers += tlsdesc.sym
+endif
+
# RISC-V's assembler also needs to know about PIC as it changes the definition
# of some assembler macros.
ASFLAGS-.os += $(pic-ccflag)
diff --git a/sysdeps/riscv/dl-lookupcfg.h b/sysdeps/riscv/dl-lookupcfg.h
new file mode 100644
index 0000000000..c003a27f63
--- /dev/null
+++ b/sysdeps/riscv/dl-lookupcfg.h
@@ -0,0 +1,27 @@
+/* Configuration of lookup functions.
+ Copyright (C) 2006-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#define DL_UNMAP_IS_SPECIAL
+
+#include_next <dl-lookupcfg.h>
+
+struct link_map;
+
+extern void _dl_unmap (struct link_map *map);
+
+#define DL_UNMAP(map) _dl_unmap (map)
\ No newline at end of file
diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
index c0c9bd93ad..ad5b3b20d0 100644
--- a/sysdeps/riscv/dl-machine.h
+++ b/sysdeps/riscv/dl-machine.h
@@ -25,6 +25,7 @@
#include <elf/elf.h>
#include <sys/asm.h>
#include <dl-tls.h>
+#include <dl-tlsdesc.h>
#include <dl-irel.h>
#include <dl-static-tls.h>
#include <dl-machine-rel.h>
@@ -219,6 +220,32 @@ elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
}
break;
+ case R_RISCV_TLSDESC:
+ struct tlsdesc *td = (struct tlsdesc *) addr_field;
+ if (sym == NULL)
+ {
+ td->entry = _dl_tlsdesc_undefweak;
+ td->arg = reloc->r_addend;
+ }
+ else
+ {
+# ifndef SHARED
+ CHECK_STATIC_TLS (map, sym_map);
+# else
+ if (!TRY_STATIC_TLS (map, sym_map))
+ {
+ td->entry = _dl_tlsdesc_dynamic;
+ td->arg = _dl_make_tlsdesc_dynamic (sym_map, sym->st_value + reloc->r_addend);
+ }
+ else
+# endif
+ {
+ td->entry = _dl_tlsdesc_return;
+ td->arg = (void *)(TLS_TPREL_VALUE(sym_map, sym) + reloc->r_addend);
+ }
+ }
+ break;
+
case R_RISCV_COPY:
{
if (__glibc_unlikely (sym == NULL))
diff --git a/sysdeps/riscv/dl-tlsdesc.S b/sysdeps/riscv/dl-tlsdesc.S
new file mode 100644
index 0000000000..bc48939739
--- /dev/null
+++ b/sysdeps/riscv/dl-tlsdesc.S
@@ -0,0 +1,204 @@
+/* Thread-local storage handling in the ELF dynamic linker.
+ RISC-V version.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <sysdep.h>
+#include <tls.h>
+#include <tlsdesc.h>
+
+#ifdef __riscv_float_abi_soft
+# define FRAME_SIZE (-((-12 * SZREG) & ALMASK))
+#else
+# define FRAME_SIZE (-((-12 * SZREG - 20 * SZFREG) & ALMASK))
+#endif
+
+ .text
+
+ /* Compute the thread pointer offset for symbols in the static
+ TLS block. The offset is the same for all threads.
+ Prototype:
+ _dl_tlsdesc_return (tlsdesc *) ;
+ */
+ENTRY (_dl_tlsdesc_return)
+ REG_L a0, SZREG(a0)
+ jr t0
+END (_dl_tlsdesc_return)
+
+ /* Handler for undefined weak TLS symbols.
+ Prototype:
+ _dl_tlsdesc_undefweak (tlsdesc *);
+
+ The second word of the descriptor contains the addend.
+ Return the addend minus the thread pointer. This ensures
+ that when the caller adds on the thread pointer it gets back
+ the addend. */
+
+ENTRY (_dl_tlsdesc_undefweak)
+ REG_L a0, SZREG(a0)
+ sub a0, a0, tp
+ jr t0
+END (_dl_tlsdesc_undefweak)
+
+#ifdef SHARED
+ /* Handler for dynamic TLS symbols.
+ Prototype:
+ _dl_tlsdesc_dynamic (tlsdesc *) ;
+
+ The second word of the descriptor points to a
+ tlsdesc_dynamic_arg structure.
+
+ Returns the offset between the thread pointer and the
+ object referenced by the argument.
+
+ unsigned long
+ _dl_tlsdesc_dynamic (struct tlsdesc *tdp)
+ {
+ struct tlsdesc_dynamic_arg *td = tdp->arg;
+ dtv_t *dtv = *(dtv_t **)((char *)__thread_pointer + TCBHEAD_DTV);
+ if (__builtin_expect (td->gen_count <= dtv[0].counter
+ && (dtv[td->tlsinfo.ti_module].pointer.val
+ != TLS_DTV_UNALLOCATED),
+ 1))
+ return dtv[td->tlsinfo.ti_module].pointer.val
+ + td->tlsinfo.ti_offset
+ - __thread_pointer;
+
+ return ___tls_get_addr (&td->tlsinfo) - __thread_pointer;
+ }
+ */
+
+ENTRY (_dl_tlsdesc_dynamic)
+ /* Save just enough registers to support fast path, if we fall
+ into slow path we will save additional registers. */
+ add sp, sp, -3*SZREG
+ REG_S t0, 0*SZREG(sp)
+ REG_S t1, 1*SZREG(sp)
+ REG_S t2, 2*SZREG(sp)
+
+ /* t0 = dtv */
+ REG_L t0, TCBHEAD_DTV(tp)
+ /* a0 = tdp->arg */
+ REG_L a0, TLSDESC_ARG(a0)
+ /* t1 = td->gen_count */
+ REG_L t1, TLSDESC_GEN_COUNT(a0)
+ /* t2 = dtv[0].counter */
+ REG_L t2, DTV_COUNTER(t0)
+ bltu t2, t1, .Lslow
+ /* t1 = td->tlsinfo.ti_module */
+ REG_L t1, TLSDESC_MODID(a0)
+ slli t1, t1, PTRLOG + 1 /* sizeof(dtv_t) == sizeof(void*) * 2 */
+ add t1, t1, t0
+ /* t1 = dtv[td->tlsinfo.ti_module].pointer.val */
+ REG_L t1, 0(t1)
+ li t2, TLS_DTV_UNALLOCATED
+ beq t1, t2, .Lslow
+ /* t2 = td->tlsinfo.ti_offset */
+ REG_L t2, TLSDESC_MODOFF(a0)
+ add a0, t1, t2
+.Lret:
+ sub a0, a0, tp
+ REG_L t0, 0*SZREG(sp)
+ REG_L t1, 1*SZREG(sp)
+ REG_L t2, 2*SZREG(sp)
+ add sp, sp, 3*SZREG
+ jr t0
+.Lslow:
+ /* This is the slow path. We need to call __tls_get_addr() which
+ means we need to save and restore all the register that the
+ callee will trash. */
+
+ /* Save the remaining registers that we must treat as caller save. */
+ addi sp, sp, -FRAME_SIZE
+ REG_S ra, 0*SZREG(sp)
+ REG_S a1, 1*SZREG(sp)
+ REG_S a2, 2*SZREG(sp)
+ REG_S a3, 3*SZREG(sp)
+ REG_S a4, 4*SZREG(sp)
+ REG_S a5, 5*SZREG(sp)
+ REG_S a6, 6*SZREG(sp)
+ REG_S a7, 7*SZREG(sp)
+ REG_S t3, 8*SZREG(sp)
+ REG_S t4, 9*SZREG(sp)
+ REG_S t5, 10*SZREG(sp)
+ REG_S t6, 11*SZREG(sp)
+#ifndef __riscv_float_abi_soft
+ FREG_S ft0, (12*SZREG + 0*SZFREG)(sp)
+ FREG_S ft1, (12*SZREG + 1*SZFREG)(sp)
+ FREG_S ft2, (12*SZREG + 2*SZFREG)(sp)
+ FREG_S ft3, (12*SZREG + 3*SZFREG)(sp)
+ FREG_S ft4, (12*SZREG + 4*SZFREG)(sp)
+ FREG_S ft5, (12*SZREG + 5*SZFREG)(sp)
+ FREG_S ft6, (12*SZREG + 6*SZFREG)(sp)
+ FREG_S ft7, (12*SZREG + 7*SZFREG)(sp)
+ FREG_S fa0, (12*SZREG + 8*SZFREG)(sp)
+ FREG_S fa1, (12*SZREG + 9*SZFREG)(sp)
+ FREG_S fa2, (12*SZREG + 10*SZFREG)(sp)
+ FREG_S fa3, (12*SZREG + 11*SZFREG)(sp)
+ FREG_S fa4, (12*SZREG + 12*SZFREG)(sp)
+ FREG_S fa5, (12*SZREG + 13*SZFREG)(sp)
+ FREG_S fa6, (12*SZREG + 14*SZFREG)(sp)
+ FREG_S fa7, (12*SZREG + 15*SZFREG)(sp)
+ FREG_S ft8, (12*SZREG + 16*SZFREG)(sp)
+ FREG_S ft9, (12*SZREG + 17*SZFREG)(sp)
+ FREG_S ft10, (12*SZREG + 18*SZFREG)(sp)
+ FREG_S ft11, (12*SZREG + 19*SZFREG)(sp)
+#endif
+
+ addi a0, a0, SZREG
+ call __tls_get_addr
+ addi a0, a0, -TLS_DTV_OFFSET
+
+ REG_L ra, 0*SZREG(sp)
+ REG_L a1, 1*SZREG(sp)
+ REG_L a2, 2*SZREG(sp)
+ REG_L a3, 3*SZREG(sp)
+ REG_L a4, 4*SZREG(sp)
+ REG_L a5, 5*SZREG(sp)
+ REG_L a6, 6*SZREG(sp)
+ REG_L a7, 7*SZREG(sp)
+ REG_L t3, 8*SZREG(sp)
+ REG_L t4, 9*SZREG(sp)
+ REG_L t5, 10*SZREG(sp)
+ REG_L t6, 11*SZREG(sp)
+#ifndef __riscv_float_abi_soft
+ FREG_L ft0, (12*SZREG + 0*SZFREG)(sp)
+ FREG_L ft1, (12*SZREG + 1*SZFREG)(sp)
+ FREG_L ft2, (12*SZREG + 2*SZFREG)(sp)
+ FREG_L ft3, (12*SZREG + 3*SZFREG)(sp)
+ FREG_L ft4, (12*SZREG + 4*SZFREG)(sp)
+ FREG_L ft5, (12*SZREG + 5*SZFREG)(sp)
+ FREG_L ft6, (12*SZREG + 6*SZFREG)(sp)
+ FREG_L ft7, (12*SZREG + 7*SZFREG)(sp)
+ FREG_L fa0, (12*SZREG + 8*SZFREG)(sp)
+ FREG_L fa1, (12*SZREG + 9*SZFREG)(sp)
+ FREG_L fa2, (12*SZREG + 10*SZFREG)(sp)
+ FREG_L fa3, (12*SZREG + 11*SZFREG)(sp)
+ FREG_L fa4, (12*SZREG + 12*SZFREG)(sp)
+ FREG_L fa5, (12*SZREG + 13*SZFREG)(sp)
+ FREG_L fa6, (12*SZREG + 14*SZFREG)(sp)
+ FREG_L fa7, (12*SZREG + 15*SZFREG)(sp)
+ FREG_L ft8, (12*SZREG + 16*SZFREG)(sp)
+ FREG_L ft9, (12*SZREG + 17*SZFREG)(sp)
+ FREG_L ft10, (12*SZREG + 18*SZFREG)(sp)
+ FREG_L ft11, (12*SZREG + 19*SZFREG)(sp)
+#endif
+ addi sp, sp, FRAME_SIZE
+ j .Lret
+END (_dl_tlsdesc_dynamic)
+#endif
diff --git a/sysdeps/riscv/dl-tlsdesc.h b/sysdeps/riscv/dl-tlsdesc.h
new file mode 100644
index 0000000000..3156f34e9c
--- /dev/null
+++ b/sysdeps/riscv/dl-tlsdesc.h
@@ -0,0 +1,49 @@
+/* Thread-local storage descriptor handling in the ELF dynamic linker.
+ RISC-V version.
+ Copyright (C) 2011-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _DL_TLSDESC_H
+# define _DL_TLSDESC_H 1
+
+#include <dl-tls.h>
+
+/* Type used to represent a TLS descriptor in the GOT. */
+struct tlsdesc
+{
+ ptrdiff_t (*entry) (struct tlsdesc *);
+ void *arg;
+};
+
+/* Type used as the argument in a TLS descriptor for a symbol that
+ needs dynamic TLS offsets. */
+struct tlsdesc_dynamic_arg
+{
+ tls_index tlsinfo;
+ size_t gen_count;
+};
+
+extern unsigned long attribute_hidden
+ _dl_tlsdesc_return(struct tlsdesc *),
+ _dl_tlsdesc_undefweak(struct tlsdesc *);
+
+# ifdef SHARED
+extern void *_dl_make_tlsdesc_dynamic (struct link_map *, size_t);
+extern unsigned long attribute_hidden _dl_tlsdesc_dynamic(struct tlsdesc *);
+# endif
+
+#endif /* _DL_TLSDESC_H */
\ No newline at end of file
diff --git a/sysdeps/riscv/linkmap.h b/sysdeps/riscv/linkmap.h
index ac170bb342..2fa3f6d43f 100644
--- a/sysdeps/riscv/linkmap.h
+++ b/sysdeps/riscv/linkmap.h
@@ -1,4 +1,5 @@
struct link_map_machine
{
ElfW(Addr) plt; /* Address of .plt. */
+ void *tlsdesc_table; /* Address of TLS descriptor hash table. */
};
diff --git a/sysdeps/riscv/tlsdesc.c b/sysdeps/riscv/tlsdesc.c
new file mode 100644
index 0000000000..a76aaa9fc5
--- /dev/null
+++ b/sysdeps/riscv/tlsdesc.c
@@ -0,0 +1,38 @@
+/* Manage TLS descriptors. RISC-V version.
+ Copyright (C) 2005-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <ldsodefs.h>
+#include <tls.h>
+#include <dl-tls.h>
+#include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
+#include <tlsdeschtab.h>
+
+/* Unmap the dynamic object, but also release its TLS descriptor table
+ if there is one. */
+
+void
+_dl_unmap (struct link_map *map)
+{
+ _dl_unmap_segments (map);
+
+#ifdef SHARED
+ if (map->l_mach.tlsdesc_table)
+ htab_delete (map->l_mach.tlsdesc_table);
+#endif
+}
diff --git a/sysdeps/riscv/tlsdesc.sym b/sysdeps/riscv/tlsdesc.sym
new file mode 100644
index 0000000000..652e72ea58
--- /dev/null
+++ b/sysdeps/riscv/tlsdesc.sym
@@ -0,0 +1,19 @@
+#include <stddef.h>
+#include <sysdep.h>
+#include <tls.h>
+#include <link.h>
+#include <dl-tls.h>
+#include <dl-tlsdesc.h>
+
+--
+
+-- Abuse tls.h macros to derive offsets relative to the thread register.
+
+TLSDESC_ARG offsetof(struct tlsdesc, arg)
+TLSDESC_GEN_COUNT offsetof(struct tlsdesc_dynamic_arg, gen_count)
+TLSDESC_MODID offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_module)
+TLSDESC_MODOFF offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_offset)
+TCBHEAD_DTV offsetof(tcbhead_t, dtv) - sizeof(tcbhead_t) - TLS_TCB_OFFSET
+DTV_COUNTER offsetof(dtv_t, counter)
+TLS_DTV_UNALLOCATED TLS_DTV_UNALLOCATED
+TLS_DTV_OFFSET TLS_DTV_OFFSET
--
2.41.0
More information about the Libc-alpha
mailing list