glibc/nptl_db/td_ta_map_lwp2thr.c
Florian Weimer 1daccf403b nptl: Move stack list variables into _rtld_global
Now __thread_gscope_wait (the function behind THREAD_GSCOPE_WAIT,
formerly __wait_lookup_done) can be implemented directly in ld.so,
eliminating the unprotected GL (dl_wait_lookup_done) function
pointer.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2020-11-16 19:33:30 +01:00

209 lines
6.0 KiB
C

/* Which thread is running on an LWP?
Copyright (C) 2003-2020 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
<https://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 = __td_ta_stack_user (ta, &list);
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);
}