• 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             break;
198 
199           for (sym = symtab;
200                sym < symtab_end;
201                sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
202             {
203               if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
204                   && sym->st_shndx != SHN_UNDEF)
205                 {
206                   val = sym->st_value;
207                   if (sym->st_shndx != SHN_ABS)
208                     val += load_offset;
209                   start = (uint64_t)val;
210                   end = start + (uint64_t)sym->st_size;
211                   if (pc >= start && pc <= end) {
212                     strncpy (buf, strtab + sym->st_name, buf_sz);
213                     buf[buf_sz - 1] = '\0';
214                     *sym_start = start;
215                     *sym_end = end;
216                     return 0;
217                   }
218                 }
219             }
220           break;
221 
222         default:
223           break;
224         }
225       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
226     }
227   return ret;
228 }
229 
230 static Elf_W (Addr)
elf_w(get_load_offset)231 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
232                          unsigned long mapoff)
233 {
234   Elf_W (Addr) offset = 0;
235   Elf_W (Ehdr) *ehdr;
236   Elf_W (Phdr) *phdr;
237   int i;
238   // mapoff is obtained from mmap informations, so is always aligned on a page size.
239   // PT_LOAD program headers p_offset however is not guaranteed to be aligned on a
240   // page size, ld.lld generate libraries where this is not the case. So we must
241   // make sure we compare both values with the same alignment.
242   unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL);
243 
244   ehdr = ei->image;
245   phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
246 
247   for (i = 0; i < ehdr->e_phnum; ++i)
248     if (phdr[i].p_type == PT_LOAD && (phdr[i].p_offset & pagesize_alignment_mask) == mapoff)
249       {
250         offset = segbase - phdr[i].p_vaddr + (phdr[i].p_offset & (~pagesize_alignment_mask));
251         break;
252       }
253 
254   return offset;
255 }
256 
257 #if HAVE_LZMA
258 static size_t
xz_uncompressed_size(uint8_t * compressed,size_t length)259 xz_uncompressed_size (uint8_t *compressed, size_t length)
260 {
261   uint64_t memlimit = UINT64_MAX;
262   size_t ret = 0, pos = 0;
263   lzma_stream_flags options;
264   lzma_index *index;
265 
266   if (length < LZMA_STREAM_HEADER_SIZE)
267     return 0;
268 
269   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
270   if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
271     return 0;
272 
273   if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
274     return 0;
275 
276   uint8_t *indexdata = footer - options.backward_size;
277   if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
278                                 &pos, options.backward_size) != LZMA_OK)
279     return 0;
280 
281   if (lzma_index_size (index) == options.backward_size)
282     {
283       ret = lzma_index_uncompressed_size (index);
284     }
285 
286   lzma_index_end (index, NULL);
287   return ret;
288 }
289 
290 static int
elf_w(extract_minidebuginfo)291 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
292 {
293   Elf_W (Shdr) *shdr;
294   uint8_t *compressed = NULL;
295   uint64_t memlimit = UINT64_MAX; /* no memory limit */
296   size_t compressed_len, uncompressed_len;
297 
298   shdr = elf_w (find_section) (ei, ".gnu_debugdata");
299   if (!shdr)
300     return 0;
301 
302   compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
303   compressed_len = shdr->sh_size;
304 
305   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
306   if (uncompressed_len == 0)
307     {
308       Debug (1, "invalid .gnu_debugdata contents\n");
309       return 0;
310     }
311 
312   mdi->size = uncompressed_len;
313   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
314                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
315 
316   if (mdi->image == MAP_FAILED)
317     return 0;
318 
319   size_t in_pos = 0, out_pos = 0;
320   lzma_ret lret;
321   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
322                                     compressed, &in_pos, compressed_len,
323                                     mdi->image, &out_pos, mdi->size);
324   if (lret != LZMA_OK)
325     {
326       Debug (1, "LZMA decompression failed: %d\n", lret);
327       munmap (mdi->image, mdi->size);
328       return 0;
329     }
330 
331   return 1;
332 }
333 #else
334 static int
elf_w(extract_minidebuginfo)335 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
336 {
337   return 0;
338 }
339 #endif /* !HAVE_LZMA */
340 
341 /* Find the ELF image that contains IP and return the "closest"
342    procedure name, if there is one.  With some caching, this could be
343    sped up greatly, but until an application materializes that's
344    sensitive to the performance of this routine, why bother...  */
345 
346 HIDDEN int
elf_w(get_proc_name_in_image)347 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
348                        unsigned long segbase,
349                        unsigned long mapoff,
350                        unw_word_t ip,
351                        char *buf, size_t buf_len, unw_word_t *offp)
352 {
353   Elf_W (Addr) load_offset;
354   Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
355   int ret;
356 
357   load_offset = segbase - ei->load_offset;
358   ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
359 
360   /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
361      there as well and replace the previously found if it is closer. */
362   struct elf_image mdi;
363   if (elf_w (extract_minidebuginfo) (ei, &mdi))
364     {
365       int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
366                                            buf_len, &min_dist);
367 
368       /* Closer symbol was found (possibly truncated). */
369       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
370         {
371           ret = ret_mdi;
372         }
373 
374       munmap (mdi.image, mdi.size);
375     }
376 
377   if (min_dist >= ei->size)
378     return -UNW_ENOINFO;                /* not found */
379   if (offp)
380     *offp = min_dist;
381   return ret;
382 }
383 
384 /* Add For Cache MAP And ELF */
385 HIDDEN int
elf_w(get_proc_name)386 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
387                        char *buf, size_t buf_len, unw_word_t *offp)
388 {
389   int ret;
390   struct map_info *map = tdep_get_elf_image (as, pid, ip);
391 
392   if (map == NULL)
393     return -UNW_ENOINFO;
394 
395   ret = elf_w (get_proc_name_in_image) (as, &map->ei, map->start, map->offset, ip, buf, buf_len, offp);
396 
397   return ret;
398 }
399 /* Add For Cache MAP And ELF */
Elf_W(Shdr)400 HIDDEN Elf_W (Shdr)*
401 elf_w (find_section) (struct elf_image *ei, const char* secname)
402 {
403   Elf_W (Ehdr) *ehdr = ei->image;
404   Elf_W (Shdr) *shdr;
405   char *strtab;
406   int i;
407 
408   if (!elf_w (valid_object) (ei))
409     return 0;
410 
411   shdr = elf_w (section_table) (ei);
412   if (!shdr)
413     return 0;
414 
415   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
416   if (!strtab)
417     return 0;
418 
419   for (i = 0; i < ehdr->e_shnum; ++i)
420     {
421       if (strcmp (strtab + shdr->sh_name, secname) == 0)
422         {
423           if (shdr->sh_offset + shdr->sh_size > ei->size)
424             {
425               Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
426                      secname,
427                      (unsigned long) shdr->sh_offset + shdr->sh_size,
428                      (unsigned long) ei->size);
429               return 0;
430             }
431 
432           Debug (16, "found section \"%s\" at 0x%lx\n",
433                  secname, (unsigned long) shdr->sh_offset);
434           return shdr;
435         }
436 
437       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
438     }
439 
440   /* section not found */
441   return 0;
442 }
443 
444 /* Load a debug section, following .gnu_debuglink if appropriate
445  * Loads ei from file if not already mapped.
446  * If is_local, will also search sys directories /usr/local/dbg
447  *
448  * Returns 0 on success, failure otherwise.
449  * ei will be mapped to file or the located .gnu_debuglink from file
450  */
451 HIDDEN int
elf_w(load_debuglink)452 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
453 {
454   int ret;
455   Elf_W (Shdr) *shdr;
456   Elf_W (Ehdr) *prev_image;
457   off_t prev_size;
458 
459   if (!ei->image)
460     {
461       ret = elf_map_image(ei, file);
462       if (ret)
463         return ret;
464     }
465 
466   prev_image = ei->image;
467   prev_size = ei->size;
468 
469   /* Ignore separate debug files which contain a .gnu_debuglink section. */
470   if (is_local == -1) {
471     return 0;
472   }
473 
474   shdr = elf_w (find_section) (ei, ".gnu_debuglink");
475   if (shdr) {
476     if (shdr->sh_size >= PATH_MAX ||
477 	(shdr->sh_offset + shdr->sh_size > ei->size))
478       {
479 	return 0;
480       }
481 
482     {
483       char linkbuf[shdr->sh_size];
484       char *link = ((char *) ei->image) + shdr->sh_offset;
485       char *p;
486       static const char *debugdir = "/usr/lib/debug";
487       char basedir[strlen(file) + 1];
488       char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
489 
490       memcpy(linkbuf, link, shdr->sh_size);
491 
492       if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
493 	return 0;
494 
495       ei->image = NULL;
496 
497       Debug(1, "Found debuglink section, following %s\n", linkbuf);
498 
499       p = strrchr (file, '/');
500       if (p != NULL)
501 	{
502 	  memcpy (basedir, file, p - file);
503 	  basedir[p - file] = '\0';
504 	}
505       else
506 	basedir[0] = 0;
507 
508       strcpy (newname, basedir);
509       strcat (newname, "/");
510       strcat (newname, linkbuf);
511       ret = elf_w (load_debuglink) (newname, ei, -1);
512 
513       if (ret == -1)
514 	{
515 	  strcpy (newname, basedir);
516 	  strcat (newname, "/.debug/");
517 	  strcat (newname, linkbuf);
518 	  ret = elf_w (load_debuglink) (newname, ei, -1);
519 	}
520 
521       if (ret == -1 && is_local == 1)
522 	{
523 	  strcpy (newname, debugdir);
524 	  strcat (newname, basedir);
525 	  strcat (newname, "/");
526 	  strcat (newname, linkbuf);
527 	  ret = elf_w (load_debuglink) (newname, ei, -1);
528 	}
529 
530       if (ret == -1)
531         {
532           /* No debuglink file found even though .gnu_debuglink existed */
533           ei->image = prev_image;
534           ei->size = prev_size;
535 
536           return 0;
537         }
538       else
539         {
540           munmap (prev_image, prev_size);
541         }
542 
543       return ret;
544     }
545   }
546 
547   return 0;
548 }
549 
elf_w(get_symbol_info_in_image)550 int elf_w (get_symbol_info_in_image) (struct elf_image *ei,
551                                       unsigned long segbase,
552                                       unsigned long mapoff,
553                                       uint64_t pc,
554                                       int buf_sz,
555                                       char *buf,
556                                       uint64_t *sym_start,
557                                       uint64_t *sym_end)
558 {
559   Elf_W (Addr) load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
560   int ret = elf_w (find_symbol_info_in_image) (ei, load_offset, pc, buf_sz, buf, sym_start, sym_end);
561 
562   struct elf_image mdi;
563   if (elf_w (extract_minidebuginfo) (ei, &mdi))
564     {
565       int ret_mdi = elf_w (find_symbol_info_in_image) (ei, load_offset, pc, buf_sz, buf, sym_start, sym_end);
566       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
567         {
568           ret = ret_mdi;
569         }
570 
571       munmap (mdi.image, mdi.size);
572     }
573 
574   return ret;
575 }