From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1551) id 273E13857405; Thu, 14 Apr 2022 19:23:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 273E13857405 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Pedro Alves To: gdb-cvs@sourceware.org Subject: [binutils-gdb] gdbserver/linux: Access memory even if threads are running X-Act-Checkin: binutils-gdb X-Git-Author: Pedro Alves X-Git-Refname: refs/heads/master X-Git-Oldrev: 366e3746c572c2c78454761e62fa9181cba413ca X-Git-Newrev: 421490af33bfbfe8a8429f0e43fb3e9f8727476e Message-Id: <20220414192319.273E13857405@sourceware.org> Date: Thu, 14 Apr 2022 19:23:19 +0000 (GMT) X-BeenThere: gdb-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Apr 2022 19:23:19 -0000 https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3D421490af33bf= bfe8a8429f0e43fb3e9f8727476e commit 421490af33bfbfe8a8429f0e43fb3e9f8727476e Author: Pedro Alves Date: Mon Mar 28 18:35:34 2022 +0100 gdbserver/linux: Access memory even if threads are running =20 Similarly to how the native Linux target was changed and subsequently reworked in these commits: =20 05c06f318fd9 Linux: Access memory even if threads are running 8a89ddbda2ec Avoid /proc/pid/mem races (PR 28065) =20 ... teach GDBserver to access memory even when the current thread is running, by always accessing memory via /proc/PID/mem. =20 The existing comment: =20 /* Neither ptrace nor /proc/PID/mem allow accessing memory through a running LWP. */ =20 ... is incorrect for /proc/PID/mem does allow that. =20 Actually, from GDB's perspective, GDBserver could already access memory while threads were running, but at the expense of pausing all threads for the duration of the memory access, via prepare_to_access_memory. This new implementation does not require pausing any thread, thus linux_process_target::prepare_to_access_memory / linux_process_target::done_accessing_memory become nops. A subsequent patch will remove the whole prepare_to_access_memory infrastructure completely. =20 The GDBserver linux-low.cc implementation is simpler than GDB's linux-nat.c's, because GDBserver always adds the unfollowed vfork/fork children to the process list immediately when the fork/vfork event is seen out of ptrace. I.e., there's no need to keep the file descriptor stored on a side map, we can store it directly in the process structure. =20 Change-Id: I0abfd782ceaa4ddce8d3e5f3e2dfc5928862ef61 Diff: --- gdbserver/linux-low.cc | 239 ++++++++++++++++++---------------------------= ---- gdbserver/linux-low.h | 11 ++- 2 files changed, 93 insertions(+), 157 deletions(-) diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index d882072e71e..449c6641f58 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -403,8 +403,22 @@ linux_process_target::low_delete_thread (arch_lwp_info= *info) gdb_assert (info =3D=3D nullptr); } =20 +/* Open the /proc/PID/mem file for PROC. */ + +static void +open_proc_mem_file (process_info *proc) +{ + gdb_assert (proc->priv->mem_fd =3D=3D -1); + + char filename[64]; + xsnprintf (filename, sizeof filename, "/proc/%d/mem", proc->pid); + + proc->priv->mem_fd + =3D gdb_open_cloexec (filename, O_RDWR | O_LARGEFILE, 0).release (); +} + process_info * -linux_process_target::add_linux_process (int pid, int attached) +linux_process_target::add_linux_process_no_mem_file (int pid, int attached) { struct process_info *proc; =20 @@ -412,7 +426,17 @@ linux_process_target::add_linux_process (int pid, int = attached) proc->priv =3D XCNEW (struct process_info_private); =20 proc->priv->arch_private =3D low_new_process (); + proc->priv->mem_fd =3D -1; + + return proc; +} + =20 +process_info * +linux_process_target::add_linux_process (int pid, int attached) +{ + process_info *proc =3D add_linux_process_no_mem_file (pid, attached); + open_proc_mem_file (proc); return proc; } =20 @@ -932,7 +956,11 @@ linux_process_target::create_inferior (const char *pro= gram, NULL, NULL, NULL, NULL); } =20 - add_linux_process (pid, 0); + /* When spawning a new process, we can't open the mem file yet. We + still have to nurse the process through the shell, and that execs + a couple times. The address space a /proc/PID/mem file is + accessing is destroyed on exec. */ + process_info *proc =3D add_linux_process_no_mem_file (pid, 0); =20 ptid =3D ptid_t (pid, pid); new_lwp =3D add_lwp (ptid); @@ -940,6 +968,10 @@ linux_process_target::create_inferior (const char *pro= gram, =20 post_fork_inferior (pid, program); =20 + /* PROC is now past the shell running the program we want, so we can + open the /proc/PID/mem file. */ + open_proc_mem_file (proc); + return pid; } =20 @@ -1095,7 +1127,9 @@ linux_process_target::attach (unsigned long pid) ptid_t ptid =3D ptid_t (pid, pid); int err; =20 - proc =3D add_linux_process (pid, 1); + /* Delay opening the /proc/PID/mem file until we've successfully + attached. */ + proc =3D add_linux_process_no_mem_file (pid, 1); =20 /* Attach to PID. We will check for other threads soon. */ @@ -1108,6 +1142,8 @@ linux_process_target::attach (unsigned long pid) error ("Cannot attach to process %ld: %s", pid, reason.c_str ()); } =20 + open_proc_mem_file (proc); + /* Don't ignore the initial SIGSTOP if we just attached to this process. It will be collected by wait shortly. */ initial_thread =3D find_thread_ptid (ptid_t (pid, pid)); @@ -1542,6 +1578,7 @@ linux_process_target::mourn (process_info *process) =20 /* Freeing all private data. */ priv =3D process->priv; + close (priv->mem_fd); low_delete_process (priv->arch_private); free (priv); process->priv =3D NULL; @@ -5309,93 +5346,71 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char= *myaddr, int len) return the_target->read_memory (memaddr, myaddr, len); } =20 -/* Copy LEN bytes from inferior's memory starting at MEMADDR - to debugger memory starting at MYADDR. */ =20 -int -linux_process_target::read_memory (CORE_ADDR memaddr, - unsigned char *myaddr, int len) +/* Helper for read_memory/write_memory 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. + One an only one of READBUF and WRITEBUF is non-null. If READBUF is + not null, then we're reading, otherwise we're writing. */ + +static int +proc_xfer_memory (CORE_ADDR memaddr, unsigned char *readbuf, + const gdb_byte *writebuf, int len) { - int pid =3D lwpid_of (current_thread); - PTRACE_XFER_TYPE *buffer; - CORE_ADDR addr; - int count; - char filename[64]; - int i; - int ret; - int fd; + gdb_assert ((readbuf =3D=3D nullptr) !=3D (writebuf =3D=3D nullptr)); =20 - /* Try using /proc. Don't bother for one word. */ - if (len >=3D 3 * sizeof (long)) + process_info *proc =3D current_process (); + + int fd =3D proc->priv->mem_fd; + if (fd =3D=3D -1) + return EIO; + + while (len > 0) { int bytes; =20 - /* We could keep this file open and cache it - possibly one per - thread. That requires some juggling, but is even faster. */ - sprintf (filename, "/proc/%d/mem", pid); - fd =3D open (filename, O_RDONLY | O_LARGEFILE); - if (fd =3D=3D -1) - goto no_proc; - /* If pread64 is available, use it. It's faster if the kernel supports it (only one syscall), and it's 64-bit safe even on 32-bit platforms (for instance, SPARC debugging a SPARC64 application). */ #ifdef HAVE_PREAD64 - bytes =3D pread64 (fd, myaddr, len, memaddr); + bytes =3D (readbuf !=3D nullptr + ? pread64 (fd, readbuf, len, memaddr) + : pwrite64 (fd, writebuf, len, memaddr)); #else bytes =3D -1; if (lseek (fd, memaddr, SEEK_SET) !=3D -1) - bytes =3D read (fd, myaddr, len); + bytes =3D (readbuf !=3D nullptr + ? read (fd, readbuf, len) + ? write (fd, writebuf, len)); #endif =20 - close (fd); - if (bytes =3D=3D len) - return 0; - - /* Some data was read, we'll try to get the rest with ptrace. */ - if (bytes > 0) + if (bytes < 0) + return errno; + else if (bytes =3D=3D 0) { - memaddr +=3D bytes; - myaddr +=3D bytes; - len -=3D bytes; + /* EOF means the address space is gone, the whole process + exited or execed. */ + return EIO; } - } =20 - no_proc: - /* Round starting address down to longword boundary. */ - addr =3D memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); - /* Round ending address up; get number of longwords that makes. */ - count =3D ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) - / sizeof (PTRACE_XFER_TYPE)); - /* Allocate buffer of that many longwords. */ - buffer =3D XALLOCAVEC (PTRACE_XFER_TYPE, count); - - /* Read all the longwords */ - errno =3D 0; - for (i =3D 0; i < count; i++, addr +=3D sizeof (PTRACE_XFER_TYPE)) - { - /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc wa= rning - about coercing an 8 byte integer to a 4 byte pointer. */ - buffer[i] =3D ptrace (PTRACE_PEEKTEXT, pid, - (PTRACE_TYPE_ARG3) (uintptr_t) addr, - (PTRACE_TYPE_ARG4) 0); - if (errno) - break; + memaddr +=3D bytes; + if (readbuf !=3D nullptr) + readbuf +=3D bytes; + else + writebuf +=3D bytes; + len -=3D bytes; } - ret =3D errno; =20 - /* Copy appropriate bytes out of the buffer. */ - if (i > 0) - { - i *=3D sizeof (PTRACE_XFER_TYPE); - i -=3D memaddr & (sizeof (PTRACE_XFER_TYPE) - 1); - memcpy (myaddr, - (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), - i < len ? i : len); - } + return 0; +} =20 - return ret; +int +linux_process_target::read_memory (CORE_ADDR memaddr, + unsigned char *myaddr, int len) +{ + return proc_xfer_memory (memaddr, myaddr, nullptr, len); } =20 /* Copy LEN bytes of data from debugger memory at MYADDR to inferior's @@ -5406,25 +5421,6 @@ int linux_process_target::write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) { - int i; - /* Round starting address down to longword boundary. */ - CORE_ADDR addr =3D memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); - /* Round ending address up; get number of longwords that makes. */ - int count - =3D (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) - / sizeof (PTRACE_XFER_TYPE); - - /* Allocate buffer of that many longwords. */ - PTRACE_XFER_TYPE *buffer =3D XALLOCAVEC (PTRACE_XFER_TYPE, count); - - int pid =3D lwpid_of (current_thread); - - if (len =3D=3D 0) - { - /* Zero length write always succeeds. */ - return 0; - } - if (debug_threads) { /* Dump up to four bytes. */ @@ -5432,7 +5428,7 @@ linux_process_target::write_memory (CORE_ADDR memaddr, char *p =3D str; int dump =3D len < 4 ? len : 4; =20 - for (i =3D 0; i < dump; i++) + for (int i =3D 0; i < dump; i++) { sprintf (p, "%02x", myaddr[i]); p +=3D 2; @@ -5440,54 +5436,10 @@ linux_process_target::write_memory (CORE_ADDR memad= dr, *p =3D '\0'; =20 threads_debug_printf ("Writing %s to 0x%08lx in process %d", - str, (long) memaddr, pid); + str, (long) memaddr, current_process ()->pid); } =20 - /* Fill start and end extra bytes of buffer with existing memory data. = */ - - errno =3D 0; - /* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning - about coercing an 8 byte integer to a 4 byte pointer. */ - buffer[0] =3D ptrace (PTRACE_PEEKTEXT, pid, - (PTRACE_TYPE_ARG3) (uintptr_t) addr, - (PTRACE_TYPE_ARG4) 0); - if (errno) - return errno; - - if (count > 1) - { - errno =3D 0; - buffer[count - 1] - =3D ptrace (PTRACE_PEEKTEXT, pid, - /* Coerce to a uintptr_t first to avoid potential gcc warning - about coercing an 8 byte integer to a 4 byte pointer. */ - (PTRACE_TYPE_ARG3) (uintptr_t) (addr + (count - 1) - * sizeof (PTRACE_XFER_TYPE)), - (PTRACE_TYPE_ARG4) 0); - if (errno) - return errno; - } - - /* Copy data to be written over corresponding part of buffer. */ - - memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), - myaddr, len); - - /* Write the entire buffer. */ - - for (i =3D 0; i < count; i++, addr +=3D sizeof (PTRACE_XFER_TYPE)) - { - errno =3D 0; - ptrace (PTRACE_POKETEXT, pid, - /* Coerce to a uintptr_t first to avoid potential gcc warning - about coercing an 8 byte integer to a 4 byte pointer. */ - (PTRACE_TYPE_ARG3) (uintptr_t) addr, - (PTRACE_TYPE_ARG4) buffer[i]); - if (errno) - return errno; - } - - return 0; + return proc_xfer_memory (memaddr, nullptr, myaddr, len); } =20 void @@ -6191,25 +6143,6 @@ linux_process_target::unpause_all (bool unfreeze) unstop_all_lwps (unfreeze, NULL); } =20 -int -linux_process_target::prepare_to_access_memory () -{ - /* Neither ptrace nor /proc/PID/mem allow accessing memory through a - running LWP. */ - if (non_stop) - target_pause_all (true); - return 0; -} - -void -linux_process_target::done_accessing_memory () -{ - /* Neither ptrace nor /proc/PID/mem allow accessing memory through a - running LWP. */ - if (non_stop) - target_unpause_all (true); -} - /* Extract &phdr and num_phdr in the inferior. Return 0 on success. */ =20 static int diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h index 27cc9641f12..4284fb3348a 100644 --- a/gdbserver/linux-low.h +++ b/gdbserver/linux-low.h @@ -127,6 +127,9 @@ struct process_info_private =20 /* &_r_debug. 0 if not yet determined. -1 if no PT_DYNAMIC in Phdrs. = */ CORE_ADDR r_debug; + + /* The /proc/pid/mem file used for reading/writing memory. */ + int mem_fd; }; =20 struct lwp_info; @@ -163,10 +166,6 @@ public: =20 void store_registers (regcache *regcache, int regno) override; =20 - int prepare_to_access_memory () override; - - void done_accessing_memory () override; - int read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) override; =20 @@ -544,6 +543,10 @@ private: data. */ process_info *add_linux_process (int pid, int attached); =20 + /* Same as add_linux_process, but don't open the /proc/PID/mem file + yet. */ + process_info *add_linux_process_no_mem_file (int pid, int attached); + /* Add a new thread. */ lwp_info *add_lwp (ptid_t ptid);