Tidy dwarf1 cached section contents
[binutils-gdb.git] / gdb / linux-nat.c
index dff8d07d3f7279c462dbb5d9fe700e05ad01a4c6..fd537d7a1d36e1b3675ca877f9fbaf27bb2f413e 100644 (file)
@@ -1,6 +1,6 @@
 /* GNU/Linux native-dependent code common to multiple platforms.
 
-   Copyright (C) 2001-2022 Free Software Foundation, Inc.
+   Copyright (C) 2001-2023 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -60,7 +60,6 @@
 #include "symfile.h"
 #include "gdbsupport/agent.h"
 #include "tracepoint.h"
-#include "gdbsupport/buffer.h"
 #include "target-descriptions.h"
 #include "gdbsupport/filestuff.h"
 #include "objfiles.h"
@@ -244,6 +243,7 @@ static int lwp_status_pending_p (struct lwp_info *lp);
 
 static void save_stop_reason (struct lwp_info *lp);
 
+static bool proc_mem_file_is_writable ();
 static void close_proc_mem_file (pid_t pid);
 static void open_proc_mem_file (ptid_t ptid);
 
@@ -371,6 +371,7 @@ linux_init_ptrace_procfs (pid_t pid, int attached)
   linux_enable_event_reporting (pid, options);
   linux_ptrace_init_warnings ();
   linux_proc_init_warnings ();
+  proc_mem_file_is_writable ();
 }
 
 linux_nat_target::~linux_nat_target ()
@@ -1115,8 +1116,7 @@ linux_nat_target::attach (const char *args, int from_tty)
                 gdb_signal_to_string (signo));
        }
 
-      internal_error (__FILE__, __LINE__,
-                     _("unexpected status %d for PID %ld"),
+      internal_error (_("unexpected status %d for PID %ld"),
                      status, (long) ptid.lwp ());
     }
 
@@ -1226,7 +1226,7 @@ get_detach_signal (struct lwp_info *lp)
          if (tp->has_pending_waitstatus ())
            {
              /* If the thread has a pending event, and it was stopped with a
-                signal, use that signal to resume it.  If it has a pending
+                signal, use that signal to resume it.  If it has a pending
                 event of another kind, it was not stopped with a signal, so
                 resume it without a signal.  */
              if (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED)
@@ -1595,32 +1595,22 @@ resume_set_callback (struct lwp_info *lp)
 }
 
 void
-linux_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
+linux_nat_target::resume (ptid_t scope_ptid, int step, enum gdb_signal signo)
 {
   struct lwp_info *lp;
-  int resume_many;
 
   linux_nat_debug_printf ("Preparing to %s %s, %s, inferior_ptid %s",
                          step ? "step" : "resume",
-                         ptid.to_string ().c_str (),
+                         scope_ptid.to_string ().c_str (),
                          (signo != GDB_SIGNAL_0
                           ? strsignal (gdb_signal_to_host (signo)) : "0"),
                          inferior_ptid.to_string ().c_str ());
 
-  /* A specific PTID means `step only this process id'.  */
-  resume_many = (minus_one_ptid == ptid
-                || ptid.is_pid ());
-
   /* Mark the lwps we're resuming as resumed and update their
      last_resume_kind to resume_continue.  */
-  iterate_over_lwps (ptid, resume_set_callback);
+  iterate_over_lwps (scope_ptid, resume_set_callback);
 
-  /* See if it's the current inferior that should be handled
-     specially.  */
-  if (resume_many)
-    lp = find_lwp_pid (inferior_ptid);
-  else
-    lp = find_lwp_pid (ptid);
+  lp = find_lwp_pid (inferior_ptid);
   gdb_assert (lp != NULL);
 
   /* Remember if we're stepping.  */
@@ -1662,18 +1652,19 @@ linux_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
 
       if (target_can_async_p ())
        {
-         target_async (1);
+         target_async (true);
          /* Tell the event loop we have something to process.  */
          async_file_mark ();
        }
       return;
     }
 
-  if (resume_many)
-    iterate_over_lwps (ptid, [=] (struct lwp_info *info)
-                            {
-                              return linux_nat_resume_callback (info, lp);
-                            });
+  /* No use iterating unless we're resuming other threads.  */
+  if (scope_ptid != lp->ptid)
+    iterate_over_lwps (scope_ptid, [=] (struct lwp_info *info)
+      {
+       return linux_nat_resume_callback (info, lp);
+      });
 
   linux_nat_debug_printf ("%s %s, %s (resume event thread)",
                          step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
@@ -1852,11 +1843,9 @@ linux_handle_extended_wait (struct lwp_info *lp, int status)
          if (ret == -1)
            perror_with_name (_("waiting for new child"));
          else if (ret != new_pid)
-           internal_error (__FILE__, __LINE__,
-                           _("wait returned unexpected PID %d"), ret);
+           internal_error (_("wait returned unexpected PID %d"), ret);
          else if (!WIFSTOPPED (status))
-           internal_error (__FILE__, __LINE__,
-                           _("wait returned unexpected status 0x%x"), status);
+           internal_error (_("wait returned unexpected status 0x%x"), status);
        }
 
       ptid_t child_ptid (new_pid, new_pid);
