mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-30 10:45:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			209 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Which thread is running on an LWP?
 | |
|    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 <stdlib.h>
 | |
| #include <byteswap.h>
 | |
| #include <sys/procfs.h>
 | |
| 
 | |
| 
 | |
| td_err_e
 | |
| __td_ta_lookup_th_unique (const td_thragent_t *ta_arg,
 | |
| 			  lwpid_t lwpid, td_thrhandle_t *th)
 | |
| {
 | |
|   td_thragent_t *const ta = (td_thragent_t *) ta_arg;
 | |
|   ps_err_e err;
 | |
|   td_err_e terr;
 | |
|   prgregset_t regs;
 | |
|   psaddr_t addr;
 | |
| 
 | |
|   if (ta->ta_howto == ta_howto_unknown)
 | |
|     {
 | |
|       /* We need to read in from the inferior the instructions what to do.  */
 | |
|       psaddr_t howto;
 | |
| 
 | |
|       err = td_lookup (ta->ph, SYM_TH_UNIQUE_CONST_THREAD_AREA, &howto);
 | |
|       if (err == PS_OK)
 | |
| 	{
 | |
| 	  err = ps_pdread (ta->ph, howto,
 | |
| 			   &ta->ta_howto_data.const_thread_area,
 | |
|  			   sizeof ta->ta_howto_data.const_thread_area);
 | |
| 	  if (err != PS_OK)
 | |
| 	    return TD_ERR;
 | |
| 	  ta->ta_howto = ta_howto_const_thread_area;
 | |
| 	  if (ta->ta_howto_data.const_thread_area & 0xff000000U)
 | |
| 	    ta->ta_howto_data.const_thread_area
 | |
| 	      = bswap_32 (ta->ta_howto_data.const_thread_area);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  switch (sizeof (regs[0]))
 | |
| 	    {
 | |
| 	    case 8:
 | |
| 	      err = td_lookup (ta->ph, SYM_TH_UNIQUE_REGISTER64, &howto);
 | |
| 	      if (err == PS_OK)
 | |
| 		ta->ta_howto = ta_howto_reg;
 | |
| 	      else if (err == PS_NOSYM)
 | |
| 		{
 | |
| 		  err = td_lookup (ta->ph,
 | |
| 				   SYM_TH_UNIQUE_REGISTER64_THREAD_AREA,
 | |
| 				   &howto);
 | |
| 		  if (err == PS_OK)
 | |
| 		    ta->ta_howto = ta_howto_reg_thread_area;
 | |
| 		}
 | |
| 	      break;
 | |
| 
 | |
| 	    case 4:
 | |
| 	      err = td_lookup (ta->ph, SYM_TH_UNIQUE_REGISTER32, &howto);
 | |
| 	      if (err == PS_OK)
 | |
| 		ta->ta_howto = ta_howto_reg;
 | |
| 	      else if (err == PS_NOSYM)
 | |
| 		{
 | |
| 		  err = td_lookup (ta->ph,
 | |
| 				   SYM_TH_UNIQUE_REGISTER32_THREAD_AREA,
 | |
| 				   &howto);
 | |
| 		  if (err == PS_OK)
 | |
| 		    ta->ta_howto = ta_howto_reg_thread_area;
 | |
| 		}
 | |
| 	      break;
 | |
| 
 | |
| 	    default:
 | |
| 	      abort ();
 | |
| 	      return TD_DBERR;
 | |
| 	    }
 | |
| 
 | |
| 	  if (err != PS_OK)
 | |
| 	    return TD_DBERR;
 | |
| 
 | |
| 	  /* For either of these methods we read in the same descriptor.  */
 | |
| 	  err = ps_pdread (ta->ph, howto,
 | |
| 			   ta->ta_howto_data.reg, DB_SIZEOF_DESC);
 | |
| 	  if (err != PS_OK)
 | |
| 	    return TD_ERR;
 | |
| 	  if (DB_DESC_SIZE (ta->ta_howto_data.reg) == 0)
 | |
| 	    return TD_DBERR;
 | |
| 	  if (DB_DESC_SIZE (ta->ta_howto_data.reg) & 0xff000000U)
 | |
| 	    {
 | |
| 	      /* Byte-swap these words, though we leave the size word
 | |
| 		 in native order as the handy way to distinguish.  */
 | |
| 	      DB_DESC_OFFSET (ta->ta_howto_data.reg)
 | |
| 		= bswap_32 (DB_DESC_OFFSET (ta->ta_howto_data.reg));
 | |
| 	      DB_DESC_NELEM (ta->ta_howto_data.reg)
 | |
| 		= bswap_32 (DB_DESC_NELEM (ta->ta_howto_data.reg));
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   switch (ta->ta_howto)
 | |
|     {
 | |
|     default:
 | |
|       return TD_DBERR;
 | |
| 
 | |
|     case ta_howto_reg:
 | |
|       /* On most machines, we are just looking at a register.  */
 | |
|       if (ps_lgetregs (ta->ph, lwpid, regs) != PS_OK)
 | |
| 	return TD_ERR;
 | |
|       terr = _td_fetch_value_local (ta, ta->ta_howto_data.reg, -1,
 | |
| 				    0, regs, &addr);
 | |
|       if (terr != TD_OK)
 | |
| 	return terr;
 | |
| 
 | |
|       /* In this descriptor the nelem word is overloaded as the bias.  */
 | |
|       addr += (int32_t) DB_DESC_NELEM (ta->ta_howto_data.reg);
 | |
|       th->th_unique = addr;
 | |
|       break;
 | |
| 
 | |
|     case ta_howto_const_thread_area:
 | |
|       /* Some hosts don't have this call and this case won't be used.  */
 | |
| # pragma weak ps_get_thread_area
 | |
|       if (&ps_get_thread_area == NULL)
 | |
| 	return TD_NOCAPAB;
 | |
| 
 | |
|       /* A la x86-64, there is a magic index for get_thread_area.  */
 | |
|       if (ps_get_thread_area (ta->ph, lwpid,
 | |
| 			      ta->ta_howto_data.const_thread_area,
 | |
| 			      &th->th_unique) != PS_OK)
 | |
| 	return TD_ERR;	/* XXX Other error value?  */
 | |
|       break;
 | |
| 
 | |
|     case ta_howto_reg_thread_area:
 | |
|       if (&ps_get_thread_area == NULL)
 | |
| 	return TD_NOCAPAB;
 | |
| 
 | |
|       /* A la i386, a register holds the index for get_thread_area.  */
 | |
|       if (ps_lgetregs (ta->ph, lwpid, regs) != PS_OK)
 | |
| 	return TD_ERR;
 | |
|       terr = _td_fetch_value_local (ta, ta->ta_howto_data.reg_thread_area,
 | |
| 				    -1, 0, regs, &addr);
 | |
|       if (terr != TD_OK)
 | |
| 	return terr;
 | |
|       /* In this descriptor the nelem word is overloaded as scale factor.  */
 | |
|       if (ps_get_thread_area
 | |
| 	  (ta->ph, lwpid,
 | |
| 	   ((addr - (psaddr_t) 0)
 | |
| 	    >> DB_DESC_NELEM (ta->ta_howto_data.reg_thread_area)),
 | |
| 	   &th->th_unique) != PS_OK)
 | |
| 	return TD_ERR;	/* XXX Other error value?  */
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   /* Found it.  Now complete the `td_thrhandle_t' object.  */
 | |
|   th->th_ta_p = ta;
 | |
| 
 | |
|   return TD_OK;
 | |
| }
 | |
| 
 | |
| td_err_e
 | |
| td_ta_map_lwp2thr (const td_thragent_t *ta_arg,
 | |
| 		   lwpid_t lwpid, td_thrhandle_t *th)
 | |
| {
 | |
|   td_thragent_t *const ta = (td_thragent_t *) ta_arg;
 | |
| 
 | |
|   LOG ("td_ta_map_lwp2thr");
 | |
| 
 | |
|   /* Test whether the TA parameter is ok.  */
 | |
|   if (! ta_ok (ta))
 | |
|     return TD_BADTA;
 | |
| 
 | |
|   /* We cannot rely on thread registers and such information at all
 | |
|      before __pthread_initialize_minimal has gotten far enough.  They
 | |
|      sometimes contain garbage that would confuse us, left by the kernel
 | |
|      at exec.  So if it looks like initialization is incomplete, we only
 | |
|      fake a special descriptor for the initial thread.  */
 | |
| 
 | |
|   psaddr_t list;
 | |
|   td_err_e err = DB_GET_SYMBOL (list, ta, __stack_user);
 | |
|   if (err != TD_OK)
 | |
|     return err;
 | |
| 
 | |
|   err = DB_GET_FIELD (list, ta, list, list_t, next, 0);
 | |
|   if (err != TD_OK)
 | |
|     return err;
 | |
| 
 | |
|   if (list == 0)
 | |
|     {
 | |
|       if (ps_getpid (ta->ph) != lwpid)
 | |
| 	return TD_ERR;
 | |
|       th->th_ta_p = ta;
 | |
|       th->th_unique = 0;
 | |
|       return TD_OK;
 | |
|     }
 | |
| 
 | |
|   return __td_ta_lookup_th_unique (ta_arg, lwpid, th);
 | |
| }
 |