about summary refs log tree commit diff
path: root/nptl_db/td_thr_tlsbase.c
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/td_thr_tlsbase.c
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/td_thr_tlsbase.c')
-rw-r--r--nptl_db/td_thr_tlsbase.c172
1 files changed, 170 insertions, 2 deletions
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;