Home | History | Annotate | Download | only in libdw
      1 /* FDE reading.
      2    Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
      3    This file is part of elfutils.
      4 
      5    This file is free software; you can redistribute it and/or modify
      6    it under the terms of either
      7 
      8      * the GNU Lesser General Public License as published by the Free
      9        Software Foundation; either version 3 of the License, or (at
     10        your option) any later version
     11 
     12    or
     13 
     14      * the GNU General Public License as published by the Free
     15        Software Foundation; either version 2 of the License, or (at
     16        your option) any later version
     17 
     18    or both in parallel, as here.
     19 
     20    elfutils is distributed in the hope that it will be useful, but
     21    WITHOUT ANY WARRANTY; without even the implied warranty of
     22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     23    General Public License for more details.
     24 
     25    You should have received copies of the GNU General Public License and
     26    the GNU Lesser General Public License along with this program.  If
     27    not, see <http://www.gnu.org/licenses/>.  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 # include <config.h>
     31 #endif
     32 
     33 #include "cfi.h"
     34 #include <search.h>
     35 #include <stdlib.h>
     36 
     37 #include "encoded-value.h"
     38 
     39 static int
     40 compare_fde (const void *a, const void *b)
     41 {
     42   const struct dwarf_fde *fde1 = a;
     43   const struct dwarf_fde *fde2 = b;
     44 
     45   /* Find out which of the two arguments is the search value.
     46      It has end offset 0.  */
     47   if (fde1->end == 0)
     48     {
     49       if (fde1->start < fde2->start)
     50 	return -1;
     51       if (fde1->start >= fde2->end)
     52 	return 1;
     53     }
     54   else
     55     {
     56       if (fde2->start < fde1->start)
     57 	return 1;
     58       if (fde2->start >= fde1->end)
     59 	return -1;
     60     }
     61 
     62   return 0;
     63 }
     64 
     65 static struct dwarf_fde *
     66 intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
     67 {
     68   /* Look up the new entry's CIE.  */
     69   struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
     70   if (cie == NULL)
     71     return (void *) -1l;
     72 
     73   struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
     74   if (fde == NULL)
     75     {
     76       __libdw_seterrno (DWARF_E_NOMEM);
     77       return NULL;
     78     }
     79 
     80   fde->instructions = entry->start;
     81   fde->instructions_end = entry->end;
     82   if (unlikely (read_encoded_value (cache, cie->fde_encoding,
     83 				    &fde->instructions, &fde->start))
     84       || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
     85 				       &fde->instructions, &fde->end)))
     86     {
     87       free (fde);
     88       __libdw_seterrno (DWARF_E_INVALID_DWARF);
     89       return NULL;
     90     }
     91   fde->end += fde->start;
     92 
     93   /* Make sure the fde actually covers a real code range.  */
     94   if (fde->start >= fde->end)
     95     {
     96       free (fde);
     97       return (void *) -1;
     98     }
     99 
    100   fde->cie = cie;
    101 
    102   if (cie->sized_augmentation_data)
    103     {
    104       /* The CIE augmentation says the FDE has a DW_FORM_block
    105 	 before its actual instruction stream.  */
    106       Dwarf_Word len;
    107       get_uleb128 (len, fde->instructions, fde->instructions_end);
    108       if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
    109 	{
    110 	  free (fde);
    111 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    112 	  return NULL;
    113 	}
    114       fde->instructions += len;
    115     }
    116   else
    117     /* We had to understand all of the CIE augmentation string.
    118        We've recorded the number of data bytes in FDEs.  */
    119     fde->instructions += cie->fde_augmentation_data_size;
    120 
    121   /* Add the new entry to the search tree.  */
    122   struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
    123   if (tres == NULL)
    124     {
    125       free (fde);
    126       __libdw_seterrno (DWARF_E_NOMEM);
    127       return NULL;
    128     }
    129   else if (*tres != fde)
    130     {
    131       /* There is already an FDE in the cache that covers the same
    132 	 address range.  That is odd.  Ignore this FDE.  And just use
    133 	 the one in the cache for consistency.  */
    134       free (fde);
    135       return *tres;
    136     }
    137 
    138   return fde;
    139 }
    140 
    141 struct dwarf_fde *
    142 internal_function
    143 __libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
    144 {
    145   Dwarf_CFI_Entry entry;
    146   Dwarf_Off next_offset;
    147   int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
    148 				       &cache->data->d, CFI_IS_EH (cache),
    149 				       offset, &next_offset, &entry);
    150   if (result != 0)
    151     {
    152       if (result > 0)
    153       invalid:
    154 	__libdw_seterrno (DWARF_E_INVALID_DWARF);
    155       return NULL;
    156     }
    157 
    158   if (unlikely (dwarf_cfi_cie_p (&entry)))
    159     goto invalid;
    160 
    161   /* We have a new FDE to consider.  */
    162   struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
    163   if (fde == (void *) -1l || fde == NULL)
    164     return NULL;
    165 
    166   /* If this happened to be what we would have read next, notice it.  */
    167   if (cache->next_offset == offset)
    168     cache->next_offset = next_offset;
    169 
    170   return fde;
    171 }
    172 
    173 /* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
    174 static Dwarf_Off
    175 binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
    176 {
    177   const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
    178 					      cache->search_table_encoding,
    179 					      NULL);
    180   if (unlikely (size == 0))
    181     return (Dwarf_Off) -1l;
    182 
    183   /* Dummy used by read_encoded_value.  */
    184   Elf_Data_Scn dummy_cfi_hdr_data =
    185     {
    186       .d = { .d_buf = (void *) cache->search_table,
    187 	     .d_size = cache->search_table_len }
    188     };
    189 
    190   Dwarf_CFI dummy_cfi =
    191     {
    192       .e_ident = cache->e_ident,
    193       .datarel = cache->search_table_vaddr,
    194       .frame_vaddr = cache->search_table_vaddr,
    195       .data = &dummy_cfi_hdr_data
    196     };
    197 
    198   size_t l = 0, u = cache->search_table_entries;
    199   while (l < u)
    200     {
    201       size_t idx = (l + u) / 2;
    202 
    203       /* Max idx * size is checked against search_table len when
    204 	 loading eh_frame_hdr.  */
    205       const uint8_t *p = &cache->search_table[idx * size];
    206       Dwarf_Addr start;
    207       if (unlikely (read_encoded_value (&dummy_cfi,
    208 					cache->search_table_encoding, &p,
    209 					&start)))
    210 	break;
    211       if (address < start)
    212 	u = idx;
    213       else
    214 	{
    215 	  l = idx + 1;
    216 
    217 	  Dwarf_Addr fde;
    218 	  if (unlikely (read_encoded_value (&dummy_cfi,
    219 					    cache->search_table_encoding, &p,
    220 					    &fde)))
    221 	    break;
    222 
    223 	  /* If this is the last entry, its upper bound is assumed to be
    224 	     the end of the module.
    225 	     XXX really should be end of containing PT_LOAD segment */
    226 	  if (l < cache->search_table_entries)
    227 	    {
    228 	      /* Look at the start address in the following entry.  */
    229 	      Dwarf_Addr end;
    230 	      if (unlikely (read_encoded_value
    231 			    (&dummy_cfi, cache->search_table_encoding, &p,
    232 			     &end)))
    233 		break;
    234 	      if (address >= end)
    235 		continue;
    236 	    }
    237 
    238 	  return fde - cache->frame_vaddr;
    239 	}
    240     }
    241 
    242   return (Dwarf_Off) -1l;
    243 }
    244 
    245 struct dwarf_fde *
    246 internal_function
    247 __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
    248 {
    249   /* Look for a cached FDE covering this address.  */
    250 
    251   const struct dwarf_fde fde_key = { .start = address, .end = 0 };
    252   struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
    253   if (found != NULL)
    254     return *found;
    255 
    256   /* Use .eh_frame_hdr binary search table if possible.  */
    257   if (cache->search_table != NULL)
    258     {
    259       Dwarf_Off offset = binary_search_fde (cache, address);
    260       if (offset == (Dwarf_Off) -1l)
    261 	goto no_match;
    262       struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
    263       if (likely (fde != NULL))
    264 	{
    265 	  /* Sanity check the address range.  */
    266 	  if (unlikely (address < fde->start))
    267 	    {
    268 	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
    269 	      return NULL;
    270 	    }
    271 	  /* .eh_frame_hdr does not indicate length covered by FDE.  */
    272 	  if (unlikely (address >= fde->end))
    273 	    goto no_match;
    274 	}
    275       return fde;
    276     }
    277 
    278   /* It's not there.  Read more CFI entries until we find it.  */
    279   while (1)
    280     {
    281       Dwarf_Off last_offset = cache->next_offset;
    282       Dwarf_CFI_Entry entry;
    283       int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
    284 					   &cache->data->d, CFI_IS_EH (cache),
    285 					   last_offset, &cache->next_offset,
    286 					   &entry);
    287       if (result > 0)
    288 	break;
    289       if (result < 0)
    290 	{
    291 	  if (cache->next_offset == last_offset)
    292 	    /* We couldn't progress past the bogus FDE.  */
    293 	    break;
    294 	  /* Skip the loser and look at the next entry.  */
    295 	  continue;
    296 	}
    297 
    298       if (dwarf_cfi_cie_p (&entry))
    299 	{
    300 	  /* This is a CIE, not an FDE.  We eagerly intern these
    301 	     because the next FDE will usually refer to this CIE.  */
    302 	  __libdw_intern_cie (cache, last_offset, &entry.cie);
    303 	  continue;
    304 	}
    305 
    306       /* We have a new FDE to consider.  */
    307       struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
    308 
    309       if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
    310 	continue;
    311 
    312       if (fde == NULL)		/* Bad data.  */
    313 	return NULL;
    314 
    315       /* Is this the one we're looking for?  */
    316       if (fde->start <= address && fde->end > address)
    317 	return fde;
    318     }
    319 
    320  no_match:
    321   /* We found no FDE covering this address.  */
    322   __libdw_seterrno (DWARF_E_NO_MATCH);
    323   return NULL;
    324 }
    325