Branch data Line data Source code
1 : : /* Core file handling.
2 : : Copyright (C) 2008-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 <config.h>
30 : : #include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
31 : : #undef _
32 : : #include "libdwflP.h"
33 : : #include <gelf.h>
34 : :
35 : : #include <sys/param.h>
36 : : #include <unistd.h>
37 : : #include <endian.h>
38 : : #include <byteswap.h>
39 : : #include "system.h"
40 : :
41 : :
42 : : /* This is a prototype of what a new libelf interface might be.
43 : : This implementation is pessimal for non-mmap cases and should
44 : : be replaced by more diddling inside libelf internals. */
45 : : static Elf *
46 : 7 : elf_begin_rand (Elf *parent, loff_t offset, loff_t size, loff_t *next)
47 : : {
48 [ + - ]: 7 : if (parent == NULL)
49 : : return NULL;
50 : :
51 : : /* On failure return, we update *NEXT to point back at OFFSET. */
52 : 0 : inline Elf *fail (int error)
53 : : {
54 [ # # ]: 0 : if (next != NULL)
55 : 0 : *next = offset;
56 : : //__libelf_seterrno (error);
57 : 0 : __libdwfl_seterrno (DWFL_E (LIBELF, error));
58 : 0 : return NULL;
59 : : }
60 : :
61 [ + - ][ + + ]: 7 : loff_t min = (parent->kind == ELF_K_ELF ?
[ # # ]
62 : 7 : (parent->class == ELFCLASS32
63 : : ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
64 : : : parent->kind == ELF_K_AR ? SARMAG
65 : : : 0);
66 : :
67 [ + - ]: 7 : if (unlikely (offset < min)
68 [ - + ]: 7 : || unlikely (offset >= (loff_t) parent->maximum_size))
69 : 0 : return fail (ELF_E_RANGE);
70 : :
71 : : /* For an archive, fetch just the size field
72 : : from the archive header to override SIZE. */
73 [ - + ]: 7 : if (parent->kind == ELF_K_AR)
74 : : {
75 : 0 : struct ar_hdr h = { .ar_size = "" };
76 : :
77 [ # # ]: 0 : if (unlikely (parent->maximum_size - offset < sizeof h))
78 : 0 : return fail (ELF_E_RANGE);
79 : :
80 [ # # ]: 0 : if (parent->map_address != NULL)
81 : 0 : memcpy (h.ar_size, parent->map_address + parent->start_offset + offset,
82 : : sizeof h.ar_size);
83 [ # # ]: 0 : else if (unlikely (pread_retry (parent->fildes,
84 : : h.ar_size, sizeof (h.ar_size),
85 : : parent->start_offset + offset
86 : : + offsetof (struct ar_hdr, ar_size))
87 : : != sizeof (h.ar_size)))
88 : 0 : return fail (ELF_E_READ_ERROR);
89 : :
90 : 0 : offset += sizeof h;
91 : :
92 : : char *endp;
93 : 0 : size = strtoll (h.ar_size, &endp, 10);
94 [ # # ]: 0 : if (unlikely (endp == h.ar_size)
95 [ # # ]: 0 : || unlikely ((loff_t) parent->maximum_size - offset < size))
96 : 0 : return fail (ELF_E_INVALID_ARCHIVE);
97 : : }
98 : :
99 [ - + ]: 7 : if (unlikely ((loff_t) parent->maximum_size - offset < size))
100 : 0 : return fail (ELF_E_RANGE);
101 : :
102 : : /* Even if we fail at this point, update *NEXT to point past the file. */
103 [ - + ]: 7 : if (next != NULL)
104 : 0 : *next = offset + size;
105 : :
106 [ - + ]: 7 : if (unlikely (offset == 0)
107 [ # # ]: 0 : && unlikely (size == (loff_t) parent->maximum_size))
108 : 0 : return elf_clone (parent, parent->cmd);
109 : :
110 : : /* Note the image is guaranteed live only as long as PARENT
111 : : lives. Using elf_memory is quite suboptimal if the whole
112 : : file is not mmap'd. We really should have something like
113 : : a generalization of the archive support. */
114 : 7 : Elf_Data *data = elf_getdata_rawchunk (parent, offset, size, ELF_T_BYTE);
115 [ + - ]: 7 : if (data == NULL)
116 : : return NULL;
117 [ - + ]: 7 : assert ((loff_t) data->d_size == size);
118 : 7 : return elf_memory (data->d_buf, size);
119 : : }
120 : :
121 : :
122 : : int
123 : 4 : dwfl_report_core_segments (Dwfl *dwfl, Elf *elf, size_t phnum, GElf_Phdr *notes)
124 : : {
125 [ + - ]: 4 : if (unlikely (dwfl == NULL))
126 : : return -1;
127 : :
128 : 4 : int result = 0;
129 : :
130 [ + - ]: 4 : if (notes != NULL)
131 : 4 : notes->p_type = PT_NULL;
132 : :
133 [ + + ]: 68 : for (size_t ndx = 0; result >= 0 && ndx < phnum; ++ndx)
134 : : {
135 : : GElf_Phdr phdr_mem;
136 : 64 : GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
137 [ - + ]: 64 : if (unlikely (phdr == NULL))
138 : : {
139 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
140 : : return -1;
141 : : }
142 [ + + - ]: 64 : switch (phdr->p_type)
143 : : {
144 : : case PT_LOAD:
145 : 60 : result = dwfl_report_segment (dwfl, ndx, phdr, 0, NULL);
146 : 60 : break;
147 : :
148 : : case PT_NOTE:
149 [ + - ]: 4 : if (notes != NULL)
150 : : {
151 : 4 : *notes = *phdr;
152 : 4 : notes = NULL;
153 : : }
154 : : break;
155 : : }
156 : : }
157 : :
158 : : return result;
159 : : }
160 : :
161 : : /* Never read more than this much without mmap. */
162 : : #define MAX_EAGER_COST 8192
163 : :
164 : : static bool
165 : 16 : core_file_read_eagerly (Dwfl_Module *mod,
166 : : void **userdata __attribute__ ((unused)),
167 : : const char *name __attribute__ ((unused)),
168 : : Dwarf_Addr start __attribute__ ((unused)),
169 : : void **buffer, size_t *buffer_available,
170 : : GElf_Off cost, GElf_Off worthwhile,
171 : : GElf_Off whole,
172 : : GElf_Off contiguous __attribute__ ((unused)),
173 : : void *arg, Elf **elfp)
174 : : {
175 : 16 : Elf *core = arg;
176 : :
177 [ + + ]: 16 : if (whole <= *buffer_available)
178 : : {
179 : : /* All there ever was, we already have on hand. */
180 : :
181 [ - + ]: 7 : if (core->map_address == NULL)
182 : : {
183 : : /* We already malloc'd the buffer. */
184 : 0 : *elfp = elf_memory (*buffer, whole);
185 [ # # ]: 0 : if (unlikely (*elfp == NULL))
186 : : return false;
187 : :
188 : 0 : (*elfp)->flags |= ELF_F_MALLOCED;
189 : 0 : *buffer = NULL;
190 : 0 : *buffer_available = 0;
191 : 0 : return true;
192 : : }
193 : :
194 : : /* We can use the image inside the core file directly. */
195 : 7 : *elfp = elf_begin_rand (core, *buffer - core->map_address, whole, NULL);
196 : 7 : *buffer = NULL;
197 : 7 : *buffer_available = 0;
198 : 7 : return *elfp != NULL;
199 : : }
200 : :
201 : : /* We don't have the whole file.
202 : : Figure out if this is better than nothing. */
203 : :
204 [ + + ]: 9 : if (worthwhile == 0)
205 : : /* Caller doesn't think so. */
206 : : return false;
207 : :
208 : : /*
209 : : XXX would like to fall back to partial file via memory
210 : : when build id find_elf fails
211 : : also, link_map name may give file name from disk better than partial here
212 : : requires find_elf hook re-doing the magic to fall back if no file found
213 : : */
214 : :
215 [ - + ]: 8 : if (mod->build_id_len > 0)
216 : : /* There is a build ID that could help us find the whole file,
217 : : which might be more useful than what we have.
218 : : We'll just rely on that. */
219 : : return false;
220 : :
221 [ # # ]: 0 : if (core->map_address != NULL)
222 : : /* It's cheap to get, so get it. */
223 : : return true;
224 : :
225 : : /* Only use it if there isn't too much to be read. */
226 : 16 : return cost <= MAX_EAGER_COST;
227 : : }
228 : :
229 : : bool
230 : 154 : dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
231 : : void **buffer, size_t *buffer_available,
232 : : GElf_Addr vaddr,
233 : : size_t minread,
234 : : void *arg)
235 : : {
236 : 154 : Elf *elf = arg;
237 : :
238 [ + + ]: 154 : if (ndx == -1)
239 : : {
240 : : /* Called for cleanup. */
241 [ - + ]: 68 : if (elf->map_address == NULL)
242 : 0 : free (*buffer);
243 : 68 : *buffer = NULL;
244 : 68 : *buffer_available = 0;
245 : : return false;
246 : : }
247 : :
248 [ + - ]: 90 : const GElf_Off align = dwfl->segment_align ?: 1;
249 : : GElf_Phdr phdr;
250 : :
251 : : do
252 [ + - ]: 90 : if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
253 : : return false;
254 : 90 : while (phdr.p_type != PT_LOAD
255 [ + + ][ - + ]: 90 : || ((phdr.p_vaddr + phdr.p_memsz + align - 1) & -align) <= vaddr);
256 : :
257 : 86 : GElf_Off start = vaddr - phdr.p_vaddr + phdr.p_offset;
258 : : GElf_Off end;
259 : : GElf_Addr end_vaddr;
260 : :
261 : 150 : inline void update_end ()
262 : : {
263 : 150 : end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align;
264 : 150 : end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align;
265 : 150 : }
266 : :
267 : 86 : update_end ();
268 : :
269 : : /* Use following contiguous segments to get towards SIZE. */
270 : 238 : inline bool more (size_t size)
271 : : {
272 [ + + ][ + + ]: 622 : while (end <= start || end - start < size)
273 : : {
274 [ + + ]: 146 : if (phdr.p_filesz < phdr.p_memsz)
275 : : /* This segment is truncated, so no following one helps us. */
276 : : return false;
277 : :
278 [ + - ]: 112 : if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
279 : : return false;
280 : :
281 [ + - ]: 112 : if (phdr.p_type == PT_LOAD)
282 : : {
283 [ + - ]: 112 : if (phdr.p_offset > end
284 [ + + ]: 112 : || phdr.p_vaddr > end_vaddr)
285 : : /* It's discontiguous! */
286 : : return false;
287 : :
288 : 302 : update_end ();
289 : : }
290 : : }
291 : : return true;
292 : : }
293 : :
294 : : /* We need at least this much. */
295 [ + + ]: 86 : if (! more (minread))
296 : : return false;
297 : :
298 : : /* See how much more we can get of what the caller wants. */
299 : 76 : (void) more (*buffer_available);
300 : :
301 : : /* If it's already on hand anyway, use as much as there is. */
302 [ + - ]: 76 : if (elf->map_address != NULL)
303 : 76 : (void) more (elf->maximum_size - start);
304 : :
305 : : /* Make sure we don't look past the end of the actual file,
306 : : even if the headers tell us to. */
307 [ - + ]: 76 : if (unlikely (end > elf->maximum_size))
308 : 0 : end = elf->maximum_size;
309 : :
310 : : /* If the file is too small, there is nothing at all to get. */
311 [ + - ]: 76 : if (unlikely (start >= end))
312 : : return false;
313 : :
314 [ + - ]: 76 : if (elf->map_address != NULL)
315 : : {
316 : 76 : void *contents = elf->map_address + elf->start_offset + start;
317 : 76 : size_t size = end - start;
318 : :
319 [ + + ]: 76 : if (minread == 0) /* String mode. */
320 : : {
321 : 9 : const void *eos = memchr (contents, '\0', size);
322 [ + + ][ + - ]: 9 : if (unlikely (eos == NULL) || unlikely (eos == contents))
323 : : return false;
324 : 8 : size = eos + 1 - contents;
325 : : }
326 : :
327 [ + - ]: 75 : if (*buffer == NULL)
328 : : {
329 : 75 : *buffer = contents;
330 : 75 : *buffer_available = size;
331 : : }
332 : : else
333 : : {
334 : 0 : *buffer_available = MIN (size, *buffer_available);
335 : 0 : memcpy (*buffer, contents, *buffer_available);
336 : : }
337 : : }
338 : : else
339 : : {
340 : 0 : void *into = *buffer;
341 [ # # ]: 0 : if (*buffer == NULL)
342 : : {
343 [ # # ][ # # ]: 0 : *buffer_available = MIN (minread ?: 512,
[ # # ]
344 : : MAX (4096, MIN (end - start,
345 : : *buffer_available)));
346 : 0 : into = malloc (*buffer_available);
347 [ # # ]: 0 : if (unlikely (into == NULL))
348 : : {
349 : 0 : __libdwfl_seterrno (DWFL_E_NOMEM);
350 : : return false;
351 : : }
352 : : }
353 : :
354 : 0 : ssize_t nread = pread_retry (elf->fildes, into, *buffer_available, start);
355 [ # # ]: 0 : if (nread < (ssize_t) minread)
356 : : {
357 [ # # ]: 0 : if (into != *buffer)
358 : 0 : free (into);
359 [ # # ]: 0 : if (nread < 0)
360 : 0 : __libdwfl_seterrno (DWFL_E_ERRNO);
361 : : return false;
362 : : }
363 : :
364 [ # # ]: 0 : if (minread == 0) /* String mode. */
365 : : {
366 : 0 : const void *eos = memchr (into, '\0', nread);
367 [ # # ][ # # ]: 0 : if (unlikely (eos == NULL) || unlikely (eos == into))
368 : : {
369 [ # # ]: 0 : if (*buffer == NULL)
370 : 0 : free (into);
371 : : return false;
372 : : }
373 : 0 : nread = eos + 1 - into;
374 : : }
375 : :
376 [ # # ]: 0 : if (*buffer == NULL)
377 : 0 : *buffer = into;
378 : 154 : *buffer_available = nread;
379 : : }
380 : :
381 : : return true;
382 : : }
383 : :
384 : : int
385 : 4 : dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
386 : : {
387 : : size_t phnum;
388 [ - + ]: 4 : if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
389 : : {
390 : 0 : __libdwfl_seterrno (DWFL_E_LIBELF);
391 : : return -1;
392 : : }
393 : :
394 : : /* First report each PT_LOAD segment. */
395 : : GElf_Phdr notes_phdr;
396 : 4 : int ndx = dwfl_report_core_segments (dwfl, elf, phnum, ¬es_phdr);
397 [ + - ]: 4 : if (unlikely (ndx <= 0))
398 : : return ndx;
399 : :
400 : : /* Now sniff segment contents for modules. */
401 : : int sniffed = 0;
402 : : ndx = 0;
403 : : do
404 : : {
405 : 36 : int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
406 : : &dwfl_elf_phdr_memory_callback, elf,
407 : : core_file_read_eagerly, elf);
408 [ + - ]: 36 : if (unlikely (seg < 0))
409 : : return seg;
410 [ + + ]: 36 : if (seg > ndx)
411 : : {
412 : 16 : ndx = seg;
413 : 16 : ++sniffed;
414 : : }
415 : : else
416 : 20 : ++ndx;
417 : : }
418 [ + + ]: 36 : while (ndx < (int) phnum);
419 : :
420 : : /* Next, we should follow the chain from DT_DEBUG. */
421 : :
422 : 4 : const void *auxv = NULL;
423 : 4 : size_t auxv_size = 0;
424 [ + - ]: 4 : if (likely (notes_phdr.p_type == PT_NOTE))
425 : : {
426 : : /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
427 : :
428 : 4 : Elf_Data *notes = elf_getdata_rawchunk (elf,
429 : 4 : notes_phdr.p_offset,
430 : : notes_phdr.p_filesz,
431 : : ELF_T_NHDR);
432 [ + - ]: 4 : if (likely (notes != NULL))
433 : : {
434 : : size_t pos = 0;
435 : : GElf_Nhdr nhdr;
436 : : size_t name_pos;
437 : : size_t desc_pos;
438 [ + - ]: 16 : while ((pos = gelf_getnote (notes, pos, &nhdr,
439 : : &name_pos, &desc_pos)) > 0)
440 [ + + ]: 12 : if (nhdr.n_type == NT_AUXV
441 [ + - ]: 4 : && nhdr.n_namesz == sizeof "CORE"
442 [ + - ]: 4 : && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
443 : : {
444 : 4 : auxv = notes->d_buf + desc_pos;
445 : 4 : auxv_size = nhdr.n_descsz;
446 : 12 : break;
447 : : }
448 : : }
449 : : }
450 : :
451 : : /* Now we have NT_AUXV contents. From here on this processing could be
452 : : used for a live process with auxv read from /proc. */
453 : :
454 : 4 : int listed = dwfl_link_map_report (dwfl, auxv, auxv_size,
455 : : dwfl_elf_phdr_memory_callback, elf);
456 : :
457 : : /* We return the number of modules we found if we found any.
458 : : If we found none, we return -1 instead of 0 if there was an
459 : : error rather than just nothing found. If link_map handling
460 : : failed, we still have the sniffed modules. */
461 [ + - ]: 4 : return sniffed == 0 || listed > sniffed ? listed : sniffed;
462 : : }
463 : : INTDEF (dwfl_core_file_report)
|