blob: 1545ca869860fd666922960d0cc61e69607f219e [file] [log] [blame]
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00001/* Core file handling.
Roland McGrathf95760a2010-01-07 20:24:34 -08002 Copyright (C) 2008-2010 Red Hat, Inc.
Mark Wielaardde2ed972012-06-05 17:15:16 +02003 This file is part of elfutils.
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00004
Mark Wielaardde2ed972012-06-05 17:15:16 +02005 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
Roland McGrathb4d6f0f2008-08-25 22:55:17 +00007
Mark Wielaardde2ed972012-06-05 17:15:16 +02008 * 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
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000021 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
Mark Wielaardde2ed972012-06-05 17:15:16 +020025 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/>. */
Roland McGrathb4d6f0f2008-08-25 22:55:17 +000028
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. */
45static Elf *
46elf_begin_rand (Elf *parent, loff_t offset, loff_t size, loff_t *next)
47{
48 if (parent == NULL)
49 return NULL;
50
51 /* On failure return, we update *NEXT to point back at OFFSET. */
52 inline Elf *fail (int error)
53 {
54 if (next != NULL)
55 *next = offset;
56 //__libelf_seterrno (error);
57 __libdwfl_seterrno (DWFL_E (LIBELF, error));
58 return NULL;
59 }
60
61 loff_t min = (parent->kind == ELF_K_ELF ?
62 (parent->class == ELFCLASS32
63 ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
64 : parent->kind == ELF_K_AR ? SARMAG
65 : 0);
66
67 if (unlikely (offset < min)
68 || unlikely (offset >= (loff_t) parent->maximum_size))
69 return fail (ELF_E_RANGE);
70
71 /* For an archive, fetch just the size field
72 from the archive header to override SIZE. */
73 if (parent->kind == ELF_K_AR)
74 {
75 struct ar_hdr h = { .ar_size = "" };
76
77 if (unlikely (parent->maximum_size - offset < sizeof h))
78 return fail (ELF_E_RANGE);
79
80 if (parent->map_address != NULL)
81 memcpy (h.ar_size, parent->map_address + parent->start_offset + offset,
82 sizeof h.ar_size);
83 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 return fail (ELF_E_READ_ERROR);
89
90 offset += sizeof h;
91
92 char *endp;
93 size = strtoll (h.ar_size, &endp, 10);
94 if (unlikely (endp == h.ar_size)
95 || unlikely ((loff_t) parent->maximum_size - offset < size))
96 return fail (ELF_E_INVALID_ARCHIVE);
97 }
98
99 if (unlikely ((loff_t) parent->maximum_size - offset < size))
100 return fail (ELF_E_RANGE);
101
102 /* Even if we fail at this point, update *NEXT to point past the file. */
103 if (next != NULL)
104 *next = offset + size;
105
106 if (unlikely (offset == 0)
107 && unlikely (size == (loff_t) parent->maximum_size))
108 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 Elf_Data *data = elf_getdata_rawchunk (parent, offset, size, ELF_T_BYTE);
115 if (data == NULL)
116 return NULL;
117 assert ((loff_t) data->d_size == size);
118 return elf_memory (data->d_buf, size);
119}
120
121
122int
Roland McGrathf95760a2010-01-07 20:24:34 -0800123dwfl_report_core_segments (Dwfl *dwfl, Elf *elf, size_t phnum, GElf_Phdr *notes)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000124{
125 if (unlikely (dwfl == NULL))
126 return -1;
127
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000128 int result = 0;
129
130 if (notes != NULL)
131 notes->p_type = PT_NULL;
132
Roland McGrathf95760a2010-01-07 20:24:34 -0800133 for (size_t ndx = 0; result >= 0 && ndx < phnum; ++ndx)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000134 {
135 GElf_Phdr phdr_mem;
136 GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
137 if (unlikely (phdr == NULL))
138 {
139 __libdwfl_seterrno (DWFL_E_LIBELF);
140 return -1;
141 }
142 switch (phdr->p_type)
143 {
144 case PT_LOAD:
145 result = dwfl_report_segment (dwfl, ndx, phdr, 0, NULL);
146 break;
147
148 case PT_NOTE:
149 if (notes != NULL)
150 {
151 *notes = *phdr;
152 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
164static bool
165core_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 Elf *core = arg;
176
177 if (whole <= *buffer_available)
178 {
179 /* All there ever was, we already have on hand. */
180
181 if (core->map_address == NULL)
182 {
183 /* We already malloc'd the buffer. */
184 *elfp = elf_memory (*buffer, whole);
185 if (unlikely (*elfp == NULL))
186 return false;
187
188 (*elfp)->flags |= ELF_F_MALLOCED;
189 *buffer = NULL;
190 *buffer_available = 0;
191 return true;
192 }
193
194 /* We can use the image inside the core file directly. */
195 *elfp = elf_begin_rand (core, *buffer - core->map_address, whole, NULL);
196 *buffer = NULL;
197 *buffer_available = 0;
198 return *elfp != NULL;
199 }
200
201 /* We don't have the whole file.
202 Figure out if this is better than nothing. */
203
204 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 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 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 return cost <= MAX_EAGER_COST;
227}
228
229bool
230dwfl_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 Elf *elf = arg;
237
238 if (ndx == -1)
239 {
240 /* Called for cleanup. */
241 if (elf->map_address == NULL)
242 free (*buffer);
243 *buffer = NULL;
244 *buffer_available = 0;
245 return false;
246 }
247
248 const GElf_Off align = dwfl->segment_align ?: 1;
249 GElf_Phdr phdr;
250
251 do
252 if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
Roland McGrathfa253262009-04-14 02:38:19 -0700253 return false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000254 while (phdr.p_type != PT_LOAD
255 || ((phdr.p_vaddr + phdr.p_memsz + align - 1) & -align) <= vaddr);
256
257 GElf_Off start = vaddr - phdr.p_vaddr + phdr.p_offset;
Petr Machata003dd322009-11-13 00:38:27 +0100258 GElf_Off end;
259 GElf_Addr end_vaddr;
260
261 inline void update_end ()
262 {
263 end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align;
264 end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align;
265 }
266
267 update_end ();
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000268
269 /* Use following contiguous segments to get towards SIZE. */
270 inline bool more (size_t size)
271 {
272 while (end <= start || end - start < size)
273 {
274 if (phdr.p_filesz < phdr.p_memsz)
275 /* This segment is truncated, so no following one helps us. */
276 return false;
277
278 if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
279 return false;
280
281 if (phdr.p_type == PT_LOAD)
282 {
Petr Machata003dd322009-11-13 00:38:27 +0100283 if (phdr.p_offset > end
284 || phdr.p_vaddr > end_vaddr)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000285 /* It's discontiguous! */
286 return false;
287
Petr Machata003dd322009-11-13 00:38:27 +0100288 update_end ();
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000289 }
290 }
291 return true;
292 }
293
294 /* We need at least this much. */
295 if (! more (minread))
296 return false;
297
298 /* See how much more we can get of what the caller wants. */
299 (void) more (*buffer_available);
300
301 /* If it's already on hand anyway, use as much as there is. */
302 if (elf->map_address != NULL)
303 (void) more (elf->maximum_size - start);
304
Roland McGrathfa253262009-04-14 02:38:19 -0700305 /* Make sure we don't look past the end of the actual file,
306 even if the headers tell us to. */
307 if (unlikely (end > elf->maximum_size))
308 end = elf->maximum_size;
309
310 /* If the file is too small, there is nothing at all to get. */
311 if (unlikely (start >= end))
312 return false;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000313
314 if (elf->map_address != NULL)
315 {
316 void *contents = elf->map_address + elf->start_offset + start;
317 size_t size = end - start;
318
319 if (minread == 0) /* String mode. */
320 {
321 const void *eos = memchr (contents, '\0', size);
322 if (unlikely (eos == NULL) || unlikely (eos == contents))
323 return false;
324 size = eos + 1 - contents;
325 }
326
327 if (*buffer == NULL)
328 {
329 *buffer = contents;
330 *buffer_available = size;
331 }
332 else
333 {
334 *buffer_available = MIN (size, *buffer_available);
335 memcpy (*buffer, contents, *buffer_available);
336 }
337 }
338 else
339 {
340 void *into = *buffer;
341 if (*buffer == NULL)
342 {
343 *buffer_available = MIN (minread ?: 512,
344 MAX (4096, MIN (end - start,
345 *buffer_available)));
346 into = malloc (*buffer_available);
347 if (unlikely (into == NULL))
348 {
349 __libdwfl_seterrno (DWFL_E_NOMEM);
350 return false;
351 }
352 }
353
354 ssize_t nread = pread_retry (elf->fildes, into, *buffer_available, start);
355 if (nread < (ssize_t) minread)
356 {
357 if (into != *buffer)
358 free (into);
359 if (nread < 0)
360 __libdwfl_seterrno (DWFL_E_ERRNO);
361 return false;
362 }
363
364 if (minread == 0) /* String mode. */
365 {
366 const void *eos = memchr (into, '\0', nread);
367 if (unlikely (eos == NULL) || unlikely (eos == into))
368 {
369 if (*buffer == NULL)
370 free (into);
371 return false;
372 }
373 nread = eos + 1 - into;
374 }
375
376 if (*buffer == NULL)
377 *buffer = into;
378 *buffer_available = nread;
379 }
380
381 return true;
382}
383
384int
Roland McGrath8068e062010-03-10 23:49:13 -0800385dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000386{
Roland McGrathf95760a2010-01-07 20:24:34 -0800387 size_t phnum;
Roland McGrath8068e062010-03-10 23:49:13 -0800388 if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
Roland McGrathf95760a2010-01-07 20:24:34 -0800389 {
Roland McGrath8068e062010-03-10 23:49:13 -0800390 __libdwfl_seterrno (DWFL_E_LIBELF);
Roland McGrathf95760a2010-01-07 20:24:34 -0800391 return -1;
392 }
393
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000394 /* First report each PT_LOAD segment. */
Roland McGrath8068e062010-03-10 23:49:13 -0800395 GElf_Phdr notes_phdr;
Roland McGrathf95760a2010-01-07 20:24:34 -0800396 int ndx = dwfl_report_core_segments (dwfl, elf, phnum, &notes_phdr);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000397 if (unlikely (ndx <= 0))
398 return ndx;
399
400 /* Now sniff segment contents for modules. */
Roland McGrath77cda9d2010-05-04 16:31:43 -0700401 int sniffed = 0;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000402 ndx = 0;
403 do
404 {
405 int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
406 &dwfl_elf_phdr_memory_callback, elf,
407 core_file_read_eagerly, elf);
408 if (unlikely (seg < 0))
409 return seg;
Roland McGrath77cda9d2010-05-04 16:31:43 -0700410 if (seg > ndx)
411 {
412 ndx = seg;
413 ++sniffed;
414 }
415 else
416 ++ndx;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000417 }
Roland McGrathf95760a2010-01-07 20:24:34 -0800418 while (ndx < (int) phnum);
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000419
420 /* Next, we should follow the chain from DT_DEBUG. */
421
422 const void *auxv = NULL;
423 size_t auxv_size = 0;
424 if (likely (notes_phdr.p_type == PT_NOTE))
425 {
426 /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
427
428 Elf_Data *notes = elf_getdata_rawchunk (elf,
429 notes_phdr.p_offset,
430 notes_phdr.p_filesz,
431 ELF_T_NHDR);
432 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 while ((pos = gelf_getnote (notes, pos, &nhdr,
439 &name_pos, &desc_pos)) > 0)
440 if (nhdr.n_type == NT_AUXV
441 && nhdr.n_namesz == sizeof "CORE"
442 && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
443 {
444 auxv = notes->d_buf + desc_pos;
445 auxv_size = nhdr.n_descsz;
446 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
Roland McGrath77cda9d2010-05-04 16:31:43 -0700454 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 return sniffed == 0 || listed > sniffed ? listed : sniffed;
Roland McGrathb4d6f0f2008-08-25 22:55:17 +0000462}
463INTDEF (dwfl_core_file_report)