Home | History | Annotate | Download | only in src
      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 (at) 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   /* End of ANDROID update. */
     84   return (char*) ((uintptr_t) ei->image + str_shdr->sh_offset);
     85   /* End of ANDROID update. */
     86 }
     87 
     88 static int
     89 elf_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 
    163 static Elf_W (Addr)
    164 elf_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
    186 static size_t
    187 xz_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 
    218 static int
    219 elf_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
    295 static int
    296 elf_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 
    307 HIDDEN int
    308 elf_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. */
    349 HIDDEN int
    350 elf_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