Blame nptl_db/td_thr_tlsbase.c

Packit Service 82fcde
/* Locate TLS data for a thread.
Packit Service 82fcde
   Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include "thread_dbP.h"
Packit Service 82fcde
#include <link.h>
Packit Service 82fcde
Packit Service 82fcde
/* Get the DTV slotinfo list head entry from the dynamic loader state
Packit Service 82fcde
   into *LISTHEAD.  */
Packit Service 82fcde
static td_err_e
Packit Service 82fcde
dtv_slotinfo_list (td_thragent_t *ta,
Packit Service 82fcde
		   psaddr_t *listhead)
Packit Service 82fcde
{
Packit Service 82fcde
  td_err_e err;
Packit Service 82fcde
  psaddr_t head;
Packit Service 82fcde
Packit Service 82fcde
  if (ta->ta_addr__rtld_global == 0
Packit Service 82fcde
      && td_mod_lookup (ta->ph, LD_SO, SYM__rtld_global,
Packit Service 82fcde
			&ta->ta_addr__rtld_global) != PS_OK)
Packit Service 82fcde
    ta->ta_addr__rtld_global = (void*)-1;
Packit Service 82fcde
Packit Service 82fcde
  if (ta->ta_addr__rtld_global != (void*)-1)
Packit Service 82fcde
    {
Packit Service 82fcde
      err = DB_GET_FIELD (head, ta, ta->ta_addr__rtld_global,
Packit Service 82fcde
			  rtld_global, _dl_tls_dtv_slotinfo_list, 0);
Packit Service 82fcde
      if (err != TD_OK)
Packit Service 82fcde
	return err;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      if (ta->ta_addr__dl_tls_dtv_slotinfo_list == 0
Packit Service 82fcde
	  && td_mod_lookup (ta->ph, NULL, SYM__dl_tls_dtv_slotinfo_list,
Packit Service 82fcde
			    &ta->ta_addr__dl_tls_dtv_slotinfo_list) != PS_OK)
Packit Service 82fcde
	return TD_ERR;
Packit Service 82fcde
Packit Service 82fcde
      err = _td_fetch_value (ta, ta->ta_var__dl_tls_dtv_slotinfo_list,
Packit Service 82fcde
			     SYM_DESC__dl_tls_dtv_slotinfo_list,
Packit Service 82fcde
			     0, ta->ta_addr__dl_tls_dtv_slotinfo_list, &head;;
Packit Service 82fcde
      if (err != TD_OK)
Packit Service 82fcde
	return err;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  *listhead = head;
Packit Service 82fcde
  return TD_OK;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Get the address of the DTV slotinfo entry for MODID into
Packit Service 82fcde
   *DTVSLOTINFO.  */
Packit Service 82fcde
static td_err_e
Packit Service 82fcde
dtv_slotinfo (td_thragent_t *ta,
Packit Service 82fcde
	      unsigned long int modid,
Packit Service 82fcde
	      psaddr_t *dtvslotinfo)
Packit Service 82fcde
{
Packit Service 82fcde
  td_err_e err;
Packit Service 82fcde
  psaddr_t slot, temp;
Packit Service 82fcde
  size_t slbase = 0;
Packit Service 82fcde
Packit Service 82fcde
  err = dtv_slotinfo_list (ta, &slot);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  while (slot)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Get the number of entries in this list entry's array.  */
Packit Service 82fcde
      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list, len, 0);
Packit Service 82fcde
      if (err != TD_OK)
Packit Service 82fcde
	return err;
Packit Service 82fcde
      size_t len = (uintptr_t)temp;
Packit Service 82fcde
Packit Service 82fcde
      /* Did we find the list entry for modid?  */
Packit Service 82fcde
      if (modid < slbase + len)
Packit Service 82fcde
	break;
Packit Service 82fcde
Packit Service 82fcde
      /* We didn't, so get the next list entry.  */
Packit Service 82fcde
      slbase += len;
Packit Service 82fcde
      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list,
Packit Service 82fcde
			  next, 0);
Packit Service 82fcde
      if (err != TD_OK)
Packit Service 82fcde
	return err;
Packit Service 82fcde
      slot = temp;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* We reached the end of the list and found nothing.  */
Packit Service 82fcde
  if (!slot)
Packit Service 82fcde
    return TD_ERR;
Packit Service 82fcde
Packit Service 82fcde
  /* Take the slotinfo for modid from the list entry.  */
Packit Service 82fcde
  err = DB_GET_FIELD_ADDRESS (temp, ta, slot, dtv_slotinfo_list,
Packit Service 82fcde
			      slotinfo, modid - slbase);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
  slot = temp;
Packit Service 82fcde
Packit Service 82fcde
  *dtvslotinfo = slot;
Packit Service 82fcde
  return TD_OK;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Return in *BASE the base address of the TLS block for MODID within
Packit Service 82fcde
   TH.
Packit Service 82fcde
Packit Service 82fcde
   It should return success and yield the correct pointer in any
Packit Service 82fcde
   circumstance where the TLS block for the module and thread
Packit Service 82fcde
   requested has already been initialized.
Packit Service 82fcde
Packit Service 82fcde
   It should fail with TD_TLSDEFER only when the thread could not
Packit Service 82fcde
   possibly have observed any values in that TLS block.  That way, the
Packit Service 82fcde
   debugger can fall back to showing initial values from the PT_TLS
Packit Service 82fcde
   segment (and refusing attempts to mutate) for the TD_TLSDEFER case,
Packit Service 82fcde
   and never fail to make the values the program will actually see
Packit Service 82fcde
   available to the user of the debugger.  */
Packit Service 82fcde
td_err_e
Packit Service 82fcde
td_thr_tlsbase (const td_thrhandle_t *th,
Packit Service 82fcde
		unsigned long int modid,
Packit Service 82fcde
		psaddr_t *base)
Packit Service 82fcde
{
Packit Service 82fcde
  td_err_e err;
Packit Service 82fcde
  psaddr_t dtv, dtvslot, dtvptr, temp;
Packit Service 82fcde
Packit Service 82fcde
  if (modid < 1)
Packit Service 82fcde
    return TD_NOTLS;
Packit Service 82fcde
Packit Service 82fcde
  psaddr_t pd = th->th_unique;
Packit Service 82fcde
  if (pd == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* This is the fake handle for the main thread before libpthread
Packit Service 82fcde
	 initialization.  We are using 0 for its th_unique because we can't
Packit Service 82fcde
	 trust that its thread register has been initialized.  But we need
Packit Service 82fcde
	 a real pointer to have any TLS access work.  In case of dlopen'd
Packit Service 82fcde
	 libpthread, initialization might not be for quite some time.  So
Packit Service 82fcde
	 try looking up the thread register now.  Worst case, it's nonzero
Packit Service 82fcde
	 uninitialized garbage and we get bogus results for TLS access
Packit Service 82fcde
	 attempted too early.  Tough.  */
Packit Service 82fcde
Packit Service 82fcde
      td_thrhandle_t main_th;
Packit Service 82fcde
      err = __td_ta_lookup_th_unique (th->th_ta_p, ps_getpid (th->th_ta_p->ph),
Packit Service 82fcde
				      &main_th);
Packit Service 82fcde
      if (err == 0)
Packit Service 82fcde
	pd = main_th.th_unique;
Packit Service 82fcde
      if (pd == 0)
Packit Service 82fcde
	return TD_TLSDEFER;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  err = dtv_slotinfo (th->th_ta_p, modid, &temp);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  psaddr_t slot;
Packit Service 82fcde
  err = DB_GET_STRUCT (slot, th->th_ta_p, temp, dtv_slotinfo);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  /* Take the link_map from the slotinfo.  */
Packit Service 82fcde
  psaddr_t map;
Packit Service 82fcde
  err = DB_GET_FIELD_LOCAL (map, th->th_ta_p, slot, dtv_slotinfo, map, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
  if (!map)
Packit Service 82fcde
    return TD_ERR;
Packit Service 82fcde
Packit Service 82fcde
  /* Ok, the modid is good, now find out what DTV generation it
Packit Service 82fcde
     requires.  */
Packit Service 82fcde
  err = DB_GET_FIELD_LOCAL (temp, th->th_ta_p, slot, dtv_slotinfo, gen, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
  size_t modgen = (uintptr_t)temp;
Packit Service 82fcde
Packit Service 82fcde
  /* Get the DTV pointer from the thread descriptor.  */
Packit Service 82fcde
  err = DB_GET_FIELD (dtv, th->th_ta_p, pd, pthread, dtvp, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  psaddr_t dtvgenloc;
Packit Service 82fcde
  /* Get the DTV generation count at dtv[0].counter.  */
Packit Service 82fcde
  err = DB_GET_FIELD_ADDRESS (dtvgenloc, th->th_ta_p, dtv, dtv, dtv, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
  err = DB_GET_FIELD (temp, th->th_ta_p, dtvgenloc, dtv_t, counter, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
  size_t dtvgen = (uintptr_t)temp;
Packit Service 82fcde
Packit Service 82fcde
  /* Is the DTV current enough?  */
Packit Service 82fcde
  if (dtvgen < modgen)
Packit Service 82fcde
    {
Packit Service 82fcde
    try_static_tls:
Packit Service 82fcde
      /* If the module uses Static TLS, we're still good.  */
Packit Service 82fcde
      err = DB_GET_FIELD (temp, th->th_ta_p, map, link_map, l_tls_offset, 0);
Packit Service 82fcde
      if (err != TD_OK)
Packit Service 82fcde
	return err;
Packit Service 82fcde
      ptrdiff_t tlsoff = (uintptr_t)temp;
Packit Service 82fcde
Packit Service 82fcde
      if (tlsoff != FORCED_DYNAMIC_TLS_OFFSET
Packit Service 82fcde
	  && tlsoff != NO_TLS_OFFSET)
Packit Service 82fcde
	{
Packit Service 82fcde
	  psaddr_t tp = pd;
Packit Service 82fcde
Packit Service 82fcde
#if TLS_TCB_AT_TP
Packit Service 82fcde
	  dtvptr = tp - tlsoff;
Packit Service 82fcde
#elif TLS_DTV_AT_TP
Packit Service 82fcde
	  dtvptr = tp + tlsoff + TLS_PRE_TCB_SIZE;
Packit Service 82fcde
#else
Packit Service 82fcde
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
	  *base = dtvptr;
Packit Service 82fcde
	  return TD_OK;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      return TD_TLSDEFER;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Find the corresponding entry in the DTV.  */
Packit Service 82fcde
  err = DB_GET_FIELD_ADDRESS (dtvslot, th->th_ta_p, dtv, dtv, dtv, modid);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  /* Extract the TLS block address from that DTV slot.  */
Packit Service 82fcde
  err = DB_GET_FIELD (dtvptr, th->th_ta_p, dtvslot, dtv_t, pointer_val, 0);
Packit Service 82fcde
  if (err != TD_OK)
Packit Service 82fcde
    return err;
Packit Service 82fcde
Packit Service 82fcde
  /* It could be that the memory for this module is not allocated for
Packit Service 82fcde
     the given thread.  */
Packit Service 82fcde
  if ((uintptr_t) dtvptr & 1)
Packit Service 82fcde
    goto try_static_tls;
Packit Service 82fcde
Packit Service 82fcde
  *base = dtvptr;
Packit Service 82fcde
  return TD_OK;
Packit Service 82fcde
}