[PATCH v13 2/6] Add generic 'extra TLS'

Mathieu Desnoyers mathieu.desnoyers@efficios.com
Tue Nov 19 20:02:21 GMT 2024


On 2024-11-19 13:58, Michael Jeanson wrote:
> Add the logic to append an 'extra TLS' block in the TLS block allocator
> with a generic stub implementation.

We could add this to the commit message for clarity:

The additions to csu/libc-tls.c cover initialization code for TLS in
statically linked applications.

The additions to elf/dl-tls.c cover TLS handling in the ELF dynamic
loader.

> 
> Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
> ---
>   csu/libc-tls.c                 | 59 ++++++++++++++++++++++++++++------
>   elf/dl-tls.c                   | 59 ++++++++++++++++++++++++++++++++++
>   sysdeps/generic/dl-extra_tls.h | 45 ++++++++++++++++++++++++++
>   3 files changed, 154 insertions(+), 9 deletions(-)
>   create mode 100644 sysdeps/generic/dl-extra_tls.h
> 
> diff --git a/csu/libc-tls.c b/csu/libc-tls.c
> index b7682bdf43..03a000c499 100644
> --- a/csu/libc-tls.c
> +++ b/csu/libc-tls.c
> @@ -26,6 +26,7 @@
>   #include <array_length.h>
>   #include <pthreadP.h>
>   #include <dl-call_tls_init_tp.h>
> +#include <dl-extra_tls.h>
>   
>   #ifdef SHARED
>    #error makefile bug, this file is for static only
> @@ -110,6 +111,7 @@ __libc_setup_tls (void)
>     size_t filesz = 0;
>     void *initimage = NULL;
>     size_t align = 0;
> +  size_t tls_blocks_size = 0;
>     size_t max_align = TCB_ALIGNMENT;
>     size_t tcb_offset;
>     const ElfW(Phdr) *phdr;
> @@ -135,22 +137,67 @@ __libc_setup_tls (void)
>     /* Calculate the size of the static TLS surplus, with 0 auditors.  */
>     _dl_tls_static_surplus_init (0);
>   
> +  /* Extra TLS block for internal usage to append at the end of the TLS blocks
> +     (in allocation order). On Linux systems this is where the rseq area will
> +     be allocated. On other systems it is currently unused and both values will
> +     be '0'.  */
> +  size_t extra_tls_size = _dl_extra_tls_get_size();
> +  size_t extra_tls_align = _dl_extra_tls_get_align();
> +
> +  /* Increase the maximum alignment with the extra TLS alignment requirements
> +     if necessary.  */
> +  max_align = MAX (max_align, extra_tls_align);
> +

ok

>     /* We have to set up the TCB block which also (possibly) contains
>        'errno'.  Therefore we avoid 'malloc' which might touch 'errno'.
>        Instead we use 'sbrk' which would only uses 'errno' if it fails.
>        In this case we are right away out of memory and the user gets
>        what she/he deserves.  */
>   #if TLS_TCB_AT_TP
> +  /* Before the the thread pointer, add the aligned tls block size and then
> +     align the extra tls block on top.  */
> +  tls_blocks_size = roundup (roundup (memsz, align ?: 1) + extra_tls_size,
> +		  extra_tls_align);
> +
> + /* Record the extra TLS block offset from the thread pointer.
> +
> +    With TLS_TCB_AT_TP the TLS blocks are allocated before the thread pointer
> +    in reverse order.  Our block is added last which results in it being the
> +    first in the static TLS block, thus record the most negative offset.
> +
> +    The alignment requirements of the pointer resulting from this offset and
> +    the thread pointer are enforced by 'max_align' which is used to align the
> +    tcb_offset.  */
> +  _dl_extra_tls_set_offset(-tls_blocks_size);
> +

ok

