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"
 
 
 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)

