This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] elf: Remove pldd (BZ#18035)


On 4/10/19 3:04 PM, Adhemerval Zanella wrote:
As reported in bugzilla itself and on man-pages [1] it has been
broken since 2.19.  Also, its design tie glibc internal definition
by duplicate struct layouts and dynamic loader objects.

I have a sustained objection to removing pldd.

Have we looked into why it's broken?

I'm happy to add a test-in-container runtime test for it so we can
see breakage when it happens.

It matches the same Solaris tool pldd, and is a useful utility.

We probably haven't seen a problem because enterprise uses might have
patched pldd to fix it, or RHEL7 which is on 2.17 still has a working
version.

Also, as noted by man-pages the same behavior can be obtained with

   $ gdb -ex "set confirm off" -ex "set height 0" -ex "info shared" \
                        -ex "quit" -p $pid | grep '^0x.*0x'

That relies on a debugger on the target system and it might be a problem
on production systems.


Or with external tools as 'lsof'.  An alternative way to show the
dynamic shared objects liked into a process on Linux would be to
use the /proc/<pid>/maps instead as following example:

---

import sys, os

def main(argv):
   with open('/proc/' + argv[0] + '/maps', 'r') as maps:
     segs = {}
     st = os.stat('/proc/' + argv[0] + '/exe')
     for line in maps:
       fields = line.split()
       if len(fields) < 5:
         continue
       major,minor = fields[3].split(':')
       dev = os.makedev(int(major), int(minor))
       inode = int(fields[4])
       if dev == 0 or inode == 0:
         continue
       # Skip the map entry if the device + inode pair match that of the exe.
       if dev == st.st_dev or inode == st.st_ino:
         continue

       # Check if file is accessible.
       try:
         segst = os.stat(fields[5])
       except OSError:
         continue

       # Print only executable segments.
       if 'x' in fields[1]:
         print(fields[5])

if __name__ == '__main__':
   main(sys.argv[1:])

This requires python in base runtime, and might not be on a production
container instance.

---

Checked on x86_64-linux-gnu.

	* elf/pldd-xx.c: Remove file.
	* elf/pldd.c: Likewise.
	* sysdeps/unix/sysv/linux/Makefile [$(subdir) == elf] (pldd): Remove
	rule.

What's wrong with fixing pldd?

[1] http://man7.org/linux/man-pages/man1/pldd.1.html
---
  elf/pldd-xx.c                    | 251 ----------------------
  elf/pldd.c                       | 344 -------------------------------
  sysdeps/unix/sysv/linux/Makefile |   4 -
  3 files changed, 599 deletions(-)
  delete mode 100644 elf/pldd-xx.c
  delete mode 100644 elf/pldd.c

diff --git a/elf/pldd-xx.c b/elf/pldd-xx.c
deleted file mode 100644
index 547f840ee1..0000000000
--- a/elf/pldd-xx.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/* Copyright (C) 2011-2019 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <http://www.gnu.org/licenses/>.  */
-
-#define E(name) E_(name, CLASS)
-#define E_(name, cl) E__(name, cl)
-#define E__(name, cl) name##cl
-#define EW(type) EW_(Elf, CLASS, type)
-#define EW_(e, w, t) EW__(e, w, _##t)
-#define EW__(e, w, t) e##w##t
-
-#define pldd_assert(name, exp) \
-  typedef int __assert_##name[((exp) != 0) - 1]
-
-
-struct E(link_map)
-{
-  EW(Addr) l_addr;
-  EW(Addr) l_name;
-  EW(Addr) l_ld;
-  EW(Addr) l_next;
-  EW(Addr) l_prev;
-  EW(Addr) l_real;
-  Lmid_t l_ns;
-  EW(Addr) l_libname;
-};
-#if CLASS == __ELF_NATIVE_CLASS
-pldd_assert (l_addr, (offsetof (struct link_map, l_addr)
-			== offsetof (struct E(link_map), l_addr)));
-pldd_assert (l_name, (offsetof (struct link_map, l_name)
-			== offsetof (struct E(link_map), l_name)));
-pldd_assert (l_next, (offsetof (struct link_map, l_next)
-			== offsetof (struct E(link_map), l_next)));
-#endif
-
-
-struct E(libname_list)
-{
-  EW(Addr) name;
-  EW(Addr) next;
-};
-#if CLASS == __ELF_NATIVE_CLASS
-pldd_assert (name, (offsetof (struct libname_list, name)
-		      == offsetof (struct E(libname_list), name)));
-pldd_assert (next, (offsetof (struct libname_list, next)
-		      == offsetof (struct E(libname_list), next)));
-#endif
-
-struct E(r_debug)
-{
-  int r_version;
-#if CLASS == 64
-  int pad;
-#endif
-  EW(Addr) r_map;
-};
-#if CLASS == __ELF_NATIVE_CLASS
-pldd_assert (r_version, (offsetof (struct r_debug, r_version)
-			   == offsetof (struct E(r_debug), r_version)));
-pldd_assert (r_map, (offsetof (struct r_debug, r_map)
-		       == offsetof (struct E(r_debug), r_map)));
-#endif
-
-
-static int
-
-E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
-{
-  EW(Addr) phdr = 0;
-  unsigned int phnum = 0;
-  unsigned int phent = 0;
-
-  EW(auxv_t) *auxvXX = (EW(auxv_t) *) auxv;
-  for (int i = 0; i < auxv_size / sizeof (EW(auxv_t)); ++i)
-    switch (auxvXX[i].a_type)
-      {
-      case AT_PHDR:
-	phdr = auxvXX[i].a_un.a_val;
-	break;
-      case AT_PHNUM:
-	phnum = auxvXX[i].a_un.a_val;
-	break;
-      case AT_PHENT:
-	phent = auxvXX[i].a_un.a_val;
-	break;
-      default:
-	break;
-      }
-
-  if (phdr == 0 || phnum == 0 || phent == 0)
-    error (EXIT_FAILURE, 0, gettext ("cannot find program header of process"));
-
-  EW(Phdr) *p = alloca (phnum * phent);
-  if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent)
-    {
-      error (0, 0, gettext ("cannot read program header"));
-      return EXIT_FAILURE;
-    }
-
-  /* Determine the load offset.  We need this for interpreting the
-     other program header entries so we do this in a separate loop.
-     Fortunately it is the first time unless someone does something
-     stupid when linking the application.  */
-  EW(Addr) offset = 0;
-  for (unsigned int i = 0; i < phnum; ++i)
-    if (p[i].p_type == PT_PHDR)
-      {
-	offset = phdr - p[i].p_vaddr;
-	break;
-      }
-
-  EW(Addr) list = 0;
-  char *interp = NULL;
-  for (unsigned int i = 0; i < phnum; ++i)
-    if (p[i].p_type == PT_DYNAMIC)
-      {
-	EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
-	if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
-	    != p[i].p_filesz)
-	  {
-	    error (0, 0, gettext ("cannot read dynamic section"));
-	    return EXIT_FAILURE;
-	  }
-
-	/* Search for the DT_DEBUG entry.  */
-	for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j)
-	  if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0)
-	    {
-	      struct E(r_debug) r;
-	      if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
-		  != sizeof (r))
-		{
-		  error (0, 0, gettext ("cannot read r_debug"));
-		  return EXIT_FAILURE;
-		}
-
-	      if (r.r_map != 0)
-		{
-		  list = r.r_map;
-		  break;
-		}
-	    }
-
-	free (dyn);
-	break;
-      }
-    else if (p[i].p_type == PT_INTERP)
-      {
-	interp = alloca (p[i].p_filesz);
-	if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
-	    != p[i].p_filesz)
-	  {
-	    error (0, 0, gettext ("cannot read program interpreter"));
-	    return EXIT_FAILURE;
-	  }
-      }
-
-  if (list == 0)
-    {
-      if (interp == NULL)
-	{
-	  // XXX check whether the executable itself is the loader
-	  return EXIT_FAILURE;
-	}
-
-      // XXX perhaps try finding ld.so and _r_debug in it
-
-      return EXIT_FAILURE;
-    }
-
-  /* Print the PID and program name first.  */
-  printf ("%lu:\t%s\n", (unsigned long int) pid, exe);
-
-  /* Iterate over the list of objects and print the information.  */
-  struct scratch_buffer tmpbuf;
-  scratch_buffer_init (&tmpbuf);
-  int status = 0;
-  do
-    {
-      struct E(link_map) m;
-      if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m))
-	{
-	  error (0, 0, gettext ("cannot read link map"));
-	  status = EXIT_FAILURE;
-	  goto out;
-	}
-
-      EW(Addr) name_offset = m.l_name;
-    again:
-      while (1)
-	{
-	  ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset);
-	  if (n == -1)
-	    {
-	      error (0, 0, gettext ("cannot read object name"));
-	      status = EXIT_FAILURE;
-	      goto out;
-	    }
-
-	  if (memchr (tmpbuf.data, '\0', n) != NULL)
-	    break;
-
-	  if (!scratch_buffer_grow (&tmpbuf))
-	    {
-	      error (0, 0, gettext ("cannot allocate buffer for object name"));
-	      status = EXIT_FAILURE;
-	      goto out;
-	    }
-	}
-
-      if (((char *)tmpbuf.data)[0] == '\0' && name_offset == m.l_name
-	  && m.l_libname != 0)
-	{
-	  /* Try the l_libname element.  */
-	  struct E(libname_list) ln;
-	  if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln))
-	    {
-	      name_offset = ln.name;
-	      goto again;
-	    }
-	}
-
-      /* Skip over the executable.  */
-      if (((char *)tmpbuf.data)[0] != '\0')
-	printf ("%s\n", (char *)tmpbuf.data);
-
-      list = m.l_next;
-    }
-  while (list != 0);
-
- out:
-  scratch_buffer_free (&tmpbuf);
-  return status;
-}
-
-
-#undef CLASS
diff --git a/elf/pldd.c b/elf/pldd.c
deleted file mode 100644
index f3fac4e487..0000000000
--- a/elf/pldd.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/* List dynamic shared objects linked into given process.
-   Copyright (C) 2011-2019 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <http://www.gnu.org/licenses/>.  */
-
-#include <alloca.h>
-#include <argp.h>
-#include <assert.h>
-#include <dirent.h>
-#include <elf.h>
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <link.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/ptrace.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <scratch_buffer.h>
-
-#include <ldsodefs.h>
-#include <version.h>
-
-/* Global variables.  */
-extern char *program_invocation_short_name;
-#define PACKAGE _libc_intl_domainname
-
-/* External functions.  */
-#include <programs/xmalloc.h>
-
-/* Name and version of program.  */
-static void print_version (FILE *stream, struct argp_state *state);
-void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
-
-/* Function to print some extra text in the help message.  */
-static char *more_help (int key, const char *text, void *input);
-
-/* Definitions of arguments for argp functions.  */
-static const struct argp_option options[] =
-{
-  { NULL, 0, NULL, 0, NULL }
-};
-
-/* Short description of program.  */
-static const char doc[] = N_("\
-List dynamic shared objects loaded into process.");
-
-/* Strings for arguments in help texts.  */
-static const char args_doc[] = N_("PID");
-
-/* Prototype for option handler.  */
-static error_t parse_opt (int key, char *arg, struct argp_state *state);
-
-/* Data structure to communicate with argp functions.  */
-static struct argp argp =
-{
-  options, parse_opt, args_doc, doc, NULL, more_help, NULL
-};
-
-// File descriptor of /proc/*/mem file.
-static int memfd;
-
-/* Name of the executable  */
-static char *exe;
-
-/* Local functions.  */
-static int get_process_info (int dfd, long int pid);
-static void wait_for_ptrace_stop (long int pid);
-
-
-int
-main (int argc, char *argv[])
-{
-  /* Parse and process arguments.  */
-  int remaining;
-  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
-
-  if (remaining != argc - 1)
-    {
-      fprintf (stderr,
-	       gettext ("Exactly one parameter with process ID required.\n"));
-      argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
-      return 1;
-    }
-
-  assert (sizeof (pid_t) == sizeof (int)
-	  || sizeof (pid_t) == sizeof (long int));
-  char *endp;
-  errno = 0;
-  long int pid = strtol (argv[remaining], &endp, 10);
-  if (pid < 0 || (pid == ULONG_MAX && errno == ERANGE) || *endp != '\0'
-      || (sizeof (pid_t) < sizeof (pid) && pid > INT_MAX))
-    error (EXIT_FAILURE, 0, gettext ("invalid process ID '%s'"),
-	   argv[remaining]);
-
-  /* Determine the program name.  */
-  char buf[7 + 3 * sizeof (pid)];
-  snprintf (buf, sizeof (buf), "/proc/%lu", pid);
-  int dfd = open (buf, O_RDONLY | O_DIRECTORY);
-  if (dfd == -1)
-    error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf);
-
-  struct scratch_buffer exebuf;
-  scratch_buffer_init (&exebuf);
-  ssize_t nexe;
-  while ((nexe = readlinkat (dfd, "exe",
-			     exebuf.data, exebuf.length)) == exebuf.length)
-    {
-      if (!scratch_buffer_grow (&exebuf))
-	{
-	  nexe = -1;
-	  break;
-	}
-    }
-  if (nexe == -1)
-    exe = (char *) "<program name undetermined>";
-  else
-    {
-      exe = exebuf.data;
-      exe[nexe] = '\0';
-    }
-
-  /* Stop all threads since otherwise the list of loaded modules might
-     change while we are reading it.  */
-  struct thread_list
-  {
-    pid_t tid;
-    struct thread_list *next;
-  } *thread_list = NULL;
-
-  int taskfd = openat (dfd, "task", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
-  if (taskfd == 1)
-    error (EXIT_FAILURE, errno, gettext ("cannot open %s/task"), buf);
-  DIR *dir = fdopendir (taskfd);
-  if (dir == NULL)
-    error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"),
-	   buf);
-
-  struct dirent64 *d;
-  while ((d = readdir64 (dir)) != NULL)
-    {
-      if (! isdigit (d->d_name[0]))
-	continue;
-
-      errno = 0;
-      long int tid = strtol (d->d_name, &endp, 10);
-      if (tid < 0 || (tid == ULONG_MAX && errno == ERANGE) || *endp != '\0'
-	  || (sizeof (pid_t) < sizeof (pid) && tid > INT_MAX))
-	error (EXIT_FAILURE, 0, gettext ("invalid thread ID '%s'"),
-	       d->d_name);
-
-      if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
-	{
-	  /* There might be a race between reading the directory and
-	     threads terminating.  Ignore errors attaching to unknown
-	     threads unless this is the main thread.  */
-	  if (errno == ESRCH && tid != pid)
-	    continue;
-
-	  error (EXIT_FAILURE, errno, gettext ("cannot attach to process %lu"),
-		 tid);
-	}
-
-      wait_for_ptrace_stop (tid);
-
-      struct thread_list *newp = alloca (sizeof (*newp));
-      newp->tid = tid;
-      newp->next = thread_list;
-      thread_list = newp;
-    }
-
-  closedir (dir);
-
-  int status = get_process_info (dfd, pid);
-
-  assert (thread_list != NULL);
-  do
-    {
-      ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL);
-      thread_list = thread_list->next;
-    }
-  while (thread_list != NULL);
-
-  close (dfd);
-
-  return status;
-}
-
-
-/* Wait for PID to enter ptrace-stop state after being attached.  */
-static void
-wait_for_ptrace_stop (long int pid)
-{
-  int status;
-
-  /* While waiting for SIGSTOP being delivered to the tracee we have to
-     reinject any other pending signal.  Ignore all other errors.  */
-  while (waitpid (pid, &status, __WALL) == pid && WIFSTOPPED (status))
-    {
-      /* The STOP signal should not be delivered to the tracee.  */
-      if (WSTOPSIG (status) == SIGSTOP)
-	return;
-      if (ptrace (PTRACE_CONT, pid, NULL,
-		  (void *) (uintptr_t) WSTOPSIG (status)))
-	/* The only possible error is that the process died.  */
-	return;
-    }
-}
-
-
-/* Handle program arguments.  */
-static error_t
-parse_opt (int key, char *arg, struct argp_state *state)
-{
-  switch (key)
-    {
-    default:
-      return ARGP_ERR_UNKNOWN;
-    }
-  return 0;
-}
-
-
-/* Print bug-reporting information in the help message.  */
-static char *
-more_help (int key, const char *text, void *input)
-{
-  char *tp = NULL;
-  switch (key)
-    {
-    case ARGP_KEY_HELP_EXTRA:
-      /* We print some extra information.  */
-      if (asprintf (&tp, gettext ("\
-For bug reporting instructions, please see:\n\
-%s.\n"), REPORT_BUGS_TO) < 0)
-	return NULL;
-      return tp;
-    default:
-      break;
-    }
-  return (char *) text;
-}
-
-/* Print the version information.  */
-static void
-print_version (FILE *stream, struct argp_state *state)
-{
-  fprintf (stream, "pldd %s%s\n", PKGVERSION, VERSION);
-  fprintf (stream, gettext ("\
-Copyright (C) %s Free Software Foundation, Inc.\n\
-This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2019");
-  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
-}
-
-
-#define CLASS 32
-#include "pldd-xx.c"
-#define CLASS 64
-#include "pldd-xx.c"
-
-
-static int
-get_process_info (int dfd, long int pid)
-{
-  memfd = openat (dfd, "mem", O_RDONLY);
-  if (memfd == -1)
-    goto no_info;
-
-  int fd = openat (dfd, "exe", O_RDONLY);
-  if (fd == -1)
-    {
-    no_info:
-      error (0, errno, gettext ("cannot get information about process %lu"),
-	     pid);
-      return EXIT_FAILURE;
-    }
-
-  char e_ident[EI_NIDENT];
-  if (read (fd, e_ident, EI_NIDENT) != EI_NIDENT)
-    goto no_info;
-
-  close (fd);
-
-  if (memcmp (e_ident, ELFMAG, SELFMAG) != 0)
-    {
-      error (0, 0, gettext ("process %lu is no ELF program"), pid);
-      return EXIT_FAILURE;
-    }
-
-  fd = openat (dfd, "auxv", O_RDONLY);
-  if (fd == -1)
-    goto no_info;
-
-  size_t auxv_size = 0;
-  void *auxv = NULL;
-  while (1)
-    {
-      auxv_size += 512;
-      auxv = xrealloc (auxv, auxv_size);
-
-      ssize_t n = pread (fd, auxv, auxv_size, 0);
-      if (n < 0)
-	goto no_info;
-      if (n < auxv_size)
-	{
-	  auxv_size = n;
-	  break;
-	}
-    }
-
-  close (fd);
-
-  int retval;
-  if (e_ident[EI_CLASS] == ELFCLASS32)
-    retval = find_maps32 (pid, auxv, auxv_size);
-  else
-    retval = find_maps64 (pid, auxv, auxv_size);
-
-  free (auxv);
-  close (memfd);
-
-  return retval;
-}
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 52ac6ad484..e799473b17 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -212,10 +212,6 @@ sysdep-rtld-routines += dl-brk dl-sbrk dl-getcwd dl-openat64 dl-opendir \
  			dl-fxstatat64
libof-lddlibc4 = lddlibc4
-
-others += pldd
-install-bin += pldd
-$(objpfx)pldd: $(objpfx)xmalloc.o
  endif
ifeq ($(subdir),rt)



--
Cheers,
Carlos.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]