Branch data Line data Source code
1 : : /* Standard libdwfl callbacks for debugging a live Linux process.
2 : : Copyright (C) 2005-2010 Red Hat, Inc.
3 : : This file is part of elfutils.
4 : :
5 : : This file is free software; you can redistribute it and/or modify
6 : : it under the terms of either
7 : :
8 : : * the GNU Lesser General Public License as published by the Free
9 : : Software Foundation; either version 3 of the License, or (at
10 : : your option) any later version
11 : :
12 : : or
13 : :
14 : : * the GNU General Public License as published by the Free
15 : : Software Foundation; either version 2 of the License, or (at
16 : : your option) any later version
17 : :
18 : : or both in parallel, as here.
19 : :
20 : : elfutils is distributed in the hope that it will be useful, but
21 : : WITHOUT ANY WARRANTY; without even the implied warranty of
22 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 : : General Public License for more details.
24 : :
25 : : You should have received copies of the GNU General Public License and
26 : : the GNU Lesser General Public License along with this program. If
27 : : not, see <http://www.gnu.org/licenses/>. */
28 : :
29 : : #include "libdwflP.h"
30 : : #include <inttypes.h>
31 : : #include <sys/types.h>
32 : : #include <errno.h>
33 : : #include <stdio.h>
34 : : #include <stdio_ext.h>
35 : : #include <stdbool.h>
36 : : #include <string.h>
37 : : #include <stdlib.h>
38 : : #include <fcntl.h>
39 : : #include <unistd.h>
40 : : #include <assert.h>
41 : : #include <endian.h>
42 : : #include "system.h"
43 : :
44 : :
45 : : #define PROCMAPSFMT "/proc/%d/maps"
46 : : #define PROCMEMFMT "/proc/%d/mem"
47 : : #define PROCAUXVFMT "/proc/%d/auxv"
48 : : #define PROCEXEFMT "/proc/%d/exe"
49 : :
50 : :
51 : : /* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable. Return
52 : : ELFCLASSNONE for an error. */
53 : :
54 : : static unsigned char
55 : 0 : get_pid_class (pid_t pid)
56 : : {
57 : : char *fname;
58 [ # # ]: 0 : if (asprintf (&fname, PROCEXEFMT, pid) < 0)
59 : : return ELFCLASSNONE;
60 : :
61 : 0 : int fd = open64 (fname, O_RDONLY);
62 : 0 : free (fname);
63 [ # # ]: 0 : if (fd < 0)
64 : : return ELFCLASSNONE;
65 : :
66 : : unsigned char buf[EI_CLASS + 1];
67 : 0 : ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
68 : 0 : close (fd);
69 [ # # ][ # # ]: 0 : if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0
70 [ # # ][ # # ]: 0 : || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
71 [ # # ]: 0 : || buf[EI_MAG3] != ELFMAG3
72 [ # # ]: 0 : || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
73 : : return ELFCLASSNONE;
74 : :
75 : : return buf[EI_CLASS];
76 : : }
77 : :
78 : : /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.
79 : :
80 : : It would be easiest to call get_pid_class and parse everything according to
81 : : the 32-bit or 64-bit class. But this would bring the overhead of syscalls
82 : : to open and read the "/proc/%d/exe" file.
83 : :
84 : : Therefore this function tries to parse the "/proc/%d/auxv" content both
85 : : ways, as if it were the 32-bit format and also if it were the 64-bit format.
86 : : Only if it gives some valid data in both cases get_pid_class gets called.
87 : : In most cases only one of the format bit sizes gives valid data and the
88 : : get_pid_class call overhead can be saved. */
89 : :
90 : : static int
91 : 5004 : grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
92 : : {
93 : : char *fname;
94 [ + - ]: 5004 : if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
95 : : return ENOMEM;
96 : :
97 : 5004 : int fd = open64 (fname, O_RDONLY);
98 : 5004 : free (fname);
99 [ - + ]: 5004 : if (fd < 0)
100 [ # # ]: 0 : return errno == ENOENT ? 0 : errno;
101 : :
102 : 5004 : GElf_Addr sysinfo_ehdr64 = 0;
103 : 5004 : GElf_Addr sysinfo_ehdr32 = 0;
104 : 5004 : GElf_Addr segment_align64 = dwfl->segment_align;
105 : 5004 : GElf_Addr segment_align32 = dwfl->segment_align;
106 : 5004 : off_t offset = 0;
107 : : ssize_t nread;
108 : : union
109 : : {
110 : : Elf64_auxv_t a64[64];
111 : : Elf32_auxv_t a32[128];
112 : : } d;
113 : : do
114 : : {
115 : : eu_static_assert (sizeof d.a64 == sizeof d.a32);
116 : 5004 : nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
117 [ + - ]: 5004 : if (nread < 0)
118 : 0 : return errno;
119 [ + + ]: 195156 : for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
120 : : {
121 : 190152 : const Elf32_auxv_t *a32 = d.a32 + a32i;
122 [ + + + ]: 190152 : switch (a32->a_type)
123 : : {
124 : : case AT_SYSINFO_EHDR:
125 : 5004 : sysinfo_ehdr32 = a32->a_un.a_val;
126 : : break;
127 : : case AT_PAGESZ:
128 : 5004 : segment_align32 = a32->a_un.a_val;
129 : : break;
130 : : }
131 : : }
132 [ + + ]: 100080 : for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
133 : : {
134 : 95076 : const Elf64_auxv_t *a64 = d.a64 + a64i;
135 [ + + + ]: 95076 : switch (a64->a_type)
136 : : {
137 : : case AT_SYSINFO_EHDR:
138 : 5004 : sysinfo_ehdr64 = a64->a_un.a_val;
139 : : break;
140 : : case AT_PAGESZ:
141 : 5004 : segment_align64 = a64->a_un.a_val;
142 : : break;
143 : : }
144 : : }
145 : 5004 : offset += nread;
146 : : }
147 [ - + ]: 5004 : while (nread == sizeof d.a64);
148 : :
149 : 5004 : close (fd);
150 : :
151 [ - + ][ # # ]: 5004 : bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align;
152 [ + - ][ + - ]: 5004 : bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align;
153 : :
154 : 5004 : unsigned char pid_class = ELFCLASSNONE;
155 [ - + ]: 5004 : if (valid64 && valid32)
156 : 0 : pid_class = get_pid_class (pid);
157 : :
158 [ + - ][ + - ]: 5004 : if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
159 : : {
160 : 5004 : *sysinfo_ehdr = sysinfo_ehdr64;
161 : 5004 : dwfl->segment_align = segment_align64;
162 : : return 0;
163 : : }
164 [ # # ][ # # ]: 0 : if (pid_class == ELFCLASS32 || (! valid64 && valid32))
165 : : {
166 : 0 : *sysinfo_ehdr = sysinfo_ehdr32;
167 : 5004 : dwfl->segment_align = segment_align32;
168 : : return 0;
169 : : }
170 : : return ENOEXEC;
171 : : }
172 : :
173 : : static int
174 : 5009 : proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
175 : : {
176 : 5009 : unsigned int last_dmajor = -1, last_dminor = -1;
177 : 5009 : uint64_t last_ino = -1;
178 : 5009 : char *last_file = NULL;
179 : 5009 : Dwarf_Addr low = 0, high = 0;
180 : :
181 : 60047 : inline bool report (void)
182 : : {
183 [ + + ]: 60047 : if (last_file != NULL)
184 : : {
185 : 50034 : Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
186 : : low, high);
187 : 50034 : free (last_file);
188 : 50034 : last_file = NULL;
189 [ + - ]: 60047 : if (unlikely (mod == NULL))
190 : : return true;
191 : : }
192 : : return false;
193 : : }
194 : :
195 : 5009 : char *line = NULL;
196 : : size_t linesz;
197 : : ssize_t len;
198 [ + + ]: 230147 : while ((len = getline (&line, &linesz, f)) > 0)
199 : : {
200 [ + - ]: 220129 : if (line[len - 1] == '\n')
201 : 220129 : line[len - 1] = '\0';
202 : :
203 : : Dwarf_Addr start, end, offset;
204 : : unsigned int dmajor, dminor;
205 : : uint64_t ino;
206 : 220129 : int nread = -1;
207 [ + - ]: 220129 : if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
208 : : " %x:%x %" PRIi64 " %n",
209 : : &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
210 [ - + ]: 220129 : || nread <= 0)
211 : : {
212 : 0 : free (line);
213 : : return ENOEXEC;
214 : : }
215 : :
216 : : /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
217 : : report the last one and then this special one. */
218 [ + + ][ + - ]: 220129 : if (start == sysinfo_ehdr && start != 0)
219 : : {
220 [ - + ]: 5004 : if (report ())
221 : : {
222 : : bad_report:
223 : 0 : free (line);
224 : 0 : fclose (f);
225 : : return -1;
226 : : }
227 : :
228 : 5004 : low = start;
229 : 5004 : high = end;
230 [ - + ]: 5004 : if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
231 [ - + ]: 5004 : || report ())
232 : : goto bad_report;
233 : : }
234 : :
235 : 440258 : char *file = line + nread + strspn (line + nread, " \t");
236 [ + + ][ + + ]: 220129 : if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
[ + - ][ + - ]
237 : : /* This line doesn't indicate a file mapping. */
238 : 50037 : continue;
239 : :
240 [ + + ]: 170092 : if (last_file != NULL
241 [ + + ][ + - ]: 165083 : && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
[ + - ]
242 : : {
243 : : /* This is another portion of the same file's mapping. */
244 [ - + ]: 125062 : assert (!strcmp (last_file, file));
245 : 125062 : high = end;
246 : : }
247 : : else
248 : : {
249 : : /* This is a different file mapping. Report the last one. */
250 [ - + ]: 45030 : if (report ())
251 : : goto bad_report;
252 : 45030 : low = start;
253 : 45030 : high = end;
254 : 45030 : last_file = strdup (file);
255 : 45030 : last_ino = ino;
256 : 45030 : last_dmajor = dmajor;
257 : 220129 : last_dminor = dminor;
258 : : }
259 : : }
260 : 5009 : free (line);
261 : :
262 [ - + ][ - + ]: 5009 : int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
263 : :
264 : : /* Report the final one. */
265 : 5009 : bool lose = report ();
266 : :
267 [ + - ][ + - ]: 5009 : return result != 0 ? result : lose ? -1 : 0;
268 : : }
269 : :
270 : : int
271 : 5 : dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
272 : : {
273 : 5 : return proc_maps_report (dwfl, f, 0, 0);
274 : : }
275 : : INTDEF (dwfl_linux_proc_maps_report)
276 : :
277 : : int
278 : 5004 : dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
279 : : {
280 [ + - ]: 5004 : if (dwfl == NULL)
281 : : return -1;
282 : :
283 : : /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */
284 : 5004 : GElf_Addr sysinfo_ehdr = 0;
285 : 5004 : int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
286 [ + - ]: 5004 : if (result != 0)
287 : : return result;
288 : :
289 : : char *fname;
290 [ + - ]: 5004 : if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
291 : : return ENOMEM;
292 : :
293 : 5004 : FILE *f = fopen (fname, "r");
294 : 5004 : free (fname);
295 [ - + ]: 5004 : if (f == NULL)
296 : 0 : return errno;
297 : :
298 : 5004 : (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
299 : :
300 : 5004 : result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
301 : :
302 : 5004 : fclose (f);
303 : :
304 : : return result;
305 : : }
306 : : INTDEF (dwfl_linux_proc_report)
307 : :
308 : : static ssize_t
309 : 6 : read_proc_memory (void *arg, void *data, GElf_Addr address,
310 : : size_t minread, size_t maxread)
311 : : {
312 : 6 : const int fd = *(const int *) arg;
313 : 6 : ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
314 : : /* Some kernels don't actually let us do this read, ignore those errors. */
315 [ - + ][ # # ]: 6 : if (nread < 0 && (errno == EINVAL || errno == EPERM))
316 : : return 0;
317 [ - + ]: 6 : if (nread > 0 && (size_t) nread < minread)
318 : 0 : nread = 0;
319 : 6 : return nread;
320 : : }
321 : :
322 : : extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
323 : : GElf_Addr *loadbasep,
324 : : ssize_t (*read_memory) (void *arg,
325 : : void *data,
326 : : GElf_Addr address,
327 : : size_t minread,
328 : : size_t maxread),
329 : : void *arg);
330 : :
331 : :
332 : : /* Dwfl_Callbacks.find_elf */
333 : :
334 : : int
335 : 5026 : dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
336 : : void **userdata __attribute__ ((unused)),
337 : : const char *module_name, Dwarf_Addr base,
338 : : char **file_name, Elf **elfp)
339 : : {
340 [ + + ]: 5026 : if (module_name[0] == '/')
341 : : {
342 : 5024 : int fd = open64 (module_name, O_RDONLY);
343 [ + - ]: 5024 : if (fd >= 0)
344 : : {
345 : 5024 : *file_name = strdup (module_name);
346 [ - + ]: 5024 : if (*file_name == NULL)
347 : : {
348 : 5024 : close (fd);
349 : : return ENOMEM;
350 : : }
351 : : }
352 : : return fd;
353 : : }
354 : :
355 : : int pid;
356 [ + - ]: 2 : if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
357 : : {
358 : : /* Special case for in-memory ELF image. */
359 : :
360 : : char *fname;
361 [ + - ]: 2 : if (asprintf (&fname, PROCMEMFMT, pid) < 0)
362 : : return -1;
363 : :
364 : 2 : int fd = open64 (fname, O_RDONLY);
365 : 2 : free (fname);
366 [ + - ]: 2 : if (fd < 0)
367 : : return -1;
368 : :
369 : 2 : *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
370 : :
371 : 2 : close (fd);
372 : :
373 : 2 : *file_name = NULL;
374 : : return -1;
375 : : }
376 : :
377 : 5026 : abort ();
378 : : return -1;
379 : : }
380 : 10013 : INTDEF (dwfl_linux_proc_find_elf)
|