[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#854007: libc6: crash during thread exit when using thread local storage



Package: libc6
Version: 2.19-18+deb8u7
Severity: normal
Tags: upstream patch

Dear Maintainer,

An application we develop crashes on exit with:
*** Error in `foo': free(): invalid pointer: 0x0000000009309bc0 ***

This issue occurs when there are a large number of threads running which use
thread local storage.  We have identified the issue as an existing upstream
glibc bug, #13862.  This bug was fixed in glibc-2.21.  See
https://sourceware.org/bugzilla/show_bug.cgi?id=13862.  The upstream bug report
has a reproducer which reliably reproduces the problem.

I have backported the upstream patch to glibc 2.19, based on the patch made by
Red Hat and CentOS for their bug
https://bugzilla.redhat.com/show_bug.cgi?id=1238778.  I additionally applied the
patch for upstream TLS-related glibc bugs 17090, 17620, 17621, and 17628, which
is the same set of patches Red Hat backported for their fix to glibc-2.17.

After rebuilding glibc-2.19-18+deb8u7 with the attached patches, the issue no
longer occurs.  Given that Debian 8 still has several years of support left, can
these patches be added to Debian's build?

Thanks,
Mike

-- System Information:
Debian Release: 8.3
  APT prefers stable
  APT policy: (500, 'stable'), (300, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386, armhf

Kernel: Linux 4.4.0-0.bpo.tmw1-amd64 (SMP w/12 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
>From 439e927dd88e2cd0d2b4b956d5db4f4a13b9f8d7 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 28 Nov 2014 07:54:07 -0800
Subject: [PATCH] Resize DTV if the current DTV isn't big enough

This patch changes _dl_allocate_tls_init to resize DTV if the current DTV
isn't big enough.  Tested on X86-64, x32 and ia32.

	[BZ #13862]
	* elf/dl-tls.c: Include <atomic.h>.
	(oom): Remove #ifdef SHARED/#endif.
	(_dl_static_dtv, _dl_initial_dtv): Moved before ...
	(_dl_resize_dtv): This.  Extracted from _dl_update_slotinfo.
	(_dl_allocate_tls_init): Resize DTV if the current DTV isn't
	big enough.
	(_dl_update_slotinfo): Call _dl_resize_dtv to resize DTV.
	* nptl/Makefile (tests): Add tst-stack4.
	(modules-names): Add tst-stack4mod.
	($(objpfx)tst-stack4): New.
	(tst-stack4mod.sos): Likewise.
	($(objpfx)tst-stack4.out): Likewise.
	($(tst-stack4mod.sos)): Likewise.
	(clean): Likewise.
	* nptl/tst-stack4.c: New file.
	* nptl/tst-stack4mod.c: Likewise.

(cherry picked from commit d8dd00805b8f3a011735d7a407097fb1c408d867)
* Modified to avoid use of atomic_load_acquire, based on RH glibc
  patch glibc-rh1189278.patch.
---
 ChangeLog            |  20 +++++++
 elf/dl-tls.c         | 105 +++++++++++++++++++++-------------
 nptl/Makefile        |  17 +++++-
 nptl/tst-stack4.c    | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++
 nptl/tst-stack4mod.c |  28 +++++++++
 5 files changed, 286 insertions(+), 43 deletions(-)
 create mode 100644 nptl/tst-stack4.c
 create mode 100644 nptl/tst-stack4mod.c

diff --git a/ChangeLog b/ChangeLog
index 92b8a2e..a1861d1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2014-11-28  H.J. Lu  <hongjiu.lu@intel.com>
+
+	[BZ #13862]
+	* elf/dl-tls.c: Include <atomic.h>.
+	(oom): Remove #ifdef SHARED/#endif.
+	(_dl_static_dtv, _dl_initial_dtv): Moved before ...
+	(_dl_resize_dtv): This.  Extracted from _dl_update_slotinfo.
+	(_dl_allocate_tls_init): Resize DTV if the current DTV isn't
+	big enough.
+	(_dl_update_slotinfo): Call _dl_resize_dtv to resize DTV.
+	* nptl/Makefile (tests): Add tst-stack4.
+	(modules-names): Add tst-stack4mod.
+	($(objpfx)tst-stack4): New.
+	(tst-stack4mod.sos): Likewise.
+	($(objpfx)tst-stack4.out): Likewise.
+	($(tst-stack4mod.sos)): Likewise.
+	(clean): Likewise.
+	* nptl/tst-stack4.c: New file.
+	* nptl/tst-stack4mod.c: Likewise.
+
 2015-01-28  Adhemerval Zanellla  <azanella@linux.vnet.ibm.com>
 
 	[BZ #16576]
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index dbaea0a..c148c54 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/param.h>
+#include <atomic.h>
 
 #include <tls.h>
 #include <dl-tls.h>
@@ -34,14 +35,12 @@
 
 
 /* Out-of-memory handler.  */
-#ifdef SHARED
 static void
 __attribute__ ((__noreturn__))
 oom (void)
 {
   _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n");
 }
-#endif
 
 
 size_t
@@ -370,6 +369,56 @@ _dl_allocate_tls_storage (void)
 }
 
 
+#ifndef SHARED
+extern dtv_t _dl_static_dtv[];
+# define _dl_initial_dtv (&_dl_static_dtv[1])
+#endif
+
+static dtv_t *
+_dl_resize_dtv (dtv_t *dtv)
+{
+  /* Resize the dtv.  */
+  dtv_t *newp;
+  /* Load GL(dl_tls_max_dtv_idx) atomically since it may be written to by
+     other threads concurrently.  -- We don't have the required atomic
+     infrastructure to load dl_tls_max_dtv_idx atomically, but on all the
+     architectures we care about it should load atomically. If this had
+     an atomic_load_acquire we would still be missing the releases for
+     the writes.  */
+  size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
+  size_t oldsize = dtv[-1].counter;
+
+  if (dtv == GL(dl_initial_dtv))
+    {
+      /* This is the initial dtv that was either statically allocated in
+	 __libc_setup_tls or allocated during rtld startup using the
+	 dl-minimal.c malloc instead of the real malloc.  We can't free
+	 it, we have to abandon the old storage.  */
+
+      newp = malloc ((2 + newsize) * sizeof (dtv_t));
+      if (newp == NULL)
+	oom ();
+      memcpy (newp, &dtv[-1], (2 + oldsize) * sizeof (dtv_t));
+    }
+  else
+    {
+      newp = realloc (&dtv[-1],
+		      (2 + newsize) * sizeof (dtv_t));
+      if (newp == NULL)
+	oom ();
+    }
+
+  newp[0].counter = newsize;
+
+  /* Clear the newly allocated part.  */
+  memset (newp + 2 + oldsize, '\0',
+	  (newsize - oldsize) * sizeof (dtv_t));
+
+  /* Return the generation counter.  */
+  return &newp[1];
+}
+
+
 void *
 internal_function
 _dl_allocate_tls_init (void *result)
@@ -383,6 +432,16 @@ _dl_allocate_tls_init (void *result)
   size_t total = 0;
   size_t maxgen = 0;
 
+  /* Check if the current dtv is big enough.   */
+  if (dtv[-1].counter < GL(dl_tls_max_dtv_idx))
+    {
+      /* Resize the dtv.  */
+      dtv = _dl_resize_dtv (dtv);
+
+      /* Install this new dtv in the thread data structures.  */
+      INSTALL_DTV (result, &dtv[-1]);
+    }
+
   /* We have to prepare the dtv for all currently loaded modules using
      TLS.  For those which are dynamically loaded we add the values
      indicating deferred allocation.  */
@@ -464,11 +523,6 @@ _dl_allocate_tls (void *mem)
 rtld_hidden_def (_dl_allocate_tls)
 
 
-#ifndef SHARED
-extern dtv_t _dl_static_dtv[];
-# define _dl_initial_dtv (&_dl_static_dtv[1])
-#endif
-
 void
 internal_function
 _dl_deallocate_tls (void *tcb, bool dealloc_tcb)
@@ -617,41 +671,10 @@ _dl_update_slotinfo (unsigned long int req_modid)
 	      assert (total + cnt == modid);
 	      if (dtv[-1].counter < modid)
 		{
-		  /* Reallocate the dtv.  */
-		  dtv_t *newp;
-		  size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
-		  size_t oldsize = dtv[-1].counter;
-
-		  assert (map->l_tls_modid <= newsize);
-
-		  if (dtv == GL(dl_initial_dtv))
-		    {
-		      /* This is the initial dtv that was allocated
-			 during rtld startup using the dl-minimal.c
-			 malloc instead of the real malloc.  We can't
-			 free it, we have to abandon the old storage.  */
-
-		      newp = malloc ((2 + newsize) * sizeof (dtv_t));
-		      if (newp == NULL)
-			oom ();
-		      memcpy (newp, &dtv[-1], (2 + oldsize) * sizeof (dtv_t));
-		    }
-		  else
-		    {
-		      newp = realloc (&dtv[-1],
-				      (2 + newsize) * sizeof (dtv_t));
-		      if (newp == NULL)
-			oom ();
-		    }
-
-		  newp[0].counter = newsize;
-
-		  /* Clear the newly allocated part.  */
-		  memset (newp + 2 + oldsize, '\0',
-			  (newsize - oldsize) * sizeof (dtv_t));
+		  /* Resize the dtv.  */
+		  dtv = _dl_resize_dtv (dtv);
 
-		  /* Point dtv to the generation counter.  */
-		  dtv = &newp[1];
+		  assert (modid <= dtv[-1].counter);
 
 		  /* Install this new dtv in the thread data
 		     structures.  */
diff --git a/nptl/Makefile b/nptl/Makefile
index aa4a444..d11b0a1 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -252,7 +252,7 @@ tests = tst-typesizes \
 	tst-exec1 tst-exec2 tst-exec3 tst-exec4 \
 	tst-exit1 tst-exit2 tst-exit3 \
 	tst-stdio1 tst-stdio2 \
-	tst-stack1 tst-stack2 tst-stack3 tst-pthread-getattr \
+	tst-stack1 tst-stack2 tst-stack3 tst-stack4 tst-pthread-getattr \
 	tst-pthread-attr-affinity \
 	tst-unload \
 	tst-dlsym1 \
@@ -303,7 +303,7 @@ endif
 
 modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
 		tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
-		tst-tls5modd tst-tls5mode tst-tls5modf \
+		tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
 		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod
 extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) tst-cleanup4aux.o
 test-extras += $(modules-names) tst-cleanup4aux
@@ -465,6 +465,19 @@ $(objpfx)tst-stack3-mem: $(objpfx)tst-stack3.out
 	$(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@
 generated += tst-stack3-mem tst-stack3.mtrace
 
+$(objpfx)tst-stack4: $(libdl) $(shared-thread-library)
+tst-stack4mod.sos=$(shell for i in 0 1 2 3 4 5 6 7 8 9 10 \
+				   11 12 13 14 15 16 17 18 19; do \
+			    for j in 0 1 2 3 4 5 6 7 8 9 10 \
+				     11 12 13 14 15 16 17 18 19; do \
+			      echo $(objpfx)tst-stack4mod-$$i-$$j.so; \
+			    done; done)
+$(objpfx)tst-stack4.out: $(tst-stack4mod.sos)
+$(tst-stack4mod.sos): $(objpfx)tst-stack4mod.so
+	cp -f $< $@
+clean:
+	rm -f $(tst-stack4mod.sos)
+
 $(objpfx)tst-cleanup4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library)
 $(objpfx)tst-cleanupx4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library)
 
diff --git a/nptl/tst-stack4.c b/nptl/tst-stack4.c
new file mode 100644
index 0000000..d9c8df2
--- /dev/null
+++ b/nptl/tst-stack4.c
@@ -0,0 +1,159 @@
+/* Test DTV size oveflow when pthread_create reuses old DTV and TLS is
+   used by dlopened shared object.
+   Copyright (C) 2014 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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <pthread.h>
+
+/* The choices of thread count, and file counts are arbitary.
+   The point is simply to run enough threads that an exiting
+   thread has it's stack reused by another thread at the same
+   time as new libraries have been loaded.  */
+#define DSO_SHARED_FILES 20
+#define DSO_OPEN_THREADS 20
+#define DSO_EXEC_THREADS 2
+
+/* Used to make sure that only one thread is calling dlopen and dlclose
+   at a time.  */
+pthread_mutex_t g_lock;
+
+typedef void (*function) (void);
+
+void *
+dso_invoke(void *dso_fun)
+{
+  function *fun_vec = (function *) dso_fun;
+  int dso;
+
+  for (dso = 0; dso < DSO_SHARED_FILES; dso++)
+    (*fun_vec[dso]) ();
+
+  pthread_exit (NULL);
+}
+
+void *
+dso_process (void * p)
+{
+  void *handle[DSO_SHARED_FILES];
+  function fun_vec[DSO_SHARED_FILES];
+  char dso_path[DSO_SHARED_FILES][100];
+  int dso;
+  uintptr_t t = (uintptr_t) p;
+
+  /* Open DSOs and get a function.  */
+  for (dso = 0; dso < DSO_SHARED_FILES; dso++)
+    {
+      sprintf (dso_path[dso], "tst-stack4mod-%i-%i.so", t, dso);
+
+      pthread_mutex_lock (&g_lock);
+
+      handle[dso] = dlopen (dso_path[dso], RTLD_NOW);
+      assert (handle[dso]);
+
+      fun_vec[dso] = (function) dlsym (handle[dso], "function");
+      assert (fun_vec[dso]);
+
+      pthread_mutex_unlock (&g_lock);
+    }
+
+  /* Spawn workers.  */
+  pthread_t thread[DSO_EXEC_THREADS];
+  int i, ret;
+  uintptr_t result = 0;
+  for (i = 0; i < DSO_EXEC_THREADS; i++)
+    {
+      pthread_mutex_lock (&g_lock);
+      ret = pthread_create (&thread[i], NULL, dso_invoke, (void *) fun_vec);
+      if (ret != 0)
+	{
+	  printf ("pthread_create failed: %d\n", ret);
+	  result = 1;
+	}
+      pthread_mutex_unlock (&g_lock);
+    }
+
+  if (!result)
+    for (i = 0; i < DSO_EXEC_THREADS; i++)
+      {
+	ret = pthread_join (thread[i], NULL);
+	if (ret != 0)
+	  {
+	    printf ("pthread_join failed: %d\n", ret);
+	    result = 1;
+	  }
+      }
+
+  /* Close all DSOs.  */
+  for (dso = 0; dso < DSO_SHARED_FILES; dso++)
+    {
+      pthread_mutex_lock (&g_lock);
+      dlclose (handle[dso]);
+      pthread_mutex_unlock (&g_lock);
+    }
+
+  /* Exit.  */
+  pthread_exit ((void *) result);
+}
+
+static int
+do_test (void)
+{
+  pthread_t thread[DSO_OPEN_THREADS];
+  int i,j;
+  int ret;
+  int result = 0;
+
+  pthread_mutex_init (&g_lock, NULL);
+
+  /* 100 is arbitrary here and is known to trigger PR 13862.  */
+  for (j = 0; j < 100; j++)
+    {
+      for (i = 0; i < DSO_OPEN_THREADS; i++)
+	{
+	  ret = pthread_create (&thread[i], NULL, dso_process,
+				(void *) (uintptr_t) i);
+	  if (ret != 0)
+	    {
+	      printf ("pthread_create failed: %d\n", ret);
+	      result = 1;
+	    }
+	}
+
+      if (result)
+	break;
+
+      for (i = 0; i < DSO_OPEN_THREADS; i++)
+	{
+	  ret = pthread_join (thread[i], NULL);
+	  if (ret != 0)
+	    {
+	      printf ("pthread_join failed: %d\n", ret);
+	      result = 1;
+	    }
+	}
+    }
+
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#define TIMEOUT 100
+#include "../test-skeleton.c"
diff --git a/nptl/tst-stack4mod.c b/nptl/tst-stack4mod.c
new file mode 100644
index 0000000..a75aa09
--- /dev/null
+++ b/nptl/tst-stack4mod.c
@@ -0,0 +1,28 @@
+/* This tests DTV usage with TLS in dlopened shared object.
+   Copyright (C) 2014 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
+   <http://www.gnu.org/licenses/>.  */
+
+/* 256 is arbitrary here and is known to trigger PR 13862.  */
+__thread int var[256] attribute_hidden = {0};
+
+void
+function (void)
+{
+  int i;
+  for (i = 0; i < sizeof (var) / sizeof (int); i++)
+    var[i] = i;
+}
-- 
2.1.4

>From 7ab0d319cc20bc71037d98daf99e655fa58eb5db Mon Sep 17 00:00:00 2001
From: Alexandre Oliva <aoliva@redhat.com>
Date: Tue, 17 Mar 2015 01:14:11 -0300
Subject: [PATCH] Fix DTV race, assert, DTV_SURPLUS Static TLS limit, and
 nptl_db garbage

for  ChangeLog

	[BZ #17090]
	[BZ #17620]
	[BZ #17621]
	[BZ #17628]
	* NEWS: Update.
	* elf/dl-tls.c (_dl_update_slotinfo): Clean up outdated DTV
	entries with Static TLS too.  Skip entries past the end of the
	allocated DTV, from Alan Modra.
	(tls_get_addr_tail): Update to glibc_likely/unlikely.  Move
	Static TLS DTV entry set up from...
	 (_dl_allocate_tls_init): ... here (fix modid assertion), ...
	* elf/dl-reloc.c (_dl_nothread_init_static_tls): ... here...
	* nptl/allocatestack.c (init_one_static_tls): ... and here...
	* elf/dlopen.c (dl_open_worker): Drop l_tls_modid upper bound
	for Static TLS.
	* elf/tlsdeschtab.h (map_generation): Return size_t.  Check
	that the slot we find is associated with the given map before
	using its generation count.
	* nptl_db/db_info.c: Include ldsodefs.h.
	(rtld_global, dtv_slotinfo_list, dtv_slotinfo): New typedefs.
	* nptl_db/structs.def (DB_RTLD_VARIABLE): New macro.
	(DB_MAIN_VARIABLE, DB_RTLD_GLOBAL_FIELD): Likewise.
	(link_map::l_tls_offset): New struct field.
	(dtv_t::counter): Likewise.
	(rtld_global): New struct.
	(_rtld_global): New rtld variable.
	(dl_tls_dtv_slotinfo_list): New rtld global field.
	(dtv_slotinfo_list): New struct.
	(dtv_slotinfo): Likewise.
	* nptl_db/td_symbol_list.c: Drop gnu/lib-names.h include.
	(td_lookup): Rename to...
	(td_mod_lookup): ... this.  Use new mod parameter instead of
	LIBPTHREAD_SO.
	* nptl_db/td_thr_tlsbase.c: Include link.h.
	(dtv_slotinfo_list, dtv_slotinfo): New functions.
	(td_thr_tlsbase): Check DTV generation.  Compute Static TLS
	addresses even if the DTV is out of date or missing them.
	* nptl_db/fetch-value.c (_td_locate_field): Do not refuse to
	index zero-length arrays.
	* nptl_db/thread_dbP.h: Include gnu/lib-names.h.
	(td_lookup): Make it a macro implemented in terms of...
	(td_mod_lookup): ... this declaration.
	* nptl_db/db-symbols.awk (DB_RTLD_VARIABLE): Override.
	(DB_MAIN_VARIABLE): Likewise.

(cherry picked from commit f8aeae347377f3dfa8cbadde057adf1827fb1d44)
---
 ChangeLog                |  47 +++++++++++++
 elf/dl-open.c            |  12 +---
 elf/dl-reloc.c           |   6 --
 elf/dl-tls.c             |  62 ++++++++---------
 elf/tlsdeschtab.h        |   4 +-
 nptl/allocatestack.c     |   9 +--
 nptl_db/db-symbols.awk   |   2 +
 nptl_db/db_info.c        |   4 ++
 nptl_db/fetch-value.c    |   3 +-
 nptl_db/structs.def      |  39 +++++++++++
 nptl_db/td_symbol_list.c |   7 +-
 nptl_db/td_thr_tlsbase.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++-
 nptl_db/thread_dbP.h     |  11 +--
 13 files changed, 310 insertions(+), 68 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a1861d1..0f270e8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,50 @@
+2015-03-17  Alexandre Oliva <aoliva@redhat.com>
+
+	[BZ #17090]
+	[BZ #17620]
+	[BZ #17621]
+	[BZ #17628]
+	* NEWS: Update.
+	* elf/dl-tls.c (_dl_update_slotinfo): Clean up outdated DTV
+	entries with Static TLS too.  Skip entries past the end of the
+	allocated DTV, from Alan Modra.
+	(tls_get_addr_tail): Update to glibc_likely/unlikely.  Move
+	Static TLS DTV entry set up from...
+	(_dl_allocate_tls_init): ... here (fix modid assertion), ...
+	* elf/dl-reloc.c (_dl_nothread_init_static_tls): ... here...
+	* nptl/allocatestack.c (init_one_static_tls): ... and here...
+	* elf/dlopen.c (dl_open_worker): Drop l_tls_modid upper bound
+	for Static TLS.
+	* elf/tlsdeschtab.h (map_generation): Return size_t.  Check
+	that the slot we find is associated with the given map before
+	using its generation count.
+	* nptl_db/db_info.c: Include ldsodefs.h.
+	(rtld_global, dtv_slotinfo_list, dtv_slotinfo): New typedefs.
+	* nptl_db/structs.def (DB_RTLD_VARIABLE): New macro.
+	(DB_MAIN_VARIABLE, DB_RTLD_GLOBAL_FIELD): Likewise.
+	(link_map::l_tls_offset): New struct field.
+	(dtv_t::counter): Likewise.
+	(rtld_global): New struct.
+	(_rtld_global): New rtld variable.
+	(dl_tls_dtv_slotinfo_list): New rtld global field.
+	(dtv_slotinfo_list): New struct.
+	(dtv_slotinfo): Likewise.
+	* nptl_db/td_symbol_list.c: Drop gnu/lib-names.h include.
+	(td_lookup): Rename to...
+	(td_mod_lookup): ... this.  Use new mod parameter instead of
+	LIBPTHREAD_SO.
+	* nptl_db/td_thr_tlsbase.c: Include link.h.
+	(dtv_slotinfo_list, dtv_slotinfo): New functions.
+	(td_thr_tlsbase): Check DTV generation.  Compute Static TLS
+	addresses even if the DTV is out of date or missing them.
+	* nptl_db/fetch-value.c (_td_locate_field): Do not refuse to
+	index zero-length arrays.
+	* nptl_db/thread_dbP.h: Include gnu/lib-names.h.
+	(td_lookup): Make it a macro implemented in terms of...
+	(td_mod_lookup): ... this declaration.
+	* nptl_db/db-symbols.awk (DB_RTLD_VARIABLE): Override.
+	(DB_MAIN_VARIABLE): Likewise.
+
 2014-11-28  H.J. Lu  <hongjiu.lu@intel.com>
 
 	[BZ #13862]
diff --git a/elf/dl-open.c b/elf/dl-open.c
index a9ca6b3..eeeac13 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -531,17 +531,7 @@ TLS generation counter wrapped!  Please report this."));
 	  && imap->l_tls_blocksize > 0)
 	{
 	  /* For static TLS we have to allocate the memory here and
-	     now.  This includes allocating memory in the DTV.  But we
-	     cannot change any DTV other than our own.  So, if we
-	     cannot guarantee that there is room in the DTV we don't
-	     even try it and fail the load.
-
-	     XXX We could track the minimum DTV slots allocated in
-	     all threads.  */
-	  if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS)
-	    _dl_signal_error (0, "dlopen", NULL, N_("\
-cannot load any more object with static TLS"));
-
+	     now, but we can delay updating the DTV.  */
 	  imap->l_need_tls_init = 0;
 #ifdef SHARED
 	  /* Update the slot information data for at least the
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1f66fcc..d09ee41 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -136,12 +136,6 @@ _dl_nothread_init_static_tls (struct link_map *map)
 # 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_t *dtv = THREAD_DTV ();
-  assert (map->l_tls_modid <= dtv[-1].counter);
-  dtv[map->l_tls_modid].pointer.val = dest;
-  dtv[map->l_tls_modid].pointer.is_static = true;
-
   /* 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);
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index c148c54..4bcc1a9 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -468,17 +468,14 @@ _dl_allocate_tls_init (void *result)
 	     not be the generation counter.  */
 	  maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
 
+	  dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
+	  dtv[map->l_tls_modid].pointer.is_static = false;
+
 	  if (map->l_tls_offset == NO_TLS_OFFSET
 	      || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
-	    {
-	      /* For dynamically loaded modules we simply store
-		 the value indicating deferred allocation.  */
-	      dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
-	      dtv[map->l_tls_modid].pointer.is_static = false;
-	      continue;
-	    }
+	    continue;
 
-	  assert (map->l_tls_modid == cnt);
+	  assert (map->l_tls_modid == total + cnt);
 	  assert (map->l_tls_blocksize >= map->l_tls_initimage_size);
 #if TLS_TCB_AT_TP
 	  assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize);
@@ -490,8 +487,6 @@ _dl_allocate_tls_init (void *result)
 #endif
 
 	  /* Copy the initialization image and clear the BSS part.  */
-	  dtv[map->l_tls_modid].pointer.val = dest;
-	  dtv[map->l_tls_modid].pointer.is_static = true;
 	  memset (__mempcpy (dest, map->l_tls_initimage,
 			     map->l_tls_initimage_size), '\0',
 		  map->l_tls_blocksize - map->l_tls_initimage_size);
@@ -654,13 +649,16 @@ _dl_update_slotinfo (unsigned long int req_modid)
 	      struct link_map *map = listp->slotinfo[cnt].map;
 	      if (map == NULL)
 		{
-		  /* If this modid was used at some point the memory
-		     might still be allocated.  */
-		  if (! dtv[total + cnt].pointer.is_static
-		      && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+		  if (dtv[-1].counter >= total + cnt)
 		    {
-		      free (dtv[total + cnt].pointer.val);
+		      /* If this modid was used at some point the memory
+			 might still be allocated.  */
+		      if (! dtv[total + cnt].pointer.is_static
+			  && (dtv[total + cnt].pointer.val
+			      != TLS_DTV_UNALLOCATED))
+			free (dtv[total + cnt].pointer.val);
 		      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
+		      dtv[total + cnt].pointer.is_static = false;
 		    }
 
 		  continue;
@@ -693,10 +691,8 @@ _dl_update_slotinfo (unsigned long int req_modid)
 		   memalign and not malloc.  */
 		free (dtv[modid].pointer.val);
 
-	      /* This module is loaded dynamically- We defer memory
-		 allocation.  */
-	      dtv[modid].pointer.is_static = false;
 	      dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
+	      dtv[modid].pointer.is_static = false;
 
 	      if (modid == req_modid)
 		the_map = map;
@@ -734,13 +730,12 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
       the_map = listp->slotinfo[idx].map;
     }
 
- again:
   /* Make sure that, if a dlopen running in parallel forces the
      variable into static storage, we'll wait until the address in the
      static TLS block is set up, and use that.  If we're undecided
      yet, make sure we make the decision holding the lock as well.  */
-  if (__builtin_expect (the_map->l_tls_offset
-			!= FORCED_DYNAMIC_TLS_OFFSET, 0))
+  if (__glibc_unlikely (the_map->l_tls_offset
+			!= FORCED_DYNAMIC_TLS_OFFSET))
     {
       __rtld_lock_lock_recursive (GL(dl_load_lock));
       if (__builtin_expect (the_map->l_tls_offset == NO_TLS_OFFSET, 1))
@@ -748,22 +743,27 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
 	  the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET;
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
 	}
-      else
+      else if (__glibc_likely (the_map->l_tls_offset
+			       != FORCED_DYNAMIC_TLS_OFFSET))
 	{
+#if TLS_TCB_AT_TP
+	  void *p = (char *) THREAD_SELF - the_map->l_tls_offset;
+#elif TLS_DTV_AT_TP
+	  void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE;
+#else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+#endif
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
-	  if (__builtin_expect (the_map->l_tls_offset
-				!= FORCED_DYNAMIC_TLS_OFFSET, 1))
-	    {
-	      void *p = dtv[GET_ADDR_MODULE].pointer.val;
-	      if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0))
-		goto again;
+	  dtv[GET_ADDR_MODULE].pointer.is_static = true;
+	  dtv[GET_ADDR_MODULE].pointer.val = p;
 
-	      return (char *) p + GET_ADDR_OFFSET;
-	    }
+	  return (char *) p + GET_ADDR_OFFSET;
 	}
+      else
+	__rtld_lock_unlock_recursive (GL(dl_load_lock));
     }
   void *p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
-  dtv[GET_ADDR_MODULE].pointer.is_static = false;
+  assert (!dtv[GET_ADDR_MODULE].pointer.is_static);
 
   return (char *) p + GET_ADDR_OFFSET;
 }
diff --git a/elf/tlsdeschtab.h b/elf/tlsdeschtab.h
index 9a0f965..4f4eba3 100644
--- a/elf/tlsdeschtab.h
+++ b/elf/tlsdeschtab.h
@@ -42,7 +42,7 @@ eq_tlsdesc (void *p, void *q)
   return tdp->tlsinfo.ti_offset == tdq->tlsinfo.ti_offset;
 }
 
-inline static int
+inline static size_t
 map_generation (struct link_map *map)
 {
   size_t idx = map->l_tls_modid;
@@ -58,7 +58,7 @@ map_generation (struct link_map *map)
 	     we can assume that, if the generation count is zero, we
 	     still haven't determined the generation count for this
 	     module.  */
-	  if (listp->slotinfo[idx].gen)
+	  if (listp->slotinfo[idx].map == map && listp->slotinfo[idx].gen)
 	    return listp->slotinfo[idx].gen;
 	  else
 	    break;
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 13eca47..e0a0e62 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -1168,7 +1168,6 @@ __nptl_setxid (struct xid_command *cmdp)
 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
@@ -1177,11 +1176,9 @@ init_one_static_tls (struct pthread *curp, struct link_map *map)
 #  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.val = dest;
-  dtv[map->l_tls_modid].pointer.is_static = true;
-
-  /* Initialize the memory.  */
+  /* We cannot delay the initialization of the Static TLS area, since
+     it can be accessed with LE or IE, but since the DTV is only used
+     by GD and LD, we can delay its update to avoid a race.  */
   memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
 	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
 }
diff --git a/nptl_db/db-symbols.awk b/nptl_db/db-symbols.awk
index f9a91b9..eb089e1 100644
--- a/nptl_db/db-symbols.awk
+++ b/nptl_db/db-symbols.awk
@@ -2,6 +2,8 @@
 # we've just built.  It checks for all the symbols used in td_symbol_list.
 
 BEGIN {
+%define DB_RTLD_VARIABLE(name) /* Nothing. */
+%define DB_MAIN_VARIABLE(name) /* Nothing. */
 %define DB_LOOKUP_NAME(idx, name)		required[STRINGIFY (name)] = 1;
 %define DB_LOOKUP_NAME_TH_UNIQUE(idx, name)	th_unique[STRINGIFY (name)] = 1;
 %include "db-symbols.h"
diff --git a/nptl_db/db_info.c b/nptl_db/db_info.c
index 69cbcaa..807366b 100644
--- a/nptl_db/db_info.c
+++ b/nptl_db/db_info.c
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include "thread_dbP.h"
 #include <tls.h>
+#include <ldsodefs.h>
 
 typedef struct pthread pthread;
 typedef struct pthread_key_struct pthread_key_struct;
@@ -37,6 +38,9 @@ typedef struct
 } dtv;
 
 typedef struct link_map link_map;
+typedef struct rtld_global rtld_global;
+typedef struct dtv_slotinfo_list dtv_slotinfo_list;
+typedef struct dtv_slotinfo dtv_slotinfo;
 
 /* Actually static in nptl/init.c, but we only need it for typeof.  */
 extern bool __nptl_initial_report_events;
diff --git a/nptl_db/fetch-value.c b/nptl_db/fetch-value.c
index e0e1d4f..45ecb07 100644
--- a/nptl_db/fetch-value.c
+++ b/nptl_db/fetch-value.c
@@ -69,7 +69,8 @@ _td_locate_field (td_thragent_t *ta,
 	}
     }
 
-  if (idx != 0 && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc))
+  if (idx != 0 && DB_DESC_NELEM (desc) != 0
+      && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc))
     /* This is an internal indicator to callers with nonzero IDX
        that the IDX value is too big.  */
     return TD_NOAPLIC;
diff --git a/nptl_db/structs.def b/nptl_db/structs.def
index ec72ef1..a2aae23 100644
--- a/nptl_db/structs.def
+++ b/nptl_db/structs.def
@@ -22,6 +22,28 @@
 # define STRUCTS_DEF_DEFAULTS 1
 #endif
 
+#ifndef DB_RTLD_VARIABLE
+# define DB_RTLD_VARIABLE(name) DB_VARIABLE (name)
+#endif
+
+#ifndef DB_MAIN_VARIABLE
+# define DB_MAIN_VARIABLE(name) DB_VARIABLE (name)
+#endif
+
+#ifndef DB_RTLD_GLOBAL_FIELD
+# if !defined IS_IN_libpthread
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_STRUCT_FIELD (rtld_global, _##field)	\
+  DB_MAIN_VARIABLE (_##field)
+# elif defined SHARED
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_STRUCT_FIELD (rtld_global, _##field)
+# else
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_MAIN_VARIABLE (_##field)
+# endif
+#endif /* DB_RTLD_GLOBAL_FIELD */
+
 DB_STRUCT (pthread)
 DB_STRUCT_FIELD (pthread, list)
 DB_STRUCT_FIELD (pthread, report_events)
@@ -70,14 +92,31 @@ DB_STRUCT (pthread_key_data_level2)
 DB_STRUCT_ARRAY_FIELD (pthread_key_data_level2, data)
 
 DB_STRUCT_FIELD (link_map, l_tls_modid)
+DB_STRUCT_FIELD (link_map, l_tls_offset)
 
 DB_STRUCT_ARRAY_FIELD (dtv, dtv)
 #define pointer_val pointer.val /* Field of anonymous struct in dtv_t.  */
 DB_STRUCT_FIELD (dtv_t, pointer_val)
+DB_STRUCT_FIELD (dtv_t, counter)
 #if !defined IS_IN_libpthread || TLS_TCB_AT_TP
 DB_STRUCT_FIELD (pthread, dtvp)
 #endif
 
+#if !(defined IS_IN_libpthread && !defined SHARED)
+DB_STRUCT (rtld_global)
+DB_RTLD_VARIABLE (_rtld_global)
+#endif
+DB_RTLD_GLOBAL_FIELD (dl_tls_dtv_slotinfo_list)
+
+DB_STRUCT (dtv_slotinfo_list)
+DB_STRUCT_FIELD (dtv_slotinfo_list, len)
+DB_STRUCT_FIELD (dtv_slotinfo_list, next)
+DB_STRUCT_ARRAY_FIELD (dtv_slotinfo_list, slotinfo)
+
+DB_STRUCT (dtv_slotinfo)
+DB_STRUCT_FIELD (dtv_slotinfo, gen)
+DB_STRUCT_FIELD (dtv_slotinfo, map)
+
 #ifdef STRUCTS_DEF_DEFAULTS
 # undef DB_STRUCT_ARRAY_FIELD
 # undef DB_ARRAY_VARIABLE
diff --git a/nptl_db/td_symbol_list.c b/nptl_db/td_symbol_list.c
index c637444..d29234d 100644
--- a/nptl_db/td_symbol_list.c
+++ b/nptl_db/td_symbol_list.c
@@ -18,7 +18,6 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
-#include <gnu/lib-names.h>
 #include "thread_dbP.h"
 
 static const char *symbol_list_arr[] =
@@ -41,12 +40,12 @@ td_symbol_list (void)
 
 
 ps_err_e
-td_lookup (struct ps_prochandle *ps, int idx, psaddr_t *sym_addr)
+td_mod_lookup (struct ps_prochandle *ps, const char *mod,
+	       int idx, psaddr_t *sym_addr)
 {
   ps_err_e result;
   assert (idx >= 0 && idx < SYM_NUM_MESSAGES);
-  result = ps_pglobal_lookup (ps, LIBPTHREAD_SO, symbol_list_arr[idx],
-			      sym_addr);
+  result = ps_pglobal_lookup (ps, mod, symbol_list_arr[idx], sym_addr);
 
 #ifdef HAVE_ASM_GLOBAL_DOT_NAME
   /* For PowerPC, 64-bit uses dot symbols but 32-bit does not.
diff --git a/nptl_db/td_thr_tlsbase.c b/nptl_db/td_thr_tlsbase.c
index 217df1f..5ea56f8 100644
--- a/nptl_db/td_thr_tlsbase.c
+++ b/nptl_db/td_thr_tlsbase.c
@@ -17,14 +17,118 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include "thread_dbP.h"
+#include <link.h>
 
+/* Get the DTV slotinfo list head entry from the dynamic loader state
+   into *LISTHEAD.  */
+static td_err_e
+dtv_slotinfo_list (td_thragent_t *ta,
+		   psaddr_t *listhead)
+{
+  td_err_e err;
+  psaddr_t head;
+
+  if (ta->ta_addr__rtld_global == 0
+      && td_mod_lookup (ta->ph, LD_SO, SYM__rtld_global,
+			&ta->ta_addr__rtld_global) != PS_OK)
+    ta->ta_addr__rtld_global = (void*)-1;
+
+  if (ta->ta_addr__rtld_global != (void*)-1)
+    {
+      err = DB_GET_FIELD (head, ta, ta->ta_addr__rtld_global,
+			  rtld_global, _dl_tls_dtv_slotinfo_list, 0);
+      if (err != TD_OK)
+	return err;
+    }
+  else
+    {
+      if (ta->ta_addr__dl_tls_dtv_slotinfo_list == 0
+	  && td_mod_lookup (ta->ph, NULL, SYM__dl_tls_dtv_slotinfo_list,
+			    &ta->ta_addr__dl_tls_dtv_slotinfo_list) != PS_OK)
+	return TD_ERR;
+
+      err = _td_fetch_value (ta, ta->ta_var__dl_tls_dtv_slotinfo_list,
+			     SYM_DESC__dl_tls_dtv_slotinfo_list,
+			     0, ta->ta_addr__dl_tls_dtv_slotinfo_list, &head);
+      if (err != TD_OK)
+	return err;
+    }
+
+  *listhead = head;
+  return TD_OK;
+}
+
+/* Get the address of the DTV slotinfo entry for MODID into
+   *DTVSLOTINFO.  */
+static td_err_e
+dtv_slotinfo (td_thragent_t *ta,
+	      unsigned long int modid,
+	      psaddr_t *dtvslotinfo)
+{
+  td_err_e err;
+  psaddr_t slot, temp;
+  size_t slbase = 0;
+
+  err = dtv_slotinfo_list (ta, &slot);
+  if (err != TD_OK)
+    return err;
+
+  while (slot)
+    {
+      /* Get the number of entries in this list entry's array.  */
+      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list, len, 0);
+      if (err != TD_OK)
+	return err;
+      size_t len = (uintptr_t)temp;
+
+      /* Did we find the list entry for modid?  */
+      if (modid < slbase + len)
+	break;
+
+      /* We didn't, so get the next list entry.  */
+      slbase += len;
+      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list,
+			  next, 0);
+      if (err != TD_OK)
+	return err;
+      slot = temp;
+    }
+
+  /* We reached the end of the list and found nothing.  */
+  if (!slot)
+    return TD_ERR;
+
+  /* Take the slotinfo for modid from the list entry.  */
+  err = DB_GET_FIELD_ADDRESS (temp, ta, slot, dtv_slotinfo_list,
+			      slotinfo, modid - slbase);
+  if (err != TD_OK)
+    return err;
+  slot = temp;
+
+  *dtvslotinfo = slot;
+  return TD_OK;
+}
+
+/* Return in *BASE the base address of the TLS block for MODID within
+   TH.
+
+   It should return success and yield the correct pointer in any
+   circumstance where the TLS block for the module and thread
+   requested has already been initialized.
+
+   It should fail with TD_TLSDEFER only when the thread could not
+   possibly have observed any values in that TLS block.  That way, the
+   debugger can fall back to showing initial values from the PT_TLS
+   segment (and refusing attempts to mutate) for the TD_TLSDEFER case,
+   and never fail to make the values the program will actually see
+   available to the user of the debugger.  */
 td_err_e
 td_thr_tlsbase (const td_thrhandle_t *th,
 		unsigned long int modid,
 		psaddr_t *base)
 {
   td_err_e err;
-  psaddr_t dtv, dtvslot, dtvptr;
+  psaddr_t dtv, dtvslot, dtvptr, temp;
 
   if (modid < 1)
     return TD_NOTLS;
@@ -50,11 +154,75 @@ td_thr_tlsbase (const td_thrhandle_t *th,
 	return TD_TLSDEFER;
     }
 
+  err = dtv_slotinfo (th->th_ta_p, modid, &temp);
+  if (err != TD_OK)
+    return err;
+
+  psaddr_t slot;
+  err = DB_GET_STRUCT (slot, th->th_ta_p, temp, dtv_slotinfo);
+  if (err != TD_OK)
+    return err;
+
+  /* Take the link_map from the slotinfo.  */
+  psaddr_t map;
+  err = DB_GET_FIELD_LOCAL (map, th->th_ta_p, slot, dtv_slotinfo, map, 0);
+  if (err != TD_OK)
+    return err;
+  if (!map)
+    return TD_ERR;
+
+  /* Ok, the modid is good, now find out what DTV generation it
+     requires.  */
+  err = DB_GET_FIELD_LOCAL (temp, th->th_ta_p, slot, dtv_slotinfo, gen, 0);
+  if (err != TD_OK)
+    return err;
+  size_t modgen = (uintptr_t)temp;
+
   /* Get the DTV pointer from the thread descriptor.  */
   err = DB_GET_FIELD (dtv, th->th_ta_p, pd, pthread, dtvp, 0);
   if (err != TD_OK)
     return err;
 
+  psaddr_t dtvgenloc;
+  /* Get the DTV generation count at dtv[0].counter.  */
+  err = DB_GET_FIELD_ADDRESS (dtvgenloc, th->th_ta_p, dtv, dtv, dtv, 0);
+  if (err != TD_OK)
+    return err;
+  err = DB_GET_FIELD (temp, th->th_ta_p, dtvgenloc, dtv_t, counter, 0);
+  if (err != TD_OK)
+    return err;
+  size_t dtvgen = (uintptr_t)temp;
+
+  /* Is the DTV current enough?  */
+  if (dtvgen < modgen)
+    {
+    try_static_tls:
+      /* If the module uses Static TLS, we're still good.  */
+      err = DB_GET_FIELD (temp, th->th_ta_p, map, link_map, l_tls_offset, 0);
+      if (err != TD_OK)
+	return err;
+      ptrdiff_t tlsoff = (uintptr_t)temp;
+
+      if (tlsoff != FORCED_DYNAMIC_TLS_OFFSET
+	  && tlsoff != NO_TLS_OFFSET)
+	{
+	  psaddr_t tp = pd;
+
+#if TLS_TCB_AT_TP
+	  dtvptr = tp - tlsoff;
+#elif TLS_DTV_AT_TP
+	  dtvptr = tp + tlsoff + TLS_PRE_TCB_SIZE;
+#else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+#endif
+
+	  *base = dtvptr;
+	  return TD_OK;
+	}
+
+      return TD_TLSDEFER;
+    }
+
   /* Find the corresponding entry in the DTV.  */
   err = DB_GET_FIELD_ADDRESS (dtvslot, th->th_ta_p, dtv, dtv, dtv, modid);
   if (err != TD_OK)
@@ -68,7 +236,7 @@ td_thr_tlsbase (const td_thrhandle_t *th,
   /* It could be that the memory for this module is not allocated for
      the given thread.  */
   if ((uintptr_t) dtvptr & 1)
-    return TD_TLSDEFER;
+    goto try_static_tls;
 
   *base = dtvptr;
   return TD_OK;
diff --git a/nptl_db/thread_dbP.h b/nptl_db/thread_dbP.h
index 66d6d90..e7fe12e 100644
--- a/nptl_db/thread_dbP.h
+++ b/nptl_db/thread_dbP.h
@@ -29,6 +29,7 @@
 #include "thread_db.h"
 #include "../nptl/pthreadP.h"  	/* This is for *_BITMASK only.  */
 #include <list.h>
+#include <gnu/lib-names.h>
 
 /* Indeces for the symbol names.  */
 enum
@@ -139,11 +140,11 @@ ta_ok (const td_thragent_t *ta)
 }
 
 
-/* Internal wrapper around ps_pglobal_lookup.  */
-extern ps_err_e td_lookup (struct ps_prochandle *ps,
-			   int idx, psaddr_t *sym_addr) attribute_hidden;
-
-
+/* Internal wrappers around ps_pglobal_lookup.  */
+extern ps_err_e td_mod_lookup (struct ps_prochandle *ps, const char *modname,
+			       int idx, psaddr_t *sym_addr) attribute_hidden;
+#define td_lookup(ps, idx, sym_addr) \
+  td_mod_lookup ((ps), LIBPTHREAD_SO, (idx), (sym_addr))
 
 
 /* Store in psaddr_t VAR the address of inferior's symbol NAME.  */
-- 
2.1.4


Reply to: