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