Blame nptl_db/td_thr_tlsbase.c

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