/* Test TLSDESC relocation.
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
. */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tst-gnu2-tls2.h"
#ifndef IS_SUPPORTED
# define IS_SUPPORTED() true
#endif
/* An architecture can define it to clobber caller-saved registers in
malloc below to verify that the implicit TLSDESC call won't change
caller-saved registers. */
#ifndef PREPARE_MALLOC
# define PREPARE_MALLOC()
#endif
extern void * __libc_malloc (size_t);
size_t malloc_counter = 0;
void *
malloc (size_t n)
{
PREPARE_MALLOC ();
malloc_counter++;
return __libc_malloc (n);
}
static void *mod[3];
#ifndef MOD
# define MOD(i) "tst-gnu2-tls2mod" #i ".so"
#endif
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)
{
struct tls var = { -1, -1, -1, -1 };
struct tls *(*f) (struct tls *) = xdlsym (mod[i], sym);
/* Check that our malloc is called. */
malloc_counter = 0;
struct tls *p = f (&var);
TEST_VERIFY (malloc_counter != 0);
printf ("access %s: %s() = %p\n", modname[i], sym, p);
TEST_VERIFY_EXIT (memcmp (p, &var, sizeof (var)) == 0);
++(p->a);
}
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. */
/* Force the slow path in GNU2 TLS descriptor call. */
access_mod (1, "apply_tls");
return arg;
}
static int
do_test (void)
{
if (!IS_SUPPORTED ())
return EXIT_UNSUPPORTED;
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