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 }