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