>     /* Align the TCB offset to the maximum alignment, as
>        _dl_allocate_tls_storage (in elf/dl-tls.c) does using __libc_memalign
>        and dl_tls_static_align.  */
> -  tcb_offset = roundup (memsz + GLRO(dl_tls_static_surplus), max_align);
> +  tcb_offset = roundup (tls_blocks_size + GLRO(dl_tls_static_surplus), max_align);
>     tlsblock = _dl_early_allocate (tcb_offset + TLS_INIT_TCB_SIZE + max_align);
>     if (tlsblock == NULL)
>       _startup_fatal_tls_error ();
>   #elif TLS_DTV_AT_TP
> +  /* Align memsz on top of the initial tcb.  */
>     tcb_offset = roundup (TLS_INIT_TCB_SIZE, align ?: 1);
> -  tlsblock = _dl_early_allocate (tcb_offset + memsz + max_align
> +
> +  /* After the thread pointer, add the initial tcb plus the tls block size and
> +     then align the extra TLS block on top.  */
> +  tls_blocks_size = roundup (tcb_offset + memsz + extra_tls_size, extra_tls_align);
> +
> + /* Record the extra TLS block offset from the thread pointer.
> +
> +    With TLS_DTV_AT_TP the TLS blocks are allocated after the thread pointer in
> +    order. Our block is added last which results in it being the last in the
> +    static TLS block, thus record the offset as the size of the static TLS
> +    block minus the size of our block. The resulting offset will be positive.
> +
> +    The alignment requirements of the pointer resulting from this offset and
> +    the thread pointer are enforced by 'max_align' which is used to align the
> +    tcb_offset.  */
> +  _dl_extra_tls_set_offset(tls_blocks_size - extra_tls_size);
> +
> +  tlsblock = _dl_early_allocate (tls_blocks_size + max_align
>   				 + TLS_PRE_TCB_SIZE
>   				 + GLRO(dl_tls_static_surplus));

ok

>     if (tlsblock == NULL)
> @@ -209,11 +256,5 @@ __libc_setup_tls (void)
>     /* static_slotinfo.slotinfo[1].gen = 0; -- Already zero.  */
>     static_slotinfo.slotinfo[1].map = main_map;
>   
> -  memsz = roundup (memsz, align ?: 1);
> -
> -#if TLS_DTV_AT_TP
> -  memsz += tcb_offset;
> -#endif
> -
> -  init_static_tls (memsz, MAX (TCB_ALIGNMENT, max_align));
> +  init_static_tls (tls_blocks_size, MAX (TCB_ALIGNMENT, max_align));

ok

>   }
> diff --git a/elf/dl-tls.c b/elf/dl-tls.c
> index 3d529b722c..57e72be4f2 100644
> --- a/elf/dl-tls.c
> +++ b/elf/dl-tls.c
> @@ -36,6 +36,8 @@
>   #define TUNABLE_NAMESPACE rtld
>   #include <dl-tunables.h>
>   
> +#include <dl-extra_tls.h>
> +
>   /* Surplus static TLS, GLRO(dl_tls_static_surplus), is used for
>   
>      - IE TLS in libc.so for all dlmopen namespaces except in the initial
> @@ -323,6 +325,33 @@ _dl_determine_tlsoffset (void)
>         slotinfo[cnt].map->l_tls_offset = off;
>       }
>   
> +  /* Insert the extra TLS block after the last TLS block.  */
> +
> +  /* Extra TLS block for internal usage to append at the end of the TLS blocks
> +     (in allocation order). On Linux systems this is where the rseq area will
> +     be allocated. On other systems it is currently unused and both values will
> +     be '0'.  */
> +  size_t extra_tls_size = _dl_extra_tls_get_size();
> +  size_t extra_tls_align = _dl_extra_tls_get_align();
> +
> +  /* Align and add the extra TLS block to the global offset.  */
> +  offset = roundup (offset, extra_tls_align) + extra_tls_size;
> +
> +  /* Increase the maximum alignment with the extra TLS alignment requirements
> +     if necessary.  */
> +  max_align = MAX (max_align, extra_tls_align);
> +
> + /* Record the extra TLS offset.
> +
> +    With TLS_TCB_AT_TP the TLS blocks are allocated before the thread pointer
> +    in reverse order.  Our block is added last which results in it being the
> +    first in the static TLS block, thus record the most negative offset.
> +
> +    The alignment requirements of the pointer resulting from this offset and
> +    the thread pointer are enforced by 'max_align' which is used to align the
> +    tcb_offset.  */
> +  _dl_extra_tls_set_offset(-offset);

