Imported Upstream version 1.3.1
[platform/upstream/libunwind.git] / src / elfxx.c
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2003-2005 Hewlett-Packard Co
3    Copyright (C) 2007 David Mosberger-Tang
4         Contributed by David Mosberger-Tang <dmosberger@gmail.com>
5
6 This file is part of libunwind.
7
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26
27 #include "libunwind_i.h"
28
29 #include <stdio.h>
30 #include <sys/param.h>
31
32 #ifdef HAVE_LZMA
33 #include <lzma.h>
34 #endif /* HAVE_LZMA */
35
36 static Elf_W (Shdr)*
37 elf_w (section_table) (struct elf_image *ei)
38 {
39   Elf_W (Ehdr) *ehdr = ei->image;
40   Elf_W (Off) soff;
41
42   soff = ehdr->e_shoff;
43   if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
44     {
45       Debug (1, "section table outside of image? (%lu > %lu)\n",
46              (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
47              (unsigned long) ei->size);
48       return NULL;
49     }
50
51   return (Elf_W (Shdr) *) ((char *) ei->image + soff);
52 }
53
54 static char*
55 elf_w (string_table) (struct elf_image *ei, int section)
56 {
57   Elf_W (Ehdr) *ehdr = ei->image;
58   Elf_W (Off) soff, str_soff;
59   Elf_W (Shdr) *str_shdr;
60
61   /* this offset is assumed to be OK */
62   soff = ehdr->e_shoff;
63
64   str_soff = soff + (section * ehdr->e_shentsize);
65   if (str_soff + ehdr->e_shentsize > ei->size)
66     {
67       Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
68              (unsigned long) (str_soff + ehdr->e_shentsize),
69              (unsigned long) ei->size);
70       return NULL;
71     }
72   str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
73
74   if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
75     {
76       Debug (1, "string table outside of image? (%lu > %lu)\n",
77              (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
78              (unsigned long) ei->size);
79       return NULL;
80     }
81
82   Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
83   return ei->image + str_shdr->sh_offset;
84 }
85
86 static int
87 elf_w (lookup_symbol) (unw_addr_space_t as,
88                        unw_word_t ip, struct elf_image *ei,
89                        Elf_W (Addr) load_offset,
90                        char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
91 {
92   size_t syment_size;
93   Elf_W (Ehdr) *ehdr = ei->image;
94   Elf_W (Sym) *sym, *symtab, *symtab_end;
95   Elf_W (Shdr) *shdr;
96   Elf_W (Addr) val;
97   int i, ret = -UNW_ENOINFO;
98   char *strtab;
99
100   if (!elf_w (valid_object) (ei))
101     return -UNW_ENOINFO;
102
103   shdr = elf_w (section_table) (ei);
104   if (!shdr)
105     return -UNW_ENOINFO;
106
107   for (i = 0; i < ehdr->e_shnum; ++i)
108     {
109       switch (shdr->sh_type)
110         {
111         case SHT_SYMTAB:
112         case SHT_DYNSYM:
113           symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
114           symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
115           syment_size = shdr->sh_entsize;
116
117           strtab = elf_w (string_table) (ei, shdr->sh_link);
118           if (!strtab)
119             break;
120
121           Debug (16, "symtab=0x%lx[%d]\n",
122                  (long) shdr->sh_offset, shdr->sh_type);
123
124           for (sym = symtab;
125                sym < symtab_end;
126                sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
127             {
128               if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
129                   && sym->st_shndx != SHN_UNDEF)
130                 {
131                   val = sym->st_value;
132                   if (sym->st_shndx != SHN_ABS)
133                     val += load_offset;
134                   if (tdep_get_func_addr (as, val, &val) < 0)
135                     continue;
136                   Debug (16, "0x%016lx info=0x%02x %s\n",
137                          (long) val, sym->st_info, strtab + sym->st_name);
138
139                   if ((Elf_W (Addr)) (ip - val) < *min_dist)
140                     {
141                       *min_dist = (Elf_W (Addr)) (ip - val);
142                       strncpy (buf, strtab + sym->st_name, buf_len);
143                       buf[buf_len - 1] = '\0';
144                       ret = (strlen (strtab + sym->st_name) >= buf_len
145                              ? -UNW_ENOMEM : 0);
146                     }
147                 }
148             }
149           break;
150
151         default:
152           break;
153         }
154       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
155     }
156   return ret;
157 }
158
159 static Elf_W (Addr)
160 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
161                          unsigned long mapoff)
162 {
163   Elf_W (Addr) offset = 0;
164   Elf_W (Ehdr) *ehdr;
165   Elf_W (Phdr) *phdr;
166   int i;
167
168   ehdr = ei->image;
169   phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
170
171   for (i = 0; i < ehdr->e_phnum; ++i)
172     if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
173       {
174         offset = segbase - phdr[i].p_vaddr;
175         break;
176       }
177
178   return offset;
179 }
180
181 #if HAVE_LZMA
182 static size_t
183 xz_uncompressed_size (uint8_t *compressed, size_t length)
184 {
185   uint64_t memlimit = UINT64_MAX;
186   size_t ret = 0, pos = 0;
187   lzma_stream_flags options;
188   lzma_index *index;
189
190   if (length < LZMA_STREAM_HEADER_SIZE)
191     return 0;
192
193   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
194   if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
195     return 0;
196
197   if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
198     return 0;
199
200   uint8_t *indexdata = footer - options.backward_size;
201   if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
202                                 &pos, options.backward_size) != LZMA_OK)
203     return 0;
204
205   if (lzma_index_size (index) == options.backward_size)
206     {
207       ret = lzma_index_uncompressed_size (index);
208     }
209
210   lzma_index_end (index, NULL);
211   return ret;
212 }
213
214 static int
215 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
216 {
217   Elf_W (Shdr) *shdr;
218   uint8_t *compressed = NULL;
219   uint64_t memlimit = UINT64_MAX; /* no memory limit */
220   size_t compressed_len, uncompressed_len;
221
222   shdr = elf_w (find_section) (ei, ".gnu_debugdata");
223   if (!shdr)
224     return 0;
225
226   compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
227   compressed_len = shdr->sh_size;
228
229   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
230   if (uncompressed_len == 0)
231     {
232       Debug (1, "invalid .gnu_debugdata contents\n");
233       return 0;
234     }
235
236   mdi->size = uncompressed_len;
237   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
238                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
239
240   if (mdi->image == MAP_FAILED)
241     return 0;
242
243   size_t in_pos = 0, out_pos = 0;
244   lzma_ret lret;
245   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
246                                     compressed, &in_pos, compressed_len,
247                                     mdi->image, &out_pos, mdi->size);
248   if (lret != LZMA_OK)
249     {
250       Debug (1, "LZMA decompression failed: %d\n", lret);
251       munmap (mdi->image, mdi->size);
252       return 0;
253     }
254
255   return 1;
256 }
257 #else
258 static int
259 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
260 {
261   return 0;
262 }
263 #endif /* !HAVE_LZMA */
264
265 /* Find the ELF image that contains IP and return the "closest"
266    procedure name, if there is one.  With some caching, this could be
267    sped up greatly, but until an application materializes that's
268    sensitive to the performance of this routine, why bother...  */
269
270 HIDDEN int
271 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
272                        unsigned long segbase,
273                        unsigned long mapoff,
274                        unw_word_t ip,
275                        char *buf, size_t buf_len, unw_word_t *offp)
276 {
277   Elf_W (Addr) load_offset;
278   Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
279   int ret;
280
281   load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
282   ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
283
284   /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
285      there as well and replace the previously found if it is closer. */
286   struct elf_image mdi;
287   if (elf_w (extract_minidebuginfo) (ei, &mdi))
288     {
289       int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
290                                            buf_len, &min_dist);
291
292       /* Closer symbol was found (possibly truncated). */
293       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
294         {
295           ret = ret_mdi;
296         }
297
298       munmap (mdi.image, mdi.size);
299     }
300
301   if (min_dist >= ei->size)
302     return -UNW_ENOINFO;                /* not found */
303   if (offp)
304     *offp = min_dist;
305   return ret;
306 }
307
308 HIDDEN int
309 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
310                        char *buf, size_t buf_len, unw_word_t *offp)
311 {
312   unsigned long segbase, mapoff;
313   struct elf_image ei;
314   int ret;
315   char file[PATH_MAX];
316
317   ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX);
318   if (ret < 0)
319     return ret;
320
321   ret = elf_w (load_debuglink) (file, &ei, 1);
322   if (ret < 0)
323     return ret;
324
325   ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
326
327   munmap (ei.image, ei.size);
328   ei.image = NULL;
329
330   return ret;
331 }
332
333 HIDDEN Elf_W (Shdr)*
334 elf_w (find_section) (struct elf_image *ei, const char* secname)
335 {
336   Elf_W (Ehdr) *ehdr = ei->image;
337   Elf_W (Shdr) *shdr;
338   char *strtab;
339   int i;
340
341   if (!elf_w (valid_object) (ei))
342     return 0;
343
344   shdr = elf_w (section_table) (ei);
345   if (!shdr)
346     return 0;
347
348   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
349   if (!strtab)
350     return 0;
351
352   for (i = 0; i < ehdr->e_shnum; ++i)
353     {
354       if (strcmp (strtab + shdr->sh_name, secname) == 0)
355         {
356           if (shdr->sh_offset + shdr->sh_size > ei->size)
357             {
358               Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
359                      secname,
360                      (unsigned long) shdr->sh_offset + shdr->sh_size,
361                      (unsigned long) ei->size);
362               return 0;
363             }
364
365           Debug (16, "found section \"%s\" at 0x%lx\n",
366                  secname, (unsigned long) shdr->sh_offset);
367           return shdr;
368         }
369
370       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
371     }
372
373   /* section not found */
374   return 0;
375 }
376
377 /* Load a debug section, following .gnu_debuglink if appropriate
378  * Loads ei from file if not already mapped.
379  * If is_local, will also search sys directories /usr/local/dbg
380  *
381  * Returns 0 on success, failure otherwise.
382  * ei will be mapped to file or the located .gnu_debuglink from file
383  */
384 HIDDEN int
385 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
386 {
387   int ret;
388   Elf_W (Shdr) *shdr;
389   Elf_W (Ehdr) *prev_image;
390   off_t prev_size;
391
392   if (!ei->image)
393     {
394       ret = elf_map_image(ei, file);
395       if (ret)
396         return ret;
397     }
398
399   prev_image = ei->image;
400   prev_size = ei->size;
401
402   /* Ignore separate debug files which contain a .gnu_debuglink section. */
403   if (is_local == -1) {
404     return 0;
405   }
406
407   shdr = elf_w (find_section) (ei, ".gnu_debuglink");
408   if (shdr) {
409     if (shdr->sh_size >= PATH_MAX ||
410         (shdr->sh_offset + shdr->sh_size > ei->size))
411       {
412         return 0;
413       }
414
415     {
416       char linkbuf[shdr->sh_size];
417       char *link = ((char *) ei->image) + shdr->sh_offset;
418       char *p;
419       static const char *debugdir = "/usr/lib/debug";
420       char basedir[strlen(file) + 1];
421       char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
422
423       memcpy(linkbuf, link, shdr->sh_size);
424
425       if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
426         return 0;
427
428       ei->image = NULL;
429
430       Debug(1, "Found debuglink section, following %s\n", linkbuf);
431
432       p = strrchr (file, '/');
433       if (p != NULL)
434         {
435           memcpy (basedir, file, p - file);
436           basedir[p - file] = '\0';
437         }
438       else
439         basedir[0] = 0;
440
441       strcpy (newname, basedir);
442       strcat (newname, "/");
443       strcat (newname, linkbuf);
444       ret = elf_w (load_debuglink) (newname, ei, -1);
445
446       if (ret == -1)
447         {
448           strcpy (newname, basedir);
449           strcat (newname, "/.debug/");
450           strcat (newname, linkbuf);
451           ret = elf_w (load_debuglink) (newname, ei, -1);
452         }
453
454       if (ret == -1 && is_local == 1)
455         {
456           strcpy (newname, debugdir);
457           strcat (newname, basedir);
458           strcat (newname, "/");
459           strcat (newname, linkbuf);
460           ret = elf_w (load_debuglink) (newname, ei, -1);
461         }
462
463       if (ret == -1)
464         {
465           /* No debuglink file found even though .gnu_debuglink existed */
466           ei->image = prev_image;
467           ei->size = prev_size;
468
469           return 0;
470         }
471       else
472         {
473           munmap (prev_image, prev_size);
474         }
475
476       return ret;
477     }
478   }
479
480   return 0;
481 }