diff mbox series

RISC-V: Implement TLS Descriptors.

Message ID 20230817181228.122674-2-ishitatsuyuki@gmail.com
State New
Headers show
Series RISC-V: Implement TLS Descriptors. | expand

Commit Message

Tatsuyuki Ishi Aug. 17, 2023, 6:12 p.m. UTC
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

Comments

Andreas Schwab Aug. 17, 2023, 6:35 p.m. UTC | #1
On Aug 18 2023, Tatsuyuki Ishi via Libc-alpha wrote:

> \ No newline at end of file

Please fix that.
Andrew Waterman Sept. 13, 2023, 7:07 p.m. UTC | #2
The 3*SZREG stack-frame size won't maintain the ABI stack alignment; I
suggest masking it with ALMASK.


On Thu, Aug 17, 2023 at 11:13 AM Tatsuyuki Ishi via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> 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
>
diff mbox series

Patch

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