/* 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.
#include <dlfcn.h>
#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"
#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.
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;
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 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. */
{
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;
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;
{
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;
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;
}
+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
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)
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);
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);
thread_db_find_new_threads_silently (thread_info *stopped)
{
- TRY
+ try
{
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,
return 1;
}
}
- END_CATCH
return 0;
}
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 ());
fail. */
linux_stop_and_wait_all_lwps ();
- TRY
+ try
{
td_err_e err = td_ta_thr_iter_p (info->thread_agent,
check_thread_db_callback,
if (!tdb_testinfo->threads_seen)
error (_("no threads seen"));
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
if (warning_pre_print)
fputs_unfiltered (warning_pre_print, gdb_stderr);
test_passed = false;
}
- END_CATCH
if (test_passed && log_progress)
debug_printf (_("libthread_db integrity checks passed.\n"));
}
/* 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;
do \
{ \
if ((a) == NULL) \
- return 0; \
+ return false; \
} while (0)
CHK (TDB_VERBOSE_DLSYM (info, td_init));
{
warning (_("Cannot initialize libthread_db: %s"),
thread_db_err_str (err));
- return 0;
+ return false;
}
CHK (TDB_VERBOSE_DLSYM (info, td_ta_new));
default:
warning (_("td_ta_new failed: %s"), thread_db_err_str (err));
}
- return 0;
+ return false;
}
/* These are essential. */
/* 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,
if (check_thread_db_on_load)
{
if (!check_thread_db (info, libthread_db_debug))
- return 0;
+ return false;
}
if (info->td_ta_thr_iter_p == NULL)
/* 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"));
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);
-
- return 1;
+ for this process. */
+ push_target (&the_thread_db_target);
+ 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;
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);
{
if (libthread_db_debug)
fprintf_unfiltered (gdb_stdlog, _("dlopen failed: %s.\n"), dlerror ());
- return 0;
+ return false;
}
if (libthread_db_debug && strchr (library, '/') == NULL)
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 (inferior_ptid.pid ());
- return 0;
+ delete_thread_db_info (current_inferior ()->process_target (),
+ inferior_ptid.pid ());
+ return false;
}
/* Subroutine of try_thread_db_load_from_pdir to simplify it.
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);
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;
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.
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
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.
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<gdb::unique_xmalloc_ptr<char>> dir_vec
= dirnames_to_char_ptr_vec (libthread_db_search_path);
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 (inferior_ptid.pid ());
+ info = get_thread_db_info (current_inferior ()->process_target (),
+ 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. */
{
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
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);
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
{
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);
/* 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,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
if (libthread_db_debug)
{
"Warning: find_new_threads_once: ");
}
}
- END_CATCH
if (libthread_db_debug)
{
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);
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);
+ thread_info *thread_info = find_thread_ptid (current_inferior (), ptid);
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.lwp ());
-
- return buf;
+ return string_printf ("Thread 0x%lx (LWP %ld)",
+ (unsigned long) priv->tid, ptid.lwp ());
}
return beneath ()->pid_to_str (ptid);
{
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);
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. */
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. */
: (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"));
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 ());