summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-07-31 07:42:20 +0000
committerUlrich Drepper <drepper@redhat.com>2003-07-31 07:42:20 +0000
commitadc12574e555ec7d6ba3969eed4f829705c235ab (patch)
treecaff1741b560ff8258ace64120781354e62b56c2
parent9722e6f3e26822835542c1c49048d855a5881510 (diff)
downloadglibc-adc12574e555ec7d6ba3969eed4f829705c235ab.tar.gz
glibc-adc12574e555ec7d6ba3969eed4f829705c235ab.tar.xz
glibc-adc12574e555ec7d6ba3969eed4f829705c235ab.zip
Update.
2003-07-30  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-reloc.c (_dl_allocate_static_tls): Don't return any value,
	call dl_signal_error directly.  If already relocated, call
	GL(dl_init_static_tls) directly, otherwise queue it for later.
	(CHECK_STATIC_TLS): Undo 2003-07-24 change.
	* elf/rtld.c (dl_main): Initialize GL(dl_init_static_tls).
	* elf/dl-open.c (dl_open_worker): Call GL_dl_init_static_tls
	for all static TLS initializations delayed in _dl_allocate_static_tls.
	* elf/dl-support.c (_dl_init_static_tls): New variable.
	* include/link.h (struct link_map): Add l_need_tls_init.
	* sysdeps/generic/ldsodefs.h (_rtld_global): Add _dl_init_static_tls.
	(_dl_nothread_init_static_tls): New prototype.
	(_dl_allocate_static_tls): Adjust prototype.

	* elf/tls-macros.h (VAR_INT_DEF): Add alignment directive.

	elf_machine_rela_relative): Adjust.
	(CHECK_STATIC_TLS): _dl_allocate_static_tls can fail now.
-rw-r--r--ChangeLog21
-rw-r--r--elf/dl-open.c10
-rw-r--r--elf/dl-support.c4
-rw-r--r--elf/rtld.c4
-rw-r--r--elf/tls-macros.h1
-rw-r--r--include/link.h4
-rw-r--r--linuxthreads/ChangeLog6
-rw-r--r--linuxthreads/pthread.c42
-rw-r--r--nptl/ChangeLog16
-rw-r--r--nptl/Makefile8
-rw-r--r--nptl/allocatestack.c38
-rw-r--r--nptl/init.c2
-rw-r--r--nptl/pthreadP.h2
-rw-r--r--nptl/tst-tls4.c191
-rw-r--r--nptl/tst-tls4moda.c56
-rw-r--r--nptl/tst-tls4modb.c65
-rw-r--r--sysdeps/generic/ldsodefs.h8
17 files changed, 472 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index ae719fa21e..d09516591e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2003-07-30  Jakub Jelinek  <jakub@redhat.com>
+
+	* elf/dl-reloc.c (_dl_allocate_static_tls): Don't return any value,
+	call dl_signal_error directly.  If already relocated, call
+	GL(dl_init_static_tls) directly, otherwise queue it for later.
+	(CHECK_STATIC_TLS): Undo 2003-07-24 change.
+	* elf/rtld.c (dl_main): Initialize GL(dl_init_static_tls).
+	* elf/dl-open.c (dl_open_worker): Call GL_dl_init_static_tls
+	for all static TLS initializations delayed in _dl_allocate_static_tls.
+	* elf/dl-support.c (_dl_init_static_tls): New variable.
+	* include/link.h (struct link_map): Add l_need_tls_init.
+	* sysdeps/generic/ldsodefs.h (_rtld_global): Add _dl_init_static_tls.
+	(_dl_nothread_init_static_tls): New prototype.
+	(_dl_allocate_static_tls): Adjust prototype.
+
+	* elf/tls-macros.h (VAR_INT_DEF): Add alignment directive.
+
 2003-07-31  Alexandre Oliva  <aoliva@redhat.com>
 
 	* elf/dynamic-link.h (elf_machine_rel, elf_machine_rela,
@@ -26,7 +43,7 @@
 	* sysdeps/s390/s390-32/dl-machine.h (elf_machine_rela,
 	elf_machine_rela_relative): Adjust.
 	* sysdeps/s390/s390-64/dl-machine.h (elf_machine_rela,
-	elf_machine_rela_relative): 
+	elf_machine_rela_relative): Adjust.
 	* sysdeps/sh/dl-machine.h (elf_machine_rela,
 	elf_machine_rela_relative): Adjust.
 	* sysdeps/sparc/sparc32/dl-machine.h (elf_machine_rela,
@@ -123,7 +140,7 @@
 	* elf/rtld.c (_dl_start_final, _dl_start, dl_main): Likewise.
 	* elf/dl-reloc.c (_dl_allocate_static_tls): Change return type to int.
 	Take l_tls_firstbyte_offset information into account.
-	(CHECK_STATIS_TLS): _dl_allocate_static_tls can fail now.
+	(CHECK_STATIC_TLS): _dl_allocate_static_tls can fail now.
 	* sysdeps/generic/ldsodefs.h: Adjust _dl_allocate_static_tls prototype.
 	* elf/Makefile: Add rules to build and run tst-tls14.
 	* elf/tst-tls14.c: New file.
diff --git a/elf/dl-open.c b/elf/dl-open.c
index aadd8f55f6..fcda37676d 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -303,6 +303,16 @@ dl_open_worker (void *a)
     }
 
 #ifdef USE_TLS
