|
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 |
}
|