gdb/fortran: Don't include module symbols when searching for types
[deliverable/binutils-gdb.git] / gdb / darwin-nat.c
index dbcf77d0c2731987604f5c1c16c0857360c706a1..8f71def069de255a2f6f2fa90684bf23a8b9edb6 100644 (file)
@@ -1,5 +1,5 @@
 /* Darwin support for GDB, the GNU debugger.
-   Copyright (C) 2008-2018 Free Software Foundation, Inc.
+   Copyright (C) 2008-2019 Free Software Foundation, Inc.
 
    Contributed by AdaCore.
 
@@ -37,7 +37,9 @@
 #include "arch-utils.h"
 #include "bfd.h"
 #include "bfd/mach-o.h"
+#include "gdbarch.h"
 
+#include <copyfile.h>
 #include <sys/ptrace.h>
 #include <sys/signal.h>
 #include <setjmp.h>
 #include <mach/port.h>
 
 #include "darwin-nat.h"
-#include "common/filestuff.h"
+#include "filenames.h"
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/gdb_unlinker.h"
+#include "gdbsupport/pathstuff.h"
+#include "gdbsupport/scoped_fd.h"
 #include "nat/fork-inferior.h"
 
 /* Quick overview.
@@ -97,9 +103,6 @@ static void darwin_deallocate_exception_ports (darwin_inferior *inf);
 static void darwin_setup_exceptions (struct inferior *inf);
 static void darwin_deallocate_threads (struct inferior *inf);
 
-/* Target operations for Darwin.  */
-static struct target_ops *darwin_ops;
-
 /* Task identifier of gdb.  */
 static task_t gdb_task;
 
@@ -122,6 +125,10 @@ static int enable_mach_exceptions;
 /* Inferior that should report a fake stop event.  */
 static struct inferior *darwin_inf_fake_stop;
 
+/* If non-NULL, the shell we actually invoke.  See maybe_cache_shell
+   for details.  */
+static const char *copied_shell;
+
 #define PAGE_TRUNC(x) ((x) & ~(mach_page_size - 1))
 #define PAGE_ROUND(x) PAGE_TRUNC((x) + mach_page_size - 1)
 
@@ -262,6 +269,9 @@ darwin_check_new_threads (struct inferior *inf)
   darwin_inferior *darwin_inf = get_darwin_inferior (inf);
   std::vector<darwin_thread_t *> new_thread_vec;
 
+  if (darwin_inf == nullptr)
+    return;
+
   /* Get list of threads.  */
   kret = task_threads (darwin_inf->task, &thread_list, &new_nbr);
   MACH_CHECK_ERROR (kret);
@@ -345,7 +355,7 @@ darwin_check_new_threads (struct inferior *inf)
          pti->msg_state = DARWIN_RUNNING;
 
          /* Add the new thread.  */
-         add_thread_with_info (ptid_build (inf->pid, 0, new_id), pti);
+         add_thread_with_info (ptid_t (inf->pid, 0, new_id), pti);
          new_thread_vec.push_back (pti);
          new_ix++;
          continue;
@@ -353,7 +363,9 @@ darwin_check_new_threads (struct inferior *inf)
       if (old_ix < old_nbr && (new_ix == new_nbr || new_id > old_id))
        {
          /* A thread was removed.  */
-         delete_thread (ptid_build (inf->pid, 0, old_id));
+         struct thread_info *thr
+           = find_thread_ptid (ptid_t (inf->pid, 0, old_id));
+         delete_thread (thr);
          kret = mach_port_deallocate (gdb_task, old_id);
          MACH_CHECK_ERROR (kret);
          old_ix++;
@@ -375,7 +387,7 @@ find_inferior_task_it (struct inferior *inf, void *port_ptr)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  return priv->task == *(task_t *)port_ptr;
+  return priv != nullptr && priv->task == *(task_t *)port_ptr;
 }
 
 static int
@@ -404,11 +416,12 @@ darwin_find_thread (struct inferior *inf, thread_t thread)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  for (darwin_thread_t *t : priv->threads)
-    {
-      if (t->gdb_port == thread)
-       return t;
-    }
+  if (priv != nullptr)
+    for (darwin_thread_t *t : priv->threads)
+      {
+       if (t->gdb_port == thread)
+         return t;
+      }
 
   return NULL;
 }
