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 \
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 \
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
{
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;
}
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))
/* 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. */
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;
}
-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. */
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
--- /dev/null
+/* 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>
--- /dev/null
+/* 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>
--- /dev/null
+/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */
+#include "tst-dlopen-tlsreinit1.c"
--- /dev/null
+/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */
+#include "tst-dlopen-tlsreinit2.c"
--- /dev/null
+/* 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. */
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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;
+}
unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update
needs to process this
lt_library map. */
+ unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen. */
/* NODELETE status of the map. Only valid for maps of type
lt_loaded. Lazy binding sets l_nodelete_active directly,
extern int _dl_scope_free (void *) attribute_hidden;
-/* 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. */
-extern void _dl_add_to_slotinfo (struct link_map *l, bool do_add)
+extern bool _dl_add_to_slotinfo (struct link_map *l, bool do_add)
attribute_hidden;
/* Update slot information data for at least the generation of the