diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 30 | ||||
-rw-r--r-- | elf/dl-open.c | 35 | ||||
-rw-r--r-- | elf/dl-tls.c | 44 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinit1.c | 40 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinit2.c | 39 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinit3.c | 2 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinit4.c | 2 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinitmod1.c | 20 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinitmod2.c | 30 | ||||
-rw-r--r-- | elf/tst-dlopen-tlsreinitmod3.c | 102 |
10 files changed, 307 insertions, 37 deletions
diff --git a/elf/Makefile b/elf/Makefile index a3475f3fb5..a03c6520d8 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -416,6 +416,10 @@ tests += \ tst-dlmopen4 \ tst-dlopen-self \ tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ + tst-dlopen-tlsreinit2 \ + tst-dlopen-tlsreinit3 \ + tst-dlopen-tlsreinit4 \ tst-dlopenfail \ tst-dlopenfail-2 \ tst-dlopenrpath \ @@ -853,6 +857,9 @@ modules-names += \ tst-dlmopen-twice-mod1 \ tst-dlmopen-twice-mod2 \ tst-dlmopen1mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ + tst-dlopen-tlsreinitmod3 \ tst-dlopenfaillinkmod \ tst-dlopenfailmod1 \ tst-dlopenfailmod2 \ @@ -3118,3 +3125,26 @@ $(objpfx)tst-recursive-tls.out: \ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) $(objpfx)tst-recursive-tlsmod%.os: tst-recursive-tlsmodN.c $(compile-command.c) -DVAR=thread_$* -DFUNC=get_threadvar_$* + +# Order matters here. The test needs the constructor for +# tst-dlopen-tlsreinitmod2.so to be called first. +LDFLAGS-tst-dlopen-tlsreinitmod1.so = -Wl,--no-as-needed +$(objpfx)tst-dlopen-tlsreinitmod1.so: \ + $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so +LDFLAGS-tst-dlopen-tlsreinit2 = -Wl,--no-as-needed +$(objpfx)tst-dlopen-tlsreinit2: \ + $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so +LDFLAGS-tst-dlopen-tlsreinit4 = -Wl,--no-as-needed +$(objpfx)tst-dlopen-tlsreinit4: \ + $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so +# tst-dlopen-tlsreinitmod2.so is underlinked and refers to +# tst-dlopen-tlsreinitmod3.so. The dependency is provided via +# $(objpfx)tst-dlopen-tlsreinitmod1.so. +tst-dlopen-tlsreinitmod2.so-no-z-defs = yes +$(objpfx)tst-dlopen-tlsreinit.out: $(objpfx)tst-dlopen-tlsreinitmod1.so \ + $(objpfx)tst-dlopen-tlsreinitmod2.so $(objpfx)tst-dlopen-tlsreinitmod3.so +# Reuse an audit module which provides ample debug logging. +$(objpfx)tst-dlopen-tlsreinit3.out: $(objpfx)tst-auditmod1.so +tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so +$(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so +tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so diff --git a/elf/dl-open.c b/elf/dl-open.c index c378da16c0..8556e7bd2f 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -363,17 +363,8 @@ resize_tls_slotinfo (struct link_map *new) { bool any_tls = false; for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) - { - struct link_map *imap = new->l_searchlist.r_list[i]; - - /* Only add TLS memory if this object is loaded now and - therefore is not yet initialized. */ - if (! imap->l_init_called && imap->l_tls_blocksize > 0) - { - _dl_add_to_slotinfo (imap, false); - any_tls = true; - } - } + if (_dl_add_to_slotinfo (new->l_searchlist.r_list[i], false)) + any_tls = true; return any_tls; } @@ -383,22 +374,8 @@ resize_tls_slotinfo (struct link_map *new) static void update_tls_slotinfo (struct link_map *new) { - unsigned int first_static_tls = new->l_searchlist.r_nlist; for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) - { - struct link_map *imap = new->l_searchlist.r_list[i]; - - /* Only add TLS memory if this object is loaded now and - therefore is not yet initialized. */ - if (! imap->l_init_called && imap->l_tls_blocksize > 0) - { - _dl_add_to_slotinfo (imap, true); - - if (imap->l_need_tls_init - && first_static_tls == new->l_searchlist.r_nlist) - first_static_tls = i; - } - } + _dl_add_to_slotinfo (new->l_searchlist.r_list[i], true); size_t newgen = GL(dl_tls_generation) + 1; if (__glibc_unlikely (newgen == 0)) @@ -410,13 +387,11 @@ TLS generation counter wrapped! Please report this.")); /* We need a second pass for static tls data, because _dl_update_slotinfo must not be run while calls to _dl_add_to_slotinfo are still pending. */ - for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) + for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) { struct link_map *imap = new->l_searchlist.r_list[i]; - if (imap->l_need_tls_init - && ! imap->l_init_called - && imap->l_tls_blocksize > 0) + if (imap->l_need_tls_init && imap->l_tls_blocksize > 0) { /* For static TLS we have to allocate the memory here and now, but we can delay updating the DTV. */ diff --git a/elf/dl-tls.c b/elf/dl-tls.c index ecb966d282..3d529b722c 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -632,17 +632,21 @@ _dl_allocate_tls_init (void *result, bool main_thread) some platforms use in static programs requires it. */ dtv[map->l_tls_modid].pointer.val = dest; - /* Copy the initialization image and clear the BSS part. For - audit modules or dependencies with initial-exec TLS, we can not - set the initial TLS image on default loader initialization - because it would already be set by the audit setup. However, - subsequent thread creation would need to follow the default - behaviour. */ + /* Copy the initialization image and clear the BSS part. + For audit modules or dependencies with initial-exec TLS, + we can not set the initial TLS image on default loader + initialization because it would already be set by the + audit setup, which uses the dlopen code and already + clears l_need_tls_init. Calls with !main_thread from + pthread_create need to initialze TLS for the current + thread regardless of namespace. */ if (map->l_ns != LM_ID_BASE && main_thread) continue; memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), '\0', map->l_tls_blocksize - map->l_tls_initimage_size); + if (main_thread) + map->l_need_tls_init = 0; } total += cnt; @@ -1099,9 +1103,32 @@ _dl_tls_initial_modid_limit_setup (void) } -void +/* Add module to slot information data. If DO_ADD is false, only the + required memory is allocated. Must be called with + GL (dl_load_tls_lock) acquired. If the function has already been + called for the link map L with !DO_ADD, then this function will not + raise an exception, otherwise it is possible that it encounters a + memory allocation failure. + + Return false if L has already been added to the slotinfo data, or + if L has no TLS data. If the returned value is true, L has been + added with this call (DO_ADD), or has been added in a previous call + (!DO_ADD). + + The expected usage is as follows: Call _dl_add_to_slotinfo for + several link maps with DO_ADD set to false, and record if any calls + result in a true result. If there was a true result, call + _dl_add_to_slotinfo again, this time with DO_ADD set to true. (For + simplicity, it's possible to call the function for link maps where + the previous result was false.) The return value from the second + round of calls can be ignored. If there was true result initially, + call _dl_update_slotinfo to update the TLS generation counter. */ +bool _dl_add_to_slotinfo (struct link_map *l, bool do_add) { + if (l->l_tls_blocksize == 0 || l->l_tls_in_slotinfo) + return false; + /* Now that we know the object is loaded successfully add modules containing TLS data to the dtv info table. We might have to increase its size. */ @@ -1157,7 +1184,10 @@ cannot create TLS data structures")); atomic_store_relaxed (&listp->slotinfo[idx].map, l); atomic_store_relaxed (&listp->slotinfo[idx].gen, GL(dl_tls_generation) + 1); + l->l_tls_in_slotinfo = true; } + + return true; } #if PTHREAD_IN_LIBC diff --git a/elf/tst-dlopen-tlsreinit1.c b/elf/tst-dlopen-tlsreinit1.c new file mode 100644 index 0000000000..2016b9b0c6 --- /dev/null +++ b/elf/tst-dlopen-tlsreinit1.c @@ -0,0 +1,40 @@ +/* Test that dlopen preserves already accessed TLS (bug 31717). + 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/>. */ + +#include <stdbool.h> +#include <support/check.h> +#include <support/xdlfcn.h> +#include <ctype.h> + +static int +do_test (void) +{ + void *handle = xdlopen ("tst-dlopen-tlsreinitmod1.so", RTLD_NOW); + + bool *tlsreinitmod3_tested = xdlsym (handle, "tlsreinitmod3_tested"); + TEST_VERIFY (*tlsreinitmod3_tested); + + xdlclose (handle); + + /* This crashes if the libc.so.6 TLS image has been reverted. */ + TEST_VERIFY (!isupper ('@')); + + return 0; +} + +#include <support/test-driver.c> diff --git a/elf/tst-dlopen-tlsreinit2.c b/elf/tst-dlopen-tlsreinit2.c new file mode 100644 index 0000000000..90ad2c7713 --- /dev/null +++ b/elf/tst-dlopen-tlsreinit2.c @@ -0,0 +1,39 @@ +/* Test that dlopen preserves already accessed TLS (bug 31717). + Variant with initially-linked modules. + 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/>. */ + +#include <ctype.h> +#include <stdbool.h> +#include <support/check.h> +#include <support/xdlfcn.h> + + +static int +do_test (void) +{ + /* Defined in tst-dlopen-tlsreinitmod3.so. */ + extern bool tlsreinitmod3_tested; + TEST_VERIFY (tlsreinitmod3_tested); + + /* This crashes if the libc.so.6 TLS image has been reverted. */ + TEST_VERIFY (!isupper ('@')); + + return 0; +} + +#include <support/test-driver.c> diff --git a/elf/tst-dlopen-tlsreinit3.c b/elf/tst-dlopen-tlsreinit3.c new file mode 100644 index 0000000000..79bd585aff --- /dev/null +++ b/elf/tst-dlopen-tlsreinit3.c @@ -0,0 +1,2 @@ +/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */ +#include "tst-dlopen-tlsreinit1.c" diff --git a/elf/tst-dlopen-tlsreinit4.c b/elf/tst-dlopen-tlsreinit4.c new file mode 100644 index 0000000000..344c9211ab --- /dev/null +++ b/elf/tst-dlopen-tlsreinit4.c @@ -0,0 +1,2 @@ +/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */ +#include "tst-dlopen-tlsreinit2.c" diff --git a/elf/tst-dlopen-tlsreinitmod1.c b/elf/tst-dlopen-tlsreinitmod1.c new file mode 100644 index 0000000000..354cc3de51 --- /dev/null +++ b/elf/tst-dlopen-tlsreinitmod1.c @@ -0,0 +1,20 @@ +/* Test that dlopen preserves already accessed TLS (bug 31717), module 1. + 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/>. */ + +/* This module triggers loading of tst-dlopen-tlsreinitmod2.so and + tst-dlopen-tlsreinitmod3.so. */ diff --git a/elf/tst-dlopen-tlsreinitmod2.c b/elf/tst-dlopen-tlsreinitmod2.c new file mode 100644 index 0000000000..677e69bd35 --- /dev/null +++ b/elf/tst-dlopen-tlsreinitmod2.c @@ -0,0 +1,30 @@ +/* Test that dlopen preserves already accessed TLS (bug 31717), module 2. + 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/>. */ + +#include <stdio.h> + +/* Defined in tst-dlopen-tlsreinitmod3.so. This an underlinked symbol + dependency. */ +extern void call_tlsreinitmod3 (void); + +static void __attribute__ ((constructor)) +tlsreinitmod2_init (void) +{ + puts ("info: constructor of tst-dlopen-tlsreinitmod2.so invoked"); + call_tlsreinitmod3 (); +} diff --git a/elf/tst-dlopen-tlsreinitmod3.c b/elf/tst-dlopen-tlsreinitmod3.c new file mode 100644 index 0000000000..ef769c5131 --- /dev/null +++ b/elf/tst-dlopen-tlsreinitmod3.c @@ -0,0 +1,102 @@ +/* Test that dlopen preserves already accessed TLS (bug 31717), module 3. + 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/>. */ + +#include <dlfcn.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +/* Used to verify from the main program that the test ran. */ +bool tlsreinitmod3_tested; + +/* This TLS variable must not revert back to the initial state after + dlopen. */ +static __thread int tlsreinitmod3_state = 1; + +/* Set from the ELF constructor during dlopen. */ +static bool tlsreinitmod3_constructed; + +/* Second half of test, behind a compiler barrier. The compiler + barrier is necessary to prevent carrying over TLS address + information from call_tlsreinitmod3 to call_tlsreinitmod3_tail. */ +void call_tlsreinitmod3_tail (void *self) __attribute__ ((weak)); + +/* Called from tst-dlopen-tlsreinitmod2.so. */ +void +call_tlsreinitmod3 (void) +{ + printf ("info: call_tlsreinitmod3 invoked (state=%d)\n", + tlsreinitmod3_state); + + if (tlsreinitmod3_constructed) + { + puts ("error: call_tlsreinitmod3 called after ELF constructor"); + fflush (stdout); + /* Cannot rely on test harness due to dynamic linking. */ + _exit (1); + } + + tlsreinitmod3_state = 2; + + /* Self-dlopen. This will run the ELF constructor. */ + void *self = dlopen ("tst-dlopen-tlsreinitmod3.so", RTLD_NOW); + if (self == NULL) + { + printf ("error: dlopen: %s\n", dlerror ()); + fflush (stdout); + /* Cannot rely on test harness due to dynamic linking. */ + _exit (1); + } + + call_tlsreinitmod3_tail (self); +} + +void +call_tlsreinitmod3_tail (void *self) +{ + printf ("info: dlopen returned in tlsreinitmod3 (state=%d)\n", + tlsreinitmod3_state); + + if (!tlsreinitmod3_constructed) + { + puts ("error: dlopen did not call tlsreinitmod3 ELF constructor"); + fflush (stdout); + /* Cannot rely on test harness due to dynamic linking. */ + _exit (1); + } + + if (tlsreinitmod3_state != 2) + { + puts ("error: TLS state reverted in tlsreinitmod3"); + fflush (stdout); + /* Cannot rely on test harness due to dynamic linking. */ + _exit (1); + } + + dlclose (self); + + /* Signal test completion to the main program. */ + tlsreinitmod3_tested = true; +} + +static void __attribute__ ((constructor)) +tlsreinitmod3_init (void) +{ + puts ("info: constructor of tst-dlopen-tlsreinitmod3.so invoked"); + tlsreinitmod3_constructed = true; +} |