@@ -420,7 +433,7 @@ darwin_suspend_inferior (struct inferior *inf)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  if (!priv->suspended)
+  if (priv != nullptr && !priv->suspended)
     {
       kern_return_t kret;
 
@@ -438,7 +451,7 @@ darwin_resume_inferior (struct inferior *inf)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  if (priv->suspended)
+  if (priv != nullptr && priv->suspended)
     {
       kern_return_t kret;
 
@@ -675,7 +688,6 @@ darwin_decode_exception_message (mach_msg_header_t *hdr,
       /* Not a known inferior.  This could happen if the child fork, as
         the created process will inherit its exception port.
         FIXME: should the exception port be restored ?  */
-      kern_return_t kret;
       mig_reply_error_t reply;
 
       inferior_debug
@@ -743,11 +755,7 @@ darwin_decode_notify_message (mach_msg_header_t *hdr, struct inferior **pinf)
   NDR_record_t *ndr = (NDR_record_t *)(hdr + 1);
   integer_t *data = (integer_t *)(ndr + 1);
   struct inferior *inf;
-  darwin_thread_t *thread;
   task_t task_port;
-  thread_t thread_port;
-  kern_return_t kret;
-  int i;
 
   /* Check message header.  */
   if (hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX)
@@ -767,11 +775,13 @@ darwin_decode_notify_message (mach_msg_header_t *hdr, struct inferior **pinf)
   inf = darwin_find_inferior_by_task (task_port);
   *pinf = inf;
 
-  darwin_inferior *priv = get_darwin_inferior (inf);
-
   /* Check message destination.  */
-  if (inf != NULL && hdr->msgh_local_port != priv->notify_port)
-    return -4;
+  if (inf != NULL)
+    {
+      darwin_inferior *priv = get_darwin_inferior (inf);
+      if (hdr->msgh_local_port != priv->notify_port)
+       return -4;
+    }
 
   return 0;
 }
@@ -810,13 +820,24 @@ darwin_send_reply (struct inferior *inf, darwin_thread_t *thread)
   priv->pending_messages--;
 }
 
+/* Wrapper around the __pthread_kill syscall.  We use this instead of the
+   pthread_kill function to be able to send a signal to any kind of thread,
+   including GCD threads.  */
+
+static int
+darwin_pthread_kill (darwin_thread_t *thread, int nsignal)
+{
+  DIAGNOSTIC_PUSH;
+  DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS;
+  int res = syscall (SYS___pthread_kill, thread->gdb_port, nsignal);
+  DIAGNOSTIC_POP;
+  return res;
+}
+
 static void
 darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread,
                      int step, int nsignal)
 {
-  kern_return_t kret;
-  int res;
-
   inferior_debug
     (3, _("darwin_resume_thread: state=%d, thread=0x%x, step=%d nsignal=%d\n"),
      thread->msg_state, thread->gdb_port, step, nsignal);
@@ -828,8 +849,8 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread,
          && thread->event.ex_data[0] == EXC_SOFT_SIGNAL)
        {
          /* Either deliver a new signal or cancel the signal received.  */
-         res = PTRACE (PT_THUPDATE, inf->pid,
-                       (caddr_t) (uintptr_t) thread->gdb_port, nsignal);
+         int res = PTRACE (PT_THUPDATE, inf->pid,
+                           (caddr_t) (uintptr_t) thread->gdb_port, nsignal);
          if (res < 0)
            inferior_debug (1, _("ptrace THUP: res=%d\n"), res);
        }
@@ -837,7 +858,7 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread,
        {
          /* Note: ptrace is allowed only if the process is stopped.
             Directly send the signal to the thread.  */
-         res = syscall (SYS___pthread_kill, thread->gdb_port, nsignal);
+         int res = darwin_pthread_kill (thread, nsignal);
          inferior_debug (4, _("darwin_resume_thread: kill 0x%x %d: %d\n"),
                          thread->gdb_port, nsignal, res);
          thread->signaled = 1;
@@ -857,7 +878,7 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread,
       break;
 
     case DARWIN_STOPPED:
-      kret = thread_resume (thread->gdb_port);
+      kern_return_t kret = thread_resume (thread->gdb_port);
       MACH_CHECK_ERROR (kret);
 
       thread->msg_state = DARWIN_RUNNING;
@@ -872,8 +893,9 @@ darwin_resume_inferior_threads (struct inferior *inf, int step, int nsignal)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  for (darwin_thread_t *thread : priv->threads)
-    darwin_resume_thread (inf, thread, step, nsignal);
+  if (priv != nullptr)
+    for (darwin_thread_t *thread : priv->threads)
+      darwin_resume_thread (inf, thread, step, nsignal);
 }
 
 struct resume_inferior_threads_param
