| # |
| # commit f8aeae347377f3dfa8cbadde057adf1827fb1d44 |
| # Author: Alexandre Oliva <aoliva@redhat.com> |
| # Date: Tue Mar 17 01:14:11 2015 -0300 |
| # |
| # 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. |
| # |
| diff -urN glibc-2.17-c758a686/elf/dl-open.c glibc-2.17-c758a686/elf/dl-open.c |
| |
| |
| @@ -535,17 +535,7 @@ |
| && imap->l_tls_blocksize > 0) |
| { |
| /* For static TLS we have to allocate the memory here and |
| - now. This includes allocating memory in the DTV. But we |
| - cannot change any DTV other than our own. So, if we |
| - cannot guarantee that there is room in the DTV we don't |
| - even try it and fail the load. |
| - |
| - XXX We could track the minimum DTV slots allocated in |
| - all threads. */ |
| - if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS) |
| - _dl_signal_error (0, "dlopen", NULL, N_("\ |
| -cannot load any more object with static TLS")); |
| - |
| + now, but we can delay updating the DTV. */ |
| imap->l_need_tls_init = 0; |
| #ifdef SHARED |
| /* Update the slot information data for at least the |
| diff -urN glibc-2.17-c758a686/elf/dl-reloc.c glibc-2.17-c758a686/elf/dl-reloc.c |
| |
| |
| @@ -136,12 +136,6 @@ |
| # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" |
| #endif |
| |
| - /* Fill in the DTV slot so that a later LD/GD access will find it. */ |
| - dtv_t *dtv = THREAD_DTV (); |
| - assert (map->l_tls_modid <= dtv[-1].counter); |
| - dtv[map->l_tls_modid].pointer.val = dest; |
| - dtv[map->l_tls_modid].pointer.is_static = true; |
| - |
| /* Initialize the memory. */ |
| memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), |
| '\0', map->l_tls_blocksize - map->l_tls_initimage_size); |
| diff -urN glibc-2.17-c758a686/elf/dl-tls.c glibc-2.17-c758a686/elf/dl-tls.c |
| |
| |
| @@ -432,17 +432,14 @@ |
| assert (listp->slotinfo[cnt].gen <= GL(dl_tls_generation)); |
| maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); |
| |
| + dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; |
| + dtv[map->l_tls_modid].pointer.is_static = false; |
| + |
| if (map->l_tls_offset == NO_TLS_OFFSET |
| || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET) |
| - { |
| - /* For dynamically loaded modules we simply store |
| - the value indicating deferred allocation. */ |
| - dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; |
| - dtv[map->l_tls_modid].pointer.is_static = false; |
| - continue; |
| - } |
| + continue; |
| |
| - assert (map->l_tls_modid == cnt); |
| + assert (map->l_tls_modid == total + cnt); |
| assert (map->l_tls_blocksize >= map->l_tls_initimage_size); |
| #if TLS_TCB_AT_TP |
| assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize); |
| @@ -454,8 +451,6 @@ |
| #endif |
| |
| /* Copy the initialization image and clear the BSS part. */ |
| - dtv[map->l_tls_modid].pointer.val = dest; |
| - dtv[map->l_tls_modid].pointer.is_static = true; |
| memset (__mempcpy (dest, map->l_tls_initimage, |
| map->l_tls_initimage_size), '\0', |
| map->l_tls_blocksize - map->l_tls_initimage_size); |
| @@ -623,13 +618,16 @@ |
| struct link_map *map = listp->slotinfo[cnt].map; |
| if (map == NULL) |
| { |
| - /* If this modid was used at some point the memory |
| - might still be allocated. */ |
| - if (! dtv[total + cnt].pointer.is_static |
| - && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED) |
| + if (dtv[-1].counter >= total + cnt) |
| { |
| - free (dtv[total + cnt].pointer.val); |
| + /* If this modid was used at some point the memory |
| + might still be allocated. */ |
| + if (! dtv[total + cnt].pointer.is_static |
| + && (dtv[total + cnt].pointer.val |
| + != TLS_DTV_UNALLOCATED)) |
| + free (dtv[total + cnt].pointer.val); |
| dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED; |
| + dtv[total + cnt].pointer.is_static = false; |
| } |
| |
| continue; |
| @@ -693,10 +691,8 @@ |
| memalign and not malloc. */ |
| free (dtv[modid].pointer.val); |
| |
| - /* This module is loaded dynamically- We defer memory |
| - allocation. */ |
| - dtv[modid].pointer.is_static = false; |
| dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; |
| + dtv[modid].pointer.is_static = false; |
| |
| if (modid == req_modid) |
| the_map = map; |
| @@ -734,13 +730,12 @@ |
| the_map = listp->slotinfo[idx].map; |
| } |
| |
| - again: |
| /* Make sure that, if a dlopen running in parallel forces the |
| variable into static storage, we'll wait until the address in the |
| static TLS block is set up, and use that. If we're undecided |
| yet, make sure we make the decision holding the lock as well. */ |
| - if (__builtin_expect (the_map->l_tls_offset |
| - != FORCED_DYNAMIC_TLS_OFFSET, 0)) |
| + if (__glibc_unlikely (the_map->l_tls_offset |
| + != FORCED_DYNAMIC_TLS_OFFSET)) |
| { |
| __rtld_lock_lock_recursive (GL(dl_load_lock)); |
| if (__builtin_expect (the_map->l_tls_offset == NO_TLS_OFFSET, 1)) |
| @@ -748,22 +743,28 @@ |
| the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET; |
| __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
| } |
| - else |
| + else if (__glibc_likely (the_map->l_tls_offset |
| + != FORCED_DYNAMIC_TLS_OFFSET)) |
| { |
| +#if TLS_TCB_AT_TP |
| + void *p = (char *) THREAD_SELF - the_map->l_tls_offset; |
| +#elif TLS_DTV_AT_TP |
| + void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE; |
| +#else |
| +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" |
| +#endif |
| __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
| - if (__builtin_expect (the_map->l_tls_offset |
| - != FORCED_DYNAMIC_TLS_OFFSET, 1)) |
| - { |
| - void *p = dtv[GET_ADDR_MODULE].pointer.val; |
| - if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0)) |
| - goto again; |
| + dtv[GET_ADDR_MODULE].pointer.is_static = true; |
| + dtv[GET_ADDR_MODULE].pointer.val = p; |
| |
| - return (char *) p + GET_ADDR_OFFSET; |
| - } |
| + return (char *) p + GET_ADDR_OFFSET; |
| } |
| + else |
| + __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
| + |
| } |
| void *p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map); |
| - dtv[GET_ADDR_MODULE].pointer.is_static = false; |
| + assert (!dtv[GET_ADDR_MODULE].pointer.is_static); |
| |
| return (char *) p + GET_ADDR_OFFSET; |
| } |
| diff -urN glibc-2.17-c758a686/elf/tlsdeschtab.h glibc-2.17-c758a686/elf/tlsdeschtab.h |
| |
| |
| @@ -42,7 +42,7 @@ |
| return tdp->tlsinfo.ti_offset == tdq->tlsinfo.ti_offset; |
| } |
| |
| -inline static int |
| +inline static size_t |
| map_generation (struct link_map *map) |
| { |
| size_t idx = map->l_tls_modid; |
| @@ -58,7 +58,7 @@ |
| we can assume that, if the generation count is zero, we |
| still haven't determined the generation count for this |
| module. */ |
| - if (listp->slotinfo[idx].gen) |
| + if (listp->slotinfo[idx].map == map && listp->slotinfo[idx].gen) |
| return listp->slotinfo[idx].gen; |
| else |
| break; |
| diff -urN glibc-2.17-c758a686/nptl/allocatestack.c glibc-2.17-c758a686/nptl/allocatestack.c |
| |
| |
| @@ -1157,7 +1157,6 @@ |
| static inline void __attribute__((always_inline)) |
| init_one_static_tls (struct pthread *curp, struct link_map *map) |
| { |
| - dtv_t *dtv = GET_DTV (TLS_TPADJ (curp)); |
| # if TLS_TCB_AT_TP |
| void *dest = (char *) curp - map->l_tls_offset; |
| # elif TLS_DTV_AT_TP |
| @@ -1166,11 +1165,9 @@ |
| # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" |
| # endif |
| |
| - /* Fill in the DTV slot so that a later LD/GD access will find it. */ |
| - dtv[map->l_tls_modid].pointer.val = dest; |
| - dtv[map->l_tls_modid].pointer.is_static = true; |
| - |
| - /* Initialize the memory. */ |
| + /* We cannot delay the initialization of the Static TLS area, since |
| + it can be accessed with LE or IE, but since the DTV is only used |
| + by GD and LD, we can delay its update to avoid a race. */ |
| memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), |
| '\0', map->l_tls_blocksize - map->l_tls_initimage_size); |
| } |
| diff -urN glibc-2.17-c758a686/nptl_db/db_info.c glibc-2.17-c758a686/nptl_db/db_info.c |
| |
| |
| @@ -20,6 +20,7 @@ |
| |
| #include "thread_dbP.h" |
| #include <tls.h> |
| +#include <ldsodefs.h> |
| |
| typedef struct pthread pthread; |
| typedef struct pthread_key_struct pthread_key_struct; |
| @@ -36,6 +37,9 @@ |
| } 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 -urN glibc-2.17-c758a686/nptl_db/db-symbols.awk glibc-2.17-c758a686/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 -urN glibc-2.17-c758a686/nptl_db/fetch-value.c glibc-2.17-c758a686/nptl_db/fetch-value.c |
| |
| |
| @@ -68,7 +68,8 @@ |
| } |
| } |
| |
| - 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 -urN glibc-2.17-c758a686/nptl_db/structs.def glibc-2.17-c758a686/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 !defined 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_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 !defined IS_IN_libpthread || TLS_TCB_AT_TP |
| DB_STRUCT_FIELD (pthread, dtvp) |
| #endif |
| |
| +#if !(defined 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 -urN glibc-2.17-c758a686/nptl_db/td_symbol_list.c glibc-2.17-c758a686/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 @@ |
| |
| |
| 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); |
| |
| #ifdef HAVE_ASM_GLOBAL_DOT_NAME |
| /* For PowerPC, 64-bit uses dot symbols but 32-bit does not. |
| diff -urN glibc-2.17-c758a686/nptl_db/td_thr_tlsbase.c glibc-2.17-c758a686/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 @@ |
| 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 @@ |
| /* 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 -urN glibc-2.17-c758a686/nptl_db/thread_dbP.h glibc-2.17-c758a686/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 @@ |
| } |
| |
| |
| -/* 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. */ |