about summary refs log tree commit diff
path: root/nptl_db
diff options
context:
space:
mode:
authorAlexandre Oliva <aoliva@redhat.com>2015-03-17 01:14:11 -0300
committerAlexandre Oliva <aoliva@redhat.com>2015-03-17 00:31:49 -0300
commitf8aeae347377f3dfa8cbadde057adf1827fb1d44 (patch)
treecee16d94d0f5b7061455667057da4f141f98b9ae /nptl_db
parentb97eb2bdb1ed72982a7821c3078be591051cef59 (diff)
downloadglibc-f8aeae347377f3dfa8cbadde057adf1827fb1d44.tar.gz
glibc-f8aeae347377f3dfa8cbadde057adf1827fb1d44.tar.xz
glibc-f8aeae347377f3dfa8cbadde057adf1827fb1d44.zip
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.
Diffstat (limited to 'nptl_db')
-rw-r--r--nptl_db/db-symbols.awk2
-rw-r--r--nptl_db/db_info.c4
-rw-r--r--nptl_db/fetch-value.c3
-rw-r--r--nptl_db/structs.def39
-rw-r--r--nptl_db/td_symbol_list.c7
-rw-r--r--nptl_db/td_thr_tlsbase.c172
-rw-r--r--nptl_db/thread_dbP.h11
7 files changed, 226 insertions, 12 deletions
diff --git a/nptl_db/db-symbols.awk b/nptl_db/db-symbols.awk
index f9a91b93bf..eb089e188a 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 d4a5438fba..b88b4c0718 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 afc26fc1ef..8a5a30cb1f 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 42e8b4df06..0d49a0ae5e 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 !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 !IS_IN (libpthread) || TLS_TCB_AT_TP
 DB_STRUCT_FIELD (pthread, dtvp)
 #endif
 
+#if !(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 6915ed78eb..b6c459f229 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);
 
   return result;
 }
diff --git a/nptl_db/td_thr_tlsbase.c b/nptl_db/td_thr_tlsbase.c
index 7092e31c5d..24a489ad46 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 4b59ce68ab..445c797758 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.  */