@@ -922,16 +944,12 @@ void
 darwin_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
 {
   struct target_waitstatus status;
-  int pid;
 
-  kern_return_t kret;
-  int res;
   int nsignal;
-  struct inferior *inf;
 
   inferior_debug
     (2, _("darwin_resume: pid=%d, tid=0x%lx, step=%d, signal=%d\n"),
-     ptid_get_pid (ptid), ptid_get_tid (ptid), step, signal);
+     ptid.pid (), ptid.tid (), step, signal);
 
   if (signal == GDB_SIGNAL_0)
     nsignal = 0;
@@ -943,7 +961,7 @@ darwin_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
     ptid = inferior_ptid;
 
   /* minus_one_ptid is RESUME_ALL.  */
-  if (ptid_equal (ptid, minus_one_ptid))
+  if (ptid == minus_one_ptid)
     {
       struct resume_inferior_threads_param param;
 
@@ -958,7 +976,7 @@ darwin_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
   else
     {
       struct inferior *inf = find_inferior_ptid (ptid);
-      long tid = ptid_get_tid (ptid);
+      long tid = ptid.tid ();
 
       /* Stop the inferior (should be useless).  */
       darwin_suspend_inferior (inf);
@@ -1075,7 +1093,7 @@ darwin_decode_message (mach_msg_header_t *hdr,
          break;
        }
 
-      return ptid_build (inf->pid, 0, thread->gdb_port);
+      return ptid_t (inf->pid, 0, thread->gdb_port);
     }
   else if (hdr->msgh_id == 0x48)
     {
@@ -1107,14 +1125,14 @@ darwin_decode_message (mach_msg_header_t *hdr,
 
          if (!priv->no_ptrace)
            {
-             pid_t res;
+             pid_t res_pid;
              int wstatus;
 
-             res = wait4 (inf->pid, &wstatus, 0, NULL);
-             if (res < 0 || res != inf->pid)
+             res_pid = wait4 (inf->pid, &wstatus, 0, NULL);
+             if (res_pid < 0 || res_pid != inf->pid)
                {
                  printf_unfiltered (_("wait4: res=%d: %s\n"),
-                                    res, safe_strerror (errno));
+                                    res_pid, safe_strerror (errno));
                  status->kind = TARGET_WAITKIND_IGNORE;
                  return minus_one_ptid;
                }
@@ -1130,12 +1148,12 @@ darwin_decode_message (mach_msg_header_t *hdr,
                }
 
              inferior_debug (4, _("darwin_wait: pid=%d exit, status=0x%x\n"),
-                             res, wstatus);
+                             res_pid, wstatus);
 
              /* Looks necessary on Leopard and harmless...  */
              wait4 (inf->pid, &wstatus, 0, NULL);
 
-             inferior_ptid = ptid_build (inf->pid, 0, 0);
+             inferior_ptid = ptid_t (inf->pid, 0, 0);
              return inferior_ptid;
            }
          else
@@ -1143,7 +1161,7 @@ darwin_decode_message (mach_msg_header_t *hdr,
              inferior_debug (4, _("darwin_wait: pid=%d\n"), inf->pid);
              status->kind = TARGET_WAITKIND_EXITED;
              status->value.integer = 0; /* Don't know.  */
-             return ptid_build (inf->pid, 0, 0);
+             return ptid_t (inf->pid, 0, 0);
            }
        }
     }
@@ -1173,7 +1191,7 @@ cancel_breakpoint (ptid_t ptid)
   if (breakpoint_inserted_here_p (regcache->aspace (), pc))
     {
       inferior_debug (4, "cancel_breakpoint for thread 0x%lx\n",
-                     (unsigned long) ptid_get_tid (ptid));
+                     (unsigned long) ptid.tid ());
 
       /* Back up the PC if necessary.  */
       if (gdbarch_decr_pc_after_break (gdbarch))
@@ -1200,7 +1218,7 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status)
 
   inferior_debug
     (2, _("darwin_wait: waiting for a message pid=%d thread=%lx\n"),
-     ptid_get_pid (ptid), ptid_get_tid (ptid));
+     ptid.pid (), ptid.tid ());
 
   /* Handle fake stop events at first.  */
   if (darwin_inf_fake_stop != NULL)