ok

> +
>     GL(dl_tls_static_used) = offset;
>     GLRO (dl_tls_static_size) = (roundup (offset + GLRO(dl_tls_static_surplus),
>   					max_align)
> @@ -368,6 +397,36 @@ _dl_determine_tlsoffset (void)
>         offset = off + slotinfo[cnt].map->l_tls_blocksize - firstbyte;
>       }
>   
> +  /* Insert the extra TLS block after the last TLS block.  */
> +
> +  /* Extra TLS block for internal usage to append at the end of the TLS blocks
> +     (in allocation order). On Linux systems this is where the rseq area will
> +     be allocated. On other systems it is currently unused and both values will
> +     be '0'.  */
> +  size_t extra_tls_size = _dl_extra_tls_get_size();
> +  size_t extra_tls_align = _dl_extra_tls_get_align();
> +
> +  /* Align the global offset to the beginning of the extra TLS block.  */
> +  offset = roundup (offset, extra_tls_align);
> +
> + /* Record the extra TLS offset.
> +
> +    With TLS_DTV_AT_TP the TLS blocks are allocated after the thread pointer in
> +    order. Our block is added last which results in it being the last in the
> +    static TLS block, thus record the offset as the size of the static TLS
> +    block minus the size of our block. The resulting offset will be positive.
> +
> +    The alignment requirements of the pointer resulting from this offset and
> +    the thread pointer are enforced by 'max_align' which is used to align the
> +    tcb_offset.  */
> +  _dl_extra_tls_set_offset(offset);
> +
> +  /* Add the extra TLS block to the global offset.  */
> +  offset += extra_tls_size;
> +
> +  /* Increase the max_align if necessary.  */
> +  max_align = MAX (max_align, extra_tls_align);
> +

ok

>     GL(dl_tls_static_used) = offset;
>     GLRO (dl_tls_static_size) = roundup (offset + GLRO(dl_tls_static_surplus),
>   				       TCB_ALIGNMENT);
> diff --git a/sysdeps/generic/dl-extra_tls.h b/sysdeps/generic/dl-extra_tls.h
> new file mode 100644
> index 0000000000..c699e88e85
> --- /dev/null
> +++ b/sysdeps/generic/dl-extra_tls.h
> @@ -0,0 +1,45 @@
> +/* extra tls utils for the dynamic linker.  Generic stub version.
> +   Copyright (C) 2024 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_EXTRA_TLS_H
> +#define _DL_EXTRA_TLS_H 1
> +#include <stddef.h>
> +
> +/* In this generic version, the extra TLS block is unused.  */
> +
> +/* Returns the size of the extra TLS block.  */
> +static inline size_t
> +_dl_extra_tls_get_size (void)
> +{
> +	return 0;
> +}
> +
> +/* Returns the alignment requirements of the extra TLS block.  */
> +static inline size_t
> +_dl_extra_tls_get_align (void)
> +{
> +	return 0;
> +}
> +
> +/* Record the offset of the extra TLS block from the thread pointer.  */
> +static inline void
> +_dl_extra_tls_set_offset (ptrdiff_t tls_offset __attribute__ ((unused)))
> +{
> +}
> +
> +#endif

And this is the generic stub. OK.

Please add my:

Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

Thanks!

Mathieu

-- 
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.com



More information about the Libc-alpha mailing list