Load ahead core file chunk only if the area is vaddr-contiguous
[platform/upstream/elfutils.git] / libdwfl / core-file.c
1 /* Core file handling.
2    Copyright (C) 2008, 2009 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, const GElf_Ehdr *ehdr,
145                            GElf_Phdr *notes)
146 {
147   if (unlikely (dwfl == NULL))
148     return -1;
149
150   if (unlikely (elf == NULL) || unlikely (ehdr == NULL))
151     {
152       __libdw_seterrno (DWFL_E_LIBELF);
153       return -1;
154     }
155
156   int result = 0;
157
158   if (notes != NULL)
159     notes->p_type = PT_NULL;
160
161   for (int ndx = 0; result >= 0 && ndx < ehdr->e_phnum; ++ndx)
162     {
163       GElf_Phdr phdr_mem;
164       GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
165       if (unlikely (phdr == NULL))
166         {
167           __libdwfl_seterrno (DWFL_E_LIBELF);
168           return -1;
169         }
170       switch (phdr->p_type)
171         {
172         case PT_LOAD:
173           result = dwfl_report_segment (dwfl, ndx, phdr, 0, NULL);
174           break;
175
176         case PT_NOTE:
177           if (notes != NULL)
178             {
179               *notes = *phdr;
180               notes = NULL;
181             }
182           break;
183         }
184     }
185
186   return result;
187 }
188
189 /* Never read more than this much without mmap.  */
190 #define MAX_EAGER_COST  8192
191
192 static bool
193 core_file_read_eagerly (Dwfl_Module *mod,
194                         void **userdata __attribute__ ((unused)),
195                         const char *name __attribute__ ((unused)),
196                         Dwarf_Addr start __attribute__ ((unused)),
197                         void **buffer, size_t *buffer_available,
198                         GElf_Off cost, GElf_Off worthwhile,
199                         GElf_Off whole,
200                         GElf_Off contiguous __attribute__ ((unused)),
201                         void *arg, Elf **elfp)
202 {
203   Elf *core = arg;
204
205   if (whole <= *buffer_available)
206     {
207       /* All there ever was, we already have on hand.  */
208
209       if (core->map_address == NULL)
210         {
211           /* We already malloc'd the buffer.  */
212           *elfp = elf_memory (*buffer, whole);
213           if (unlikely (*elfp == NULL))
214             return false;
215
216           (*elfp)->flags |= ELF_F_MALLOCED;
217           *buffer = NULL;
218           *buffer_available = 0;
219           return true;
220         }
221
222       /* We can use the image inside the core file directly.  */
223       *elfp = elf_begin_rand (core, *buffer - core->map_address, whole, NULL);
224       *buffer = NULL;
225       *buffer_available = 0;
226       return *elfp != NULL;
227     }
228
229   /* We don't have the whole file.
230      Figure out if this is better than nothing.  */
231
232   if (worthwhile == 0)
233     /* Caller doesn't think so.  */
234     return false;
235
236   /*
237     XXX would like to fall back to partial file via memory
238     when build id find_elf fails
239     also, link_map name may give file name from disk better than partial here
240     requires find_elf hook re-doing the magic to fall back if no file found
241   */
242
243   if (mod->build_id_len > 0)
244     /* There is a build ID that could help us find the whole file,
245        which might be more useful than what we have.
246        We'll just rely on that.  */
247     return false;
248
249   if (core->map_address != NULL)
250     /* It's cheap to get, so get it.  */
251     return true;
252
253   /* Only use it if there isn't too much to be read.  */
254   return cost <= MAX_EAGER_COST;
255 }
256
257 bool
258 dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
259                                void **buffer, size_t *buffer_available,
260                                GElf_Addr vaddr,
261                                size_t minread,
262                                void *arg)
263 {
264   Elf *elf = arg;
265
266   if (ndx == -1)
267     {
268       /* Called for cleanup.  */
269       if (elf->map_address == NULL)
270         free (*buffer);
271       *buffer = NULL;
272       *buffer_available = 0;
273       return false;
274     }
275
276   const GElf_Off align = dwfl->segment_align ?: 1;
277   GElf_Phdr phdr;
278
279   do
280     if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
281       return false;
282   while (phdr.p_type != PT_LOAD
283          || ((phdr.p_vaddr + phdr.p_memsz + align - 1) & -align) <= vaddr);
284
285   GElf_Off start = vaddr - phdr.p_vaddr + phdr.p_offset;
286   GElf_Off end;
287   GElf_Addr end_vaddr;
288
289   inline void update_end ()
290   {
291     end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align;
292     end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align;
293   }
294
295   update_end ();
296
297   /* Use following contiguous segments to get towards SIZE.  */
298   inline bool more (size_t size)
299   {
300     while (end <= start || end - start < size)
301       {
302         if (phdr.p_filesz < phdr.p_memsz)
303           /* This segment is truncated, so no following one helps us.  */
304           return false;
305
306         if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
307           return false;
308
309         if (phdr.p_type == PT_LOAD)
310           {
311             if (phdr.p_offset > end
312                 || phdr.p_vaddr > end_vaddr)
313               /* It's discontiguous!  */
314               return false;
315
316             update_end ();
317           }
318       }
319     return true;
320   }
321
322   /* We need at least this much.  */
323   if (! more (minread))
324     return false;
325
326   /* See how much more we can get of what the caller wants.  */
327   (void) more (*buffer_available);
328
329   /* If it's already on hand anyway, use as much as there is.  */
330   if (elf->map_address != NULL)
331     (void) more (elf->maximum_size - start);
332
333   /* Make sure we don't look past the end of the actual file,
334      even if the headers tell us to.  */
335   if (unlikely (end > elf->maximum_size))
336     end = elf->maximum_size;
337
338   /* If the file is too small, there is nothing at all to get.  */
339   if (unlikely (start >= end))
340     return false;
341
342   if (elf->map_address != NULL)
343     {
344       void *contents = elf->map_address + elf->start_offset + start;
345       size_t size = end - start;
346
347       if (minread == 0)         /* String mode.  */
348         {
349           const void *eos = memchr (contents, '\0', size);
350           if (unlikely (eos == NULL) || unlikely (eos == contents))
351             return false;
352           size = eos + 1 - contents;
353         }
354
355       if (*buffer == NULL)
356         {
357           *buffer = contents;
358           *buffer_available = size;
359         }
360       else
361         {
362           *buffer_available = MIN (size, *buffer_available);
363           memcpy (*buffer, contents, *buffer_available);
364         }
365     }
366   else
367     {
368       void *into = *buffer;
369       if (*buffer == NULL)
370         {
371           *buffer_available = MIN (minread ?: 512,
372                                    MAX (4096, MIN (end - start,
373                                                    *buffer_available)));
374           into = malloc (*buffer_available);
375           if (unlikely (into == NULL))
376             {
377               __libdwfl_seterrno (DWFL_E_NOMEM);
378               return false;
379             }
380         }
381
382       ssize_t nread = pread_retry (elf->fildes, into, *buffer_available, start);
383       if (nread < (ssize_t) minread)
384         {
385           if (into != *buffer)
386             free (into);
387           if (nread < 0)
388             __libdwfl_seterrno (DWFL_E_ERRNO);
389           return false;
390         }
391
392       if (minread == 0)         /* String mode.  */
393         {
394           const void *eos = memchr (into, '\0', nread);
395           if (unlikely (eos == NULL) || unlikely (eos == into))
396             {
397               if (*buffer == NULL)
398                 free (into);
399               return false;
400             }
401           nread = eos + 1 - into;
402         }
403
404       if (*buffer == NULL)
405         *buffer = into;
406       *buffer_available = nread;
407     }
408
409   return true;
410 }
411
412 int
413 dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const GElf_Ehdr *ehdr)
414 {
415   GElf_Phdr notes_phdr;
416
417   /* First report each PT_LOAD segment.  */
418   int ndx = dwfl_report_core_segments (dwfl, elf, ehdr, &notes_phdr);
419   if (unlikely (ndx <= 0))
420     return ndx;
421
422   /* Now sniff segment contents for modules.  */
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       ndx = seg > ndx ? seg : ndx + 1;
432     }
433   while (ndx < ehdr->e_phnum);
434
435   /* Next, we should follow the chain from DT_DEBUG.  */
436
437   const void *auxv = NULL;
438   size_t auxv_size = 0;
439   if (likely (notes_phdr.p_type == PT_NOTE))
440     {
441       /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
442
443       Elf_Data *notes = elf_getdata_rawchunk (elf,
444                                               notes_phdr.p_offset,
445                                               notes_phdr.p_filesz,
446                                               ELF_T_NHDR);
447       if (likely (notes != NULL))
448         {
449           size_t pos = 0;
450           GElf_Nhdr nhdr;
451           size_t name_pos;
452           size_t desc_pos;
453           while ((pos = gelf_getnote (notes, pos, &nhdr,
454                                       &name_pos, &desc_pos)) > 0)
455             if (nhdr.n_type == NT_AUXV
456                 && nhdr.n_namesz == sizeof "CORE"
457                 && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
458               {
459                 auxv = notes->d_buf + desc_pos;
460                 auxv_size = nhdr.n_descsz;
461                 break;
462               }
463         }
464     }
465
466   /* Now we have NT_AUXV contents.  From here on this processing could be
467      used for a live process with auxv read from /proc.  */
468
469   return dwfl_link_map_report (dwfl, auxv, auxv_size,
470                                dwfl_elf_phdr_memory_callback, elf);
471 }
472 INTDEF (dwfl_core_file_report)