Re: [PATCH] riscv: Implement TLS Descriptors
Luke Zhuang
luke.zhuang@linux.alibaba.com
Wed Jul 15 04:30:13 GMT 2026
Hi Andreas,
Oh, sorry for not noticing it happens to be the code freeze period, it's my first time contributing to glibc :) That's not in an urgent feature so I can wait till the next development window. May I ask is that just leave the patch as it is, and ping later? I'll keep track on the releasing process and go back to it after it's done.
Thank you very much!
Best,
Luke Zhuang
------------------------------------------------------------------
From:Andreas K. Huettel <dilfridge@gentoo.org>
Send Time:Wed, Jul 15, 2026, 09:57
To:"libc-alpha"<libc-alpha@sourceware.org>
CC:"adhemerval.zanella"<adhemerval.zanella@linaro.org>; fweimer<fweimer@redhat.com>; schwab<schwab@suse.de>; palmer<palmer@rivosinc.com>; andrew<andrew@sifive.com>; rui314<rui314@gmail.com>; ruiu<ruiu@bluewhale.systems>; Luke Zhuang<luke.zhuang@linux.alibaba.com>; Tatsuyuki Ishi<ishitatsuyuki@gmail.com>
Subject:Re: [PATCH] riscv: Implement TLS Descriptors
Am Dienstag, 14. Juli 2026, 23:30:42 Japanische Normalzeit schrieb Luke Zhuang:
> From: "Luke Zhuang" <luke.zhuang@linux.alibaba.com>
>
> Implement TLS Descriptors (TLSDESC) for RISC-V, as specified by the
> RISC-V psABI TLSDESC extension [1].
>
That's post-release material.
> This adds the RISC-V TLSDESC relocation handling and the three resolver
> entry points used by the dynamic loader:
>
> _dl_tlsdesc_undefweak
> _dl_tlsdesc_return
> _dl_tlsdesc_dynamic
>
> The dynamic resolver implements the usual fast path by checking the DTV
> generation counter, and falls back to __tls_get_addr on the slow path.
>
> This work is based on Tatsuyuki Ishi's original patch series [2], and
> is being relanded with his authorization. The reland includes the
> following updates:
>
> * Do not save and restore vector registers in _dl_tlsdesc_dynamic.
> The RISC-V psABI now defines vector registers and vector CSRs as
> caller-clobbered across TLSDESC calls [3], so preserving live vector
> state is the compiler's responsibility.
>
> * Call HIDDEN_JUMPTARGET(__tls_get_addr) from ld.so, avoiding a PLT
> reference to __tls_get_addr.
>
> * Publish TLSDESC entries in elf_machine_rela in arg-before-entry
> order, and use a volatile tlsdesc pointer, matching the ordering
> used by other architectures.
>
> * Add sysdeps/riscv/tst-gnu2-tls2.h to test that the RISC-V TLSDESC
> resolver preserves the required caller-saved GPR and FPR state
> across the slow path.
>
> Basic GCC and binutils support for RISC-V TLSDESC relocations is already
> upstream [4][5]. Linker relaxation support is on the way but should not
> be a blocker.
>
> [1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/373 <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/373 >
> [2] https://inbox.sourceware.org/libc-alpha/09A14C77-9EEA-4707-9E36-D3AAF8A98B24@gmail.com/T/#u <https://inbox.sourceware.org/libc-alpha/09A14C77-9EEA-4707-9E36-D3AAF8A98B24@gmail.com/T/#u >
> [3] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/496 <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/496 >
> [4] https://gcc.gnu.org/cgit/gcc/commit/?id=97069657c4e40b209c7b774e12faaca13812a86c <https://gcc.gnu.org/cgit/gcc/commit/?id=97069657c4e40b209c7b774e12faaca13812a86c >
> [5] https://sourceware.org/git/?p=binutils-gdb.git <https://sourceware.org/git/?p=binutils-gdb.git >;a=commitdiff;h=159afbb7617d2c8a9cd3f3350374711f37f60442
>
> Co-authored-by: Tatsuyuki Ishi <ishitatsuyuki@gmail.com>
> Signed-off-by: Luke Zhuang <luke.zhuang@linux.alibaba.com>
> ---
> sysdeps/riscv/Makefile | 10 ++
> sysdeps/riscv/dl-lookupcfg.h | 27 +++++
> sysdeps/riscv/dl-machine.h | 54 ++++++++-
> sysdeps/riscv/dl-tlsdesc.S | 221 ++++++++++++++++++++++++++++++++++
> sysdeps/riscv/dl-tlsdesc.h | 48 ++++++++
> sysdeps/riscv/linkmap.h | 1 +
> sysdeps/riscv/preconfigure | 1 +
> sysdeps/riscv/tlsdesc.c | 38 ++++++
> sysdeps/riscv/tlsdesc.sym | 19 +++
> sysdeps/riscv/tst-gnu2-tls2.h | 201 +++++++++++++++++++++++++++++++
> 10 files changed, 619 insertions(+), 1 deletion(-)
> 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
> create mode 100644 sysdeps/riscv/tst-gnu2-tls2.h
>
> diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
> index c08753ae8a..fc16081cde 100644
> --- a/sysdeps/riscv/Makefile
> +++ b/sysdeps/riscv/Makefile
> @@ -4,6 +4,16 @@ endif
>
> ifeq ($(subdir),elf)
> gen-as-const-headers += dl-link.sym
> +sysdep-dl-routines += \
> + dl-tlsdesc \
> + tlsdesc \
> + # routines
> +endif
> +
> +ifeq ($(subdir),csu)
> +gen-as-const-headers += \
> + tlsdesc.sym \
> + # gen-as-const-headers
> endif
>
> # RISC-V's assembler also needs to know about PIC as it changes the definition
> diff --git a/sysdeps/riscv/dl-lookupcfg.h b/sysdeps/riscv/dl-lookupcfg.h
> new file mode 100644
> index 0000000000..414c8c8f8e
> --- /dev/null
> +++ b/sysdeps/riscv/dl-lookupcfg.h
> @@ -0,0 +1,27 @@
> +/* Configuration of lookup functions.
> + Copyright (C) 2026 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/> <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)
> diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
> index babb52af20..79f0d02cab 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>
> @@ -51,7 +52,8 @@
> || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_TPREL32) \
> || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPREL64) \
> || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPMOD64) \
> - || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_TPREL64))) \
> + || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_TPREL64) \
> + || ((type) == R_RISCV_TLSDESC))) \
> | (ELF_RTYPE_CLASS_COPY * ((type) == R_RISCV_COPY)))
>
> /* Return nonzero iff ELF header is compatible with the running host. */
> @@ -220,6 +222,37 @@ elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
> }
> break;
>
> + case R_RISCV_TLSDESC:
> + {
> + struct tlsdesc volatile *td =
> + (struct tlsdesc volatile *)reloc_addr;
> + if (!sym)
> + {
> + td->arg = (void *) reloc->r_addend;
> + td->entry = _dl_tlsdesc_undefweak;
> + }
> + else
> + {
> +# ifndef SHARED
> + CHECK_STATIC_TLS (map, sym_map);
> +# else
> + if (!TRY_STATIC_TLS (map, sym_map))
> + {
> + td->arg = _dl_make_tlsdesc_dynamic
> + (sym_map, sym->st_value + reloc->r_addend);
> + td->entry = _dl_tlsdesc_dynamic;
> + }
> + else
> +# endif
> + {
> + td->arg
> + = (void *) (TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend);
> + td->entry = _dl_tlsdesc_return;
> + }
> + }
> + break;
> + }
> +
> case R_RISCV_COPY:
> {
> if (__glibc_unlikely (sym == NULL))
> @@ -290,6 +323,25 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
> else
> *reloc_addr = map->l_mach.plt;
> }
> + else if (__glibc_likely (r_type == R_RISCV_TLSDESC))
> + {
> + const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info);
> + const ElfW (Sym) *symtab = (const void *)D_PTR (map, l_info[DT_SYMTAB]);
> + const ElfW (Sym) *sym = &symtab[symndx];
> + const struct r_found_version *version = NULL;
> +
> + if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
> + {
> + const ElfW (Half) *vernum =
> + (const void *)D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
> + version = &map->l_versions[vernum[symndx] & 0x7fff];
> + }
> +
> + /* Always initialize TLS descriptors completely, because lazy
> + initialization requires synchronization at every TLS access. */
> + elf_machine_rela (map, scope, reloc, sym, version, reloc_addr,
> + skip_ifunc);
> + }
> else if (__glibc_unlikely (r_type == R_RISCV_IRELATIVE))
> {
> ElfW(Addr) value = map->l_addr + reloc->r_addend;
> diff --git a/sysdeps/riscv/dl-tlsdesc.S b/sysdeps/riscv/dl-tlsdesc.S
> new file mode 100644
> index 0000000000..b11bd25e62
> --- /dev/null
> +++ b/sysdeps/riscv/dl-tlsdesc.S
> @@ -0,0 +1,221 @@
> +/* Thread-local storage handling in the ELF dynamic linker.
> + RISC-V version.
> + Copyright (C) 2026 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/> <https://www.gnu.org/licenses/> >. */
> +
> +#include <sysdep.h>
> +#include <tls.h>
> +#include <tlsdesc.h>
> +
> +/* The fast path does not call function and does not need to align sp, but
> + to simplify handling when going into the slow path, keep sp aligned all
> + the time.
> + */
> +#define FRAME_SIZE_FAST (-((-3 * SZREG) & ALMASK))
> +
> +/* The slow path save slot layout, from lower address to higher address, is:
> + 1. 12 GP registers
> + 2. 20 FP registers
> +
> + Besides a0 and t0, the vector registers are also caller-saved per the psABI
> + (riscv-non-isa/riscv-elf-psabi-doc#496) so they are not preserved across
> + the TLSDESC ifunc.
> + */
> +#if defined(__riscv_float_abi_soft)
> +# define FRAME_SIZE_SLOW (-((-12 * SZREG) & ALMASK))
> +#else
> +# define FRAME_SIZE_SLOW (-((-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, TLSDESC_ARG(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, TLSDESC_ARG(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, -FRAME_SIZE_FAST
> + 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, FRAME_SIZE_FAST
> + 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_SLOW
> + 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
> +
> + call HIDDEN_JUMPTARGET(__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_SLOW
> + j .Lret
> +END (_dl_tlsdesc_dynamic)
> + .hidden HIDDEN_JUMPTARGET(__tls_get_addr)
> +#endif
> diff --git a/sysdeps/riscv/dl-tlsdesc.h b/sysdeps/riscv/dl-tlsdesc.h
> new file mode 100644
> index 0000000000..366b454183
> --- /dev/null
> +++ b/sysdeps/riscv/dl-tlsdesc.h
> @@ -0,0 +1,48 @@
> +/* Thread-local storage descriptor handling in the ELF dynamic linker.
> + RISC-V version.
> + Copyright (C) 2026 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/> <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
> +{
> + unsigned long (*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 _dl_tlsdesc_return (struct tlsdesc *) attribute_hidden;
> +extern unsigned long _dl_tlsdesc_undefweak (struct tlsdesc *) attribute_hidden;
> +
> +# ifdef SHARED
> +extern void *_dl_make_tlsdesc_dynamic (struct link_map *, size_t);
> +extern unsigned long _dl_tlsdesc_dynamic (struct tlsdesc *) attribute_hidden;
> +# endif
> +
> +#endif /* _DL_TLSDESC_H */
> 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/preconfigure b/sysdeps/riscv/preconfigure
> index 57fe6822cf..7c30cf045c 100755
> --- a/sysdeps/riscv/preconfigure
> +++ b/sysdeps/riscv/preconfigure
> @@ -74,6 +74,7 @@ riscv*)
>
> base_machine=riscv
> machine=riscv/rv$xlen/$float_machine
> + mtls_descriptor=desc
>
> printf "%s\n" "#define RISCV_ABI_XLEN $xlen" >>confdefs.h
>
> diff --git a/sysdeps/riscv/tlsdesc.c b/sysdeps/riscv/tlsdesc.c
> new file mode 100644
> index 0000000000..f2cef3db77
> --- /dev/null
> +++ b/sysdeps/riscv/tlsdesc.c
> @@ -0,0 +1,38 @@
> +/* Manage TLS descriptors. RISC-V version.
> + Copyright (C) 2026 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/> <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
> diff --git a/sysdeps/riscv/tst-gnu2-tls2.h b/sysdeps/riscv/tst-gnu2-tls2.h
> new file mode 100644
> index 0000000000..ef5b345ef2
> --- /dev/null
> +++ b/sysdeps/riscv/tst-gnu2-tls2.h
> @@ -0,0 +1,201 @@
> +/* Test TLSDESC relocation. RISC-V version.
> + Copyright (C) 2026 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/> <https://www.gnu.org/licenses/> >. */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> +
> +/* The TLSDESC calling convention requires _dl_tlsdesc_dynamic to preserve
> + every register except a0 (the argument/return value) and t0 (the return
> + address). On the slow path it calls __tls_get_addr, an ordinary function
> + that may clobber all caller-saved registers, so the resolver must save and
> + restore the caller-saved general-purpose registers a1-a7 and t1-t6, and
> + the 20 caller-saved floating-point registers (ft0-ft7, fa0-fa7, ft8-ft11),
> + around that call. Load sentinel values into them before the TLS access and
> + verify that they are unchanged afterwards.
> +
> + Vector registers and vector CSRs are intentionally *not* checked: per the
> + RISC-V psABI they are clobbered by the TLSDESC call (see
> + riscv-non-isa/riscv-elf-psabi-doc#496), so the compiler, not the dynamic
> + linker, is responsible for preserving them across the access. */
> +
> +#define INIT_TLSDESC_CALL() \
> + register unsigned long tlsdesc_gp_a1 asm ("a1") = 11; \
> + register unsigned long tlsdesc_gp_a2 asm ("a2") = 12; \
> + register unsigned long tlsdesc_gp_a3 asm ("a3") = 13; \
> + register unsigned long tlsdesc_gp_a4 asm ("a4") = 14; \
> + register unsigned long tlsdesc_gp_a5 asm ("a5") = 15; \
> + register unsigned long tlsdesc_gp_a6 asm ("a6") = 16; \
> + register unsigned long tlsdesc_gp_a7 asm ("a7") = 17; \
> + register unsigned long tlsdesc_gp_t1 asm ("t1") = 21; \
> + register unsigned long tlsdesc_gp_t2 asm ("t2") = 22; \
> + register unsigned long tlsdesc_gp_t3 asm ("t3") = 23; \
> + register unsigned long tlsdesc_gp_t4 asm ("t4") = 24; \
> + register unsigned long tlsdesc_gp_t5 asm ("t5") = 25; \
> + register unsigned long tlsdesc_gp_t6 asm ("t6") = 26;
> +
> +#define LOAD_REGISTER_GP() \
> + asm volatile ("" \
> + : "+r" (tlsdesc_gp_a1), "+r" (tlsdesc_gp_a2), \
> + "+r" (tlsdesc_gp_a3), "+r" (tlsdesc_gp_a4), \
> + "+r" (tlsdesc_gp_a5), "+r" (tlsdesc_gp_a6), \
> + "+r" (tlsdesc_gp_a7), "+r" (tlsdesc_gp_t1), \
> + "+r" (tlsdesc_gp_t2), "+r" (tlsdesc_gp_t3), \
> + "+r" (tlsdesc_gp_t4), "+r" (tlsdesc_gp_t5), \
> + "+r" (tlsdesc_gp_t6) \
> + : \
> + : "memory")
> +
> +#define SAVE_REGISTER_GP() \
> + LOAD_REGISTER_GP (); \
> + if (tlsdesc_gp_a1 != 11 || tlsdesc_gp_a2 != 12 \
> + || tlsdesc_gp_a3 != 13 || tlsdesc_gp_a4 != 14 \
> + || tlsdesc_gp_a5 != 15 || tlsdesc_gp_a6 != 16 \
> + || tlsdesc_gp_a7 != 17 || tlsdesc_gp_t1 != 21 \
> + || tlsdesc_gp_t2 != 22 || tlsdesc_gp_t3 != 23 \
> + || tlsdesc_gp_t4 != 24 || tlsdesc_gp_t5 != 25 \
> + || tlsdesc_gp_t6 != 26) \
> + { \
> + printf ("GP registers compare failed!\n"); \
> + abort (); \
> + }
> +
> +#define CLOBBER_REGISTER_GP() \
> + asm volatile ("li a1, 101" ::: "a1"); \
> + asm volatile ("li a2, 102" ::: "a2"); \
> + asm volatile ("li a3, 103" ::: "a3"); \
> + asm volatile ("li a4, 104" ::: "a4"); \
> + asm volatile ("li a5, 105" ::: "a5"); \
> + asm volatile ("li a6, 106" ::: "a6"); \
> + asm volatile ("li a7, 107" ::: "a7"); \
> + asm volatile ("li t1, 111" ::: "t1"); \
> + asm volatile ("li t2, 112" ::: "t2"); \
> + asm volatile ("li t3, 113" ::: "t3"); \
> + asm volatile ("li t4, 114" ::: "t4"); \
> + asm volatile ("li t5, 115" ::: "t5"); \
> + asm volatile ("li t6, 116" ::: "t6")
> +
> +#ifndef __riscv_float_abi_soft
> +
> +# define LOAD_REGISTER_FLOAT() \
> + double src_float[20]; \
> + for (int i = 0; i < 20; i++) \
> + src_float[i] = i + 1; \
> + asm volatile ("fld ft0, %0" :: "m" (src_float[0]) : "ft0"); \
> + asm volatile ("fld ft1, %0" :: "m" (src_float[1]) : "ft1"); \
> + asm volatile ("fld ft2, %0" :: "m" (src_float[2]) : "ft2"); \
> + asm volatile ("fld ft3, %0" :: "m" (src_float[3]) : "ft3"); \
> + asm volatile ("fld ft4, %0" :: "m" (src_float[4]) : "ft4"); \
> + asm volatile ("fld ft5, %0" :: "m" (src_float[5]) : "ft5"); \
> + asm volatile ("fld ft6, %0" :: "m" (src_float[6]) : "ft6"); \
> + asm volatile ("fld ft7, %0" :: "m" (src_float[7]) : "ft7"); \
> + asm volatile ("fld fa0, %0" :: "m" (src_float[8]) : "fa0"); \
> + asm volatile ("fld fa1, %0" :: "m" (src_float[9]) : "fa1"); \
> + asm volatile ("fld fa2, %0" :: "m" (src_float[10]) : "fa2"); \
> + asm volatile ("fld fa3, %0" :: "m" (src_float[11]) : "fa3"); \
> + asm volatile ("fld fa4, %0" :: "m" (src_float[12]) : "fa4"); \
> + asm volatile ("fld fa5, %0" :: "m" (src_float[13]) : "fa5"); \
> + asm volatile ("fld fa6, %0" :: "m" (src_float[14]) : "fa6"); \
> + asm volatile ("fld fa7, %0" :: "m" (src_float[15]) : "fa7"); \
> + asm volatile ("fld ft8, %0" :: "m" (src_float[16]) : "ft8"); \
> + asm volatile ("fld ft9, %0" :: "m" (src_float[17]) : "ft9"); \
> + asm volatile ("fld ft10, %0" :: "m" (src_float[18]) : "ft10"); \
> + asm volatile ("fld ft11, %0" :: "m" (src_float[19]) : "ft11");
> +
> +# define SAVE_REGISTER_FLOAT() \
> + double restore_float[20]; \
> + asm volatile ("fsd ft0, %0" : "=m" (restore_float[0])); \
> + asm volatile ("fsd ft1, %0" : "=m" (restore_float[1])); \
> + asm volatile ("fsd ft2, %0" : "=m" (restore_float[2])); \
> + asm volatile ("fsd ft3, %0" : "=m" (restore_float[3])); \
> + asm volatile ("fsd ft4, %0" : "=m" (restore_float[4])); \
> + asm volatile ("fsd ft5, %0" : "=m" (restore_float[5])); \
> + asm volatile ("fsd ft6, %0" : "=m" (restore_float[6])); \
> + asm volatile ("fsd ft7, %0" : "=m" (restore_float[7])); \
> + asm volatile ("fsd fa0, %0" : "=m" (restore_float[8])); \
> + asm volatile ("fsd fa1, %0" : "=m" (restore_float[9])); \
> + asm volatile ("fsd fa2, %0" : "=m" (restore_float[10])); \
> + asm volatile ("fsd fa3, %0" : "=m" (restore_float[11])); \
> + asm volatile ("fsd fa4, %0" : "=m" (restore_float[12])); \
> + asm volatile ("fsd fa5, %0" : "=m" (restore_float[13])); \
> + asm volatile ("fsd fa6, %0" : "=m" (restore_float[14])); \
> + asm volatile ("fsd fa7, %0" : "=m" (restore_float[15])); \
> + asm volatile ("fsd ft8, %0" : "=m" (restore_float[16])); \
> + asm volatile ("fsd ft9, %0" : "=m" (restore_float[17])); \
> + asm volatile ("fsd ft10, %0" : "=m" (restore_float[18])); \
> + asm volatile ("fsd ft11, %0" : "=m" (restore_float[19])); \
> + if (memcmp (src_float, restore_float, sizeof (src_float)) != 0) \
> + { \
> + printf ("Float registers compare failed!\n"); \
> + abort (); \
> + }
> +
> +/* Clobber the caller-saved floating-point registers inside malloc, which is
> + reached through __tls_get_addr on the TLSDESC slow path. If the resolver
> + fails to save and restore them, the sentinels planted by
> + BEFORE_TLSDESC_CALL will be corrupted and AFTER_TLSDESC_CALL will abort. */
> +# define PREPARE_MALLOC() \
> +{ \
> + static const double clobber_float[20] = \
> + { 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, \
> + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120 }; \
> + CLOBBER_REGISTER_GP (); \
> + asm volatile ("fld ft0, %0" :: "m" (clobber_float[0]) : "ft0"); \
> + asm volatile ("fld ft1, %0" :: "m" (clobber_float[1]) : "ft1"); \
> + asm volatile ("fld ft2, %0" :: "m" (clobber_float[2]) : "ft2"); \
> + asm volatile ("fld ft3, %0" :: "m" (clobber_float[3]) : "ft3"); \
> + asm volatile ("fld ft4, %0" :: "m" (clobber_float[4]) : "ft4"); \
> + asm volatile ("fld ft5, %0" :: "m" (clobber_float[5]) : "ft5"); \
> + asm volatile ("fld ft6, %0" :: "m" (clobber_float[6]) : "ft6"); \
> + asm volatile ("fld ft7, %0" :: "m" (clobber_float[7]) : "ft7"); \
> + asm volatile ("fld fa0, %0" :: "m" (clobber_float[8]) : "fa0"); \
> + asm volatile ("fld fa1, %0" :: "m" (clobber_float[9]) : "fa1"); \
> + asm volatile ("fld fa2, %0" :: "m" (clobber_float[10]) : "fa2"); \
> + asm volatile ("fld fa3, %0" :: "m" (clobber_float[11]) : "fa3"); \
> + asm volatile ("fld fa4, %0" :: "m" (clobber_float[12]) : "fa4"); \
> + asm volatile ("fld fa5, %0" :: "m" (clobber_float[13]) : "fa5"); \
> + asm volatile ("fld fa6, %0" :: "m" (clobber_float[14]) : "fa6"); \
> + asm volatile ("fld fa7, %0" :: "m" (clobber_float[15]) : "fa7"); \
> + asm volatile ("fld ft8, %0" :: "m" (clobber_float[16]) : "ft8"); \
> + asm volatile ("fld ft9, %0" :: "m" (clobber_float[17]) : "ft9"); \
> + asm volatile ("fld ft10, %0" :: "m" (clobber_float[18]) : "ft10"); \
> + asm volatile ("fld ft11, %0" :: "m" (clobber_float[19]) : "ft11"); \
> +}
> +
> +# define BEFORE_TLSDESC_CALL() \
> + LOAD_REGISTER_GP (); \
> + LOAD_REGISTER_FLOAT ()
> +
> +# define AFTER_TLSDESC_CALL() \
> + SAVE_REGISTER_GP (); \
> + SAVE_REGISTER_FLOAT ()
> +
> +#else /* __riscv_float_abi_soft */
> +
> +# define PREPARE_MALLOC() \
> + CLOBBER_REGISTER_GP ()
> +
> +# define BEFORE_TLSDESC_CALL() \
> + LOAD_REGISTER_GP ()
> +
> +# define AFTER_TLSDESC_CALL() \
> + SAVE_REGISTER_GP ()
> +
> +#endif /* __riscv_float_abi_soft */
> +
> +#include_next <tst-gnu2-tls2.h>
>
--
PD Dr. Andreas K. Hüttel
dilfridge@gentoo.org
Gentoo Linux developer
(council, comrel, toolchain, base-system, perl, libreoffice)
https://wiki.gentoo.org/wiki/User:Dilfridge <https://wiki.gentoo.org/wiki/User:Dilfridge >
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20260715/a57d91e7/attachment-0001.htm>
More information about the Libc-alpha
mailing list