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 : :
43 : :
44 : : #define PROCMAPSFMT "/proc/%d/maps"
45 : : #define PROCMEMFMT "/proc/%d/mem"
46 : : #define PROCAUXVFMT "/proc/%d/auxv"
47 : :
48 : :
49 : : /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */
50 : :
51 : : static int
52 : 5004 : grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
53 : : {
54 : : char *fname;
55 [ + - ]: 5004 : if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
56 : : return ENOMEM;
57 : :
58 : 5004 : int fd = open64 (fname, O_RDONLY);
59 : 5004 : free (fname);
60 [ - + ]: 5004 : if (fd < 0)
61 [ # # ]: 5004 : return errno == ENOENT ? 0 : errno;
62 : :
63 : : ssize_t nread;
64 : : do
65 : : {
66 : : union
67 : : {
68 : : char buffer[sizeof (long int) * 2 * 64];
69 : : Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
70 : : Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
71 : : } d;
72 : 10008 : nread = read (fd, &d, sizeof d);
73 [ + + ]: 10008 : if (nread > 0)
74 : : {
75 : : switch (sizeof (long int))
76 : : {
77 : : case 4:
78 : : for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
79 : : if (d.a32[i].a_type == AT_SYSINFO_EHDR)
80 : : {
81 : : *sysinfo_ehdr = d.a32[i].a_un.a_val;
82 : : if (dwfl->segment_align > 1)
83 : : {
84 : : nread = 0;
85 : : break;
86 : : }
87 : : }
88 : : else if (d.a32[i].a_type == AT_PAGESZ
89 : : && dwfl->segment_align <= 1)
90 : : dwfl->segment_align = d.a32[i].a_un.a_val;
91 : : break;
92 : : case 8:
93 [ + + ]: 100080 : for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
94 [ + + ]: 95076 : if (d.a64[i].a_type == AT_SYSINFO_EHDR)
95 : : {
96 : 5004 : *sysinfo_ehdr = d.a64[i].a_un.a_val;
97 [ + - ]: 5004 : if (dwfl->segment_align > 1)
98 : : {
99 : : nread = 0;
100 : : break;
101 : : }
102 : : }
103 [ + + ]: 90072 : else if (d.a64[i].a_type == AT_PAGESZ
104 [ + - ]: 5004 : && dwfl->segment_align <= 1)
105 : 5004 : dwfl->segment_align = d.a64[i].a_un.a_val;
106 : : break;
107 : : default:
108 : : abort ();
109 : : break;
110 : : }
111 : : }
112 : : }
113 [ + + ]: 10008 : while (nread > 0);
114 : :
115 : 5004 : close (fd);
116 : :
117 [ - + ]: 5004 : return nread < 0 ? errno : 0;
118 : : }
119 : :
120 : : static int
121 : 5009 : proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
122 : : {
123 : 5009 : unsigned int last_dmajor = -1, last_dminor = -1;
124 : 5009 : uint64_t last_ino = -1;
125 : 5009 : char *last_file = NULL;
126 : 5009 : Dwarf_Addr low = 0, high = 0;
127 : :
128 : 60047 : inline bool report (void)
129 : : {
130 [ + + ]: 60047 : if (last_file != NULL)
131 : : {
132 : 50034 : Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
133 : : low, high);
134 : 50034 : free (last_file);
135 : 50034 : last_file = NULL;
136 [ + - ]: 60047 : if (unlikely (mod == NULL))
137 : : return true;
138 : : }
139 : : return false;
140 : : }
141 : :
142 : 5009 : char *line = NULL;
143 : : size_t linesz;
144 : : ssize_t len;
145 [ + + ]: 215143 : while ((len = getline (&line, &linesz, f)) > 0)
146 : : {
147 [ + - ]: 205125 : if (line[len - 1] == '\n')
148 : 205125 : line[len - 1] = '\0';
149 : :
150 : : Dwarf_Addr start, end, offset;
151 : : unsigned int dmajor, dminor;
152 : : uint64_t ino;
153 : 205125 : int nread = -1;
154 [ + - ]: 205125 : if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
155 : : " %x:%x %" PRIi64 " %n",
156 : : &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
157 [ - + ]: 205125 : || nread <= 0)
158 : : {
159 : 0 : free (line);
160 : : return ENOEXEC;
161 : : }
162 : :
163 : : /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
164 : : report the last one and then this special one. */
165 [ + + ][ + - ]: 205125 : if (start == sysinfo_ehdr && start != 0)
166 : : {
167 [ - + ]: 5004 : if (report ())
168 : : {
169 : : bad_report:
170 : 0 : free (line);
171 : 0 : fclose (f);
172 : : return -1;
173 : : }
174 : :
175 : 5004 : low = start;
176 : 5004 : high = end;
177 [ - + ]: 5004 : if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
178 [ - + ]: 5004 : || report ())
179 : : goto bad_report;
180 : : }
181 : :
182 : 410250 : char *file = line + nread + strspn (line + nread, " \t");
183 [ + + ][ + + ]: 205125 : if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
[ + - ][ + - ]
184 : : /* This line doesn't indicate a file mapping. */
185 : 50037 : continue;
186 : :
187 [ + + ]: 155088 : if (last_file != NULL
188 [ + + ][ + - ]: 150079 : && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
[ + - ]
189 : : {
190 : : /* This is another portion of the same file's mapping. */
191 [ - + ]: 110058 : assert (!strcmp (last_file, file));
192 : 110058 : high = end;
193 : : }
194 : : else
195 : : {
196 : : /* This is a different file mapping. Report the last one. */
197 [ - + ]: 45030 : if (report ())
198 : : goto bad_report;
199 : 45030 : low = start;
200 : 45030 : high = end;
201 : 45030 : last_file = strdup (file);
202 : 45030 : last_ino = ino;
203 : 45030 : last_dmajor = dmajor;
204 : 205125 : last_dminor = dminor;
205 : : }
206 : : }
207 : 5009 : free (line);
208 : :
209 [ - + ][ - + ]: 5009 : int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
210 : :
211 : : /* Report the final one. */
212 : 5009 : bool lose = report ();
213 : :
214 [ + - ][ + - ]: 5009 : return result != 0 ? result : lose ? -1 : 0;
215 : : }
216 : :
217 : : int
218 : 5 : dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
219 : : {
220 : 5 : return proc_maps_report (dwfl, f, 0, 0);
221 : : }
222 : : INTDEF (dwfl_linux_proc_maps_report)
223 : :
224 : : int
225 : 5004 : dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
226 : : {
227 [ + - ]: 5004 : if (dwfl == NULL)
228 : : return -1;
229 : :
230 : : /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */
231 : 5004 : GElf_Addr sysinfo_ehdr = 0;
232 : 5004 : int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
233 [ + - ]: 5004 : if (result != 0)
234 : : return result;
235 : :
236 : : char *fname;
237 [ + - ]: 5004 : if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
238 : : return ENOMEM;
239 : :
240 : 5004 : FILE *f = fopen (fname, "r");
241 : 5004 : free (fname);
242 [ - + ]: 5004 : if (f == NULL)
243 : 0 : return errno;
244 : :
245 : 5004 : (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
246 : :
247 : 5004 : result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
248 : :
249 : 5004 : fclose (f);
250 : :
251 : : return result;
252 : : }
253 : : INTDEF (dwfl_linux_proc_report)
254 : :
255 : : static ssize_t
256 : 6 : read_proc_memory (void *arg, void *data, GElf_Addr address,
257 : : size_t minread, size_t maxread)
258 : : {
259 : 6 : const int fd = *(const int *) arg;
260 : 6 : ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
261 : : /* Some kernels don't actually let us do this read, ignore those errors. */
262 [ - + ][ # # ]: 6 : if (nread < 0 && (errno == EINVAL || errno == EPERM))
263 : : return 0;
264 [ - + ]: 6 : if (nread > 0 && (size_t) nread < minread)
265 : 0 : nread = 0;
266 : 6 : return nread;
267 : : }
268 : :
269 : : extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
270 : : GElf_Addr *loadbasep,
271 : : ssize_t (*read_memory) (void *arg,
272 : : void *data,
273 : : GElf_Addr address,
274 : : size_t minread,
275 : : size_t maxread),
276 : : void *arg);
277 : :
278 : :
279 : : /* Dwfl_Callbacks.find_elf */
280 : :
281 : : int
282 : 5026 : dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
283 : : void **userdata __attribute__ ((unused)),
284 : : const char *module_name, Dwarf_Addr base,
285 : : char **file_name, Elf **elfp)
286 : : {
287 [ + + ]: 5026 : if (module_name[0] == '/')
288 : : {
289 : 5024 : int fd = open64 (module_name, O_RDONLY);
290 [ + - ]: 5024 : if (fd >= 0)
291 : : {
292 : 5024 : *file_name = strdup (module_name);
293 [ - + ]: 5024 : if (*file_name == NULL)
294 : : {
295 : 5024 : close (fd);
296 : : return ENOMEM;
297 : : }
298 : : }
299 : : return fd;
300 : : }
301 : :
302 : : int pid;
303 [ + - ]: 2 : if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
304 : : {
305 : : /* Special case for in-memory ELF image. */
306 : :
307 : : char *fname;
308 [ + - ]: 2 : if (asprintf (&fname, PROCMEMFMT, pid) < 0)
309 : : return -1;
310 : :
311 : 2 : int fd = open64 (fname, O_RDONLY);
312 : 2 : free (fname);
313 [ + - ]: 2 : if (fd < 0)
314 : : return -1;
315 : :
316 : 2 : *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
317 : :
318 : 2 : close (fd);
319 : :
320 : 2 : *file_name = NULL;
321 : : return -1;
322 : : }
323 : :
324 : 5026 : abort ();
325 : : return -1;
326 : : }
327 : 10013 : INTDEF (dwfl_linux_proc_find_elf)
|