| commit ffb17e7ba3a5ba9632cee97330b325072fbe41dd |
| Author: Szabolcs Nagy <szabolcs.nagy@arm.com> |
| Date: Wed Jun 10 13:40:40 2020 +0100 |
| |
| rtld: Avoid using up static TLS surplus for optimizations [BZ #25051] |
| |
| 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. |
| |
| Reviewed-by: Carlos O'Donell <carlos@redhat.com> |
| |
| Conflicts: |
| elf/Makefile |
| (Missing __libc_single_threaded downstream.) |
| |
| diff --git a/csu/libc-tls.c b/csu/libc-tls.c |
| index 6f2a47dc86222407..76aa1b98ea059a43 100644 |
| |
| |
| @@ -62,6 +62,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; |
| |
| /* Generation counter for the dtv. */ |
| size_t _dl_tls_generation; |
| diff --git a/elf/Makefile b/elf/Makefile |
| index cbced7605ebe2443..8b96bfefd852b79f 100644 |
| |
| |
| @@ -197,7 +197,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ |
| 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 |
| # reldep9 |
| tests-internal += loadtest unload unload2 circleload1 \ |
| neededtest neededtest2 neededtest3 neededtest4 \ |
| @@ -313,7 +314,10 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ |
| tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \ |
| tst-dlopenfailmod3 \ |
| 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 |
| |
| # Most modules build with _ISOMAC defined, but those filtered out |
| # depend on internal headers. |
| @@ -1690,3 +1694,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 |
| + |
| +$(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 |
| diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c |
| index afeace4d3e49180c..c6139b89d4ecddc8 100644 |
| |
| |
| @@ -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) |
| { |
| /* 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 cfda76f6de96df57..4f8c35b7d37bfc18 100644 |
| |
| |
| @@ -60,8 +60,6 @@ |
| 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 |
| |
| /* Calculate the size of the static TLS surplus, when the given |
| number of audit modules are loaded. Must be called after the |
| @@ -69,13 +67,15 @@ |
| void |
| _dl_tls_static_surplus_init (size_t naudit) |
| { |
| - 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; |
| @@ -84,9 +84,10 @@ _dl_tls_static_surplus_init (size_t naudit) |
| (unsigned long) naudit, (unsigned long) (DL_NNS - nns)); |
| nns += naudit; |
| |
| + 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); |
| } |
| |
| /* Out-of-memory handler. */ |
| diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list |
| index 7337fb85062c91a7..6408a8e5ae92d2c6 100644 |
| |
| |
| @@ -134,5 +134,10 @@ glibc { |
| maxval: 16 |
| default: 4 |
| } |
| + optional_static_tls { |
| + type: SIZE_T |
| + minval: 0 |
| + default: 512 |
| + } |
| } |
| } |
| diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h |
| index 9e9d5a3b28bc06c5..2fc3c91b7defe84e 100644 |
| |
| |
| @@ -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 0000000000000000..c7b5c688e362c861 |
| |
| |
| @@ -0,0 +1,112 @@ |
| +/* 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. */ |
| + |
| +#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/check.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) |
| + FAIL_EXIT1 ("error: expected dlmopen to fail because there is " |
| + "not enough surplus static TLS.\n"); |
| + 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 0000000000000000..46b362a9b783d214 |
| |
| |
| @@ -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 0000000000000000..2450686e400e1141 |
| |
| |
| @@ -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 0000000000000000..849ff91e53b0a518 |
| |
| |
| @@ -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 0000000000000000..23915ab67bab0ada |
| |
| |
| @@ -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 0000000000000000..5395f844a5999ea9 |
| |
| |
| @@ -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 0000000000000000..93ac2eacae292d86 |
| |
| |
| @@ -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 0000000000000000..84b3fd285b5b5a3e |
| |
| |
| @@ -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 0000000000000000..c736bf0684f3b08f |
| |
| |
| @@ -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 0000000000000000..2dc0894480417389 |
| |
| |
| @@ -0,0 +1,111 @@ |
| +/* 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/check.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) |
| + FAIL_EXIT1 ("error: expected dlopen to fail because there is " |
| + "not enough surplus static TLS.\n"); |
| + 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 e6a3e9a2cf5c959c..bd737b5d57080462 100644 |
| |
| |
| @@ -249,6 +249,23 @@ increase the per-thread memory usage as necessary, so this tunable does |
| not need to be changed to allow many audit modules e.g. via @env{LD_AUDIT}. |
| @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 293f3ab5a496afdf..37f1915b0c75a020 100644 |
| |
| |
| @@ -441,6 +441,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 |