@@ -1214,7 +1232,7 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status)
       status->value.sig = GDB_SIGNAL_TRAP;
       thread = priv->threads[0];
       thread->msg_state = DARWIN_STOPPED;
-      return ptid_build (inf->pid, 0, thread->gdb_port);
+      return ptid_t (inf->pid, 0, thread->gdb_port);
     }
 
   do
@@ -1245,7 +1263,7 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status)
        darwin_dump_message (hdr, darwin_debug_flag > 11);
 
       res = darwin_decode_message (hdr, &thread, &inf, status);
-      if (ptid_equal (res, minus_one_ptid))
+      if (res == minus_one_ptid)
        continue;
 
       /* Early return in case an inferior has exited.  */
@@ -1286,7 +1304,7 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status)
          && thread->event.ex_type == EXC_BREAKPOINT)
        {
          if (thread->single_step
-             || cancel_breakpoint (ptid_build (inf->pid, 0, thread->gdb_port)))
+             || cancel_breakpoint (ptid_t (inf->pid, 0, thread->gdb_port)))
            {
              gdb_assert (thread->msg_state == DARWIN_MESSAGE);
              darwin_send_reply (inf, thread);
@@ -1344,7 +1362,6 @@ darwin_nat_target::mourn_inferior ()
   darwin_inferior *priv = get_darwin_inferior (inf);
   kern_return_t kret;
   mach_port_t prev;
-  int i;
 
   /* Deallocate threads.  */
   darwin_deallocate_threads (inf);
@@ -1403,8 +1420,6 @@ darwin_stop_inferior (struct inferior *inf)
 {
   struct target_waitstatus wstatus;
   ptid_t ptid;
-  kern_return_t kret;
-  int status;
   int res;
   darwin_inferior *priv = get_darwin_inferior (inf);
 
@@ -1485,7 +1500,6 @@ darwin_setup_exceptions (struct inferior *inf)
 {
   darwin_inferior *priv = get_darwin_inferior (inf);
   kern_return_t kret;
-  int traps_expected;
   exception_mask_t mask;
 
   kret = darwin_save_exception_ports (priv);
@@ -1515,10 +1529,9 @@ darwin_nat_target::kill ()
   struct target_waitstatus wstatus;
   ptid_t ptid;
   kern_return_t kret;
-  int status;
   int res;
 
-  if (ptid_equal (inferior_ptid, null_ptid))
+  if (inferior_ptid == null_ptid)
     return;
 
   gdb_assert (inf != NULL);
@@ -1532,6 +1545,23 @@ darwin_nat_target::kill ()
 
   if (res == 0)
     {
+      /* On MacOS version Sierra, the darwin_restore_exception_ports call
+         does not work as expected.
+         When the kill function is called, the SIGKILL signal is received
+         by gdb whereas it should have been received by the kernel since
+         the exception ports have been restored.
+         This behavior is not the expected one thus gdb does not reply to
+         the received SIGKILL message. This situation leads to a "busy"
+         resource from the kernel point of view and the inferior is never
+         released, causing it to remain as a zombie process, even after
+        GDB exits.
+         To work around this, we mark all the threads of the inferior as
+         signaled thus darwin_decode_message function knows that the kill
+         signal was sent by gdb and will take the appropriate action
+         (cancel signal and reply to the signal message).  */
+      for (darwin_thread_t *thread : priv->threads)
+        thread->signaled = 1;
+
       darwin_resume_inferior (inf);
 
       ptid = darwin_wait (inferior_ptid, &wstatus);
@@ -1576,86 +1606,96 @@ static void
 darwin_attach_pid (struct inferior *inf)
 {
   kern_return_t kret;
-  mach_port_t prev_port;
-  int traps_expected;
-  mach_port_t prev_not;
-  exception_mask_t mask;
 
   darwin_inferior *priv = new darwin_inferior;
   inf->priv.reset (priv);
 
-  kret = task_for_pid (gdb_task, inf->pid, &priv->task);
-  if (kret != KERN_SUCCESS)
+  try
     {
-      int status;
-
-      if (!inf->attach_flag)
+      kret = task_for_pid (gdb_task, inf->pid, &priv->task);
+      if (kret != KERN_SUCCESS)
        {
-         kill (inf->pid, 9);
-         waitpid (inf->pid, &status, 0);
-       }
+         int status;
+
+         if (!inf->attach_flag)
+           {
+             kill (inf->pid, 9);
+             waitpid (inf->pid, &status, 0);
+           }
 
-      error (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n"
+         error
+           (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n"
               " (please check gdb is codesigned - see taskgated(8))"),
-             inf->pid, mach_error_string (kret), (unsigned long) kret);
-    }
+            inf->pid, mach_error_string (kret), (unsigned long) kret);
+       }
 
-  inferior_debug (2, _("inferior task: 0x%x, pid: %d\n"),
-                 priv->task, inf->pid);
+      inferior_debug (2, _("inferior task: 0x%x, pid: %d\n"),
+                     priv->task, inf->pid);
 
-  if (darwin_ex_port == MACH_PORT_NULL)
-    {
-      /* Create a port to get exceptions.  */
-      kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE,
-                                &darwin_ex_port);
-      if (kret != KERN_SUCCESS)
-       error (_("Unable to create exception port, mach_port_allocate "
-                "returned: %d"),
-              kret);
+      if (darwin_ex_port == MACH_PORT_NULL)
+       {
+         /* Create a port to get exceptions.  */
+         kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE,
+                                    &darwin_ex_port);
+         if (kret != KERN_SUCCESS)
+           error (_("Unable to create exception port, mach_port_allocate "
+                    "returned: %d"),
+                  kret);
 
-      kret = mach_port_insert_right (gdb_task, darwin_ex_port, darwin_ex_port,
-                                    MACH_MSG_TYPE_MAKE_SEND);
-      if (kret != KERN_SUCCESS)
-       error (_("Unable to create exception port, mach_port_insert_right "
-                "returned: %d"),
-              kret);
+         kret = mach_port_insert_right (gdb_task, darwin_ex_port,
+                                        darwin_ex_port,
+                                        MACH_MSG_TYPE_MAKE_SEND);
+         if (kret != KERN_SUCCESS)
+           error (_("Unable to create exception port, mach_port_insert_right "
+                    "returned: %d"),
+                  kret);
+
+         /* Create a port set and put ex_port in it.  */
+         kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_PORT_SET,
+                                    &darwin_port_set);
+         if (kret != KERN_SUCCESS)
+           error (_("Unable to create port set, mach_port_allocate "
+                    "returned: %d"),
+                  kret);
+
+         kret = mach_port_move_member (gdb_task, darwin_ex_port,
+                                       darwin_port_set);
+         if (kret != KERN_SUCCESS)
+           error (_("Unable to move exception port into new port set, "
+                    "mach_port_move_member\n"
+                    "returned: %d"),
+                  kret);
+       }
 
-      /* Create a port set and put ex_port in it.  */
-      kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_PORT_SET,
-                                &darwin_port_set);
+      /* Create a port to be notified when the child task terminates.  */
+      kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE,
+                                &priv->notify_port);
       if (kret != KERN_SUCCESS)
