public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [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).