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)
131 if (tdep_get_func_addr (as, sym->st_value, &val) < 0)
133 if (sym->st_shndx != SHN_ABS)
135 Debug (16, "0x%016lx info=0x%02x %s\n",
136 (long) val, sym->st_info, strtab + sym->st_name);
138 if ((Elf_W (Addr)) (ip - val) < *min_dist)
140 *min_dist = (Elf_W (Addr)) (ip - val);
141 strncpy (buf, strtab + sym->st_name, buf_len);
142 buf[buf_len - 1] = '\0';
143 ret = (strlen (strtab + sym->st_name) >= buf_len
153 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
159 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
160 unsigned long mapoff)
162 Elf_W (Addr) offset = 0;
168 phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
170 for (i = 0; i < ehdr->e_phnum; ++i)
171 if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
173 offset = segbase - phdr[i].p_vaddr;
182 xz_uncompressed_size (uint8_t *compressed, size_t length)
184 uint64_t memlimit = UINT64_MAX;
185 size_t ret = 0, pos = 0;
186 lzma_stream_flags options;
189 if (length < LZMA_STREAM_HEADER_SIZE)
192 uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
193 if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
196 if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
199 uint8_t *indexdata = footer - options.backward_size;
200 if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
201 &pos, options.backward_size) != LZMA_OK)
204 if (lzma_index_size (index) == options.backward_size)
206 ret = lzma_index_uncompressed_size (index);
209 lzma_index_end (index, NULL);
214 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
216 Elf_W (Ehdr) *ehdr = ei->image;
220 uint8_t *compressed = NULL;
221 uint64_t memlimit = UINT64_MAX; /* no memory limit */
222 size_t compressed_len, uncompressed_len;
224 if (!elf_w (valid_object) (ei))
227 shdr = elf_w (section_table) (ei);
231 strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
235 for (i = 0; i < ehdr->e_shnum; ++i)
237 if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
239 if (shdr->sh_offset + shdr->sh_size > ei->size)
241 Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
242 (unsigned long) shdr->sh_offset + shdr->sh_size,
243 (unsigned long) ei->size);
247 Debug (16, "found .gnu_debugdata at 0x%lx\n",
248 (unsigned long) shdr->sh_offset);
249 compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
250 compressed_len = shdr->sh_size;
254 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
261 uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
262 if (uncompressed_len == 0)
264 Debug (1, "invalid .gnu_debugdata contents\n");
268 mdi->size = uncompressed_len;
269 mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
270 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
272 if (mdi->image == MAP_FAILED)
275 size_t in_pos = 0, out_pos = 0;
277 lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
278 compressed, &in_pos, compressed_len,
279 mdi->image, &out_pos, mdi->size);
282 Debug (1, "LZMA decompression failed: %d\n", lret);
283 munmap (mdi->image, mdi->size);
291 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
295 #endif /* !HAVE_LZMA */
297 /* Find the ELF image that contains IP and return the "closest"
298 procedure name, if there is one. With some caching, this could be
299 sped up greatly, but until an application materializes that's
300 sensitive to the performance of this routine, why bother... */
303 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
304 unsigned long segbase,
305 unsigned long mapoff,
307 char *buf, size_t buf_len, unw_word_t *offp)
309 Elf_W (Addr) load_offset;
310 Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
313 load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
314 ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
316 /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
317 there as well and replace the previously found if it is closer. */
318 struct elf_image mdi;
319 if (elf_w (extract_minidebuginfo) (ei, &mdi))
323 load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff);
324 ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
327 /* Closer symbol was found (possibly truncated). */
328 if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
333 munmap (mdi.image, mdi.size);
336 if (min_dist >= ei->size)
337 return -UNW_ENOINFO; /* not found */
344 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
345 char *buf, size_t buf_len, unw_word_t *offp)
347 unsigned long segbase, mapoff;
351 ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
355 ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
357 munmap (ei.image, ei.size);