-       error (_("Unable to create port set, mach_port_allocate "
+       error (_("Unable to create notification port, mach_port_allocate "
                 "returned: %d"),
               kret);
 
-      kret = mach_port_move_member (gdb_task, darwin_ex_port, darwin_port_set);
+      kret = mach_port_move_member (gdb_task,
+                                   priv->notify_port, darwin_port_set);
       if (kret != KERN_SUCCESS)
-       error (_("Unable to move exception port into new port set, "
+       error (_("Unable to move notification port into new port set, "
                 "mach_port_move_member\n"
                 "returned: %d"),
               kret);
-    }
 
-  /* Create a port to be notified when the child task terminates.  */
-  kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE,
-                            &priv->notify_port);
-  if (kret != KERN_SUCCESS)
-    error (_("Unable to create notification port, mach_port_allocate "
-            "returned: %d"),
-          kret);
-
-  kret = mach_port_move_member (gdb_task,
-                               priv->notify_port, darwin_port_set);
-  if (kret != KERN_SUCCESS)
-    error (_("Unable to move notification port into new port set, "
-            "mach_port_move_member\n"
-            "returned: %d"),
-          kret);
+      darwin_setup_request_notification (inf);
 
-  darwin_setup_request_notification (inf);
+      darwin_setup_exceptions (inf);
+    }
+  catch (const gdb_exception &ex)
+    {
+      exit_inferior (inf);
+      inferior_ptid = null_ptid;
 
-  darwin_setup_exceptions (inf);
+      throw;
+    }
 
+  target_ops *darwin_ops = get_native_target ();
   if (!target_is_pushed (darwin_ops))
     push_target (darwin_ops);
 }
