Re: [patch] Allow gdbserver to dynamically lookup libthread_db.so.1

Paul Pluzhnikov
2009-10-07T00:27:17Z

[-- Attachment #1: Type: text/plain, Size: 2000 bytes --]

On Tue, Oct 6, 2009 at 4:44 PM, Pedro Alves <pedro@codesourcery.com> wrote:

>> Perhaps a better fix is to skip this check altogether, and always use
>> dlopen on Linux?
>
> I guess your patch's already doing that.

It did that IFF it could find some libthread_db.

Now it will just always use libdl. If libthread_db usable by the inferior
is found at runtime, it will be used; if not, tough luck.

> Could you tweak the interface a bit, to return a boolean indicating
> if the command was handled or not?

Good call. Patch attached.

> Might want to
> force a whitespace after "set libthread-db-search-path", so that
> "set libthread-db-search-pathfoofoo" isn't accepted while at it.

Done and re-tested.

Thanks,
--
Paul Pluzhnikov

doc/
2009-10-06 Paul Pluzhnikov <ppluzhnikov@google.com>

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

gdbserver/
2009-10-06 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): 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.

[-- Attachment #2: gdb-gdbserver-threaddb-search-20091006-2.txt --]
[-- Type: text/plain, Size: 29584 bytes --]

Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.631
diff -u -p -u -r1.631 gdb.texinfo
--- doc/gdb.texinfo 6 Oct 2009 18:02:36 -0000 1.631
+++ doc/gdb.texinfo 7 Oct 2009 00:09:37 -0000
@@ -14903,6 +14903,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 7 Oct 2009 00:09:37 -0000
@@ -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: gdbserver/configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v
retrieving revision 1.26
diff -u -p -u -r1.26 configure.ac
--- gdbserver/configure.ac 6 Jul 2009 18:31:19 -0000 1.26
+++ gdbserver/configure.ac 7 Oct 2009 00:09:37 -0000
@@ -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";
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: 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 7 Oct 2009 00:09:37 -0000
@@ -42,6 +42,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include <sys/vfs.h>
+#include <dlfcn.h>

#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
@@ -118,6 +119,11 @@ int stopping_threads;
/* FIXME make into a target method? */
int using_threads = 1;

+#ifdef USE_THREAD_DB
+/* From thread-db.c */
+extern int thread_db_handle_monitor_command (char *);
+#endif
+
/* This flag is true iff we've just created or attached to our first
inferior but it has not stopped yet. As soon as it does, we need
to call the low target's arch_setup callback. Doing this only on
@@ -261,8 +267,11 @@ 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;
+
+ thread_db_free (process);
+ free (priv->arch_private);
+ free (priv);
remove_process (process);
}

@@ -1122,7 +1131,7 @@ linux_wait_for_event_1 (ptid_t ptid, int
&& !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 +2651,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 +3179,12 @@ static struct target_ops linux_target_op
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: 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 7 Oct 2009 00:09:37 -0000
@@ -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;
@@ -205,6 +199,7 @@ int elf_64_file_p (const char *file);
void linux_attach_lwp (unsigned long pid);

int thread_db_init (int use_events);
+void thread_db_free (struct process_info *);
int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
CORE_ADDR load_module, CORE_ADDR *address);

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 7 Oct 2009 00:09:37 -0000
@@ -660,6 +660,53 @@ handle_search_memory (char *own_buf, int
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_

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: gdbserver/target.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/target.h,v
retrieving revision 1.39
diff -u -p -u -r1.39 target.h
--- gdbserver/target.h 25 Jun 2009 22:13:53 -0000 1.39
+++ gdbserver/target.h 7 Oct 2009 00:09:37 -0000
@@ -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: 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 7 Oct 2009 00:09:37 -0000
@@ -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,11 @@ 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)
+ /* 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 +181,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 +200,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 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 = 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 +227,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 +238,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 +249,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 +258,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 +281,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 +325,8 @@ maybe_attach_thread (const td_thrhandle_

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 +338,9 @@ find_new_threads_callback (const td_thrh
{
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 +358,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 +367,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 +383,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 +395,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 +423,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 = 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 +434,195 @@ 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 thread_db tdb;
+ struct process_info *proc = current_process ();
+
+ if (proc->private->thread_db != NULL)
+ {
+ fprintf (stderr, "Internal error: thread_db != NULL in %s:%d\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ 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 = malloc (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 +637,64 @@ 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;
+}
+
+/* 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;
}