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
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH 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
32#ifdef HAVE_LZMA
33#include <lzma.h>
34#endif /* HAVE_LZMA */
35
36static Elf_W (Shdr)*
37elf_w (section_table) (struct elf_image *ei)
38{
39  Elf_W (Ehdr) *ehdr = ei->image;
40  Elf_W (Off) soff;
41
42  soff = ehdr->e_shoff;
43  if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
44    {
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);
48      return NULL;
49    }
50
51  return (Elf_W (Shdr) *) ((char *) ei->image + soff);
52}
53
54static char*
55elf_w (string_table) (struct elf_image *ei, int section)
56{
57  Elf_W (Ehdr) *ehdr = ei->image;
58  Elf_W (Off) soff, str_soff;
59  Elf_W (Shdr) *str_shdr;
60
61  /* this offset is assumed to be OK */
62  soff = ehdr->e_shoff;
63
64  str_soff = soff + (section * ehdr->e_shentsize);
65  if (str_soff + ehdr->e_shentsize > ei->size)
66    {
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);
70      return NULL;
71    }
72  str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
73
74  if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
75    {
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);
79      return NULL;
80    }
81
82  Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
83  /* End of ANDROID update. */
84  return (char*) ((uintptr_t) ei->image + str_shdr->sh_offset);
85  /* End of ANDROID update. */
86}
87
88static int
89elf_w (lookup_symbol) (unw_addr_space_t as,
90		       unw_word_t ip, struct elf_image *ei,
91		       Elf_W (Addr) load_offset,
92		       char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
93{
94  size_t syment_size;
95  Elf_W (Ehdr) *ehdr = ei->image;
96  Elf_W (Sym) *sym, *symtab, *symtab_end;
97  Elf_W (Shdr) *shdr;
98  Elf_W (Addr) val;
99  int i, ret = -UNW_ENOINFO;
100  char *strtab;
101
102  if (!elf_w (valid_object) (ei))
103    return -UNW_ENOINFO;
104
105  shdr = elf_w (section_table) (ei);
106  if (!shdr)
107    return -UNW_ENOINFO;
108
109  for (i = 0; i < ehdr->e_shnum; ++i)
110    {
111      switch (shdr->sh_type)
112	{
113	case SHT_SYMTAB:
114	case SHT_DYNSYM:
115	  symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
116	  symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
117	  syment_size = shdr->sh_entsize;
118
119	  strtab = elf_w (string_table) (ei, shdr->sh_link);
120	  if (!strtab)
121	    break;
122
123	  Debug (16, "symtab=0x%lx[%d]\n",
124		 (long) shdr->sh_offset, shdr->sh_type);
125
126	  for (sym = symtab;
127	       sym < symtab_end;
128	       sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
129	    {
130	      if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
131		  && sym->st_shndx != SHN_UNDEF)
132		{
133		  if (tdep_get_func_addr (as, sym->st_value, &val) < 0)
134		    continue;
135		  if (sym->st_shndx != SHN_ABS)
136		    val += load_offset;
137		  Debug (16, "0x%016lx info=0x%02x %s\n",
138			 (long) val, sym->st_info, strtab + sym->st_name);
139
140                  /* ANDROID support update */
141                  if ((Elf_W (Addr)) (ip - val) < *min_dist
142                      && (Elf_W (Addr)) (ip - val) < sym->st_size)
143                  /* End of ANDROID update */
144		    {
145		      *min_dist = (Elf_W (Addr)) (ip - val);
146		      strncpy (buf, strtab + sym->st_name, buf_len);
147		      buf[buf_len - 1] = '\0';
148		      ret = (strlen (strtab + sym->st_name) >= buf_len
149			     ? -UNW_ENOMEM : 0);
150		    }
151		}
152	    }
153	  break;
154
155	default:
156	  break;
157	}
158      shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
159    }
160  return ret;
161}
162
163static Elf_W (Addr)
164elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
165			 unsigned long mapoff)
166{
167  Elf_W (Addr) offset = 0;
168  Elf_W (Ehdr) *ehdr;
169  Elf_W (Phdr) *phdr;
170  int i;
171
172  ehdr = ei->image;
173  phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
174
175  for (i = 0; i < ehdr->e_phnum; ++i)
176    if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
177      {
178	offset = segbase - phdr[i].p_vaddr;
179	break;
180      }
181
182  return offset;
183}
184
185#if HAVE_LZMA
186static size_t
187xz_uncompressed_size (uint8_t *compressed, size_t length)
188{
189  uint64_t memlimit = UINT64_MAX;
190  size_t ret = 0, pos = 0;
191  lzma_stream_flags options;
192  lzma_index *index;
193
194  if (length < LZMA_STREAM_HEADER_SIZE)
195    return 0;
196
197  uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
198  if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
199    return 0;
200
201  if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
202    return 0;
203
204  uint8_t *indexdata = footer - options.backward_size;
205  if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
206				&pos, options.backward_size) != LZMA_OK)
207    return 0;
208
209  if (lzma_index_size (index) == options.backward_size)
210    {
211      ret = lzma_index_uncompressed_size (index);
212    }
213
214  lzma_index_end (index, NULL);
215  return ret;
216}
217
218static int
219elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
220{
221  Elf_W (Ehdr) *ehdr = ei->image;
222  Elf_W (Shdr) *shdr;
223  char *strtab;
224  int i;
225  uint8_t *compressed = NULL;
226  uint64_t memlimit = UINT64_MAX; /* no memory limit */
227  size_t compressed_len, uncompressed_len;
228
229  if (!elf_w (valid_object) (ei))
230    return 0;
231
232  shdr = elf_w (section_table) (ei);
233  if (!shdr)
234    return 0;
235
236  strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
237  if (!strtab)
238    return 0;
239
240  for (i = 0; i < ehdr->e_shnum; ++i)
241    {
242      if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
243	{
244	  if (shdr->sh_offset + shdr->sh_size > ei->size)
245	    {
246	      Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
247		     (unsigned long) shdr->sh_offset + shdr->sh_size,
248		     (unsigned long) ei->size);
249	      return 0;
250	    }
251
252	  Debug (16, "found .gnu_debugdata at 0x%lx\n",
253		 (unsigned long) shdr->sh_offset);
254	  compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
255	  compressed_len = shdr->sh_size;
256	  break;
257	}
258
259      shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
260    }
261
262  /* not found */
263  if (!compressed)
264    return 0;
265
266  uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
267  if (uncompressed_len == 0)
268    {
269      Debug (1, "invalid .gnu_debugdata contents\n");
270      return 0;
271    }
272
273  mdi->size = uncompressed_len;
274  mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
275		     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
276
277  if (mdi->image == MAP_FAILED)
278    return 0;
279
280  size_t in_pos = 0, out_pos = 0;
281  lzma_ret lret;
282  lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
283				    compressed, &in_pos, compressed_len,
284				    mdi->image, &out_pos, mdi->size);
285  if (lret != LZMA_OK)
286    {
287      Debug (1, "LZMA decompression failed: %d\n", lret);
288      munmap (mdi->image, mdi->size);
289      return 0;
290    }
291
292  return 1;
293}
294#else
295static int
296elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
297{
298  return 0;
299}
300#endif /* !HAVE_LZMA */
301
302/* Find the ELF image that contains IP and return the "closest"
303   procedure name, if there is one.  With some caching, this could be
304   sped up greatly, but until an application materializes that's
305   sensitive to the performance of this routine, why bother...  */
306
307HIDDEN int
308elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
309		       unsigned long segbase,
310		       unsigned long mapoff,
311		       unw_word_t ip,
312		       char *buf, size_t buf_len, unw_word_t *offp)
313{
314  Elf_W (Addr) load_offset;
315  Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
316  int ret;
317
318  load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
319  ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
320
321  /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
322     there as well and replace the previously found if it is closer. */
323  struct elf_image mdi;
324  if (elf_w (extract_minidebuginfo) (ei, &mdi))
325    {
326      int ret_mdi;
327
328      load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff);
329      ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
330				       buf_len, &min_dist);
331
332      /* Closer symbol was found (possibly truncated). */
333      if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
334	{
335	  ret = ret_mdi;
336	}
337
338      munmap (mdi.image, mdi.size);
339    }
340
341  if (min_dist >= ei->size)
342    return -UNW_ENOINFO;		/* not found */
343  if (offp)
344    *offp = min_dist;
345  return ret;
346}
347
348/* ANDROID support update. */
349HIDDEN int
350elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
351		       char *buf, size_t buf_len, unw_word_t *offp)
352{
353  unsigned long segbase, mapoff;
354  struct elf_image ei;
355  int ret;
356
357  ret = tdep_get_elf_image(as, &ei, pid, ip, &segbase, &mapoff, NULL);
358  if (ret < 0)
359    return ret;
360
361  return elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
362}
363/* ANDROID support update. */
364