mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
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.
This commit is contained in:
@ -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;
|
||||
|
Reference in New Issue
Block a user