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>
6 This file is part of libunwind.
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:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
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. */
27 #include "libunwind_i.h"
30 #include <sys/param.h>
34 #endif /* HAVE_LZMA */
37 elf_w (section_table) (struct elf_image *ei)
39 Elf_W (Ehdr) *ehdr = ei->image;
43 if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
45 Debug (1, "section table outside of image? (%lu > %lu)\n",
46 (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
47 (unsigned long) ei->size);
51 return (Elf_W (Shdr) *) ((char *) ei->image + soff);
55 elf_w (string_table) (struct elf_image *ei, int section)
57 Elf_W (Ehdr) *ehdr = ei->image;
58 Elf_W (Off) soff, str_soff;
59 Elf_W (Shdr) *str_shdr;
61 /* this offset is assumed to be OK */
64 str_soff = soff + (section * ehdr->e_shentsize);
65 if (str_soff + ehdr->e_shentsize > ei->size)
67 Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
68 (unsigned long) (str_soff + ehdr->e_shentsize),
69 (unsigned long) ei->size);
72 str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
74 if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
76 Debug (1, "string table outside of image? (%lu > %lu)\n",
77 (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
78 (unsigned long) ei->size);
82 Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
83 return ei->image + str_shdr->sh_offset;
87 elf_w (lookup_symbol) (unw_addr_space_t as,
88 unw_word_t ip, struct elf_image *ei,
89 Elf_W (Addr) load_offset,
90 char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
93 Elf_W (Ehdr) *ehdr = ei->image;
94 Elf_W (Sym) *sym, *symtab, *symtab_end;
97 int i, ret = -UNW_ENOINFO;
100 if (!elf_w (valid_object) (ei))
103 shdr = elf_w (section_table) (ei);
107 for (i = 0; i < ehdr->e_shnum; ++i)
109 switch (shdr->sh_type)
113 symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
114 symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
115 syment_size = shdr->sh_entsize;
117 strtab = elf_w (string_table) (ei, shdr->sh_link);
121 Debug (16, "symtab=0x%lx[%d]\n",
122 (long) shdr->sh_offset, shdr->sh_type);
126 sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
128 if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
129 && sym->st_shndx != SHN_UNDEF)
132 if (sym->st_shndx != SHN_ABS)
134 if (tdep_get_func_addr (as, val, &val) < 0)
136 Debug (16, "0x%016lx info=0x%02x %s\n",
137 (long) val, sym->st_info, strtab + sym->st_name);
139 if ((Elf_W (Addr)) (ip - val) < *min_dist)
141 *min_dist = (Elf_W (Addr)) (ip - val);
142 strncpy (buf, strtab + sym->st_name, buf_len);
143 buf[buf_len - 1] = '\0';
144 ret = (strlen (strtab + sym->st_name) >= buf_len
154 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
160 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
161 unsigned long mapoff)
163 Elf_W (Addr) offset = 0;
169 phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
171 for (i = 0; i < ehdr->e_phnum; ++i)
172 if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
174 offset = segbase - phdr[i].p_vaddr;
183 xz_uncompressed_size (uint8_t *compressed, size_t length)
185 uint64_t memlimit = UINT64_MAX;
186 size_t ret = 0, pos = 0;
187 lzma_stream_flags options;
190 if (length < LZMA_STREAM_HEADER_SIZE)
193 uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
194 if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
197 if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
200 uint8_t *indexdata = footer - options.backward_size;
201 if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
202 &pos, options.backward_size) != LZMA_OK)
205 if (lzma_index_size (index) == options.backward_size)
207 ret = lzma_index_uncompressed_size (index);
210 lzma_index_end (index, NULL);
215 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
218 uint8_t *compressed = NULL;
219 uint64_t memlimit = UINT64_MAX; /* no memory limit */
220 size_t compressed_len, uncompressed_len;
222 shdr = elf_w (find_section) (ei, ".gnu_debugdata");
226 compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
227 compressed_len = shdr->sh_size;
229 uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
230 if (uncompressed_len == 0)
232 Debug (1, "invalid .gnu_debugdata contents\n");
236 mdi->size = uncompressed_len;
237 mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
238 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
240 if (mdi->image == MAP_FAILED)
243 size_t in_pos = 0, out_pos = 0;
245 lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
246 compressed, &in_pos, compressed_len,
247 mdi->image, &out_pos, mdi->size);
250 Debug (1, "LZMA decompression failed: %d\n", lret);
251 munmap (mdi->image, mdi->size);
259 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
263 #endif /* !HAVE_LZMA */
265 /* Find the ELF image that contains IP and return the "closest"
266 procedure name, if there is one. With some caching, this could be
267 sped up greatly, but until an application materializes that's
268 sensitive to the performance of this routine, why bother... */
271 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
272 unsigned long segbase,
273 unsigned long mapoff,
275 char *buf, size_t buf_len, unw_word_t *offp)
277 Elf_W (Addr) load_offset;
278 Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
281 load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
282 ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
284 /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
285 there as well and replace the previously found if it is closer. */
286 struct elf_image mdi;
287 if (elf_w (extract_minidebuginfo) (ei, &mdi))
289 int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
292 /* Closer symbol was found (possibly truncated). */
293 if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
298 munmap (mdi.image, mdi.size);
301 if (min_dist >= ei->size)
302 return -UNW_ENOINFO; /* not found */
309 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
310 char *buf, size_t buf_len, unw_word_t *offp)
312 unsigned long segbase, mapoff;
317 ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX);
321 ret = elf_w (load_debuglink) (file, &ei, 1);
325 ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
327 munmap (ei.image, ei.size);
334 elf_w (find_section) (struct elf_image *ei, const char* secname)
336 Elf_W (Ehdr) *ehdr = ei->image;
341 if (!elf_w (valid_object) (ei))
344 shdr = elf_w (section_table) (ei);
348 strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
352 for (i = 0; i < ehdr->e_shnum; ++i)
354 if (strcmp (strtab + shdr->sh_name, secname) == 0)
356 if (shdr->sh_offset + shdr->sh_size > ei->size)
358 Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
360 (unsigned long) shdr->sh_offset + shdr->sh_size,
361 (unsigned long) ei->size);
365 Debug (16, "found section \"%s\" at 0x%lx\n",
366 secname, (unsigned long) shdr->sh_offset);
370 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
373 /* section not found */
377 /* Load a debug section, following .gnu_debuglink if appropriate
378 * Loads ei from file if not already mapped.
379 * If is_local, will also search sys directories /usr/local/dbg
381 * Returns 0 on success, failure otherwise.
382 * ei will be mapped to file or the located .gnu_debuglink from file
385 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
389 Elf_W (Ehdr) *prev_image;
394 ret = elf_map_image(ei, file);
399 prev_image = ei->image;
400 prev_size = ei->size;
402 /* Ignore separate debug files which contain a .gnu_debuglink section. */
403 if (is_local == -1) {
407 shdr = elf_w (find_section) (ei, ".gnu_debuglink");
409 if (shdr->sh_size >= PATH_MAX ||
410 (shdr->sh_offset + shdr->sh_size > ei->size))
416 char linkbuf[shdr->sh_size];
417 char *link = ((char *) ei->image) + shdr->sh_offset;
419 static const char *debugdir = "/usr/lib/debug";
420 char basedir[strlen(file) + 1];
421 char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
423 memcpy(linkbuf, link, shdr->sh_size);
425 if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
430 Debug(1, "Found debuglink section, following %s\n", linkbuf);
432 p = strrchr (file, '/');
435 memcpy (basedir, file, p - file);
436 basedir[p - file] = '\0';
441 strcpy (newname, basedir);
442 strcat (newname, "/");
443 strcat (newname, linkbuf);
444 ret = elf_w (load_debuglink) (newname, ei, -1);
448 strcpy (newname, basedir);
449 strcat (newname, "/.debug/");
450 strcat (newname, linkbuf);
451 ret = elf_w (load_debuglink) (newname, ei, -1);
454 if (ret == -1 && is_local == 1)
456 strcpy (newname, debugdir);
457 strcat (newname, basedir);
458 strcat (newname, "/");
459 strcat (newname, linkbuf);
460 ret = elf_w (load_debuglink) (newname, ei, -1);
465 /* No debuglink file found even though .gnu_debuglink existed */
466 ei->image = prev_image;
467 ei->size = prev_size;
473 munmap (prev_image, prev_size);