diff --git a/elf/Makefile b/elf/Makefile index 87aac923ba..ba7b71035c 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -469,6 +469,7 @@ tests += \ tst-tls21 \ tst-tlsalign \ tst-tlsalign-extern \ + tst-tlsgap \ tst-unique1 \ tst-unique2 \ tst-unwind-ctor \ @@ -896,6 +897,9 @@ modules-names += \ tst-tls20mod-bad \ tst-tls21mod \ tst-tlsalign-lib \ + tst-tlsgap-mod0 \ + tst-tlsgap-mod1 \ + tst-tlsgap-mod2 \ tst-tlsmod1 \ tst-tlsmod10 \ tst-tlsmod11 \ @@ -3031,3 +3035,14 @@ $(objpfx)tst-nodeps2-mod.so: $(common-objpfx)libc.so \ $(LINK.o) -Wl,--no-as-needed -nostartfiles -nostdlib -shared -o $@ $^ $(objpfx)tst-nodeps2.out: \ $(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.so + +$(objpfx)tst-tlsgap: $(shared-thread-library) +$(objpfx)tst-tlsgap.out: \ + $(objpfx)tst-tlsgap-mod0.so \ + $(objpfx)tst-tlsgap-mod1.so \ + $(objpfx)tst-tlsgap-mod2.so +ifeq (yes,$(have-mtls-dialect-gnu2)) +CFLAGS-tst-tlsgap-mod0.c += -mtls-dialect=gnu2 +CFLAGS-tst-tlsgap-mod1.c += -mtls-dialect=gnu2 +CFLAGS-tst-tlsgap-mod2.c += -mtls-dialect=gnu2 +endif diff --git a/elf/tst-tlsgap-mod0.c b/elf/tst-tlsgap-mod0.c new file mode 100644 index 0000000000..1478b0beac --- /dev/null +++ b/elf/tst-tlsgap-mod0.c @@ -0,0 +1,2 @@ +int __thread tls0; +int *f0(void) { return &tls0; } diff --git a/elf/tst-tlsgap-mod1.c b/elf/tst-tlsgap-mod1.c new file mode 100644 index 0000000000..b10fc3702c --- /dev/null +++ b/elf/tst-tlsgap-mod1.c @@ -0,0 +1,2 @@ +int __thread tls1[100]; /* Size > glibc.rtld.optional_static_tls / 2. */ +int *f1(void) { return tls1; } diff --git a/elf/tst-tlsgap-mod2.c b/elf/tst-tlsgap-mod2.c new file mode 100644 index 0000000000..166c27d7f3 --- /dev/null +++ b/elf/tst-tlsgap-mod2.c @@ -0,0 +1,2 @@ +int __thread tls2; +int *f2(void) { return &tls2; } diff --git a/elf/tst-tlsgap.c b/elf/tst-tlsgap.c new file mode 100644 index 0000000000..4932885076 --- /dev/null +++ b/elf/tst-tlsgap.c @@ -0,0 +1,92 @@ +/* TLS modid gap reuse regression test for bug 29039. + Copyright (C) 2023 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 + . */ + +#include +#include +#include +#include +#include +#include + +static void *mod[3]; +#define MOD(i) "tst-tlsgap-mod" #i ".so" +static const char *modname[3] = { MOD(0), MOD(1), MOD(2) }; +#undef MOD + +static void +open_mod (int i) +{ + mod[i] = xdlopen (modname[i], RTLD_LAZY); + printf ("open %s\n", modname[i]); +} + +static void +close_mod (int i) +{ + xdlclose (mod[i]); + mod[i] = NULL; + printf ("close %s\n", modname[i]); +} + +static void +access_mod (int i, const char *sym) +{ + int *(*f) (void) = xdlsym (mod[i], sym); + int *p = f (); + printf ("access %s: %s() = %p\n", modname[i], sym, p); + TEST_VERIFY_EXIT (p != NULL); + ++*p; +} + +static void * +start (void *arg) +{ + /* The DTV generation is at the last dlopen of mod0 and the + entry for mod1 is NULL. */ + + open_mod (1); /* Reuse modid of mod1. Uses dynamic TLS. */ + + /* DTV is unchanged: dlopen only updates the DTV to the latest + generation if static TLS is allocated for a loaded module. + + With bug 29039, the TLSDESC relocation in mod1 uses the old + dlclose generation of mod1 instead of the new dlopen one so + DTV is not updated on TLS access. */ + + access_mod (1, "f1"); + + return arg; +} + +static int +do_test (void) +{ + open_mod (0); + open_mod (1); + open_mod (2); + close_mod (0); + close_mod (1); /* Create modid gap at mod1. */ + open_mod (0); /* Reuse modid of mod0, bump generation count. */ + + /* Create a thread where DTV of mod1 is NULL. */ + pthread_t t = xpthread_create (NULL, start, NULL); + xpthread_join (t); + return 0; +} + +#include