[PATCH] elf: fix pldd on targets that map objects above 2^63 [BZ #34641]
Stian Halseth
stian@itx.no
Thu Sep 17 07:47:01 GMT 2026
Hi,
Sam commented on #34641, and I was made aware of the same type of fixed
bug in gdb: https://sourceware.org/bugzilla/show_bug.cgi?id=30525
I see that gdb falls back to ptrace, which it had used before it
switched to pread64.
pldd has no such prior path, so I opted to fall back to
process_vm_readv based on pread EINVAL.
Very much open to other ideas, and to change the approach.
--
Best regards
Stian Halseth
On Wed, 2026-09-16 at 20:02 +0200, Stian Halseth wrote:
> pldd reads target's memory with pread on /proc/PID/mem. The offset
> argument of pread is signed, and ksys_pread64 rejects a negative
> position. On sparc64 the dynamic linker and the shared objects are
> mapped at 0xfff8000100000000 and above, so every such read fails with
> EINVAL and pldd exits with "cannot read r_debug". elf/tst-pldd fails
> as a result. This is not visible on x86_64, where objects are mapped
> low.
>
> Route the reads through a helper that tries pread first and, when it
> fails with EINVAL, falls back to process_vm_readv, which takes the
> address as a pointer. pread stays the primary path so that a 32-bit
> pldd can still read a 64-bit target, which process_vm_readv cannot do
> once the address no longer fits a pointer; the fallback is taken only
> when it does fit. pldd already attaches with ptrace, so the
> permissions
> are in place, and both calls report a partial transfer as a short
> count,
> which the object-name loop in find_maps relies on.
>
> Tested on sparc64: pldd fails on every process before this change and
> lists them correctly after it. Under strace the reads of the main
> executable, which is mapped low, go through pread, and the reads that
> hit EINVAL, those into ld.so and libc above 2^63, are retried with
> process_vm_readv.
>
> Signed-off-by: Stian Halseth <stian@itx.no>
> ---
> elf/pldd-xx.c | 27 +++++++++++++++++++++------
> elf/pldd.c | 3 +++
> 2 files changed, 24 insertions(+), 6 deletions(-)
>
> diff --git a/elf/pldd-xx.c b/elf/pldd-xx.c
> index 29bbb7307..073b145f9 100644
> --- a/elf/pldd-xx.c
> +++ b/elf/pldd-xx.c
> @@ -73,6 +73,21 @@ _Static_assert (offsetof (struct r_debug, r_map)
> #endif
>
>
> +/* Read LEN bytes at ADDR in process PID into BUF. */
> +static ssize_t
> +E(read_mem) (int memfd, pid_t pid, void *buf, size_t len, EW(Addr)
> addr)
> +{
> + ssize_t n = pread (memfd, buf, len, addr);
> + if (n != -1)
> + return n;
> + if (errno != EINVAL || (EW(Addr)) (uintptr_t) addr != addr)
> + return -1;
> + struct iovec local = { .iov_base = buf, .iov_len = len };
> + struct iovec remote = { .iov_base = (void *) (uintptr_t) addr,
> + .iov_len = len };
> + return process_vm_readv (pid, &local, 1, &remote, 1, 0);
> +}
> +
> static int
>
> E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv,
> @@ -103,7 +118,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> pid, void *auxv,
> error (EXIT_FAILURE, 0, gettext ("cannot find program header of
> process"));
>
> EW(Phdr) *p = xmalloc (phnum * phent);
> - if (pread (memfd, p, phnum * phent, phdr) != phnum * phent)
> + if (E(read_mem) (memfd, pid, p, phnum * phent, phdr) != phnum *
> phent)
> error (EXIT_FAILURE, 0, gettext ("cannot read program header"));
>
> /* Determine the load offset. We need this for interpreting the
> @@ -124,7 +139,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> pid, void *auxv,
> if (p[i].p_type == PT_DYNAMIC)
> {
> EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
> - if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
> + if (E(read_mem) (memfd, pid, dyn, p[i].p_filesz, offset +
> p[i].p_vaddr)
> != p[i].p_filesz)
> error (EXIT_FAILURE, 0, gettext ("cannot read dynamic section"));
>
> @@ -136,7 +151,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> pid, void *auxv,
> if (off != 0)
> {
> struct E(r_debug) r;
> - if (pread (memfd, &r, sizeof (r), off)
> + if (E(read_mem) (memfd, pid, &r, sizeof (r), off)
> != sizeof (r))
> error (EXIT_FAILURE, 0, gettext ("cannot read r_debug"));
>
> @@ -154,7 +169,7 @@ E(find_maps) (const char *exe, int memfd, pid_t
> pid, void *auxv,
> else if (p[i].p_type == PT_INTERP)
> {
> interp = xmalloc (p[i].p_filesz);
> - if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
> + if (E(read_mem) (memfd, pid, interp, p[i].p_filesz, offset +
> p[i].p_vaddr)
> != p[i].p_filesz)
> error (EXIT_FAILURE, 0, gettext ("cannot read program
> interpreter"));
> }
> @@ -184,13 +199,13 @@ E(find_maps) (const char *exe, int memfd, pid_t
> pid, void *auxv,
> do
> {
> struct E(link_map) m;
> - if (pread (memfd, &m, sizeof (m), list) != sizeof (m))
> + if (E(read_mem) (memfd, pid, &m, sizeof (m), list) != sizeof
> (m))
> error (EXIT_FAILURE, 0, gettext ("cannot read link map"));
>
> EW(Addr) name_offset = m.l_name;
> while (1)
> {
> - ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length,
> name_offset);
> + ssize_t n = E(read_mem) (memfd, pid, tmpbuf.data, tmpbuf.length,
> name_offset);
> if (n == -1)
> error (EXIT_FAILURE, 0, gettext ("cannot read object name"));
>
> diff --git a/elf/pldd.c b/elf/pldd.c
> index 62e660c3f..762770026 100644
> --- a/elf/pldd.c
> +++ b/elf/pldd.c
> @@ -27,6 +27,9 @@
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/ptrace.h>
> +#include <sys/uio.h>
> +#include <stdint.h>
> +#include <errno.h>
> #include <sys/wait.h>
> #include <scratch_buffer.h>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: This is a digitally signed message part
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20260917/125d4c03/attachment.sig>
More information about the Libc-alpha
mailing list