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

Paul Pluzhnikov
2009-10-08T19:36:15Z

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

On Thu, Oct 8, 2009 at 11:06 AM, Pedro Alves <pedro@codesourcery.com> wrote:

> '* configure: Regenerate.' or so is missing. Please always
> add that to the changelog even if not posting the regenerated
> files.

Ok.

I believe attached patch fixes all your comments.

> I notice that this return 0 here meant that
> linux-low.c:linux_look_up_symbols still retry thread_db_init,
> and that after your patch it won't. I'm inclined to think this
> was a buglet and that following thread_db_init retries
> wouldn't work anyway. Do you agree, or is this something
> that we still need to keep addressing?

Thanks for noticing this. Looking closer I discovered a buglet in
thread_db_enable_reporting -- it shouldn't return TD_ERR (== 1) here:

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;

Fixed.

I think it's theoretically possible for thread_db_enable_reporting to fail
early in the inferior lifetime, and start working later; so I fixed this
part and linux_look_up_symbols will now keep retrying.

Re-tested on Linux/x86_64.

Thanks,
--
Paul Pluzhnikov

doc/
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.

[-- Attachment #2: gdb-gdbserver-threaddb-search-20091008.txt --]
[-- Type: text/plain, Size: 34020 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 8 Oct 2009 19:14:25 -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 8 Oct 2009 19:14:25 -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 8 Oct 2009 19:14:25 -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 8 Oct 2009 19:14:25 -0000
@@ -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
&& !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_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 8 Oct 2009 19:14:25 -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;
@@ -203,9 +197,11 @@ char *linux_child_pid_to_exec_file (int
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: 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 8 Oct 2009 19:14:25 -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 8 Oct 2009 19:14:25 -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 8 Oct 2009 19:14:25 -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,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_

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_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 +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
/* 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
}
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;
}
Index: gdbserver/config.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/config.in,v
retrieving revision 1.23
diff -u -p -u -r1.23 config.in
--- gdbserver/config.in 22 Aug 2009 16:56:42 -0000 1.23
+++ gdbserver/config.in 8 Oct 2009 19:14:25 -0000
@@ -130,9 +130,6 @@
/* 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: gdbserver/configure
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure,v
retrieving revision 1.40
diff -u -p -u -r1.40 configure
--- gdbserver/configure 22 Aug 2009 16:56:42 -0000 1.40
+++ gdbserver/configure 8 Oct 2009 19:14:25 -0000
@@ -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_obj
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" >&
$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