+  /* Do static TLS initialization now if it has been delayed because
+     the TLS template might not be fully relocated at _dl_allocate_static_tls
+     time.  */
+  for (l = new; l; l = l->l_next)
+    if (l->l_need_tls_init)
+      {
+	l->l_need_tls_init = 0;
+	GL(dl_init_static_tls) (l);
+      }
+
   /* We normally don't bump the TLS generation counter.  There must be
      actually a need to do this.  */
   any_tls = false;
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 52447f489a..8c58b2ba69 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -98,6 +98,10 @@ int _dl_starting_up = 1;
 hp_timing_t _dl_cpuclock_offset;
 #endif
 
+#ifdef USE_TLS
+void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls;
+#endif
+
 /* This is zero at program start to signal that the global scope map is
    allocated by rtld.  Later it keeps the size of the map.  It might be
    reset if in _dl_close if the last global object is removed.  */
diff --git a/elf/rtld.c b/elf/rtld.c
index c63405ac99..54e568804d 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -622,6 +622,10 @@ dl_main (const ElfW(Phdr) *phdr,
   GL(dl_error_catch_tsd) = &_dl_initial_error_catch_tsd;
 #endif
 
+#ifdef USE_TLS
+  GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
+#endif
+
   /* Process the environment variable which control the behaviour.  */
   process_envvars (&mode);
 
diff --git a/elf/tls-macros.h b/elf/tls-macros.h
index bb243d819d..7c2542400a 100644
--- a/elf/tls-macros.h
+++ b/elf/tls-macros.h
@@ -9,6 +9,7 @@
 #define VAR_INT_DEF(x) \
   asm (".section .tdata\n\t"						      \
        ".globl " #x "\n"						      \
+       ".balign 4\n"							      \
        #x ":\t.long 0\n\t"						      \
        ".size " #x ",4\n\t"						      \
        ".previous")
diff --git a/include/link.h b/include/link.h
index cd66222795..5d10bd6679 100644
--- a/include/link.h
+++ b/include/link.h
@@ -186,7 +186,9 @@ struct link_map
 				      the l_libname list.  */
     unsigned int l_faked:1;	/* Nonzero if this is a faked descriptor
 				   without associated file.  */
-
+    unsigned int l_need_tls_init:1; /* Nonzero if GL(dl_init_static_tls)
+				       should be called on this link map
+				       when relocation finishes.  */
     /* Array with version names.  */
     unsigned int l_nversions;
     struct r_found_version *l_versions;
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index b151dc3406..288d3523ab 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,9 @@
+2003-07-30  Jakub Jelinek  <jakub@redhat.com>
+
+	* pthread.c (init_one_static_tls, __pthread_init_static_tls): New
+	functions.
+	(pthread_initialize): Initialize GL(dl_init_static_tls).
+
 2003-06-19  Daniel Jacobowitz  <drow@mvista.com>
 
 	* sysdeps/pthread/timer_create.c (timer_create): Call timer_delref
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index 6211124b31..f7081139b2 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -462,6 +462,44 @@ __libc_dl_error_tsd (void)
 # endif
 #endif
 
+#ifdef USE_TLS
+static inline void __attribute__((always_inline))
+init_one_static_tls (pthread_descr descr, struct link_map *map)
+{
+# if TLS_TCB_AT_TP
+  dtv_t *dtv = GET_DTV (descr);
+  void *dest = (char *) descr - map->l_tls_offset;
+# elif TLS_DTV_AT_TP
+  dtv_t *dtv = GET_DTV ((pthread_descr) ((char *) descr + TLS_PRE_TCB_SIZE));
+  void *dest = (char *) descr + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+# else
+#  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+  /* Fill in the DTV slot so that a later LD/GD access will find it.  */
+  dtv[map->l_tls_modid].pointer = dest;
+
+  /* Initialize the memory.  */
+  memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
+	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
+}
+
+static void
+__pthread_init_static_tls (struct link_map *map)
+{
+  size_t i;
+
+  for (i = 0; i < PTHREAD_THREADS_MAX; ++i)
+    if (__pthread_handles[i].h_descr != NULL && i != 1)
+      {
+        __pthread_lock (&__pthread_handles[i].h_lock, NULL);
+	if (__pthread_handles[i].h_descr != NULL)
+	  init_one_static_tls (__pthread_handles[i].h_descr, map);
+        __pthread_unlock (&__pthread_handles[i].h_lock);
+      }
+}
+#endif
+
 static void pthread_initialize(void)
 {
   struct sigaction sa;
@@ -551,6 +589,10 @@ static void pthread_initialize(void)
   *__libc_dl_error_tsd () = *(*GL(dl_error_catch_tsd)) ();
   GL(dl_error_catch_tsd) = &__libc_dl_error_tsd;
 #endif
+
+#ifdef USE_TLS
+  GL(dl_init_static_tls) = &__pthread_init_static_tls;
+#endif
 }
 
 void __pthread_initialize(void)
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 45037eb615..d79e7988fd 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,19 @@
+2003-07-30  Jakub Jelinek  <jakub@redhat.com>
+
+	* init.c (__pthread_initialize_minimal_internal): Initialize
+	GL(dl_init_static_tls).
+	* pthreadP.h (__pthread_init_static_tls): New prototype.
+	* allocatestack.c (init_one_static_tls, __pthread_init_static_tls):
+	New functions.
+	* Makefile (tests): Add tst-tls4.
+	(modules-names): Add tst-tls4moda and tst-tls4modb.
+	($(objpfx)tst-tls4): Link against libdl and libpthread.
+	($(objpfx)tst-tls4.out): Depend on tst-tls4moda.so and
+	tst-tls4modb.so.
+	* tst-tls4.c: New file.
+	* tst-tls4moda.c: New file.
+	* tst-tls4modb.c: New file.
+
 2003-06-19  Daniel Jacobowitz  <drow@mvista.com>
 
 	* sysdeps/pthread/timer_create.c (timer_create): Call timer_delref
diff --git a/nptl/Makefile b/nptl/Makefile
index 6beacdc40b..cfb82e920b 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -251,10 +251,11 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
 	 tst-oncex3 tst-oncex4
 endif
 ifeq ($(build-shared),yes)
-tests += tst-atfork2 tst-tls3 tst-_res1
+tests += tst-atfork2 tst-tls3 tst-tls4 tst-_res1
 endif
 
-modules-names = tst-atfork2mod tst-tls3mod tst-_res1mod1 tst-_res1mod2
+modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
+		tst-_res1mod1 tst-_res1mod2
 extra-objs += $(addsuffix .os,$(strip $(modules-names)))
 test-extras += $(modules-names)
 test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
@@ -395,6 +396,9 @@ LDFLAGS-tst-tls3 = -rdynamic
 $(objpfx)tst-tls3.out: $(objpfx)tst-tls3mod.so
 $(objpfx)tst-tls3mod.so: $(shared-thread-library)
 
+$(objpfx)tst-tls4: $(libdl) $(shared-thread-library)
+$(objpfx)tst-tls4.out: $(objpfx)tst-tls4moda.so $(objpfx)tst-tls4modb.so
+
 $(objpfx)tst-dlsym1: $(libdl) $(shared-thread-library)
 
 ifeq (yes,$(build-shared))
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index e042cf4840..729f3b8542 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -710,3 +710,41 @@ __find_thread_by_id (pid_t tid)
   return result;
 }
 #endif