@@ -1923,7 +1912,6 @@ linux_handle_extended_wait (struct lwp_info *lp, int status)
            {
              /* The process is not using thread_db.  Add the LWP to
                 GDB's list.  */
-             target_post_attach (new_lp->ptid.lwp ());
              add_thread (linux_target, new_lp->ptid);
            }
 
@@ -1997,8 +1985,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status)
        return 0;
     }
 
-  internal_error (__FILE__, __LINE__,
-                 _("unknown ptrace event %d"), event);
+  internal_error (_("unknown ptrace event %d"), event);
 }
 
 /* Suspend waiting for a signal.  We're mostly interested in
@@ -2548,6 +2535,10 @@ save_stop_reason (struct lwp_info *lp)
   if (!linux_target->low_status_is_event (lp->status))
     return;
 
+  inferior *inf = find_inferior_ptid (linux_target, lp->ptid);
+  if (inf->starting_up)
+    return;
+
   regcache = get_thread_regcache (linux_target, lp->ptid);
   gdbarch = regcache->arch ();
 
@@ -3615,28 +3606,21 @@ siginfo_fixup (siginfo_t *siginfo, gdb_byte *inf_siginfo, int direction)
 }
 
 static enum target_xfer_status
-linux_xfer_siginfo (enum target_object object,
+linux_xfer_siginfo (ptid_t ptid, enum target_object object,
                    const char *annex, gdb_byte *readbuf,
                    const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
                    ULONGEST *xfered_len)
 {
-  int pid;
   siginfo_t siginfo;
   gdb_byte inf_siginfo[sizeof (siginfo_t)];
 
   gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO);
   gdb_assert (readbuf || writebuf);
 
-  pid = inferior_ptid.lwp ();
-  if (pid == 0)
-    pid = inferior_ptid.pid ();
-
   if (offset > sizeof (siginfo))
     return TARGET_XFER_E_IO;
 
-  errno = 0;
-  ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
-  if (errno != 0)
+  if (!linux_nat_get_siginfo (ptid, &siginfo))
     return TARGET_XFER_E_IO;
 
   /* When GDB is built as a 64-bit application, ptrace writes into
@@ -3659,6 +3643,7 @@ linux_xfer_siginfo (enum target_object object,
       /* Convert back to ptrace layout before flushing it out.  */
       siginfo_fixup (&siginfo, inf_siginfo, 1);
 
+      int pid = get_ptrace_pid (ptid);
       errno = 0;
       ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
       if (errno != 0)
@@ -3676,8 +3661,9 @@ linux_nat_xfer_osdata (enum target_object object,
                       ULONGEST *xfered_len);
 
 static enum target_xfer_status
-linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
-                               ULONGEST offset, LONGEST len, ULONGEST *xfered_len);
+linux_proc_xfer_memory_partial (int pid, gdb_byte *readbuf,
+                               const gdb_byte *writebuf, ULONGEST offset,
+                               LONGEST len, ULONGEST *xfered_len);
 
 enum target_xfer_status
 linux_nat_target::xfer_partial (enum target_object object,
@@ -3686,7 +3672,7 @@ linux_nat_target::xfer_partial (enum target_object object,
                                ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
 {
   if (object == TARGET_OBJECT_SIGNAL_INFO)
-    return linux_xfer_siginfo (object, annex, readbuf, writebuf,
+    return linux_xfer_siginfo (inferior_ptid, object, annex, readbuf, writebuf,
                               offset, len, xfered_len);
 
   /* The target is connected but no live inferior is selected.  Pass
@@ -3715,8 +3701,16 @@ linux_nat_target::xfer_partial (enum target_object object,
       if (addr_bit < (sizeof (ULONGEST) * HOST_CHAR_BIT))
        offset &= ((ULONGEST) 1 << addr_bit) - 1;
 
-      return linux_proc_xfer_memory_partial (readbuf, writebuf,
-                                            offset, len, xfered_len);
+      /* If /proc/pid/mem is writable, don't fallback to ptrace.  If
+        the write via /proc/pid/mem fails because the inferior execed
+        (and we haven't seen the exec event yet), a subsequent ptrace
+        poke would incorrectly write memory to the post-exec address
+        space, while the core was trying to write to the pre-exec
+        address space.  */
+      if (proc_mem_file_is_writable ())
+       return linux_proc_xfer_memory_partial (inferior_ptid.pid (), readbuf,
+                                              writebuf, offset, len,
+                                              xfered_len);
     }
 
   return inf_ptrace_target::xfer_partial (object, annex, readbuf, writebuf,
@@ -3773,7 +3767,7 @@ linux_nat_target::thread_name (struct thread_info *thr)
 /* Accepts an integer PID; Returns a string representing a file that
    can be opened to get the symbols for the child process.  */
 
-char *
+const char *
 linux_nat_target::pid_to_exec_file (int pid)
 {
   return linux_proc_pid_to_exec_file (pid);
@@ -3887,25 +3881,19 @@ open_proc_mem_file (ptid_t ptid)
                          fd, ptid.pid (), ptid.lwp ());
 }
 
-/* Implement the to_xfer_partial target method using /proc/PID/mem.
-   Because we can use a single read/write call, this can be much more
-   efficient than banging away at PTRACE_PEEKTEXT.  Also, unlike
-   PTRACE_PEEKTEXT/PTRACE_POKETEXT, this works with running
-   threads.  */
+/* Helper for linux_proc_xfer_memory_partial and
+   proc_mem_file_is_writable.  FD is the already opened /proc/pid/mem
+   file, and PID is the pid of the corresponding process.  The rest of
+   the arguments are like linux_proc_xfer_memory_partial's.  */
 
 static enum target_xfer_status
-linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
-                               ULONGEST offset, LONGEST len,
-                               ULONGEST *xfered_len)
+linux_proc_xfer_memory_partial_fd (int fd, int pid,
+                                  gdb_byte *readbuf, const gdb_byte *writebuf,
+                                  ULONGEST offset, LONGEST len,
+                                  ULONGEST *xfered_len)
 {
   ssize_t ret;
 
-  auto iter = proc_mem_file_map.find (inferior_ptid.pid ());
-  if (iter == proc_mem_file_map.end ())
-    return TARGET_XFER_EOF;
-
-  int fd = iter->second.fd ();
-
   gdb_assert (fd != -1);
 
   /* Use pread64/pwrite64 if available, since they save a syscall and can
@@ -3924,16 +3912,15 @@ linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
   if (ret == -1)
     {
       linux_nat_debug_printf ("accessing fd %d for pid %d failed: %s (%d)",
-                             fd, inferior_ptid.pid (),
-                             safe_strerror (errno), errno);
-      return TARGET_XFER_EOF;
+                             fd, pid, safe_strerror (errno), errno);
+      return TARGET_XFER_E_IO;
     }
   else if (ret == 0)
     {
       /* EOF means the address space is gone, the whole process exited
         or execed.  */
       linux_nat_debug_printf ("accessing fd %d for pid %d got EOF",
-                             fd, inferior_ptid.pid ());
+                             fd, pid);
       return TARGET_XFER_EOF;
     }
   else
@@ -3943,6 +3930,83 @@ linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
     }
 }
 
+/* Implement the to_xfer_partial target method using /proc/PID/mem.
+   Because we can use a single read/write call, this can be much more
+   efficient than banging away at PTRACE_PEEKTEXT.  Also, unlike
+   PTRACE_PEEKTEXT/PTRACE_POKETEXT, this works with running
+   threads.  */
+
+static enum target_xfer_status
+linux_proc_xfer_memory_partial (int pid, gdb_byte *readbuf,
+                               const gdb_byte *writebuf, ULONGEST offset,
+                               LONGEST len, ULONGEST *xfered_len)
+{
+  auto iter = proc_mem_file_map.find (pid);
+  if (iter == proc_mem_file_map.end ())
+    return TARGET_XFER_EOF;
+
+  int fd = iter->second.fd ();
+
+  return linux_proc_xfer_memory_partial_fd (fd, pid, readbuf, writebuf, offset,
+                                           len, xfered_len);
+}
+
+/* Check whether /proc/pid/mem is writable in the current kernel, and
+   return true if so.  It wasn't writable before Linux 2.6.39, but
+   there's no way to know whether the feature was backported to older
+   kernels.  So we check to see if it works.  The result is cached,
+   and this is garanteed to be called once early during inferior
+   startup, so that any warning is printed out consistently between
+   GDB invocations.  Note we don't call it during GDB startup instead
+   though, because then we might warn with e.g. just "gdb --version"
+   on sandboxed systems.  See PR gdb/29907.  */
+
+static bool
+proc_mem_file_is_writable ()
+{
+  static gdb::optional<bool> writable;
+
+  if (writable.has_value ())
+    return *writable;
+
+  writable.emplace (false);
+
+  /* We check whether /proc/pid/mem is writable by trying to write to
+     one of our variables via /proc/self/mem.  */
+
+  int fd = gdb_open_cloexec ("/proc/self/mem", O_RDWR | O_LARGEFILE, 0).release ();
+
+  if (fd == -1)
+    {
+      warning (_("opening /proc/self/mem file failed: %s (%d)"),
+              safe_strerror (errno), errno);
+      return *writable;
+    }
+
+  SCOPE_EXIT { close (fd); };
+
+  /* This is the variable we try to write to.  Note OFFSET below.  */
+  volatile gdb_byte test_var = 0;
+
+  gdb_byte writebuf[] = {0x55};
+  ULONGEST offset = (uintptr_t) &test_var;
+  ULONGEST xfered_len;
+
+  enum target_xfer_status res
+    = linux_proc_xfer_memory_partial_fd (fd, getpid (), nullptr, writebuf,
+                                        offset, 1, &xfered_len);
+
+  if (res == TARGET_XFER_OK)
+    {
+      gdb_assert (xfered_len == 1);
+      gdb_assert (test_var == 0x55);
+      /* Success.  */
+      *writable = true;
+    }
+
+  return *writable;
+}
+
 /* Parse LINE as a signal set and add its set bits to SIGS.  */
 
 static void
