/* This testcase is part of GDB, the GNU debugger.

   Copyright 2024 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <dlfcn.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

typedef void (*setter_ftype) (int which, int val);

__thread int tls_main_tbss_1;
__thread int tls_main_tbss_2;
__thread int tls_main_tdata_1 = 96;
__thread int tls_main_tdata_2 = 97;

extern void set_tls_lib10_var (int which, int val);
extern void set_tls_lib11_var (int which, int val);

volatile int data;

static void
set_tls_main_var (int which, int val)
{
  switch (which)
    {
    case 1:
      tls_main_tbss_1 = val;
      break;
    case 2:
      tls_main_tbss_2 = val;
      break;
    case 3:
      tls_main_tdata_1 = val;
      break;
    case 4:
      tls_main_tdata_2 = val;
      break;
    }
}

void
use_it (int a)
{
  data = a;
}

static void *
load_dso (char *dso_name, int n, setter_ftype *setterp)
{
  char buf[80];
  void *sym;
  void *handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL);
  if (handle == NULL)
    {
      fprintf (stderr, "dlopen of DSO '%s' failed: %s\n", dso_name, dlerror ());
      exit (1);
    }
  sprintf (buf, "set_tls_lib%d_var", n);
  sym = dlsym (handle, buf);
  assert (sym != NULL);
  *setterp = sym;

  /* Some libc implementations (for some architectures) refuse to
     initialize TLS data structures (specifically, the DTV) without
     first calling dlsym on one of the TLS symbols.  */
  sprintf (buf, "tls_lib%d_tdata_1", n);
  assert (dlsym (handle, buf) != NULL);

  return handle;
}

int
main (int argc, char **argv)
{
  int i, status;
  setter_ftype s0, s1, s2, s3, s4, s10, s11;
  void *h1 = load_dso (OBJ1, 1, &s1);
  void *h2 = load_dso (OBJ2, 2, &s2);
  void *h3 = load_dso (OBJ3, 3, &s3);
  void *h4 = load_dso (OBJ4, 4, &s4);
  s0 = set_tls_main_var;
  s10 = set_tls_lib10_var;
  s11 = set_tls_lib11_var;

  use_it (0);		/* main-breakpoint-1 */

  /* Set TLS variables in main program and all libraries.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 10 + i);
  for (i = 1; i <= 4; i++)
    s1 (i, 110 + i);
  for (i = 1; i <= 4; i++)
    s2 (i, 210 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 310 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 410 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1010 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1110 + i);

  use_it (0);		/* main-breakpoint-2 */

  /* Unload lib2 and lib3.  */
  status = dlclose (h2);
  assert (status == 0);
  status = dlclose (h3);
  assert (status == 0);

  /* Set TLS variables in main program and in libraries which are still
     loaded.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 20 + i);
  for (i = 1; i <= 4; i++)
    s1 (i, 120 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 420 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1020 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1120 + i);

  use_it (0);		/* main-breakpoint-3 */

  /* Load lib3.  */
  h3 = load_dso (OBJ3, 3, &s3);

  /* Set TLS vars again; currently, only lib2 is not loaded.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 30 + i);
  for (i = 1; i <= 4; i++)
    s1 (i, 130 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 330 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 430 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1030 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1130 + i);

  use_it (0);		/* main-breakpoint-4 */

  /* Unload lib1 and lib4; load lib2.  */
  status = dlclose (h1);
  assert (status == 0);
  status = dlclose (h4);
  assert (status == 0);
  h2 = load_dso (OBJ2, 2, &s2);

  /* Set TLS vars; currently, lib2 and lib3 are loaded,
     lib1 and lib4 are not.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 40 + i);
  for (i = 1; i <= 4; i++)
    s2 (i, 240 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 340 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1040 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1140 + i);

  use_it (0);		/* main-breakpoint-5 */

  /* Load lib4 and lib1.  Unload lib2.  */
  h4 = load_dso (OBJ4, 4, &s4);
  h1 = load_dso (OBJ1, 1, &s1);
  status = dlclose (h2);
  assert (status == 0);

  /* Set TLS vars; currently, lib1, lib3, and lib4 are loaded;
     lib2 is not loaded.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 50 + i);
  for (i = 1; i <= 4; i++)
    s1 (i, 150 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 350 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 450 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1050 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1150 + i);

  use_it (0);		/* main-breakpoint-6 */

  /* Load lib2, unload lib1, lib3, and lib4; then load lib3 again.  */
  h2 = load_dso (OBJ2, 2, &s2);
  status = dlclose (h1);
  assert (status == 0);
  status = dlclose (h3);
  assert (status == 0);
  status = dlclose (h4);
  assert (status == 0);
  h3 = load_dso (OBJ3, 3, &s3);

  /* Set TLS vars; currently, lib2 and lib3 are loaded;
     lib1 and lib4 are not loaded.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 60 + i);
  for (i = 1; i <= 4; i++)
    s2 (i, 260 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 360 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1060 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1160 + i);

  use_it (0);		/* main-breakpoint-7 */

  /* Unload lib3 and lib2, then (re)load lib4, lib3, lib2, and lib1,
     in that order.  */
  status = dlclose (h3);
  assert (status == 0);
  status = dlclose (h2);
  assert (status == 0);
  h4 = load_dso (OBJ4, 4, &s4);
  h3 = load_dso (OBJ3, 3, &s3);
  h2 = load_dso (OBJ2, 2, &s2);
  h1 = load_dso (OBJ1, 1, &s1);

  /* Set TLS vars; currently, lib1, lib2, lib3, and lib4 are all
     loaded.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 70 + i);
  for (i = 1; i <= 4; i++)
    s1 (i, 170 + i);
  for (i = 1; i <= 4; i++)
    s2 (i, 270 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 370 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 470 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1070 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1170 + i);

  use_it (0);		/* main-breakpoint-8 */

  /* Unload lib3, lib1, and lib4.  */
  status = dlclose (h3);
  assert (status == 0);
  status = dlclose (h1);
  assert (status == 0);
  status = dlclose (h4);
  assert (status == 0);

  /* Set TLS vars; currently, lib2 is loaded; lib1, lib3, and lib4 are
     not.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 80 + i);
  for (i = 1; i <= 4; i++)
    s2 (i, 280 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1080 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1180 + i);

  use_it (0);		/* main-breakpoint-9 */

  /* Load lib3, unload lib2, load lib4.  */
  h3 = load_dso (OBJ3, 3, &s3);
  status = dlclose (h2);
  assert (status == 0);
  h4 = load_dso (OBJ4, 4, &s4);

  /* Set TLS vars; currently, lib3 and lib4 are loaded; lib1 and lib2
     are not.  */
  for (i = 1; i <= 4; i++)
    s0 (i, 90 + i);
  for (i = 1; i <= 4; i++)
    s3 (i, 390 + i);
  for (i = 1; i <= 4; i++)
    s4 (i, 490 + i);
  for (i = 1; i <= 4; i++)
    s10 (i, 1090 + i);
  for (i = 1; i <= 4; i++)
    s11 (i, 1190 + i);

  use_it (0);		/* main-breakpoint-10 */

  /* Attempt to keep variables in the main program from being optimized
     away.  */
  use_it (tls_main_tbss_1);
  use_it (tls_main_tbss_2);
  use_it (tls_main_tdata_1);
  use_it (tls_main_tdata_2);

  use_it (100);		/* main-breakpoint-last */

  return 0;
}