@@ -1665,19 +1705,15 @@ darwin_attach_pid (struct inferior *inf)
 static struct thread_info *
 thread_info_from_private_thread_info (darwin_thread_info *pti)
 {
-  struct thread_info *it;
-
-  ALL_THREADS (it)
+  for (struct thread_info *it : all_threads ())
     {
       darwin_thread_info *iter_pti = get_darwin_thread_info (it);
 
       if (iter_pti->gdb_port == pti->gdb_port)
-       break;
+       return it;
     }
 
-  gdb_assert (it != NULL);
-
-  return it;
+  gdb_assert_not_reached ("did not find gdb thread for darwin thread");
 }
 
 static void
@@ -1750,10 +1786,6 @@ darwin_pre_ptrace (void)
 static void
 darwin_ptrace_him (int pid)
 {
-  task_t itask;
-  kern_return_t kret;
-  mach_port_t prev_port;
-  int traps_expected;
   struct inferior *inf = current_inferior ();
 
   darwin_attach_pid (inf);
@@ -1804,14 +1836,169 @@ darwin_execvp (const char *file, char * const argv[], char * const env[])
   posix_spawnp (NULL, argv[0], NULL, &attr, argv, env);
 }
 
+/* Read kernel version, and return TRUE if this host may have System
+   Integrity Protection (Sierra or later).  */
+
+static bool
+may_have_sip ()
+{
+  char str[16];
+  size_t sz = sizeof (str);
+  int ret;
+
+  ret = sysctlbyname ("kern.osrelease", str, &sz, NULL, 0);
+  if (ret == 0 && sz < sizeof (str))
+    {
+      unsigned long ver = strtoul (str, NULL, 10);
+      if (ver >= 16)
+        return true;
+    }
+  return false;
+}
+
+/* A helper for maybe_cache_shell.  This copies the shell to the
+   cache.  It will throw an exception on any failure.  */
+
+static void
+copy_shell_to_cache (const char *shell, const std::string &new_name)
+{
+  scoped_fd from_fd (gdb_open_cloexec (shell, O_RDONLY, 0));
+  if (from_fd.get () < 0)
+    error (_("Could not open shell (%s) for reading: %s"),
+          shell, safe_strerror (errno));
+
+  std::string new_dir = ldirname (new_name.c_str ());
+  if (!mkdir_recursive (new_dir.c_str ()))
+    error (_("Could not make cache directory \"%s\": %s"),
+          new_dir.c_str (), safe_strerror (errno));
+
+  gdb::char_vector temp_name = make_temp_filename (new_name);
+  scoped_fd to_fd (gdb_mkostemp_cloexec (&temp_name[0]));
+  gdb::unlinker unlink_file_on_error (temp_name.data ());
+
+  if (to_fd.get () < 0)
+    error (_("Could not open temporary file \"%s\" for writing: %s"),
+          temp_name.data (), safe_strerror (errno));
+
+  if (fcopyfile (from_fd.get (), to_fd.get (), nullptr,
+                COPYFILE_STAT | COPYFILE_DATA) != 0)
+    error (_("Could not copy shell to cache as \"%s\": %s"),
+          temp_name.data (), safe_strerror (errno));
+
+  /* Be sure that the caching is atomic so that we don't get bad
+     results from multiple copies of gdb running at the same time.  */
+  if (rename (temp_name.data (), new_name.c_str ()) != 0)
+    error (_("Could not rename shell cache file to \"%s\": %s"),
+          new_name.c_str (), safe_strerror (errno));
+
+  unlink_file_on_error.keep ();
+}
+
+/* If $SHELL is restricted, try to cache a copy.  Starting with El
+   Capitan, macOS introduced System Integrity Protection.  Among other
+   things, this prevents certain executables from being ptrace'd.  In
+   particular, executables in /bin, like most shells, are affected.
+   To work around this, while preserving command-line glob expansion
+   and redirections, gdb will cache a copy of the shell.  Return true
+   if all is well -- either the shell is not subject to SIP or it has
+   been successfully cached.  Returns false if something failed.  */
+
+static bool
+maybe_cache_shell ()
+{
+  /* SF_RESTRICTED is defined in sys/stat.h and lets us determine if a
+     given file is subject to SIP.  */
+#ifdef SF_RESTRICTED
+
+  /* If a check fails we want to revert -- maybe the user deleted the
+     cache while gdb was running, or something like that.  */
+  copied_shell = nullptr;
+
+  const char *shell = get_shell ();
+  if (!IS_ABSOLUTE_PATH (shell))
+    {
+      warning (_("This version of macOS has System Integrity Protection.\n\
+Normally gdb would try to work around this by caching a copy of your shell,\n\
+but because your shell (%s) is not an absolute path, this is being skipped."),
+              shell);
+      return false;
+    }
+
+  struct stat sb;
+  if (stat (shell, &sb) < 0)
+    {
+      warning (_("This version of macOS has System Integrity Protection.\n\
+Normally gdb would try to work around this by caching a copy of your shell,\n\
+but because gdb could not stat your shell (%s), this is being skipped.\n\
+The error was: %s"),
+              shell, safe_strerror (errno));
+      return false;
+    }
+
+  if ((sb.st_flags & SF_RESTRICTED) == 0)
+    return true;
+
+  /* Put the copy somewhere like ~/Library/Caches/gdb/bin/sh.  */
+  std::string new_name = get_standard_cache_dir ();
+  /* There's no need to insert a directory separator here, because
+     SHELL is known to be absolute.  */
+  new_name.append (shell);
+
+  /* Maybe it was cached by some earlier gdb.  */
+  if (stat (new_name.c_str (), &sb) != 0 || !S_ISREG (sb.st_mode))
+    {
+      try
+       {
+         copy_shell_to_cache (shell, new_name);
+       }
+      catch (const gdb_exception_error &ex)
+       {
+         warning (_("This version of macOS has System Integrity Protection.\n\
+Because `startup-with-shell' is enabled, gdb tried to work around SIP by\n\
+caching a copy of your shell.  However, this failed:\n\
+%s\n\
+If you correct the problem, gdb will automatically try again the next time\n\
+you \"run\".  To prevent these attempts, you can use:\n\
+    set startup-with-shell off"),
+                  ex.what ());
+         return false;
+       }
+
+      printf_filtered (_("Note: this version of macOS has System Integrity Protection.\n\
+Because `startup-with-shell' is enabled, gdb has worked around this by\n\
+caching a copy of your shell.  The shell used by \"run\" is now:\n\
+    %s\n"),
+                      new_name.c_str ());
+    }
+
+  /* We need to make sure that the new name has the correct lifetime.  */
+  static std::string saved_shell = std::move (new_name);
+  copied_shell = saved_shell.c_str ();
+
+#endif /* SF_RESTRICTED */
+
+  return true;
+}
+
 void
 darwin_nat_target::create_inferior (const char *exec_file,
                                    const std::string &allargs,
                                    char **env, int from_tty)
 {
+  gdb::optional<scoped_restore_tmpl<int>> restore_startup_with_shell;
+
+  if (startup_with_shell && may_have_sip ())
+    {
+      if (!maybe_cache_shell ())
+       {
+         warning (_("startup-with-shell is now temporarily disabled"));
+         restore_startup_with_shell.emplace (&startup_with_shell, 0);
+       }
+    }
+
   /* Do the hard work.  */
   fork_inferior (exec_file, allargs, env, darwin_ptrace_me,
-                darwin_ptrace_him, darwin_pre_ptrace, NULL,
+                darwin_ptrace_him, darwin_pre_ptrace, copied_shell,
                 darwin_execvp);
 }
 \f
