1
0
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:
Alexandre Oliva
2015-03-17 01:14:11 -03:00
parent b97eb2bdb1
commit f8aeae3473
14 changed files with 317 additions and 73 deletions

View File

@ -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;