[PATCH v5 2/2] rtld: Avoid using up static TLS surplus for optimizations [BZ #25051]

Carlos O'Donell carlos@redhat.com
Mon Jul 6 13:19:34 GMT 2020


On 6/22/20 12:21 PM, Szabolcs Nagy wrote:
> On some targets static TLS surplus area can be used opportunistically
> for dynamically loaded modules such that the TLS access then becomes
> faster (TLSDESC and powerpc TLS optimization). However we don't want
> all surplus TLS to be used for this optimization because dynamically
> loaded modules with initial-exec model TLS can only use surplus TLS.
> 
> The new contract for surplus static TLS use is:
> 
> - libc.so can have up to 192 bytes of IE TLS,
> - other system libraries together can have up to 144 bytes of IE TLS.
> - Some "optional" static TLS is available for opportunistic use.
> 
> The optional TLS is now tunable: rtld.optional_static_tls, so users
> can directly affect the allocated static TLS size. (Note that module
> unloading with dlclose does not reclaim static TLS. After the optional
> TLS runs out, TLS access is no longer optimized to use static TLS.)
> 
> The default setting of rtld.optional_static_tls is 512 so the surplus
> TLS is 3*192 + 4*144 + 512 = 1664 by default, the same as before.
> 
> Fixes BZ #25051.
> 
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.

OK for mater.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

 
> ---
> 
> v5:
> - Split the patch into two: rtld.nns and tls optimization parts.
> - Use rtld tunable namespace.
> - Set tunable min val to 0.
> - New test using dlmopen.
> - Tests now have check for failing IE TLS dlopen.
> - Updated the tunable documentation.
> v4:
> - Rebased and moved this log out of the commit message.
> - Minor commit message wording changes.
> v3:
> - archived at
>   https://sourceware.org/pipermail/libc-alpha/2020-March/111660.html
> - Replace TLS_STATIC_SURPLUS with GLRO(dl_tls_static_surplus) and
>   simplify related logic.
> - In case of static linking, replace GL(dl_tls_static_size) with
>   GLRO(dl_tls_static_surplus) in the code paths before the
>   GL(dl_tls_static_size) value is actually computed.
> - Update comments and the test code.
> - Document the new tunables.
> - Update description, mention static linking.
> v2:
> - Add dl.nns tunable.
> - Add dl.optional_static_tls tunable.
> - New surplus TLS usage contract that works reliably up to dl.nns
>   namespaces.
> ---
>  csu/libc-tls.c             |   3 +
>  elf/Makefile               |  29 +++++++++-
>  elf/dl-reloc.c             |  37 +++++++++---
>  elf/dl-tls.c               |   9 +--
>  elf/dl-tunables.list       |   5 ++
>  elf/dynamic-link.h         |   5 +-
>  elf/tst-tls-ie-dlmopen.c   | 114 +++++++++++++++++++++++++++++++++++++
>  elf/tst-tls-ie-mod.h       |  40 +++++++++++++
>  elf/tst-tls-ie-mod0.c      |   4 ++
>  elf/tst-tls-ie-mod1.c      |   4 ++
>  elf/tst-tls-ie-mod2.c      |   4 ++
>  elf/tst-tls-ie-mod3.c      |   4 ++
>  elf/tst-tls-ie-mod4.c      |   4 ++
>  elf/tst-tls-ie-mod5.c      |   4 ++
>  elf/tst-tls-ie-mod6.c      |   4 ++
>  elf/tst-tls-ie.c           | 113 ++++++++++++++++++++++++++++++++++++
>  manual/tunables.texi       |  17 ++++++
>  sysdeps/generic/ldsodefs.h |   3 +
>  18 files changed, 386 insertions(+), 17 deletions(-)
>  create mode 100644 elf/tst-tls-ie-dlmopen.c
>  create mode 100644 elf/tst-tls-ie-mod.h
>  create mode 100644 elf/tst-tls-ie-mod0.c
>  create mode 100644 elf/tst-tls-ie-mod1.c
>  create mode 100644 elf/tst-tls-ie-mod2.c
>  create mode 100644 elf/tst-tls-ie-mod3.c
>  create mode 100644 elf/tst-tls-ie-mod4.c
>  create mode 100644 elf/tst-tls-ie-mod5.c
>  create mode 100644 elf/tst-tls-ie-mod6.c
>  create mode 100644 elf/tst-tls-ie.c
> 
> diff --git a/csu/libc-tls.c b/csu/libc-tls.c
> index e2603157e8..fb77cd94fa 100644
> --- a/csu/libc-tls.c
> +++ b/csu/libc-tls.c
> @@ -56,6 +56,9 @@ size_t _dl_tls_static_align;
>     loaded modules with IE-model TLS or for TLSDESC optimization.
>     See comments in elf/dl-tls.c where it is initialized.  */
>  size_t _dl_tls_static_surplus;
> +/* Remaining amount of static TLS that may be used for optimizing
> +   dynamic TLS access (e.g. with TLSDESC).  */
> +size_t _dl_tls_static_optional;

OK.

>  
>  /* Generation counter for the dtv.  */
>  size_t _dl_tls_generation;
> diff --git a/elf/Makefile b/elf/Makefile
> index 6fe1df90bb..5fadaec27c 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -204,7 +204,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail \
>  	 tst-dlopenfail-2 \
>  	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
> -	 tst-audit14 tst-audit15 tst-audit16
> +	 tst-audit14 tst-audit15 tst-audit16 \
> +	 tst-tls-ie tst-tls-ie-dlmopen

OK.

>  #	 reldep9
>  tests-internal += loadtest unload unload2 circleload1 \
>  	 neededtest neededtest2 neededtest3 neededtest4 \
> @@ -317,7 +318,11 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
>  		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
>  		tst-dlopenfailmod3 tst-ldconfig-ld-mod \
>  		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee \
> -		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3
> +		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
> +		tst-tls-ie-mod0 tst-tls-ie-mod1 tst-tls-ie-mod2 \
> +		tst-tls-ie-mod3 tst-tls-ie-mod4 tst-tls-ie-mod5 \
> +		tst-tls-ie-mod6

OK.

> +
>  # Most modules build with _ISOMAC defined, but those filtered out
>  # depend on internal headers.
>  modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
> @@ -1748,3 +1753,23 @@ $(objpfx)tst-auxobj: $(objpfx)tst-filterobj-aux.so
>  $(objpfx)tst-auxobj-dlopen: $(libdl)
>  $(objpfx)tst-auxobj.out: $(objpfx)tst-filterobj-filtee.so
>  $(objpfx)tst-auxobj-dlopen.out: $(objpfx)tst-filterobj-filtee.so
> +
> +$(objpfx)tst-tls-ie: $(libdl) $(shared-thread-library)
> +$(objpfx)tst-tls-ie.out: \
> +  $(objpfx)tst-tls-ie-mod0.so \
> +  $(objpfx)tst-tls-ie-mod1.so \
> +  $(objpfx)tst-tls-ie-mod2.so \
> +  $(objpfx)tst-tls-ie-mod3.so \
> +  $(objpfx)tst-tls-ie-mod4.so \
> +  $(objpfx)tst-tls-ie-mod5.so \
> +  $(objpfx)tst-tls-ie-mod6.so

OK.

> +
> +$(objpfx)tst-tls-ie-dlmopen: $(libdl) $(shared-thread-library)
> +$(objpfx)tst-tls-ie-dlmopen.out: \
> +  $(objpfx)tst-tls-ie-mod0.so \
> +  $(objpfx)tst-tls-ie-mod1.so \
> +  $(objpfx)tst-tls-ie-mod2.so \
> +  $(objpfx)tst-tls-ie-mod3.so \
> +  $(objpfx)tst-tls-ie-mod4.so \
> +  $(objpfx)tst-tls-ie-mod5.so \
> +  $(objpfx)tst-tls-ie-mod6.so

OK.

> diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
> index ffcc84d396..6d32e49467 100644
> --- a/elf/dl-reloc.c
> +++ b/elf/dl-reloc.c
> @@ -39,13 +39,16 @@
>  /* We are trying to perform a static TLS relocation in MAP, but it was
>     dynamically loaded.  This can only work if there is enough surplus in
>     the static TLS area already allocated for each running thread.  If this
> -   object's TLS segment is too big to fit, we fail.  If it fits,
> -   we set MAP->l_tls_offset and return.
> -   This function intentionally does not return any value but signals error
> -   directly, as static TLS should be rare and code handling it should
> -   not be inlined as much as possible.  */
> +   object's TLS segment is too big to fit, we fail with -1.  If it fits,
> +   we set MAP->l_tls_offset and return 0.
> +   A portion of the surplus static TLS can be optionally used to optimize
> +   dynamic TLS access (with TLSDESC or powerpc TLS optimizations).
> +   If OPTIONAL is true then TLS is allocated for such optimization and
> +   the caller must have a fallback in case the optional portion of surplus
> +   TLS runs out.  If OPTIONAL is false then the entire surplus TLS area is
> +   considered and the allocation only fails if that runs out.  */
>  int
> -_dl_try_allocate_static_tls (struct link_map *map)
> +_dl_try_allocate_static_tls (struct link_map *map, bool optional)

OK.

>  {
>    /* If we've already used the variable with dynamic access, or if the
>       alignment requirements are too high, fail.  */
> @@ -68,8 +71,14 @@ _dl_try_allocate_static_tls (struct link_map *map)
>  
>    size_t n = (freebytes - blsize) / map->l_tls_align;
>  
> -  size_t offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align
> -					    - map->l_tls_firstbyte_offset);
> +  /* Account optional static TLS surplus usage.  */
> +  size_t use = freebytes - n * map->l_tls_align - map->l_tls_firstbyte_offset;
> +  if (optional && use > GL(dl_tls_static_optional))
> +    goto fail;
> +  else if (optional)
> +    GL(dl_tls_static_optional) -= use;
> +
> +  size_t offset = GL(dl_tls_static_used) + use;
>  
>    map->l_tls_offset = GL(dl_tls_static_used) = offset;
>  #elif TLS_DTV_AT_TP
> @@ -83,6 +92,13 @@ _dl_try_allocate_static_tls (struct link_map *map)
>    if (used > GL(dl_tls_static_size))
>      goto fail;
>  
> +  /* Account optional static TLS surplus usage.  */
> +  size_t use = used - GL(dl_tls_static_used);
> +  if (optional && use > GL(dl_tls_static_optional))
> +    goto fail;
> +  else if (optional)
> +    GL(dl_tls_static_optional) -= use;
> +
>    map->l_tls_offset = offset;
>    map->l_tls_firstbyte_offset = GL(dl_tls_static_used);
>    GL(dl_tls_static_used) = used;
> @@ -110,12 +126,15 @@ _dl_try_allocate_static_tls (struct link_map *map)
>    return 0;
>  }
>  
> +/* This function intentionally does not return any value but signals error
> +   directly, as static TLS should be rare and code handling it should
> +   not be inlined as much as possible.  */
>  void
>  __attribute_noinline__
>  _dl_allocate_static_tls (struct link_map *map)
>  {
>    if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
> -      || _dl_try_allocate_static_tls (map))
> +      || _dl_try_allocate_static_tls (map, false))
>      {
>        _dl_signal_error (0, map->l_name, NULL, N_("\
>  cannot allocate memory in static TLS block"));
> diff --git a/elf/dl-tls.c b/elf/dl-tls.c
> index 2201a1cc1d..af5db12d08 100644
> --- a/elf/dl-tls.c
> +++ b/elf/dl-tls.c
> @@ -57,25 +57,26 @@
>     This should be large enough to cover runtime libraries of the
>     compiler such as libgomp and libraries in libc other than libc.so.  */
>  #define OTHER_IE_TLS 144
> -/* Size of additional surplus TLS, placeholder for TLS optimizations.  */
> -#define OPT_SURPLUS_TLS 512
>  
>  void
>  _dl_tls_static_surplus_init (void)
>  {
> -  size_t nns;
> +  size_t nns, opt_tls;
>  
>  #if HAVE_TUNABLES
>    nns = TUNABLE_GET (nns, size_t, NULL);
> +  opt_tls = TUNABLE_GET (optional_static_tls, size_t, NULL);
>  #else
>    /* Default values of the tunables.  */
>    nns = 4;
> +  opt_tls = 512;
>  #endif
>    if (nns > DL_NNS)
>      nns = DL_NNS;
> +  GL(dl_tls_static_optional) = opt_tls;
>    GLRO(dl_tls_static_surplus) = ((nns - 1) * LIBC_IE_TLS
>  				 + nns * OTHER_IE_TLS
> -				 + OPT_SURPLUS_TLS);
> +				 + opt_tls);

OK.

>  }
>  
>  /* Out-of-memory handler.  */
> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
> index b07742d7b3..35634ef24d 100644
> --- a/elf/dl-tunables.list
> +++ b/elf/dl-tunables.list
> @@ -134,5 +134,10 @@ glibc {
>        maxval: 16
>        default: 4
>      }
> +    optional_static_tls {
> +      type: SIZE_T
> +      minval: 0
> +      default: 512

OK.

> +    }
>    }
>  }
> diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
> index bb7a66f4cd..6727233e1a 100644
> --- a/elf/dynamic-link.h
> +++ b/elf/dynamic-link.h
> @@ -40,9 +40,10 @@
>      (__builtin_expect ((sym_map)->l_tls_offset				\
>  		       != FORCED_DYNAMIC_TLS_OFFSET, 1)			\
>       && (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET, 1)	\
> -	 || _dl_try_allocate_static_tls (sym_map) == 0))
> +	 || _dl_try_allocate_static_tls (sym_map, true) == 0))
>  
> -int _dl_try_allocate_static_tls (struct link_map *map) attribute_hidden;
> +int _dl_try_allocate_static_tls (struct link_map *map, bool optional)
> +  attribute_hidden;
>  
>  #include <elf.h>
>  
> diff --git a/elf/tst-tls-ie-dlmopen.c b/elf/tst-tls-ie-dlmopen.c
> new file mode 100644
> index 0000000000..0be47c7237
> --- /dev/null
> +++ b/elf/tst-tls-ie-dlmopen.c
> @@ -0,0 +1,114 @@
> +/* Test dlopen of modules with initial-exec TLS after dlmopen.
> +   Copyright (C) 2016-2020 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/>.  */
> +
> +/* This test tries to check that surplus static TLS is not used up for
> +   dynamic TLS optimizations and 4*144 = 576 bytes of static TLS is
> +   still available for dlopening modules with initial-exec TLS after 3
> +   new dlmopen namespaces are created.  It depends on rtld.nns=4 and
> +   rtld.optional_static_tls=512 tunable settings.  */

