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 }