@@ -4049,9 +4113,7 @@ linux_nat_target::static_tracepoint_markers_by_strid (const char *strid)
   /* Pause all */
   target_stop (ptid);
 
-  memcpy (s, "qTfSTM", sizeof ("qTfSTM"));
-  s[sizeof ("qTfSTM")] = 0;
-
+  strcpy (s, "qTfSTM");
   agent_run_command (pid, s, strlen (s) + 1);
 
   /* Unpause all.  */
@@ -4068,8 +4130,7 @@ linux_nat_target::static_tracepoint_markers_by_strid (const char *strid)
        }
       while (*p++ == ',');     /* comma-separated list */
 
-      memcpy (s, "qTsSTM", sizeof ("qTsSTM"));
-      s[sizeof ("qTsSTM")] = 0;
+      strcpy (s, "qTsSTM");
       agent_run_command (pid, s, strlen (s) + 1);
       p = s;
     }
@@ -4148,9 +4209,9 @@ handle_target_event (int error, gdb_client_data client_data)
 /* target_async implementation.  */
 
 void
-linux_nat_target::async (int enable)
+linux_nat_target::async (bool enable)
 {
-  if ((enable != 0) == is_async_p ())
+  if (enable == is_async_p ())
     return;
 
   /* Block child signals while we create/destroy the pipe, as their
@@ -4160,7 +4221,7 @@ linux_nat_target::async (int enable)
   if (enable)
     {
       if (!async_file_open ())
-       internal_error (__FILE__, __LINE__, "creating event pipe failed.");
+       internal_error ("creating event pipe failed.");
 
       add_file_handler (async_wait_fd (), handle_target_event, NULL,
                        "linux-nat");
@@ -4301,7 +4362,7 @@ linux_nat_fileio_pid_of (struct inferior *inf)
 int
 linux_nat_target::fileio_open (struct inferior *inf, const char *filename,
                               int flags, int mode, int warn_if_slow,
-                              int *target_errno)
+                              fileio_error *target_errno)
 {
   int nat_flags;
   mode_t nat_mode;
@@ -4326,7 +4387,7 @@ linux_nat_target::fileio_open (struct inferior *inf, const char *filename,
 
 gdb::optional<std::string>
 linux_nat_target::fileio_readlink (struct inferior *inf, const char *filename,
-                                  int *target_errno)
+                                  fileio_error *target_errno)
 {
   char buf[PATH_MAX];
   int len;
@@ -4346,7 +4407,7 @@ linux_nat_target::fileio_readlink (struct inferior *inf, const char *filename,
 
 int
 linux_nat_target::fileio_unlink (struct inferior *inf, const char *filename,
-                                int *target_errno)
+                                fileio_error *target_errno)
 {
   int ret;
 
@@ -4377,23 +4438,11 @@ linux_nat_target::linux_nat_target ()
 
 /* See linux-nat.h.  */
 
-int
+bool
 linux_nat_get_siginfo (ptid_t ptid, siginfo_t *siginfo)
 {
-  int pid;
-
-  pid = ptid.lwp ();
-  if (pid == 0)
-    pid = ptid.pid ();
-
-  errno = 0;
-  ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, siginfo);
-  if (errno != 0)
-    {
-      memset (siginfo, 0, sizeof (*siginfo));
-      return 0;
-    }
-  return 1;
+  int pid = get_ptrace_pid (ptid);
+  return ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, siginfo) == 0;
 }
 
 /* See nat/linux-nat.h.  */