OK. Good comment.

> +
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +static int do_test (void);
> +#include <support/xthread.h>
> +#include <support/xdlfcn.h>
> +#include <support/test-driver.c>
> +
> +/* Have some big TLS in the main exe: should not use surplus TLS.  */
> +__thread char maintls[1000];
> +
> +static pthread_barrier_t barrier;
> +
> +/* Forces multi-threaded behaviour.  */
> +static void *
> +blocked_thread_func (void *closure)
> +{
> +  xpthread_barrier_wait (&barrier);
> +  /* TLS load and access tests run here in the main thread.  */
> +  xpthread_barrier_wait (&barrier);
> +  return NULL;
> +}
> +
> +static void *
> +load_and_access (Lmid_t lmid, const char *mod, const char *func)
> +{
> +  /* Load module with TLS.  */
> +  void *p = xdlmopen (lmid, mod, RTLD_NOW);
> +  /* Access the TLS variable to ensure it is allocated.  */
> +  void (*f) (void) = (void (*) (void))xdlsym (p, func);
> +  f ();
> +  return p;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  void *mods[5];
> +
> +  {
> +    int ret = pthread_barrier_init (&barrier, NULL, 2);
> +    if (ret != 0)
> +      {
> +        errno = ret;
> +        printf ("error: pthread_barrier_init: %m\n");
> +        exit (1);
> +      }
> +  }
> +
> +  pthread_t blocked_thread = xpthread_create (NULL, blocked_thread_func, NULL);
> +  xpthread_barrier_wait (&barrier);
> +
> +  printf ("maintls[%zu]:\t %p .. %p\n",
> +	   sizeof maintls, maintls, maintls + sizeof maintls);
> +  memset (maintls, 1, sizeof maintls);
> +
> +  /* Load modules with dynamic TLS (use surplus static TLS for libc
> +     in new namespaces and may be for TLS optimizations too).  */
> +  mods[0] = load_and_access (LM_ID_BASE, "tst-tls-ie-mod0.so", "access0");
> +  mods[1] = load_and_access (LM_ID_NEWLM, "tst-tls-ie-mod1.so", "access1");
> +  mods[2] = load_and_access (LM_ID_NEWLM, "tst-tls-ie-mod2.so", "access2");
> +  mods[3] = load_and_access (LM_ID_NEWLM, "tst-tls-ie-mod3.so", "access3");
> +  /* Load modules with initial-exec TLS (can only use surplus static TLS).  */
> +  mods[4] = load_and_access (LM_ID_BASE, "tst-tls-ie-mod6.so", "access6");
> +
> +  /* Here 576 bytes + 3 * libc use of surplus static TLS is in use so less
> +     than 1024 bytes are available (exact number depends on TLS optimizations
> +     and the libc TLS use).  */
> +  printf ("The next dlmopen should fail...\n");
> +  void *p = dlmopen (LM_ID_BASE, "tst-tls-ie-mod4.so", RTLD_NOW);
> +  if (p != NULL)
> +    {
> +      printf ("error: expected dlmopen to fail because there is "
> +	      "not enough surplus static TLS.\n");
> +      exit (1);

OK. You could have used FAIL_EXIT1 to merge both.

> +    }
> +  printf ("...OK failed with: %s.\n", dlerror ());
> +
> +  xpthread_barrier_wait (&barrier);
> +  xpthread_join (blocked_thread);
> +
> +  /* Close the modules.  */
> +  for (int i = 0; i < 5; ++i)
> +    xdlclose (mods[i]);
> +
> +  return 0;
> +}
> diff --git a/elf/tst-tls-ie-mod.h b/elf/tst-tls-ie-mod.h
> new file mode 100644
> index 0000000000..46b362a9b7
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod.h
> @@ -0,0 +1,40 @@
> +/* Module with specified TLS size and model.
> +   Copyright (C) 2020 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/>.  */
> +
> +/* This file is parameterized by macros N, SIZE and MODEL.  */
> +
> +#include <stdio.h>
> +#include <string.h>
> +
> +#define CONCATX(x, y) x ## y
> +#define CONCAT(x, y) CONCATX (x, y)
> +#define STRX(x) #x
> +#define STR(x) STRX (x)
> +
> +#define VAR CONCAT (var, N)
> +
> +__attribute__ ((aligned (8), tls_model (MODEL)))
> +__thread char VAR[SIZE];
> +
> +void
> +CONCAT (access, N) (void)
> +{
> +  printf (STR (VAR) "[%d]:\t %p .. %p " MODEL "\n", SIZE, VAR, VAR + SIZE);
> +  fflush (stdout);
> +  memset (VAR, 1, SIZE);
> +}
> diff --git a/elf/tst-tls-ie-mod0.c b/elf/tst-tls-ie-mod0.c
> new file mode 100644
> index 0000000000..2450686e40
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod0.c
> @@ -0,0 +1,4 @@
> +#define N 0
> +#define SIZE 480
> +#define MODEL "global-dynamic"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod1.c b/elf/tst-tls-ie-mod1.c
> new file mode 100644
> index 0000000000..849ff91e53
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod1.c
> @@ -0,0 +1,4 @@
> +#define N 1
> +#define SIZE 120
> +#define MODEL "global-dynamic"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod2.c b/elf/tst-tls-ie-mod2.c
> new file mode 100644
> index 0000000000..23915ab67b
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod2.c
> @@ -0,0 +1,4 @@
> +#define N 2
> +#define SIZE 24
> +#define MODEL "global-dynamic"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod3.c b/elf/tst-tls-ie-mod3.c
> new file mode 100644
> index 0000000000..5395f844a5
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod3.c
> @@ -0,0 +1,4 @@
> +#define N 3
> +#define SIZE 16
> +#define MODEL "global-dynamic"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod4.c b/elf/tst-tls-ie-mod4.c
> new file mode 100644
> index 0000000000..93ac2eacae
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod4.c
> @@ -0,0 +1,4 @@
> +#define N 4
> +#define SIZE 1024
> +#define MODEL "initial-exec"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod5.c b/elf/tst-tls-ie-mod5.c
> new file mode 100644
> index 0000000000..84b3fd285b
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod5.c
> @@ -0,0 +1,4 @@
> +#define N 5
> +#define SIZE 128
> +#define MODEL "initial-exec"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie-mod6.c b/elf/tst-tls-ie-mod6.c
> new file mode 100644
> index 0000000000..c736bf0684
> --- /dev/null
> +++ b/elf/tst-tls-ie-mod6.c
> @@ -0,0 +1,4 @@
> +#define N 6
> +#define SIZE 576
> +#define MODEL "initial-exec"
> +#include "tst-tls-ie-mod.h"
> diff --git a/elf/tst-tls-ie.c b/elf/tst-tls-ie.c
> new file mode 100644
> index 0000000000..c06454c50c
> --- /dev/null
> +++ b/elf/tst-tls-ie.c
> @@ -0,0 +1,113 @@
> +/* Test dlopen of modules with initial-exec TLS.
> +   Copyright (C) 2016-2020 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/>.  */
> +
> +/* This test tries to check that surplus static TLS is not used up for
> +   dynamic TLS optimizations and 3*192 + 4*144 = 1152 bytes of static
> +   TLS is available for dlopening modules with initial-exec TLS.  It
> +   depends on rtld.nns=4 and rtld.optional_static_tls=512 tunable setting.  */
> +
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +static int do_test (void);
> +#include <support/xthread.h>
> +#include <support/xdlfcn.h>
> +#include <support/test-driver.c>
> +
> +/* Have some big TLS in the main exe: should not use surplus TLS.  */
> +__thread char maintls[1000];
> +
> +static pthread_barrier_t barrier;
> +
> +/* Forces multi-threaded behaviour.  */
> +static void *
> +blocked_thread_func (void *closure)
> +{
> +  xpthread_barrier_wait (&barrier);
> +  /* TLS load and access tests run here in the main thread.  */
> +  xpthread_barrier_wait (&barrier);
> +  return NULL;
> +}
> +
> +static void *
> +load_and_access (const char *mod, const char *func)
> +{
> +  /* Load module with TLS.  */
> +  void *p = xdlopen (mod, RTLD_NOW);
> +  /* Access the TLS variable to ensure it is allocated.  */
> +  void (*f) (void) = (void (*) (void))xdlsym (p, func);
> +  f ();
> +  return p;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  void *mods[6];
> +
> +  {
> +    int ret = pthread_barrier_init (&barrier, NULL, 2);
> +    if (ret != 0)
> +      {
> +        errno = ret;
> +        printf ("error: pthread_barrier_init: %m\n");
> +        exit (1);
> +      }
> +  }
> +
> +  pthread_t blocked_thread = xpthread_create (NULL, blocked_thread_func, NULL);
> +  xpthread_barrier_wait (&barrier);
> +
> +  printf ("maintls[%zu]:\t %p .. %p\n",
> +	   sizeof maintls, maintls, maintls + sizeof maintls);
> +  memset (maintls, 1, sizeof maintls);
> +
> +  /* Load modules with dynamic TLS (may use surplus static TLS
> +     opportunistically).  */
> +  mods[0] = load_and_access ("tst-tls-ie-mod0.so", "access0");
> +  mods[1] = load_and_access ("tst-tls-ie-mod1.so", "access1");
> +  mods[2] = load_and_access ("tst-tls-ie-mod2.so", "access2");
> +  mods[3] = load_and_access ("tst-tls-ie-mod3.so", "access3");
> +  /* Load modules with initial-exec TLS (can only use surplus static TLS).  */
> +  mods[4] = load_and_access ("tst-tls-ie-mod4.so", "access4");
> +  mods[5] = load_and_access ("tst-tls-ie-mod5.so", "access5");
> +
> +  /* Here 1152 bytes of surplus static TLS is in use and at most 512 bytes
> +     are available (depending on TLS optimizations).  */
> +  printf ("The next dlopen should fail...\n");
> +  void *p = dlopen ("tst-tls-ie-mod6.so", RTLD_NOW);
> +  if (p != NULL)
> +    {
> +      printf ("error: expected dlopen to fail because there is "
> +	      "not enough surplus static TLS.\n");
> +      exit (1);

OK.

> +    }
> +  printf ("...OK failed with: %s.\n", dlerror ());
> +
> +  xpthread_barrier_wait (&barrier);
> +  xpthread_join (blocked_thread);
> +
> +  /* Close the modules.  */
> +  for (int i = 0; i < 6; ++i)
> +    xdlclose (mods[i]);
> +
> +  return 0;
> +}
> diff --git a/manual/tunables.texi b/manual/tunables.texi
> index 978e08f4fb..7f891c2710 100644
> --- a/manual/tunables.texi
> +++ b/manual/tunables.texi
> @@ -247,6 +247,23 @@ e.g. LD_AUDIT, or will use more than 4 dynamic link namespaces as created
>  by @code{dlmopen} with an lmid argument of @code{LM_ID_NEWLM}.
>  @end deftp
>  
> +@deftp Tunable glibc.rtld.optional_static_tls
> +Sets the amount of surplus static TLS in bytes to allocate at program
> +startup.  Every thread created allocates this amount of specified surplus
> +static TLS. This is a minimum value and additional space may be allocated
> +for internal purposes including alignment.  Optional static TLS is used for
> +optimizing dynamic TLS access for platforms that support such optimizations
> +e.g. TLS descriptors or optimized TLS access for POWER (@code{DT_PPC64_OPT}
> +and @code{DT_PPC_OPT}).  In order to make the best use of such optimizations
> +the value should be as many bytes as would be required to hold all TLS
> +variables in all dynamic loaded shared libraries.  The value cannot be known
> +by the dynamic loader because it doesn't know the expected set of shared
> +libraries which will be loaded.  The existing static TLS space cannot be
> +changed once allocated at process startup.  The default allocation of
> +optional static TLS is 512 bytes and is allocated in every thread.
> +@end deftp
> +
> +
>  @node Elision Tunables
>  @section Elision Tunables
>  @cindex elision tunables
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 3b0c6d9620..997084fb4b 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -442,6 +442,9 @@ struct rtld_global
>    EXTERN size_t _dl_tls_static_used;
>    /* Alignment requirement of the static TLS block.  */
>    EXTERN size_t _dl_tls_static_align;
> +  /* Remaining amount of static TLS that may be used for optimizing
> +     dynamic TLS access (e.g. with TLSDESC).  */
> +  EXTERN size_t _dl_tls_static_optional;
>  
>  /* Number of additional entries in the slotinfo array of each slotinfo
>     list element.  A large number makes it almost certain take we never
> 


-- 
Cheers,
Carlos.



More information about the Libc-alpha mailing list