(root)/
glibc-2.38/
elf/
pldd.c
       1  /* List dynamic shared objects linked into given process.
       2     Copyright (C) 2011-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #define _FILE_OFFSET_BITS 64
      20  
      21  #include <argp.h>
      22  #include <dirent.h>
      23  #include <error.h>
      24  #include <fcntl.h>
      25  #include <libintl.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <unistd.h>
      29  #include <sys/ptrace.h>
      30  #include <sys/wait.h>
      31  #include <scratch_buffer.h>
      32  
      33  #include <ldsodefs.h>
      34  #include <version.h>
      35  
      36  /* Global variables.  */
      37  extern char *program_invocation_short_name;
      38  #define PACKAGE _libc_intl_domainname
      39  
      40  /* External functions.  */
      41  #include <programs/xmalloc.h>
      42  
      43  /* Name and version of program.  */
      44  static void print_version (FILE *stream, struct argp_state *state);
      45  void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
      46  
      47  /* Function to print some extra text in the help message.  */
      48  static char *more_help (int key, const char *text, void *input);
      49  
      50  /* Definitions of arguments for argp functions.  */
      51  static const struct argp_option options[] =
      52  {
      53    { NULL, 0, NULL, 0, NULL }
      54  };
      55  
      56  /* Short description of program.  */
      57  static const char doc[] = N_("\
      58  List dynamic shared objects loaded into process.");
      59  
      60  /* Strings for arguments in help texts.  */
      61  static const char args_doc[] = N_("PID");
      62  
      63  /* Prototype for option handler.  */
      64  static error_t parse_opt (int key, char *arg, struct argp_state *state);
      65  
      66  /* Data structure to communicate with argp functions.  */
      67  static struct argp argp =
      68  {
      69    options, parse_opt, args_doc, doc, NULL, more_help, NULL
      70  };
      71  
      72  
      73  /* Local functions.  */
      74  static int get_process_info (const char *exe, int dfd, long int pid);
      75  static void wait_for_ptrace_stop (long int pid);
      76  
      77  
      78  int
      79  main (int argc, char *argv[])
      80  {
      81    /* Parse and process arguments.  */
      82    int remaining;
      83    argp_parse (&argp, argc, argv, 0, &remaining, NULL);
      84  
      85    if (remaining != argc - 1)
      86      {
      87        fprintf (stderr,
      88  	       gettext ("Exactly one parameter with process ID required.\n"));
      89        argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
      90        return 1;
      91      }
      92  
      93    _Static_assert (sizeof (pid_t) == sizeof (int)
      94  		  || sizeof (pid_t) == sizeof (long int),
      95  		  "sizeof (pid_t) != sizeof (int) or sizeof (long int)");
      96  
      97    char *endp;
      98    errno = 0;
      99    long int pid = strtol (argv[remaining], &endp, 10);
     100    if (pid < 0 || (pid == ULONG_MAX && errno == ERANGE) || *endp != '\0'
     101        || (sizeof (pid_t) < sizeof (pid) && pid > INT_MAX))
     102      error (EXIT_FAILURE, 0, gettext ("invalid process ID '%s'"),
     103  	   argv[remaining]);
     104  
     105    /* Determine the program name.  */
     106    char buf[7 + 3 * sizeof (pid)];
     107    snprintf (buf, sizeof (buf), "/proc/%lu", pid);
     108    int dfd = open (buf, O_RDONLY | O_DIRECTORY);
     109    if (dfd == -1)
     110      error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf);
     111  
     112    /* Name of the executable  */
     113    struct scratch_buffer exe;
     114    scratch_buffer_init (&exe);
     115    ssize_t nexe;
     116    while ((nexe = readlinkat (dfd, "exe",
     117  			     exe.data, exe.length)) == exe.length)
     118      {
     119        if (!scratch_buffer_grow (&exe))
     120  	{
     121  	  nexe = -1;
     122  	  break;
     123  	}
     124      }
     125    if (nexe == -1)
     126      /* Default stack allocation is at least 1024.  */
     127      snprintf (exe.data, exe.length, "<program name undetermined>");
     128    else
     129      ((char*)exe.data)[nexe] = '\0';
     130  
     131    /* Stop all threads since otherwise the list of loaded modules might
     132       change while we are reading it.  */
     133    struct thread_list
     134    {
     135      pid_t tid;
     136      struct thread_list *next;
     137    } *thread_list = NULL;
     138  
     139    int taskfd = openat (dfd, "task", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
     140    if (taskfd == 1)
     141      error (EXIT_FAILURE, errno, gettext ("cannot open %s/task"), buf);
     142    DIR *dir = fdopendir (taskfd);
     143    if (dir == NULL)
     144      error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"),
     145  	   buf);
     146  
     147    struct dirent *d;
     148    while ((d = readdir (dir)) != NULL)
     149      {
     150        if (! isdigit (d->d_name[0]))
     151  	continue;
     152  
     153        errno = 0;
     154        long int tid = strtol (d->d_name, &endp, 10);
     155        if (tid < 0 || (tid == ULONG_MAX && errno == ERANGE) || *endp != '\0'
     156  	  || (sizeof (pid_t) < sizeof (pid) && tid > INT_MAX))
     157  	error (EXIT_FAILURE, 0, gettext ("invalid thread ID '%s'"),
     158  	       d->d_name);
     159  
     160        if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
     161  	{
     162  	  /* There might be a race between reading the directory and
     163  	     threads terminating.  Ignore errors attaching to unknown
     164  	     threads unless this is the main thread.  */
     165  	  if (errno == ESRCH && tid != pid)
     166  	    continue;
     167  
     168  	  error (EXIT_FAILURE, errno, gettext ("cannot attach to process %lu"),
     169  		 tid);
     170  	}
     171  
     172        wait_for_ptrace_stop (tid);
     173  
     174        struct thread_list *newp = xmalloc (sizeof (*newp));
     175        newp->tid = tid;
     176        newp->next = thread_list;
     177        thread_list = newp;
     178      }
     179  
     180    closedir (dir);
     181  
     182    if (thread_list == NULL)
     183      error (EXIT_FAILURE, 0, gettext ("no valid %s/task entries"), buf);
     184  
     185    int status = get_process_info (exe.data, dfd, pid);
     186  
     187    do
     188      {
     189        ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL);
     190        struct thread_list *prev = thread_list;
     191        thread_list = thread_list->next;
     192        free (prev);
     193      }
     194    while (thread_list != NULL);
     195  
     196    close (dfd);
     197    scratch_buffer_free (&exe);
     198  
     199    return status;
     200  }
     201  
     202  
     203  /* Wait for PID to enter ptrace-stop state after being attached.  */
     204  static void
     205  wait_for_ptrace_stop (long int pid)
     206  {
     207    int status;
     208  
     209    /* While waiting for SIGSTOP being delivered to the tracee we have to
     210       reinject any other pending signal.  Ignore all other errors.  */
     211    while (waitpid (pid, &status, __WALL) == pid && WIFSTOPPED (status))
     212      {
     213        /* The STOP signal should not be delivered to the tracee.  */
     214        if (WSTOPSIG (status) == SIGSTOP)
     215  	return;
     216        if (ptrace (PTRACE_CONT, pid, NULL,
     217  		  (void *) (uintptr_t) WSTOPSIG (status)))
     218  	/* The only possible error is that the process died.  */
     219  	return;
     220      }
     221  }
     222  
     223  
     224  /* Handle program arguments.  */
     225  static error_t
     226  parse_opt (int key, char *arg, struct argp_state *state)
     227  {
     228    switch (key)
     229      {
     230      default:
     231        return ARGP_ERR_UNKNOWN;
     232      }
     233    return 0;
     234  }
     235  
     236  
     237  /* Print bug-reporting information in the help message.  */
     238  static char *
     239  more_help (int key, const char *text, void *input)
     240  {
     241    char *tp = NULL;
     242    switch (key)
     243      {
     244      case ARGP_KEY_HELP_EXTRA:
     245        /* We print some extra information.  */
     246        if (asprintf (&tp, gettext ("\
     247  For bug reporting instructions, please see:\n\
     248  %s.\n"), REPORT_BUGS_TO) < 0)
     249  	return NULL;
     250        return tp;
     251      default:
     252        break;
     253      }
     254    return (char *) text;
     255  }
     256  
     257  /* Print the version information.  */
     258  static void
     259  print_version (FILE *stream, struct argp_state *state)
     260  {
     261    fprintf (stream, "pldd %s%s\n", PKGVERSION, VERSION);
     262    fprintf (stream, gettext ("\
     263  Copyright (C) %s Free Software Foundation, Inc.\n\
     264  This is free software; see the source for copying conditions.  There is NO\n\
     265  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
     266  "), "2023");
     267    fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
     268  }
     269  
     270  
     271  #define CLASS 32
     272  #include "pldd-xx.c"
     273  #define CLASS 64
     274  #include "pldd-xx.c"
     275  
     276  
     277  static int
     278  get_process_info (const char *exe, int dfd, long int pid)
     279  {
     280    /* File descriptor of /proc/<pid>/mem file.  */
     281    int memfd = openat (dfd, "mem", O_RDONLY);
     282    if (memfd == -1)
     283      goto no_info;
     284  
     285    int fd = openat (dfd, "exe", O_RDONLY);
     286    if (fd == -1)
     287      {
     288      no_info:
     289        error (0, errno, gettext ("cannot get information about process %lu"),
     290  	     pid);
     291        return EXIT_FAILURE;
     292      }
     293  
     294    char e_ident[EI_NIDENT];
     295    if (read (fd, e_ident, EI_NIDENT) != EI_NIDENT)
     296      goto no_info;
     297  
     298    close (fd);
     299  
     300    if (memcmp (e_ident, ELFMAG, SELFMAG) != 0)
     301      {
     302        error (0, 0, gettext ("process %lu is no ELF program"), pid);
     303        return EXIT_FAILURE;
     304      }
     305  
     306    fd = openat (dfd, "auxv", O_RDONLY);
     307    if (fd == -1)
     308      goto no_info;
     309  
     310    size_t auxv_size = 0;
     311    void *auxv = NULL;
     312    while (1)
     313      {
     314        auxv_size += 512;
     315        auxv = xrealloc (auxv, auxv_size);
     316  
     317        ssize_t n = pread (fd, auxv, auxv_size, 0);
     318        if (n < 0)
     319  	goto no_info;
     320        if (n < auxv_size)
     321  	{
     322  	  auxv_size = n;
     323  	  break;
     324  	}
     325      }
     326  
     327    close (fd);
     328  
     329    int retval;
     330    if (e_ident[EI_CLASS] == ELFCLASS32)
     331      retval = find_maps32 (exe, memfd, pid, auxv, auxv_size);
     332    else
     333      retval = find_maps64 (exe, memfd, pid, auxv, auxv_size);
     334  
     335    free (auxv);
     336    close (memfd);
     337  
     338    return retval;
     339  }