• 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 Elf_W (Addr)
elf_w(get_load_offset)161 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
162                          unsigned long mapoff)
163 {
164   Elf_W (Addr) offset = 0;
165   Elf_W (Ehdr) *ehdr;
166   Elf_W (Phdr) *phdr;
167   int i;
168   // mapoff is obtained from mmap informations, so is always aligned on a page size.
169   // PT_LOAD program headers p_offset however is not guaranteed to be aligned on a
170   // page size, ld.lld generate libraries where this is not the case. So we must
171   // make sure we compare both values with the same alignment.
172   unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL);
173 
174   ehdr = ei->image;
175   phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
176 
177   for (i = 0; i < ehdr->e_phnum; ++i)
178     if (phdr[i].p_type == PT_LOAD && (phdr[i].p_offset & pagesize_alignment_mask) == mapoff)
179       {
180         offset = segbase - phdr[i].p_vaddr + (phdr[i].p_offset & (~pagesize_alignment_mask));
181         break;
182       }
183 
184   return offset;
185 }
186 
187 #if HAVE_LZMA
188 static size_t
xz_uncompressed_size(uint8_t * compressed,size_t length)189 xz_uncompressed_size (uint8_t *compressed, size_t length)
190 {
191   uint64_t memlimit = UINT64_MAX;
192   size_t ret = 0, pos = 0;
193   lzma_stream_flags options;
194   lzma_index *index;
195 
196   if (length < LZMA_STREAM_HEADER_SIZE)
197     return 0;
198 
199   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
200   if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
201     return 0;
202 
203   if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
204     return 0;
205 
206   uint8_t *indexdata = footer - options.backward_size;
207   if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
208                                 &pos, options.backward_size) != LZMA_OK)
209     return 0;
210 
211   if (lzma_index_size (index) == options.backward_size)
212     {
213       ret = lzma_index_uncompressed_size (index);
214     }
215 
216   lzma_index_end (index, NULL);
217   return ret;
218 }
219 
220 static int
elf_w(extract_minidebuginfo)221 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
222 {
223   Elf_W (Shdr) *shdr;
224   uint8_t *compressed = NULL;
225   uint64_t memlimit = UINT64_MAX; /* no memory limit */
226   size_t compressed_len, uncompressed_len;
227 
228   shdr = elf_w (find_section) (ei, ".gnu_debugdata");
229   if (!shdr)
230     return 0;
231 
232   compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
233   compressed_len = shdr->sh_size;
234 
235   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
236   if (uncompressed_len == 0)
237     {
238       Debug (1, "invalid .gnu_debugdata contents\n");
239       return 0;
240     }
241 
242   mdi->size = uncompressed_len;
243   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
244                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
245 
246   if (mdi->image == MAP_FAILED)
247     return 0;
248 
249   size_t in_pos = 0, out_pos = 0;
250   lzma_ret lret;
251   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
252                                     compressed, &in_pos, compressed_len,
253                                     mdi->image, &out_pos, mdi->size);
254   if (lret != LZMA_OK)
255     {
256       Debug (1, "LZMA decompression failed: %d\n", lret);
257       munmap (mdi->image, mdi->size);
258       return 0;
259     }
260 
261   return 1;
262 }
263 #else
264 static int
elf_w(extract_minidebuginfo)265 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
266 {
267   return 0;
268 }
269 #endif /* !HAVE_LZMA */
270 
271 /* Find the ELF image that contains IP and return the "closest"
272    procedure name, if there is one.  With some caching, this could be
273    sped up greatly, but until an application materializes that's
274    sensitive to the performance of this routine, why bother...  */
275 
276 HIDDEN int
elf_w(get_proc_name_in_image)277 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
278                        unsigned long segbase,
279                        unsigned long mapoff,
280                        unw_word_t ip,
281                        char *buf, size_t buf_len, unw_word_t *offp)
282 {
283   Elf_W (Addr) load_offset;
284   Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
285   int ret;
286 
287   load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
288   ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
289 
290   /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
291      there as well and replace the previously found if it is closer. */
292   struct elf_image mdi;
293   if (elf_w (extract_minidebuginfo) (ei, &mdi))
294     {
295       int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
296                                            buf_len, &min_dist);
297 
298       /* Closer symbol was found (possibly truncated). */
299       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
300         {
301           ret = ret_mdi;
302         }
303 
304       munmap (mdi.image, mdi.size);
305     }
306 
307   if (min_dist >= ei->size)
308     return -UNW_ENOINFO;                /* not found */
309   if (offp)
310     *offp = min_dist;
311   return ret;
312 }
313 
314 HIDDEN int
elf_w(get_proc_name)315 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
316                        char *buf, size_t buf_len, unw_word_t *offp)
317 {
318   unsigned long segbase, mapoff;
319   struct elf_image ei;
320   int ret;
321   char file[PATH_MAX];
322 
323   ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX);
324   if (ret < 0)
325     return ret;
326 
327   ret = elf_w (load_debuglink) (file, &ei, 1);
328   if (ret < 0)
329     return ret;
330 
331   ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
332 
333   munmap (ei.image, ei.size);
334   ei.image = NULL;
335 
336   return ret;
337 }
338 
Elf_W(Shdr)339 HIDDEN Elf_W (Shdr)*
340 elf_w (find_section) (struct elf_image *ei, const char* secname)
341 {
342   Elf_W (Ehdr) *ehdr = ei->image;
343   Elf_W (Shdr) *shdr;
344   char *strtab;
345   int i;
346 
347   if (!elf_w (valid_object) (ei))
348     return 0;
349 
350   shdr = elf_w (section_table) (ei);
351   if (!shdr)
352     return 0;
353 
354   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
355   if (!strtab)
356     return 0;
357 
358   for (i = 0; i < ehdr->e_shnum; ++i)
359     {
360       if (strcmp (strtab + shdr->sh_name, secname) == 0)
361         {
362           if (shdr->sh_offset + shdr->sh_size > ei->size)
363             {
364               Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
365                      secname,
366                      (unsigned long) shdr->sh_offset + shdr->sh_size,
367                      (unsigned long) ei->size);
368               return 0;
369             }
370 
371           Debug (16, "found section \"%s\" at 0x%lx\n",
372                  secname, (unsigned long) shdr->sh_offset);
373           return shdr;
374         }
375 
376       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
377     }
378 
379   /* section not found */
380   return 0;
381 }
382 
383 /* Load a debug section, following .gnu_debuglink if appropriate
384  * Loads ei from file if not already mapped.
385  * If is_local, will also search sys directories /usr/local/dbg
386  *
387  * Returns 0 on success, failure otherwise.
388  * ei will be mapped to file or the located .gnu_debuglink from file
389  */
390 HIDDEN int
elf_w(load_debuglink)391 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
392 {
393   int ret;
394   Elf_W (Shdr) *shdr;
395   Elf_W (Ehdr) *prev_image;
396   off_t prev_size;
397 
398   if (!ei->image)
399     {
400       ret = elf_map_image(ei, file);
401       if (ret)
402 	return ret;
403     }
404 
405   prev_image = ei->image;
406   prev_size = ei->size;
407 
408   /* Ignore separate debug files which contain a .gnu_debuglink section. */
409   if (is_local == -1) {
410     return 0;
411   }
412 
413   shdr = elf_w (find_section) (ei, ".gnu_debuglink");
414   if (shdr) {
415     if (shdr->sh_size >= PATH_MAX ||
416 	(shdr->sh_offset + shdr->sh_size > ei->size))
417       {
418 	return 0;
419       }
420 
421     {
422       char linkbuf[shdr->sh_size];
423       char *link = ((char *) ei->image) + shdr->sh_offset;
424       char *p;
425       static const char *debugdir = "/usr/lib/debug";
426       char basedir[strlen(file) + 1];
427       char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
428 
429       memcpy(linkbuf, link, shdr->sh_size);
430 
431       if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
432 	return 0;
433 
434       ei->image = NULL;
435 
436       Debug(1, "Found debuglink section, following %s\n", linkbuf);
437 
438       p = strrchr (file, '/');
439       if (p != NULL)
440 	{
441 	  memcpy (basedir, file, p - file);
442 	  basedir[p - file] = '\0';
443 	}
444       else
445 	basedir[0] = 0;
446 
447       strcpy (newname, basedir);
448       strcat (newname, "/");
449       strcat (newname, linkbuf);
450       ret = elf_w (load_debuglink) (newname, ei, -1);
451 
452       if (ret == -1)
453 	{
454 	  strcpy (newname, basedir);
455 	  strcat (newname, "/.debug/");
456 	  strcat (newname, linkbuf);
457 	  ret = elf_w (load_debuglink) (newname, ei, -1);
458 	}
459 
460       if (ret == -1 && is_local == 1)
461 	{
462 	  strcpy (newname, debugdir);
463 	  strcat (newname, basedir);
464 	  strcat (newname, "/");
465 	  strcat (newname, linkbuf);
466 	  ret = elf_w (load_debuglink) (newname, ei, -1);
467 	}
468 
469       if (ret == -1)
470         {
471           /* No debuglink file found even though .gnu_debuglink existed */
472           ei->image = prev_image;
473           ei->size = prev_size;
474 
475           return 0;
476         }
477       else
478         {
479           munmap (prev_image, prev_size);
480         }
481 
482       return ret;
483     }
484   }
485 
486   return 0;
487 }
488