3d87331716fa04df9b55cada749c84a2bb30796e
[platform/upstream/libunwind.git] / src / elfxx.c
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
32 #ifdef HAVE_LZMA
33 #include <lzma.h>
34 #endif /* HAVE_LZMA */
35
36 static Elf_W (Shdr)*
37 elf_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
54 static char*
55 elf_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   return ei->image + str_shdr->sh_offset;
84 }
85
86 static int
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)
91 {
92   size_t syment_size;
93   Elf_W (Ehdr) *ehdr = ei->image;
94   Elf_W (Sym) *sym, *symtab, *symtab_end;
95   Elf_W (Shdr) *shdr;
96   Elf_W (Addr) val;
97   int i, ret = -UNW_ENOINFO;
98   char *strtab;
99
100   if (!elf_w (valid_object) (ei))
101     return -UNW_ENOINFO;
102
103   shdr = elf_w (section_table) (ei);
104   if (!shdr)
105     return -UNW_ENOINFO;
106
107   for (i = 0; i < ehdr->e_shnum; ++i)
108     {
109       switch (shdr->sh_type)
110         {
111         case SHT_SYMTAB:
112         case SHT_DYNSYM:
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;
116
117           strtab = elf_w (string_table) (ei, shdr->sh_link);
118           if (!strtab)
119             break;
120
121           Debug (16, "symtab=0x%lx[%d]\n",
122                  (long) shdr->sh_offset, shdr->sh_type);
123
124           for (sym = symtab;
125                sym < symtab_end;
126                sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
127             {
128               if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
129                   && sym->st_shndx != SHN_UNDEF)
130                 {
131                   if (tdep_get_func_addr (as, sym->st_value, &val) < 0)
132                     continue;
133                   if (sym->st_shndx != SHN_ABS)
134                     val += load_offset;
135                   Debug (16, "0x%016lx info=0x%02x %s\n",
136                          (long) val, sym->st_info, strtab + sym->st_name);
137
138                   if ((Elf_W (Addr)) (ip - val) < *min_dist)
139                     {
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
144                              ? -UNW_ENOMEM : 0);
145                     }
146                 }
147             }
148           break;
149
150         default:
151           break;
152         }
153       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
154     }
155   return ret;
156 }
157
158 static Elf_W (Addr)
159 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
160                          unsigned long mapoff)
161 {
162   Elf_W (Addr) offset = 0;
163   Elf_W (Ehdr) *ehdr;
164   Elf_W (Phdr) *phdr;
165   int i;
166
167   ehdr = ei->image;
168   phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
169
170   for (i = 0; i < ehdr->e_phnum; ++i)
171     if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
172       {
173         offset = segbase - phdr[i].p_vaddr;
174         break;
175       }
176
177   return offset;
178 }
179
180 #if HAVE_LZMA
181 static size_t
182 xz_uncompressed_size (uint8_t *compressed, size_t length)
183 {
184   uint64_t memlimit = UINT64_MAX;
185   size_t ret = 0, pos = 0;
186   lzma_stream_flags options;
187   lzma_index *index;
188
189   if (length < LZMA_STREAM_HEADER_SIZE)
190     return 0;
191
192   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
193   if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
194     return 0;
195
196   if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
197     return 0;
198
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)
202     return 0;
203
204   if (lzma_index_size (index) == options.backward_size)
205     {
206       ret = lzma_index_uncompressed_size (index);
207     }
208
209   lzma_index_end (index, NULL);
210   return ret;
211 }
212
213 static int
214 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
215 {
216   Elf_W (Ehdr) *ehdr = ei->image;
217   Elf_W (Shdr) *shdr;
218   char *strtab;
219   int i;
220   uint8_t *compressed = NULL;
221   uint64_t memlimit = UINT64_MAX; /* no memory limit */
222   size_t compressed_len, uncompressed_len;
223
224   if (!elf_w (valid_object) (ei))
225     return 0;
226
227   shdr = elf_w (section_table) (ei);
228   if (!shdr)
229     return 0;
230
231   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
232   if (!strtab)
233     return 0;
234
235   for (i = 0; i < ehdr->e_shnum; ++i)
236     {
237       if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
238         {
239           if (shdr->sh_offset + shdr->sh_size > ei->size)
240             {
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);
244               return 0;
245             }
246
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;
251           break;
252         }
253
254       shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
255     }
256
257   /* not found */
258   if (!compressed)
259     return 0;
260
261   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
262   if (uncompressed_len == 0)
263     {
264       Debug (1, "invalid .gnu_debugdata contents\n");
265       return 0;
266     }
267
268   mdi->size = uncompressed_len;
269   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
270                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
271
272   if (mdi->image == MAP_FAILED)
273     return 0;
274
275   size_t in_pos = 0, out_pos = 0;
276   lzma_ret lret;
277   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
278                                     compressed, &in_pos, compressed_len,
279                                     mdi->image, &out_pos, mdi->size);
280   if (lret != LZMA_OK)
281     {
282       Debug (1, "LZMA decompression failed: %d\n", lret);
283       munmap (mdi->image, mdi->size);
284       return 0;
285     }
286
287   return 1;
288 }
289 #else
290 static int
291 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
292 {
293   return 0;
294 }
295 #endif /* !HAVE_LZMA */
296
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...  */
301
302 HIDDEN int
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,
306                        unw_word_t ip,
307                        char *buf, size_t buf_len, unw_word_t *offp)
308 {
309   Elf_W (Addr) load_offset;
310   Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
311   int ret;
312
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);
315
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))
320     {
321       int ret_mdi;
322
323       load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff);
324       ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
325                                        buf_len, &min_dist);
326
327       /* Closer symbol was found (possibly truncated). */
328       if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
329         {
330           ret = ret_mdi;
331         }
332
333       munmap (mdi.image, mdi.size);
334     }
335
336   if (min_dist >= ei->size)
337     return -UNW_ENOINFO;                /* not found */
338   if (offp)
339     *offp = min_dist;
340   return ret;
341 }
342
343 HIDDEN int
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)
346 {
347   unsigned long segbase, mapoff;
348   struct elf_image ei;
349   int ret;
350
351   ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
352   if (ret < 0)
353     return ret;
354
355   ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
356
357   munmap (ei.image, ei.size);
358   ei.image = NULL;
359
360   return ret;
361 }