+ /* The thread library was detected. Activate the thread_db target
+ if this is the first process using it. */
+ if (thread_db_list->next == NULL)
+ push_target (&thread_db_ops);
+
+ /* Enable event reporting, but not when debugging a core file. */
+ if (target_has_execution)
+ enable_thread_event_reporting ();
+
+ /* There appears to be a bug in glibc-2.3.6: calls to td_thr_get_info fail
+ with TD_ERR for statically linked executables if td_thr_get_info is
+ called before glibc has initialized itself. Silently ignore such
+ errors, and let gdb enumerate threads again later. */
+ thread_db_find_new_threads_silently (inferior_ptid);
+
+ return 1;
+}
+
+/* Attempt to use LIBRARY as libthread_db. LIBRARY could be absolute,
+ relative, or just LIBTHREAD_DB. */
+
+static int
+try_thread_db_load (const char *library)
+{
+ void *handle;
+ struct thread_db_info *info;
+
+ if (libthread_db_debug)
+ printf_unfiltered (_("Trying host libthread_db library: %s.\n"),
+ library);
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (libthread_db_debug)
+ printf_unfiltered (_("dlopen failed: %s.\n"), dlerror ());
+ return 0;
+ }
+
+ if (libthread_db_debug && 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)
+ printf_unfiltered (_("Host %s resolved to: %s.\n"),
+ library, libpath);
+ }
+ }
+
+ info = add_thread_db_info (handle);
+
+ /* Do not save system library name, that one is always trusted. */
+ if (strchr (library, '/') != NULL)
+ info->filename = gdb_realpath (library);
+
+ if (try_thread_db_load_1 (info))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ delete_thread_db_info (GET_PID (inferior_ptid));
+ return 0;
+}
+
+/* Subroutine of try_thread_db_load_from_pdir to simplify it.
+ Try loading libthread_db from the same directory as OBJ.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_pdir_1 (struct objfile *obj)
+{
+ struct cleanup *cleanup;
+ char *path, *cp;
+ int result;
+
+ if (obj->name[0] != '/')
+ {
+ warning (_("Expected absolute pathname for libpthread in the"
+ " inferior, but got %s."), obj->name);
+ return 0;
+ }
+
+ path = xmalloc (strlen (obj->name) + 1 + strlen (LIBTHREAD_DB_SO) + 1);
+ cleanup = make_cleanup (xfree, path);
+
+ strcpy (path, obj->name);
+ cp = strrchr (path, '/');
+ /* This should at minimum hit the first character. */
+ gdb_assert (cp != NULL);
+ strcpy (cp + 1, LIBTHREAD_DB_SO);
+
+ if (!file_is_auto_load_safe (path, _("auto-load: Loading libthread-db "
+ "library \"%s\" from $pdir.\n"),
+ path))
+ result = 0;
+ else
+ result = try_thread_db_load (path);
+
+ do_cleanups (cleanup);
+ return result;
+}
+
+/* Handle $pdir in libthread-db-search-path.
+ Look for libthread_db in the directory of libpthread.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_pdir (void)
+{
+ struct objfile *obj;
+
+ if (!auto_load_thread_db)
+ return 0;
+
+ ALL_OBJFILES (obj)
+ if (libpthread_name_p (obj->name))
+ {
+ if (try_thread_db_load_from_pdir_1 (obj))
+ return 1;
+
+ /* We may have found the separate-debug-info version of
+ libpthread, and it may live in a directory without a matching
+ libthread_db. */
+ if (obj->separate_debug_objfile_backlink != NULL)
+ return try_thread_db_load_from_pdir_1 (obj->separate_debug_objfile_backlink);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Handle $sdir in libthread-db-search-path.
+ Look for libthread_db in the system dirs, or wherever a plain
+ dlopen(file_without_path) will look.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_sdir (void)
+{
+ return try_thread_db_load (LIBTHREAD_DB_SO);
+}
+
+/* Try to load libthread_db from directory DIR of length DIR_LEN.
+ The result is true for success. */
+
+static int
+try_thread_db_load_from_dir (const char *dir, size_t dir_len)
+{
+ struct cleanup *cleanup;
+ char *path;
+ int result;
+
+ if (!auto_load_thread_db)
+ return 0;
+
+ path = xmalloc (dir_len + 1 + strlen (LIBTHREAD_DB_SO) + 1);
+ cleanup = make_cleanup (xfree, path);
+
+ memcpy (path, dir, dir_len);
+ path[dir_len] = '/';
+ strcpy (path + dir_len + 1, LIBTHREAD_DB_SO);
+
+ if (!file_is_auto_load_safe (path, _("auto-load: Loading libthread-db "
+ "library \"%s\" from explicit "
+ "directory.\n"),
+ path))
+ result = 0;
+ else
+ result = try_thread_db_load (path);
+
+ do_cleanups (cleanup);
+ return result;
+}
+
+/* Search libthread_db_search_path for libthread_db which "agrees"
+ to work on current inferior.
+ The result is true for success. */
+
+static int
+thread_db_load_search (void)
+{
+ const char *search_path = libthread_db_search_path;
+ int rc = 0;
+
+ while (*search_path)
+ {
+ const char *end = strchr (search_path, ':');
+ const char *this_dir = search_path;
+ size_t this_dir_len;
+
+ if (end)
+ {
+ this_dir_len = end - search_path;
+ search_path += this_dir_len + 1;
+ }
+ else
+ {
+ this_dir_len = strlen (this_dir);
+ search_path += this_dir_len;
+ }
+
+ if (this_dir_len == sizeof ("$pdir") - 1
+ && strncmp (this_dir, "$pdir", this_dir_len) == 0)
+ {
+ if (try_thread_db_load_from_pdir ())
+ {
+ rc = 1;
+ break;
+ }
+ }
+ else if (this_dir_len == sizeof ("$sdir") - 1
+ && strncmp (this_dir, "$sdir", this_dir_len) == 0)
+ {
+ if (try_thread_db_load_from_sdir ())
+ {
+ rc = 1;
+ break;
+ }
+ }
+ else
+ {
+ if (try_thread_db_load_from_dir (this_dir, this_dir_len))
+ {
+ rc = 1;
+ break;
+ }
+ }
+ }
+
+ if (libthread_db_debug)
+ printf_unfiltered (_("thread_db_load_search returning %d\n"), rc);
+ return rc;
+}
+
+/* Return non-zero if the inferior has a libpthread. */
+
+static int
+has_libpthread (void)
+{
+ struct objfile *obj;
+
+ ALL_OBJFILES (obj)
+ if (libpthread_name_p (obj->name))
+ return 1;
+
+ return 0;
+}
+
+/* Attempt to load and initialize libthread_db.
+ Return 1 on success. */
+
+static int
+thread_db_load (void)
+{
+ struct thread_db_info *info;
+
+ info = get_thread_db_info (GET_PID (inferior_ptid));
+
+ if (info != NULL)
+ return 1;
+
+ /* Don't attempt to use thread_db on executables not running
+ yet. */
+ if (!target_has_registers)
+ return 0;
+
+ /* Don't attempt to use thread_db for remote targets. */
+ if (!(target_can_run (¤t_target) || core_bfd))
+ return 0;
+
+ if (thread_db_load_search ())
+ return 1;
+
+ /* We couldn't find a libthread_db.
+ If the inferior has a libpthread warn the user. */
+ if (has_libpthread ())
+ {
+ warning (_("Unable to find libthread_db matching inferior's thread"
+ " library, thread debugging will not be available."));
+ return 0;
+ }
+
+ /* Either this executable isn't using libpthread at all, or it is
+ statically linked. Since we can't easily distinguish these two cases,
+ no warning is issued. */
+ return 0;
+}
+
+static void
+disable_thread_event_reporting (struct thread_db_info *info)
+{
+ if (info->td_ta_clear_event_p != NULL)
+ {
+ td_thr_events_t events;
+
+ /* Set the process wide mask saying we aren't interested in any
+ events anymore. */
+ td_event_fillset (&events);
+ info->td_ta_clear_event_p (info->thread_agent, &events);
+ }
+
+ info->td_create_bp_addr = 0;
+ info->td_death_bp_addr = 0;
+}
+
+static void
+check_thread_signals (void)
+{
+ if (!thread_signals)
+ {
+ sigset_t mask;
+ int i;
+
+ lin_thread_get_thread_signals (&mask);
+ sigemptyset (&thread_stop_set);
+ sigemptyset (&thread_print_set);
+
+ for (i = 1; i < NSIG; i++)
+ {
+ if (sigismember (&mask, i))
+ {
+ if (signal_stop_update (target_signal_from_host (i), 0))
+ sigaddset (&thread_stop_set, i);
+ if (signal_print_update (target_signal_from_host (i), 0))
+ sigaddset (&thread_print_set, i);
+ thread_signals = 1;
+ }
+ }
+ }
+}
+
+/* Check whether thread_db is usable. This function is called when
+ an inferior is created (or otherwise acquired, e.g. attached to)
+ and when new shared libraries are loaded into a running process. */
+
+void
+check_for_thread_db (void)
+{
+ /* Do nothing if we couldn't load libthread_db.so.1. */
+ if (!thread_db_load ())
+ return;
+}
+
+/* This function is called via the new_objfile observer. */
+
+static void
+thread_db_new_objfile (struct objfile *objfile)
+{
+ /* This observer must always be called with inferior_ptid set
+ correctly. */
+
+ if (objfile != NULL
+ /* Only check for thread_db if we loaded libpthread,
+ or if this is the main symbol file.
+ We need to check OBJF_MAINLINE to handle the case of debugging
+ a statically linked executable AND the symbol file is specified AFTER
+ the exec file is loaded (e.g., gdb -c core ; file foo).
+ For dynamically linked executables, libpthread can be near the end
+ of the list of shared libraries to load, and in an app of several
+ thousand shared libraries, this can otherwise be painful. */
+ && ((objfile->flags & OBJF_MAINLINE) != 0
+ || libpthread_name_p (objfile->name)))
+ check_for_thread_db ();
+}
+
+/* This function is called via the inferior_created observer.
+ This handles the case of debugging statically linked executables. */
+
+static void
+thread_db_inferior_created (struct target_ops *target, int from_tty)
+{
+ check_for_thread_db ();