Support loading two libthread_db DSOs. In this case, the LinuxThreads and NPTL ones. --- gdb/linux-thread-db.c | 299 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 184 insertions(+), 115 deletions(-) Index: gdb-6.6.dfsg/gdb/linux-thread-db.c =================================================================== --- gdb-6.6.dfsg.orig/gdb/linux-thread-db.c 2007-01-27 20:49:03.000000000 -0500 +++ gdb-6.6.dfsg/gdb/linux-thread-db.c 2007-01-27 20:50:19.000000000 -0500 @@ -82,45 +82,63 @@ static td_thragent_t *thread_agent; /* Pointers to the libthread_db functions. */ -static td_err_e (*td_init_p) (void); +struct thread_db_pointers +{ + const char *filename; + + td_err_e (*td_init_p) (void); -static td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, - td_thragent_t **ta); -static td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt, - td_thrhandle_t *__th); -static td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, - lwpid_t lwpid, td_thrhandle_t *th); -static td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta, - td_thr_iter_f *callback, void *cbdata_p, - td_thr_state_e state, int ti_pri, - sigset_t *ti_sigmask_p, - unsigned int ti_user_flags); -static td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta, - td_event_e event, td_notify_t *ptr); -static td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta, - td_thr_events_t *event); -static td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta, - td_event_msg_t *msg); - -static td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th); -static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th, - td_thrinfo_t *infop); -static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, - int event); - -static td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, - void *map_address, - size_t offset, void **address); + td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, + td_thragent_t **ta); + td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt, + td_thrhandle_t *__th); + td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, + lwpid_t lwpid, td_thrhandle_t *th); + + 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_ta_event_addr_p) (const td_thragent_t *ta, + td_event_e event, td_notify_t *ptr); + td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta, + td_thr_events_t *event); + td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta, + td_event_msg_t *msg); + + td_err_e (*td_thr_validate_p) (const 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_getfpregs_p) (const td_thrhandle_t *th, + gdb_prfpregset_t *regset); + td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th, + prgregset_t gregs); + td_err_e (*td_thr_setfpregs_p) (const td_thrhandle_t *th, + const gdb_prfpregset_t *fpregs); + td_err_e (*td_thr_setgregs_p) (const td_thrhandle_t *th, + prgregset_t gregs); + td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, + int event); + + td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, + void *map_address, + size_t offset, void **address); + + struct thread_db_pointers *next; +}; /* Location of the thread creation event breakpoint. The code at this location in the child process will be called by the pthread library whenever a new thread is created. By setting a special breakpoint at this location, GDB can detect when a new thread is created. We obtain this location via the td_ta_event_addr call. */ -static CORE_ADDR td_create_bp_addr; +CORE_ADDR td_create_bp_addr; /* Location of the thread death event breakpoint. */ -static CORE_ADDR td_death_bp_addr; +CORE_ADDR td_death_bp_addr; + +static struct thread_db_pointers *current_pointers, *all_pointers; /* Prototypes for local functions. */ static void thread_db_find_new_threads (void); @@ -231,7 +249,7 @@ thread_get_info_callback (const td_thrha struct thread_info *thread_info; ptid_t thread_ptid; - err = td_thr_get_info_p (thp, &ti); + err = current_pointers->td_thr_get_info_p (thp, &ti); if (err != TD_OK) error (_("thread_get_info_callback: cannot get thread info: %s"), thread_db_err_str (err)); @@ -285,8 +303,9 @@ thread_db_map_id2thr (struct thread_info if (thread_info->private->th_valid) return; - err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (thread_info->ptid), - &thread_info->private->th); + err = current_pointers->td_ta_map_id2thr_p (thread_agent, + GET_THREAD (thread_info->ptid), + &thread_info->private->th); if (err != TD_OK) { if (fatal) @@ -313,7 +332,8 @@ thread_from_lwp (ptid_t ptid) gdb_assert (is_lwp (ptid)); - err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th); + err = current_pointers->td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), + &th); if (err != TD_OK) error (_("Cannot find user-level thread for LWP %ld: %s"), GET_LWP (ptid), thread_db_err_str (err)); @@ -360,69 +380,86 @@ verbose_dlsym (void *handle, const char return sym; } -static int -thread_db_load (void) +static struct thread_db_pointers * +thread_db_load (const char *name) { + struct thread_db_pointers *ptrs; + Dl_info info; void *handle; td_err_e err; - handle = dlopen (LIBTHREAD_DB_SO, RTLD_NOW); + ptrs = xcalloc (1, sizeof (struct thread_db_pointers)); + + handle = dlopen (name, RTLD_NOW); if (handle == NULL) { - fprintf_filtered (gdb_stderr, "\n\ndlopen failed on '%s' - %s\n", - LIBTHREAD_DB_SO, dlerror ()); - fprintf_filtered (gdb_stderr, - "GDB will not be able to debug pthreads.\n\n"); + if (all_pointers == NULL) + { + fprintf_filtered (gdb_stderr, "\n\ndlopen failed on '%s' - %s\n", + name, dlerror ()); + fprintf_filtered (gdb_stderr, + "GDB will not be able to debug pthreads.\n\n"); + } return 0; } /* Initialize pointers to the dynamic library functions we will use. Essential functions first. */ - td_init_p = verbose_dlsym (handle, "td_init"); - if (td_init_p == NULL) + ptrs->td_init_p = verbose_dlsym (handle, "td_init"); + if (ptrs->td_init_p == NULL) return 0; - td_ta_new_p = verbose_dlsym (handle, "td_ta_new"); - if (td_ta_new_p == NULL) + ptrs->td_ta_new_p = verbose_dlsym (handle, "td_ta_new"); + if (ptrs->td_ta_new_p == NULL) return 0; - td_ta_map_id2thr_p = verbose_dlsym (handle, "td_ta_map_id2thr"); - if (td_ta_map_id2thr_p == NULL) + ptrs->td_ta_map_id2thr_p = verbose_dlsym (handle, "td_ta_map_id2thr"); + if (ptrs->td_ta_map_id2thr_p == NULL) return 0; - td_ta_map_lwp2thr_p = verbose_dlsym (handle, "td_ta_map_lwp2thr"); - if (td_ta_map_lwp2thr_p == NULL) + ptrs->td_ta_map_lwp2thr_p = verbose_dlsym (handle, "td_ta_map_lwp2thr"); + if (ptrs->td_ta_map_lwp2thr_p == NULL) return 0; - td_ta_thr_iter_p = verbose_dlsym (handle, "td_ta_thr_iter"); - if (td_ta_thr_iter_p == NULL) + ptrs->td_ta_thr_iter_p = verbose_dlsym (handle, "td_ta_thr_iter"); + if (ptrs->td_ta_thr_iter_p == NULL) return 0; - td_thr_validate_p = verbose_dlsym (handle, "td_thr_validate"); - if (td_thr_validate_p == NULL) + ptrs->td_thr_validate_p = verbose_dlsym (handle, "td_thr_validate"); + if (ptrs->td_thr_validate_p == NULL) return 0; - td_thr_get_info_p = verbose_dlsym (handle, "td_thr_get_info"); - if (td_thr_get_info_p == NULL) + ptrs->td_thr_get_info_p = verbose_dlsym (handle, "td_thr_get_info"); + if (ptrs->td_thr_get_info_p == NULL) return 0; /* Initialize the library. */ - err = td_init_p (); + err = ptrs->td_init_p (); if (err != TD_OK) { warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err)); + xfree (ptrs); return 0; } /* These are not essential. */ - td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr"); - td_ta_set_event_p = dlsym (handle, "td_ta_set_event"); - td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"); - td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"); - td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"); + ptrs->td_ta_event_addr_p = dlsym (handle, "td_ta_event_addr"); + ptrs->td_ta_set_event_p = dlsym (handle, "td_ta_set_event"); + ptrs->td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"); + ptrs->td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"); + ptrs->td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"); + + if (dladdr (ptrs->td_ta_new_p, &info) != 0) + ptrs->filename = info.dli_fname; + + /* Try dlinfo? */ + + if (ptrs->filename == NULL) + /* Paranoid - don't let a NULL path slip through. */ + ptrs->filename = name; - return 1; + return ptrs; } static td_err_e @@ -432,7 +469,7 @@ enable_thread_event (td_thragent_t *thre td_err_e err; /* Get the breakpoint address for thread EVENT. */ - err = td_ta_event_addr_p (thread_agent, event, ¬ify); + err = current_pointers->td_ta_event_addr_p (thread_agent, event, ¬ify); if (err != TD_OK) return err; @@ -463,8 +500,10 @@ enable_thread_event_reporting (void) /* We cannot use the thread event reporting facility if these functions aren't available. */ - if (td_ta_event_addr_p == NULL || td_ta_set_event_p == NULL - || td_ta_event_getmsg_p == NULL || td_thr_event_enable_p == NULL) + if (current_pointers->td_ta_event_addr_p == NULL + || current_pointers->td_ta_set_event_p == NULL + || current_pointers->td_ta_event_getmsg_p == NULL + || current_pointers->td_thr_event_enable_p == NULL) return; /* Set the process wide mask saying which events we're interested in. */ @@ -481,7 +520,7 @@ enable_thread_event_reporting (void) #endif td_event_addset (&events, TD_DEATH); - err = td_ta_set_event_p (thread_agent, &events); + err = current_pointers->td_ta_set_event_p (thread_agent, &events); if (err != TD_OK) { warning (_("Unable to set global thread event mask: %s"), @@ -521,7 +560,7 @@ disable_thread_event_reporting (void) /* Set the process wide mask saying we aren't interested in any events anymore. */ td_event_emptyset (&events); - td_ta_set_event_p (thread_agent, &events); + current_pointers->td_ta_set_event_p (thread_agent, &events); /* Delete thread event breakpoints, if any. */ remove_thread_event_breakpoints (); @@ -564,28 +603,20 @@ check_thread_signals (void) void check_for_thread_db (void) { - td_err_e err; static int already_loaded; + /* Do nothing if we couldn't load libthread_db.so.1. */ + if (all_pointers == NULL) + return; + /* First time through, report that libthread_db was successfuly loaded. Can't print this in in thread_db_load as, at that stage, the interpreter and it's console haven't started. */ if (!already_loaded) { - Dl_info info; - const char *library = NULL; - if (dladdr ((*td_ta_new_p), &info) != 0) - library = info.dli_fname; - - /* Try dlinfo? */ - - if (library == NULL) - /* Paranoid - don't let a NULL path slip through. */ - library = LIBTHREAD_DB_SO; - printf_unfiltered (_("Using host libthread_db library \"%s\".\n"), - library); + all_pointers->filename); already_loaded = 1; } @@ -607,28 +638,34 @@ check_for_thread_db (void) proc_handle.pid = GET_PID (inferior_ptid); /* Now attempt to open a connection to the thread library. */ - err = td_ta_new_p (&proc_handle, &thread_agent); - switch (err) + for (current_pointers = all_pointers; + current_pointers != NULL; + current_pointers = current_pointers->next) { - case TD_NOLIBTHREAD: - /* No thread library was detected. */ - break; - - case TD_OK: - printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n")); + td_err_e err; + err = current_pointers->td_ta_new_p (&proc_handle, &thread_agent); + switch (err) + { + case TD_NOLIBTHREAD: + /* No thread library was detected. */ + break; - /* The thread library was detected. Activate the thread_db target. */ - push_target (&thread_db_ops); - using_thread_db = 1; + case TD_OK: + printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n")); - enable_thread_event_reporting (); - thread_db_find_new_threads (); - break; + /* The thread library was detected. Activate the thread_db target. */ + push_target (&thread_db_ops); + using_thread_db = 1; + + enable_thread_event_reporting (); + thread_db_find_new_threads (); + return; - default: - warning (_("Cannot initialize thread debugging library: %s"), - thread_db_err_str (err)); - break; + default: + warning (_("Cannot initialize thread debugging library: %s"), + thread_db_err_str (err)); + break; + } } } @@ -693,7 +730,7 @@ attach_thread (ptid_t ptid, const td_thr #endif /* Enable thread event reporting for this thread. */ - err = td_thr_event_enable_p (th_p, 1); + err = current_pointers->td_thr_event_enable_p (th_p, 1); if (err != TD_OK) error (_("Cannot enable thread event reporting for %s: %s"), target_pid_to_str (ptid), thread_db_err_str (err)); @@ -819,7 +856,7 @@ check_event (ptid_t ptid) do { - err = td_ta_event_getmsg_p (thread_agent, &msg); + err = current_pointers->td_ta_event_getmsg_p (thread_agent, &msg); if (err != TD_OK) { if (err == TD_NOMSG) @@ -829,7 +866,7 @@ check_event (ptid_t ptid) thread_db_err_str (err)); } - err = td_thr_get_info_p (msg.th_p, &ti); + err = current_pointers->td_thr_get_info_p (msg.th_p, &ti); if (err != TD_OK) error (_("Cannot get thread info: %s"), thread_db_err_str (err)); @@ -992,7 +1029,7 @@ find_new_threads_callback (const td_thrh td_err_e err; ptid_t ptid; - err = td_thr_get_info_p (th_p, &ti); + err = current_pointers->td_thr_get_info_p (th_p, &ti); if (err != TD_OK) error (_("find_new_threads_callback: cannot get thread info: %s"), thread_db_err_str (err)); @@ -1014,9 +1051,10 @@ thread_db_find_new_threads (void) td_err_e err; /* Iterate over all user-space threads to discover new threads. */ - err = td_ta_thr_iter_p (thread_agent, find_new_threads_callback, NULL, - TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + err = current_pointers->td_ta_thr_iter_p + (thread_agent, find_new_threads_callback, NULL, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); if (err != TD_OK) error (_("Cannot find new threads: %s"), thread_db_err_str (err)); } @@ -1073,7 +1111,7 @@ thread_db_get_thread_local_address (ptid struct thread_info *thread_info; /* glibc doesn't provide the needed interface. */ - if (!td_thr_tls_get_addr_p) + if (!current_pointers->td_thr_tls_get_addr_p) throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR, _("No TLS library support")); @@ -1085,9 +1123,8 @@ thread_db_get_thread_local_address (ptid thread_db_map_id2thr (thread_info, 1); /* Finally, get the address of the variable. */ - err = td_thr_tls_get_addr_p (&thread_info->private->th, - (void *)(size_t) lm, - offset, &address); + err = current_pointers->td_thr_tls_get_addr_p + (&thread_info->private->th, (void *)(size_t) lm, offset, &address); #ifdef THREAD_DB_HAS_TD_NOTALLOC /* The memory hasn't been allocated, yet. */ @@ -1147,14 +1184,46 @@ init_thread_db_ops (void) void _initialize_thread_db (void) { + struct thread_db_pointers *ptrs; + const char *p; + /* Only initialize the module if we can load libthread_db. */ - if (thread_db_load ()) + ptrs = thread_db_load (LIBTHREAD_DB_SO); + if (ptrs == NULL) + return; + + all_pointers = ptrs; + + /* Some GNU/Linux systems have more than one binary-compatible copy + of libthread_db. If we can find a second one, load that too. + The inferior may force the use of a different threading package + than we expect. Our guess for the location is somewhat hokey: + strip out anything between /lib (or /lib64) and LIBTHREAD_DB_SO. + If we loaded the NPTL libthread_db by default, this may find us + the LinuxThreads copy. */ + p = strrchr (ptrs->filename, '/'); + while (p != NULL && p > ptrs->filename) { - init_thread_db_ops (); - add_target (&thread_db_ops); + const char *component; - /* Add ourselves to objfile event chain. */ - target_new_objfile_chain = deprecated_target_new_objfile_hook; - deprecated_target_new_objfile_hook = thread_db_new_objfile; + component = memrchr (ptrs->filename, '/', p - ptrs->filename); + if (component != NULL && strncmp (component, "/lib", 4) == 0) + { + char *new_name = xmalloc (p - ptrs->filename + 2 + + strlen (LIBTHREAD_DB_SO)); + memcpy (new_name, ptrs->filename, p - ptrs->filename + 1); + strcpy (new_name + (p - ptrs->filename) + 1, LIBTHREAD_DB_SO); + ptrs->next = thread_db_load (new_name); + xfree (new_name); + break; + } + p = component; } + + init_thread_db_ops (); + add_target (&thread_db_ops); + + /* Add ourselves to objfile event chain. */ + target_new_objfile_chain = deprecated_target_new_objfile_hook; + deprecated_target_new_objfile_hook = thread_db_new_objfile; }