2006-12-18 Fred Fish * Makefile.in (fbsd-threads.o): New target. * fbsd-threads.c: New file from FreeBSD 6.1, with tweaks. * i386bsd-nat.c: Add supply_gregset, supply_fpregset, fill_gregset, and fill_fpregset. * i386fbsd-nat.c (_initialize_i386fbsd_nat): Call thread_db_init with current target to save it as target_beneath. * inf-child.c: Add global variable child_suppress_run. (inf_child_can_run): Return !child_suppress_run instead of 1. * config/i386/fbsd.mh (NATDEPFILES): Add fbsd-threads.o (LOADLIBES): Add -rdynamic. Index: Makefile.in =================================================================== RCS file: /services/cvs/cvsroot/latest/gdb/gdb/Makefile.in,v retrieving revision 1.1.1.35.2.5 diff -u -p -r1.1.1.35.2.5 Makefile.in --- Makefile.in 19 Jan 2007 05:21:06 -0000 1.1.1.35.2.5 +++ Makefile.in 7 Feb 2007 00:12:16 -0000 @@ -1989,6 +1989,13 @@ expprint.o: expprint.c $(defs_h) $(symta fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) \ $(regset_h) $(gdb_assert_h) $(gdb_string_h) $(elf_bfd_h) \ $(fbsd_nat_h) +fbsd-threads.o: $(defs_h) $(gdbthread_h) $(value_h) $(symtab_h) \ + $(inferior_h) $(solib_h) $(gdb_assert_h) $(bfdlink_h) $(objfiles_h) \ + $(dcache_h) $(target_h) $(memattr_h) $(symfile_h) $(regcache_h) \ + $(command_h) $(ui_out_h) $(gdbcmd_h) $(gregset_h) $(solib_svr4_h) \ + $(i387_tdep_h) $(gdb_obstack_h) $(gdb_events_h) $(expression_h) \ + $(hashtab_h) $(gdbtypes_h) $(doublest_h) $(gdbcore_h) $(elf_external_h) \ + $(elf_internal_h) $(elf_common_h) $(symcat_h) $(ui_file_h) f-exp.o: f-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \ $(parser_defs_h) $(language_h) $(f_lang_h) $(bfd_h) $(symfile_h) \ $(objfiles_h) $(block_h) Index: fbsd-threads.c =================================================================== RCS file: fbsd-threads.c diff -N fbsd-threads.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fbsd-threads.c 7 Feb 2007 00:12:16 -0000 @@ -0,0 +1,1638 @@ +/* $FreeBSD: src/gnu/usr.bin/gdb/libgdb/fbsd-threads.c,v 1.13.2.1 2006/02/16 02:40:56 davidxu Exp $ */ +/* FreeBSD libthread_db assisted debugging support. + Copyright 1999, 2000, 2001 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include + +#include "proc_service.h" +#include "thread_db.h" + +#include "defs.h" +#include "bfd.h" +#include "elf-bfd.h" +#include "gdb_assert.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "inferior.h" +#include "objfiles.h" +#include "regcache.h" +#include "solib.h" +#include "symfile.h" +#include "symtab.h" +#include "target.h" +#include "gdbcmd.h" +#include "solib-svr4.h" +#include "gregset.h" +#include "i387-tdep.h" + +#define LIBTHREAD_DB_SO "libthread_db.so" + +struct ps_prochandle +{ + pid_t pid; +}; + +extern int child_suppress_run; + +/* The target vector that we call for things this module can't handle. */ +static struct target_ops *target_beneath; + +/* This module's target vectors. */ +static struct target_ops fbsd_thread_ops; +static struct target_ops fbsd_core_ops; + +/* Saved copy of orignal core_ops. */ +static struct target_ops orig_core_ops; +extern struct target_ops core_ops; + +/* Pointer to the next function on the objfile event chain. */ +static void (*target_new_objfile_chain) (struct objfile *objfile); + +/* Non-zero if there is a thread module */ +static int fbsd_thread_present; + +/* Non-zero if we're using this module's target vector. */ +static int fbsd_thread_active; + +/* Non-zero if core_open is called */ +static int fbsd_thread_core = 0; + +/* Non-zero if we have to keep this module's target vector active + across re-runs. */ +static int keep_thread_db; + +/* Structure that identifies the child process for the + interface. */ +static struct ps_prochandle proc_handle; + +/* Connection to the libthread_db library. */ +static td_thragent_t *thread_agent; + +/* The last thread we are single stepping */ +static ptid_t last_single_step_thread; + +/* Pointers to the libthread_db functions. */ + +static td_err_e (*td_init_p) (void); + +static td_err_e (*td_ta_new_p) (struct ps_prochandle *ps, td_thragent_t **ta); +static td_err_e (*td_ta_delete_p) (td_thragent_t *); +static td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt, + td_thrhandle_t *__th); +static td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, lwpid_t lwpid, + td_thrhandle_t *th); +static td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta, + td_thr_iter_f *callback, + void *cbdata_p, td_thr_state_e state, + int ti_pri, sigset_t *ti_sigmask_p, + unsigned int ti_user_flags); +static td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta, + td_event_e event, td_notify_t *ptr); +static td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta, + td_thr_events_t *event); +static td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta, + td_event_msg_t *msg); +static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th, + td_thrinfo_t *infop); +#ifdef PT_GETXMMREGS +static td_err_e (*td_thr_getxmmregs_p) (const td_thrhandle_t *th, + char *regset); +#endif +static td_err_e (*td_thr_getfpregs_p) (const td_thrhandle_t *th, + prfpregset_t *regset); +static td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th, + prgregset_t gregs); +#ifdef PT_GETXMMREGS +static td_err_e (*td_thr_setxmmregs_p) (const td_thrhandle_t *th, + const char *fpregs); +#endif +static td_err_e (*td_thr_setfpregs_p) (const td_thrhandle_t *th, + const prfpregset_t *fpregs); +static td_err_e (*td_thr_setgregs_p) (const td_thrhandle_t *th, + prgregset_t gregs); +static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, int event); + +static td_err_e (*td_thr_sstep_p) (td_thrhandle_t *th, int step); + +static td_err_e (*td_ta_tsd_iter_p) (const td_thragent_t *ta, + td_key_iter_f *func, void *data); +static td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, + void *map_address, + size_t offset, void **address); +static td_err_e (*td_thr_dbsuspend_p) (const td_thrhandle_t *); +static td_err_e (*td_thr_dbresume_p) (const td_thrhandle_t *); + +static CORE_ADDR td_create_bp_addr; + +/* Location of the thread death event breakpoint. */ +static CORE_ADDR td_death_bp_addr; + +/* Prototypes for local functions. */ +static void fbsd_thread_find_new_threads (void); +static int fbsd_thread_alive (ptid_t ptid); +static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, + const td_thrinfo_t *ti_p, int verbose); +static void fbsd_thread_detach (char *args, int from_tty); + +/* Building process ids. */ + +#define GET_PID(ptid) ptid_get_pid (ptid) +#define GET_LWP(ptid) ptid_get_lwp (ptid) +#define GET_THREAD(ptid) ptid_get_tid (ptid) + +#define IS_LWP(ptid) (GET_LWP (ptid) != 0) +#define IS_THREAD(ptid) (GET_THREAD (ptid) != 0) + +#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) +#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid) + +static char * +thread_db_err_str (td_err_e err) +{ + static char buf[64]; + + switch (err) + { + case TD_OK: + return "generic 'call succeeded'"; + case TD_ERR: + return "generic error"; + case TD_NOTHR: + return "no thread to satisfy query"; + case TD_NOSV: + return "no sync handle to satisfy query"; + case TD_NOLWP: + return "no LWP to satisfy query"; + case TD_BADPH: + return "invalid process handle"; + case TD_BADTH: + return "invalid thread handle"; + case TD_BADSH: + return "invalid synchronization handle"; + case TD_BADTA: + return "invalid thread agent"; + case TD_BADKEY: + return "invalid key"; + case TD_NOMSG: + return "no event message for getmsg"; + case TD_NOFPREGS: + return "FPU register set not available"; + case TD_NOLIBTHREAD: + return "application not linked with libthread"; + case TD_NOEVENT: + return "requested event is not supported"; + case TD_NOCAPAB: + return "capability not available"; + case TD_DBERR: + return "debugger service failed"; + case TD_NOAPLIC: + return "operation not applicable to"; + case TD_NOTSD: + return "no thread-specific data for this thread"; + case TD_MALLOC: + return "malloc failed"; + case TD_PARTIALREG: + return "only part of register set was written/read"; + case TD_NOXREGS: + return "X register set not available for this thread"; + default: + snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err); + return buf; + } +} + +static char * +thread_db_state_str (td_thr_state_e state) +{ + static char buf[64]; + + switch (state) + { + case TD_THR_STOPPED: + return "stopped by debugger"; + case TD_THR_RUN: + return "runnable"; + case TD_THR_ACTIVE: + return "active"; + case TD_THR_ZOMBIE: + return "zombie"; + case TD_THR_SLEEP: + return "sleeping"; + case TD_THR_STOPPED_ASLEEP: + return "stopped by debugger AND blocked"; + default: + snprintf (buf, sizeof (buf), "unknown thread_db state %d", state); + return buf; + } +} + +/* Convert LWP to user-level thread id. */ +static ptid_t +thread_from_lwp (ptid_t ptid, td_thrhandle_t *th, td_thrinfo_t *ti) +{ + td_err_e err; + + gdb_assert (IS_LWP (ptid)); + + if (fbsd_thread_active) + { + err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), th); + if (err == TD_OK) + { + err = td_thr_get_info_p (th, ti); + if (err != TD_OK) + error ("Cannot get thread info: %s", thread_db_err_str (err)); + return BUILD_THREAD (ti->ti_tid, GET_PID (ptid)); + } + } + + /* the LWP is not mapped to user thread */ + return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid)); +} + +static void +fbsd_core_get_first_lwp (bfd *abfd, asection *asect, void *obj) +{ + if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0) + return; + + if (*(lwpid_t *)obj != 0) + return; + + *(lwpid_t *)obj = atoi (bfd_section_name (abfd, asect) + 5); +} + +static long +get_current_lwp (int pid) +{ + struct ptrace_lwpinfo pl; + lwpid_t lwpid; + + if (!target_has_execution) + { + lwpid = 0; + bfd_map_over_sections (core_bfd, fbsd_core_get_first_lwp, &lwpid); + return lwpid; + } + if (ptrace (PT_LWPINFO, pid, (caddr_t)&pl, sizeof(pl))) + perror_with_name("PT_LWPINFO"); + + return (long)pl.pl_lwpid; +} + +static void +get_current_thread () +{ + td_thrhandle_t th; + td_thrinfo_t ti; + long lwp; + ptid_t tmp, ptid; + + lwp = get_current_lwp (proc_handle.pid); + tmp = BUILD_LWP (lwp, proc_handle.pid); + ptid = thread_from_lwp (tmp, &th, &ti); + if (!in_thread_list (ptid)) + { + attach_thread (ptid, &th, &ti, 1); + } + inferior_ptid = ptid; +} + +static td_err_e +enable_thread_event (td_thragent_t *thread_agent, int event, CORE_ADDR *bp) +{ + td_notify_t notify; + td_err_e err; + + /* Get the breakpoint address for thread EVENT. */ + err = td_ta_event_addr_p (thread_agent, event, ¬ify); + if (err != TD_OK) + return err; + + /* Set up the breakpoint. */ + (*bp) = gdbarch_convert_from_func_ptr_addr (current_gdbarch, + extract_typed_address((const gdb_byte *)¬ify.u.bptaddr, builtin_type_void_func_ptr), + ¤t_target); + create_thread_event_breakpoint ((*bp)); + + return TD_OK; +} + +static void +enable_thread_event_reporting (void) +{ + td_thr_events_t events; + td_notify_t notify; + td_err_e err; + + /* We cannot use the thread event reporting facility if these + functions aren't available. */ + if (td_ta_event_addr_p == NULL || td_ta_set_event_p == NULL + || td_ta_event_getmsg_p == NULL || td_thr_event_enable_p == NULL) + return; + + /* Set the process wide mask saying which events we're interested in. */ + td_event_emptyset (&events); + td_event_addset (&events, TD_CREATE); + td_event_addset (&events, TD_DEATH); + + err = td_ta_set_event_p (thread_agent, &events); + if (err != TD_OK) + { + warning ("Unable to set global thread event mask: %s", + thread_db_err_str (err)); + return; + } + + /* Delete previous thread event breakpoints, if any. */ + remove_thread_event_breakpoints (); + td_create_bp_addr = 0; + td_death_bp_addr = 0; + + /* Set up the thread creation event. */ + err = enable_thread_event (thread_agent, TD_CREATE, &td_create_bp_addr); + if (err != TD_OK) + { + warning ("Unable to get location for thread creation breakpoint: %s", + thread_db_err_str (err)); + return; + } + + /* Set up the thread death event. */ + err = enable_thread_event (thread_agent, TD_DEATH, &td_death_bp_addr); + if (err != TD_OK) + { + warning ("Unable to get location for thread death breakpoint: %s", + thread_db_err_str (err)); + return; + } +} + +static void +disable_thread_event_reporting (void) +{ + td_thr_events_t events; + + /* Set the process wide mask saying we aren't interested in any + events anymore. */ + td_event_emptyset (&events); + td_ta_set_event_p (thread_agent, &events); + + /* Delete thread event breakpoints, if any. */ + remove_thread_event_breakpoints (); + td_create_bp_addr = 0; + td_death_bp_addr = 0; +} + +static void +fbsd_thread_activate (void) +{ + fbsd_thread_active = 1; + init_thread_list(); + if (fbsd_thread_core == 0) + enable_thread_event_reporting (); + fbsd_thread_find_new_threads (); + get_current_thread (); +} + +static void +fbsd_thread_deactivate (void) +{ + if (fbsd_thread_core == 0) + disable_thread_event_reporting(); + td_ta_delete_p (thread_agent); + + inferior_ptid = pid_to_ptid (proc_handle.pid); + proc_handle.pid = 0; + fbsd_thread_active = 0; + fbsd_thread_present = 0; + init_thread_list (); +} + +static void +fbsd_thread_new_objfile (struct objfile *objfile) +{ + td_err_e err; + + if (objfile == NULL) + { + /* All symbols have been discarded. If the thread_db target is + active, deactivate it now. */ + if (fbsd_thread_active) + { + gdb_assert (proc_handle.pid == 0); + fbsd_thread_active = 0; + } + + goto quit; + } + + if (!child_suppress_run) + goto quit; + + /* Nothing to do. The thread library was already detected and the + target vector was already activated. */ + if (fbsd_thread_active) + goto quit; + + /* Initialize the structure that identifies the child process. Note + that at this point there is no guarantee that we actually have a + child process. */ + proc_handle.pid = GET_PID (inferior_ptid); + + /* Now attempt to open a connection to the thread library. */ + err = td_ta_new_p (&proc_handle, &thread_agent); + switch (err) + { + case TD_NOLIBTHREAD: + /* No thread library was detected. */ + break; + + case TD_OK: + /* The thread library was detected. Activate the thread_db target. */ + fbsd_thread_present = 1; + + /* We can only poke around if there actually is a child process. + If there is no child process alive, postpone the steps below + until one has been created. */ + if (fbsd_thread_core == 0 && proc_handle.pid != 0) + { + push_target(&fbsd_thread_ops); + fbsd_thread_activate(); + } + else + { + td_ta_delete_p(thread_agent); + thread_agent = NULL; + } + break; + + default: + warning ("Cannot initialize thread debugging library: %s", + thread_db_err_str (err)); + break; + } + + quit: + if (target_new_objfile_chain) + target_new_objfile_chain (objfile); +} + +static void +fbsd_thread_attach (char *args, int from_tty) +{ + fbsd_thread_core = 0; + + target_beneath->to_attach (args, from_tty); + + /* Must get symbols from solibs before libthread_db can run! */ + solib_add (NULL, from_tty, (struct target_ops *) 0, auto_solib_add); + + if (fbsd_thread_present && !fbsd_thread_active) + push_target(&fbsd_thread_ops); +} + +static void +fbsd_thread_post_attach (int pid) +{ + target_beneath->to_post_attach (pid); + + if (fbsd_thread_present && !fbsd_thread_active) + { + proc_handle.pid = GET_PID (inferior_ptid); + fbsd_thread_activate (); + } +} + +static void +fbsd_thread_detach (char *args, int from_tty) +{ + fbsd_thread_deactivate (); + unpush_target (&fbsd_thread_ops); + + /* Clear gdb solib information and symbol file + cache, so that after detach and re-attach, new_objfile + hook will be called */ + + clear_solib(); + symbol_file_clear(0); + proc_handle.pid = 0; + target_beneath->to_detach (args, from_tty); +} + +static int +suspend_thread_callback (const td_thrhandle_t *th_p, void *data) +{ + int err = td_thr_dbsuspend_p (th_p); + if (err != 0) + fprintf_filtered(gdb_stderr, "%s %s\n", __func__, thread_db_err_str (err)); + return (err); +} + +static int +resume_thread_callback (const td_thrhandle_t *th_p, void *data) +{ + int err = td_thr_dbresume_p (th_p); + if (err != 0) + fprintf_filtered(gdb_stderr, "%s %s\n", __func__, thread_db_err_str (err)); + return (err); +} + +static void +fbsd_thread_resume (ptid_t ptid, int step, enum target_signal signo) +{ + td_thrhandle_t th; + td_thrinfo_t ti; + ptid_t work_ptid; + int resume_all, ret; + long lwp, thvalid = 0; + + if (!fbsd_thread_active) + { + target_beneath->to_resume (ptid, step, signo); + return; + } + + if (GET_PID(ptid) != -1 && step != 0) + { + resume_all = 0; + work_ptid = ptid; + } + else + { + resume_all = 1; + work_ptid = inferior_ptid; + } + + lwp = GET_LWP (work_ptid); + if (lwp == 0) + { + /* check user thread */ + ret = td_ta_map_id2thr_p (thread_agent, GET_THREAD(work_ptid), &th); + if (ret) + error (_("Cannot find thread: %s"),thread_db_err_str (ret)); + + /* For M:N thread, we need to tell UTS to set/unset single step + flag at context switch time, the flag will be written into + thread mailbox. This becauses some architecture may not have + machine single step flag in ucontext, so we put the flag in mailbox, + when the thread switches back, kse_switchin restores the single step + state. */ + ret = td_thr_sstep_p (&th, step); + if (ret) + error (_("Cannot set/unset single step flag: %s"),thread_db_err_str (ret)); + ret = td_thr_get_info_p (&th, &ti); + if (ret) + error (_("Cannot get thread info: %s"),thread_db_err_str (ret)); + thvalid = 1; + lwp = ti.ti_lid; + } + + if (lwp) + { + int req = step ? PT_SETSTEP : PT_CLEARSTEP; + if (ptrace (req, (pid_t) lwp, (caddr_t) 1, target_signal_to_host(signo))) + perror_with_name ("PT_SETSTEP/PT_CLEARSTEP"); + } + + if (!ptid_equal (last_single_step_thread, null_ptid)) + { + ret = td_ta_thr_iter_p (thread_agent, resume_thread_callback, NULL, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + if (ret != TD_OK) + error ("resume error: %s", thread_db_err_str (ret)); + } + + if (!resume_all) + { + ret = td_ta_thr_iter_p (thread_agent, suspend_thread_callback, NULL, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + if (ret != TD_OK) + error ("suspend error: %s", thread_db_err_str (ret)); + last_single_step_thread = work_ptid; + } + else + last_single_step_thread = null_ptid; + + if (thvalid) + { + ret = td_thr_dbresume_p (&th); + if (ret != TD_OK) + error ("resume error: %s", thread_db_err_str (ret)); + } + else + { + /* it is not necessary, put it here for completness */ + ret = ptrace(PT_RESUME, lwp, 0, 0); + } + + /* now continue the process, suspended thread wont run */ + if (ptrace (PT_CONTINUE, proc_handle.pid , (caddr_t)1, + target_signal_to_host(signo))) + perror_with_name ("PT_CONTINUE"); +} + +static void +attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, + const td_thrinfo_t *ti_p, int verbose) +{ + td_err_e err; + + /* Add the thread to GDB's thread list. */ + if (!in_thread_list (ptid)) { + add_thread (ptid); + if (verbose) + printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid)); + } + + if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE) + return; /* A zombie thread -- do not attach. */ + + if (! IS_THREAD(ptid)) + return; + if (fbsd_thread_core != 0) + return; + /* Enable thread event reporting for this thread. */ + err = td_thr_event_enable_p (th_p, 1); + if (err != TD_OK) + error ("Cannot enable thread event reporting for %s: %s", + target_pid_to_str (ptid), thread_db_err_str (err)); +} + +static void +detach_thread (ptid_t ptid, int verbose) +{ + if (verbose) + printf_unfiltered ("[%s exited]\n", target_pid_to_str (ptid)); +} + +static void +check_event (ptid_t ptid) +{ + td_event_msg_t msg; + td_thrinfo_t ti; + td_err_e err; + CORE_ADDR stop_pc; + int loop = 0; + + /* Bail out early if we're not at a thread event breakpoint. */ + stop_pc = read_pc_pid (ptid) - DECR_PC_AFTER_BREAK; + if (stop_pc != td_create_bp_addr && stop_pc != td_death_bp_addr) + return; + loop = 1; + + do + { + err = td_ta_event_getmsg_p (thread_agent, &msg); + if (err != TD_OK) + { + if (err == TD_NOMSG) + return; + error ("Cannot get thread event message: %s", + thread_db_err_str (err)); + } + err = td_thr_get_info_p (msg.th_p, &ti); + if (err != TD_OK) + error ("Cannot get thread info: %s", thread_db_err_str (err)); + ptid = BUILD_THREAD (ti.ti_tid, GET_PID (ptid)); + switch (msg.event) + { + case TD_CREATE: + /* We may already know about this thread, for instance when the + user has issued the `info threads' command before the SIGTRAP + for hitting the thread creation breakpoint was reported. */ + attach_thread (ptid, msg.th_p, &ti, 1); + break; + case TD_DEATH: + if (!in_thread_list (ptid)) + error ("Spurious thread death event."); + detach_thread (ptid, 1); + break; + default: + error ("Spurious thread event."); + } + } + while (loop); +} + +static ptid_t +fbsd_thread_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + ptid_t ret; + long lwp; + CORE_ADDR stop_pc; + td_thrhandle_t th; + td_thrinfo_t ti; + + ret = target_beneath->to_wait (ptid, ourstatus); + if (GET_PID(ret) >= 0 && ourstatus->kind == TARGET_WAITKIND_STOPPED) + { + lwp = get_current_lwp (GET_PID(ret)); + ret = thread_from_lwp (BUILD_LWP(lwp, GET_PID(ret)), + &th, &ti); + if (!in_thread_list(ret)) { + /* + * We have to enable event reporting for initial thread + * which was not mapped before. + */ + attach_thread(ret, &th, &ti, 1); + } + if (ourstatus->value.sig == TARGET_SIGNAL_TRAP) + check_event(ret); + /* this is a hack, if an event won't cause gdb to stop, for example, + SIGARLM, gdb resumes the process immediatly without setting + inferior_ptid to the new thread returned here, this is a bug + because inferior_ptid may already not exist there, and passing + a none existing thread to fbsd_thread_resume causes error. */ + if (!fbsd_thread_alive (inferior_ptid)) + { + delete_thread (inferior_ptid); + inferior_ptid = ret; + } + } + + return (ret); +} + +static LONGEST +fbsd_thread_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, LONGEST len) +{ + struct cleanup *old_chain = save_inferior_ptid (); + LONGEST xfer; + +#if 0 + if (is_thread (inferior_ptid)) + { + /* FIXME: This seems to be necessary to make sure breakpoints + are removed. */ + if (!target_thread_alive (inferior_ptid)) + inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid)); + else + inferior_ptid = lwp_from_thread (inferior_ptid); + } +#endif + + xfer = target_beneath->to_xfer_partial (ops, object, annex, + readbuf, writebuf, offset, len); + + do_cleanups (old_chain); + return xfer; +} + +static void +fbsd_lwp_fetch_registers (int regno) +{ + gregset_t gregs; + fpregset_t fpregs; + lwpid_t lwp; +#ifdef PT_GETXMMREGS + char xmmregs[512]; +#endif + + if (!target_has_execution) + { + orig_core_ops.to_fetch_registers (-1); + return; + } + + /* XXX: We've replaced the pid with the lwpid for GDB's benefit. */ + lwp = GET_PID (inferior_ptid); + + if (ptrace (PT_GETREGS, lwp, (caddr_t) &gregs, 0) == -1) + error ("Cannot get lwp %d registers: %s\n", lwp, safe_strerror (errno)); + supply_gregset (&gregs); + +#ifdef PT_GETXMMREGS + if (ptrace (PT_GETXMMREGS, lwp, xmmregs, 0) == 0) + { + i387_supply_fxsave (current_regcache, -1, xmmregs); + } + else + { +#endif + if (ptrace (PT_GETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1) + error ("Cannot get lwp %d registers: %s\n ", lwp, safe_strerror (errno)); + supply_fpregset (&fpregs); +#ifdef PT_GETXMMREGS + } +#endif +} + +static void +fbsd_thread_fetch_registers (int regno) +{ + prgregset_t gregset; + prfpregset_t fpregset; + td_thrhandle_t th; + td_err_e err; +#ifdef PT_GETXMMREGS + char xmmregs[512]; +#endif + + if (!IS_THREAD (inferior_ptid)) + { + fbsd_lwp_fetch_registers (regno); + return; + } + + err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (inferior_ptid), &th); + if (err != TD_OK) + error ("Cannot find thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), + GET_THREAD (inferior_ptid), thread_db_err_str (err)); + + err = td_thr_getgregs_p (&th, gregset); + if (err != TD_OK) + error ("Cannot fetch general-purpose registers for thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), + GET_THREAD (inferior_ptid), thread_db_err_str (err)); +#ifdef PT_GETXMMREGS + err = td_thr_getxmmregs_p (&th, xmmregs); + if (err == TD_OK) + { + i387_supply_fxsave (current_regcache, -1, xmmregs); + } + else + { +#endif + err = td_thr_getfpregs_p (&th, &fpregset); + if (err != TD_OK) + error ("Cannot get floating-point registers for thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), + GET_THREAD (inferior_ptid), thread_db_err_str (err)); + supply_fpregset (&fpregset); +#ifdef PT_GETXMMREGS + } +#endif + + supply_gregset (gregset); +} + +static void +fbsd_lwp_store_registers (int regno) +{ + gregset_t gregs; + fpregset_t fpregs; + lwpid_t lwp; +#ifdef PT_GETXMMREGS + char xmmregs[512]; +#endif + + /* FIXME, is it possible ? */ + if (!IS_LWP (inferior_ptid)) + { + target_beneath->to_store_registers (regno); + return ; + } + + lwp = GET_LWP (inferior_ptid); + if (regno != -1) + if (ptrace (PT_GETREGS, lwp, (caddr_t) &gregs, 0) == -1) + error ("Cannot get lwp %d registers: %s\n", lwp, safe_strerror (errno)); + + fill_gregset (&gregs, regno); + if (ptrace (PT_SETREGS, lwp, (caddr_t) &gregs, 0) == -1) + error ("Cannot set lwp %d registers: %s\n", lwp, safe_strerror (errno)); + +#ifdef PT_GETXMMREGS + if (regno != -1) + if (ptrace (PT_GETXMMREGS, lwp, xmmregs, 0) == -1) + goto noxmm; + + i387_fill_fxsave (xmmregs, regno); + if (ptrace (PT_SETXMMREGS, lwp, xmmregs, 0) == -1) + goto noxmm; + + return; + +noxmm: +#endif + + if (regno != -1) + if (ptrace (PT_GETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1) + error ("Cannot get lwp %d float registers: %s\n", lwp, + safe_strerror (errno)); + + fill_fpregset (&fpregs, regno); + if (ptrace (PT_SETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1) + error ("Cannot set lwp %d float registers: %s\n", lwp, + safe_strerror (errno)); +} + +static void +fbsd_thread_store_registers (int regno) +{ + prgregset_t gregset; + prfpregset_t fpregset; + td_thrhandle_t th; + td_err_e err; +#ifdef PT_GETXMMREGS + char xmmregs[512]; +#endif + + if (!IS_THREAD (inferior_ptid)) + { + fbsd_lwp_store_registers (regno); + return; + } + + err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (inferior_ptid), &th); + if (err != TD_OK) + error ("Cannot find thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), + GET_THREAD (inferior_ptid), + thread_db_err_str (err)); + + if (regno != -1) + { + char old_value[MAX_REGISTER_SIZE]; + + regcache_raw_collect (current_regcache, regno, old_value); + err = td_thr_getgregs_p (&th, gregset); + if (err != TD_OK) + error ("%s: td_thr_getgregs %s", __func__, thread_db_err_str (err)); + err = td_thr_getfpregs_p (&th, &fpregset); + if (err != TD_OK) + error ("%s: td_thr_getfpgregs %s", __func__, thread_db_err_str (err)); + regcache_raw_supply (current_regcache, regno, old_value); + } + + fill_gregset (gregset, regno); + fill_fpregset (&fpregset, regno); +#ifdef PT_GETXMMREGS + i387_fill_fxsave (xmmregs, regno); +#endif + + err = td_thr_setgregs_p (&th, gregset); + if (err != TD_OK) + error ("Cannot store general-purpose registers for thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), GET_THREAD (inferior_ptid), + thread_db_err_str (err)); + +#ifdef PT_GETXMMREGS + err = td_thr_setxmmregs_p (&th, xmmregs); + if (err == TD_OK) + return; +#endif + + err = td_thr_setfpregs_p (&th, &fpregset); + if (err != TD_OK) + error ("Cannot store floating-point registers for thread %d: Thread ID=%ld, %s", + pid_to_thread_id (inferior_ptid), GET_THREAD (inferior_ptid), + thread_db_err_str (err)); +} + +static void +fbsd_thread_kill (void) +{ + target_beneath->to_kill(); +} + +static int +fbsd_thread_can_run (void) +{ + return child_suppress_run; +} + +static void +fbsd_thread_create_inferior (char *exec_file, char *allargs, char **env, int from_tty) +{ + if (fbsd_thread_present && !fbsd_thread_active) + push_target(&fbsd_thread_ops); + + target_beneath->to_create_inferior (exec_file, allargs, env, from_tty); +} + +static void +fbsd_thread_post_startup_inferior (ptid_t ptid) +{ + if (fbsd_thread_present && !fbsd_thread_active) + { + /* The child process is now the actual multi-threaded + program. Snatch its process ID... */ + proc_handle.pid = GET_PID (ptid); + td_ta_new_p (&proc_handle, &thread_agent); + fbsd_thread_activate(); + } +} + +static void +fbsd_thread_mourn_inferior (void) +{ + if (fbsd_thread_active) + fbsd_thread_deactivate (); + + unpush_target (&fbsd_thread_ops); + + target_beneath->to_mourn_inferior (); +} + +static void +fbsd_core_check_lwp (bfd *abfd, asection *asect, void *obj) +{ + lwpid_t lwp; + + if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0) + return; + + /* already found */ + if (*(lwpid_t *)obj == 0) + return; + + lwp = atoi (bfd_section_name (abfd, asect) + 5); + if (*(lwpid_t *)obj == lwp) + *(lwpid_t *)obj = 0; +} + +static int +fbsd_thread_alive (ptid_t ptid) +{ + td_thrhandle_t th; + td_thrinfo_t ti; + td_err_e err; + gregset_t gregs; + lwpid_t lwp; + + if (IS_THREAD (ptid)) + { + err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (ptid), &th); + if (err != TD_OK) + return 0; + + err = td_thr_get_info_p (&th, &ti); + if (err != TD_OK) + return 0; + + /* A zombie thread. */ + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return 0; + + return 1; + } + else if (GET_LWP (ptid) == 0) + { + /* we sometimes are called with lwp == 0 */ + return 1; + } + + if (fbsd_thread_active) + { + err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th); + + /* + * if the lwp was already mapped to user thread, don't use it + * directly, please use user thread id instead. + */ + if (err == TD_OK) + return 0; + } + + if (!target_has_execution) + { + lwp = GET_LWP (ptid); + bfd_map_over_sections (core_bfd, fbsd_core_check_lwp, &lwp); + return (lwp == 0); + } + + /* check lwp in kernel */ + return ptrace (PT_GETREGS, GET_LWP (ptid), (caddr_t)&gregs, 0) == 0; +} + +static void +fbsd_thread_files_info (struct target_ops *ignore) +{ + target_beneath->to_files_info (ignore); +} + +static int +find_new_threads_callback (const td_thrhandle_t *th_p, void *data) +{ + td_thrinfo_t ti; + td_err_e err; + ptid_t ptid; + + err = td_thr_get_info_p (th_p, &ti); + if (err != TD_OK) + error ("Cannot get thread info: %s", thread_db_err_str (err)); + + /* Ignore zombie */ + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return 0; + + ptid = BUILD_THREAD (ti.ti_tid, proc_handle.pid); + attach_thread (ptid, th_p, &ti, 1); + return 0; +} + +static void +fbsd_thread_find_new_threads (void) +{ + td_err_e err; + + if (!fbsd_thread_active) + return; + + /* Iterate over all user-space threads to discover new threads. */ + err = td_ta_thr_iter_p (thread_agent, find_new_threads_callback, NULL, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + if (err != TD_OK) + error ("Cannot find new threads: %s", thread_db_err_str (err)); +} + +static char * +fbsd_thread_pid_to_str (ptid_t ptid) +{ + static char buf[64]; + + if (IS_THREAD (ptid)) + { + td_thrhandle_t th; + td_thrinfo_t ti; + td_err_e err; + + err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (ptid), &th); + if (err != TD_OK) + error ("Cannot find thread, Thread ID=%ld, %s", + GET_THREAD (ptid), thread_db_err_str (err)); + + err = td_thr_get_info_p (&th, &ti); + if (err != TD_OK) + error ("Cannot get thread info, Thread ID=%ld, %s", + GET_THREAD (ptid), thread_db_err_str (err)); + + if (ti.ti_lid != 0) + { + snprintf (buf, sizeof (buf), "Thread %p (LWP %d)", + th.th_thread, ti.ti_lid); + } + else + { + snprintf (buf, sizeof (buf), "Thread %p (%s)", + th.th_thread, thread_db_state_str (ti.ti_state)); + } + + return buf; + } + else if (IS_LWP (ptid)) + { + snprintf (buf, sizeof (buf), "LWP %d", (int) GET_LWP (ptid)); + return buf; + } + return normal_pid_to_str (ptid); +} + +CORE_ADDR +fbsd_thread_get_local_address(ptid_t ptid, CORE_ADDR lm, CORE_ADDR offset) +{ + td_thrhandle_t th; + void *address; + void *lm2; + int ret; + + if (IS_THREAD (ptid)) + { + if (!td_thr_tls_get_addr_p) + error ("Cannot find thread-local interface in thread_db library."); + + ret = td_ta_map_id2thr_p (thread_agent, GET_THREAD(ptid), &th); + + /* get the address of the variable. */ + store_typed_address((gdb_byte *)&lm2, builtin_type_void_data_ptr, lm); + ret = td_thr_tls_get_addr_p (&th, lm2, offset, &address); + + if (ret != TD_OK) + { + error ("Cannot find thread-local storage for thread %ld: %s", + (long) GET_THREAD (ptid), thread_db_err_str (ret)); + } + + /* Cast assuming host == target. */ + return extract_typed_address((const gdb_byte *)&address, builtin_type_void_data_ptr); + } + return (0); +} + +static int +tsd_cb (thread_key_t key, void (*destructor)(void *), void *ignore) +{ + struct minimal_symbol *ms; + char *name; + + ms = lookup_minimal_symbol_by_pc ( + extract_typed_address((const gdb_byte *)&destructor, builtin_type_void_func_ptr)); + if (!ms) + name = "???"; + else + name = DEPRECATED_SYMBOL_NAME (ms); + + printf_filtered ("Destructor %p <%s>\n", destructor, name); + return 0; +} + +static void +fbsd_thread_tsd_cmd (char *exp, int from_tty) +{ + if (fbsd_thread_active) + td_ta_tsd_iter_p (thread_agent, tsd_cb, NULL); +} + +static int +ignore (CORE_ADDR addr, char *contents) +{ + return 0; +} + +static void +fbsd_core_open (char *filename, int from_tty) +{ + int err; + + fbsd_thread_core = 1; + + orig_core_ops.to_open (filename, from_tty); + + if (fbsd_thread_present) + { + err = td_ta_new_p (&proc_handle, &thread_agent); + if (err == TD_OK) + { + proc_handle.pid = elf_tdata (core_bfd)->core_pid; + fbsd_thread_activate (); + } + else + error ("fbsd_core_open: td_ta_new: %s", thread_db_err_str (err)); + } +} + +static void +fbsd_core_close (int quitting) +{ + orig_core_ops.to_close (quitting); +} + +static void +fbsd_core_detach (char *args, int from_tty) +{ + if (fbsd_thread_active) + fbsd_thread_deactivate (); + unpush_target (&fbsd_thread_ops); + orig_core_ops.to_detach (args, from_tty); + + /* Clear gdb solib information and symbol file + cache, so that after detach and re-attach, new_objfile + hook will be called */ + clear_solib(); + symbol_file_clear(0); +} + +static void +fbsd_core_files_info (struct target_ops *ignore) +{ + orig_core_ops.to_files_info (ignore); +} + +static void +init_fbsd_core_ops (void) +{ + fbsd_core_ops.to_shortname = "FreeBSD-core"; + fbsd_core_ops.to_longname = "FreeBSD core thread."; + fbsd_core_ops.to_doc = "FreeBSD threads support for core files."; + fbsd_core_ops.to_open = fbsd_core_open; + fbsd_core_ops.to_close = fbsd_core_close; + fbsd_core_ops.to_attach = 0; + fbsd_core_ops.to_post_attach = 0; + fbsd_core_ops.to_detach = fbsd_core_detach; + /* fbsd_core_ops.to_resume = 0; */ + /* fbsd_core_ops.to_wait = 0; */ + fbsd_core_ops.to_fetch_registers = fbsd_thread_fetch_registers; + /* fbsd_core_ops.to_store_registers = 0; */ + /* fbsd_core_ops.to_prepare_to_store = 0; */ + fbsd_core_ops.to_xfer_partial = fbsd_thread_xfer_partial; + fbsd_core_ops.to_files_info = fbsd_core_files_info; + fbsd_core_ops.to_insert_breakpoint = ignore; + fbsd_core_ops.to_remove_breakpoint = ignore; + /* fbsd_core_ops.to_lookup_symbol = 0; */ + fbsd_core_ops.to_create_inferior = fbsd_thread_create_inferior; + fbsd_core_ops.to_stratum = core_stratum; + fbsd_core_ops.to_has_all_memory = 0; + fbsd_core_ops.to_has_memory = 1; + fbsd_core_ops.to_has_stack = 1; + fbsd_core_ops.to_has_registers = 1; + fbsd_core_ops.to_has_execution = 0; + fbsd_core_ops.to_has_thread_control = tc_none; + fbsd_core_ops.to_thread_alive = fbsd_thread_alive; + fbsd_core_ops.to_pid_to_str = fbsd_thread_pid_to_str; + fbsd_core_ops.to_find_new_threads = fbsd_thread_find_new_threads; + fbsd_core_ops.to_sections = 0; + fbsd_core_ops.to_sections_end = 0; + fbsd_core_ops.to_magic = OPS_MAGIC; +} + +static void +init_fbsd_thread_ops (void) +{ + fbsd_thread_ops.to_shortname = "freebsd-threads"; + fbsd_thread_ops.to_longname = "FreeBSD multithreaded child process."; + fbsd_thread_ops.to_doc = "FreeBSD threads support."; + fbsd_thread_ops.to_attach = fbsd_thread_attach; + fbsd_thread_ops.to_detach = fbsd_thread_detach; + fbsd_thread_ops.to_post_attach = fbsd_thread_post_attach; + fbsd_thread_ops.to_resume = fbsd_thread_resume; + fbsd_thread_ops.to_wait = fbsd_thread_wait; + fbsd_thread_ops.to_fetch_registers = fbsd_thread_fetch_registers; + fbsd_thread_ops.to_store_registers = fbsd_thread_store_registers; + fbsd_thread_ops.to_xfer_partial = fbsd_thread_xfer_partial; + fbsd_thread_ops.to_files_info = fbsd_thread_files_info; + fbsd_thread_ops.to_kill = fbsd_thread_kill; + fbsd_thread_ops.to_create_inferior = fbsd_thread_create_inferior; + fbsd_thread_ops.to_post_startup_inferior = fbsd_thread_post_startup_inferior; + fbsd_thread_ops.to_mourn_inferior = fbsd_thread_mourn_inferior; + fbsd_thread_ops.to_can_run = fbsd_thread_can_run; + fbsd_thread_ops.to_thread_alive = fbsd_thread_alive; + fbsd_thread_ops.to_find_new_threads = fbsd_thread_find_new_threads; + fbsd_thread_ops.to_pid_to_str = fbsd_thread_pid_to_str; + fbsd_thread_ops.to_stratum = thread_stratum; + fbsd_thread_ops.to_has_thread_control = tc_none; + fbsd_thread_ops.to_has_all_memory = 1; + fbsd_thread_ops.to_has_memory = 1; + fbsd_thread_ops.to_has_stack = 1; + fbsd_thread_ops.to_has_registers = 1; + fbsd_thread_ops.to_has_execution = 1; + fbsd_thread_ops.to_insert_breakpoint = memory_insert_breakpoint; + fbsd_thread_ops.to_remove_breakpoint = memory_remove_breakpoint; + fbsd_thread_ops.to_get_thread_local_address = fbsd_thread_get_local_address; + fbsd_thread_ops.to_magic = OPS_MAGIC; +} + +static int +thread_db_load (void) +{ + void *handle; + td_err_e err; + + handle = dlopen (LIBTHREAD_DB_SO, RTLD_NOW); + if (handle == NULL) + return 0; + +#define resolve(X) \ + if (!(X##_p = dlsym (handle, #X))) \ + return 0; + + resolve(td_init); + resolve(td_ta_new); + resolve(td_ta_delete); + resolve(td_ta_map_id2thr); + resolve(td_ta_map_lwp2thr); + resolve(td_ta_thr_iter); + resolve(td_thr_get_info); +#ifdef PT_GETXMMREGS + resolve(td_thr_getxmmregs); +#endif + resolve(td_thr_getfpregs); + resolve(td_thr_getgregs); +#ifdef PT_GETXMMREGS + resolve(td_thr_setxmmregs); +#endif + resolve(td_thr_setfpregs); + resolve(td_thr_setgregs); + resolve(td_thr_sstep); + resolve(td_ta_tsd_iter); + resolve(td_thr_dbsuspend); + resolve(td_thr_dbresume); + resolve(td_thr_tls_get_addr); + + /* Initialize the library. */ + err = td_init_p (); + if (err != TD_OK) + { + warning ("Cannot initialize libthread_db: %s", thread_db_err_str (err)); + return 0; + } + + /* These are not essential. */ + td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr"); + td_ta_set_event_p = dlsym (handle, "td_ta_set_event"); + td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"); + td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"); + td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"); + + return 1; +} + +void +thread_db_init (struct target_ops *target) +{ + target_beneath = target; +} + +/* we suppress the call to add_target of core_ops in corelow because + if there are two targets in the stratum core_stratum, find_core_target + won't know which one to return. see corelow.c for an additonal + comment on coreops_suppress_target. */ + +int coreops_suppress_target = 1; + +void +_initialize_thread_db (void) +{ + init_fbsd_thread_ops (); + init_fbsd_core_ops (); + + if (thread_db_load ()) + { + add_target (&fbsd_thread_ops); + + /* "thread tsd" command */ + add_cmd ("tsd", class_run, fbsd_thread_tsd_cmd, + "Show the thread-specific data keys and destructors " + "for the process.\n", + &thread_cmd_list); + + memcpy (&orig_core_ops, &core_ops, sizeof (struct target_ops)); + memcpy (&core_ops, &fbsd_core_ops, sizeof (struct target_ops)); + add_target (&core_ops); + + /* Add ourselves to objfile event chain. */ + target_new_objfile_chain = deprecated_target_new_objfile_hook; + deprecated_target_new_objfile_hook = fbsd_thread_new_objfile; + + child_suppress_run = 1; + } + else + { + fprintf_unfiltered (gdb_stderr, + "[GDB will not be able to debug user-mode threads: %s]\n", dlerror()); + + /* allow the user to debug non-threaded core files */ + add_target (&core_ops); + } +} + +/* proc service functions */ +void +ps_plog (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vfprintf_filtered (gdb_stderr, fmt, args); + va_end (args); +} + +ps_err_e +ps_pglobal_lookup (struct ps_prochandle *ph, const char *obj, + const char *name, psaddr_t *sym_addr) +{ + struct minimal_symbol *ms; + CORE_ADDR addr; + + ms = lookup_minimal_symbol (name, NULL, NULL); + if (ms == NULL) + return PS_NOSYM; + + addr = SYMBOL_VALUE_ADDRESS (ms); + store_typed_address((gdb_byte *)sym_addr, builtin_type_void_data_ptr, addr); + return PS_OK; +} + +ps_err_e +ps_pread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len) +{ + int err = target_read_memory ( + extract_typed_address((const gdb_byte *)&addr, builtin_type_void_data_ptr), buf, len); + return (err == 0 ? PS_OK : PS_ERR); +} + +ps_err_e +ps_pwrite (struct ps_prochandle *ph, psaddr_t addr, const void *buf, + size_t len) +{ + int err = target_write_memory ( + extract_typed_address((const gdb_byte *)&addr, builtin_type_void_data_ptr), (void *)buf, len); + return (err == 0 ? PS_OK : PS_ERR); +} + +ps_err_e +ps_lgetregs (struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + + /* XXX: Target operation isn't lwp aware: replace pid with lwp */ + inferior_ptid = BUILD_LWP (0, lwpid); + + target_fetch_registers (-1); + fill_gregset (gregset, -1); + do_cleanups (old_chain); + return PS_OK; +} + +ps_err_e +ps_lsetregs (struct ps_prochandle *ph, lwpid_t lwpid, const prgregset_t gregset) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid)); + supply_gregset (gregset); + target_store_registers (-1); + do_cleanups (old_chain); + return PS_OK; +} + +ps_err_e +ps_lgetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *fpregset) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid)); + target_fetch_registers (-1); + fill_fpregset (fpregset, -1); + do_cleanups (old_chain); + return PS_OK; +} + +ps_err_e +ps_lsetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, + const prfpregset_t *fpregset) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid)); + supply_fpregset (fpregset); + target_store_registers (-1); + do_cleanups (old_chain); + return PS_OK; +} + +#ifdef PT_GETXMMREGS +ps_err_e +ps_lgetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid, char *xmmregs) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid)); + target_fetch_registers (-1); + i387_fill_fxsave (xmmregs, -1); + do_cleanups (old_chain); + return PS_OK; +} + +ps_err_e +ps_lsetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid, + const char *xmmregs) +{ + struct cleanup *old_chain; + + old_chain = save_inferior_ptid (); + inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid)); + i387_supply_fxsave (current_regcache, -1, xmmregs); + target_store_registers (-1); + do_cleanups (old_chain); + return PS_OK; +} +#endif + +ps_err_e +ps_lstop(struct ps_prochandle *ph, lwpid_t lwpid) +{ + if (ptrace (PT_SUSPEND, lwpid, 0, 0) == -1) + return PS_ERR; + return PS_OK; +} + +ps_err_e +ps_lcontinue(struct ps_prochandle *ph, lwpid_t lwpid) +{ + if (ptrace (PT_RESUME, lwpid, 0, 0) == -1) + return PS_ERR; + return PS_OK; +} Index: i386bsd-nat.c =================================================================== RCS file: /services/cvs/cvsroot/latest/gdb/gdb/i386bsd-nat.c,v retrieving revision 1.1.1.2.4.1 diff -u -p -r1.1.1.2.4.1 i386bsd-nat.c --- i386bsd-nat.c 19 Jan 2007 05:21:41 -0000 1.1.1.2.4.1 +++ i386bsd-nat.c 7 Feb 2007 00:12:16 -0000 @@ -105,6 +105,21 @@ i386bsd_supply_gregset (struct regcache } } +void +supply_gregset (void *gregsetp) /* FIXME: arg type */ +{ + i386bsd_supply_gregset (current_regcache, (const void *) gregsetp); +} + +/* Fill GDB's register array with the floating-point register values in + *FPREGSETP. */ + +void +supply_fpregset (void *fpregsetp) +{ + i387_supply_fsave (current_regcache, -1, fpregsetp); +} + /* Collect register REGNUM from REGCACHE and store its contents in GREGS. If REGNUM is -1, collect and store all appropriate registers. */ @@ -128,6 +143,22 @@ i386bsd_collect_gregset (const struct re } } +void +fill_gregset (void *gregsetp, int regno) +{ + i386bsd_collect_gregset (current_regcache, (const void *) gregsetp, regno); +} + +/* Fill register REGNO (if it is a floating-point register) in + *FPREGSETP with the value in GDB's register array. If REGNO is -1, + do this for all registers. */ + +void +fill_fpregset (void *fpregsetp, int regno) +{ + i387_fill_fsave ((char *) fpregsetp, regno); +} + /* Fetch register REGNUM from the inferior. If REGNUM is -1, do this for all registers (including the floating point registers). */ Index: i386fbsd-nat.c =================================================================== RCS file: /services/cvs/cvsroot/latest/gdb/gdb/i386fbsd-nat.c,v retrieving revision 1.1.1.2.4.1 diff -u -p -r1.1.1.2.4.1 i386fbsd-nat.c --- i386fbsd-nat.c 19 Jan 2007 05:21:41 -0000 1.1.1.2.4.1 +++ i386fbsd-nat.c 7 Feb 2007 00:12:16 -0000 @@ -131,6 +131,10 @@ _initialize_i386fbsd_nat (void) t->to_make_corefile_notes = fbsd_make_corefile_notes; add_target (t); + /* TODO: Eliminate this and have libthread_db use + find_target_beneath. */ + thread_db_init (t); + /* Support debugging kernel virtual memory images. */ bsd_kvm_add_target (i386fbsd_supply_pcb); Index: inf-child.c =================================================================== RCS file: /services/cvs/cvsroot/latest/gdb/gdb/inf-child.c,v retrieving revision 1.1.1.2.4.1 diff -u -p -r1.1.1.2.4.1 inf-child.c --- inf-child.c 19 Jan 2007 05:21:43 -0000 1.1.1.2.4.1 +++ inf-child.c 7 Feb 2007 00:12:16 -0000 @@ -29,6 +29,11 @@ #include "inferior.h" #include "gdb_string.h" +int child_suppress_run = 0; /* Non-zero if inf-child should pretend not to + be a runnable target. Used by targets + that can sit atop inftarg, such as FreeBSD + thread support. */ + /* Fetch register REGNUM from the inferior. If REGNUM is -1, do this for all registers. */ @@ -154,7 +159,7 @@ inf_child_reported_exec_events_per_exec_ static int inf_child_can_run (void) { - return 1; + return !child_suppress_run; } static struct symtab_and_line * Index: config/i386/fbsd.mh =================================================================== RCS file: /services/cvs/cvsroot/latest/gdb/gdb/config/i386/fbsd.mh,v retrieving revision 1.1.1.1 diff -u -p -r1.1.1.1 fbsd.mh --- config/i386/fbsd.mh 8 Oct 2005 19:36:19 -0000 1.1.1.1 +++ config/i386/fbsd.mh 7 Feb 2007 00:12:16 -0000 @@ -1,7 +1,9 @@ # Host: FreeBSD/i386 NATDEPFILES= fork-child.o inf-ptrace.o \ fbsd-nat.o i386-nat.o i386bsd-nat.o i386fbsd-nat.o \ - gcore.o bsd-kvm.o + gcore.o bsd-kvm.o fbsd-threads.o NAT_FILE= nm-fbsd.h -LOADLIBES= -lkvm +# The dynamically loaded libthread_db needs access to symbols in the +# gdb executable. +LOADLIBES= -lkvm -rdynamic