]>
Commit | Line | Data |
---|---|---|
11988f8f | 1 | /* List dynamic shared objects linked into given process. |
d4697bc9 | 2 | Copyright (C) 2011-2014 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> | |
37 | ||
38 | #include <ldsodefs.h> | |
39 | #include <version.h> | |
40 | ||
41 | /* Global variables. */ | |
42 | extern char *program_invocation_short_name; | |
43 | #define PACKAGE _libc_intl_domainname | |
44 | ||
45 | /* External functions. */ | |
6ff444c4 | 46 | #include <programs/xmalloc.h> |
11988f8f UD |
47 | |
48 | /* Name and version of program. */ | |
49 | static void print_version (FILE *stream, struct argp_state *state); | |
50 | void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; | |
51 | ||
8b748aed JM |
52 | /* Function to print some extra text in the help message. */ |
53 | static char *more_help (int key, const char *text, void *input); | |
11988f8f UD |
54 | |
55 | /* Definitions of arguments for argp functions. */ | |
56 | static const struct argp_option options[] = | |
57 | { | |
58 | { NULL, 0, NULL, 0, NULL } | |
59 | }; | |
60 | ||
61 | /* Short description of program. */ | |
62 | static const char doc[] = N_("\ | |
63 | List dynamic shared objects loaded into process."); | |
64 | ||
65 | /* Strings for arguments in help texts. */ | |
66 | static const char args_doc[] = N_("PID"); | |
67 | ||
68 | /* Prototype for option handler. */ | |
69 | static error_t parse_opt (int key, char *arg, struct argp_state *state); | |
70 | ||
71 | /* Data structure to communicate with argp functions. */ | |
72 | static struct argp argp = | |
73 | { | |
8b748aed | 74 | options, parse_opt, args_doc, doc, NULL, more_help, NULL |
11988f8f UD |
75 | }; |
76 | ||
77 | // File descriptor of /proc/*/mem file. | |
78 | static int memfd; | |
79 | ||
80 | /* Name of the executable */ | |
81 | static char *exe; | |
82 | ||
83 | /* Local functions. */ | |
27724598 | 84 | static int get_process_info (int dfd, long int pid); |
11988f8f UD |
85 | |
86 | ||
87 | int | |
88 | main (int argc, char *argv[]) | |
89 | { | |
90 | /* Parse and process arguments. */ | |
91 | int remaining; | |
92 | argp_parse (&argp, argc, argv, 0, &remaining, NULL); | |
93 | ||
94 | if (remaining != argc - 1) | |
95 | { | |
96 | fprintf (stderr, | |
97 | gettext ("Exactly one parameter with process ID required.\n")); | |
98 | argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); | |
99 | return 1; | |
100 | } | |
101 | ||
27724598 UD |
102 | assert (sizeof (pid_t) == sizeof (int) |
103 | || sizeof (pid_t) == sizeof (long int)); | |
11988f8f UD |
104 | char *endp; |
105 | errno = 0; | |
27724598 UD |
106 | long int pid = strtol (argv[remaining], &endp, 10); |
107 | if (pid < 0 || (pid == ULONG_MAX && errno == ERANGE) || *endp != '\0' | |
108 | || (sizeof (pid_t) < sizeof (pid) && pid > INT_MAX)) | |
11988f8f UD |
109 | error (EXIT_FAILURE, 0, gettext ("invalid process ID '%s'"), |
110 | argv[remaining]); | |
111 | ||
112 | /* Determine the program name. */ | |
27724598 UD |
113 | char buf[7 + 3 * sizeof (pid)]; |
114 | snprintf (buf, sizeof (buf), "/proc/%lu", pid); | |
115 | int dfd = open (buf, O_RDONLY | O_DIRECTORY); | |
116 | if (dfd == -1) | |
117 | error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf); | |
118 | ||
11988f8f | 119 | size_t exesize = 1024; |
27724598 UD |
120 | #ifdef PATH_MAX |
121 | exesize = PATH_MAX; | |
122 | #endif | |
11988f8f UD |
123 | exe = alloca (exesize); |
124 | ssize_t nexe; | |
27724598 | 125 | while ((nexe = readlinkat (dfd, "exe", exe, exesize)) == exesize) |
11988f8f UD |
126 | extend_alloca (exe, exesize, 2 * exesize); |
127 | if (nexe == -1) | |
128 | exe = (char *) "<program name undetermined>"; | |
129 | else | |
130 | exe[nexe] = '\0'; | |
131 | ||
27724598 UD |
132 | /* Stop all threads since otherwise the list of loaded modules might |
133 | change while we are reading it. */ | |
134 | struct thread_list | |
135 | { | |
136 | pid_t tid; | |
137 | struct thread_list *next; | |
138 | } *thread_list = NULL; | |
139 | ||
140 | int taskfd = openat (dfd, "task", O_RDONLY | O_DIRECTORY | O_CLOEXEC); | |
141 | if (taskfd == 1) | |
142 | error (EXIT_FAILURE, errno, gettext ("cannot open %s/task"), buf); | |
143 | DIR *dir = fdopendir (taskfd); | |
144 | if (dir == NULL) | |
145 | error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"), | |
146 | buf); | |
147 | ||
148 | struct dirent64 *d; | |
149 | while ((d = readdir64 (dir)) != NULL) | |
150 | { | |
151 | if (! isdigit (d->d_name[0])) | |
152 | continue; | |
153 | ||
154 | errno = 0; | |
155 | long int tid = strtol (d->d_name, &endp, 10); | |
156 | if (tid < 0 || (tid == ULONG_MAX && errno == ERANGE) || *endp != '\0' | |
157 | || (sizeof (pid_t) < sizeof (pid) && tid > INT_MAX)) | |
158 | error (EXIT_FAILURE, 0, gettext ("invalid thread ID '%s'"), | |
159 | d->d_name); | |
160 | ||
161 | if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) | |
162 | { | |
163 | /* There might be a race between reading the directory and | |
164 | threads terminating. Ignore errors attaching to unknown | |
165 | threads unless this is the main thread. */ | |
166 | if (errno == ESRCH && tid != pid) | |
167 | continue; | |
168 | ||
169 | error (EXIT_FAILURE, errno, gettext ("cannot attach to process %lu"), | |
170 | tid); | |
171 | } | |
172 | ||
173 | struct thread_list *newp = alloca (sizeof (*newp)); | |
174 | newp->tid = tid; | |
175 | newp->next = thread_list; | |
176 | thread_list = newp; | |
177 | } | |
178 | ||
179 | closedir (dir); | |
11988f8f | 180 | |
27724598 UD |
181 | int status = get_process_info (dfd, pid); |
182 | ||
183 | assert (thread_list != NULL); | |
184 | do | |
185 | { | |
186 | ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL); | |
187 | thread_list = thread_list->next; | |
188 | } | |
189 | while (thread_list != NULL); | |
11988f8f | 190 | |
27724598 | 191 | close (dfd); |
11988f8f UD |
192 | |
193 | return status; | |
194 | } | |
195 | ||
196 | ||
197 | /* Handle program arguments. */ | |
198 | static error_t | |
199 | parse_opt (int key, char *arg, struct argp_state *state) | |
200 | { | |
201 | switch (key) | |
202 | { | |
203 | default: | |
204 | return ARGP_ERR_UNKNOWN; | |
205 | } | |
206 | return 0; | |
207 | } | |
208 | ||
209 | ||
8b748aed JM |
210 | /* Print bug-reporting information in the help message. */ |
211 | static char * | |
212 | more_help (int key, const char *text, void *input) | |
213 | { | |
214 | char *tp = NULL; | |
215 | switch (key) | |
216 | { | |
217 | case ARGP_KEY_HELP_EXTRA: | |
218 | /* We print some extra information. */ | |
219 | if (asprintf (&tp, gettext ("\ | |
220 | For bug reporting instructions, please see:\n\ | |
221 | %s.\n"), REPORT_BUGS_TO) < 0) | |
222 | return NULL; | |
223 | return tp; | |
224 | default: | |
225 | break; | |
226 | } | |
227 | return (char *) text; | |
228 | } | |
229 | ||
11988f8f UD |
230 | /* Print the version information. */ |
231 | static void | |
232 | print_version (FILE *stream, struct argp_state *state) | |
233 | { | |
8b748aed | 234 | fprintf (stream, "pldd %s%s\n", PKGVERSION, VERSION); |
11988f8f UD |
235 | fprintf (stream, gettext ("\ |
236 | Copyright (C) %s Free Software Foundation, Inc.\n\ | |
237 | This is free software; see the source for copying conditions. There is NO\n\ | |
238 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ | |
0549fbba | 239 | "), "2013"); |
11988f8f UD |
240 | fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); |
241 | } | |
242 | ||
243 | ||
244 | #define CLASS 32 | |
245 | #include "pldd-xx.c" | |
246 | #define CLASS 64 | |
247 | #include "pldd-xx.c" | |
248 | ||
249 | ||
250 | static int | |
27724598 | 251 | get_process_info (int dfd, long int pid) |
11988f8f | 252 | { |
27724598 | 253 | memfd = openat (dfd, "mem", O_RDONLY); |
11988f8f UD |
254 | if (memfd == -1) |
255 | goto no_info; | |
256 | ||
27724598 | 257 | int fd = openat (dfd, "exe", O_RDONLY); |
11988f8f UD |
258 | if (fd == -1) |
259 | { | |
260 | no_info: | |
261 | error (0, errno, gettext ("cannot get information about process %lu"), | |
27724598 | 262 | pid); |
11988f8f UD |
263 | return EXIT_FAILURE; |
264 | } | |
265 | ||
6585cb60 UD |
266 | char e_ident[EI_NIDENT]; |
267 | if (read (fd, e_ident, EI_NIDENT) != EI_NIDENT) | |
11988f8f UD |
268 | goto no_info; |
269 | ||
270 | close (fd); | |
271 | ||
6585cb60 | 272 | if (memcmp (e_ident, ELFMAG, SELFMAG) != 0) |
11988f8f | 273 | { |
27724598 | 274 | error (0, 0, gettext ("process %lu is no ELF program"), pid); |
11988f8f UD |
275 | return EXIT_FAILURE; |
276 | } | |
277 | ||
27724598 | 278 | fd = openat (dfd, "auxv", O_RDONLY); |
11988f8f UD |
279 | if (fd == -1) |
280 | goto no_info; | |
281 | ||
282 | size_t auxv_size = 0; | |
283 | void *auxv = NULL; | |
284 | while (1) | |
285 | { | |
286 | auxv_size += 512; | |
287 | auxv = xrealloc (auxv, auxv_size); | |
288 | ||
c5305d88 | 289 | ssize_t n = pread (fd, auxv, auxv_size, 0); |
11988f8f UD |
290 | if (n < 0) |
291 | goto no_info; | |
292 | if (n < auxv_size) | |
293 | { | |
294 | auxv_size = n; | |
295 | break; | |
296 | } | |
297 | } | |
298 | ||
299 | close (fd); | |
300 | ||
27724598 | 301 | int retval; |
6585cb60 UD |
302 | if (e_ident[EI_CLASS] == ELFCLASS32) |
303 | retval = find_maps32 (pid, auxv, auxv_size); | |
11988f8f | 304 | else |
6585cb60 | 305 | retval = find_maps64 (pid, auxv, auxv_size); |
27724598 UD |
306 | |
307 | free (auxv); | |
308 | close (memfd); | |
309 | ||
310 | return retval; | |
11988f8f | 311 | } |