X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Flinux-thread-db.c;h=54d96e9b19f2045c9716bf3d2385f322cfc3827f;hb=b899eb3bb807be1094fde9a2f1c8628232bc0743;hp=920e15edf44a5400530811eeec3f6e7f2bb38682;hpb=b6a8c27bb8fb383be6f57724eb9aafa9f2f83aa5;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 920e15edf4..54d96e9b19 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -1,6 +1,6 @@ /* libthread_db assisted debugging support, generic parts. - Copyright (C) 1999-2018 Free Software Foundation, Inc. + Copyright (C) 1999-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -21,7 +21,7 @@ #include #include "gdb_proc_service.h" #include "nat/gdb_thread_db.h" -#include "gdb_vecs.h" +#include "gdbsupport/gdb_vecs.h" #include "bfd.h" #include "command.h" #include "gdbcmd.h" @@ -46,7 +46,9 @@ #include #include "nat/linux-namespaces.h" #include -#include "common/pathstuff.h" +#include "gdbsupport/pathstuff.h" +#include "valprint.h" +#include "cli/cli-style.h" /* GNU/Linux libthread_db support. @@ -84,17 +86,17 @@ static const target_info thread_db_target_info = { class thread_db_target final : public target_ops { public: - thread_db_target (); - const target_info &info () const override { return thread_db_target_info; } + strata stratum () const override { return thread_stratum; } + void detach (inferior *, int) override; ptid_t wait (ptid_t, struct target_waitstatus *, int) override; void resume (ptid_t, int, enum gdb_signal) override; void mourn_inferior () override; void update_thread_list () override; - const char *pid_to_str (ptid_t) override; + std::string pid_to_str (ptid_t) override; CORE_ADDR get_thread_local_address (ptid_t ptid, CORE_ADDR load_module_addr, CORE_ADDR offset) override; @@ -104,18 +106,18 @@ public: thread_info *thread_handle_to_thread_info (const gdb_byte *thread_handle, int handle_len, inferior *inf) override; + gdb::byte_vector thread_info_to_thread_handle (struct thread_info *) override; }; -thread_db_target::thread_db_target () -{ - this->to_stratum = thread_stratum; -} - static char *libthread_db_search_path; -/* Set to non-zero if thread_db auto-loading is enabled +/* Set to true if thread_db auto-loading is enabled by the "set auto-load libthread-db" command. */ -static int auto_load_thread_db = 1; +static bool auto_load_thread_db = true; + +/* Set to true if load-time libthread_db tests have been enabled + by the "maintenance set check-libthread-db" command. */ +static bool check_thread_db_on_load = false; /* "show" command for the auto_load_thread_db configuration variable. */ @@ -194,6 +196,7 @@ struct thread_db_info td_init_ftype *td_init_p; td_ta_new_ftype *td_ta_new_p; + td_ta_delete_ftype *td_ta_delete_p; td_ta_map_lwp2thr_ftype *td_ta_map_lwp2thr_p; td_ta_thr_iter_ftype *td_ta_thr_iter_p; td_thr_get_info_ftype *td_thr_get_info_p; @@ -205,8 +208,9 @@ struct thread_db_info bookkeeping. */ struct thread_db_info *thread_db_list; -static void thread_db_find_new_threads_1 (ptid_t ptid); -static void thread_db_find_new_threads_2 (ptid_t ptid, int until_no_new); +static void thread_db_find_new_threads_1 (thread_info *stopped); +static void thread_db_find_new_threads_2 (thread_info *stopped, + bool until_no_new); static void check_thread_signals (void); @@ -224,7 +228,7 @@ add_thread_db_info (void *handle) { struct thread_db_info *info = XCNEW (struct thread_db_info); - info->pid = ptid_get_pid (inferior_ptid); + info->pid = inferior_ptid.pid (); info->handle = handle; /* The workaround works by reading from /proc/pid/status, so it is @@ -253,6 +257,8 @@ get_thread_db_info (int pid) return NULL; } +static const char *thread_db_err_str (td_err_e err); + /* When PID has exited or has been detached, we no longer want to keep track of it as using libpthread. Call this function to discard thread_db related info related to PID. Note that this closes @@ -272,6 +278,16 @@ delete_thread_db_info (int pid) if (info == NULL) return; + if (info->thread_agent != NULL && info->td_ta_delete_p != NULL) + { + td_err_e err = info->td_ta_delete_p (info->thread_agent); + + if (err != TD_OK) + warning (_("Cannot deregister process %d from libthread_db: %s"), + pid, thread_db_err_str (err)); + info->thread_agent = NULL; + } + if (info->handle != NULL) dlclose (info->handle); @@ -371,10 +387,11 @@ thread_db_err_str (td_err_e err) } } -/* Fetch the user-level thread id of PTID. */ +/* Fetch the user-level thread id of PTID. STOPPED is a stopped + thread that we can use to access memory. */ static struct thread_info * -thread_from_lwp (ptid_t ptid) +thread_from_lwp (thread_info *stopped, ptid_t ptid) { td_thrhandle_t th; td_thrinfo_t ti; @@ -387,17 +404,17 @@ thread_from_lwp (ptid_t ptid) /* This ptid comes from linux-nat.c, which should always fill in the LWP. */ - gdb_assert (ptid_get_lwp (ptid) != 0); + gdb_assert (ptid.lwp () != 0); - info = get_thread_db_info (ptid_get_pid (ptid)); + info = get_thread_db_info (ptid.pid ()); /* Access an lwp we know is stopped. */ - info->proc_handle.ptid = ptid; - err = info->td_ta_map_lwp2thr_p (info->thread_agent, ptid_get_lwp (ptid), + info->proc_handle.thread = stopped; + err = info->td_ta_map_lwp2thr_p (info->thread_agent, ptid.lwp (), &th); if (err != TD_OK) error (_("Cannot find user-level thread for LWP %ld: %s"), - ptid_get_lwp (ptid), thread_db_err_str (err)); + ptid.lwp (), thread_db_err_str (err)); err = info->td_thr_get_info_p (&th, &ti); if (err != TD_OK) @@ -417,16 +434,18 @@ thread_db_notice_clone (ptid_t parent, ptid_t child) { struct thread_db_info *info; - info = get_thread_db_info (ptid_get_pid (child)); + info = get_thread_db_info (child.pid ()); if (info == NULL) return 0; - thread_from_lwp (child); + thread_info *stopped = find_thread_ptid (parent); + + thread_from_lwp (stopped, child); - /* If we do not know about the main thread yet, this would be a good - time to find it. */ - thread_from_lwp (parent); + /* If we do not know about the main thread's pthread info yet, this + would be a good time to find it. */ + thread_from_lwp (stopped, parent); return 1; } @@ -477,15 +496,15 @@ inferior_has_bug (const char *ver_symbol, int ver_major_min, int ver_minor_min) otherwise. */ static int -thread_db_find_new_threads_silently (ptid_t ptid) +thread_db_find_new_threads_silently (thread_info *stopped) { - TRY + try { - thread_db_find_new_threads_2 (ptid, 1); + thread_db_find_new_threads_2 (stopped, true); } - CATCH (except, RETURN_MASK_ERROR) + catch (const gdb_exception_error &except) { if (libthread_db_debug) exception_fprintf (gdb_stdlog, except, @@ -515,7 +534,6 @@ thread_db_find_new_threads_silently (ptid_t ptid) return 1; } } - END_CATCH return 0; } @@ -534,13 +552,256 @@ dladdr_to_soname (const void *addr) return NULL; } +/* State for check_thread_db_callback. */ + +struct check_thread_db_info +{ + /* The libthread_db under test. */ + struct thread_db_info *info; + + /* True if progress should be logged. */ + bool log_progress; + + /* True if the callback was called. */ + bool threads_seen; + + /* Name of last libthread_db function called. */ + const char *last_call; + + /* Value returned by last libthread_db call. */ + td_err_e last_result; +}; + +static struct check_thread_db_info *tdb_testinfo; + +/* Callback for check_thread_db. */ + +static int +check_thread_db_callback (const td_thrhandle_t *th, void *arg) +{ + gdb_assert (tdb_testinfo != NULL); + tdb_testinfo->threads_seen = true; + +#define LOG(fmt, args...) \ + do \ + { \ + if (tdb_testinfo->log_progress) \ + { \ + debug_printf (fmt, ## args); \ + gdb_flush (gdb_stdlog); \ + } \ + } \ + while (0) + +#define CHECK_1(expr, args...) \ + do \ + { \ + if (!(expr)) \ + { \ + LOG (" ... FAIL!\n"); \ + error (args); \ + } \ + } \ + while (0) + +#define CHECK(expr) \ + CHECK_1 (expr, "(%s) == false", #expr) + +#define CALL_UNCHECKED(func, args...) \ + do \ + { \ + tdb_testinfo->last_call = #func; \ + tdb_testinfo->last_result \ + = tdb_testinfo->info->func ## _p (args); \ + } \ + while (0) + +#define CHECK_CALL() \ + CHECK_1 (tdb_testinfo->last_result == TD_OK, \ + _("%s failed: %s"), \ + tdb_testinfo->last_call, \ + thread_db_err_str (tdb_testinfo->last_result)) \ + +#define CALL(func, args...) \ + do \ + { \ + CALL_UNCHECKED (func, args); \ + CHECK_CALL (); \ + } \ + while (0) + + LOG (" Got thread"); + + /* Check td_ta_thr_iter passed consistent arguments. */ + CHECK (th != NULL); + CHECK (arg == (void *) tdb_testinfo); + CHECK (th->th_ta_p == tdb_testinfo->info->thread_agent); + + LOG (" %s", core_addr_to_string_nz ((CORE_ADDR) th->th_unique)); + + /* Check td_thr_get_info. */ + td_thrinfo_t ti; + CALL (td_thr_get_info, th, &ti); + + LOG (" => %d", ti.ti_lid); + + CHECK (ti.ti_ta_p == th->th_ta_p); + CHECK (ti.ti_tid == (thread_t) th->th_unique); + + /* Check td_ta_map_lwp2thr. */ + td_thrhandle_t th2; + memset (&th2, 23, sizeof (td_thrhandle_t)); + CALL_UNCHECKED (td_ta_map_lwp2thr, th->th_ta_p, ti.ti_lid, &th2); + + if (tdb_testinfo->last_result == TD_ERR && !target_has_execution) + { + /* Some platforms require execution for td_ta_map_lwp2thr. */ + LOG (_("; can't map_lwp2thr")); + } + else + { + CHECK_CALL (); + + LOG (" => %s", core_addr_to_string_nz ((CORE_ADDR) th2.th_unique)); + + CHECK (memcmp (th, &th2, sizeof (td_thrhandle_t)) == 0); + } + + /* Attempt TLS access. Assuming errno is TLS, this calls + thread_db_get_thread_local_address, which in turn calls + td_thr_tls_get_addr for live inferiors or td_thr_tlsbase + for core files. This test is skipped if the thread has + not been recorded; proceeding in that case would result + in the test having the side-effect of noticing threads + which seems wrong. + + Note that in glibc's libthread_db td_thr_tls_get_addr is + a thin wrapper around td_thr_tlsbase; this check always + hits the bulk of the code. + + Note also that we don't actually check any libthread_db + calls are made, we just assume they were; future changes + to how GDB accesses TLS could result in this passing + without exercising the calls it's supposed to. */ + ptid_t ptid = ptid_t (tdb_testinfo->info->pid, ti.ti_lid, 0); + struct thread_info *thread_info = find_thread_ptid (ptid); + if (thread_info != NULL && thread_info->priv != NULL) + { + LOG ("; errno"); + + scoped_restore_current_thread restore_current_thread; + switch_to_thread (ptid); + + expression_up expr = parse_expression ("(int) errno"); + struct value *val = evaluate_expression (expr.get ()); + + if (tdb_testinfo->log_progress) + { + struct value_print_options opts; + + get_user_print_options (&opts); + LOG (" = "); + value_print (val, gdb_stdlog, &opts); + } + } + + LOG (" ... OK\n"); + +#undef LOG +#undef CHECK_1 +#undef CHECK +#undef CALL_UNCHECKED +#undef CHECK_CALL +#undef CALL + + return 0; +} + +/* Run integrity checks on the dlopen()ed libthread_db described by + INFO. Returns true on success, displays a warning and returns + false on failure. Logs progress messages to gdb_stdlog during + the test if LOG_PROGRESS is true. */ + +static bool +check_thread_db (struct thread_db_info *info, bool log_progress) +{ + bool test_passed = true; + + if (log_progress) + debug_printf (_("Running libthread_db integrity checks:\n")); + + /* GDB avoids using td_ta_thr_iter wherever possible (see comment + in try_thread_db_load_1 below) so in order to test it we may + have to locate it ourselves. */ + td_ta_thr_iter_ftype *td_ta_thr_iter_p = info->td_ta_thr_iter_p; + if (td_ta_thr_iter_p == NULL) + { + void *thr_iter = verbose_dlsym (info->handle, "td_ta_thr_iter"); + if (thr_iter == NULL) + return 0; + + td_ta_thr_iter_p = (td_ta_thr_iter_ftype *) thr_iter; + } + + /* Set up the test state we share with the callback. */ + gdb_assert (tdb_testinfo == NULL); + struct check_thread_db_info tdb_testinfo_buf; + tdb_testinfo = &tdb_testinfo_buf; + + memset (tdb_testinfo, 0, sizeof (struct check_thread_db_info)); + tdb_testinfo->info = info; + tdb_testinfo->log_progress = log_progress; + + /* td_ta_thr_iter shouldn't be used on running processes. Note that + it's possible the inferior will stop midway through modifying one + of its thread lists, in which case the check will spuriously + fail. */ + linux_stop_and_wait_all_lwps (); + + try + { + td_err_e err = td_ta_thr_iter_p (info->thread_agent, + check_thread_db_callback, + tdb_testinfo, + TD_THR_ANY_STATE, + TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, + TD_THR_ANY_USER_FLAGS); + + if (err != TD_OK) + error (_("td_ta_thr_iter failed: %s"), thread_db_err_str (err)); + + if (!tdb_testinfo->threads_seen) + error (_("no threads seen")); + } + catch (const gdb_exception_error &except) + { + if (warning_pre_print) + fputs_unfiltered (warning_pre_print, gdb_stderr); + + exception_fprintf (gdb_stderr, except, + _("libthread_db integrity checks failed: ")); + + test_passed = false; + } + + if (test_passed && log_progress) + debug_printf (_("libthread_db integrity checks passed.\n")); + + tdb_testinfo = NULL; + + linux_unstop_all_lwps (); + + return test_passed; +} + /* Attempt to initialize dlopen()ed libthread_db, described by INFO. - Return 1 on success. + Return true on success. Failure could happen if libthread_db does not have symbols we expect, or when it refuses to work with the current inferior (e.g. due to version mismatch between libthread_db and libpthread). */ -static int +static bool try_thread_db_load_1 (struct thread_db_info *info) { td_err_e err; @@ -558,7 +819,7 @@ try_thread_db_load_1 (struct thread_db_info *info) do \ { \ if ((a) == NULL) \ - return 0; \ + return false; \ } while (0) CHK (TDB_VERBOSE_DLSYM (info, td_init)); @@ -568,13 +829,13 @@ try_thread_db_load_1 (struct thread_db_info *info) { warning (_("Cannot initialize libthread_db: %s"), thread_db_err_str (err)); - return 0; + return false; } CHK (TDB_VERBOSE_DLSYM (info, td_ta_new)); /* Initialize the structure that identifies the child process. */ - info->proc_handle.ptid = inferior_ptid; + info->proc_handle.thread = inferior_thread (); /* Now attempt to open a connection to the thread library. */ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent); @@ -597,7 +858,7 @@ try_thread_db_load_1 (struct thread_db_info *info) default: warning (_("td_ta_new failed: %s"), thread_db_err_str (err)); } - return 0; + return false; } /* These are essential. */ @@ -607,6 +868,7 @@ try_thread_db_load_1 (struct thread_db_info *info) /* These are not essential. */ TDB_DLSYM (info, td_thr_tls_get_addr); TDB_DLSYM (info, td_thr_tlsbase); + TDB_DLSYM (info, td_ta_delete); /* It's best to avoid td_ta_thr_iter if possible. That walks data structures in the inferior's address space that may be corrupted, @@ -618,7 +880,7 @@ try_thread_db_load_1 (struct thread_db_info *info) td_ta_map_lwp2thr uses ps_get_thread_area, but we can't use that currently on core targets, as it uses ptrace directly. */ if (target_has_execution - && linux_proc_task_list_dir_exists (ptid_get_pid (inferior_ptid))) + && linux_proc_task_list_dir_exists (inferior_ptid.pid ())) info->td_ta_thr_iter_p = NULL; else CHK (TDB_VERBOSE_DLSYM (info, td_ta_thr_iter)); @@ -627,25 +889,33 @@ try_thread_db_load_1 (struct thread_db_info *info) #undef TDB_DLSYM #undef CHK + /* Run integrity checks if requested. */ + if (check_thread_db_on_load) + { + if (!check_thread_db (info, libthread_db_debug)) + return false; + } + if (info->td_ta_thr_iter_p == NULL) { struct lwp_info *lp; - int pid = ptid_get_pid (inferior_ptid); + int pid = inferior_ptid.pid (); + thread_info *curr_thread = inferior_thread (); linux_stop_and_wait_all_lwps (); ALL_LWPS (lp) - if (ptid_get_pid (lp->ptid) == pid) - thread_from_lwp (lp->ptid); + if (lp->ptid.pid () == pid) + thread_from_lwp (curr_thread, lp->ptid); linux_unstop_all_lwps (); } - else if (thread_db_find_new_threads_silently (inferior_ptid) != 0) + else if (thread_db_find_new_threads_silently (inferior_thread ()) != 0) { /* Even if libthread_db initializes, if the thread list is corrupted, we'd not manage to list any threads. Better reject this thread_db, and fall back to at least listing LWPs. */ - return 0; + return false; } printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n")); @@ -664,8 +934,9 @@ try_thread_db_load_1 (struct thread_db_info *info) enabled. User visible output should not depend on debug settings. */ file = *libthread_db_search_path != '\0' ? gdb_stdout : gdb_stdlog; - fprintf_unfiltered (file, _("Using host libthread_db library \"%s\".\n"), - library); + fprintf_unfiltered (file, + _("Using host libthread_db library \"%ps\".\n"), + styled_string (file_name_style.style (), library)); } /* The thread library was detected. Activate the thread_db target @@ -673,14 +944,14 @@ try_thread_db_load_1 (struct thread_db_info *info) if (thread_db_list->next == NULL) push_target (&the_thread_db_target); - return 1; + return true; } /* 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, int check_auto_load_safe) +static bool +try_thread_db_load (const char *library, bool check_auto_load_safe) { void *handle; struct thread_db_info *info; @@ -699,14 +970,14 @@ try_thread_db_load (const char *library, int check_auto_load_safe) if (libthread_db_debug) fprintf_unfiltered (gdb_stdlog, _("open failed: %s.\n"), safe_strerror (errno)); - return 0; + return false; } if (!file_is_auto_load_safe (library, _("auto-load: Loading libthread-db " "library \"%s\" from explicit " "directory.\n"), library)) - return 0; + return false; } handle = dlopen (library, RTLD_NOW); @@ -714,7 +985,7 @@ try_thread_db_load (const char *library, int check_auto_load_safe) { if (libthread_db_debug) fprintf_unfiltered (gdb_stdlog, _("dlopen failed: %s.\n"), dlerror ()); - return 0; + return false; } if (libthread_db_debug && strchr (library, '/') == NULL) @@ -739,11 +1010,11 @@ try_thread_db_load (const char *library, int check_auto_load_safe) info->filename = gdb_realpath (library).release (); if (try_thread_db_load_1 (info)) - return 1; + return true; /* This library "refused" to work on current inferior. */ - delete_thread_db_info (ptid_get_pid (inferior_ptid)); - return 0; + delete_thread_db_info (inferior_ptid.pid ()); + return false; } /* Subroutine of try_thread_db_load_from_pdir to simplify it. @@ -751,7 +1022,7 @@ try_thread_db_load (const char *library, int check_auto_load_safe) SUBDIR may be NULL. It may also be something like "../lib64". The result is true for success. */ -static int +static bool try_thread_db_load_from_pdir_1 (struct objfile *obj, const char *subdir) { const char *obj_name = objfile_name (obj); @@ -759,8 +1030,9 @@ try_thread_db_load_from_pdir_1 (struct objfile *obj, const char *subdir) if (obj_name[0] != '/') { warning (_("Expected absolute pathname for libpthread in the" - " inferior, but got %s."), obj_name); - return 0; + " inferior, but got %ps."), + styled_string (file_name_style.style (), obj_name)); + return false; } std::string path = obj_name; @@ -772,7 +1044,7 @@ try_thread_db_load_from_pdir_1 (struct objfile *obj, const char *subdir) path = path + subdir + "/"; path += LIBTHREAD_DB_SO; - return try_thread_db_load (path.c_str (), 1); + return try_thread_db_load (path.c_str (), true); } /* Handle $pdir in libthread-db-search-path. @@ -780,19 +1052,17 @@ try_thread_db_load_from_pdir_1 (struct objfile *obj, const char *subdir) SUBDIR may be NULL. It may also be something like "../lib64". The result is true for success. */ -static int +static bool try_thread_db_load_from_pdir (const char *subdir) { - struct objfile *obj; - if (!auto_load_thread_db) - return 0; + return false; - ALL_OBJFILES (obj) + for (objfile *obj : current_program_space->objfiles ()) if (libpthread_name_p (objfile_name (obj))) { if (try_thread_db_load_from_pdir_1 (obj, subdir)) - return 1; + return true; /* We may have found the separate-debug-info version of libpthread, and it may live in a directory without a matching @@ -801,10 +1071,10 @@ try_thread_db_load_from_pdir (const char *subdir) return try_thread_db_load_from_pdir_1 (obj->separate_debug_objfile_backlink, subdir); - return 0; + return false; } - return 0; + return false; } /* Handle $sdir in libthread-db-search-path. @@ -812,34 +1082,34 @@ try_thread_db_load_from_pdir (const char *subdir) dlopen(file_without_path) will look. The result is true for success. */ -static int +static bool try_thread_db_load_from_sdir (void) { - return try_thread_db_load (LIBTHREAD_DB_SO, 0); + return try_thread_db_load (LIBTHREAD_DB_SO, false); } /* Try to load libthread_db from directory DIR of length DIR_LEN. The result is true for success. */ -static int +static bool try_thread_db_load_from_dir (const char *dir, size_t dir_len) { if (!auto_load_thread_db) - return 0; + return false; std::string path = std::string (dir, dir_len) + "/" + LIBTHREAD_DB_SO; - return try_thread_db_load (path.c_str (), 1); + return try_thread_db_load (path.c_str (), true); } /* Search libthread_db_search_path for libthread_db which "agrees" to work on current inferior. The result is true for success. */ -static int +static bool thread_db_load_search (void) { - int rc = 0; + bool rc = false; std::vector> dir_vec = dirnames_to_char_ptr_vec (libthread_db_search_path); @@ -892,44 +1162,42 @@ thread_db_load_search (void) return rc; } -/* Return non-zero if the inferior has a libpthread. */ +/* Return true if the inferior has a libpthread. */ -static int +static bool has_libpthread (void) { - struct objfile *obj; - - ALL_OBJFILES (obj) + for (objfile *obj : current_program_space->objfiles ()) if (libpthread_name_p (objfile_name (obj))) - return 1; + return true; - return 0; + return false; } /* Attempt to load and initialize libthread_db. Return 1 on success. */ -static int +static bool thread_db_load (void) { struct thread_db_info *info; - info = get_thread_db_info (ptid_get_pid (inferior_ptid)); + info = get_thread_db_info (inferior_ptid.pid ()); if (info != NULL) - return 1; + return true; /* Don't attempt to use thread_db on executables not running yet. */ if (!target_has_registers) - return 0; + return false; /* Don't attempt to use thread_db for remote targets. */ if (!(target_can_run () || core_bfd)) - return 0; + return false; if (thread_db_load_search ()) - return 1; + return true; /* We couldn't find a libthread_db. If the inferior has a libpthread warn the user. */ @@ -937,13 +1205,13 @@ thread_db_load (void) { warning (_("Unable to find libthread_db matching inferior's thread" " library, thread debugging will not be available.")); - return 0; + return false; } /* 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; + return false; } static void @@ -1023,7 +1291,7 @@ check_pid_namespace_match (void) child's thread list, we'll mistakenly think it has no threads since the thread PID fields won't match the PID we give to libthread_db. */ - if (!linux_ns_same (ptid_get_pid (inferior_ptid), LINUX_NS_PID)) + if (!linux_ns_same (inferior_ptid.pid (), LINUX_NS_PID)) { warning (_ ("Target and debugger are in different PID " "namespaces; thread lists and other data are " @@ -1094,11 +1362,9 @@ record_thread (struct thread_db_info *info, void thread_db_target::detach (inferior *inf, int from_tty) { - struct target_ops *target_beneath = find_target_beneath (this); - delete_thread_db_info (inf->pid); - target_beneath->detach (inf, from_tty); + beneath ()->detach (inf, from_tty); /* NOTE: From this point on, inferior_ptid is null_ptid. */ @@ -1113,9 +1379,8 @@ thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options) { struct thread_db_info *info; - struct target_ops *beneath = find_target_beneath (this); - ptid = beneath->wait (ptid, ourstatus, options); + ptid = beneath ()->wait (ptid, ourstatus, options); switch (ourstatus->kind) { @@ -1126,7 +1391,7 @@ thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, return ptid; } - info = get_thread_db_info (ptid_get_pid (ptid)); + info = get_thread_db_info (ptid.pid ()); /* If this process isn't using thread_db, we're done. */ if (info == NULL) @@ -1136,7 +1401,7 @@ thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, { /* New image, it may or may not end up using thread_db. Assume not unless we find otherwise. */ - delete_thread_db_info (ptid_get_pid (ptid)); + delete_thread_db_info (ptid.pid ()); if (!thread_db_list) unpush_target (&the_thread_db_target); @@ -1144,7 +1409,7 @@ thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, } /* Fill in the thread's user-level thread id and status. */ - thread_from_lwp (ptid); + thread_from_lwp (find_thread_ptid (ptid), ptid); return ptid; } @@ -1152,11 +1417,9 @@ thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, void thread_db_target::mourn_inferior () { - struct target_ops *target_beneath = find_target_beneath (this); - - delete_thread_db_info (ptid_get_pid (inferior_ptid)); + delete_thread_db_info (inferior_ptid.pid ()); - target_beneath->mourn_inferior (); + beneath ()->mourn_inferior (); /* Detach thread_db target ops. */ if (!thread_db_list) @@ -1174,7 +1437,6 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data) { td_thrinfo_t ti; td_err_e err; - ptid_t ptid; struct thread_info *tp; struct callback_data *cb_data = (struct callback_data *) data; struct thread_db_info *info = cb_data->info; @@ -1225,7 +1487,7 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data) return 0; } - ptid = ptid_build (info->pid, ti.ti_lid, 0); + ptid_t ptid (info->pid, ti.ti_lid); tp = find_thread_ptid (ptid); if (tp == NULL || tp->priv == NULL) record_thread (info, tp, ptid, th_p, &ti); @@ -1249,7 +1511,7 @@ find_new_threads_once (struct thread_db_info *info, int iteration, /* See comment in thread_db_update_thread_list. */ gdb_assert (info->td_ta_thr_iter_p != NULL); - TRY + try { /* Iterate over all user-space threads to discover new threads. */ err = info->td_ta_thr_iter_p (info->thread_agent, @@ -1260,7 +1522,7 @@ find_new_threads_once (struct thread_db_info *info, int iteration, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); } - CATCH (except, RETURN_MASK_ERROR) + catch (const gdb_exception_error &except) { if (libthread_db_debug) { @@ -1268,7 +1530,6 @@ find_new_threads_once (struct thread_db_info *info, int iteration, "Warning: find_new_threads_once: "); } } - END_CATCH if (libthread_db_debug) { @@ -1288,16 +1549,16 @@ find_new_threads_once (struct thread_db_info *info, int iteration, searches in a row do not discover any new threads. */ static void -thread_db_find_new_threads_2 (ptid_t ptid, int until_no_new) +thread_db_find_new_threads_2 (thread_info *stopped, bool until_no_new) { td_err_e err = TD_OK; struct thread_db_info *info; int i, loop; - info = get_thread_db_info (ptid_get_pid (ptid)); + info = get_thread_db_info (stopped->ptid.pid ()); /* Access an lwp we know is stopped. */ - info->proc_handle.ptid = ptid; + info->proc_handle.thread = stopped; if (until_no_new) { @@ -1320,9 +1581,9 @@ thread_db_find_new_threads_2 (ptid_t ptid, int until_no_new) } static void -thread_db_find_new_threads_1 (ptid_t ptid) +thread_db_find_new_threads_1 (thread_info *stopped) { - thread_db_find_new_threads_2 (ptid, 0); + thread_db_find_new_threads_2 (stopped, 0); } /* Implement the to_update_thread_list target method for this @@ -1332,11 +1593,10 @@ void thread_db_target::update_thread_list () { struct thread_db_info *info; - struct inferior *inf; prune_threads (); - ALL_INFERIORS (inf) + for (inferior *inf : all_inferiors ()) { struct thread_info *thread; @@ -1347,7 +1607,7 @@ thread_db_target::update_thread_list () if (info == NULL) continue; - thread = any_live_thread_of_process (inf->pid); + thread = any_live_thread_of_inferior (inf); if (thread == NULL || thread->executing) continue; @@ -1365,32 +1625,27 @@ thread_db_target::update_thread_list () if (target_has_execution_1 (thread->ptid)) continue; - thread_db_find_new_threads_1 (thread->ptid); + thread_db_find_new_threads_1 (thread); } /* Give the beneath target a chance to do extra processing. */ this->beneath ()->update_thread_list (); } -const char * +std::string thread_db_target::pid_to_str (ptid_t ptid) { struct thread_info *thread_info = find_thread_ptid (ptid); - struct target_ops *beneath; if (thread_info != NULL && thread_info->priv != NULL) { - static char buf[64]; thread_db_thread_info *priv = get_thread_db_thread_info (thread_info); - snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)", - (unsigned long) priv->tid, ptid_get_lwp (ptid)); - - return buf; + return string_printf ("Thread 0x%lx (LWP %ld)", + (unsigned long) priv->tid, ptid.lwp ()); } - beneath = find_target_beneath (this); - return beneath->pid_to_str (ptid); + return beneath ()->pid_to_str (ptid); } /* Return a string describing the state of the thread specified by @@ -1418,30 +1673,52 @@ thread_db_target::thread_handle_to_thread_info (const gdb_byte *thread_handle, int handle_len, inferior *inf) { - struct thread_info *tp; thread_t handle_tid; - /* Thread handle sizes must match in order to proceed. We don't use an - assert here because the resulting internal error will cause GDB to - exit. This isn't necessarily an internal error due to the possibility - of garbage being passed as the thread handle via the python interface. */ - if (handle_len != sizeof (handle_tid)) + /* When debugging a 32-bit target from a 64-bit host, handle_len + will be 4 and sizeof (handle_tid) will be 8. This requires + a different cast than the more straightforward case where + the sizes are the same. + + Use "--target_board unix/-m32" from a native x86_64 linux build + to test the 32/64-bit case. */ + if (handle_len == 4 && sizeof (handle_tid) == 8) + handle_tid = (thread_t) * (const uint32_t *) thread_handle; + else if (handle_len == sizeof (handle_tid)) + handle_tid = * (const thread_t *) thread_handle; + else error (_("Thread handle size mismatch: %d vs %zu (from libthread_db)"), handle_len, sizeof (handle_tid)); - handle_tid = * (const thread_t *) thread_handle; - - ALL_NON_EXITED_THREADS (tp) + for (thread_info *tp : inf->non_exited_threads ()) { thread_db_thread_info *priv = get_thread_db_thread_info (tp); - if (tp->inf == inf && priv != NULL && handle_tid == priv->tid) + if (priv != NULL && handle_tid == priv->tid) return tp; } return NULL; } +/* Return the thread handle associated the thread_info pointer TP. */ + +gdb::byte_vector +thread_db_target::thread_info_to_thread_handle (struct thread_info *tp) +{ + thread_db_thread_info *priv = get_thread_db_thread_info (tp); + + if (priv == NULL) + return gdb::byte_vector (); + + int handle_size = sizeof (priv->tid); + gdb::byte_vector rv (handle_size); + + memcpy (rv.data (), &priv->tid, handle_size); + + return rv; +} + /* Get the address of the thread local variable in load module LM which is stored at OFFSET within the thread local storage for thread PTID. */ @@ -1451,20 +1728,19 @@ thread_db_target::get_thread_local_address (ptid_t ptid, CORE_ADDR offset) { struct thread_info *thread_info; - struct target_ops *beneath; /* Find the matching thread. */ thread_info = find_thread_ptid (ptid); /* We may not have discovered the thread yet. */ if (thread_info != NULL && thread_info->priv == NULL) - thread_info = thread_from_lwp (ptid); + thread_info = thread_from_lwp (thread_info, ptid); if (thread_info != NULL && thread_info->priv != NULL) { td_err_e err; psaddr_t address; - thread_db_info *info = get_thread_db_info (ptid_get_pid (ptid)); + thread_db_info *info = get_thread_db_info (ptid.pid ()); thread_db_thread_info *priv = get_thread_db_thread_info (thread_info); /* Finally, get the address of the variable. */ @@ -1523,8 +1799,7 @@ thread_db_target::get_thread_local_address (ptid_t ptid, : (CORE_ADDR) (uintptr_t) address); } - beneath = find_target_beneath (this); - return beneath->get_thread_local_address (ptid, lm, offset); + return beneath ()->get_thread_local_address (ptid, lm, offset); } /* Implement the to_get_ada_task_ptid target method for this target. */ @@ -1533,19 +1808,18 @@ ptid_t thread_db_target::get_ada_task_ptid (long lwp, long thread) { /* NPTL uses a 1:1 model, so the LWP id suffices. */ - return ptid_build (ptid_get_pid (inferior_ptid), lwp, 0); + return ptid_t (inferior_ptid.pid (), lwp, 0); } void thread_db_target::resume (ptid_t ptid, int step, enum gdb_signal signo) { - struct target_ops *beneath = find_target_beneath (this); struct thread_db_info *info; - if (ptid_equal (ptid, minus_one_ptid)) - info = get_thread_db_info (ptid_get_pid (inferior_ptid)); + if (ptid == minus_one_ptid) + info = get_thread_db_info (inferior_ptid.pid ()); else - info = get_thread_db_info (ptid_get_pid (ptid)); + info = get_thread_db_info (ptid.pid ()); /* This workaround is only needed for child fork lwps stopped in a PTRACE_O_TRACEFORK event. When the inferior is resumed, the @@ -1553,7 +1827,7 @@ thread_db_target::resume (ptid_t ptid, int step, enum gdb_signal signo) if (info) info->need_stale_parent_threads_check = 0; - beneath->resume (ptid, step, signo); + beneath ()->resume (ptid, step, signo); } /* std::sort helper function for info_auto_load_libthread_db, sort the @@ -1668,6 +1942,24 @@ info_auto_load_libthread_db (const char *args, int from_tty) uiout->message (_("No auto-loaded libthread-db.\n")); } +/* Implement 'maintenance check libthread-db'. */ + +static void +maintenance_check_libthread_db (const char *args, int from_tty) +{ + int inferior_pid = inferior_ptid.pid (); + struct thread_db_info *info; + + if (inferior_pid == 0) + error (_("No inferior running")); + + info = get_thread_db_info (inferior_pid); + if (info == NULL) + error (_("No libthread_db loaded")); + + check_thread_db (info, true); +} + void _initialize_thread_db (void) { @@ -1708,7 +2000,7 @@ Show whether auto-loading inferior specific libthread_db is enabled."), _("\ If enabled, libthread_db will be searched in 'set libthread-db-search-path'\n\ locations to load libthread_db compatible with the inferior.\n\ Standard system libthread_db still gets loaded even with this option off.\n\ -This options has security implications for untrusted inferiors."), +This option has security implications for untrusted inferiors."), NULL, show_auto_load_thread_db, auto_load_set_cmdlist_get (), auto_load_show_cmdlist_get ()); @@ -1718,6 +2010,23 @@ This options has security implications for untrusted inferiors."), Usage: info auto-load libthread-db"), auto_load_info_cmdlist_get ()); + add_cmd ("libthread-db", class_maintenance, + maintenance_check_libthread_db, _("\ +Run integrity checks on the current inferior's libthread_db."), + &maintenancechecklist); + + add_setshow_boolean_cmd ("check-libthread-db", + class_maintenance, + &check_thread_db_on_load, _("\ +Set whether to check libthread_db at load time."), _("\ +Show whether to check libthread_db at load time."), _("\ +If enabled GDB will run integrity checks on inferior specific libthread_db\n\ +as they are loaded."), + NULL, + NULL, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); + /* Add ourselves to objfile event chain. */ gdb::observers::new_objfile.attach (thread_db_new_objfile);