[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