+
+static inline void __attribute__((always_inline))
+init_one_static_tls (struct pthread *curp, struct link_map *map)
+{
+  dtv_t *dtv = GET_DTV (TLS_TPADJ (curp));
+# if TLS_TCB_AT_TP
+  void *dest = (char *) curp - map->l_tls_offset;
+# elif TLS_DTV_AT_TP
+  void *dest = (char *) curp + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+# else
+#  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+  /* Fill in the DTV slot so that a later LD/GD access will find it.  */
+  dtv[map->l_tls_modid].pointer = dest;
+
+  /* Initialize the memory.  */
+  memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
+	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
+}
+
+void
+attribute_hidden
+__pthread_init_static_tls (struct link_map *map)
+{
+  lll_lock (stack_cache_lock);
+
+  /* Iterate over the list with system-allocated threads first.  */
+  list_t *runp;
+  list_for_each (runp, &stack_used)
+    init_one_static_tls (list_entry (runp, struct pthread, list), map);
+
+  /* Now the list with threads using user-allocated stacks.  */
+  list_for_each (runp, &__stack_user)
+    init_one_static_tls (list_entry (runp, struct pthread, list), map);  
+
+  lll_unlock (stack_cache_lock);
+}
diff --git a/nptl/init.c b/nptl/init.c
index 9c76773504..dae24f1eac 100644
--- a/nptl/init.c
+++ b/nptl/init.c
@@ -272,6 +272,8 @@ __pthread_initialize_minimal_internal (void)
   GL(dl_error_catch_tsd) = &__libc_dl_error_tsd;
 #endif
 