@@ -1849,11 +2036,7 @@ void
 darwin_nat_target::attach (const char *args, int from_tty)
 {
   pid_t pid;
-  pid_t pid2;
-  int wstatus;
-  int res;
   struct inferior *inf;
-  kern_return_t kret;
 
   pid = parse_pid_to_attach (args);
 
@@ -1866,19 +2049,17 @@ darwin_nat_target::attach (const char *args, int from_tty)
 
       if (exec_file)
        printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file,
-                          target_pid_to_str (pid_to_ptid (pid)));
+                          target_pid_to_str (ptid_t (pid)).c_str ());
       else
        printf_unfiltered (_("Attaching to %s\n"),
-                          target_pid_to_str (pid_to_ptid (pid)));
-
-      gdb_flush (gdb_stdout);
+                          target_pid_to_str (ptid_t (pid)).c_str ());
     }
 
   if (pid == 0 || ::kill (pid, 0) < 0)
     error (_("Can't attach to process %d: %s (%d)"),
            pid, safe_strerror (errno), errno);
 
-  inferior_ptid = pid_to_ptid (pid);
+  inferior_ptid = ptid_t (pid);
   inf = current_inferior ();
   inferior_appeared (inf, pid);
   inf->attach_flag = 1;
@@ -1891,7 +2072,7 @@ darwin_nat_target::attach (const char *args, int from_tty)
 
   darwin_inferior *priv = get_darwin_inferior (inf);
 
