/* libthread_db assisted debugging support, generic parts.
- Copyright (C) 1999-2019 Free Software Foundation, Inc.
+ Copyright (C) 1999-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include <dlfcn.h>
#include "gdb_proc_service.h"
#include "nat/gdb_thread_db.h"
-#include "common/gdb_vecs.h"
+#include "gdbsupport/gdb_vecs.h"
#include "bfd.h"
#include "command.h"
#include "gdbcmd.h"
#include <ctype.h>
#include "nat/linux-namespaces.h"
#include <algorithm>
-#include "common/pathstuff.h"
+#include "gdbsupport/pathstuff.h"
#include "valprint.h"
+#include "cli/cli-style.h"
/* GNU/Linux libthread_db support.
strata stratum () const override { return thread_stratum; }
void detach (inferior *, int) override;
- ptid_t wait (ptid_t, struct target_waitstatus *, int) override;
+ ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
void resume (ptid_t, int, enum gdb_signal) override;
void mourn_inferior () override;
void update_thread_list () override;
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 non-zero if load-time libthread_db tests have been enabled
- by the "maintenence set check-libthread-db" command. */
-static int check_thread_db_on_load = 0;
+/* 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. */
/* Non-zero if we have determined the signals used by the threads
library. */
static int thread_signals;
-static sigset_t thread_stop_set;
-static sigset_t thread_print_set;
struct thread_db_info
{
struct thread_db_info *next;
+ /* The target this thread_db_info is bound to. */
+ process_stratum_target *process_target;
+
/* Process id this object refers to. */
int pid;
/* List of known processes using thread_db, and the required
bookkeeping. */
-struct thread_db_info *thread_db_list;
+static thread_db_info *thread_db_list;
static void thread_db_find_new_threads_1 (thread_info *stopped);
static void thread_db_find_new_threads_2 (thread_info *stopped,
{
struct thread_db_info *info = XCNEW (struct thread_db_info);
+ info->process_target = current_inferior ()->process_target ();
info->pid = inferior_ptid.pid ();
info->handle = handle;
/* The workaround works by reading from /proc/pid/status, so it is
disabled for core files. */
- if (target_has_execution)
+ if (target_has_execution ())
info->need_stale_parent_threads_check = 1;
info->next = thread_db_list;
related to process PID, if any; NULL otherwise. */
static struct thread_db_info *
-get_thread_db_info (int pid)
+get_thread_db_info (process_stratum_target *targ, int pid)
{
struct thread_db_info *info;
for (info = thread_db_list; info; info = info->next)
- if (pid == info->pid)
+ if (targ == info->process_target && pid == info->pid)
return info;
return NULL;
LIBTHREAD_DB_SO's dlopen'ed handle. */
static void
-delete_thread_db_info (int pid)
+delete_thread_db_info (process_stratum_target *targ, int pid)
{
struct thread_db_info *info, *info_prev;
info_prev = NULL;
for (info = thread_db_list; info; info_prev = info, info = info->next)
- if (pid == info->pid)
+ if (targ == info->process_target && pid == info->pid)
break;
if (info == NULL)
LWP. */
gdb_assert (ptid.lwp () != 0);
- info = get_thread_db_info (ptid.pid ());
+ info = get_thread_db_info (stopped->inf->process_target (), ptid.pid ());
/* Access an lwp we know is stopped. */
info->proc_handle.thread = stopped;
thread_db_err_str (err));
/* Fill the cache. */
- tp = find_thread_ptid (ptid);
+ tp = find_thread_ptid (stopped->inf->process_target (), ptid);
return record_thread (info, tp, ptid, &th, &ti);
}
\f
{
struct thread_db_info *info;
- info = get_thread_db_info (child.pid ());
+ info = get_thread_db_info (linux_target, child.pid ());
if (info == NULL)
return 0;
- thread_info *stopped = find_thread_ptid (parent);
+ thread_info *stopped = find_thread_ptid (linux_target, parent);
thread_from_lwp (stopped, child);
{
struct bound_minimal_symbol version_msym;
CORE_ADDR version_addr;
- gdb::unique_xmalloc_ptr<char> version;
- int err, got, retval = 0;
+ int got, retval = 0;
version_msym = lookup_minimal_symbol (ver_symbol, NULL, NULL);
if (version_msym.minsym == NULL)
return 0;
version_addr = BMSYMBOL_VALUE_ADDRESS (version_msym);
- got = target_read_string (version_addr, &version, 32, &err);
- if (err == 0 && memchr (version.get (), 0, got) == version.get () + got - 1)
+ gdb::unique_xmalloc_ptr<char> version
+ = target_read_string (version_addr, 32, &got);
+ if (version != nullptr
+ && memchr (version.get (), 0, got) == version.get () + got - 1)
{
int major, minor;
corrupted. For core files it does not apply, no 'later enumeration'
is possible. */
- if (!target_has_execution || !inferior_has_bug ("nptl_version", 2, 7))
+ if (!target_has_execution () || !inferior_has_bug ("nptl_version", 2, 7))
{
exception_fprintf (gdb_stderr, except,
_("Warning: couldn't activate thread debugging "
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)
+ if (tdb_testinfo->last_result == TD_ERR && !target_has_execution ())
{
/* Some platforms require execution for td_ta_map_lwp2thr. */
LOG (_("; can't map_lwp2thr"));
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);
+ thread_info *thread_info = find_thread_ptid (linux_target, ptid);
if (thread_info != NULL && thread_info->priv != NULL)
{
LOG ("; errno");
scoped_restore_current_thread restore_current_thread;
- switch_to_thread (ptid);
+ switch_to_thread (thread_info);
expression_up expr = parse_expression ("(int) errno");
struct value *val = evaluate_expression (expr.get ());
fprintf_unfiltered (gdb_stdlog, _("td_ta_new failed: %s\n"),
thread_db_err_str (err));
else
- switch (err)
- {
- case TD_NOLIBTHREAD:
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
#ifdef THREAD_DB_HAS_TD_VERSION
- case TD_VERSION:
+ case TD_VERSION:
#endif
- /* The errors above are not unexpected and silently ignored:
- they just mean we haven't found correct version of
- libthread_db yet. */
- break;
- default:
- warning (_("td_ta_new failed: %s"), thread_db_err_str (err));
- }
+ /* The errors above are not unexpected and silently ignored:
+ they just mean we haven't found correct version of
+ libthread_db yet. */
+ break;
+ default:
+ warning (_("td_ta_new failed: %s"), thread_db_err_str (err));
+ }
return false;
}
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
+ if (target_has_execution ()
&& linux_proc_task_list_dir_exists (inferior_ptid.pid ()))
info->td_ta_thr_iter_p = NULL;
else
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. */
+ corrupted, we'd not manage to list any threads. Better reject this
+ thread_db, and fall back to at least listing LWPs. */
return false;
}
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
- if this is the first process using it. */
- if (thread_db_list->next == NULL)
- push_target (&the_thread_db_target);
-
+ for this process. */
+ push_target (&the_thread_db_target);
return true;
}
return false;
}
- if (!file_is_auto_load_safe (library, _("auto-load: Loading libthread-db "
- "library \"%s\" from explicit "
- "directory.\n"),
- library))
+ auto_load_debug_printf
+ ("Loading libthread-db library \"%s\" from explicit directory.",
+ library);
+
+ if (!file_is_auto_load_safe (library))
return false;
}
td_init = dlsym (handle, "td_init");
if (td_init != NULL)
- {
- const char *const libpath = dladdr_to_soname (td_init);
+ {
+ const char *const libpath = dladdr_to_soname (td_init);
- if (libpath != NULL)
- fprintf_unfiltered (gdb_stdlog, _("Host %s resolved to: %s.\n"),
- library, libpath);
- }
+ if (libpath != NULL)
+ fprintf_unfiltered (gdb_stdlog, _("Host %s resolved to: %s.\n"),
+ library, libpath);
+ }
}
info = add_thread_db_info (handle);
return true;
/* This library "refused" to work on current inferior. */
- delete_thread_db_info (inferior_ptid.pid ());
+ delete_thread_db_info (current_inferior ()->process_target (),
+ inferior_ptid.pid ());
return false;
}
if (obj_name[0] != '/')
{
warning (_("Expected absolute pathname for libpthread in the"
- " inferior, but got %s."), obj_name);
+ " inferior, but got %ps."),
+ styled_string (file_name_style.style (), obj_name));
return false;
}
{
struct thread_db_info *info;
- info = get_thread_db_info (inferior_ptid.pid ());
+ info = get_thread_db_info (current_inferior ()->process_target (),
+ inferior_ptid.pid ());
if (info != NULL)
return true;
/* Don't attempt to use thread_db on executables not running
yet. */
- if (!target_has_registers)
+ if (!target_has_registers ())
return false;
/* Don't attempt to use thread_db for remote targets. */
{
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++)
+ for (i = 0; i < lin_thread_get_thread_signal_num (); i++)
{
- if (sigismember (&mask, i))
- {
- if (signal_stop_update (gdb_signal_from_host (i), 0))
- sigaddset (&thread_stop_set, i);
- if (signal_print_update (gdb_signal_from_host (i), 0))
- sigaddset (&thread_print_set, i);
- thread_signals = 1;
- }
+ int sig = lin_thread_get_thread_signal (i);
+ signal_stop_update (gdb_signal_from_host (sig), 0);
+ signal_print_update (gdb_signal_from_host (sig), 0);
+ thread_signals = 1;
}
}
}
}
static void
-check_pid_namespace_match (void)
+check_pid_namespace_match (inferior *inf)
{
/* Check is only relevant for local targets targets. */
if (target_can_run ())
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 (inferior_ptid.pid (), LINUX_NS_PID))
+ if (!linux_ns_same (inf->pid, LINUX_NS_PID))
{
warning (_ ("Target and debugger are in different PID "
"namespaces; thread lists and other data are "
This handles the case of debugging statically linked executables. */
static void
-thread_db_inferior_created (struct target_ops *target, int from_tty)
+thread_db_inferior_created (inferior *inf)
{
- check_pid_namespace_match ();
+ check_pid_namespace_match (inf);
check_for_thread_db ();
}
thread with this PTID, but it's marked exited, then the kernel
reused the tid of an old thread. */
if (tp == NULL || tp->state == THREAD_EXITED)
- tp = add_thread_with_info (ptid, priv);
+ tp = add_thread_with_info (info->process_target, ptid, priv);
else
tp->priv.reset (priv);
- if (target_has_execution)
+ if (target_has_execution ())
check_thread_signals ();
return tp;
void
thread_db_target::detach (inferior *inf, int from_tty)
{
- delete_thread_db_info (inf->pid);
+ delete_thread_db_info (inf->process_target (), inf->pid);
beneath ()->detach (inf, from_tty);
/* NOTE: From this point on, inferior_ptid is null_ptid. */
- /* If there are no more processes using libpthread, detach the
- thread_db target ops. */
- if (!thread_db_list)
- unpush_target (this);
+ /* Detach the thread_db target from this inferior. */
+ unpush_target (this);
}
ptid_t
thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
- int options)
+ target_wait_flags options)
{
struct thread_db_info *info;
- ptid = beneath ()->wait (ptid, ourstatus, options);
+ process_stratum_target *beneath
+ = as_process_stratum_target (this->beneath ());
+
+ ptid = beneath->wait (ptid, ourstatus, options);
switch (ourstatus->kind)
{
return ptid;
}
- info = get_thread_db_info (ptid.pid ());
+ info = get_thread_db_info (beneath, ptid.pid ());
/* If this process isn't using thread_db, we're done. */
if (info == NULL)
{
/* New image, it may or may not end up using thread_db. Assume
not unless we find otherwise. */
- delete_thread_db_info (ptid.pid ());
- if (!thread_db_list)
- unpush_target (&the_thread_db_target);
+ delete_thread_db_info (beneath, ptid.pid ());
+ unpush_target (this);
return ptid;
}
/* Fill in the thread's user-level thread id and status. */
- thread_from_lwp (find_thread_ptid (ptid), ptid);
+ thread_from_lwp (find_thread_ptid (beneath, ptid), ptid);
return ptid;
}
void
thread_db_target::mourn_inferior ()
{
- delete_thread_db_info (inferior_ptid.pid ());
+ process_stratum_target *target_beneath
+ = as_process_stratum_target (this->beneath ());
+
+ delete_thread_db_info (target_beneath, inferior_ptid.pid ());
- beneath ()->mourn_inferior ();
+ target_beneath->mourn_inferior ();
- /* Detach thread_db target ops. */
- if (!thread_db_list)
- unpush_target (&the_thread_db_target);
+ /* Detach the thread_db target from this inferior. */
+ unpush_target (this);
}
struct callback_data
}
ptid_t ptid (info->pid, ti.ti_lid);
- tp = find_thread_ptid (ptid);
+ tp = find_thread_ptid (info->process_target, ptid);
if (tp == NULL || tp->priv == NULL)
record_thread (info, tp, ptid, th_p, &ti);
struct thread_db_info *info;
int i, loop;
- info = get_thread_db_info (stopped->ptid.pid ());
+ info = get_thread_db_info (stopped->inf->process_target (),
+ stopped->ptid.pid ());
/* Access an lwp we know is stopped. */
info->proc_handle.thread = stopped;
for (inferior *inf : all_inferiors ())
{
- struct thread_info *thread;
-
if (inf->pid == 0)
continue;
- info = get_thread_db_info (inf->pid);
+ info = get_thread_db_info (inf->process_target (), inf->pid);
if (info == NULL)
continue;
- thread = any_live_thread_of_inferior (inf);
+ thread_info *thread = any_live_thread_of_inferior (inf);
if (thread == NULL || thread->executing)
continue;
stop. That uses thread_db entry points that do not walk
libpthread's thread list, so should be safe, as well as more
efficient. */
- if (target_has_execution_1 (thread->ptid))
+ if (thread->inf->has_execution ())
continue;
thread_db_find_new_threads_1 (thread);
std::string
thread_db_target::pid_to_str (ptid_t ptid)
{
- struct thread_info *thread_info = find_thread_ptid (ptid);
+ thread_info *thread_info = find_thread_ptid (current_inferior (), ptid);
if (thread_info != NULL && thread_info->priv != NULL)
{
{
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;
-
for (thread_info *tp : inf->non_exited_threads ())
{
thread_db_thread_info *priv = get_thread_db_thread_info (tp);
if (priv != NULL && handle_tid == priv->tid)
- return tp;
+ return tp;
}
return NULL;
CORE_ADDR offset)
{
struct thread_info *thread_info;
-
+ process_stratum_target *beneath
+ = as_process_stratum_target (this->beneath ());
/* Find the matching thread. */
- thread_info = find_thread_ptid (ptid);
+ thread_info = find_thread_ptid (beneath, ptid);
/* We may not have discovered the thread yet. */
if (thread_info != NULL && thread_info->priv == NULL)
{
td_err_e err;
psaddr_t address;
- thread_db_info *info = get_thread_db_info (ptid.pid ());
+ thread_db_info *info = get_thread_db_info (beneath, ptid.pid ());
thread_db_thread_info *priv = get_thread_db_thread_info (thread_info);
/* Finally, get the address of the variable. */
/* Now, if libthread_db provided the initialization image's
address, we *could* try to build a non-lvalue value from
the initialization image. */
- throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
- _("TLS not allocated yet"));
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS not allocated yet"));
#endif
/* Something else went wrong. */
if (err != TD_OK)
- throw_error (TLS_GENERIC_ERROR,
- (("%s")), thread_db_err_str (err));
+ throw_error (TLS_GENERIC_ERROR,
+ (("%s")), thread_db_err_str (err));
/* Cast assuming host == target. Joy. */
/* Do proper sign extension for the target. */
- gdb_assert (exec_bfd);
- return (bfd_get_sign_extend_vma (exec_bfd) > 0
+ gdb_assert (current_program_space->exec_bfd ());
+ return (bfd_get_sign_extend_vma (current_program_space->exec_bfd ()) > 0
? (CORE_ADDR) (intptr_t) address
: (CORE_ADDR) (uintptr_t) address);
}
- 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. */
void
thread_db_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
{
- struct thread_db_info *info;
+ process_stratum_target *beneath
+ = as_process_stratum_target (this->beneath ());
- if (ptid == minus_one_ptid)
- info = get_thread_db_info (inferior_ptid.pid ());
- else
- info = get_thread_db_info (ptid.pid ());
+ thread_db_info *info
+ = get_thread_db_info (beneath, (ptid == minus_one_ptid
+ ? inferior_ptid.pid ()
+ : ptid.pid ()));
/* This workaround is only needed for child fork lwps stopped in a
PTRACE_O_TRACEFORK event. When the inferior is resumed, the
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
if (inferior_pid == 0)
error (_("No inferior running"));
- info = get_thread_db_info (inferior_pid);
+ info = get_thread_db_info (current_inferior ()->process_target (),
+ inferior_pid);
if (info == NULL)
error (_("No libthread_db loaded"));
check_thread_db (info, true);
}
+void _initialize_thread_db ();
void
-_initialize_thread_db (void)
+_initialize_thread_db ()
{
/* Defer loading of libthread_db.so until inferior is running.
This allows gdb to load correct libthread_db for a given