doc/
authorPaul Pluzhnikov <ppluzhnikov@google.com>
Fri, 9 Oct 2009 00:31:01 +0000 (00:31 +0000)
committerPaul Pluzhnikov <ppluzhnikov@google.com>
Fri, 9 Oct 2009 00:31:01 +0000 (00:31 +0000)
2009-10-08  Paul Pluzhnikov  <ppluzhnikov@google.com>

* gdb.texinfo (Server): Document libthread-db-search-path.

gdbserver/
2009-10-08  Paul Pluzhnikov  <ppluzhnikov@google.com>

* acinclude.m4: (SRV_CHECK_THREAD_DB, SRV_CHECK_TLS_GET_ADDR): Remove.
* configure.ac: Adjust.
* linux-low.h (struct process_info_private): Move members to struct
thread_db.
(thread_db_free, thread_db_handle_monitor_command): New prototype.
* linux-low.c (linux_remove_process): Adjust.
(linux_wait_for_event_1, linux_look_up_symbols): Likewise.
* server.c (handle_query): Move code ...
(handle_monitor_command): ... here. New function.
* target.h (struct target_ops): New member.
* thread-db.c (struct thread_db): New.
(libthread_db_search_path): New variable.
(thread_db_create_event, thread_db_enable_reporting)
(find_one_thread, maybe_attach_thread, find_new_threads_callback)
(thread_db_find_new_threads, (thread_db_get_tls_address): Adjust.
(try_thread_db_load_1, dladdr_to_soname): New functions.
(try_thread_db_load, thread_db_load_search): New functions.
(thread_db_init): Search for libthread_db.
(thread_db_free): New function.
(thread_db_handle_monitor_command): Likewise.
* config.in: Regenerate.
* configure: Regenerate.

gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/acinclude.m4
gdb/gdbserver/config.in
gdb/gdbserver/configure
gdb/gdbserver/configure.ac
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/server.c
gdb/gdbserver/target.h
gdb/gdbserver/thread-db.c

index 80e9901e717fed9cc8b3871dfb81efcf7080d8d7..3f9fd06fa9f67bdf33a015d88a983a201b5b4c98 100644 (file)
@@ -14903,6 +14903,13 @@ Disable or enable general debugging messages.
 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 a85f26b659a71d9102303c5af0053901e76b236a..9333c2524e65e96bc1f00078949010906bd84d60 100644 (file)
@@ -1,3 +1,28 @@
+2009-10-08  Paul Pluzhnikov  <ppluzhnikov@google.com>
+
+       * acinclude.m4: (SRV_CHECK_THREAD_DB, SRV_CHECK_TLS_GET_ADDR): Remove.
+       * configure.ac: Adjust.
+       * linux-low.h (struct process_info_private): Move members to struct
+       thread_db.
+       (thread_db_free, thread_db_handle_monitor_command): New prototype.
+       * linux-low.c (linux_remove_process): Adjust.
+       (linux_wait_for_event_1, linux_look_up_symbols): Likewise.
+       * server.c (handle_query): Move code ...
+       (handle_monitor_command): ... here. New function.
+       * target.h (struct target_ops): New member.
+       * thread-db.c (struct thread_db): New.
+       (libthread_db_search_path): New variable.
+       (thread_db_create_event, thread_db_enable_reporting)
+       (find_one_thread, maybe_attach_thread, find_new_threads_callback)
+       (thread_db_find_new_threads, (thread_db_get_tls_address): Adjust.
+       (try_thread_db_load_1, dladdr_to_soname): New functions.
+       (try_thread_db_load, thread_db_load_search): New functions.
+       (thread_db_init): Search for libthread_db.
+       (thread_db_free): New function.
+       (thread_db_handle_monitor_command): Likewise.
+       * config.in: Regenerate.
+       * configure: Regenerate.
+       
 2009-09-27  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * spu-low.c (spu_kill): Wait for inferior to terminate.
index 8770ad07bcd783f93b8da66ad90a9eaa5705b507..87d0db5f02e67057e47b762abb9d131795ce404a 100644 (file)
@@ -7,63 +7,3 @@ sinclude(../../config/override.m4)
 dnl For ACX_PKGVERSION and ACX_BUGURL.
 sinclude(../../config/acx.m4)
 
-AC_DEFUN([SRV_CHECK_THREAD_DB],
-[AC_CACHE_CHECK([for libthread_db],[srv_cv_thread_db],
- [old_LIBS="$LIBS"
-  LIBS="$LIBS -lthread_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_ta_new();],
-  [srv_cv_thread_db="-lthread_db"],
-  [srv_cv_thread_db=no
-
- if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then
-  thread_db="/lib/libthread_db.so.1"
- else
-  thread_db='$prefix/lib/libthread_db.so.1'
- fi
- LIBS="$old_LIBS `eval echo "$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_ta_new();],
-  [srv_cv_thread_db="$thread_db"],
-  [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 dc976dc1eb3c6637e4df61479941de7acdc0e780..7eb11b9941ab572f1bf3d5a6a9cd51a40e4de148 100644 (file)
 /* Define to 1 if you have the <sys/wait.h> header file. */
 #undef HAVE_SYS_WAIT_H
 
-/* Define if td_thr_tls_get_addr is available. */
-#undef HAVE_TD_THR_TLS_GET_ADDR
-
 /* Define if TD_VERSION is available. */
 #undef HAVE_TD_VERSION
 
index b777c1b08ea99ef7241dfe1e2dffc6d129d91787..b88ed5b605deabe1a02eff5c2cf9bc29131eec22 100755 (executable)
@@ -4210,125 +4210,7 @@ srv_libs=
 USE_THREAD_DB=
 
 if test "$srv_linux_thread_db" = "yes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libthread_db" >&5
-$as_echo_n "checking for libthread_db... " >&6; }
-if test "${srv_cv_thread_db+set}" = set; then :
-  $as_echo_n "(cached) " >&6
-else
-  old_LIBS="$LIBS"
-  LIBS="$LIBS -lthread_db"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-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() {}
-int
-main ()
-{
-td_ta_new();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  srv_cv_thread_db="-lthread_db"
-else
-  srv_cv_thread_db=no
-
- if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then
-  thread_db="/lib/libthread_db.so.1"
- else
-  thread_db='$prefix/lib/libthread_db.so.1'
- fi
- LIBS="$old_LIBS `eval echo "$thread_db"`"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-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() {}
-int
-main ()
-{
-td_ta_new();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  srv_cv_thread_db="$thread_db"
-else
-  srv_cv_thread_db=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
- LIBS="$old_LIBS"
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $srv_cv_thread_db" >&5
-$as_echo "$srv_cv_thread_db" >&6; }
-  if test "$srv_cv_thread_db" = no; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find libthread_db." >&5
-$as_echo "$as_me: WARNING: Could not find libthread_db." >&2;}
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling thread support in gdbserver." >&5
-$as_echo "$as_me: WARNING: Disabling thread support in gdbserver." >&2;}
-    srv_linux_thread_db=no
-  else
-    srv_libs="$srv_cv_thread_db"
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for thread_db_tls_get_addr" >&5
-$as_echo_n "checking for thread_db_tls_get_addr... " >&6; }
-if test "${srv_cv_tls_get_addr+set}" = set; then :
-  $as_echo_n "(cached) " >&6
-else
-  old_LIBS="$LIBS"
-  LIBS="$LIBS $srv_cv_thread_db"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-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() {}
-int
-main ()
-{
-td_thr_tls_get_addr();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  srv_cv_tls_get_addr=yes
-else
-  srv_cv_tls_get_addr=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  LIBS="$old_LIBS"
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $srv_cv_tls_get_addr" >&5
-$as_echo "$srv_cv_tls_get_addr" >&6; }
-  fi
+  srv_libs="-ldl"
   old_LDFLAGS="$LDFLAGS"
   LDFLAGS="$LDFLAGS -rdynamic"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4351,9 +4233,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 
   LDFLAGS="$old_LDFLAGS"
-fi
 
-if test "$srv_linux_thread_db" = "yes"; then
   srv_thread_depfiles="thread-db.o proc-service.o"
   USE_THREAD_DB="-DUSE_THREAD_DB"
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TD_VERSION" >&5
@@ -4386,12 +4266,6 @@ $as_echo "$gdbsrv_cv_have_td_version" >&6; }
 $as_echo "#define HAVE_TD_VERSION 1" >>confdefs.h
 
   fi
-
-  if test "$srv_cv_tls_get_addr" = yes; then
-
-$as_echo "#define HAVE_TD_THR_TLS_GET_ADDR 1" >>confdefs.h
-
-  fi
 fi
 
 if test "$srv_xmlfiles" != ""; then
index 0bba8244dd7cde6b6fdf51bd1f372afae521050c..a631c7eb0fa396e1d7a548c0914e83d07b2539f2 100644 (file)
@@ -140,23 +140,13 @@ srv_libs=
 USE_THREAD_DB=
 
 if test "$srv_linux_thread_db" = "yes"; then
-  SRV_CHECK_THREAD_DB
-  if test "$srv_cv_thread_db" = no; then
-    AC_WARN([Could not find libthread_db.])
-    AC_WARN([Disabling thread support in gdbserver.])
-    srv_linux_thread_db=no
-  else
-    srv_libs="$srv_cv_thread_db"
-    SRV_CHECK_TLS_GET_ADDR
-  fi
+  srv_libs="-ldl"
   old_LDFLAGS="$LDFLAGS"
   LDFLAGS="$LDFLAGS -rdynamic"
   AC_TRY_LINK([], [], [RDYNAMIC=-rdynamic], [RDYNAMIC=])
   AC_SUBST(RDYNAMIC)
   LDFLAGS="$old_LDFLAGS"
-fi
 
-if test "$srv_linux_thread_db" = "yes"; then
   srv_thread_depfiles="thread-db.o proc-service.o"
   USE_THREAD_DB="-DUSE_THREAD_DB"
   AC_CACHE_CHECK([for TD_VERSION], gdbsrv_cv_have_td_version,
@@ -166,10 +156,6 @@ if test "$srv_linux_thread_db" = "yes"; then
   if test $gdbsrv_cv_have_td_version = yes; then
     AC_DEFINE(HAVE_TD_VERSION, 1, [Define if TD_VERSION is available.])
   fi
-
-  if test "$srv_cv_tls_get_addr" = yes; then
-    AC_DEFINE(HAVE_TD_THR_TLS_GET_ADDR, 1, [Define if td_thr_tls_get_addr is available.])
-  fi
 fi
 
 if test "$srv_xmlfiles" != ""; then
index a88f4d1f02e8192477898c72082ee034a4d20571..8a882376e6944d11db4251c23be339960f3e97e2 100644 (file)
@@ -261,8 +261,14 @@ 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;
+
+#ifdef USE_THREAD_DB
+  thread_db_free (process);
+#endif
+
+  free (priv->arch_private);
+  free (priv);
   remove_process (process);
 }
 
@@ -1122,7 +1128,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
          && !event_child->stepping
          && (
 #ifdef USE_THREAD_DB
-             (current_process ()->private->thread_db_active
+             (current_process ()->private->thread_db != NULL
               && (WSTOPSIG (*wstat) == __SIGRTMIN
                   || WSTOPSIG (*wstat) == __SIGRTMIN + 1))
              ||
@@ -2642,11 +2648,10 @@ linux_look_up_symbols (void)
 #ifdef USE_THREAD_DB
   struct process_info *proc = current_process ();
 
-  if (proc->private->thread_db_active)
+  if (proc->private->thread_db != NULL)
     return;
 
-  proc->private->thread_db_active
-    = thread_db_init (!linux_supports_tracefork_flag);
+  thread_db_init (!linux_supports_tracefork_flag);
 #endif
 }
 
@@ -3171,7 +3176,12 @@ static struct target_ops linux_target_ops = {
   linux_supports_non_stop,
   linux_async,
   linux_start_non_stop,
-  linux_supports_multi_process
+  linux_supports_multi_process,
+#ifdef USE_THREAD_DB
+  thread_db_handle_monitor_command
+#else
+  NULL
+#endif
 };
 
 static void
index 603963e9b089a9386b06ab3fbfdd2f46972daa0c..9a3815c995a2364a7ff90598acb4130ddd2ebe4d 100644 (file)
@@ -47,18 +47,12 @@ struct siginfo;
 
 struct process_info_private
 {
-  /* True if this process has loaded thread_db, and it is active.  */
-  int thread_db_active;
-
-  /* Structure that identifies the child process for the
-     <proc_service.h> interface.  */
-  struct ps_prochandle proc_handle;
-
-  /* Connection to the libthread_db library.  */
-  td_thragent_t *thread_agent;
-
   /* Arch-specific additions.  */
   struct arch_process_info *arch_private;
+
+  /* libthread_db-specific additions.  Not NULL if this process has loaded
+     thread_db, and it is active.  */
+  struct thread_db *thread_db;
 };
 
 struct lwp_info;
@@ -203,9 +197,11 @@ char *linux_child_pid_to_exec_file (int pid);
 int elf_64_file_p (const char *file);
 
 void linux_attach_lwp (unsigned long pid);
+struct lwp_info *find_lwp_pid (ptid_t ptid);
 
+/* From thread-db.c  */
 int thread_db_init (int use_events);
+void thread_db_free (struct process_info *);
+int thread_db_handle_monitor_command (char *);
 int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
                               CORE_ADDR load_module, CORE_ADDR *address);
-
-struct lwp_info *find_lwp_pid (ptid_t ptid);
index d704180a5399894f23ab0a23c79de5ec133166fb..9bf4f3dbd00189b82c1706856057103c11e682e2 100644 (file)
@@ -660,6 +660,53 @@ handle_search_memory (char *own_buf, int packet_len)
       return;                                  \
     }
 
+/* Handle monitor commands not handled by target-specific handlers.  */
+
+static void
+handle_monitor_command (char *mon)
+{
+  if (strcmp (mon, "set debug 1") == 0)
+    {
+      debug_threads = 1;
+      monitor_output ("Debug output enabled.\n");
+    }
+  else if (strcmp (mon, "set debug 0") == 0)
+    {
+      debug_threads = 0;
+      monitor_output ("Debug output disabled.\n");
+    }
+  else if (strcmp (mon, "set debug-hw-points 1") == 0)
+    {
+      debug_hw_points = 1;
+      monitor_output ("H/W point debugging output enabled.\n");
+    }
+  else if (strcmp (mon, "set debug-hw-points 0") == 0)
+    {
+      debug_hw_points = 0;
+      monitor_output ("H/W point debugging output disabled.\n");
+    }
+  else if (strcmp (mon, "set remote-debug 1") == 0)
+    {
+      remote_debug = 1;
+      monitor_output ("Protocol debug output enabled.\n");
+    }
+  else if (strcmp (mon, "set remote-debug 0") == 0)
+    {
+      remote_debug = 0;
+      monitor_output ("Protocol debug output disabled.\n");
+    }
+  else if (strcmp (mon, "help") == 0)
+    monitor_show_help ();
+  else if (strcmp (mon, "exit") == 0)
+    exit_requested = 1;
+  else
+    {
+      monitor_output ("Unknown monitor command.\n\n");
+      monitor_show_help ();
+      write_enn (own_buf);
+    }
+}
+
 /* Handle all of the extended 'q' packets.  */
 void
 handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1211,46 +1258,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       write_ok (own_buf);
 
-      if (strcmp (mon, "set debug 1") == 0)
-       {
-         debug_threads = 1;
-         monitor_output ("Debug output enabled.\n");
-       }
-      else if (strcmp (mon, "set debug 0") == 0)
-       {
-         debug_threads = 0;
-         monitor_output ("Debug output disabled.\n");
-       }
-      else if (strcmp (mon, "set debug-hw-points 1") == 0)
-       {
-         debug_hw_points = 1;
-         monitor_output ("H/W point debugging output enabled.\n");
-       }
-      else if (strcmp (mon, "set debug-hw-points 0") == 0)
-       {
-         debug_hw_points = 0;
-         monitor_output ("H/W point debugging output disabled.\n");
-       }
-      else if (strcmp (mon, "set remote-debug 1") == 0)
-       {
-         remote_debug = 1;
-         monitor_output ("Protocol debug output enabled.\n");
-       }
-      else if (strcmp (mon, "set remote-debug 0") == 0)
-       {
-         remote_debug = 0;
-         monitor_output ("Protocol debug output disabled.\n");
-       }
-      else if (strcmp (mon, "help") == 0)
-       monitor_show_help ();
-      else if (strcmp (mon, "exit") == 0)
-       exit_requested = 1;
-      else
-       {
-         monitor_output ("Unknown monitor command.\n\n");
-         monitor_show_help ();
-         write_enn (own_buf);
-       }
+      if (the_target->handle_monitor_command == NULL
+         || (*the_target->handle_monitor_command) (mon) == 0)
+       /* Default processing.  */
+       handle_monitor_command (mon);
 
       free (mon);
       return;
index 5fdfee312a89b8b5bb394687eaedd8350ae1f37f..0e4b12774728cc840ea11602e0b95e94870caea4 100644 (file)
@@ -279,6 +279,10 @@ struct target_ops
 
   /* Returns true if the target supports multi-process debugging.  */
   int (*supports_multi_process) (void);
+
+  /* If not NULL, target-specific routine to process monitor command.
+     Returns 1 if handled, or 0 to perform default processing.  */
+  int (*handle_monitor_command) (char *);
 };
 
 extern struct target_ops *the_target;
index 4a26be951b26333b191020b7da44874940f99a55..4d1008fce2907b0fecf5ce9317aeae972d238473 100644 (file)
@@ -27,13 +27,51 @@ extern int debug_threads;
 
 static int thread_db_use_events;
 
-#ifdef HAVE_THREAD_DB_H
-#include <thread_db.h>
-#endif
-
 #include "gdb_proc_service.h"
+#include "../gdb_thread_db.h"
 
+#include <dlfcn.h>
 #include <stdint.h>
+#include <limits.h>
+#include <ctype.h>
+
+struct thread_db
+{
+  /* Structure that identifies the child process for the
+     <proc_service.h> interface.  */
+  struct ps_prochandle proc_handle;
+
+  /* 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);
+};
+
+static 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);
@@ -130,7 +168,10 @@ thread_db_create_event (CORE_ADDR where)
   td_event_msg_t msg;
   td_err_e err;
   struct lwp_info *lwp;
-  struct process_info_private *proc = current_process()->private;
+  struct thread_db *thread_db = current_process ()->private->thread_db;
+
+  if (thread_db->td_ta_event_getmsg_p == NULL)
+    fatal ("unexpected thread_db->td_ta_event_getmsg_p == NULL");
 
   if (debug_threads)
     fprintf (stderr, "Thread creation event.\n");
@@ -139,7 +180,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 = thread_db->td_ta_event_getmsg_p (thread_db->thread_agent, &msg);
   if (err != TD_OK)
     fprintf (stderr, "thread getmsg err: %s\n",
             thread_db_err_str (err));
@@ -158,36 +199,25 @@ thread_db_create_event (CORE_ADDR where)
   return 0;
 }
 
-#if 0
-static int
-thread_db_death_event (CORE_ADDR where)
-{
-  if (debug_threads)
-    fprintf (stderr, "Thread death event.\n");
-
-  return 0;
-}
-#endif
-
 static int
 thread_db_enable_reporting ()
 {
   td_thr_events_t events;
   td_notify_t notify;
   td_err_e err;
-  struct process_info_private *proc = current_process()->private;
+  struct thread_db *thread_db = current_process ()->private->thread_db;
+
+  if (thread_db->td_ta_set_event_p == NULL
+      || thread_db->td_ta_event_addr_p == NULL
+      || thread_db->td_ta_event_getmsg_p == NULL)
+    /* This libthread_db is missing required support.  */
+    return 0;
 
   /* 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 = thread_db->td_ta_set_event_p (thread_db->thread_agent, &events);
   if (err != TD_OK)
     {
       warning ("Unable to set global thread event mask: %s",
@@ -196,7 +226,8 @@ thread_db_enable_reporting ()
     }
 
   /* Get address for thread creation breakpoint.  */
-  err = td_ta_event_addr (proc->thread_agent, TD_CREATE, &notify);
+  err = thread_db->td_ta_event_addr_p (thread_db->thread_agent, TD_CREATE,
+                                      &notify);
   if (err != TD_OK)
     {
       warning ("Unable to get location for thread creation breakpoint: %s",
@@ -206,22 +237,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, &notify);
-  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 +248,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 thread_db *thread_db = current_process ()->private->thread_db;
   int lwpid = ptid_get_lwp (ptid);
 
   inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid);
@@ -242,13 +257,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 = thread_db->td_ta_map_lwp2thr_p (thread_db->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 = thread_db->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 +280,7 @@ find_one_thread (ptid_t ptid)
 
   if (thread_db_use_events)
     {
-      err = td_thr_event_enable (&th, 1);
+      err = thread_db->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 +324,8 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
 
   if (thread_db_use_events)
     {
-      err = td_thr_event_enable (th_p, 1);
+      struct thread_db *thread_db = current_process ()->private->thread_db;
+      err = thread_db->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 +337,9 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
 {
   td_thrinfo_t ti;
   td_err_e err;
+  struct thread_db *thread_db = current_process ()->private->thread_db;
 
-  err = td_thr_get_info (th_p, &ti);
+  err = thread_db->td_thr_get_info_p (th_p, &ti);
   if (err != TD_OK)
     error ("Cannot get thread info: %s", thread_db_err_str (err));
 
@@ -341,7 +357,7 @@ thread_db_find_new_threads (void)
 {
   td_err_e err;
   ptid_t ptid = ((struct inferior_list_entry *) current_inferior)->id;
-  struct process_info_private *proc = current_process()->private;
+  struct thread_db *thread_db = current_process ()->private->thread_db;
 
   /* This function is only called when we first initialize thread_db.
      First locate the initial thread.  If it is not ready for
@@ -350,10 +366,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 = thread_db->td_ta_thr_iter_p (thread_db->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 +382,11 @@ thread_db_find_new_threads (void)
 static void
 thread_db_look_up_symbols (void)
 {
-  const char **sym_list = td_symbol_list ();
+  struct thread_db *thread_db = current_process ()->private->thread_db;
+  const char **sym_list;
   CORE_ADDR unused;
 
-  for (sym_list = td_symbol_list (); *sym_list; sym_list++)
+  for (sym_list = thread_db->td_symbol_list_p (); *sym_list; sym_list++)
     look_up_one_symbol (*sym_list, &unused);
 }
 
@@ -377,16 +394,23 @@ 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 thread_db *thread_db;
+
+  proc = get_thread_process (thread);
+  thread_db = proc->private->thread_db;
 
   /* If the thread layer is not (yet) initialized, fail.  */
-  if (!get_thread_process (thread)->all_symbols_looked_up)
+  if (!proc->all_symbols_looked_up)
     return TD_ERR;
 
+  if (thread_db->td_thr_tls_get_addr_p == NULL)
+    return -1;
+
   lwp = get_thread_lwp (thread);
   if (!lwp->thread_known)
     find_one_thread (lwp->head.id);
@@ -398,8 +422,9 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
   /* 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 = thread_db->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 +433,190 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
     }
   else
     return err;
-#else
-  return -1;
-#endif
+}
+
+static int
+try_thread_db_load_1 (void *handle)
+{
+  td_err_e err;
+  struct thread_db tdb;
+  struct process_info *proc = current_process ();
+
+  if (proc->private->thread_db != NULL)
+    fatal ("unexpected: proc->private->thread_db != NULL");
+
+  tdb.handle = handle;
+
+  /* Initialize pointers to the dynamic library functions we will use.
+     Essential functions first.  */
+
+#define CHK(required, a)                                       \
+  do                                                           \
+    {                                                          \
+      if ((a) == NULL)                                         \
+       {                                                       \
+         if (debug_threads)                                    \
+           fprintf (stderr, "dlsym: %s\n", dlerror ());        \
+         if (required)                                         \
+           return 0;                                           \
+       }                                                       \
+    }                                                          \
+  while (0)
+
+  CHK (1, tdb.td_ta_new_p = dlsym (handle, "td_ta_new"));
+
+  /* Attempt to open a connection to the thread library.  */
+  err = tdb.td_ta_new_p (&tdb.proc_handle, &tdb.thread_agent);
+  if (err != TD_OK)
+    {
+      if (debug_threads)
+       fprintf (stderr, "td_ta_new(): %s\n", thread_db_err_str (err));
+      return 0;
+    }
+
+  CHK (1, tdb.td_ta_map_lwp2thr_p = dlsym (handle, "td_ta_map_lwp2thr"));
+  CHK (1, tdb.td_thr_get_info_p = dlsym (handle, "td_thr_get_info"));
+  CHK (1, tdb.td_ta_thr_iter_p = dlsym (handle, "td_ta_thr_iter"));
+  CHK (1, tdb.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,
+       tdb.td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"));
+
+  /* These are not essential.  */
+  CHK (0, tdb.td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr"));
+  CHK (0, tdb.td_ta_set_event_p = dlsym (handle, "td_ta_set_event"));
+  CHK (0, tdb.td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"));
+  CHK (0, tdb.td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"));
+
+#undef CHK
+
+  proc->private->thread_db = xmalloc (sizeof (tdb));
+  memcpy (proc->private->thread_db, &tdb, sizeof (tdb));
+
+  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 (void)
+{
+  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,27 +631,68 @@ 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;
+       {
+         /* Keep trying; maybe event reporting will work later.  */
+         thread_db_free (proc);
+         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;
+}
+
+/* Disconnect from libthread_db and free resources.  */
+
+void
+thread_db_free (struct process_info *proc)
+{
+  struct thread_db *thread_db = proc->private->thread_db;
+  if (thread_db)
+    {
+      td_err_e (*td_ta_delete_p) (td_thragent_t *);
+
+      td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete");
+      if (td_ta_delete_p != NULL)
+       (*td_ta_delete_p) (thread_db->thread_agent);
+
+      dlclose (thread_db->handle);
+      free (thread_db);
+      proc->private->thread_db = NULL;
+    }
+}
+
+/* Handle "set libthread-db-search-path" monitor command and return 1.
+   For any other command, return 0.  */
+
+int
+thread_db_handle_monitor_command (char *mon)
+{
+  if (strncmp (mon, "set libthread-db-search-path ", 29) == 0)
+    {
+      const char *cp = mon + 29;
+
+      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");
+      return 1;
     }
 
+  /* Tell server.c to perform default processing.  */
   return 0;
 }