-  darwin_check_osabi (priv, ptid_get_tid (inferior_ptid));
+  darwin_check_osabi (priv, inferior_ptid.tid ());
 
   darwin_setup_fake_stop_event (inf);
 
@@ -1909,7 +2090,6 @@ darwin_nat_target::attach (const char *args, int from_tty)
 void
 darwin_nat_target::detach (inferior *inf, int from_tty)
 {
-  pid_t pid = ptid_get_pid (inferior_ptid);
   darwin_inferior *priv = get_darwin_inferior (inf);
   kern_return_t kret;
   int res;
@@ -1943,26 +2123,22 @@ darwin_nat_target::detach (inferior *inf, int from_tty)
   mourn_inferior ();
 }
 
-const char *
+std::string
 darwin_nat_target::pid_to_str (ptid_t ptid)
 {
-  static char buf[80];
-  long tid = ptid_get_tid (ptid);
+  long tid = ptid.tid ();
 
   if (tid != 0)
-    {
-      snprintf (buf, sizeof (buf), _("Thread 0x%lx of process %u"),
-               tid, ptid_get_pid (ptid));
-      return buf;
-    }
+    return string_printf (_("Thread 0x%lx of process %u"),
+                         tid, ptid.pid ());
 
   return normal_pid_to_str (ptid);
 }
 
-int
+bool
 darwin_nat_target::thread_alive (ptid_t ptid)
 {
-  return 1;
+  return true;
 }
 
 /* If RDADDR is not NULL, read inferior task's LEN bytes from ADDR and
@@ -1978,11 +2154,6 @@ darwin_read_write_inferior (task_t task, CORE_ADDR addr,
 {
   kern_return_t kret;
   mach_vm_size_t res_length = 0;
-  pointer_t copied;
-  mach_msg_type_number_t copy_count;
-  mach_vm_size_t remaining_length;
-  mach_vm_address_t region_address;
-  mach_vm_size_t region_length;
 
   inferior_debug (8, _("darwin_read_write_inferior(task=0x%x, %s, len=%s)\n"),
                  task, core_addr_to_string (addr), pulongest (length));
@@ -2136,7 +2307,6 @@ darwin_read_dyld_info (task_t task, CORE_ADDR addr, gdb_byte *rdaddr,
 {
   struct task_dyld_info task_dyld_info;
   mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
-  int sz = TASK_DYLD_INFO_COUNT * sizeof (natural_t);
   kern_return_t kret;
 
   if (addr != 0 || length > sizeof (mach_vm_address_t))
@@ -2208,7 +2378,7 @@ static void
 set_enable_mach_exceptions (const char *args, int from_tty,
                            struct cmd_list_element *c)
 {
-  if (!ptid_equal (inferior_ptid, null_ptid))
+  if (inferior_ptid != null_ptid)
     {
       struct inferior *inf = current_inferior ();
       darwin_inferior *priv = get_darwin_inferior (inf);
@@ -2257,7 +2427,7 @@ darwin_nat_target::get_ada_task_ptid (long lwp, long thread)
   for (darwin_thread_t *t : priv->threads)
     {
       if (t->inf_port == lwp)
-       return ptid_build (ptid_get_pid (inferior_ptid), 0, t->gdb_port);
+       return ptid_t (inferior_ptid.pid (), 0, t->gdb_port);
     }
 
   /* Maybe the port was never extract.  Do it now.  */
@@ -2301,15 +2471,15 @@ darwin_nat_target::get_ada_task_ptid (long lwp, long thread)
                  names_count * sizeof (mach_port_t));
 
   if (res)
-    return ptid_build (ptid_get_pid (inferior_ptid), 0, res);
+    return ptid_t (inferior_ptid.pid (), 0, res);
   else
     return null_ptid;
 }
 
-int
+bool
 darwin_nat_target::supports_multi_process ()
 {
-  return 1;
+  return true;
 }
 
 void
This page took 0.037117 seconds and 4 git commands to generate.