Use std::string in dwarf2read.c
[deliverable/binutils-gdb.git] / gdb / darwin-nat.c
index ecc7635d049a77e2867dc552011a296bb4106628..2a33a7d522f272bf77e6643ccf39bfe0278fac8c 100644 (file)
@@ -1,5 +1,5 @@
 /* Darwin support for GDB, the GNU debugger.
-   Copyright (C) 2008-2018 Free Software Foundation, Inc.
+   Copyright (C) 2008-2020 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.
@@ -114,11 +120,15 @@ static vm_size_t mach_page_size;
 
 /* If Set, catch all mach exceptions (before they are converted to signals
    by the kernel).  */
-static int enable_mach_exceptions;
+static bool 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)
 
@@ -678,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
@@ -1116,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;
                }
@@ -1139,7 +1148,7 @@ 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);
@@ -1550,7 +1559,6 @@ darwin_nat_target::kill ()
          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).  */
-      darwin_inferior *priv = get_darwin_inferior (inf);
       for (darwin_thread_t *thread : priv->threads)
         thread->signaled = 1;
 
@@ -1602,7 +1610,7 @@ darwin_attach_pid (struct inferior *inf)
   darwin_inferior *priv = new darwin_inferior;
   inf->priv.reset (priv);
 
-  TRY
+  try
     {
       kret = task_for_pid (gdb_task, inf->pid, &priv->task);
       if (kret != KERN_SUCCESS)
@@ -1679,14 +1687,13 @@ darwin_attach_pid (struct inferior *inf)
 
       darwin_setup_exceptions (inf);
     }
-  CATCH (ex, RETURN_MASK_ALL)
+  catch (const gdb_exception &ex)
     {
       exit_inferior (inf);
       inferior_ptid = null_ptid;
 
-      throw_exception (ex);
+      throw;
     }
-  END_CATCH
 
   target_ops *darwin_ops = get_native_target ();
   if (!target_is_pushed (darwin_ops))
@@ -1698,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
@@ -1833,10 +1836,11 @@ 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 on Sierra or later.  */
+/* Read kernel version, and return TRUE if this host may have System
+   Integrity Protection (Sierra or later).  */
 
 static bool
-should_disable_startup_with_shell ()
+may_have_sip ()
 {
   char str[16];
   size_t sz = sizeof (str);
@@ -1852,23 +1856,149 @@ should_disable_startup_with_shell ()
   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;
+  gdb::optional<scoped_restore_tmpl<bool>> restore_startup_with_shell;
 
-  if (startup_with_shell && should_disable_startup_with_shell ())
+  if (startup_with_shell && may_have_sip ())
     {
-      warning (_("startup-with-shell not supported on this macOS version,"
-                " disabling it."));
-      restore_startup_with_shell.emplace (&startup_with_shell, 0);
+      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
@@ -1915,16 +2045,14 @@ darwin_nat_target::attach (const char *args, int from_tty)
 
   if (from_tty)
     {
-      char *exec_file = get_exec_file (0);
+      const char *exec_file = get_exec_file (0);
 
       if (exec_file)
        printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file,
-                          target_pid_to_str (ptid_t (pid)));
+                          target_pid_to_str (ptid_t (pid)).c_str ());
       else
        printf_unfiltered (_("Attaching to %s\n"),
-                          target_pid_to_str (ptid_t (pid)));
-
-      gdb_flush (gdb_stdout);
+                          target_pid_to_str (ptid_t (pid)).c_str ());
     }
 
   if (pid == 0 || ::kill (pid, 0) < 0)
@@ -1995,18 +2123,14 @@ 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.tid ();
 
   if (tid != 0)
-    {
-      snprintf (buf, sizeof (buf), _("Thread 0x%lx of process %u"),
-               tid, ptid.pid ());
-      return buf;
-    }
+    return string_printf (_("Thread 0x%lx of process %u"),
+                         tid, ptid.pid ());
 
   return normal_pid_to_str (ptid);
 }
@@ -2021,7 +2145,7 @@ darwin_nat_target::thread_alive (ptid_t ptid)
    copy it to RDADDR in gdb's address space.
    If WRADDR is not NULL, write gdb's LEN bytes from WRADDR and copy it
    to ADDR in inferior task's address space.
-   Return 0 on failure; number of bytes read / writen otherwise.  */
+   Return 0 on failure; number of bytes read / written otherwise.  */
 
 static int
 darwin_read_write_inferior (task_t task, CORE_ADDR addr,
This page took 0.03033 seconds and 4 git commands to generate.