diff options
Diffstat (limited to 'REORG.TODO/nptl_db/td_thr_tlsbase.c')
-rw-r--r-- | REORG.TODO/nptl_db/td_thr_tlsbase.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/REORG.TODO/nptl_db/td_thr_tlsbase.c b/REORG.TODO/nptl_db/td_thr_tlsbase.c new file mode 100644 index 0000000000..11d7ad3a1c --- /dev/null +++ b/REORG.TODO/nptl_db/td_thr_tlsbase.c @@ -0,0 +1,243 @@ +/* Locate TLS data for a thread. + Copyright (C) 2003-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <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, temp; + + if (modid < 1) + return TD_NOTLS; + + psaddr_t pd = th->th_unique; + if (pd == 0) + { + /* This is the fake handle for the main thread before libpthread + initialization. We are using 0 for its th_unique because we can't + trust that its thread register has been initialized. But we need + a real pointer to have any TLS access work. In case of dlopen'd + libpthread, initialization might not be for quite some time. So + try looking up the thread register now. Worst case, it's nonzero + uninitialized garbage and we get bogus results for TLS access + attempted too early. Tough. */ + + td_thrhandle_t main_th; + err = __td_ta_lookup_th_unique (th->th_ta_p, ps_getpid (th->th_ta_p->ph), + &main_th); + if (err == 0) + pd = main_th.th_unique; + if (pd == 0) + 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) + return err; + + /* Extract the TLS block address from that DTV slot. */ + err = DB_GET_FIELD (dtvptr, th->th_ta_p, dtvslot, dtv_t, pointer_val, 0); + if (err != TD_OK) + return err; + + /* It could be that the memory for this module is not allocated for + the given thread. */ + if ((uintptr_t) dtvptr & 1) + goto try_static_tls; + + *base = dtvptr; + return TD_OK; +} |