| 1 | /* Locate TLS data for a thread. |
| 2 | Copyright (C) 2003-2022 Free Software Foundation, Inc. |
| 3 | This file is part of the GNU C Library. |
| 4 | |
| 5 | The GNU C Library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2.1 of the License, or (at your option) any later version. |
| 9 | |
| 10 | The GNU C Library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Lesser General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Lesser General Public |
| 16 | License along with the GNU C Library; if not, see |
| 17 | <https://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #include "thread_dbP.h" |
| 20 | #include <link.h> |
| 21 | |
| 22 | /* Get the DTV slotinfo list head entry from the dynamic loader state |
| 23 | into *LISTHEAD. */ |
| 24 | static td_err_e |
| 25 | dtv_slotinfo_list (td_thragent_t *ta, |
| 26 | psaddr_t *listhead) |
| 27 | { |
| 28 | td_err_e err; |
| 29 | psaddr_t head; |
| 30 | |
| 31 | if (__td_ta_rtld_global (ta)) |
| 32 | { |
| 33 | err = DB_GET_FIELD (head, ta, ta->ta_addr__rtld_global, |
| 34 | rtld_global, _dl_tls_dtv_slotinfo_list, 0); |
| 35 | if (err != TD_OK) |
| 36 | return err; |
| 37 | } |
| 38 | else |
| 39 | { |
| 40 | if (ta->ta_addr__dl_tls_dtv_slotinfo_list == 0 |
| 41 | && td_mod_lookup (ta->ph, NULL, SYM__dl_tls_dtv_slotinfo_list, |
| 42 | &ta->ta_addr__dl_tls_dtv_slotinfo_list) != PS_OK) |
| 43 | return TD_ERR; |
| 44 | |
| 45 | err = _td_fetch_value (ta, ta->ta_var__dl_tls_dtv_slotinfo_list, |
| 46 | SYM_DESC__dl_tls_dtv_slotinfo_list, |
| 47 | 0, ta->ta_addr__dl_tls_dtv_slotinfo_list, &head); |
| 48 | if (err != TD_OK) |
| 49 | return err; |
| 50 | } |
| 51 | |
| 52 | *listhead = head; |
| 53 | return TD_OK; |
| 54 | } |
| 55 | |
| 56 | /* Get the address of the DTV slotinfo entry for MODID into |
| 57 | *DTVSLOTINFO. */ |
| 58 | static td_err_e |
| 59 | dtv_slotinfo (td_thragent_t *ta, |
| 60 | unsigned long int modid, |
| 61 | psaddr_t *dtvslotinfo) |
| 62 | { |
| 63 | td_err_e err; |
| 64 | psaddr_t slot, temp; |
| 65 | size_t slbase = 0; |
| 66 | |
| 67 | err = dtv_slotinfo_list (ta, &slot); |
| 68 | if (err != TD_OK) |
| 69 | return err; |
| 70 | |
| 71 | while (slot) |
| 72 | { |
| 73 | /* Get the number of entries in this list entry's array. */ |
| 74 | err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list, len, 0); |
| 75 | if (err != TD_OK) |
| 76 | return err; |
| 77 | size_t len = (uintptr_t)temp; |
| 78 | |
| 79 | /* Did we find the list entry for modid? */ |
| 80 | if (modid < slbase + len) |
| 81 | break; |
| 82 | |
| 83 | /* We didn't, so get the next list entry. */ |
| 84 | slbase += len; |
| 85 | err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list, |
| 86 | next, 0); |
| 87 | if (err != TD_OK) |
| 88 | return err; |
| 89 | slot = temp; |
| 90 | } |
| 91 | |
| 92 | /* We reached the end of the list and found nothing. */ |
| 93 | if (!slot) |
| 94 | return TD_ERR; |
| 95 | |
| 96 | /* Take the slotinfo for modid from the list entry. */ |
| 97 | err = DB_GET_FIELD_ADDRESS (temp, ta, slot, dtv_slotinfo_list, |
| 98 | slotinfo, modid - slbase); |
| 99 | if (err != TD_OK) |
| 100 | return err; |
| 101 | slot = temp; |
| 102 | |
| 103 | *dtvslotinfo = slot; |
| 104 | return TD_OK; |
| 105 | } |
| 106 | |
| 107 | /* Return in *BASE the base address of the TLS block for MODID within |
| 108 | TH. |
| 109 | |
| 110 | It should return success and yield the correct pointer in any |
| 111 | circumstance where the TLS block for the module and thread |
| 112 | requested has already been initialized. |
| 113 | |
| 114 | It should fail with TD_TLSDEFER only when the thread could not |
| 115 | possibly have observed any values in that TLS block. That way, the |
| 116 | debugger can fall back to showing initial values from the PT_TLS |
| 117 | segment (and refusing attempts to mutate) for the TD_TLSDEFER case, |
| 118 | and never fail to make the values the program will actually see |
| 119 | available to the user of the debugger. */ |
| 120 | td_err_e |
| 121 | td_thr_tlsbase (const td_thrhandle_t *th, |
| 122 | unsigned long int modid, |
| 123 | psaddr_t *base) |
| 124 | { |
| 125 | td_err_e err; |
| 126 | psaddr_t dtv, dtvslot, dtvptr, temp; |
| 127 | |
| 128 | if (modid < 1) |
| 129 | return TD_NOTLS; |
| 130 | |
| 131 | psaddr_t pd = th->th_unique; |
| 132 | if (pd == 0) |
| 133 | { |
| 134 | /* This is the fake handle for the main thread before libpthread |
| 135 | initialization. We are using 0 for its th_unique because we can't |
| 136 | trust that its thread register has been initialized. But we need |
| 137 | a real pointer to have any TLS access work. In case of dlopen'd |
| 138 | libpthread, initialization might not be for quite some time. So |
| 139 | try looking up the thread register now. Worst case, it's nonzero |
| 140 | uninitialized garbage and we get bogus results for TLS access |
| 141 | attempted too early. Tough. */ |
| 142 | |
| 143 | td_thrhandle_t main_th; |
| 144 | err = __td_ta_lookup_th_unique (th->th_ta_p, ps_getpid (th->th_ta_p->ph), |
| 145 | &main_th); |
| 146 | if (err == 0) |
| 147 | pd = main_th.th_unique; |
| 148 | if (pd == 0) |
| 149 | return TD_TLSDEFER; |
| 150 | } |
| 151 | |
| 152 | err = dtv_slotinfo (th->th_ta_p, modid, &temp); |
| 153 | if (err != TD_OK) |
| 154 | return err; |
| 155 | |
| 156 | psaddr_t slot; |
| 157 | err = DB_GET_STRUCT (slot, th->th_ta_p, temp, dtv_slotinfo); |
| 158 | if (err != TD_OK) |
| 159 | return err; |
| 160 | |
| 161 | /* Take the link_map from the slotinfo. */ |
| 162 | psaddr_t map; |
| 163 | err = DB_GET_FIELD_LOCAL (map, th->th_ta_p, slot, dtv_slotinfo, map, 0); |
| 164 | if (err != TD_OK) |
| 165 | return err; |
| 166 | if (!map) |
| 167 | return TD_ERR; |
| 168 | |
| 169 | /* Ok, the modid is good, now find out what DTV generation it |
| 170 | requires. */ |
| 171 | err = DB_GET_FIELD_LOCAL (temp, th->th_ta_p, slot, dtv_slotinfo, gen, 0); |
| 172 | if (err != TD_OK) |
| 173 | return err; |
| 174 | size_t modgen = (uintptr_t)temp; |
| 175 | |
| 176 | /* Get the DTV pointer from the thread descriptor. */ |
| 177 | err = DB_GET_FIELD (dtv, th->th_ta_p, pd, pthread, dtvp, 0); |
| 178 | if (err != TD_OK) |
| 179 | return err; |
| 180 | |
| 181 | psaddr_t dtvgenloc; |
| 182 | /* Get the DTV generation count at dtv[0].counter. */ |
| 183 | err = DB_GET_FIELD_ADDRESS (dtvgenloc, th->th_ta_p, dtv, dtv, dtv, 0); |
| 184 | if (err != TD_OK) |
| 185 | return err; |
| 186 | err = DB_GET_FIELD (temp, th->th_ta_p, dtvgenloc, dtv_t, counter, 0); |
| 187 | if (err != TD_OK) |
| 188 | return err; |
| 189 | size_t dtvgen = (uintptr_t)temp; |
| 190 | |
| 191 | /* Is the DTV current enough? */ |
| 192 | if (dtvgen < modgen) |
| 193 | { |
| 194 | try_static_tls: |
| 195 | /* If the module uses Static TLS, we're still good. */ |
| 196 | err = DB_GET_FIELD (temp, th->th_ta_p, map, link_map, l_tls_offset, 0); |
| 197 | if (err != TD_OK) |
| 198 | return err; |
| 199 | ptrdiff_t tlsoff = (uintptr_t)temp; |
| 200 | |
| 201 | if (tlsoff != FORCED_DYNAMIC_TLS_OFFSET |
| 202 | && tlsoff != NO_TLS_OFFSET) |
| 203 | { |
| 204 | psaddr_t tp = pd; |
| 205 | |
| 206 | #if TLS_TCB_AT_TP |
| 207 | dtvptr = tp - tlsoff; |
| 208 | #elif TLS_DTV_AT_TP |
| 209 | dtvptr = tp + tlsoff + TLS_PRE_TCB_SIZE; |
| 210 | #else |
| 211 | # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" |
| 212 | #endif |
| 213 | |
| 214 | *base = dtvptr; |
| 215 | return TD_OK; |
| 216 | } |
| 217 | |
| 218 | return TD_TLSDEFER; |
| 219 | } |
| 220 | |
| 221 | /* Find the corresponding entry in the DTV. */ |
| 222 | err = DB_GET_FIELD_ADDRESS (dtvslot, th->th_ta_p, dtv, dtv, dtv, modid); |
| 223 | if (err != TD_OK) |
| 224 | return err; |
| 225 | |
| 226 | /* Extract the TLS block address from that DTV slot. */ |
| 227 | err = DB_GET_FIELD (dtvptr, th->th_ta_p, dtvslot, dtv_t, pointer_val, 0); |
| 228 | if (err != TD_OK) |
| 229 | return err; |
| 230 | |
| 231 | /* It could be that the memory for this module is not allocated for |
| 232 | the given thread. */ |
| 233 | if ((uintptr_t) dtvptr & 1) |
| 234 | goto try_static_tls; |
| 235 | |
| 236 | *base = dtvptr; |
| 237 | return TD_OK; |
| 238 | } |
| 239 | |