* [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support
@ 2018-09-15 13:21 Rajendra SY
2018-09-17 17:11 ` John Baldwin
0 siblings, 1 reply; 5+ messages in thread
From: Rajendra SY @ 2018-09-15 13:21 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 869 bytes --]
Problem:
Missing libthread_db integration with GDB
Cause:
GDB missing libthread_db integration on FreeBSD target because of this
GDB failed to access TLS variable values.
Tests failed:
- gdb.threads/tls-shared.exp
- gdb.threads/tls-nodebug-pie.exp
- gdb.threads/tls-so_extern.exp
- gdb.threads/tls-var.exp
- gdb.threads/tls.exp
- gdb.threads/tls-core.exp
gdb/ChangeLog:
2018-09-15 Rajendra SY <syrajendra@juniper.net>
PR gdb/23660
* gdb/amd64-fbsd-nat.c (supply_gregset):
(fill_gregset):
(supply_fpregset):
(fill_fpregset):
* gdb/amd64-fbsd-tdep.c (amd64fbsd_init_abi):
* gdb/configure.nat:
* gdb/fbsd-thread-db.c: New file.
* gdb/i386-bsd-nat.c (supply_gregset):
(supply_fpregset):
(fill_gregset):
(fill_fpregset):
* gdb/i386-fbsd-tdep.c (i386fbsd4_init_abi):
[-- Attachment #2: 0001-Add-thread-db-support-to-FreeBSD-platform.patch --]
[-- Type: application/octet-stream, Size: 44233 bytes --]
From bf862280901e68b011c6103ffdb6fa6537eb30cd Mon Sep 17 00:00:00 2001
From: Rajendra S Y <syrajendra@juniper.net>
Date: Fri, 14 Sep 2018 20:14:46 +0530
Subject: [PATCH] Add thread-db support to FreeBSD platform
Signed-off-by: Rajendra S Y <syrajendra@juniper.net>
---
gdb/amd64-fbsd-nat.c | 44 ++
gdb/amd64-fbsd-tdep.c | 4 +
gdb/configure.nat | 7 +-
gdb/fbsd-thread-db.c | 1348 +++++++++++++++++++++++++++++++++++++++++++++++++
gdb/i386-bsd-nat.c | 32 ++
gdb/i386-fbsd-tdep.c | 4 +
6 files changed, 1434 insertions(+), 5 deletions(-)
create mode 100644 gdb/fbsd-thread-db.c
diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 7c7c963..f7e8f0c 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -35,6 +35,7 @@
#include "amd64-bsd-nat.h"
#include "x86-nat.h"
#include "x86-xstate.h"
+#include "gregset.h"
\f
class amd64_fbsd_nat_target final
@@ -306,3 +307,46 @@ Please report this to <bug-gdb@gnu.org>."),
}
#endif
}
+
+/* Transfering the registers between GDB, inferiors and core files. */
+
+/* Fill GDB's register array with the general-purpose register values
+ in *GREGSETP. */
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregs)
+{
+ amd64_supply_native_gregset (regcache, gregs, -1);
+}
+
+/* Fill register REGNUM (if it is a general-purpose register) in
+ *GREGSETPS with the value in GDB's register array. If REGNUM is -1,
+ do this for all registers. */
+
+void
+fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregs, int regno)
+{
+ amd64_collect_native_gregset (regcache, gregs, regno);
+}
+
+/* Fill GDB's register array with the floating-point register values
+ in *FPREGSETP. */
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregs)
+{
+ amd64_supply_fxsave (regcache, -1, fpregs);
+}
+
+/* Fill register REGNUM (if it is a floating-point register) in
+ *FPREGSETP with the value in GDB's register array. If REGNUM is -1,
+ do this for all registers. */
+
+void
+fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregs,
+ int regno)
+{
+ amd64_collect_fxsave (regcache, regno, fpregs);
+}
+
+
diff --git a/gdb/amd64-fbsd-tdep.c b/gdb/amd64-fbsd-tdep.c
index 7f7ecfc..7c90108 100644
--- a/gdb/amd64-fbsd-tdep.c
+++ b/gdb/amd64-fbsd-tdep.c
@@ -238,6 +238,10 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_gdbarch_core_read_description (gdbarch,
amd64fbsd_core_read_description);
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
/* FreeBSD uses SVR4-style shared libraries. */
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_lp64_fetch_link_map_offsets);
diff --git a/gdb/configure.nat b/gdb/configure.nat
index feddeaa..6ccb43e 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -62,7 +62,8 @@ case ${gdb_host} in
LOADLIBES='-ldl $(RDYNAMIC)'
;;
fbsd*)
- NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o fbsd-nat.o'
+ NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o fbsd-nat.o \
+ fbsd-thread-db.o'
HAVE_NATIVE_GCORE_HOST=1
LOADLIBES='-lkvm'
;;
@@ -267,10 +268,6 @@ case ${gdb_host} in
# Host: PowerPC, running Linux
NATDEPFILES="${NATDEPFILES} ppc-linux-nat.o ppc-linux.o"
;;
- riscv*)
- # Host: RISC-V, running Linux
- NATDEPFILES="${NATDEPFILES} riscv-linux-nat.o"
- ;;
s390)
# Host: S390, running Linux
NATDEPFILES="${NATDEPFILES} s390-linux-nat.o"
diff --git a/gdb/fbsd-thread-db.c b/gdb/fbsd-thread-db.c
new file mode 100644
index 0000000..7cbc42d
--- /dev/null
+++ b/gdb/fbsd-thread-db.c
@@ -0,0 +1,1348 @@
+/* libthread_db assisted debugging support.
+
+ Copyright (C) 1999-2018 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include <dlfcn.h>
+#include "gdb_proc_service.h"
+#include "nat/gdb_thread_db.h"
+#include "gdb_vecs.h"
+#include "bfd.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "infrun.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "target.h"
+#include "regcache.h"
+#include "gregset.h"
+#include "solib.h"
+#include "solib-svr4.h"
+#include "gdbcore.h"
+#include "observable.h"
+#include "fbsd-nat.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#if defined(__i386__)
+#include "i387-tdep.h"
+#endif
+
+#if defined (__i386__) && defined (PT_GETXMMREGS)
+#define I386_GETXMMREGS
+#endif
+
+/* FreeBSD libthread_db support. */
+static const target_info thread_db_target_info = {
+ "freebsd-threads",
+ N_("FreeBSD multithreaded child process."),
+ N_("FreeBSD threads support.")
+};
+
+
+class thread_db_target final : public target_ops
+{
+public:
+
+ thread_db_target ();
+
+ const target_info &info () const override
+ { return thread_db_target_info; }
+
+ void detach (inferior *, int) override;
+ void mourn_inferior () override;
+ ptid_t wait (ptid_t, struct target_waitstatus *, int) override;
+ void resume (ptid_t, int, enum gdb_signal) override;
+ void update_thread_list () override;
+ const char *pid_to_str (ptid_t) override;
+ const char *extra_thread_info (struct thread_info *) override;
+ ptid_t get_ada_task_ptid (long lwp, long thread) override;
+ struct thread_info *thread_handle_to_thread_info (
+ const gdb_byte *thread_handle,
+ int handle_len,
+ inferior *inf) override;
+ CORE_ADDR get_thread_local_address (ptid_t ptid,
+ CORE_ADDR load_module_addr,
+ CORE_ADDR offset) override;
+
+ ptid_t get_event_ptid (int pid);
+ struct thread_info *attach_thread (struct thread_db_info *info,
+ struct thread_info *tp,
+ ptid_t ptid, const td_thrhandle_t *th_p,
+ const td_thrinfo_t *ti_p);
+ struct thread_info *attach_lwp (struct thread_info *stopped, ptid_t ptid);
+};
+
+thread_db_target::thread_db_target ()
+{
+ this->to_stratum = thread_stratum;
+}
+
+static const char *libthread_db_name = "libthread_db.so";
+static const char *libthread_db_path = "/usr/lib";
+
+/* If non-zero, print details of libthread_db processing. */
+static unsigned int libthread_db_debug;
+
+static void
+show_libthread_db_debug (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("FreeBSD libthread-db debug is %s\n"), value);
+}
+
+/* This module's target vector. */
+static thread_db_target the_thread_db_target;
+
+struct thread_db_info
+{
+ struct thread_db_info *next;
+
+ /* Process id this object refers to. */
+ int pid;
+
+ /* Handle from dlopen for libthread_db.so. */
+ void *handle;
+
+ /* Absolute pathname from gdb_realpath to disk file used for dlopen-ing
+ HANDLE. It may be NULL for system library. */
+ char *filename;
+
+ /* Structure that identifies the child process for the
+ <proc_service.h> interface. */
+ struct ps_prochandle proc_handle;
+
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
+
+ /* True if we need to apply the workaround for glibc/BZ5983. When
+ we catch a PTRACE_O_TRACEFORK, and go query the child's thread
+ list, nptl_db returns the parent's threads in addition to the new
+ (single) child thread. If this flag is set, we do extra work to
+ be able to ignore such stale entries. */
+ int need_stale_parent_threads_check;
+
+ /* Pointers to the libthread_db functions. */
+
+ td_init_ftype *td_init_p;
+ td_ta_new_ftype *td_ta_new_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;
+ td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
+};
+
+/* List of known processes using thread_db, and the required
+ bookkeeping. */
+struct 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,
+ bool until_no_new);
+static int init_threads;
+
+
+/* Add the current inferior to the list of processes using libpthread.
+ Return a pointer to the newly allocated object that was added to
+ THREAD_DB_LIST. HANDLE is the handle returned by dlopen'ing
+ LIBTHREAD_DB_SO. */
+
+static struct thread_db_info *
+add_thread_db_info (void *handle)
+{
+ struct thread_db_info *info = XCNEW (struct thread_db_info);
+
+ 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)
+ info->need_stale_parent_threads_check = 1;
+
+ info->next = thread_db_list;
+ thread_db_list = info;
+
+ return info;
+}
+
+/* Return the thread_db_info object representing the bookkeeping
+ related to process PID, if any; NULL otherwise. */
+
+static struct thread_db_info *
+get_thread_db_info (int pid)
+{
+ struct thread_db_info *info;
+
+ for (info = thread_db_list; info; info = info->next)
+ if (pid == info->pid)
+ return info;
+
+ return NULL;
+}
+
+/* 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)
+{
+ 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)
+ break;
+
+ if (info == NULL)
+ return;
+
+ if (info->handle != NULL)
+ dlclose (info->handle);
+
+ if (info_prev)
+ info_prev->next = info->next;
+ else
+ thread_db_list = info->next;
+
+ xfree (info);
+}
+
+/* Use "struct private_thread_info" to cache thread state. This is
+ a substantial optimization. */
+
+struct thread_db_thread_info : public private_thread_info
+{
+ /* Flag set when we see a TD_DEATH event for this thread. */
+ bool dying = false;
+
+ /* Cached thread state. */
+ td_thrhandle_t th {};
+ thread_t tid {};
+ psaddr_t tls {};
+};
+
+static thread_db_thread_info *
+get_thread_db_thread_info (thread_info *thread)
+{
+ return static_cast<thread_db_thread_info *> (thread->priv.get ());
+}
+
+static const char *
+thread_db_err_str (td_err_e err)
+{
+ static char buf[64];
+
+ switch (err)
+ {
+ case TD_OK:
+ return "generic 'call succeeded'";
+ case TD_ERR:
+ return "generic error";
+ case TD_NOTHR:
+ return "no thread to satisfy query";
+ case TD_NOSV:
+ return "no sync handle to satisfy query";
+ case TD_NOLWP:
+ return "no LWP to satisfy query";
+ case TD_BADPH:
+ return "invalid process handle";
+ case TD_BADTH:
+ return "invalid thread handle";
+ case TD_BADSH:
+ return "invalid synchronization handle";
+ case TD_BADTA:
+ return "invalid thread agent";
+ case TD_BADKEY:
+ return "invalid key";
+ case TD_NOMSG:
+ return "no event message for getmsg";
+ case TD_NOFPREGS:
+ return "FPU register set not available";
+ case TD_NOLIBTHREAD:
+ return "application not linked with libthread";
+ case TD_NOEVENT:
+ return "requested event is not supported";
+ case TD_NOCAPAB:
+ return "capability not available";
+ case TD_DBERR:
+ return "debugger service failed";
+ case TD_NOAPLIC:
+ return "operation not applicable to";
+ case TD_NOTSD:
+ return "no thread-specific data for this thread";
+ case TD_MALLOC:
+ return "malloc failed";
+ case TD_PARTIALREG:
+ return "only part of register set was written/read";
+ case TD_NOXREGS:
+ return "X register set not available for this thread";
+#ifdef THREAD_DB_HAS_TD_NOTALLOC
+ case TD_NOTALLOC:
+ return "thread has not yet allocated TLS for given module";
+#endif
+#ifdef THREAD_DB_HAS_TD_VERSION
+ case TD_VERSION:
+ return "versions of libpthread and libthread_db do not match";
+#endif
+#ifdef THREAD_DB_HAS_TD_NOTLS
+ case TD_NOTLS:
+ return "there is no TLS segment in the given module";
+#endif
+ default:
+ snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
+ return buf;
+ }
+}
+
+static void *
+verbose_dlsym (void *handle, const char *name)
+{
+ void *sym = dlsym (handle, name);
+ if (sym == NULL)
+ warning (_("THR-DB: Symbol \"%s\" not found in libthread_db: %s"),
+ name, dlerror ());
+ return sym;
+}
+
+struct callback_data
+{
+ struct thread_db_info *info;
+ int new_threads;
+};
+
+static void
+update_thread_state (thread_db_thread_info *priv,
+ const td_thrinfo_t *ti_p)
+{
+ priv->dying = (ti_p->ti_state == TD_THR_UNKNOWN
+ || ti_p->ti_state == TD_THR_ZOMBIE);
+}
+
+static void
+core_get_first_lwp_callback (bfd *abfd, asection *asect, void *obj)
+{
+ if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0)
+ return;
+
+ if (*(lwpid_t *) obj != 0)
+ return;
+
+ *(lwpid_t *) obj = atoi (bfd_section_name (abfd, asect) + 5);
+}
+
+static int
+have_threads_callback (struct thread_info *thread, void *args)
+{
+ int pid = * (int *) args;
+
+ if (thread->ptid.pid() != pid)
+ return 0;
+
+ thread_db_thread_info *priv = get_thread_db_thread_info (thread);
+ return priv != NULL;
+}
+
+static int
+find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
+{
+ td_thrinfo_t ti;
+ td_err_e err;
+
+ struct thread_info *tp;
+ struct callback_data *cb_data = (struct callback_data *)data;
+ struct thread_db_info *info = cb_data->info;
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: called\n"), __func__);
+
+ err = info->td_thr_get_info_p (th_p, &ti);
+ if (err != TD_OK)
+ error (_("%s: cannot get thread info: %s"),
+ __func__,
+ thread_db_err_str (err));
+
+ if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
+ return 0; /* A zombie -- ignore. */
+
+ ptid_t ptid (info->pid, ti.ti_lid, 0);
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: %s\n"), __func__, target_pid_to_str(ptid));
+
+ tp = find_thread_ptid (ptid);
+ thread_db_thread_info *priv = get_thread_db_thread_info (tp);
+
+ if (tp == NULL || priv == NULL)
+ if (the_thread_db_target.attach_thread (info, tp, ptid, th_p, &ti))
+ cb_data->new_threads += 1;
+
+ return 0;
+}
+
+void
+thread_db_target::detach (inferior *inf, int from_tty)
+{
+ 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);
+}
+
+/* Attach to a new thread. This function is called when we receive a
+ TD_CREATE event or when we iterate over all threads and find one
+ that wasn't already in our list. Returns thread info on success. */
+
+struct thread_info *
+thread_db_target::attach_thread (struct thread_db_info *info,
+ struct thread_info *tp,
+ ptid_t ptid, const td_thrhandle_t *th_p,
+ const td_thrinfo_t *ti_p)
+{
+ thread_db_thread_info *priv;
+
+ /* A thread ID of zero may mean the thread library has not
+ initialized yet. Leave private == NULL until the thread library
+ has initialized. */
+ if (ti_p->ti_tid == 0)
+ return tp;
+
+ priv = get_thread_db_thread_info (tp);
+ if (priv != NULL)
+ {
+ if (!priv->dying)
+ return NULL;
+
+ delete_thread_db_info (ptid.pid ());
+ delete_thread (find_thread_ptid(ptid));
+ tp = NULL;
+ }
+
+ /* Construct the thread's private data. */
+ priv = new thread_db_thread_info;
+
+ priv->th = *th_p;
+ priv->tid = ti_p->ti_tid;
+ priv->tls = ti_p->ti_tls;
+ update_thread_state (priv, ti_p);
+
+ /* Add the thread to GDB's thread list. If we already know about a
+ 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);
+ else
+ tp->priv.reset(priv);
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, "THR-DB %s: %s \n",
+ __func__,
+ target_pid_to_str(ptid));
+
+ return tp;
+}
+
+/* Attach to lwp PTID, doing whatever else is required to have this
+ LWP under the debugger's control --- e.g., enabling event
+ reporting. Returns true on success. */
+struct thread_info *
+thread_db_target::attach_lwp (struct thread_info *stopped, ptid_t ptid)
+{
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
+ td_err_e err;
+ struct thread_db_info *info;
+ struct thread_info *tp;
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: %s\n"), __func__, target_pid_to_str(ptid));
+
+ gdb_assert (ptid.lwp () != 0);
+ info = get_thread_db_info (ptid.pid ());
+ if (info == NULL)
+ return NULL;
+
+ /* Access an lwp we know is stopped. */
+ info->proc_handle.thread = stopped;
+
+ if (init_threads) {
+ /* Access an lwp we know is stopped. */
+ err = info->td_ta_map_lwp2thr_p (info->thread_agent, ptid.lwp (), &th);
+ if (err != TD_OK) {
+ /* Cannot find user-level thread. */
+ error (_("Cannot find user-level thread for LWP %ld: %s"),
+ ptid.lwp (), thread_db_err_str (err));
+ return NULL;
+ }
+ err = info->td_thr_get_info_p (&th, &ti);
+ if (err != TD_OK)
+ {
+ warning (_("Cannot get thread info: %s"), thread_db_err_str (err));
+ return NULL;
+ }
+ /* Fill the cache. */
+ tp = find_thread_ptid (ptid);
+ return the_thread_db_target.attach_thread (info, tp, ptid, &th, &ti);
+ }
+ return stopped;
+}
+
+void
+thread_db_target::mourn_inferior ()
+{
+ delete_thread_db_info (inferior_ptid.pid ());
+
+ beneath ()->mourn_inferior ();
+
+ /* Detach thread_db target ops. */
+ if (!thread_db_list)
+ unpush_target (&the_thread_db_target);
+}
+
+ptid_t
+thread_db_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options)
+{
+ struct thread_db_info *info;
+ struct thread_info *tp;
+ thread_db_thread_info *priv;
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, "THR-DB %s: waiting for %s\n",
+ __func__,
+ target_pid_to_str (ptid));
+
+ ptid = beneath ()->wait (ptid, ourstatus, options);
+
+ switch (ourstatus->kind)
+ {
+ case TARGET_WAITKIND_IGNORE:
+ case TARGET_WAITKIND_EXITED:
+ case TARGET_WAITKIND_THREAD_EXITED:
+ case TARGET_WAITKIND_SIGNALLED:
+ return ptid;
+ }
+
+ info = get_thread_db_info (ptid.pid ());
+
+ /* If this process isn't using thread_db, we're done. */
+ if (info == NULL)
+ return ptid;
+
+ if (ourstatus->kind == TARGET_WAITKIND_EXECD)
+ {
+ /* 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);
+
+ return ptid;
+ }
+
+ /* Fill in the thread's user-level thread id and status. */
+ tp = find_thread_ptid (ptid);
+ priv = get_thread_db_thread_info (tp);
+ if (tp == NULL || priv == NULL)
+ {
+ tp = attach_lwp (tp, ptid);
+ if (tp == NULL)
+ return ptid;
+ }
+
+ return ptid;
+}
+
+/* Implement the to_update_thread_list target method for this
+ target. */
+
+void
+thread_db_target::update_thread_list ()
+{
+ struct thread_db_info *info;
+ struct inferior *inf;
+
+ prune_threads ();
+
+ ALL_INFERIORS (inf)
+ {
+ struct thread_info *thread;
+
+ if (inf->pid == 0)
+ continue;
+
+ info = get_thread_db_info (inf->pid);
+ if (info == NULL)
+ continue;
+
+ thread = any_live_thread_of_inferior (inf);
+ if (thread == NULL || thread->executing)
+ continue;
+
+ /* 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, or, if the target is running, the list may change
+ while we walk it. In the latter case, it's possible that a
+ thread exits just at the exact time that causes GDB to get
+ stuck in an infinite loop. To avoid pausing all threads
+ whenever the core wants to refresh the thread list, we
+ instead use thread_from_lwp immediately when we see an LWP
+ 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))
+ continue;
+
+ thread_db_find_new_threads_1 (thread);
+ }
+
+ /* Give the beneath target a chance to do extra processing. */
+ this->beneath ()->update_thread_list ();
+}
+
+const char *
+thread_db_target::pid_to_str (ptid_t ptid)
+{
+ struct thread_info *thread_info = find_thread_ptid (ptid);
+
+ if (thread_info != NULL)
+ {
+ thread_db_thread_info *priv = get_thread_db_thread_info (thread_info);
+ if (priv != NULL) {
+ static char buf[64];
+
+ snprintf (buf, sizeof (buf), "Thread (tid %ld) (LWP %ld)",
+ (unsigned long) priv->tid, ptid.lwp ());
+
+ return buf;
+ }
+ }
+ return beneath ()->pid_to_str (ptid);
+}
+
+const char *
+thread_db_target::extra_thread_info (struct thread_info *info)
+{
+ thread_db_thread_info *priv = get_thread_db_thread_info (info);
+ if (priv == NULL)
+ return NULL;
+
+ if (priv->dying)
+ return "Exiting";
+
+ return NULL;
+}
+
+/* Return pointer to the thread_info struct which corresponds to
+ THREAD_HANDLE (having length HANDLE_LEN). */
+
+thread_info *
+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))
+ 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)
+ {
+ thread_db_thread_info *priv = get_thread_db_thread_info (tp);
+
+ if (tp->inf == inf && priv != NULL && handle_tid == priv->tid)
+ return tp;
+ }
+
+ return NULL;
+}
+
+CORE_ADDR
+thread_db_target::get_thread_local_address (ptid_t ptid,
+ CORE_ADDR linkmap,
+ CORE_ADDR offset)
+{
+ struct thread_info *thread_info;
+ thread_db_thread_info *priv;
+ int pid = ptid.pid();
+
+ /* Find the matching thread. */
+ thread_info = find_thread_ptid (ptid);
+
+ /* If we have not discovered any threads yet, check now. */
+ if (iterate_over_threads (have_threads_callback, &pid) == NULL)
+ thread_db_find_new_threads_1 (thread_info);
+
+ /* We may not have discovered the thread yet. */
+ if (thread_info != NULL && thread_info->priv == NULL)
+ thread_info = the_thread_db_target.attach_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.pid ());
+ priv = get_thread_db_thread_info (thread_info);
+
+ if (linkmap == 0x0)
+ {
+ /* This code path handles the case of -static -lpthread executables: */
+ throw_error (TLS_GENERIC_ERROR,
+ (("Failed to handle -static -lpthread executable")));
+ // Below does not seem to work
+ //address = priv->tls + offset;
+ }
+
+ /* glibc doesn't provide the needed interface. */
+ if (!info->td_thr_tls_get_addr_p)
+ throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
+ _("No TLS library support"));
+
+ /* Note the cast through uintptr_t: this interface only works if
+ a target address fits in a psaddr_t, which is a host pointer.
+ So a 32-bit debugger can not access 64-bit TLS through this. */
+ err = info->td_thr_tls_get_addr_p (&priv->th,
+ (psaddr_t)(uintptr_t) linkmap,
+ offset, &address);
+
+#ifdef THREAD_DB_HAS_TD_NOTALLOC
+ /* The memory hasn't been allocated, yet. */
+ if (err == TD_NOTALLOC)
+ /* 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"));
+#endif
+ /* Something else went wrong. */
+ if (err != TD_OK)
+ 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
+ ? (CORE_ADDR) (intptr_t) address
+ : (CORE_ADDR) (uintptr_t) address);
+ }
+ return beneath ()->get_thread_local_address (ptid, linkmap, offset);
+}
+
+/* Implement the to_get_ada_task_ptid target method for this target. */
+
+ptid_t
+thread_db_target::get_ada_task_ptid (long lwp, long thread)
+{
+ return ptid_t (inferior_ptid.pid (), lwp, 0);
+}
+
+
+void
+thread_db_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
+{
+ struct thread_db_info *info;
+
+ if (ptid == minus_one_ptid)
+ info = get_thread_db_info (inferior_ptid.pid ());
+ else
+ 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
+ workaround can be disabled. */
+ if (info)
+ info->need_stale_parent_threads_check = 0;
+
+ beneath ()->resume (ptid, step, signo);
+}
+
+ptid_t
+thread_db_target::get_event_ptid (int pid)
+{
+ struct ptrace_lwpinfo pl;
+ lwpid_t lwpid = 0;
+
+ if (target_has_execution)
+ {
+ if (ptrace (PT_LWPINFO, pid, (caddr_t) &pl, sizeof (pl)))
+ perror_with_name ("PT_LWPINFO");
+
+ return ptid_t (pid, (long) pl.pl_lwpid, 0);
+ }
+ else
+ {
+ bfd_map_over_sections (core_bfd, core_get_first_lwp_callback, &lwpid);
+ return ptid_t (pid, lwpid, 0);
+ }
+}
+
+/* Helper for thread_db_find_new_threads_2.
+ Returns number of new threads found. */
+
+static int
+find_new_threads_once (struct thread_db_info *info, int iteration,
+ td_err_e *errp)
+{
+ struct callback_data data;
+ td_err_e err = TD_ERR;
+
+ data.info = info;
+ data.new_threads = 0;
+
+ /* See comment in thread_db_update_thread_list. */
+ gdb_assert (info->td_ta_thr_iter_p != NULL);
+
+ TRY
+ {
+ /* Iterate over all user-space threads to discover new threads. */
+ err = info->td_ta_thr_iter_p (info->thread_agent,
+ find_new_threads_callback,
+ &data,
+ TD_THR_ANY_STATE,
+ TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK,
+ TD_THR_ANY_USER_FLAGS);
+ }
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ if (libthread_db_debug)
+ {
+ exception_fprintf (gdb_stdlog, except,
+ "Warning: %s: td_ta_thr_iter failed\n", __func__);
+ }
+ }
+ END_CATCH
+ if (libthread_db_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: Found %d new threads in iteration %d\n"),
+ __func__, data.new_threads, iteration);
+ }
+
+ if (errp != NULL)
+ *errp = err;
+
+ return data.new_threads;
+}
+
+
+/* Search for new threads, accessing memory through stopped thread
+ PTID. If UNTIL_NO_NEW is true, repeat searching until several
+ searches in a row do not discover any new threads. */
+
+static void
+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, num_threads = 0;
+
+ info = get_thread_db_info (stopped->ptid.pid ());
+
+ /* Access an lwp we know is stopped. */
+ info->proc_handle.thread = stopped;
+
+ if (until_no_new)
+ {
+ /* Require 4 successive iterations which do not find any new threads.
+ The 4 is a heuristic: there is an inherent race here, and I have
+ seen that 2 iterations in a row are not always sufficient to
+ "capture" all threads. */
+ for (i = 0, loop = 0; loop < 4 && err == TD_OK; ++i, ++loop) {
+ num_threads = find_new_threads_once (info, i, &err);
+ if (num_threads != 0)
+ {
+ /* Found some new threads. Restart the loop from beginning. */
+ loop = -1;
+ }
+ }
+ }
+ else
+ num_threads = find_new_threads_once (info, 0, &err);
+
+ if (err != TD_OK)
+ error (_("Cannot find new threads: %s"), thread_db_err_str (err));
+
+ if (init_threads == 0 && num_threads > 0) init_threads = num_threads;
+}
+
+static void
+thread_db_find_new_threads_1 (thread_info *stopped)
+{
+ thread_db_find_new_threads_2 (stopped, false);
+}
+
+/* Same as thread_db_find_new_threads_1, but silently ignore errors. */
+
+static int
+thread_db_find_new_threads_silently (thread_info *stopped)
+{
+ TRY
+ {
+ thread_db_find_new_threads_2 (stopped, true);
+ }
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ if (libthread_db_debug)
+ exception_fprintf (gdb_stdlog, except,
+ "Warning: %s: ", __func__);
+ return 1;
+ }
+ END_CATCH
+ return 0;
+}
+
+/* Attempt to initialize dlopen()ed libthread_db, described by INFO.
+ Return 1 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
+try_thread_db_load_1 (struct thread_db_info *info)
+{
+ td_err_e err;
+
+#define TDB_VERBOSE_DLSYM(info, func) \
+ info->func ## _p = (func ## _ftype *) verbose_dlsym (info->handle, #func)
+
+#define CHK(a) \
+ do \
+ { \
+ if ((a) == NULL) \
+ return 0; \
+ } while (0)
+
+ /* Initialize pointers to the dynamic library functions we will use.
+ Essential functions first. */
+ CHK (TDB_VERBOSE_DLSYM (info, td_init));
+
+ err = info->td_init_p ();
+ if (err != TD_OK)
+ {
+ warning (_("Cannot initialize libthread_db: %s"),
+ thread_db_err_str (err));
+ return 0;
+ }
+
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_new));
+
+ /* Initialize the structure that identifies the child process. */
+ info->proc_handle.thread = find_thread_ptid (inferior_ptid);
+
+ /* Now attempt to open a connection to the thread library. */
+ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent);
+ if (err != TD_OK)
+ {
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("td_ta_new failed: %s\n"),
+ thread_db_err_str (err));
+ else
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
+#ifdef THREAD_DB_HAS_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));
+ }
+ return 0;
+ }
+
+ /* These are essential. */
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_map_lwp2thr));
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_thr_iter));
+ CHK (TDB_VERBOSE_DLSYM (info, td_thr_get_info));
+ CHK (TDB_VERBOSE_DLSYM (info, td_thr_tls_get_addr));
+
+ printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
+
+ /* 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);
+
+ ptid_t save_ptid = inferior_ptid;
+ inferior_ptid = the_thread_db_target.get_event_ptid (inferior_ptid.pid());
+ thread_change_ptid(save_ptid, inferior_ptid);
+
+ /* 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_thread ());
+
+ return 1;
+}
+
+/* Attempt to use LIBRARY as libthread_db. LIBRARY could be absolute,
+ relative, or just LIBTHREAD_DB. */
+
+static int
+try_thread_db_load (char *library)
+{
+ void *handle;
+ struct thread_db_info *info;
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Trying host libthread_db library: %s\n"),
+ library);
+
+ if (access (library, R_OK) != 0)
+ {
+ /* Do not print warnings by file_is_auto_load_safe if the library does
+ not exist at this place. */
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("open failed: %s.\n"),
+ safe_strerror (errno));
+ return 0;
+ }
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("dlopen failed: %s.\n"), dlerror ());
+ return 0;
+ }
+ info = add_thread_db_info (handle);
+ info->filename = library;
+
+ if (try_thread_db_load_1 (info))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ delete_thread_db_info (inferior_ptid.pid ());
+ return 0;
+}
+
+/* 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)
+{
+ int rc = 0;
+ static char libpath[PATH_MAX];
+
+ sprintf(libpath, "%s/%s", libthread_db_path, libthread_db_name);
+ if (try_thread_db_load (libpath))
+ {
+ rc = 1;
+ }
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: returning %d\n"), __func__, 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 (objfile_name (obj)))
+ 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 (inferior_ptid.pid ());
+
+ 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 () || 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 (_("THR-DB: 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;
+}
+
+/* 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 */
+ 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)
+ 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 ();
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_thread_db;
+
+void
+_initialize_thread_db (void)
+{
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("THR-DB: %s: Debugging enabled by default : %d.\n"),
+ __func__, libthread_db_debug);
+
+ add_setshow_zuinteger_cmd ("libthread-db", class_maintenance,
+ &libthread_db_debug, _("\
+ Set libthread-db debugging."), _("\
+ Show libthread-db debugging."), _("\
+ When non-zero, libthread-db debugging is enabled."),
+ NULL,
+ show_libthread_db_debug,
+ &setdebuglist, &showdebuglist);
+
+ /* Add ourselves to objfile event chain. */
+ gdb::observers::new_objfile.attach (thread_db_new_objfile);
+
+ /* Add ourselves to inferior_created event chain.
+ This is needed to handle debugging statically linked programs where
+ the new_objfile observer won't get called for libpthread. */
+ gdb::observers::inferior_created.attach (thread_db_inferior_created);
+}
+
+/* proc service functions */
+void
+ps_plog (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vfprintf_filtered (gdb_stdlog, fmt, args);
+ va_end (args);
+}
+
+ps_err_e
+ps_pglobal_lookup (struct ps_prochandle *ph, const char *obj,
+ const char *name, psaddr_t *sym_addr)
+{
+ struct bound_minimal_symbol ms;
+ CORE_ADDR addr;
+
+ ms = lookup_bound_minimal_symbol (name);
+ if (ms.minsym == NULL) {
+ fprintf_unfiltered (gdb_stdlog, _("THR-DB %s: Sym : %s not found\n"),
+ __func__, name);
+ return PS_NOSYM;
+ }
+
+ addr = BMSYMBOL_VALUE_ADDRESS (ms);
+ store_typed_address ((gdb_byte *) sym_addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr, addr);
+ return PS_OK;
+}
+
+ps_err_e
+ps_pread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len)
+{
+ int err = target_read_memory (
+ extract_typed_address ((const gdb_byte *) &addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr), (gdb_byte *)buf, len);
+ return (err == 0 ? PS_OK : PS_ERR);
+}
+
+ps_err_e
+ps_pwrite (struct ps_prochandle *ph, psaddr_t addr, const void *buf,
+ size_t len)
+{
+ int err = target_write_memory (
+ extract_typed_address ((const gdb_byte *) &addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr), (const gdb_byte *) buf, len);
+ return (err == 0 ? PS_OK : PS_ERR);
+}
+
+ps_err_e
+ps_lgetregs (struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+
+ /* Target operation isn't lwp aware: replace pid with lwp */
+ inferior_ptid = ptid_t (lwpid, 0, 0);
+ target_fetch_registers (get_current_regcache(), -1);
+ fill_gregset (get_current_regcache(), gregset, -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const prgregset_t gregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ supply_gregset (get_current_regcache(), (const gdb_gregset_t *) gregset);
+ target_store_registers (get_current_regcache(), -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lgetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *fpregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ target_fetch_registers (get_current_regcache(), -1);
+ fill_fpregset (get_current_regcache(), fpregset, -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetfpregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const prfpregset_t *fpregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ supply_fpregset (get_current_regcache(), (gdb_fpregset_t *) fpregset);
+ target_store_registers (get_current_regcache(), -1);
+ return PS_OK;
+}
+
+#ifdef I386_GETXMMREGS
+ps_err_e
+ps_lgetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid, char *xmmregs)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ target_fetch_registers (get_current_regcache (), -1);
+ i387_collect_fxsave (get_current_regcache (), -1, xmmregs);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const char *xmmregs)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ i387_supply_fxsave (get_current_regcache (), -1, xmmregs);
+ target_store_registers (get_current_regcache (), -1);
+ return PS_OK;
+}
+#endif
+
+ps_err_e
+ps_lstop (struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ if (ptrace (PT_SUSPEND, lwpid, 0, 0) == -1)
+ return PS_ERR;
+ return PS_OK;
+}
+
+ps_err_e
+ps_lcontinue (struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ if (ptrace (PT_RESUME, lwpid, 0, 0) == -1)
+ return PS_ERR;
+ return PS_OK;
+}
+
+ps_err_e
+ps_linfo (struct ps_prochandle *ph, lwpid_t lwpid, void *info)
+{
+ if (target_has_execution)
+ {
+ if (ptrace (PT_LWPINFO, lwpid, (caddr_t)info,
+ sizeof (struct ptrace_lwpinfo)) == -1)
+ return PS_ERR;
+ return PS_OK;
+ }
+ else
+ {
+ memset (info, 0, sizeof (struct ptrace_lwpinfo));
+ return PS_OK;
+ }
+}
+
+
diff --git a/gdb/i386-bsd-nat.c b/gdb/i386-bsd-nat.c
index 09ea99f..b9e8ee3 100644
--- a/gdb/i386-bsd-nat.c
+++ b/gdb/i386-bsd-nat.c
@@ -340,3 +340,35 @@ Please report this to <bug-gdb@gnu.org>."),
#endif /* SC_REG_OFFSET */
}
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+ i386bsd_supply_gregset (regcache, (const void *) gregsetp);
+}
+
+/* Fill GDB's register array with the floating-point register values in
+ *FPREGSETP. */
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
+{
+ i387_supply_fsave (regcache, -1, fpregsetp);
+}
+
+void
+fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregsetp, int regno)
+{
+ i386bsd_collect_gregset (regcache, (void *) gregsetp, regno);
+}
+
+/* Fill register REGNO (if it is a floating-point register) in
+ *FPREGSETP with the value in GDB's register array. If REGNO is -1,
+ do this for all registers. */
+
+void
+fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregsetp, int regno)
+{
+ i387_collect_fsave (regcache, regno, fpregsetp);
+}
+
diff --git a/gdb/i386-fbsd-tdep.c b/gdb/i386-fbsd-tdep.c
index 22c8d45..b4b81ca 100644
--- a/gdb/i386-fbsd-tdep.c
+++ b/gdb/i386-fbsd-tdep.c
@@ -416,6 +416,10 @@ i386fbsd4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_gdbarch_iterate_over_regset_sections
(gdbarch, i386fbsd_iterate_over_regset_sections);
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
set_gdbarch_core_read_description (gdbarch,
i386fbsd_core_read_description);
}
--
1.7.9.5
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support
2018-09-15 13:21 [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support Rajendra SY
@ 2018-09-17 17:11 ` John Baldwin
2018-09-19 16:52 ` Rajendra SY
0 siblings, 1 reply; 5+ messages in thread
From: John Baldwin @ 2018-09-17 17:11 UTC (permalink / raw)
To: Rajendra SY, gdb-patches
On 9/15/18 6:21 AM, Rajendra SY wrote:
> Problem:
> Missing libthread_db integration with GDB
>
> Cause:
> GDB missing libthread_db integration on FreeBSD target because of this
> GDB failed to access TLS variable values.
>
> Tests failed:
> - gdb.threads/tls-shared.exp
> - gdb.threads/tls-nodebug-pie.exp
> - gdb.threads/tls-so_extern.exp
> - gdb.threads/tls-var.exp
> - gdb.threads/tls.exp
> - gdb.threads/tls-core.exp
I'll have to take some time to review this. I had hoped to avoid using
libthread_db at all on FreeBSD (it doesn't support AVX or 32-bit processes
on 64-bit kernels, etc.). For TLS I was planning on teaching fbsd-tdep.c
about the various special variables libthread_db knows from rtld and libthr
and using that along with having each FreeBSD architecture provide the
thread pointer register to implement TLS.
We could instead use libthread_db for now, but if so I'd prefer to only use
libthread_db for TLS and not for anything else. This means that in theory
much of the proc-service API can be stubs that should never be called.
I would also be tempted to not have a separate thread-stratum target at all,
but just have the native target in fbsd-nat.c call into fbsd-thread-db as if
it was just a library for things like extra thread info (the pthread_t
pointer value) and TLS.
--
John Baldwin
                                                                           Â
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support
2018-09-17 17:11 ` John Baldwin
@ 2018-09-19 16:52 ` Rajendra SY
2018-09-19 16:59 ` Pedro Alves
0 siblings, 1 reply; 5+ messages in thread
From: Rajendra SY @ 2018-09-19 16:52 UTC (permalink / raw)
To: John Baldwin; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 2031 bytes --]
John,
Thanks for reviewing the patch.
> We could instead use libthread_db for now,
I tried to clean up the thread_db implementation (attached a diff file
for reference) as you suggested.
Please see if this needs further cleanup? All TLS test cases pass except below.
The test case "gdb.threads/tls-core.exp" fails with this new
implementation, I am yet to debug this.
I need some help in fixing "gdb.threads/staticthreads.exp" test case
where executable is "statically" linked.
For statically linked executable linkmap is NULL. How do we get link
map address?
Thanks
Rajendra
On Mon, Sep 17, 2018 at 10:41 PM, John Baldwin <jhb@freebsd.org> wrote:
> On 9/15/18 6:21 AM, Rajendra SY wrote:
>> Problem:
>> Missing libthread_db integration with GDB
>>
>> Cause:
>> GDB missing libthread_db integration on FreeBSD target because of this
>> GDB failed to access TLS variable values.
>>
>> Tests failed:
>> - gdb.threads/tls-shared.exp
>> - gdb.threads/tls-nodebug-pie.exp
>> - gdb.threads/tls-so_extern.exp
>> - gdb.threads/tls-var.exp
>> - gdb.threads/tls.exp
>> - gdb.threads/tls-core.exp
>
> I'll have to take some time to review this. I had hoped to avoid using
> libthread_db at all on FreeBSD (it doesn't support AVX or 32-bit processes
> on 64-bit kernels, etc.). For TLS I was planning on teaching fbsd-tdep.c
> about the various special variables libthread_db knows from rtld and libthr
> and using that along with having each FreeBSD architecture provide the
> thread pointer register to implement TLS.
>
> We could instead use libthread_db for now, but if so I'd prefer to only use
> libthread_db for TLS and not for anything else. This means that in theory
> much of the proc-service API can be stubs that should never be called.
> I would also be tempted to not have a separate thread-stratum target at all,
> but just have the native target in fbsd-nat.c call into fbsd-thread-db as if
> it was just a library for things like extra thread info (the pthread_t
> pointer value) and TLS.
>
> --
> John Baldwin
>
>
[-- Attachment #2: 0001-New-TLS-implementation-for-FreeBSD.patch --]
[-- Type: application/octet-stream, Size: 26716 bytes --]
From 7f6d431e4825d545bda95ac9149fd4e952e98f38 Mon Sep 17 00:00:00 2001
From: Rajendra S Y <syrajendra@syrajendra-mbp.jnpr.net>
Date: Wed, 19 Sep 2018 22:01:39 +0530
Subject: [PATCH] New TLS implementation for FreeBSD
Signed-off-by: Rajendra S Y <syrajendra@syrajendra-mbp.jnpr.net>
---
gdb/amd64-fbsd-nat.c | 43 +++
gdb/configure.nat | 3 +-
gdb/fbsd-nat.c | 8 +
gdb/fbsd-nat.h | 8 +
gdb/fbsd-thread-db.c | 737 +++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/i386-bsd-nat.c | 32 +++
6 files changed, 830 insertions(+), 1 deletion(-)
create mode 100644 gdb/fbsd-thread-db.c
diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 7c7c963485..ae2a02d762 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -35,6 +35,7 @@
#include "amd64-bsd-nat.h"
#include "x86-nat.h"
#include "x86-xstate.h"
+#include "gregset.h"
\f
class amd64_fbsd_nat_target final
@@ -306,3 +307,45 @@ Please report this to <bug-gdb@gnu.org>."),
}
#endif
}
+
+/* Transfering the registers between GDB, inferiors and core files. */
+
+/* Fill GDB's register array with the general-purpose register values
+ in *GREGSETP. */
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregs)
+{
+ amd64_supply_native_gregset (regcache, gregs, -1);
+}
+
+/* Fill register REGNUM (if it is a general-purpose register) in
+ *GREGSETPS with the value in GDB's register array. If REGNUM is -1,
+ do this for all registers. */
+
+void
+fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregs, int regno)
+{
+ amd64_collect_native_gregset (regcache, gregs, regno);
+}
+
+/* Fill GDB's register array with the floating-point register values
+ in *FPREGSETP. */
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregs)
+{
+ amd64_supply_fxsave (regcache, -1, fpregs);
+}
+
+/* Fill register REGNUM (if it is a floating-point register) in
+ *FPREGSETP with the value in GDB's register array. If REGNUM is -1,
+ do this for all registers. */
+
+void
+fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregs,
+ int regno)
+{
+ amd64_collect_fxsave (regcache, regno, fpregs);
+}
+
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 7611266d86..7b53ae319a 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -62,7 +62,8 @@ case ${gdb_host} in
LOADLIBES='-ldl $(RDYNAMIC)'
;;
fbsd*)
- NATDEPFILES='fork-child.o fork-inferior.o inf-ptrace.o fbsd-nat.o'
+ NATDEPFILES='inf-ptrace.o fork-child.o fork-inferior.o fbsd-nat.o \
+ fbsd-thread-db.o'
HAVE_NATIVE_GCORE_HOST=1
LOADLIBES='-lkvm'
;;
diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index a255318d14..3e2157f81d 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -1625,6 +1625,14 @@ fbsd_nat_target::post_attach (int pid)
fbsd_add_threads (pid);
}
+CORE_ADDR
+fbsd_nat_target::get_thread_local_address (ptid_t ptid,
+ CORE_ADDR linkmap,
+ CORE_ADDR offset)
+{
+ return fbsd_thr_db_get_tls_address(ptid, linkmap, offset);
+}
+
#ifdef PL_FLAG_EXEC
/* If the FreeBSD kernel supports PL_FLAG_EXEC, then traced processes
will always stop after exec. */
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index 3e34600e15..120e89b8ce 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -30,6 +30,10 @@
# endif
#endif
+/* Below TLS function is implmented in fbsd-thread-db.c */
+extern CORE_ADDR fbsd_thr_db_get_tls_address (ptid_t ptid, CORE_ADDR linkmap,
+ CORE_ADDR offset);
+
/* A prototype FreeBSD target. */
class fbsd_nat_target : public inf_ptrace_target
@@ -60,6 +64,10 @@ public:
void update_thread_list () override;
+ CORE_ADDR get_thread_local_address (ptid_t ptid,
+ CORE_ADDR load_module_addr,
+ CORE_ADDR offset) override;
+
thread_control_capabilities get_thread_control_capabilities () override
{ return tc_schedlock; }
diff --git a/gdb/fbsd-thread-db.c b/gdb/fbsd-thread-db.c
new file mode 100644
index 0000000000..dcc942d351
--- /dev/null
+++ b/gdb/fbsd-thread-db.c
@@ -0,0 +1,737 @@
+/* libthread_db assisted debugging support.
+
+ Copyright (C) 1999-2018 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include <dlfcn.h>
+#include "gdb_proc_service.h"
+#include "nat/gdb_thread_db.h"
+#include "gdb_vecs.h"
+#include "bfd.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "infrun.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "target.h"
+#include "regcache.h"
+#include "gregset.h"
+#include "solib.h"
+#include "solib-svr4.h"
+#include "gdbcore.h"
+#include "observable.h"
+#include "fbsd-nat.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#if defined(__i386__)
+#include "i387-tdep.h"
+#endif
+
+#if defined (__i386__) && defined (PT_GETXMMREGS)
+#define I386_GETXMMREGS
+#endif
+
+
+static const char *libthread_db_name = "libthread_db.so";
+static const char *libthread_db_path = "/usr/lib";
+
+/* If non-zero, print details of libthread_db processing. */
+static unsigned int libthread_db_debug;
+
+struct thread_db_info
+{
+ struct thread_db_info *next;
+
+ /* Process id this object refers to. */
+ int pid;
+
+ /* Handle from dlopen for libthread_db.so. */
+ void *handle;
+
+ /* Absolute pathname from gdb_realpath to disk file used for dlopen-ing
+ HANDLE. It may be NULL for system library. */
+ char *filename;
+
+ /* Structure that identifies the child process for the
+ <proc_service.h> interface. */
+ struct ps_prochandle proc_handle;
+
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
+
+ /* True if we need to apply the workaround for glibc/BZ5983. When
+ we catch a PTRACE_O_TRACEFORK, and go query the child's thread
+ list, nptl_db returns the parent's threads in addition to the new
+ (single) child thread. If this flag is set, we do extra work to
+ be able to ignore such stale entries. */
+ int need_stale_parent_threads_check;
+
+ /* Pointers to the libthread_db functions. */
+
+ td_init_ftype *td_init_p;
+ td_ta_new_ftype *td_ta_new_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;
+ td_thr_tls_get_addr_ftype *td_thr_tls_get_addr_p;
+};
+
+/* List of known processes using thread_db, and the required
+ bookkeeping. */
+struct thread_db_info *thread_db_list;
+
+/* Add the current inferior to the list of processes using libpthread.
+ Return a pointer to the newly allocated object that was added to
+ THREAD_DB_LIST. HANDLE is the handle returned by dlopen'ing
+ LIBTHREAD_DB_SO. */
+
+static struct thread_db_info *
+add_thread_db_info (void *handle)
+{
+ struct thread_db_info *info = XCNEW (struct thread_db_info);
+
+ 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)
+ info->need_stale_parent_threads_check = 1;
+
+ info->next = thread_db_list;
+ thread_db_list = info;
+
+ return info;
+}
+
+/* Return the thread_db_info object representing the bookkeeping
+ related to process PID, if any; NULL otherwise. */
+
+static struct thread_db_info *
+get_thread_db_info (int pid)
+{
+ struct thread_db_info *info;
+
+ for (info = thread_db_list; info; info = info->next)
+ if (pid == info->pid)
+ return info;
+
+ return NULL;
+}
+
+/* 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)
+{
+ 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)
+ break;
+
+ if (info == NULL)
+ return;
+
+ if (info->handle != NULL)
+ dlclose (info->handle);
+
+ if (info_prev)
+ info_prev->next = info->next;
+ else
+ thread_db_list = info->next;
+
+ xfree (info);
+}
+
+static const char *
+thread_db_err_str (td_err_e err)
+{
+ static char buf[64];
+
+ switch (err)
+ {
+ case TD_OK:
+ return "generic 'call succeeded'";
+ case TD_ERR:
+ return "generic error";
+ case TD_NOTHR:
+ return "no thread to satisfy query";
+ case TD_NOSV:
+ return "no sync handle to satisfy query";
+ case TD_NOLWP:
+ return "no LWP to satisfy query";
+ case TD_BADPH:
+ return "invalid process handle";
+ case TD_BADTH:
+ return "invalid thread handle";
+ case TD_BADSH:
+ return "invalid synchronization handle";
+ case TD_BADTA:
+ return "invalid thread agent";
+ case TD_BADKEY:
+ return "invalid key";
+ case TD_NOMSG:
+ return "no event message for getmsg";
+ case TD_NOFPREGS:
+ return "FPU register set not available";
+ case TD_NOLIBTHREAD:
+ return "application not linked with libthread";
+ case TD_NOEVENT:
+ return "requested event is not supported";
+ case TD_NOCAPAB:
+ return "capability not available";
+ case TD_DBERR:
+ return "debugger service failed";
+ case TD_NOAPLIC:
+ return "operation not applicable to";
+ case TD_NOTSD:
+ return "no thread-specific data for this thread";
+ case TD_MALLOC:
+ return "malloc failed";
+ case TD_PARTIALREG:
+ return "only part of register set was written/read";
+ case TD_NOXREGS:
+ return "X register set not available for this thread";
+#ifdef THREAD_DB_HAS_TD_NOTALLOC
+ case TD_NOTALLOC:
+ return "thread has not yet allocated TLS for given module";
+#endif
+#ifdef THREAD_DB_HAS_TD_VERSION
+ case TD_VERSION:
+ return "versions of libpthread and libthread_db do not match";
+#endif
+#ifdef THREAD_DB_HAS_TD_NOTLS
+ case TD_NOTLS:
+ return "there is no TLS segment in the given module";
+#endif
+ default:
+ snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
+ return buf;
+ }
+}
+
+static void
+core_get_first_lwp_callback (bfd *abfd, asection *asect, void *obj)
+{
+ if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0)
+ return;
+
+ if (*(lwpid_t *) obj != 0)
+ return;
+
+ *(lwpid_t *) obj = atoi (bfd_section_name (abfd, asect) + 5);
+}
+
+CORE_ADDR
+fbsd_thr_db_get_tls_address (ptid_t ptid, CORE_ADDR linkmap, CORE_ADDR offset)
+{
+ td_thrhandle_t th;
+ struct thread_info *thread_info;
+ lwpid_t lwpid = 0;
+
+ if (!target_has_execution)
+ {
+ bfd_map_over_sections (core_bfd, core_get_first_lwp_callback, &lwpid);
+ ptid = ptid_t (ptid.pid(), lwpid, 0);
+ }
+
+ /* Find the matching thread. */
+ thread_info = find_thread_ptid (ptid);
+ if (thread_info != NULL)
+ {
+ td_err_e err;
+ psaddr_t address;
+ thread_db_info *info = get_thread_db_info (ptid.pid ());
+
+ if (linkmap == 0x0)
+ {
+ /* This code path handles the case of -static -lpthread executables: */
+ throw_error (TLS_GENERIC_ERROR,
+ (("Failed to handle -static -lpthread executable")));
+ }
+
+ err = info->td_ta_map_lwp2thr_p (info->thread_agent, ptid.lwp (), &th);
+ if (err != TD_OK) {
+ /* Cannot find user-level thread. */
+ error (_("Cannot find user-level thread for LWP %ld: %s"),
+ ptid.lwp (), thread_db_err_str (err));
+ return 0x0;
+ }
+
+ /* glibc doesn't provide the needed interface. */
+ if (!info->td_thr_tls_get_addr_p)
+ throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
+ _("No TLS library support"));
+
+ /* Note the cast through uintptr_t: this interface only works if
+ a target address fits in a psaddr_t, which is a host pointer.
+ So a 32-bit debugger can not access 64-bit TLS through this. */
+ err = info->td_thr_tls_get_addr_p (&th,
+ (psaddr_t)(uintptr_t) linkmap,
+ offset, &address);
+
+#ifdef THREAD_DB_HAS_TD_NOTALLOC
+ /* The memory hasn't been allocated, yet. */
+ if (err == TD_NOTALLOC)
+ /* 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"));
+#endif
+ /* Something else went wrong. */
+ if (err != TD_OK)
+ throw_error (TLS_GENERIC_ERROR,
+ (("%s")), thread_db_err_str (err));
+
+ /* Do proper sign extension for the target. */
+ gdb_assert (exec_bfd);
+ return (bfd_get_sign_extend_vma (exec_bfd) > 0
+ ? (CORE_ADDR) (intptr_t) address
+ : (CORE_ADDR) (uintptr_t) address);
+ } else {
+ throw_error (TLS_GENERIC_ERROR,
+ (("Failed to get valid thread_info pointer")));
+ }
+ return 0x0;
+}
+
+static void
+show_libthread_db_debug (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("FreeBSD libthread-db debug is %s\n"), value);
+}
+
+static void *
+verbose_dlsym (void *handle, const char *name)
+{
+ void *sym = dlsym (handle, name);
+ if (sym == NULL)
+ warning (_("THR-DB: Symbol \"%s\" not found in libthread_db: %s"),
+ name, dlerror ());
+ return sym;
+}
+
+/* Attempt to initialize dlopen()ed libthread_db, described by INFO.
+ Return 1 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
+try_thread_db_load_1 (struct thread_db_info *info)
+{
+ td_err_e err;
+
+#define TDB_VERBOSE_DLSYM(info, func) \
+ info->func ## _p = (func ## _ftype *) verbose_dlsym (info->handle, #func)
+
+#define CHK(a) \
+ do \
+ { \
+ if ((a) == NULL) \
+ return 0; \
+ } while (0)
+
+ /* Initialize pointers to the dynamic library functions we will use.
+ Essential functions first. */
+ CHK (TDB_VERBOSE_DLSYM (info, td_init));
+
+ err = info->td_init_p ();
+ if (err != TD_OK)
+ {
+ warning (_("Cannot initialize libthread_db: %s"),
+ thread_db_err_str (err));
+ return 0;
+ }
+
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_new));
+
+ /* Initialize the structure that identifies the child process. */
+ info->proc_handle.thread = find_thread_ptid (inferior_ptid);
+
+ /* Now attempt to open a connection to the thread library. */
+ err = info->td_ta_new_p (&info->proc_handle, &info->thread_agent);
+ if (err != TD_OK)
+ {
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("td_ta_new failed: %s\n"),
+ thread_db_err_str (err));
+ else
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
+#ifdef THREAD_DB_HAS_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));
+ }
+ return 0;
+ }
+
+ /* These are essential. */
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_map_lwp2thr));
+ CHK (TDB_VERBOSE_DLSYM (info, td_ta_thr_iter));
+ CHK (TDB_VERBOSE_DLSYM (info, td_thr_get_info));
+ CHK (TDB_VERBOSE_DLSYM (info, td_thr_tls_get_addr));
+
+ printf_unfiltered (_("[Thread debugging using libthread_db enabled]\n"));
+
+ return 1;
+}
+
+/* Attempt to use LIBRARY as libthread_db. LIBRARY could be absolute,
+ relative, or just LIBTHREAD_DB. */
+
+static int
+try_thread_db_load (char *library)
+{
+ void *handle;
+ struct thread_db_info *info;
+
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("Trying host libthread_db library: %s\n"),
+ library);
+
+ if (access (library, R_OK) != 0)
+ {
+ /* Do not print warnings by file_is_auto_load_safe if the library does
+ not exist at this place. */
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("open failed: %s.\n"),
+ safe_strerror (errno));
+ return 0;
+ }
+ handle = dlopen (library, RTLD_NOW);
+ if (handle == NULL)
+ {
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog, _("dlopen failed: %s.\n"), dlerror ());
+ return 0;
+ }
+ info = add_thread_db_info (handle);
+ info->filename = library;
+
+ if (try_thread_db_load_1 (info))
+ return 1;
+
+ /* This library "refused" to work on current inferior. */
+ delete_thread_db_info (inferior_ptid.pid ());
+ return 0;
+}
+
+/* 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)
+{
+ int rc = 0;
+ static char libpath[PATH_MAX];
+
+ sprintf(libpath, "%s/%s", libthread_db_path, libthread_db_name);
+ if (try_thread_db_load (libpath))
+ {
+ rc = 1;
+ }
+ if (libthread_db_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ _("THR-DB %s: returning %d\n"), __func__, 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 (objfile_name (obj)))
+ 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 (inferior_ptid.pid ());
+
+ 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 () || 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 (_("THR-DB: 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;
+}
+
+/* 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 */
+ 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)
+ 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 ();
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_thread_db;
+
+void
+_initialize_thread_db (void)
+{
+ add_setshow_zuinteger_cmd ("libthread-db", class_maintenance,
+ &libthread_db_debug, _("\
+ Set libthread-db debugging."), _("\
+ Show libthread-db debugging."), _("\
+ When non-zero, libthread-db debugging is enabled."),
+ NULL,
+ show_libthread_db_debug,
+ &setdebuglist, &showdebuglist);
+
+ /* Add ourselves to objfile event chain. */
+ gdb::observers::new_objfile.attach (thread_db_new_objfile);
+
+ /* Add ourselves to inferior_created event chain.
+ This is needed to handle debugging statically linked programs where
+ the new_objfile observer won't get called for libpthread. */
+ gdb::observers::inferior_created.attach (thread_db_inferior_created);
+}
+
+/* proc service functions */
+void
+ps_plog (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vfprintf_filtered (gdb_stdlog, fmt, args);
+ va_end (args);
+}
+
+ps_err_e
+ps_pglobal_lookup (struct ps_prochandle *ph, const char *obj,
+ const char *name, psaddr_t *sym_addr)
+{
+ struct bound_minimal_symbol ms;
+ CORE_ADDR addr;
+
+ ms = lookup_bound_minimal_symbol (name);
+ if (ms.minsym == NULL) {
+ if (libthread_db_debug) {
+ fprintf_unfiltered (gdb_stdlog, _("THR-DB %s: Sym : %s not found\n"),
+ __func__, name);
+ }
+ return PS_NOSYM;
+ }
+
+ addr = BMSYMBOL_VALUE_ADDRESS (ms);
+ store_typed_address ((gdb_byte *) sym_addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr, addr);
+ return PS_OK;
+}
+
+ps_err_e
+ps_pread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len)
+{
+ int err = target_read_memory (
+ extract_typed_address ((const gdb_byte *) &addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr), (gdb_byte *)buf, len);
+ return (err == 0 ? PS_OK : PS_ERR);
+}
+
+ps_err_e
+ps_pwrite (struct ps_prochandle *ph, psaddr_t addr, const void *buf,
+ size_t len)
+{
+ int err = target_write_memory (
+ extract_typed_address ((const gdb_byte *) &addr,
+ builtin_type (target_gdbarch ())->builtin_data_ptr), (const gdb_byte *) buf, len);
+ return (err == 0 ? PS_OK : PS_ERR);
+}
+
+ps_err_e
+ps_lgetregs (struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+
+ /* Target operation isn't lwp aware: replace pid with lwp */
+ inferior_ptid = ptid_t (lwpid, 0, 0);
+ target_fetch_registers (get_current_regcache(), -1);
+ fill_gregset (get_current_regcache(), gregset, -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const prgregset_t gregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ supply_gregset (get_current_regcache(), (const gdb_gregset_t *) gregset);
+ target_store_registers (get_current_regcache(), -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lgetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *fpregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ target_fetch_registers (get_current_regcache(), -1);
+ fill_fpregset (get_current_regcache(), fpregset, -1);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetfpregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const prfpregset_t *fpregset)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ supply_fpregset (get_current_regcache(), (gdb_fpregset_t *) fpregset);
+ target_store_registers (get_current_regcache(), -1);
+ return PS_OK;
+}
+
+#ifdef I386_GETXMMREGS
+ps_err_e
+ps_lgetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid, char *xmmregs)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ target_fetch_registers (get_current_regcache (), -1);
+ i387_collect_fxsave (get_current_regcache (), -1, xmmregs);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetxmmregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const char *xmmregs)
+{
+ scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+ inferior_ptid = ptid_t (inferior_ptid.pid (), lwpid, 0);
+ i387_supply_fxsave (get_current_regcache (), -1, xmmregs);
+ target_store_registers (get_current_regcache (), -1);
+ return PS_OK;
+}
+#endif
+
+ps_err_e
+ps_lstop (struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ if (ptrace (PT_SUSPEND, lwpid, 0, 0) == -1)
+ return PS_ERR;
+ return PS_OK;
+}
+
+ps_err_e
+ps_lcontinue (struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ if (ptrace (PT_RESUME, lwpid, 0, 0) == -1)
+ return PS_ERR;
+ return PS_OK;
+}
+
+ps_err_e
+ps_linfo (struct ps_prochandle *ph, lwpid_t lwpid, void *info)
+{
+ if (target_has_execution)
+ {
+ if (ptrace (PT_LWPINFO, lwpid, (caddr_t)info,
+ sizeof (struct ptrace_lwpinfo)) == -1)
+ return PS_ERR;
+ return PS_OK;
+ }
+ else
+ {
+ memset (info, 0, sizeof (struct ptrace_lwpinfo));
+ return PS_OK;
+ }
+}
diff --git a/gdb/i386-bsd-nat.c b/gdb/i386-bsd-nat.c
index 09ea99fd19..b9e8ee3a53 100644
--- a/gdb/i386-bsd-nat.c
+++ b/gdb/i386-bsd-nat.c
@@ -340,3 +340,35 @@ Please report this to <bug-gdb@gnu.org>."),
#endif /* SC_REG_OFFSET */
}
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+ i386bsd_supply_gregset (regcache, (const void *) gregsetp);
+}
+
+/* Fill GDB's register array with the floating-point register values in
+ *FPREGSETP. */
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
+{
+ i387_supply_fsave (regcache, -1, fpregsetp);
+}
+
+void
+fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregsetp, int regno)
+{
+ i386bsd_collect_gregset (regcache, (void *) gregsetp, regno);
+}
+
+/* Fill register REGNO (if it is a floating-point register) in
+ *FPREGSETP with the value in GDB's register array. If REGNO is -1,
+ do this for all registers. */
+
+void
+fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregsetp, int regno)
+{
+ i387_collect_fsave (regcache, regno, fpregsetp);
+}
+
--
2.14.3 (Apple Git-98)
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support
2018-09-19 16:52 ` Rajendra SY
@ 2018-09-19 16:59 ` Pedro Alves
2018-09-20 4:38 ` Rajendra SY
0 siblings, 1 reply; 5+ messages in thread
From: Pedro Alves @ 2018-09-19 16:59 UTC (permalink / raw)
To: Rajendra SY, John Baldwin; +Cc: gdb-patches
On 09/19/2018 05:52 PM, Rajendra SY wrote:
> + /* True if we need to apply the workaround for glibc/BZ5983. When
> + we catch a PTRACE_O_TRACEFORK, and go query the child's thread
> + list, nptl_db returns the parent's threads in addition to the new
> + (single) child thread. If this flag is set, we do extra work to
> + be able to ignore such stale entries. */
> + int need_stale_parent_threads_check;
Surely FreeBSD doesn't need a workaround for a glibc bug.
There are other references to glibc in the file.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support
2018-09-19 16:59 ` Pedro Alves
@ 2018-09-20 4:38 ` Rajendra SY
0 siblings, 0 replies; 5+ messages in thread
From: Rajendra SY @ 2018-09-20 4:38 UTC (permalink / raw)
To: Pedro Alves; +Cc: John Baldwin, gdb-patches
Pedro,
I'll clean up the glibc workaround code.
Thanks
Regards,
Rajendra
On Wed, Sep 19, 2018 at 10:29 PM, Pedro Alves <palves@redhat.com> wrote:
> On 09/19/2018 05:52 PM, Rajendra SY wrote:
>> + /* True if we need to apply the workaround for glibc/BZ5983. When
>> + we catch a PTRACE_O_TRACEFORK, and go query the child's thread
>> + list, nptl_db returns the parent's threads in addition to the new
>> + (single) child thread. If this flag is set, we do extra work to
>> + be able to ignore such stale entries. */
>> + int need_stale_parent_threads_check;
>
> Surely FreeBSD doesn't need a workaround for a glibc bug.
>
> There are other references to glibc in the file.
>
> Thanks,
> Pedro Alves
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2018-09-20 4:38 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-15 13:21 [PATCH] [PR 23660] On FreeBSD platform missing implementation of thread-db support Rajendra SY
2018-09-17 17:11 ` John Baldwin
2018-09-19 16:52 ` Rajendra SY
2018-09-19 16:59 ` Pedro Alves
2018-09-20 4:38 ` Rajendra SY
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).