diff options
author | Alexandre Oliva <aoliva@redhat.com> | 2015-03-17 01:14:11 -0300 |
---|---|---|
committer | Alexandre Oliva <aoliva@redhat.com> | 2015-03-17 00:31:49 -0300 |
commit | f8aeae347377f3dfa8cbadde057adf1827fb1d44 (patch) | |
tree | cee16d94d0f5b7061455667057da4f141f98b9ae /nptl_db | |
parent | b97eb2bdb1ed72982a7821c3078be591051cef59 (diff) | |
download | glibc-f8aeae347377f3dfa8cbadde057adf1827fb1d44.tar.gz |
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.
Diffstat (limited to 'nptl_db')
-rw-r--r-- | nptl_db/db-symbols.awk | 2 | ||||
-rw-r--r-- | nptl_db/db_info.c | 4 | ||||
-rw-r--r-- | nptl_db/fetch-value.c | 3 | ||||
-rw-r--r-- | nptl_db/structs.def | 39 | ||||
-rw-r--r-- | nptl_db/td_symbol_list.c | 7 | ||||
-rw-r--r-- | nptl_db/td_thr_tlsbase.c | 172 | ||||
-rw-r--r-- | nptl_db/thread_dbP.h | 11 |
7 files changed, 226 insertions, 12 deletions
diff --git a/nptl_db/db-symbols.awk b/nptl_db/db-symbols.awk index f9a91b93bf..eb089e188a 100644 --- a/nptl_db/db-symbols.awk +++ b/nptl_db/db-symbols.awk @@ -2,6 +2,8 @@ # we've just built. It checks for all the symbols used in td_symbol_list. BEGIN { +%define DB_RTLD_VARIABLE(name) /* Nothing. */ +%define DB_MAIN_VARIABLE(name) /* Nothing. */ %define DB_LOOKUP_NAME(idx, name) required[STRINGIFY (name)] = 1; %define DB_LOOKUP_NAME_TH_UNIQUE(idx, name) th_unique[STRINGIFY (name)] = 1; %include "db-symbols.h" diff --git a/nptl_db/db_info.c b/nptl_db/db_info.c index d4a5438fba..b88b4c0718 100644 --- a/nptl_db/db_info.c +++ b/nptl_db/db_info.c @@ -21,6 +21,7 @@ #include <stdint.h> #include "thread_dbP.h" #include <tls.h> +#include <ldsodefs.h> typedef struct pthread pthread; typedef struct pthread_key_struct pthread_key_struct; @@ -37,6 +38,9 @@ typedef struct } dtv; typedef struct link_map link_map; +typedef struct rtld_global rtld_global; +typedef struct dtv_slotinfo_list dtv_slotinfo_list; +typedef struct dtv_slotinfo dtv_slotinfo; /* Actually static in nptl/init.c, but we only need it for typeof. */ extern bool __nptl_initial_report_events; diff --git a/nptl_db/fetch-value.c b/nptl_db/fetch-value.c index afc26fc1ef..8a5a30cb1f 100644 --- a/nptl_db/fetch-value.c +++ b/nptl_db/fetch-value.c @@ -69,7 +69,8 @@ _td_locate_field (td_thragent_t *ta, } } - if (idx != 0 && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc)) + if (idx != 0 && DB_DESC_NELEM (desc) != 0 + && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc)) /* This is an internal indicator to callers with nonzero IDX that the IDX value is too big. */ return TD_NOAPLIC; diff --git a/nptl_db/structs.def b/nptl_db/structs.def index 42e8b4df06..0d49a0ae5e 100644 --- a/nptl_db/structs.def +++ b/nptl_db/structs.def @@ -22,6 +22,28 @@ # define STRUCTS_DEF_DEFAULTS 1 #endif +#ifndef DB_RTLD_VARIABLE +# define DB_RTLD_VARIABLE(name) DB_VARIABLE (name) +#endif + +#ifndef DB_MAIN_VARIABLE +# define DB_MAIN_VARIABLE(name) DB_VARIABLE (name) +#endif + +#ifndef DB_RTLD_GLOBAL_FIELD +# if !IS_IN (libpthread) +# define DB_RTLD_GLOBAL_FIELD(field) \ + DB_STRUCT_FIELD (rtld_global, _##field) \ + DB_MAIN_VARIABLE (_##field) +# elif defined SHARED +# define DB_RTLD_GLOBAL_FIELD(field) \ + DB_STRUCT_FIELD (rtld_global, _##field) +# else +# define DB_RTLD_GLOBAL_FIELD(field) \ + DB_MAIN_VARIABLE (_##field) +# endif +#endif /* DB_RTLD_GLOBAL_FIELD */ + DB_STRUCT (pthread) DB_STRUCT_FIELD (pthread, list) DB_STRUCT_FIELD (pthread, report_events) @@ -70,14 +92,31 @@ DB_STRUCT (pthread_key_data_level2) DB_STRUCT_ARRAY_FIELD (pthread_key_data_level2, data) DB_STRUCT_FIELD (link_map, l_tls_modid) +DB_STRUCT_FIELD (link_map, l_tls_offset) DB_STRUCT_ARRAY_FIELD (dtv, dtv) #define pointer_val pointer.val /* Field of anonymous struct in dtv_t. */ DB_STRUCT_FIELD (dtv_t, pointer_val) +DB_STRUCT_FIELD (dtv_t, counter) #if !IS_IN (libpthread) || TLS_TCB_AT_TP DB_STRUCT_FIELD (pthread, dtvp) #endif +#if !(IS_IN (libpthread) && !defined SHARED) +DB_STRUCT (rtld_global) +DB_RTLD_VARIABLE (_rtld_global) +#endif +DB_RTLD_GLOBAL_FIELD (dl_tls_dtv_slotinfo_list) + +DB_STRUCT (dtv_slotinfo_list) +DB_STRUCT_FIELD (dtv_slotinfo_list, len) +DB_STRUCT_FIELD (dtv_slotinfo_list, next) +DB_STRUCT_ARRAY_FIELD (dtv_slotinfo_list, slotinfo) + +DB_STRUCT (dtv_slotinfo) +DB_STRUCT_FIELD (dtv_slotinfo, gen) +DB_STRUCT_FIELD (dtv_slotinfo, map) + #ifdef STRUCTS_DEF_DEFAULTS # undef DB_STRUCT_ARRAY_FIELD # undef DB_ARRAY_VARIABLE diff --git a/nptl_db/td_symbol_list.c b/nptl_db/td_symbol_list.c index 6915ed78eb..b6c459f229 100644 --- a/nptl_db/td_symbol_list.c +++ b/nptl_db/td_symbol_list.c @@ -18,7 +18,6 @@ <http://www.gnu.org/licenses/>. */ #include <assert.h> -#include <gnu/lib-names.h> #include "thread_dbP.h" static const char *symbol_list_arr[] = @@ -41,12 +40,12 @@ td_symbol_list (void) ps_err_e -td_lookup (struct ps_prochandle *ps, int idx, psaddr_t *sym_addr) +td_mod_lookup (struct ps_prochandle *ps, const char *mod, + int idx, psaddr_t *sym_addr) { ps_err_e result; assert (idx >= 0 && idx < SYM_NUM_MESSAGES); - result = ps_pglobal_lookup (ps, LIBPTHREAD_SO, symbol_list_arr[idx], - sym_addr); + result = ps_pglobal_lookup (ps, mod, symbol_list_arr[idx], sym_addr); return result; } diff --git a/nptl_db/td_thr_tlsbase.c b/nptl_db/td_thr_tlsbase.c index 7092e31c5d..24a489ad46 100644 --- a/nptl_db/td_thr_tlsbase.c +++ b/nptl_db/td_thr_tlsbase.c @@ -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; diff --git a/nptl_db/thread_dbP.h b/nptl_db/thread_dbP.h index 4b59ce68ab..445c797758 100644 --- a/nptl_db/thread_dbP.h +++ b/nptl_db/thread_dbP.h @@ -29,6 +29,7 @@ #include "thread_db.h" #include "../nptl/pthreadP.h" /* This is for *_BITMASK only. */ #include <list.h> +#include <gnu/lib-names.h> /* Indeces for the symbol names. */ enum @@ -139,11 +140,11 @@ ta_ok (const td_thragent_t *ta) } -/* Internal wrapper around ps_pglobal_lookup. */ -extern ps_err_e td_lookup (struct ps_prochandle *ps, - int idx, psaddr_t *sym_addr) attribute_hidden; - - +/* Internal wrappers around ps_pglobal_lookup. */ +extern ps_err_e td_mod_lookup (struct ps_prochandle *ps, const char *modname, + int idx, psaddr_t *sym_addr) attribute_hidden; +#define td_lookup(ps, idx, sym_addr) \ + td_mod_lookup ((ps), LIBPTHREAD_SO, (idx), (sym_addr)) /* Store in psaddr_t VAR the address of inferior's symbol NAME. */ |