[4/5] gdb/linux-nat: Read auxv from any thread of the process
Commit Message
If the initial thread of the process exits, reading the process' auxiliary
vector via /proc/PID/auxv fails in one of two ways:
1. If GDB is root, then opening the file succeeds but reading from it
returns 0 bytes.
2. If gdbserver isn't root, then opening the file fails with EACCES.
This race isn't easy to run into because one of the first things that GDB
does when starting an inferior is to read its auxiliary vector and store it
in the auxv cache. All further queries of the auxiliary vector will be
served from there, unless one of the cache-clearing events
("inferior_exit", "inferior_appeared", "executable_changed") occurs.
To fix the race, use linux_proc_read_auxv instead of the generic procfs
implementation.
Suggested-by: Pedro Alves <pedro@palves.net>
---
gdb/linux-nat.c | 40 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
@@ -3667,6 +3667,33 @@ linux_xfer_siginfo (ptid_t ptid, enum target_object object,
return TARGET_XFER_OK;
}
+/* Implement the to_xfer_partial target_ops method for TARGET_OBJECT_AUXV. This
+ function handles access via /proc/LWP/auxv, which allows handling possible
+ races in multi-threaded inferiors. */
+
+static enum target_xfer_status
+linux_nat_xfer_auxv (gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
+{
+ /* Linux doesn't support writing to the auxv file. */
+ if (readbuf == nullptr || writebuf != nullptr)
+ return TARGET_XFER_E_IO;
+
+ ssize_t xfered;
+ bool rc = linux_proc_read_auxv (inferior_ptid.pid (), readbuf, (off_t) offset,
+ (size_t) len, xfered);
+
+ if (!rc)
+ return TARGET_XFER_E_IO;
+ else if (xfered == 0)
+ return TARGET_XFER_EOF;
+ else
+ {
+ *xfered_len = (ULONGEST) xfered;
+ return TARGET_XFER_OK;
+ }
+}
+
static enum target_xfer_status
linux_nat_xfer_osdata (enum target_object object,
const char *annex, gdb_byte *readbuf,
@@ -3695,8 +3722,17 @@ linux_nat_target::xfer_partial (enum target_object object,
return TARGET_XFER_EOF;
if (object == TARGET_OBJECT_AUXV)
- return memory_xfer_auxv (this, object, annex, readbuf, writebuf,
- offset, len, xfered_len);
+ {
+ /* For attached inferiors, use memory_xfer_auxv's ld.so support which
+ works with virtual executables being executed by Valgrind's
+ memcheck. */
+ if (current_inferior ()->attach_flag)
+ return memory_xfer_auxv (this, object, annex, readbuf, writebuf,
+ offset, len, xfered_len);
+ else
+ return linux_nat_xfer_auxv (readbuf, writebuf, offset, len,
+ xfered_len);
+ }
if (object == TARGET_OBJECT_OSDATA)
return linux_nat_xfer_osdata (object, annex, readbuf, writebuf,