Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.629 diff -u -p -u -r1.629 gdb.texinfo --- doc/gdb.texinfo 26 Sep 2009 16:47:13 -0000 1.629 +++ doc/gdb.texinfo 2 Oct 2009 23:49:31 -0000 @@ -14900,6 +14900,13 @@ Disable or enable general debugging mess Disable or enable specific debugging messages associated with the remote protocol (@pxref{Remote Protocol}). +@item monitor set libthread-db-search-path [PATH] +@cindex gdbserver, search path for @code{libthread_db} +When this command is issued, @var{path} is a colon-separated list of +directories to search for @code{libthread_db} (@pxref{Threads,,set +libthread-db-search-path}). If you omit @var{path}, +@samp{libthread-db-search-path} will be reset to an empty list. + @item monitor exit Tell gdbserver to exit immediately. This command should be followed by @code{disconnect} to close the debugging session. @code{gdbserver} will Index: gdbserver/acinclude.m4 =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/acinclude.m4,v retrieving revision 1.7 diff -u -p -u -r1.7 acinclude.m4 --- gdbserver/acinclude.m4 5 Jun 2008 22:36:57 -0000 1.7 +++ gdbserver/acinclude.m4 2 Oct 2009 23:49:31 -0000 @@ -22,7 +22,7 @@ AC_DEFUN([SRV_CHECK_THREAD_DB], void ps_get_thread_area() {} void ps_getpid() {}], [td_ta_new();], - [srv_cv_thread_db="-lthread_db"], + [srv_cv_thread_db="-ldl"], [srv_cv_thread_db=no if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then @@ -42,28 +42,9 @@ AC_DEFUN([SRV_CHECK_THREAD_DB], void ps_get_thread_area() {} void ps_getpid() {}], [td_ta_new();], - [srv_cv_thread_db="$thread_db"], + [srv_cv_thread_db="-ldl"], [srv_cv_thread_db=no]) ]) LIBS="$old_LIBS" ])]) -AC_DEFUN([SRV_CHECK_TLS_GET_ADDR], -[AC_CACHE_CHECK([for thread_db_tls_get_addr],[srv_cv_tls_get_addr], - [old_LIBS="$LIBS" - LIBS="$LIBS $srv_cv_thread_db" - AC_TRY_LINK( - [void ps_pglobal_lookup() {} - void ps_pdread() {} - void ps_pdwrite() {} - void ps_lgetregs() {} - void ps_lsetregs() {} - void ps_lgetfpregs() {} - void ps_lsetfpregs() {} - void ps_get_thread_area() {} - void ps_getpid() {}], - [td_thr_tls_get_addr();], - [srv_cv_tls_get_addr=yes], - [srv_cv_tls_get_addr=no]) - LIBS="$old_LIBS" -])]) Index: gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.112 diff -u -p -u -r1.112 linux-low.c --- gdbserver/linux-low.c 31 Jul 2009 15:25:22 -0000 1.112 +++ gdbserver/linux-low.c 2 Oct 2009 23:49:31 -0000 @@ -42,6 +42,7 @@ #include #include #include +#include #ifndef SPUFS_MAGIC #define SPUFS_MAGIC 0x23c9b64e @@ -261,8 +262,12 @@ linux_add_process (int pid, int attached static void linux_remove_process (struct process_info *process) { - free (process->private->arch_private); - free (process->private); + struct process_info_private *priv = process->private; + + if (priv->handle != NULL) + dlclose (priv->handle); + free (priv->arch_private); + free (priv); remove_process (process); } Index: gdbserver/linux-low.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v retrieving revision 1.32 diff -u -p -u -r1.32 linux-low.h --- gdbserver/linux-low.h 30 Jun 2009 16:35:25 -0000 1.32 +++ gdbserver/linux-low.h 2 Oct 2009 23:49:31 -0000 @@ -57,6 +57,32 @@ struct process_info_private /* Connection to the libthread_db library. */ td_thragent_t *thread_agent; + /* Handle of the libthread_db from dlopen. */ + void *handle; + + /* Addresses of libthread_db functions. */ + td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, td_thragent_t **ta); + td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta, + td_event_msg_t *msg); + td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta, + td_thr_events_t *event); + td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta, + td_event_e event, td_notify_t *ptr); + td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, lwpid_t lwpid, + td_thrhandle_t *th); + td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th, + td_thrinfo_t *infop); + td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, int event); + 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); + td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, + void *map_address, + size_t offset, void **address); + const char ** (*td_symbol_list_p) (void); + /* Arch-specific additions. */ struct arch_process_info *arch_private; }; Index: gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.102 diff -u -p -u -r1.102 server.c --- gdbserver/server.c 30 Jun 2009 16:35:25 -0000 1.102 +++ gdbserver/server.c 2 Oct 2009 23:49:31 -0000 @@ -32,6 +32,8 @@ #include #endif +#include + ptid_t cont_thread; ptid_t general_thread; ptid_t step_thread; @@ -51,6 +53,10 @@ static char **program_argv, **wrapper_ar was originally used to debug LinuxThreads support. */ int debug_threads; +/* If not NULL, a colon-separated list of paths to use while looking for + libthread_db. */ +char *libthread_db_search_path; + /* Enable debugging of h/w breakpoint/watchpoint support. */ int debug_hw_points; @@ -1245,6 +1251,23 @@ handle_query (char *own_buf, int packet_ monitor_show_help (); else if (strcmp (mon, "exit") == 0) exit_requested = 1; + else if (strncmp (mon, "set libthread-db-search-path", 28) == 0) + { + const char *cp = mon + 28; + + if (libthread_db_search_path != NULL) + free (libthread_db_search_path); + + /* Skip leading space (if any). */ + while (isspace (*cp)) + ++cp; + + libthread_db_search_path = xstrdup (cp); + + monitor_output ("libthread-db-search-path set to `"); + monitor_output (libthread_db_search_path); + monitor_output ("'\n"); + } else { monitor_output ("Unknown monitor command.\n\n"); Index: gdbserver/thread-db.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/thread-db.c,v retrieving revision 1.23 diff -u -p -u -r1.23 thread-db.c --- gdbserver/thread-db.c 3 Apr 2009 20:15:51 -0000 1.23 +++ gdbserver/thread-db.c 2 Oct 2009 23:49:31 -0000 @@ -27,13 +27,14 @@ extern int debug_threads; static int thread_db_use_events; -#ifdef HAVE_THREAD_DB_H -#include -#endif - #include "gdb_proc_service.h" +#include "../gdb_thread_db.h" +#include #include +#include + +extern char *libthread_db_search_path; static int find_one_thread (ptid_t); static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); @@ -132,6 +133,10 @@ thread_db_create_event (CORE_ADDR where) struct lwp_info *lwp; struct process_info_private *proc = current_process()->private; + if (proc->td_ta_event_getmsg_p == NULL) + /* We shouldn't have ever be here in the first place. */ + return TD_ERR; + if (debug_threads) fprintf (stderr, "Thread creation event.\n"); @@ -139,7 +144,7 @@ thread_db_create_event (CORE_ADDR where) In the LinuxThreads implementation, this is safe, because all events come from the manager thread (except for its own creation, of course). */ - err = td_ta_event_getmsg (proc->thread_agent, &msg); + err = proc->td_ta_event_getmsg_p (proc->thread_agent, &msg); if (err != TD_OK) fprintf (stderr, "thread getmsg err: %s\n", thread_db_err_str (err)); @@ -177,17 +182,17 @@ thread_db_enable_reporting () td_err_e err; struct process_info_private *proc = current_process()->private; + if (proc->td_ta_set_event_p == NULL + || proc->td_ta_event_addr_p == NULL + || proc->td_ta_event_getmsg_p == NULL) + /* This libthread_db is missing required support. */ + return TD_ERR; + /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); -#if 0 - /* This is reported to be broken in glibc 2.1.3. A different approach - will be necessary to support that. */ - td_event_addset (&events, TD_DEATH); -#endif - - err = td_ta_set_event (proc->thread_agent, &events); + err = proc->td_ta_set_event_p (proc->thread_agent, &events); if (err != TD_OK) { warning ("Unable to set global thread event mask: %s", @@ -196,7 +201,7 @@ thread_db_enable_reporting () } /* Get address for thread creation breakpoint. */ - err = td_ta_event_addr (proc->thread_agent, TD_CREATE, ¬ify); + err = proc->td_ta_event_addr_p (proc->thread_agent, TD_CREATE, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread creation breakpoint: %s", @@ -206,22 +211,6 @@ thread_db_enable_reporting () set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, thread_db_create_event); -#if 0 - /* Don't concern ourselves with reported thread deaths, only - with actual thread deaths (via wait). */ - - /* Get address for thread death breakpoint. */ - err = td_ta_event_addr (proc->thread_agent, TD_DEATH, ¬ify); - if (err != TD_OK) - { - warning ("Unable to get location for thread death breakpoint: %s", - thread_db_err_str (err)); - return; - } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_death_event); -#endif - return 1; } @@ -233,7 +222,7 @@ find_one_thread (ptid_t ptid) td_err_e err; struct thread_info *inferior; struct lwp_info *lwp; - struct process_info_private *proc; + struct process_info_private *proc = current_process()->private; int lwpid = ptid_get_lwp (ptid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); @@ -242,13 +231,12 @@ find_one_thread (ptid_t ptid) return 1; /* Get information about this thread. */ - proc = get_thread_process (inferior)->private; - err = td_ta_map_lwp2thr (proc->thread_agent, lwpid, &th); + err = proc->td_ta_map_lwp2thr_p (proc->thread_agent, lwpid, &th); if (err != TD_OK) error ("Cannot get thread handle for LWP %d: %s", lwpid, thread_db_err_str (err)); - err = td_thr_get_info (&th, &ti); + err = proc->td_thr_get_info_p (&th, &ti); if (err != TD_OK) error ("Cannot get thread info for LWP %d: %s", lwpid, thread_db_err_str (err)); @@ -266,7 +254,7 @@ find_one_thread (ptid_t ptid) if (thread_db_use_events) { - err = td_thr_event_enable (&th, 1); + err = proc->td_thr_event_enable_p (&th, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti.ti_lid, thread_db_err_str (err)); @@ -310,7 +298,8 @@ maybe_attach_thread (const td_thrhandle_ if (thread_db_use_events) { - err = td_thr_event_enable (th_p, 1); + struct process_info_private *proc = current_process()->private; + err = proc->td_thr_event_enable_p (th_p, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti_p->ti_lid, thread_db_err_str (err)); @@ -322,8 +311,9 @@ find_new_threads_callback (const td_thrh { td_thrinfo_t ti; td_err_e err; + struct process_info_private *proc = current_process()->private; - err = td_thr_get_info (th_p, &ti); + err = proc->td_thr_get_info_p (th_p, &ti); if (err != TD_OK) error ("Cannot get thread info: %s", thread_db_err_str (err)); @@ -350,10 +340,10 @@ thread_db_find_new_threads (void) return; /* Iterate over all user-space threads to discover new threads. */ - err = td_ta_thr_iter (proc->thread_agent, - find_new_threads_callback, NULL, - TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + err = proc->td_ta_thr_iter_p (proc->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)); } @@ -366,10 +356,11 @@ thread_db_find_new_threads (void) static void thread_db_look_up_symbols (void) { - const char **sym_list = td_symbol_list (); + struct process_info_private *proc = current_process()->private; + const char **sym_list; CORE_ADDR unused; - for (sym_list = td_symbol_list (); *sym_list; sym_list++) + for (sym_list = proc->td_symbol_list_p (); *sym_list; sym_list++) look_up_one_symbol (*sym_list, &unused); } @@ -377,14 +368,21 @@ int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, CORE_ADDR load_module, CORE_ADDR *address) { -#if HAVE_TD_THR_TLS_GET_ADDR psaddr_t addr; td_err_e err; struct lwp_info *lwp; struct thread_info *saved_inferior; + struct process_info *proc; + struct process_info_private *priv; + + proc = get_thread_process (thread); + priv = proc->private; /* If the thread layer is not (yet) initialized, fail. */ - if (!get_thread_process (thread)->all_symbols_looked_up) + if (proc->all_symbols_looked_up == 0) + return TD_ERR; + + if (priv->td_thr_tls_get_addr_p == NULL) return TD_ERR; lwp = get_thread_lwp (thread); @@ -398,8 +396,9 @@ thread_db_get_tls_address (struct thread /* Note the cast through uintptr_t: this interface only works if a target address fits in a psaddr_t, which is a host pointer. So a 32-bit debugger can not access 64-bit TLS through this. */ - err = td_thr_tls_get_addr (&lwp->th, (psaddr_t) (uintptr_t) load_module, - offset, &addr); + err = priv->td_thr_tls_get_addr_p (&lwp->th, + (psaddr_t) (uintptr_t) load_module, + offset, &addr); current_inferior = saved_inferior; if (err == TD_OK) { @@ -408,17 +407,180 @@ thread_db_get_tls_address (struct thread } else return err; -#else - return -1; -#endif +} + +static int +try_thread_db_load_1 (void *handle) +{ + td_err_e err; + struct process_info *proc = current_process (); + struct process_info_private *priv = proc->private; + + /* Initialize pointers to the dynamic library functions we will use. + Essential functions first. */ + +#define CHK(required, a) \ + if ((a) == NULL) { \ + if (debug_threads) { \ + fprintf (stderr, "dlsym: %s\n", dlerror ()); \ + } \ + if (required) \ + return 0; \ + } + + CHK (1, priv->td_ta_new_p = dlsym (handle, "td_ta_new")); + + /* Attempt to open a connection to the thread library. */ + err = priv->td_ta_new_p (&priv->proc_handle, &priv->thread_agent); + if (err != TD_OK) + { + priv->td_ta_new_p = NULL; + if (debug_threads) + fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err)); + return 0; + } + + CHK (1, priv->td_ta_map_lwp2thr_p = dlsym (handle, "td_ta_map_lwp2thr")); + CHK (1, priv->td_thr_get_info_p = dlsym (handle, "td_thr_get_info")); + CHK (1, priv->td_ta_thr_iter_p = dlsym (handle, "td_ta_thr_iter")); + CHK (1, priv->td_symbol_list_p = dlsym (handle, "td_symbol_list")); + + /* This is required only when thread_db_use_events is on. */ + CHK (thread_db_use_events, + priv->td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable")); + + /* These are not essential. */ + CHK (0, priv->td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr")); + CHK (0, priv->td_ta_set_event_p = dlsym (handle, "td_ta_set_event")); + CHK (0, priv->td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg")); + CHK (0, priv->td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr")); + +#undef CHK + + return 1; +} + +/* Lookup a library in which given symbol resides. + Note: this is looking in the GDBSERVER process, not in the inferior. + Returns library name, or NULL. */ + +static const char * +dladdr_to_soname (const void *addr) +{ + Dl_info info; + + if (dladdr (addr, &info) != 0) + return info.dli_fname; + return NULL; +} + +static int +try_thread_db_load (const char *library) +{ + void *handle; + + if (debug_threads) + fprintf (stderr, "Trying host libthread_db library: %s.\n", + library); + handle = dlopen (library, RTLD_NOW); + if (handle == NULL) + { + if (debug_threads) + fprintf (stderr, "dlopen failed: %s.\n", dlerror ()); + return 0; + } + + if (debug_threads && strchr (library, '/') == NULL) + { + void *td_init; + + td_init = dlsym (handle, "td_init"); + if (td_init != NULL) + { + const char *const libpath = dladdr_to_soname (td_init); + + if (libpath != NULL) + fprintf (stderr, "Host %s resolved to: %s.\n", + library, libpath); + } + } + + if (try_thread_db_load_1 (handle)) + return 1; + + /* This library "refused" to work on current inferior. */ + dlclose (handle); + return 0; +} + +static int +thread_db_load_search () +{ + char path[PATH_MAX]; + const char *search_path; + int rc = 0; + + + if (libthread_db_search_path == NULL) + libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH); + + search_path = libthread_db_search_path; + while (*search_path) + { + const char *end = strchr (search_path, ':'); + if (end) + { + size_t len = end - search_path; + if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) + { + char *cp = xmalloc (len + 1); + memcpy (cp, search_path, len); + cp[len] = '\0'; + warning ("libthread_db_search_path component too long, " + "ignored: %s.", cp); + free (cp); + search_path += len + 1; + continue; + } + memcpy (path, search_path, len); + path[len] = '\0'; + search_path += len + 1; + } + else + { + size_t len = strlen (search_path); + + if (len + 1 + strlen (LIBTHREAD_DB_SO) + 1 > sizeof (path)) + { + warning ("libthread_db_search_path component too long," + " ignored: %s.", search_path); + break; + } + memcpy (path, search_path, len + 1); + search_path += len; + } + strcat (path, "/"); + strcat (path, LIBTHREAD_DB_SO); + if (debug_threads) + fprintf (stderr, "thread_db_load_search trying %s\n", path); + if (try_thread_db_load (path)) + { + rc = 1; + break; + } + } + if (rc == 0) + rc = try_thread_db_load (LIBTHREAD_DB_SO); + + if (debug_threads) + fprintf (stderr, "thread_db_load_search returning %d\n", rc); + return rc; } int thread_db_init (int use_events) { - int err; struct process_info *proc = current_process (); - struct process_info_private *priv = proc->private; /* FIXME drow/2004-10-16: This is the "overall process ID", which GNU/Linux calls tgid, "thread group ID". When we support @@ -433,26 +595,14 @@ thread_db_init (int use_events) thread_db_use_events = use_events; - err = td_ta_new (&priv->proc_handle, &priv->thread_agent); - switch (err) + if (thread_db_load_search ()) { - case TD_NOLIBTHREAD: - /* No thread library was detected. */ - return 0; - - case TD_OK: - /* The thread library was detected. */ - if (use_events && thread_db_enable_reporting () == 0) return 0; thread_db_find_new_threads (); thread_db_look_up_symbols (); proc->all_symbols_looked_up = 1; return 1; - - default: - warning ("error initializing thread_db library: %s", - thread_db_err_str (err)); } return 0;