Home | History | Annotate | Download | only in dwarf
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2003-2004 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5 This file is part of libunwind.
      6 
      7 Permission is hereby granted, free of charge, to any person obtaining
      8 a copy of this software and associated documentation files (the
      9 "Software"), to deal in the Software without restriction, including
     10 without limitation the rights to use, copy, modify, merge, publish,
     11 distribute, sublicense, and/or sell copies of the Software, and to
     12 permit persons to whom the Software is furnished to do so, subject to
     13 the following conditions:
     14 
     15 The above copyright notice and this permission notice shall be
     16 included in all copies or substantial portions of the Software.
     17 
     18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     25 
     26 #include <fcntl.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 
     30 #include <sys/mman.h>
     31 
     32 #include "libunwind_i.h"
     33 #include "dwarf-eh.h"
     34 #include "dwarf_i.h"
     35 
     36 static bool get_dyn_gp(struct elf_image* ei, Elf_W(Off) dyn_phdr_offset, unw_word_t* gp) {
     37   Elf_W(Phdr) phdr;
     38   GET_PHDR_FIELD(ei, dyn_phdr_offset, &phdr, p_offset);
     39   Elf_W(Dyn) dyn;
     40   Elf_W(Off) dyn_offset = phdr.p_offset;
     41   unw_word_t map_size = ei->u.memory.end - ei->u.memory.start;
     42   while (dyn_offset + sizeof(dyn) < map_size) {
     43     GET_DYN_FIELD(ei, dyn_offset, &dyn, d_tag);
     44     if (dyn.d_tag == DT_NULL) {
     45       break;
     46     }
     47     if (dyn.d_tag == DT_PLTGOT) {
     48       // Assume that _DYNAMIC is writable and GLIBC has
     49       // relocated it (true for x86 at least).
     50       GET_DYN_FIELD(ei, dyn_offset, &dyn, d_un.d_ptr);
     51       *gp = dyn.d_un.d_ptr;
     52       return true;
     53     }
     54     dyn_offset += sizeof(dyn);
     55   }
     56   Debug(1, "DT_PLTGOT not found in dynamic header\n");
     57   return false;
     58 }
     59 
     60 static bool get_eh_frame_info(
     61     struct elf_image* ei, unw_word_t phdr_offset, unw_word_t load_base, unw_dyn_info_t* di_cache) {
     62   Elf_W(Phdr) phdr;
     63   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
     64   unw_word_t hdr_offset = phdr.p_offset;
     65   struct dwarf_eh_frame_hdr hdr;
     66   // Read the entire hdr since we are going to use every value in the struct.
     67   if (sizeof(hdr) != elf_w (memory_read) (ei, ei->u.memory.start + phdr.p_offset,
     68                             (uint8_t*) &hdr, sizeof(hdr), false)) {
     69     Debug(1, "Failed to read dwarf_eh_frame_hdr from in memory elf image.\n");
     70     return false;
     71   }
     72 
     73   if (hdr.version != DW_EH_VERSION) {
     74     Debug (1, "table has unexpected version %d\n", hdr.version);
     75     return false;
     76   }
     77 
     78   // Fill in a dummy proc_info structure.  We just need to fill in
     79   // enough to ensure that dwarf_read_encoded_pointer() can do its
     80   // job.  Since we don't have a procedure-context at this point, all
     81   // we have to do is fill in the global-pointer.
     82   unw_proc_info_t pi;
     83   memset (&pi, 0, sizeof (pi));
     84   pi.gp = di_cache->gp;
     85 
     86   unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
     87   unw_word_t addr = (unw_word_t) (uintptr_t) (hdr_offset + sizeof(struct dwarf_eh_frame_hdr));
     88   addr += ei->u.memory.start;
     89 
     90   unw_word_t eh_frame_start;
     91   if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.eh_frame_ptr_enc, &pi,
     92                                   &eh_frame_start, ei->u.memory.as_arg) < 0) {
     93     Debug(1, "Failed to read encoded frame start.\n");
     94     return false;
     95   }
     96 
     97   unw_word_t fde_count;
     98   if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.fde_count_enc, &pi,
     99                                   &fde_count, ei->u.memory.as_arg) < 0) {
    100     Debug(1, "Failed to read fde count.\n");
    101     return false;
    102   }
    103 
    104   if (hdr.table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
    105     // Unsupported table format.
    106     Debug(1, "Unsupported header table format %d\n", hdr.table_enc);
    107     return false;
    108   }
    109 
    110   di_cache->u.rti.name_ptr = 0;
    111   // two 32-bit values (ip_offset/fde_offset) per table-entry:
    112   di_cache->u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
    113 
    114   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_vaddr);
    115   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
    116   di_cache->u.rti.table_data =
    117       load_base + phdr.p_vaddr + addr - (uintptr_t) ei->u.memory.start - phdr.p_offset;
    118 
    119   // For the binary-search table in the eh_frame_hdr, data-relative
    120   // means relative to the start of that section...
    121   di_cache->u.rti.segbase = ((load_base + phdr.p_vaddr) + (hdr_offset - phdr.p_offset));
    122 
    123   return true;
    124 }
    125 
    126 static bool dwarf_find_unwind_table_memory (
    127     struct elf_dyn_info *edi, struct elf_image *ei, unw_addr_space_t as, char *path,
    128     unw_word_t segbase, unw_word_t mapoff, unw_word_t ip) {
    129   Elf_W(Ehdr) ehdr;
    130   GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
    131   GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
    132 
    133   Elf_W(Off) offset = ehdr.e_phoff;
    134   Elf_W(Off) txt_phdr_offset = 0;
    135   Elf_W(Addr) txt_pvaddr = 0;
    136   Elf_W(Off) dyn_phdr_offset = 0;
    137 #if UNW_TARGET_ARM
    138   Elf_W(Off) arm_exidx_phdr_offset = 0;
    139 #endif
    140   int i;
    141   unw_word_t start_ip = (unw_word_t) -1;
    142   unw_word_t end_ip = 0;
    143   Elf_W(Off) eh_frame_phdr_offset = 0;
    144   for (i = 0; i < ehdr.e_phnum; ++i) {
    145     Elf_W(Phdr) phdr;
    146     GET_PHDR_FIELD(ei, offset, &phdr, p_type);
    147     switch (phdr.p_type) {
    148       case PT_LOAD:
    149         GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
    150         if (phdr.p_vaddr < start_ip) {
    151           start_ip = phdr.p_vaddr;
    152         }
    153 
    154         GET_PHDR_FIELD(ei, offset, &phdr, p_memsz);
    155         if (phdr.p_vaddr + phdr.p_memsz > end_ip) {
    156           end_ip = phdr.p_vaddr + phdr.p_memsz;
    157         }
    158 
    159         GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
    160         if (phdr.p_offset == mapoff) {
    161           txt_phdr_offset = offset;
    162           txt_pvaddr = phdr.p_vaddr;
    163         }
    164         break;
    165 
    166       case PT_GNU_EH_FRAME:
    167         eh_frame_phdr_offset = offset;
    168         break;
    169 
    170       case PT_DYNAMIC:
    171         dyn_phdr_offset = offset;
    172         break;
    173 
    174 #if UNW_TARGET_ARM
    175       case PT_ARM_EXIDX:
    176         arm_exidx_phdr_offset = offset;
    177         break;
    178 #endif
    179 
    180       default:
    181         break;
    182     }
    183     offset += sizeof(phdr);
    184   }
    185 
    186   if (txt_phdr_offset == 0) {
    187     Debug(1, "PT_LOAD section not found.\n");
    188     return false;
    189   }
    190 
    191   unw_word_t load_base = segbase - txt_pvaddr;
    192   start_ip += load_base;
    193   end_ip += load_base;
    194 
    195   bool found = false;
    196   if (eh_frame_phdr_offset) {
    197     // For dynamicly linked executables and shared libraries,
    198     // DT_PLTGOT is the value that data-relative addresses are
    199     // relative to for that object.  We call this the "gp".
    200     // Otherwise this is a static executable with no _DYNAMIC.  Assume
    201     // that data-relative addresses are relative to 0, i.e.,
    202     // absolute.
    203     edi->di_cache.gp = 0;
    204     if (dyn_phdr_offset) {
    205       // Ignore failures, we'll attempt to keep going with a zero gp.
    206       get_dyn_gp(ei, dyn_phdr_offset, &edi->di_cache.gp);
    207     }
    208 
    209     found = get_eh_frame_info(ei, eh_frame_phdr_offset, load_base, &edi->di_cache);
    210     if (found) {
    211       edi->di_cache.start_ip = start_ip;
    212       edi->di_cache.end_ip = end_ip;
    213       edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
    214     }
    215   }
    216 
    217 #if UNW_TARGET_ARM
    218   // Verify that the map contains enough space for the arm unwind data.
    219   if (arm_exidx_phdr_offset &&
    220     arm_exidx_phdr_offset + sizeof(Elf_W(Phdr)) < ei->u.memory.end - ei->u.memory.start) {
    221     Elf_W(Phdr) phdr;
    222     GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_vaddr);
    223     GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_memsz);
    224     edi->di_arm.u.rti.table_data = load_base + phdr.p_vaddr;
    225     edi->di_arm.u.rti.table_len = phdr.p_memsz;
    226 
    227     edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
    228     edi->di_arm.start_ip = start_ip;
    229     edi->di_arm.end_ip = end_ip;
    230     edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
    231     found = true;
    232   }
    233 #endif
    234 
    235   return found;
    236 }
    237 
    238 int
    239 dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
    240 			 unw_addr_space_t as, char *path,
    241 			 unw_word_t segbase, unw_word_t mapoff, unw_word_t ip)
    242 {
    243   Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
    244   unw_word_t addr, eh_frame_start, fde_count, load_base;
    245 #if 0
    246   // Not currently used.
    247   unw_word_t max_load_addr = 0;
    248 #endif
    249   unw_word_t start_ip = (unw_word_t) -1;
    250   unw_word_t end_ip = 0;
    251   struct dwarf_eh_frame_hdr *hdr;
    252   unw_proc_info_t pi;
    253   unw_accessors_t *a;
    254   Elf_W(Ehdr) *ehdr;
    255 #if UNW_TARGET_ARM
    256   const Elf_W(Phdr) *parm_exidx = NULL;
    257 #endif
    258   int i, ret, found = 0;
    259 
    260   /* XXX: Much of this code is Linux/LSB-specific.  */
    261 
    262   if (!ei->valid)
    263     return -UNW_ENOINFO;
    264 
    265   if (!ei->mapped) {
    266     if (dwarf_find_unwind_table_memory (edi, ei, as, path, segbase, mapoff, ip)) {
    267       return 1;
    268     }
    269     return -UNW_ENOINFO;
    270   }
    271 
    272   /* ANDROID support update. */
    273   ehdr = ei->u.mapped.image;
    274   phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
    275   /* End of ANDROID update. */
    276 
    277   for (i = 0; i < ehdr->e_phnum; ++i)
    278     {
    279       switch (phdr[i].p_type)
    280 	{
    281 	case PT_LOAD:
    282 	  if (phdr[i].p_vaddr < start_ip)
    283 	    start_ip = phdr[i].p_vaddr;
    284 
    285 	  if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
    286 	    end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
    287 
    288 	  if (phdr[i].p_offset == mapoff)
    289 	    ptxt = phdr + i;
    290 
    291 #if 0
    292 	  // Not currently used.
    293 	  if ((uintptr_t) ei->u.mapped.image + phdr->p_filesz > max_load_addr)
    294 	    max_load_addr = (uintptr_t) ei->u.mapped.image + phdr->p_filesz;
    295 #endif
    296 	  break;
    297 
    298 	case PT_GNU_EH_FRAME:
    299 	  peh_hdr = phdr + i;
    300 	  break;
    301 
    302 	case PT_DYNAMIC:
    303 	  pdyn = phdr + i;
    304 	  break;
    305 
    306 #if UNW_TARGET_ARM
    307 	case PT_ARM_EXIDX:
    308 	  parm_exidx = phdr + i;
    309 	  break;
    310 #endif
    311 
    312 	default:
    313 	  break;
    314 	}
    315     }
    316 
    317   if (!ptxt)
    318     return 0;
    319 
    320   load_base = segbase - ptxt->p_vaddr;
    321   start_ip += load_base;
    322   end_ip += load_base;
    323 
    324   if (peh_hdr)
    325     {
    326       // For dynamicly linked executables and shared libraries,
    327       // DT_PLTGOT is the value that data-relative addresses are
    328       // relative to for that object.  We call this the "gp".
    329       // Otherwise this is a static executable with no _DYNAMIC.  Assume
    330       // that data-relative addresses are relative to 0, i.e.,
    331       // absolute.
    332       edi->di_cache.gp = 0;
    333       if (pdyn) {
    334         Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset + (char *) ei->u.mapped.image);
    335         while ((char*) dyn - (char*) ei->u.mapped.image + sizeof(Elf_W(Dyn)) < ei->u.mapped.size
    336                && dyn->d_tag != DT_NULL) {
    337           if (dyn->d_tag == DT_PLTGOT) {
    338             // Assume that _DYNAMIC is writable and GLIBC has
    339             // relocated it (true for x86 at least).
    340             edi->di_cache.gp = dyn->d_un.d_ptr;
    341             break;
    342           }
    343           dyn++;
    344         }
    345       }
    346 
    347       /* ANDROID support update. */
    348       hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
    349 					   + (char *) ei->u.mapped.image);
    350       /* End of ANDROID update. */
    351       if (hdr->version != DW_EH_VERSION)
    352 	{
    353 	  Debug (1, "table `%s' has unexpected version %d\n",
    354 		 path, hdr->version);
    355 	  return -UNW_ENOINFO;
    356 	}
    357 
    358       a = unw_get_accessors (unw_local_addr_space);
    359       /* ANDROID support update. */
    360       addr = (unw_word_t) (uintptr_t) (hdr + 1);
    361       /* End of ANDROID update. */
    362 
    363       /* Fill in a dummy proc_info structure.  We just need to fill in
    364 	 enough to ensure that dwarf_read_encoded_pointer() can do its
    365 	 job.  Since we don't have a procedure-context at this point, all
    366 	 we have to do is fill in the global-pointer.  */
    367       memset (&pi, 0, sizeof (pi));
    368       pi.gp = edi->di_cache.gp;
    369 
    370       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
    371 					     &addr, hdr->eh_frame_ptr_enc, &pi,
    372 					     &eh_frame_start, NULL)) < 0)
    373 	return -UNW_ENOINFO;
    374 
    375       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
    376 					     &addr, hdr->fde_count_enc, &pi,
    377 					     &fde_count, NULL)) < 0)
    378 	return -UNW_ENOINFO;
    379 
    380       if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
    381 	{
    382 #if 1
    383           // Right now do nothing.
    384 	  //abort ();
    385 #else
    386 	  unw_word_t eh_frame_end;
    387 
    388 	  /* If there is no search table or it has an unsupported
    389 	     encoding, fall back on linear search.  */
    390 	  if (hdr->table_enc == DW_EH_PE_omit)
    391 	    Debug (4, "EH lacks search table; doing linear search\n");
    392 	  else
    393 	    Debug (4, "EH table has encoding 0x%x; doing linear search\n",
    394 		   hdr->table_enc);
    395 
    396 	  eh_frame_end = max_load_addr;	/* XXX can we do better? */
    397 
    398 	  if (hdr->fde_count_enc == DW_EH_PE_omit)
    399 	    fde_count = ~0UL;
    400 	  if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
    401 	    abort ();
    402 
    403 	  return linear_search (unw_local_addr_space, ip,
    404 				eh_frame_start, eh_frame_end, fde_count,
    405 				pi, need_unwind_info, NULL);
    406 #endif
    407 	}
    408       else
    409         {
    410           edi->di_cache.start_ip = start_ip;
    411           edi->di_cache.end_ip = end_ip;
    412           edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
    413           edi->di_cache.u.rti.name_ptr = 0;
    414           /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
    415           edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
    416           /* ANDROID support update. */
    417           edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
    418                                            + (addr - (uintptr_t) ei->u.mapped.image
    419                                            - peh_hdr->p_offset));
    420           /* End of ANDROID update. */
    421 
    422           /* For the binary-search table in the eh_frame_hdr, data-relative
    423              means relative to the start of that section... */
    424 
    425           /* ANDROID support update. */
    426           edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
    427                                         + ((uintptr_t) hdr - (uintptr_t) ei->u.mapped.image
    428                                         - peh_hdr->p_offset));
    429           /* End of ANDROID update. */
    430           found = 1;
    431         }
    432     }
    433 
    434 #if UNW_TARGET_ARM
    435   if (parm_exidx)
    436     {
    437       edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
    438       edi->di_arm.start_ip = start_ip;
    439       edi->di_arm.end_ip = end_ip;
    440       edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
    441       edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
    442       edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
    443       found = 1;
    444     }
    445 #endif
    446 
    447 #ifdef CONFIG_DEBUG_FRAME
    448   /* Try .debug_frame. */
    449   found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path,
    450 				  start_ip, end_ip);
    451 #endif
    452 
    453   return found;
    454 }
    455