mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-30 10:45:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			244 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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;
 | |
| }
 |