Home | History | Annotate | Download | only in libdw
      1 /* FDE reading.
      2    Copyright (C) 2009-2010 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #ifdef HAVE_CONFIG_H
     51 # include <config.h>
     52 #endif
     53 
     54 #include "cfi.h"
     55 #include <search.h>
     56 #include <stdlib.h>
     57 
     58 #include "encoded-value.h"
     59 
     60 static int
     61 compare_fde (const void *a, const void *b)
     62 {
     63   const struct dwarf_fde *fde1 = a;
     64   const struct dwarf_fde *fde2 = b;
     65 
     66   /* Find out which of the two arguments is the search value.
     67      It has end offset 0.  */
     68   if (fde1->end == 0)
     69     {
     70       if (fde1->start < fde2->start)
     71 	return -1;
     72       if (fde1->start >= fde2->end)
     73 	return 1;
     74     }
     75   else
     76     {
     77       if (fde2->start < fde1->start)
     78 	return 1;
     79       if (fde2->start >= fde1->end)
     80 	return -1;
     81     }
     82 
     83   return 0;
     84 }
     85 
     86 static struct dwarf_fde *
     87 intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
     88 {
     89   /* Look up the new entry's CIE.  */
     90   struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
     91   if (cie == NULL)
     92     return (void *) -1l;
     93 
     94   struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
     95   if (fde == NULL)
     96     {
     97       __libdw_seterrno (DWARF_E_NOMEM);
     98       return NULL;
     99     }
    100 
    101   fde->instructions = entry->start;
    102   fde->instructions_end = entry->end;
    103   if (unlikely (read_encoded_value (cache, cie->fde_encoding,
    104 				    &fde->instructions, &fde->start))
    105       || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
    106 				       &fde->instructions, &fde->end)))
    107     return NULL;
    108   fde->end += fde->start;
    109 
    110   fde->cie = cie;
    111 
    112   if (cie->sized_augmentation_data)
    113     {
    114       /* The CIE augmentation says the FDE has a DW_FORM_block
    115 	 before its actual instruction stream.  */
    116       Dwarf_Word len;
    117       get_uleb128 (len, fde->instructions);
    118       if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
    119 	{
    120 	  free (fde);
    121 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    122 	  return NULL;
    123 	}
    124       fde->instructions += len;
    125     }
    126   else
    127     /* We had to understand all of the CIE augmentation string.
    128        We've recorded the number of data bytes in FDEs.  */
    129     fde->instructions += cie->fde_augmentation_data_size;
    130 
    131   /* Add the new entry to the search tree.  */
    132   if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
    133     {
    134       free (fde);
    135       __libdw_seterrno (DWARF_E_NOMEM);
    136       return NULL;
    137     }
    138 
    139   return fde;
    140 }
    141 
    142 struct dwarf_fde *
    143 internal_function
    144 __libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
    145 {
    146   Dwarf_CFI_Entry entry;
    147   Dwarf_Off next_offset;
    148   int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
    149 				       &cache->data->d, CFI_IS_EH (cache),
    150 				       offset, &next_offset, &entry);
    151   if (result != 0)
    152     {
    153       if (result > 0)
    154       invalid:
    155 	__libdw_seterrno (DWARF_E_INVALID_DWARF);
    156       return NULL;
    157     }
    158 
    159   if (unlikely (dwarf_cfi_cie_p (&entry)))
    160     goto invalid;
    161 
    162   /* We have a new FDE to consider.  */
    163   struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
    164   if (fde == (void *) -1l || fde == NULL)
    165     return NULL;
    166 
    167   /* If this happened to be what we would have read next, notice it.  */
    168   if (cache->next_offset == offset)
    169     cache->next_offset = next_offset;
    170 
    171   return fde;
    172 }
    173 
    174 /* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
    175 static Dwarf_Off
    176 binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
    177 {
    178   const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
    179 					      cache->search_table_encoding,
    180 					      NULL);
    181 
    182   /* Dummy used by read_encoded_value.  */
    183   Dwarf_CFI dummy_cfi =
    184     {
    185       .e_ident = cache->e_ident,
    186       .datarel = cache->search_table_vaddr,
    187       .frame_vaddr = cache->search_table_vaddr,
    188     };
    189 
    190   size_t l = 0, u = cache->search_table_entries;
    191   while (l < u)
    192     {
    193       size_t idx = (l + u) / 2;
    194 
    195       const uint8_t *p = &cache->search_table[idx * size];
    196       Dwarf_Addr start;
    197       if (unlikely (read_encoded_value (&dummy_cfi,
    198 					cache->search_table_encoding, &p,
    199 					&start)))
    200 	break;
    201       if (address < start)
    202 	u = idx;
    203       else
    204 	{
    205 	  Dwarf_Addr fde;
    206 	  if (unlikely (read_encoded_value (&dummy_cfi,
    207 					    cache->search_table_encoding, &p,
    208 					    &fde)))
    209 	    break;
    210 	  if (address >= start)
    211 	    {
    212 	      l = idx + 1;
    213 
    214 	      /* If this is the last entry, its upper bound is assumed to be
    215 		 the end of the module.
    216 		 XXX really should be end of containing PT_LOAD segment */
    217 	      if (l < cache->search_table_entries)
    218 		{
    219 		  /* Look at the start address in the following entry.  */
    220 		  Dwarf_Addr end;
    221 		  if (unlikely (read_encoded_value
    222 				(&dummy_cfi, cache->search_table_encoding, &p,
    223 				 &end)))
    224 		    break;
    225 		  if (address >= end)
    226 		    continue;
    227 		}
    228 
    229 	      return fde - cache->frame_vaddr;
    230 	    }
    231 	}
    232     }
    233 
    234   return (Dwarf_Off) -1l;
    235 }
    236 
    237 struct dwarf_fde *
    238 internal_function
    239 __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
    240 {
    241   /* Look for a cached FDE covering this address.  */
    242 
    243   const struct dwarf_fde fde_key = { .start = address, .end = 0 };
    244   struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
    245   if (found != NULL)
    246     return *found;
    247 
    248   /* Use .eh_frame_hdr binary search table if possible.  */
    249   if (cache->search_table != NULL)
    250     {
    251       Dwarf_Off offset = binary_search_fde (cache, address);
    252       if (offset == (Dwarf_Off) -1l)
    253 	goto no_match;
    254       struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
    255       if (unlikely (fde != NULL)
    256 	  /* Sanity check the address range.  */
    257 	  && unlikely (address < fde->start || address >= fde->end))
    258 	{
    259 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    260 	  return NULL;
    261 	}
    262       return fde;
    263     }
    264 
    265   /* It's not there.  Read more CFI entries until we find it.  */
    266   while (1)
    267     {
    268       Dwarf_Off last_offset = cache->next_offset;
    269       Dwarf_CFI_Entry entry;
    270       int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
    271 					   &cache->data->d, CFI_IS_EH (cache),
    272 					   last_offset, &cache->next_offset,
    273 					   &entry);
    274       if (result > 0)
    275 	break;
    276       if (result < 0)
    277 	{
    278 	  if (cache->next_offset == last_offset)
    279 	    /* We couldn't progress past the bogus FDE.  */
    280 	    break;
    281 	  /* Skip the loser and look at the next entry.  */
    282 	  continue;
    283 	}
    284 
    285       if (dwarf_cfi_cie_p (&entry))
    286 	{
    287 	  /* This is a CIE, not an FDE.  We eagerly intern these
    288 	     because the next FDE will usually refer to this CIE.  */
    289 	  __libdw_intern_cie (cache, last_offset, &entry.cie);
    290 	  continue;
    291 	}
    292 
    293       /* We have a new FDE to consider.  */
    294       struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
    295 
    296       if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
    297 	continue;
    298 
    299       if (fde == NULL)		/* Bad data.  */
    300 	return NULL;
    301 
    302       /* Is this the one we're looking for?  */
    303       if (fde->start <= address && fde->end > address)
    304 	return fde;
    305     }
    306 
    307  no_match:
    308   /* We found no FDE covering this address.  */
    309   __libdw_seterrno (DWARF_E_NO_MATCH);
    310   return NULL;
    311 }
    312