• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <limits.h>
32 
33 #ifdef HAVE_LZMA
34 #include <lzma.h>
35 #endif /* HAVE_LZMA */
36 
Elf_W(Shdr)37 static Elf_W (Shdr)*
38 elf_w (section_table) (struct elf_image *ei)
39 {
40   Elf_W (Ehdr) *ehdr = ei->image;
41   Elf_W (Off) soff;
42 
43   soff = ehdr->e_shoff;
44   if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
45     {
46       Debug (1, "section table outside of image? (%lu > %lu)\n",
47              (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
48              (unsigned long) ei->size);
49       return NULL;
50     }
51 
52   return (Elf_W (Shdr) *) ((char *) ei->image + soff);
53 }
54 
55 static char*
elf_w(string_table)56 elf_w (string_table) (struct elf_image *ei, int section)
57 {
58   Elf_W (Ehdr) *ehdr = ei->image;
59   Elf_W (Off) soff, str_soff;
60   Elf_W (Shdr) *str_shdr;
61 
62   /* this offset is assumed to be OK */
63   soff = ehdr->e_shoff;
64 
65   str_soff = soff + (section * ehdr->e_shentsize);
66   if (str_soff + ehdr->e_shentsize > ei->size)
67     {
68       Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
69              (unsigned long) (str_soff + ehdr->e_shentsize),
70              (unsigned long) ei->size);
71       return NULL;
72     }
73   str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
74 
75   if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
76     {
77       Debug (1, "string table outside of image? (%lu > %lu)\n",
78              (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
79              (unsigned long) ei->size);
80       return NULL;
81     }
82 
83   Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
84   return ei->image + str_shdr->sh_offset;
85 }
86 
87 static int
elf_w(lookup_symbol)88 elf_w (lookup_symbol) (unw_addr_space_t as,
89                        unw_word_t ip, struct elf_image *ei,
90                        Elf_W (Addr) load_offset,
91                        char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
92 {
93   size_t syment_size;
94   Elf_W (Ehdr) *ehdr = ei->image;
95   Elf_W (Sym) *sym, *symtab, *symtab_end;
96   Elf_W (Shdr) *shdr;
97   Elf_W (Addr) val;
98   int i, ret = -UNW_ENOINFO;
99   char *strtab;
100 
101   if (!elf_w (valid_object) (ei))
102     return -UNW_ENOINFO;
103 
104   shdr = elf_w (section_table) (ei);
105   if (!shdr)
106     return -UNW_ENOINFO;
107 
108   for (i = 0; i < ehdr->e_shnum; ++i)
109     {
110       switch (shdr->sh_type)
111         {
112         case SHT_SYMTAB:
113         case SHT_DYNSYM:
114           symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
115           symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
116           syment_size = shdr->sh_entsize;
117 
118           strtab = elf_w (string_table) (ei, shdr->sh_link);
119           if (!strtab)
120             break;
121 
122           Debug (16, "symtab=0x%lx[%d]\n",
123                  (long) shdr->sh_offset, shdr->sh_type);
124 
125           for (sym = symtab;
126                sym < symtab_end;
127                sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
128             {
129               if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
130                   && sym->st_shndx != SHN_UNDEF)
131                 {
132                   val = sym->st_value;
133                   if (sym->st_shndx != SHN_ABS)
134                     val += load_offset;
135                   if (tdep_get_func_addr (as, val, &val) < 0)
136                     continue;
137                   Debug (16, "0x%016lx info=0x%02x %s\n",
138                          (long) val, sym->st_info, strtab + sym->st_name);
139 
140                   if ((Elf_W (Addr)) (ip - val) < *min_dist)
141                     {
142                       *min_dist = (Elf_W (Addr)) (ip - val);
143                       strncpy (buf, strtab + sym->st_name, buf_len);
144                       buf[buf_len - 1] = '\0';
145                       ret = (strlen (strtab + sym->st_name) >= buf_len
146                              ? -UNW_ENOMEM : 0);
147                     }
148                 }
149             }
150           break;
151 
152         default:
153           break;
154         }
155       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
156     }
157   return ret;
158 }
159 
160 static int
elf_w(find_symbol_info_in_image)161 elf_w (find_symbol_info_in_image) (struct elf_image *ei,
162                                    unw_word_t load_offset,
163                                    uint64_t pc,
164                                    int buf_sz,
165                                    char *buf,
166                                    uint64_t *sym_start,
167                                    uint64_t *sym_end)
168 {
169   size_t syment_size;
170   Elf_W (Ehdr) *ehdr = ei->image;
171   Elf_W (Sym) *sym, *symtab, *symtab_end;
172   Elf_W (Shdr) *shdr;
173   Elf_W (Addr) val;
174   int i, ret = -UNW_ENOINFO;
175   char *strtab;
176   uint64_t start = 0;
177   uint64_t end = 0;
178 
179   if (!elf_w (valid_object) (ei))
180     return -UNW_ENOINFO;
181 
182   shdr = elf_w (section_table) (ei);
183   if (!shdr)
184     return -UNW_ENOINFO;
185 
186   for (i = 0; i < ehdr->e_shnum; ++i)
187     {
188       switch (shdr->sh_type)
189         {
190         case SHT_SYMTAB:
191         case SHT_DYNSYM:
192           symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
193           symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
194           syment_size = shdr->sh_entsize;
195           strtab = elf_w (string_table) (ei, shdr->sh_link);
196           if (!strtab) {
197             Dprintf( "no strtab?\n");
198             break;
199           }
200 
201           for (sym = symtab;
202                sym < symtab_end;
203                sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
204             {
205               if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
206                   && sym->st_shndx != SHN_UNDEF)
207                 {
208                   val = sym->st_value;
209                   if (sym->st_shndx != SHN_ABS)
210                     val += load_offset;
211                   start = (uint64_t)val;
212                   end = start + (uint64_t)sym->st_size;
213                   if (pc >= start && pc <= end) {
214                     strncpy (buf, strtab + sym->st_name, buf_sz);
215                     buf[buf_sz - 1] = '\0';
216                     *sym_start = start;
217                     *sym_end = end;
218                     return 0;
219                   }
220                 }
221             }
222           break;
223 
224         default:
225           break;
226         }
227       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
228     }
229   return ret;
230 }
231 
232 static Elf_W (Addr)
elf_w(get_load_offset)233 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
234                          unsigned long mapoff)
235 {
236   Elf_W (Addr) offset = 0;
237   Elf_W (Ehdr) *ehdr;
238   Elf_W (Phdr) *phdr;
239   int i;
240   // mapoff is obtained from mmap informations, so is always aligned on a page size.
241   // PT_LOAD program headers p_offset however is not guaranteed to be aligned on a
242   // page size, ld.lld generate libraries where this is not the case. So we must
243   // make sure we compare both values with the same alignment.
244   unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL);
245 
246   ehdr = ei->image;
247   phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
248 
249   for (i = 0; i < ehdr->e_phnum; ++i)
250     if (phdr[i].p_type == PT_LOAD && (phdr[i].p_offset & pagesize_alignment_mask) == mapoff)
251       {
252         offset = segbase - phdr[i].p_vaddr + (phdr[i].p_offset & (~pagesize_alignment_mask));
253         break;
254       }
255 
256   return offset;
257 }
258 
259 #if HAVE_LZMA
260 static size_t
xz_uncompressed_size(uint8_t * compressed,size_t length)261 xz_uncompressed_size (uint8_t *compressed, size_t length)
262 {
263   uint64_t memlimit = UINT64_MAX;
264   size_t ret = 0, pos = 0;
265   lzma_stream_flags options;
266   lzma_index *index;
267 
268   if (length < LZMA_STREAM_HEADER_SIZE)
269     return 0;
270 
271   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
272   if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
273     return 0;
274 
275   if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
276     return 0;
277 
278   uint8_t *indexdata = footer - options.backward_size;
279   if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
280                                 &pos, options.backward_size) != LZMA_OK)
281     return 0;
282 
283   if (lzma_index_size (index) == options.backward_size)
284     {
285       ret = lzma_index_uncompressed_size (index);
286     }
287 
288   lzma_index_end (index, NULL);
289   return ret;
290 }
291 
292 static int
elf_w(extract_minidebuginfo)293 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
294 {
295   Elf_W (Shdr) *shdr;
296   uint8_t *compressed = NULL;
297   uint64_t memlimit = UINT64_MAX; /* no memory limit */
298   size_t compressed_len, uncompressed_len;
299   mdi->has_try_load = 1;
300   shdr = elf_w (find_section) (ei, ".gnu_debugdata");
301   if (!shdr)
302     return 0;
303 
304   compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
305   compressed_len = shdr->sh_size;
306 
307   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
308   if (uncompressed_len == 0)
309     {
310       Dprintf("invalid .gnu_debugdata contents\n");
311       return 0;
312     }
313 
314   mdi->size = uncompressed_len;
315   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
316                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
317 
318   if (mdi->image == MAP_FAILED)
319     return 0;
320 
321   size_t in_pos = 0, out_pos = 0;
322   lzma_ret lret;
323   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
324                                     compressed, &in_pos, compressed_len,
325                                     mdi->image, &out_pos, mdi->size);
326   if (lret != LZMA_OK)
327     {
328       Dprintf( "LZMA decompression failed: %d\n", lret);
329       munmap (mdi->image, mdi->size);
330       mdi->image = NULL;
331       return 0;
332     }
333 
334   return 1;
335 }
336 #else
337 static int
elf_w(extract_minidebuginfo)338 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
339 {
340   return 0;
341 }
342 #endif /* !HAVE_LZMA */
343 
344 /* Find the ELF image that contains IP and return the "closest"
345    procedure name, if there is one.  With some caching, this could be
346    sped up greatly, but until an application materializes that's
347    sensitive to the performance of this routine, why bother...  */
348 
349 HIDDEN int
elf_w(get_proc_name_in_image)350 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
351                        unsigned long segbase,
352                        unsigned long mapoff,
353                        unw_word_t ip,
354                        char *buf, size_t buf_len, unw_word_t *offp)
355 {
356   Elf_W (Addr) load_offset;
357   Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
358   int ret;
359 
360   load_offset = segbase - ei->load_offset;
361   ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
362 
363   /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
364      there as well and replace the previously found if it is closer. */
365   struct elf_image mdi;
366   if (elf_w (extract_minidebuginfo) (ei, &mdi))
367     {
368       int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
369                                            buf_len, &min_dist);
370 
371       /* Closer symbol was found (possibly truncated). */
372       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
373         {
374           ret = ret_mdi;
375         }
376 
377       munmap (mdi.image, mdi.size);
378     }
379 
380   if (min_dist >= ei->size)
381     return -UNW_ENOINFO;                /* not found */
382   if (offp)
383     *offp = min_dist;
384   return ret;
385 }
386 
387 /* Add For Cache MAP And ELF */
388 HIDDEN int
elf_w(get_proc_name)389 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
390                        char *buf, size_t buf_len, unw_word_t *offp)
391 {
392   int ret;
393   struct map_info *map = tdep_get_elf_image (as, pid, ip);
394 
395   if (map == NULL)
396     return -UNW_ENOINFO;
397 
398   ret = elf_w (get_proc_name_in_image) (as, &map->ei, map->start, map->offset, ip, buf, buf_len, offp);
399 
400   return ret;
401 }
402 /* Add For Cache MAP And ELF */
Elf_W(Shdr)403 HIDDEN Elf_W (Shdr)*
404 elf_w (find_section) (struct elf_image *ei, const char* secname)
405 {
406   Elf_W (Ehdr) *ehdr = ei->image;
407   Elf_W (Shdr) *shdr;
408   char *strtab;
409   int i;
410 
411   if (!elf_w (valid_object) (ei))
412     return 0;
413 
414   shdr = elf_w (section_table) (ei);
415   if (!shdr)
416     return 0;
417 
418   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
419   if (!strtab)
420     return 0;
421 
422   for (i = 0; i < ehdr->e_shnum; ++i)
423     {
424       if (strcmp (strtab + shdr->sh_name, secname) == 0)
425         {
426           if (shdr->sh_offset + shdr->sh_size > ei->size)
427             {
428               Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
429                      secname,
430                      (unsigned long) shdr->sh_offset + shdr->sh_size,
431                      (unsigned long) ei->size);
432               return 0;
433             }
434 
435           Debug (16, "found section \"%s\" at 0x%lx\n",
436                  secname, (unsigned long) shdr->sh_offset);
437           return shdr;
438         }
439 
440       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
441     }
442 
443   /* section not found */
444   return 0;
445 }
446 
447 /* Load a debug section, following .gnu_debuglink if appropriate
448  * Loads ei from file if not already mapped.
449  * If is_local, will also search sys directories /usr/local/dbg
450  *
451  * Returns 0 on success, failure otherwise.
452  * ei will be mapped to file or the located .gnu_debuglink from file
453  */
454 HIDDEN int
elf_w(load_debuglink)455 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
456 {
457   int ret;
458   Elf_W (Shdr) *shdr;
459   Elf_W (Ehdr) *prev_image;
460   off_t prev_size;
461 
462   if (!ei->image)
463     {
464       ret = elf_map_image(ei, file);
465       if (ret)
466         return ret;
467     }
468 
469   prev_image = ei->image;
470   prev_size = ei->size;
471 
472   /* Ignore separate debug files which contain a .gnu_debuglink section. */
473   if (is_local == -1) {
474     return 0;
475   }
476 
477   shdr = elf_w (find_section) (ei, ".gnu_debuglink");
478   if (shdr) {
479     if (shdr->sh_size >= PATH_MAX ||
480 	(shdr->sh_offset + shdr->sh_size > ei->size))
481       {
482 	return 0;
483       }
484 
485     {
486       char linkbuf[shdr->sh_size];
487       char *link = ((char *) ei->image) + shdr->sh_offset;
488       char *p;
489       static const char *debugdir = "/usr/lib/debug";
490       char basedir[strlen(file) + 1];
491       char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
492 
493       memcpy(linkbuf, link, shdr->sh_size);
494 
495       if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
496 	return 0;
497 
498       ei->image = NULL;
499 
500       Debug(1, "Found debuglink section, following %s\n", linkbuf);
501 
502       p = strrchr (file, '/');
503       if (p != NULL)
504 	{
505 	  memcpy (basedir, file, p - file);
506 	  basedir[p - file] = '\0';
507 	}
508       else
509 	basedir[0] = 0;
510 
511       strcpy (newname, basedir);
512       strcat (newname, "/");
513       strcat (newname, linkbuf);
514       ret = elf_w (load_debuglink) (newname, ei, -1);
515 
516       if (ret == -1)
517 	{
518 	  strcpy (newname, basedir);
519 	  strcat (newname, "/.debug/");
520 	  strcat (newname, linkbuf);
521 	  ret = elf_w (load_debuglink) (newname, ei, -1);
522 	}
523 
524       if (ret == -1 && is_local == 1)
525 	{
526 	  strcpy (newname, debugdir);
527 	  strcat (newname, basedir);
528 	  strcat (newname, "/");
529 	  strcat (newname, linkbuf);
530 	  ret = elf_w (load_debuglink) (newname, ei, -1);
531 	}
532 
533       if (ret == -1)
534         {
535           /* No debuglink file found even though .gnu_debuglink existed */
536           ei->image = prev_image;
537           ei->size = prev_size;
538 
539           return 0;
540         }
541       else
542         {
543           munmap (prev_image, prev_size);
544         }
545 
546       return ret;
547     }
548   }
549 
550   return 0;
551 }
552 
elf_w(get_symbol_info_in_image)553 int elf_w (get_symbol_info_in_image) (struct elf_image *ei,
554                                       unsigned long segbase,
555                                       unsigned long mapoff,
556                                       uint64_t pc,
557                                       int buf_sz,
558                                       char *buf,
559                                       uint64_t *sym_start,
560                                       uint64_t *sym_end)
561 {
562   Elf_W (Addr) load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
563   int ret = elf_w (find_symbol_info_in_image) (ei, load_offset, pc, buf_sz, buf, sym_start, sym_end);
564   if (ret == 0) {
565     return ret;
566   }
567 
568   if (ei->mdi == NULL) {
569     return ret;
570   }
571 
572   if (ei->mdi->image == NULL && ei->mdi->has_try_load) {
573     return ret;
574   }
575 
576   if (ei->mdi->image != NULL || elf_w (extract_minidebuginfo) (ei, ei->mdi)) {
577       ret = elf_w (find_symbol_info_in_image) (ei->mdi, load_offset, pc, buf_sz, buf, sym_start, sym_end);
578   }
579   return ret;
580 }