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