| /* Solaris threads debugging interface. | 
 |  | 
 |    Copyright (C) 1996-2022 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program 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 General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* This module implements a sort of half target that sits between the | 
 |    machine-independent parts of GDB and the /proc interface (procfs.c) | 
 |    to provide access to the Solaris user-mode thread implementation. | 
 |  | 
 |    Solaris threads are true user-mode threads, which are invoked via | 
 |    the thr_* and pthread_* (native and POSIX respectively) interfaces. | 
 |    These are mostly implemented in user-space, with all thread context | 
 |    kept in various structures that live in the user's heap.  These | 
 |    should not be confused with lightweight processes (LWPs), which are | 
 |    implemented by the kernel, and scheduled without explicit | 
 |    intervention by the process. | 
 |  | 
 |    Just to confuse things a little, Solaris threads (both native and | 
 |    POSIX) are actually implemented using LWPs.  In general, there are | 
 |    going to be more threads than LWPs.  There is no fixed | 
 |    correspondence between a thread and an LWP.  When a thread wants to | 
 |    run, it gets scheduled onto the first available LWP and can | 
 |    therefore migrate from one LWP to another as time goes on.  A | 
 |    sleeping thread may not be associated with an LWP at all! | 
 |  | 
 |    To make it possible to mess with threads, Sun provides a library | 
 |    called libthread_db.so.1 (not to be confused with | 
 |    libthread_db.so.0, which doesn't have a published interface).  This | 
 |    interface has an upper part, which it provides, and a lower part | 
 |    which we provide.  The upper part consists of the td_* routines, | 
 |    which allow us to find all the threads, query their state, etc... | 
 |    The lower part consists of all of the ps_*, which are used by the | 
 |    td_* routines to read/write memory, manipulate LWPs, lookup | 
 |    symbols, etc...  The ps_* routines actually do most of their work | 
 |    by calling functions in procfs.c.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include <thread.h> | 
 | #include <proc_service.h> | 
 | #include <thread_db.h> | 
 | #include "gdbthread.h" | 
 | #include "target.h" | 
 | #include "inferior.h" | 
 | #include <fcntl.h> | 
 | #include <sys/stat.h> | 
 | #include <dlfcn.h> | 
 | #include "gdbcmd.h" | 
 | #include "gdbcore.h" | 
 | #include "regcache.h" | 
 | #include "solib.h" | 
 | #include "symfile.h" | 
 | #include "observable.h" | 
 | #include "procfs.h" | 
 | #include "symtab.h" | 
 | #include "minsyms.h" | 
 | #include "objfiles.h" | 
 |  | 
 | static const target_info thread_db_target_info = { | 
 |   "solaris-threads", | 
 |   N_("Solaris threads and pthread."), | 
 |   N_("Solaris threads and pthread support.") | 
 | }; | 
 |  | 
 | class sol_thread_target final : public target_ops | 
 | { | 
 | public: | 
 |   const target_info &info () const override | 
 |   { return thread_db_target_info; } | 
 |  | 
 |   strata stratum () const override { return thread_stratum; } | 
 |  | 
 |   void detach (inferior *, int) override; | 
 |   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override; | 
 |   void resume (ptid_t, int, enum gdb_signal) override; | 
 |   void mourn_inferior () override; | 
 |   std::string pid_to_str (ptid_t) override; | 
 |   ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override; | 
 |  | 
 |   void fetch_registers (struct regcache *, int) override; | 
 |   void store_registers (struct regcache *, int) override; | 
 |  | 
 |   enum target_xfer_status xfer_partial (enum target_object object, | 
 | 					const char *annex, | 
 | 					gdb_byte *readbuf, | 
 | 					const gdb_byte *writebuf, | 
 | 					ULONGEST offset, ULONGEST len, | 
 | 					ULONGEST *xfered_len) override; | 
 |  | 
 |   bool thread_alive (ptid_t ptid) override; | 
 |   void update_thread_list () override; | 
 | }; | 
 |  | 
 | static sol_thread_target sol_thread_ops; | 
 |  | 
 | /* Prototypes for supply_gregset etc.  */ | 
 | #include "gregset.h" | 
 |  | 
 | /* This struct is defined by us, but mainly used for the proc_service | 
 |    interface.  We don't have much use for it, except as a handy place | 
 |    to get a real PID for memory accesses.  */ | 
 |  | 
 | struct ps_prochandle | 
 | { | 
 |   ptid_t ptid; | 
 | }; | 
 |  | 
 | struct string_map | 
 | { | 
 |   int num; | 
 |   const char *str; | 
 | }; | 
 |  | 
 | static struct ps_prochandle main_ph; | 
 | static td_thragent_t *main_ta; | 
 | static int sol_thread_active = 0; | 
 |  | 
 | /* Default definitions: These must be defined in tm.h if they are to | 
 |    be shared with a process module such as procfs.  */ | 
 |  | 
 | /* Types of the libthread_db functions.  */ | 
 |  | 
 | typedef void (td_log_ftype)(const int on_off); | 
 | typedef td_err_e (td_ta_new_ftype)(const struct ps_prochandle *ph_p, | 
 | 				   td_thragent_t **ta_pp); | 
 | typedef td_err_e (td_ta_delete_ftype)(td_thragent_t *ta_p); | 
 | typedef td_err_e (td_init_ftype)(void); | 
 | typedef td_err_e (td_ta_get_ph_ftype)(const td_thragent_t *ta_p, | 
 | 				      struct ps_prochandle **ph_pp); | 
 | typedef td_err_e (td_ta_get_nthreads_ftype)(const td_thragent_t *ta_p, | 
 | 					    int *nthread_p); | 
 | typedef td_err_e (td_ta_tsd_iter_ftype)(const td_thragent_t *ta_p, | 
 | 					td_key_iter_f *cb, void *cbdata_p); | 
 | typedef td_err_e (td_ta_thr_iter_ftype)(const td_thragent_t *ta_p, | 
 | 					td_thr_iter_f *cb, void *cbdata_p, | 
 | 					td_thr_state_e state, int ti_pri, | 
 | 					sigset_t *ti_sigmask_p, | 
 | 					unsigned ti_user_flags); | 
 | typedef td_err_e (td_thr_validate_ftype)(const td_thrhandle_t *th_p); | 
 | typedef td_err_e (td_thr_tsd_ftype)(const td_thrhandle_t * th_p, | 
 | 				    const thread_key_t key, void **data_pp); | 
 | typedef td_err_e (td_thr_get_info_ftype)(const td_thrhandle_t *th_p, | 
 | 					 td_thrinfo_t *ti_p); | 
 | typedef td_err_e (td_thr_getfpregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					  prfpregset_t *fpregset); | 
 | typedef td_err_e (td_thr_getxregsize_ftype)(const td_thrhandle_t *th_p, | 
 | 					    int *xregsize); | 
 | typedef td_err_e (td_thr_getxregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					 const caddr_t xregset); | 
 | typedef td_err_e (td_thr_sigsetmask_ftype)(const td_thrhandle_t *th_p, | 
 | 					   const sigset_t ti_sigmask); | 
 | typedef td_err_e (td_thr_setprio_ftype)(const td_thrhandle_t *th_p, | 
 | 					const int ti_pri); | 
 | typedef td_err_e (td_thr_setsigpending_ftype)(const td_thrhandle_t *th_p, | 
 | 					      const uchar_t ti_pending_flag, | 
 | 					      const sigset_t ti_pending); | 
 | typedef td_err_e (td_thr_setfpregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					  const prfpregset_t *fpregset); | 
 | typedef td_err_e (td_thr_setxregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					 const caddr_t xregset); | 
 | typedef td_err_e (td_ta_map_id2thr_ftype)(const td_thragent_t *ta_p, | 
 | 					  thread_t tid, | 
 | 					  td_thrhandle_t *th_p); | 
 | typedef td_err_e (td_ta_map_lwp2thr_ftype)(const td_thragent_t *ta_p, | 
 | 					   lwpid_t lwpid, | 
 | 					   td_thrhandle_t *th_p); | 
 | typedef td_err_e (td_thr_getgregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					 prgregset_t regset); | 
 | typedef td_err_e (td_thr_setgregs_ftype)(const td_thrhandle_t *th_p, | 
 | 					 const prgregset_t regset); | 
 |  | 
 | /* Pointers to routines from libthread_db resolved by dlopen().  */ | 
 |  | 
 | static td_log_ftype *p_td_log; | 
 | static td_ta_new_ftype *p_td_ta_new; | 
 | static td_ta_delete_ftype *p_td_ta_delete; | 
 | static td_init_ftype *p_td_init; | 
 | static td_ta_get_ph_ftype *p_td_ta_get_ph; | 
 | static td_ta_get_nthreads_ftype *p_td_ta_get_nthreads; | 
 | static td_ta_tsd_iter_ftype *p_td_ta_tsd_iter; | 
 | static td_ta_thr_iter_ftype *p_td_ta_thr_iter; | 
 | static td_thr_validate_ftype *p_td_thr_validate; | 
 | static td_thr_tsd_ftype *p_td_thr_tsd; | 
 | static td_thr_get_info_ftype *p_td_thr_get_info; | 
 | static td_thr_getfpregs_ftype *p_td_thr_getfpregs; | 
 | static td_thr_getxregsize_ftype *p_td_thr_getxregsize; | 
 | static td_thr_getxregs_ftype *p_td_thr_getxregs; | 
 | static td_thr_sigsetmask_ftype *p_td_thr_sigsetmask; | 
 | static td_thr_setprio_ftype *p_td_thr_setprio; | 
 | static td_thr_setsigpending_ftype *p_td_thr_setsigpending; | 
 | static td_thr_setfpregs_ftype *p_td_thr_setfpregs; | 
 | static td_thr_setxregs_ftype *p_td_thr_setxregs; | 
 | static td_ta_map_id2thr_ftype *p_td_ta_map_id2thr; | 
 | static td_ta_map_lwp2thr_ftype *p_td_ta_map_lwp2thr; | 
 | static td_thr_getgregs_ftype *p_td_thr_getgregs; | 
 | static td_thr_setgregs_ftype *p_td_thr_setgregs; | 
 |  | 
 |  | 
 | /* Return the libthread_db error string associated with ERRCODE.  If | 
 |    ERRCODE is unknown, return an appropriate message.  */ | 
 |  | 
 | static const char * | 
 | td_err_string (td_err_e errcode) | 
 | { | 
 |   static struct string_map td_err_table[] = | 
 |   { | 
 |     { TD_OK, "generic \"call succeeded\"" }, | 
 |     { TD_ERR, "generic error." }, | 
 |     { TD_NOTHR, "no thread can be found to satisfy query" }, | 
 |     { TD_NOSV, "no synch. variable can be found to satisfy query" }, | 
 |     { TD_NOLWP, "no lwp can be found to satisfy query" }, | 
 |     { TD_BADPH, "invalid process handle" }, | 
 |     { TD_BADTH, "invalid thread handle" }, | 
 |     { TD_BADSH, "invalid synchronization handle" }, | 
 |     { TD_BADTA, "invalid thread agent" }, | 
 |     { TD_BADKEY, "invalid key" }, | 
 |     { TD_NOMSG, "td_thr_event_getmsg() called when there was no message" }, | 
 |     { TD_NOFPREGS, "FPU register set not available for given thread" }, | 
 |     { TD_NOLIBTHREAD, "application not linked with libthread" }, | 
 |     { TD_NOEVENT, "requested event is not supported" }, | 
 |     { TD_NOCAPAB, "capability not available" }, | 
 |     { TD_DBERR, "Debugger service failed" }, | 
 |     { TD_NOAPLIC, "Operation not applicable to" }, | 
 |     { TD_NOTSD, "No thread specific data for this thread" }, | 
 |     { TD_MALLOC, "Malloc failed" }, | 
 |     { TD_PARTIALREG, "Only part of register set was written/read" }, | 
 |     { TD_NOXREGS, "X register set not available for given thread" } | 
 |   }; | 
 |   const int td_err_size = sizeof td_err_table / sizeof (struct string_map); | 
 |   int i; | 
 |   static char buf[50]; | 
 |  | 
 |   for (i = 0; i < td_err_size; i++) | 
 |     if (td_err_table[i].num == errcode) | 
 |       return td_err_table[i].str; | 
 |  | 
 |   xsnprintf (buf, sizeof (buf), "Unknown libthread_db error code: %d", | 
 | 	     errcode); | 
 |  | 
 |   return buf; | 
 | } | 
 |  | 
 | /* Return the libthread_db state string associated with STATECODE. | 
 |    If STATECODE is unknown, return an appropriate message.  */ | 
 |  | 
 | static const char * | 
 | td_state_string (td_thr_state_e statecode) | 
 | { | 
 |   static struct string_map td_thr_state_table[] = | 
 |   { | 
 |     { TD_THR_ANY_STATE, "any state" }, | 
 |     { TD_THR_UNKNOWN, "unknown" }, | 
 |     { TD_THR_STOPPED, "stopped" }, | 
 |     { TD_THR_RUN, "run" }, | 
 |     { TD_THR_ACTIVE, "active" }, | 
 |     { TD_THR_ZOMBIE, "zombie" }, | 
 |     { TD_THR_SLEEP, "sleep" }, | 
 |     { TD_THR_STOPPED_ASLEEP, "stopped asleep" } | 
 |   }; | 
 |   const int td_thr_state_table_size = | 
 |     sizeof td_thr_state_table / sizeof (struct string_map); | 
 |   int i; | 
 |   static char buf[50]; | 
 |  | 
 |   for (i = 0; i < td_thr_state_table_size; i++) | 
 |     if (td_thr_state_table[i].num == statecode) | 
 |       return td_thr_state_table[i].str; | 
 |  | 
 |   xsnprintf (buf, sizeof (buf), "Unknown libthread_db state code: %d", | 
 | 	     statecode); | 
 |  | 
 |   return buf; | 
 | } | 
 |  | 
 |  | 
 | /* Convert a POSIX or Solaris thread ID into a LWP ID.  If THREAD_ID | 
 |    doesn't exist, that's an error.  If it's an inactive thread, return | 
 |    DEFAULT_LWP. | 
 |  | 
 |    NOTE: This function probably shouldn't call error().  */ | 
 |  | 
 | static ptid_t | 
 | thread_to_lwp (ptid_t thread_id, int default_lwp) | 
 | { | 
 |   td_thrinfo_t ti; | 
 |   td_thrhandle_t th; | 
 |   td_err_e val; | 
 |  | 
 |   if (thread_id.lwp_p ()) | 
 |     return thread_id;		/* It's already an LWP ID.  */ | 
 |  | 
 |   /* It's a thread.  Convert to LWP.  */ | 
 |  | 
 |   val = p_td_ta_map_id2thr (main_ta, thread_id.tid (), &th); | 
 |   if (val == TD_NOTHR) | 
 |     return ptid_t (-1);	/* Thread must have terminated.  */ | 
 |   else if (val != TD_OK) | 
 |     error (_("thread_to_lwp: td_ta_map_id2thr %s"), td_err_string (val)); | 
 |  | 
 |   val = p_td_thr_get_info (&th, &ti); | 
 |   if (val == TD_NOTHR) | 
 |     return ptid_t (-1);	/* Thread must have terminated.  */ | 
 |   else if (val != TD_OK) | 
 |     error (_("thread_to_lwp: td_thr_get_info: %s"), td_err_string (val)); | 
 |  | 
 |   if (ti.ti_state != TD_THR_ACTIVE) | 
 |     { | 
 |       if (default_lwp != -1) | 
 | 	return ptid_t (default_lwp); | 
 |       error (_("thread_to_lwp: thread state not active: %s"), | 
 | 	     td_state_string (ti.ti_state)); | 
 |     } | 
 |  | 
 |   return ptid_t (thread_id.pid (), ti.ti_lid, 0); | 
 | } | 
 |  | 
 | /* Convert an LWP ID into a POSIX or Solaris thread ID.  If LWP_ID | 
 |    doesn't exists, that's an error. | 
 |  | 
 |    NOTE: This function probably shouldn't call error().  */ | 
 |  | 
 | static ptid_t | 
 | lwp_to_thread (ptid_t lwp) | 
 | { | 
 |   td_thrinfo_t ti; | 
 |   td_thrhandle_t th; | 
 |   td_err_e val; | 
 |  | 
 |   if (lwp.tid_p ()) | 
 |     return lwp;			/* It's already a thread ID.  */ | 
 |  | 
 |   /* It's an LWP.  Convert it to a thread ID.  */ | 
 |  | 
 |   if (!target_thread_alive (lwp)) | 
 |     return ptid_t (-1);	/* Must be a defunct LPW.  */ | 
 |  | 
 |   val = p_td_ta_map_lwp2thr (main_ta, lwp.lwp (), &th); | 
 |   if (val == TD_NOTHR) | 
 |     return ptid_t (-1);	/* Thread must have terminated.  */ | 
 |   else if (val != TD_OK) | 
 |     error (_("lwp_to_thread: td_ta_map_lwp2thr: %s."), td_err_string (val)); | 
 |  | 
 |   val = p_td_thr_validate (&th); | 
 |   if (val == TD_NOTHR) | 
 |     return lwp;			/* Unknown to libthread; just return LPW,  */ | 
 |   else if (val != TD_OK) | 
 |     error (_("lwp_to_thread: td_thr_validate: %s."), td_err_string (val)); | 
 |  | 
 |   val = p_td_thr_get_info (&th, &ti); | 
 |   if (val == TD_NOTHR) | 
 |     return ptid_t (-1);	/* Thread must have terminated.  */ | 
 |   else if (val != TD_OK) | 
 |     error (_("lwp_to_thread: td_thr_get_info: %s."), td_err_string (val)); | 
 |  | 
 |   return ptid_t (lwp.pid (), 0 , ti.ti_tid); | 
 | } | 
 |  | 
 |  | 
 | /* Most target vector functions from here on actually just pass | 
 |    through to the layer beneath, as they don't need to do anything | 
 |    specific for threads.  */ | 
 |  | 
 | /* Take a program previously attached to and detaches it.  The program | 
 |    resumes execution and will no longer stop on signals, etc.  We'd | 
 |    better not have left any breakpoints in the program or it'll die | 
 |    when it hits one.  For this to work, it may be necessary for the | 
 |    process to have been previously attached.  It *might* work if the | 
 |    program was started via the normal ptrace (PTRACE_TRACEME).  */ | 
 |  | 
 | void | 
 | sol_thread_target::detach (inferior *inf, int from_tty) | 
 | { | 
 |   target_ops *beneath = this->beneath (); | 
 |  | 
 |   sol_thread_active = 0; | 
 |   inferior_ptid = ptid_t (main_ph.ptid.pid ()); | 
 |   inf->unpush_target (this); | 
 |   beneath->detach (inf, from_tty); | 
 | } | 
 |  | 
 | /* Resume execution of process PTID.  If STEP is nonzero, then just | 
 |    single step it.  If SIGNAL is nonzero, restart it with that signal | 
 |    activated.  We may have to convert PTID from a thread ID to an LWP | 
 |    ID for procfs.  */ | 
 |  | 
 | void | 
 | sol_thread_target::resume (ptid_t ptid, int step, enum gdb_signal signo) | 
 | { | 
 |   scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); | 
 |  | 
 |   inferior_ptid = thread_to_lwp (inferior_ptid, main_ph.ptid.pid ()); | 
 |   if (inferior_ptid.pid () == -1) | 
 |     inferior_ptid = procfs_first_available (); | 
 |  | 
 |   if (ptid.pid () != -1) | 
 |     { | 
 |       ptid_t save_ptid = ptid; | 
 |  | 
 |       ptid = thread_to_lwp (ptid, -2); | 
 |       if (ptid.pid () == -2)		/* Inactive thread.  */ | 
 | 	error (_("This version of Solaris can't start inactive threads.")); | 
 |       if (info_verbose && ptid.pid () == -1) | 
 | 	warning (_("Specified thread %ld seems to have terminated"), | 
 | 		 save_ptid.tid ()); | 
 |     } | 
 |  | 
 |   beneath ()->resume (ptid, step, signo); | 
 | } | 
 |  | 
 | /* Wait for any threads to stop.  We may have to convert PTID from a | 
 |    thread ID to an LWP ID, and vice versa on the way out.  */ | 
 |  | 
 | ptid_t | 
 | sol_thread_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, | 
 | 			 target_wait_flags options) | 
 | { | 
 |   if (ptid.pid () != -1) | 
 |     { | 
 |       ptid_t ptid_for_warning = ptid; | 
 |  | 
 |       ptid = thread_to_lwp (ptid, -2); | 
 |       if (ptid.pid () == -2)		/* Inactive thread.  */ | 
 | 	error (_("This version of Solaris can't start inactive threads.")); | 
 |       if (info_verbose && ptid.pid () == -1) | 
 | 	warning (_("Specified thread %ld seems to have terminated"), | 
 | 		 ptid_for_warning.tid ()); | 
 |     } | 
 |  | 
 |   ptid_t rtnval = beneath ()->wait (ptid, ourstatus, options); | 
 |  | 
 |   if (ourstatus->kind () != TARGET_WAITKIND_EXITED) | 
 |     { | 
 |       /* Map the LWP of interest back to the appropriate thread ID.  */ | 
 |       ptid_t thr_ptid = lwp_to_thread (rtnval); | 
 |       if (thr_ptid.pid () != -1) | 
 | 	rtnval = thr_ptid; | 
 |  | 
 |       /* See if we have a new thread.  */ | 
 |       if (rtnval.tid_p ()) | 
 | 	{ | 
 | 	  thread_info *thr = find_thread_ptid (current_inferior (), rtnval); | 
 | 	  if (thr == NULL || thr->state == THREAD_EXITED) | 
 | 	    { | 
 | 	      process_stratum_target *proc_target | 
 | 		= current_inferior ()->process_target (); | 
 | 	      add_thread (proc_target, rtnval); | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   /* During process initialization, we may get here without the thread | 
 |      package being initialized, since that can only happen after we've | 
 |      found the shared libs.  */ | 
 |  | 
 |   return rtnval; | 
 | } | 
 |  | 
 | void | 
 | sol_thread_target::fetch_registers (struct regcache *regcache, int regnum) | 
 | { | 
 |   thread_t thread; | 
 |   td_thrhandle_t thandle; | 
 |   td_err_e val; | 
 |   prgregset_t gregset; | 
 |   prfpregset_t fpregset; | 
 |   gdb_gregset_t *gregset_p = &gregset; | 
 |   gdb_fpregset_t *fpregset_p = &fpregset; | 
 |   ptid_t ptid = regcache->ptid (); | 
 |  | 
 |   if (!ptid.tid_p ()) | 
 |     { | 
 |       /* It's an LWP; pass the request on to the layer beneath.  */ | 
 |       beneath ()->fetch_registers (regcache, regnum); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Solaris thread: convert PTID into a td_thrhandle_t.  */ | 
 |   thread = ptid.tid (); | 
 |   if (thread == 0) | 
 |     error (_("sol_thread_fetch_registers: thread == 0")); | 
 |  | 
 |   val = p_td_ta_map_id2thr (main_ta, thread, &thandle); | 
 |   if (val != TD_OK) | 
 |     error (_("sol_thread_fetch_registers: td_ta_map_id2thr: %s"), | 
 | 	   td_err_string (val)); | 
 |  | 
 |   /* Get the general-purpose registers.  */ | 
 |  | 
 |   val = p_td_thr_getgregs (&thandle, gregset); | 
 |   if (val != TD_OK && val != TD_PARTIALREG) | 
 |     error (_("sol_thread_fetch_registers: td_thr_getgregs %s"), | 
 | 	   td_err_string (val)); | 
 |  | 
 |   /* For SPARC, TD_PARTIALREG means that only %i0...%i7, %l0..%l7, %pc | 
 |      and %sp are saved (by a thread context switch).  */ | 
 |  | 
 |   /* And, now the floating-point registers.  */ | 
 |  | 
 |   val = p_td_thr_getfpregs (&thandle, &fpregset); | 
 |   if (val != TD_OK && val != TD_NOFPREGS) | 
 |     error (_("sol_thread_fetch_registers: td_thr_getfpregs %s"), | 
 | 	   td_err_string (val)); | 
 |  | 
 |   /* Note that we must call supply_gregset and supply_fpregset *after* | 
 |      calling the td routines because the td routines call ps_lget* | 
 |      which affect the values stored in the registers array.  */ | 
 |  | 
 |   supply_gregset (regcache, (const gdb_gregset_t *) gregset_p); | 
 |   supply_fpregset (regcache, (const gdb_fpregset_t *) fpregset_p); | 
 | } | 
 |  | 
 | void | 
 | sol_thread_target::store_registers (struct regcache *regcache, int regnum) | 
 | { | 
 |   thread_t thread; | 
 |   td_thrhandle_t thandle; | 
 |   td_err_e val; | 
 |   prgregset_t gregset; | 
 |   prfpregset_t fpregset; | 
 |   ptid_t ptid = regcache->ptid (); | 
 |  | 
 |   if (!ptid.tid_p ()) | 
 |     { | 
 |       /* It's an LWP; pass the request on to the layer beneath.  */ | 
 |       beneath ()->store_registers (regcache, regnum); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Solaris thread: convert PTID into a td_thrhandle_t.  */ | 
 |   thread = ptid.tid (); | 
 |  | 
 |   val = p_td_ta_map_id2thr (main_ta, thread, &thandle); | 
 |   if (val != TD_OK) | 
 |     error (_("sol_thread_store_registers: td_ta_map_id2thr %s"), | 
 | 	   td_err_string (val)); | 
 |  | 
 |   if (regnum != -1) | 
 |     { | 
 |       val = p_td_thr_getgregs (&thandle, gregset); | 
 |       if (val != TD_OK) | 
 | 	error (_("sol_thread_store_registers: td_thr_getgregs %s"), | 
 | 	       td_err_string (val)); | 
 |       val = p_td_thr_getfpregs (&thandle, &fpregset); | 
 |       if (val != TD_OK) | 
 | 	error (_("sol_thread_store_registers: td_thr_getfpregs %s"), | 
 | 	       td_err_string (val)); | 
 |     } | 
 |  | 
 |   fill_gregset (regcache, (gdb_gregset_t *) &gregset, regnum); | 
 |   fill_fpregset (regcache, (gdb_fpregset_t *) &fpregset, regnum); | 
 |  | 
 |   val = p_td_thr_setgregs (&thandle, gregset); | 
 |   if (val != TD_OK) | 
 |     error (_("sol_thread_store_registers: td_thr_setgregs %s"), | 
 | 	   td_err_string (val)); | 
 |   val = p_td_thr_setfpregs (&thandle, &fpregset); | 
 |   if (val != TD_OK) | 
 |     error (_("sol_thread_store_registers: td_thr_setfpregs %s"), | 
 | 	   td_err_string (val)); | 
 | } | 
 |  | 
 | /* Perform partial transfers on OBJECT.  See target_read_partial and | 
 |    target_write_partial for details of each variant.  One, and only | 
 |    one, of readbuf or writebuf must be non-NULL.  */ | 
 |  | 
 | enum target_xfer_status | 
 | sol_thread_target::xfer_partial (enum target_object object, | 
 | 				 const char *annex, gdb_byte *readbuf, | 
 | 				 const gdb_byte *writebuf, | 
 | 				 ULONGEST offset, ULONGEST len, | 
 | 				 ULONGEST *xfered_len) | 
 | { | 
 |   scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); | 
 |  | 
 |   if (inferior_ptid.tid_p () || !target_thread_alive (inferior_ptid)) | 
 |     { | 
 |       /* It's either a thread or an LWP that isn't alive.  Any live | 
 | 	 LWP will do so use the first available. | 
 |  | 
 | 	 NOTE: We don't need to call switch_to_thread; we're just | 
 | 	 reading memory.  */ | 
 |       inferior_ptid = procfs_first_available (); | 
 |     } | 
 |  | 
 |   return beneath ()->xfer_partial (object, annex, readbuf, | 
 | 				   writebuf, offset, len, xfered_len); | 
 | } | 
 |  | 
 | static void | 
 | check_for_thread_db (void) | 
 | { | 
 |   td_err_e err; | 
 |   ptid_t ptid; | 
 |  | 
 |   /* Don't attempt to use thread_db for remote targets.  */ | 
 |   if (!(target_can_run () || core_bfd)) | 
 |     return; | 
 |  | 
 |   /* Do nothing if we couldn't load libthread_db.so.1.  */ | 
 |   if (p_td_ta_new == NULL) | 
 |     return; | 
 |  | 
 |   if (sol_thread_active) | 
 |     /* Nothing to do.  The thread library was already detected and the | 
 |        target vector was already activated.  */ | 
 |     return; | 
 |  | 
 |   /* Now, initialize libthread_db.  This needs to be done after the | 
 |      shared libraries are located because it needs information from | 
 |      the user's thread library.  */ | 
 |  | 
 |   err = p_td_init (); | 
 |   if (err != TD_OK) | 
 |     { | 
 |       warning (_("sol_thread_new_objfile: td_init: %s"), td_err_string (err)); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Now attempt to open a connection to the thread library.  */ | 
 |   err = p_td_ta_new (&main_ph, &main_ta); | 
 |   switch (err) | 
 |     { | 
 |     case TD_NOLIBTHREAD: | 
 |       /* No thread library was detected.  */ | 
 |       break; | 
 |  | 
 |     case TD_OK: | 
 |       gdb_printf (_("[Thread debugging using libthread_db enabled]\n")); | 
 |  | 
 |       /* The thread library was detected.  Activate the sol_thread target.  */ | 
 |       current_inferior ()->push_target (&sol_thread_ops); | 
 |       sol_thread_active = 1; | 
 |  | 
 |       main_ph.ptid = inferior_ptid; /* Save for xfer_memory.  */ | 
 |       ptid = lwp_to_thread (inferior_ptid); | 
 |       if (ptid.pid () != -1) | 
 | 	inferior_ptid = ptid; | 
 |  | 
 |       target_update_thread_list (); | 
 |       break; | 
 |  | 
 |     default: | 
 |       warning (_("Cannot initialize thread debugging library: %s"), | 
 | 	       td_err_string (err)); | 
 |       break; | 
 |     } | 
 | } | 
 |  | 
 | /* This routine is called whenever a new symbol table is read in, or | 
 |    when all symbol tables are removed.  libthread_db can only be | 
 |    initialized when it finds the right variables in libthread.so. | 
 |    Since it's a shared library, those variables don't show up until | 
 |    the library gets mapped and the symbol table is read in.  */ | 
 |  | 
 | static void | 
 | sol_thread_new_objfile (struct objfile *objfile) | 
 | { | 
 |   if (objfile != NULL) | 
 |     check_for_thread_db (); | 
 | } | 
 |  | 
 | /* Clean up after the inferior dies.  */ | 
 |  | 
 | void | 
 | sol_thread_target::mourn_inferior () | 
 | { | 
 |   target_ops *beneath = this->beneath (); | 
 |  | 
 |   sol_thread_active = 0; | 
 |  | 
 |   current_inferior ()->unpush_target (this); | 
 |  | 
 |   beneath->mourn_inferior (); | 
 | } | 
 |  | 
 | /* Return true if PTID is still active in the inferior.  */ | 
 |  | 
 | bool | 
 | sol_thread_target::thread_alive (ptid_t ptid) | 
 | { | 
 |   if (ptid.tid_p ()) | 
 |     { | 
 |       /* It's a (user-level) thread.  */ | 
 |       td_err_e val; | 
 |       td_thrhandle_t th; | 
 |       int pid; | 
 |  | 
 |       pid = ptid.tid (); | 
 |       val = p_td_ta_map_id2thr (main_ta, pid, &th); | 
 |       if (val != TD_OK) | 
 | 	return false;		/* Thread not found.  */ | 
 |       val = p_td_thr_validate (&th); | 
 |       if (val != TD_OK) | 
 | 	return false;		/* Thread not valid.  */ | 
 |       return true;		/* Known thread.  */ | 
 |     } | 
 |   else | 
 |     { | 
 |       /* It's an LPW; pass the request on to the layer below.  */ | 
 |       return beneath ()->thread_alive (ptid); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /* These routines implement the lower half of the thread_db interface, | 
 |    i.e. the ps_* routines.  */ | 
 |  | 
 | /* The next four routines are called by libthread_db to tell us to | 
 |    stop and stop a particular process or lwp.  Since GDB ensures that | 
 |    these are all stopped by the time we call anything in thread_db, | 
 |    these routines need to do nothing.  */ | 
 |  | 
 | /* Process stop.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pstop (struct ps_prochandle *ph) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Process continue.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pcontinue (struct ps_prochandle *ph) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* LWP stop.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lstop (struct ps_prochandle *ph, lwpid_t lwpid) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* LWP continue.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lcontinue (struct ps_prochandle *ph, lwpid_t lwpid) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Looks up the symbol LD_SYMBOL_NAME in the debugger's symbol table.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pglobal_lookup (struct ps_prochandle *ph, const char *ld_object_name, | 
 | 		   const char *ld_symbol_name, psaddr_t *ld_symbol_addr) | 
 | { | 
 |   struct bound_minimal_symbol ms; | 
 |  | 
 |   ms = lookup_minimal_symbol (ld_symbol_name, NULL, NULL); | 
 |   if (!ms.minsym) | 
 |     return PS_NOSYM; | 
 |  | 
 |   *ld_symbol_addr = ms.value_address (); | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Common routine for reading and writing memory.  */ | 
 |  | 
 | static ps_err_e | 
 | rw_common (int dowrite, const struct ps_prochandle *ph, psaddr_t addr, | 
 | 	   gdb_byte *buf, int size) | 
 | { | 
 |   int ret; | 
 |  | 
 |   scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); | 
 |  | 
 |   if (inferior_ptid.tid_p () || !target_thread_alive (inferior_ptid)) | 
 |     { | 
 |       /* It's either a thread or an LWP that isn't alive.  Any live | 
 | 	 LWP will do so use the first available. | 
 |  | 
 | 	 NOTE: We don't need to call switch_to_thread; we're just | 
 | 	 reading memory.  */ | 
 |       inferior_ptid = procfs_first_available (); | 
 |     } | 
 |  | 
 | #if defined (__sparcv9) | 
 |   /* For Sparc64 cross Sparc32, make sure the address has not been | 
 |      accidentally sign-extended (or whatever) to beyond 32 bits.  */ | 
 |   if (bfd_get_arch_size (current_program_space->exec_bfd ()) == 32) | 
 |     addr &= 0xffffffff; | 
 | #endif | 
 |  | 
 |   if (dowrite) | 
 |     ret = target_write_memory (addr, (gdb_byte *) buf, size); | 
 |   else | 
 |     ret = target_read_memory (addr, (gdb_byte *) buf, size); | 
 |  | 
 |   return (ret == 0 ? PS_OK : PS_ERR); | 
 | } | 
 |  | 
 | /* Copies SIZE bytes from target process .data segment to debugger memory.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pdread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t size) | 
 | { | 
 |   return rw_common (0, ph, addr, (gdb_byte *) buf, size); | 
 | } | 
 |  | 
 | /* Copies SIZE bytes from debugger memory .data segment to target process.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pdwrite (struct ps_prochandle *ph, psaddr_t addr, | 
 | 	    const void *buf, size_t size) | 
 | { | 
 |   return rw_common (1, ph, addr, (gdb_byte *) buf, size); | 
 | } | 
 |  | 
 | /* Copies SIZE bytes from target process .text segment to debugger memory.  */ | 
 |  | 
 | ps_err_e | 
 | ps_ptread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t size) | 
 | { | 
 |   return rw_common (0, ph, addr, (gdb_byte *) buf, size); | 
 | } | 
 |  | 
 | /* Copies SIZE bytes from debugger memory .text segment to target process.  */ | 
 |  | 
 | ps_err_e | 
 | ps_ptwrite (struct ps_prochandle *ph, psaddr_t addr, | 
 | 	    const void *buf, size_t size) | 
 | { | 
 |   return rw_common (1, ph, addr, (gdb_byte *) buf, size); | 
 | } | 
 |  | 
 | /* Get general-purpose registers for LWP.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lgetregs (struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset) | 
 | { | 
 |   ptid_t ptid = ptid_t (current_inferior ()->pid, lwpid, 0); | 
 |   struct regcache *regcache | 
 |     = get_thread_arch_regcache (current_inferior ()->process_target (), | 
 | 				ptid, target_gdbarch ()); | 
 |  | 
 |   target_fetch_registers (regcache, -1); | 
 |   fill_gregset (regcache, (gdb_gregset_t *) gregset, -1); | 
 |  | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Set general-purpose registers for LWP.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lsetregs (struct ps_prochandle *ph, lwpid_t lwpid, | 
 | 	     const prgregset_t gregset) | 
 | { | 
 |   ptid_t ptid = ptid_t (current_inferior ()->pid, lwpid, 0); | 
 |   struct regcache *regcache | 
 |     = get_thread_arch_regcache (current_inferior ()->process_target (), | 
 | 				ptid, target_gdbarch ()); | 
 |  | 
 |   supply_gregset (regcache, (const gdb_gregset_t *) gregset); | 
 |   target_store_registers (regcache, -1); | 
 |  | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Log a message (sends to gdb_stderr).  */ | 
 |  | 
 | void | 
 | ps_plog (const char *fmt, ...) | 
 | { | 
 |   va_list args; | 
 |  | 
 |   va_start (args, fmt); | 
 |  | 
 |   gdb_vprintf (gdb_stderr, fmt, args); | 
 | } | 
 |  | 
 | /* Get size of extra register set.  Currently a noop.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lgetxregsize (struct ps_prochandle *ph, lwpid_t lwpid, int *xregsize) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Get extra register set.  Currently a noop.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lgetxregs (struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Set extra register set.  Currently a noop.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lsetxregs (struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset) | 
 | { | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Get floating-point registers for LWP.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lgetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, | 
 | 	       prfpregset_t *fpregset) | 
 | { | 
 |   ptid_t ptid = ptid_t (current_inferior ()->pid, lwpid, 0); | 
 |   struct regcache *regcache | 
 |     = get_thread_arch_regcache (current_inferior ()->process_target (), | 
 | 				ptid, target_gdbarch ()); | 
 |  | 
 |   target_fetch_registers (regcache, -1); | 
 |   fill_fpregset (regcache, (gdb_fpregset_t *) fpregset, -1); | 
 |  | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Set floating-point regs for LWP.  */ | 
 |  | 
 | ps_err_e | 
 | ps_lsetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, | 
 | 	       const prfpregset_t * fpregset) | 
 | { | 
 |   ptid_t ptid = ptid_t (current_inferior ()->pid, lwpid, 0); | 
 |   struct regcache *regcache | 
 |     = get_thread_arch_regcache (current_inferior ()->process_target (), | 
 | 				ptid, target_gdbarch ()); | 
 |  | 
 |   supply_fpregset (regcache, (const gdb_fpregset_t *) fpregset); | 
 |   target_store_registers (regcache, -1); | 
 |  | 
 |   return PS_OK; | 
 | } | 
 |  | 
 | /* Identify process as 32-bit or 64-bit.  At the moment we're using | 
 |    BFD to do this.  There might be a more Solaris-specific | 
 |    (e.g. procfs) method, but this ought to work.  */ | 
 |  | 
 | ps_err_e | 
 | ps_pdmodel (struct ps_prochandle *ph, int *data_model) | 
 | { | 
 |   if (current_program_space->exec_bfd () == 0) | 
 |     *data_model = PR_MODEL_UNKNOWN; | 
 |   else if (bfd_get_arch_size (current_program_space->exec_bfd ()) == 32) | 
 |     *data_model = PR_MODEL_ILP32; | 
 |   else | 
 |     *data_model = PR_MODEL_LP64; | 
 |  | 
 |   return PS_OK; | 
 | } | 
 |  | 
 |  | 
 | /* Convert PTID to printable form.  */ | 
 |  | 
 | std::string | 
 | sol_thread_target::pid_to_str (ptid_t ptid) | 
 | { | 
 |   if (ptid.tid_p ()) | 
 |     { | 
 |       ptid_t lwp; | 
 |  | 
 |       lwp = thread_to_lwp (ptid, -2); | 
 |  | 
 |       if (lwp.pid () == -1) | 
 | 	return string_printf ("Thread %ld (defunct)", | 
 | 			      ptid.tid ()); | 
 |       else if (lwp.pid () != -2) | 
 | 	return string_printf ("Thread %ld (LWP %ld)", | 
 | 			      ptid.tid (), lwp.lwp ()); | 
 |       else | 
 | 	return string_printf ("Thread %ld        ", | 
 | 			      ptid.tid ()); | 
 |     } | 
 |   else if (ptid.lwp () != 0) | 
 |     return string_printf ("LWP    %ld        ", ptid.lwp ()); | 
 |   else | 
 |     return string_printf ("process %d    ", ptid.pid ()); | 
 | } | 
 |  | 
 |  | 
 | /* Worker bee for update_thread_list.  Callback function that gets | 
 |    called once per user-level thread (i.e. not for LWP's).  */ | 
 |  | 
 | static int | 
 | sol_update_thread_list_callback (const td_thrhandle_t *th, void *ignored) | 
 | { | 
 |   td_err_e retval; | 
 |   td_thrinfo_t ti; | 
 |  | 
 |   retval = p_td_thr_get_info (th, &ti); | 
 |   if (retval != TD_OK) | 
 |     return -1; | 
 |  | 
 |   ptid_t ptid = ptid_t (current_inferior ()->pid, 0, ti.ti_tid); | 
 |   thread_info *thr = find_thread_ptid (current_inferior (), ptid); | 
 |   if (thr == NULL || thr->state == THREAD_EXITED) | 
 |     { | 
 |       process_stratum_target *proc_target | 
 | 	= current_inferior ()->process_target (); | 
 |       add_thread (proc_target, ptid); | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | void | 
 | sol_thread_target::update_thread_list () | 
 | { | 
 |   /* Delete dead threads.  */ | 
 |   prune_threads (); | 
 |  | 
 |   /* Find any new LWP's.  */ | 
 |   beneath ()->update_thread_list (); | 
 |  | 
 |   /* Then find any new user-level threads.  */ | 
 |   p_td_ta_thr_iter (main_ta, sol_update_thread_list_callback, (void *) 0, | 
 | 		    TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, | 
 | 		    TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); | 
 | } | 
 |  | 
 | /* Worker bee for the "info sol-thread" command.  This is a callback | 
 |    function that gets called once for each Solaris user-level thread | 
 |    (i.e. not for LWPs) in the inferior.  Print anything interesting | 
 |    that we can think of.  */ | 
 |  | 
 | static int | 
 | info_cb (const td_thrhandle_t *th, void *s) | 
 | { | 
 |   td_err_e ret; | 
 |   td_thrinfo_t ti; | 
 |  | 
 |   ret = p_td_thr_get_info (th, &ti); | 
 |   if (ret == TD_OK) | 
 |     { | 
 |       gdb_printf ("%s thread #%d, lwp %d, ", | 
 | 		  ti.ti_type == TD_THR_SYSTEM ? "system" : "user  ", | 
 | 		  ti.ti_tid, ti.ti_lid); | 
 |       switch (ti.ti_state) | 
 | 	{ | 
 | 	default: | 
 | 	case TD_THR_UNKNOWN: | 
 | 	  gdb_printf ("<unknown state>"); | 
 | 	  break; | 
 | 	case TD_THR_STOPPED: | 
 | 	  gdb_printf ("(stopped)"); | 
 | 	  break; | 
 | 	case TD_THR_RUN: | 
 | 	  gdb_printf ("(run)    "); | 
 | 	  break; | 
 | 	case TD_THR_ACTIVE: | 
 | 	  gdb_printf ("(active) "); | 
 | 	  break; | 
 | 	case TD_THR_ZOMBIE: | 
 | 	  gdb_printf ("(zombie) "); | 
 | 	  break; | 
 | 	case TD_THR_SLEEP: | 
 | 	  gdb_printf ("(asleep) "); | 
 | 	  break; | 
 | 	case TD_THR_STOPPED_ASLEEP: | 
 | 	  gdb_printf ("(stopped asleep)"); | 
 | 	  break; | 
 | 	} | 
 |       /* Print thr_create start function.  */ | 
 |       if (ti.ti_startfunc != 0) | 
 | 	{ | 
 | 	  const struct bound_minimal_symbol msym | 
 | 	    = lookup_minimal_symbol_by_pc (ti.ti_startfunc); | 
 |  | 
 | 	  gdb_printf ("   startfunc=%s", | 
 | 		      msym.minsym | 
 | 		      ? msym.minsym->print_name () | 
 | 		      : paddress (target_gdbarch (), ti.ti_startfunc)); | 
 | 	} | 
 |  | 
 |       /* If thread is asleep, print function that went to sleep.  */ | 
 |       if (ti.ti_state == TD_THR_SLEEP) | 
 | 	{ | 
 | 	  const struct bound_minimal_symbol msym | 
 | 	    = lookup_minimal_symbol_by_pc (ti.ti_pc); | 
 |  | 
 | 	  gdb_printf ("   sleepfunc=%s", | 
 | 		      msym.minsym | 
 | 		      ? msym.minsym->print_name () | 
 | 		      : paddress (target_gdbarch (), ti.ti_pc)); | 
 | 	} | 
 |  | 
 |       gdb_printf ("\n"); | 
 |     } | 
 |   else | 
 |     warning (_("info sol-thread: failed to get info for thread.")); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* List some state about each Solaris user-level thread in the | 
 |    inferior.  */ | 
 |  | 
 | static void | 
 | info_solthreads (const char *args, int from_tty) | 
 | { | 
 |   p_td_ta_thr_iter (main_ta, info_cb, (void *) args, | 
 | 		    TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, | 
 | 		    TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); | 
 | } | 
 |  | 
 | /* Callback routine used to find a thread based on the TID part of | 
 |    its PTID.  */ | 
 |  | 
 | static int | 
 | thread_db_find_thread_from_tid (struct thread_info *thread, void *data) | 
 | { | 
 |   ULONGEST *tid = (ULONGEST *) data; | 
 |  | 
 |   if (thread->ptid.tid () == *tid) | 
 |     return 1; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | ptid_t | 
 | sol_thread_target::get_ada_task_ptid (long lwp, ULONGEST thread) | 
 | { | 
 |   struct thread_info *thread_info = | 
 |     iterate_over_threads (thread_db_find_thread_from_tid, &thread); | 
 |  | 
 |   if (thread_info == NULL) | 
 |     { | 
 |       /* The list of threads is probably not up to date.  Find any | 
 | 	 thread that is missing from the list, and try again.  */ | 
 |       update_thread_list (); | 
 |       thread_info = iterate_over_threads (thread_db_find_thread_from_tid, | 
 | 					  &thread); | 
 |     } | 
 |  | 
 |   gdb_assert (thread_info != NULL); | 
 |  | 
 |   return (thread_info->ptid); | 
 | } | 
 |  | 
 | void _initialize_sol_thread (); | 
 | void | 
 | _initialize_sol_thread () | 
 | { | 
 |   void *dlhandle; | 
 |  | 
 |   dlhandle = dlopen ("libthread_db.so.1", RTLD_NOW); | 
 |   if (!dlhandle) | 
 |     goto die; | 
 |  | 
 | #define resolve(X) \ | 
 |   if (!(p_##X = (X ## _ftype *) dlsym (dlhandle, #X)))	\ | 
 |     goto die; | 
 |  | 
 |   resolve (td_log); | 
 |   resolve (td_ta_new); | 
 |   resolve (td_ta_delete); | 
 |   resolve (td_init); | 
 |   resolve (td_ta_get_ph); | 
 |   resolve (td_ta_get_nthreads); | 
 |   resolve (td_ta_tsd_iter); | 
 |   resolve (td_ta_thr_iter); | 
 |   resolve (td_thr_validate); | 
 |   resolve (td_thr_tsd); | 
 |   resolve (td_thr_get_info); | 
 |   resolve (td_thr_getfpregs); | 
 |   resolve (td_thr_getxregsize); | 
 |   resolve (td_thr_getxregs); | 
 |   resolve (td_thr_sigsetmask); | 
 |   resolve (td_thr_setprio); | 
 |   resolve (td_thr_setsigpending); | 
 |   resolve (td_thr_setfpregs); | 
 |   resolve (td_thr_setxregs); | 
 |   resolve (td_ta_map_id2thr); | 
 |   resolve (td_ta_map_lwp2thr); | 
 |   resolve (td_thr_getgregs); | 
 |   resolve (td_thr_setgregs); | 
 |  | 
 |   add_cmd ("sol-threads", class_maintenance, info_solthreads, | 
 | 	   _("Show info on Solaris user threads."), &maintenanceinfolist); | 
 |  | 
 |   /* Hook into new_objfile notification.  */ | 
 |   gdb::observers::new_objfile.attach (sol_thread_new_objfile, "sol-thread"); | 
 |   return; | 
 |  | 
 |  die: | 
 |   gdb_printf (gdb_stderr, "\ | 
 | [GDB will not be able to debug user-mode threads: %s]\n", dlerror ()); | 
 |  | 
 |   if (dlhandle) | 
 |     dlclose (dlhandle); | 
 |  | 
 |   return; | 
 | } |