+  GL(dl_init_static_tls) = &__pthread_init_static_tls;
+
   /* Register the fork generation counter with the libc.  */
 #ifndef TLS_MULTIPLE_THREADS_IN_TCB
   __libc_multiple_threads_ptr =
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index a8cefabc4c..b07c6da961 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -257,6 +257,8 @@ extern int *__libc_multiple_threads_ptr attribute_hidden;
 /* Find a thread given its TID.  */
 extern struct pthread *__find_thread_by_id (pid_t tid) attribute_hidden;
 
+extern void __pthread_init_static_tls (struct link_map *) attribute_hidden;
+
 
 /* Namespace save aliases.  */
 extern int __pthread_getschedparam (pthread_t thread_id, int *policy,
diff --git a/nptl/tst-tls4.c b/nptl/tst-tls4.c
new file mode 100644
index 0000000000..52775dee8c
--- /dev/null
+++ b/nptl/tst-tls4.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <tls.h>
+
+#if HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE
+
+#define N 3
+
+void (*test1) (void), (*test2) (void);
+
+pthread_barrier_t b2, b3;
+
+static void *
+tf (void *arg)
+{
+  int i;
+
+  for (i = 0; i <= (uintptr_t) arg; ++i)
+    {
+      int r = pthread_barrier_wait (&b3);
+      if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  puts ("tf: barrier_wait failed");
+	  exit (1);
+	}
+    }
+
+  test1 ();
+
+  for (i = 0; i < 3; ++i)
+    {
+      int r = pthread_barrier_wait (&b3);
+      if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  puts ("tf: barrier_wait failed");
+	  exit (1);
+	}
+    }
+
+  test2 ();
+
+  for (i = 0; i < 3 - (uintptr_t) arg; ++i)
+    {
+      int r = pthread_barrier_wait (&b3);
+      if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+	{
+	  puts ("tf: barrier_wait failed");
+	  exit (1);
+	}
+    }
+
+  return NULL;
+}
+
+static void *
+tf2 (void *arg)
+{
+  int r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("tf2: barrier_wait failed");
+      exit (1);
+    }
+
+  int i;
+  for (i = 0; i < N; ++i)
+    tf (arg);
+  return NULL;
+}
+
+int
+do_test (void)
+{
+  pthread_t th[2];
+  const char *modules[N]
+    = { "tst-tls4moda.so", "tst-tls4moda.so", "tst-tls4modb.so" };
+
+  if (pthread_barrier_init (&b2, NULL, 2) != 0)
+    {
+      puts ("barrier_init failed");
+      return 1;
+    }
+
+  if (pthread_barrier_init (&b3, NULL, 3) != 0)
+    {
+      puts ("barrier_init failed");
+      return 1;
+    }
+
+  if (pthread_create (&th[0], NULL, tf2, (void *) (uintptr_t) 1))
+    {
+      puts ("pthread_create failed");
+      return 1;
+    }
+
+  int r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("barrier_wait failed");
+      return 1;
+    }
+
+  int i;
+  for (i = 0; i < N; ++i)
+    {
+      void *h = dlopen (modules[i], RTLD_LAZY);
+      if (h == NULL)
+	{
+	  printf ("dlopen failed %s\n", dlerror ());
+	  return 1;
+	}
+
+      test1 = dlsym (h, "test1");
+      if (test1 == NULL)
+	{
+	  printf ("dlsym for test1 failed %s\n", dlerror ());
+	  return 1;
+	}
+
+      test2 = dlsym (h, "test2");
+      if (test2 == NULL)
+	{
+	  printf ("dlsym for test2 failed %s\n", dlerror ());
+	  return 1;
+	}
+
+      if (pthread_create (&th[1], NULL, tf, (void *) (uintptr_t) 2))
+	{
+	  puts ("pthread_create failed");
+	  return 1;
+	}
+
+      tf ((void *) (uintptr_t) 0);
+
+      if (pthread_join (th[1], NULL) != 0)
+	{
+	  puts ("join failed");
+	  return 1;
+	}
+
+      if (dlclose (h))
+	{
+	  puts ("dlclose failed");
+	  return 1;
+	}
+
+      printf ("test %d with %s succeeded\n", i, modules[i]);
+    }
+
+  if (pthread_join (th[0], NULL) != 0)
+    {
+      puts ("join failed");
+      return 1;
+    }
+
+  return 0;
+}
+
+#define TIMEOUT 5
+#define TEST_FUNCTION do_test ()
+
+#else
+
+#define TEST_FUNCTION 0
+
+#endif
+
+#include "../test-skeleton.c"
diff --git a/nptl/tst-tls4moda.c b/nptl/tst-tls4moda.c
new file mode 100644
index 0000000000..ff7ee56042
--- /dev/null
+++ b/nptl/tst-tls4moda.c
@@ -0,0 +1,56 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <tls.h>
+
+#if HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE
+
+static __thread unsigned char foo [32]
+  __attribute__ ((tls_model ("initial-exec"), aligned (sizeof (void *))));
+
+void
+test1 (void)
+{
+  size_t s;
+
+  for (s = 0; s < sizeof (foo); ++s)
+    {
+      if (foo [s])
+        abort ();
+      foo [s] = s;
+    }
+}
+
+void
+test2 (void)
+{
+  size_t s;
+
+  for (s = 0; s < sizeof (foo); ++s)
+    {
+      if (foo [s] != s)
+        abort ();
+      foo [s] = sizeof (foo) - s;
+    }
+}
+
+#endif
diff --git a/nptl/tst-tls4modb.c b/nptl/tst-tls4modb.c
new file mode 100644
index 0000000000..99f3b5405f
--- /dev/null
+++ b/nptl/tst-tls4modb.c
@@ -0,0 +1,65 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <tls.h>
+
+#if HAVE___THREAD && defined HAVE_TLS_MODEL_ATTRIBUTE
+
+static int i;
+int bar;
+
+static __thread void *foo [32 / sizeof (void *)]
+  __attribute__ ((tls_model ("initial-exec"), aligned (sizeof (void *))))
+  = { &i, &bar };
+
+void
+test1 (void)
+{
+  size_t s;
+
+  if (foo [0] != &i || foo [1] != &bar)
+    abort ();
+
+  foo [0] = NULL;
+  foo [1] = NULL;
+  for (s = 0; s < sizeof (foo) / sizeof (void *); ++s)
+    {
+      if (foo [s])
+        abort ();
+      foo [s] = &foo[s];
+    }
+}
+
+void
+test2 (void)
+{
+  size_t s;
+
+  for (s = 0; s < sizeof (foo) / sizeof (void *); ++s)
+    {
+      if (foo [s] != &foo [s])
+        abort ();
+      foo [s] = &foo [s ^ 1];
+    }
+}
+
+#endif
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 565edb3fd8..b647ea45e7 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -390,6 +390,8 @@ struct rtld_global
   EXTERN void *_dl_initial_dtv;
   /* Generation counter for the dtv.  */
   EXTERN size_t _dl_tls_generation;
+
+  EXTERN void (*_dl_init_static_tls) (struct link_map *);
 #endif
 
 #ifdef NEED_DL_SYSINFO
@@ -797,7 +799,7 @@ rtld_hidden_proto (_dl_allocate_tls)
 extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp)
      internal_function;
 
-extern int _dl_allocate_static_tls (struct link_map *map)
+extern void _dl_allocate_static_tls (struct link_map *map)
      internal_function attribute_hidden;
 
 /* These are internal entry points to the two halves of _dl_allocate_tls,
@@ -816,6 +818,10 @@ rtld_hidden_proto (_dl_deallocate_tls)
 extern void *_dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
      internal_function;
 
+#if defined USE_TLS
+extern void _dl_nothread_init_static_tls (struct link_map *) attribute_hidden;
+#endif
+
 __END_DECLS
 
 #endif /* ldsodefs.h */