Home | History | Annotate | Download | only in libdw
      1 /* CIE reading.
      2    Copyright (C) 2009-2010 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 "encoded-value.h"
     35 #include <assert.h>
     36 #include <search.h>
     37 #include <stdlib.h>
     38 
     39 
     40 static int
     41 compare_cie (const void *a, const void *b)
     42 {
     43   const struct dwarf_cie *cie1 = a;
     44   const struct dwarf_cie *cie2 = b;
     45   if (cie1->offset < cie2->offset)
     46     return -1;
     47   if (cie1->offset > cie2->offset)
     48     return 1;
     49   return 0;
     50 }
     51 
     52 /* There is no CIE at OFFSET in the tree.  Add it.  */
     53 static struct dwarf_cie *
     54 intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
     55 {
     56   struct dwarf_cie *cie = malloc (sizeof (struct dwarf_cie));
     57   if (cie == NULL)
     58     {
     59       __libdw_seterrno (DWARF_E_NOMEM);
     60       return NULL;
     61     }
     62 
     63   cie->offset = offset;
     64   cie->code_alignment_factor = info->code_alignment_factor;
     65   cie->data_alignment_factor = info->data_alignment_factor;
     66   cie->return_address_register = info->return_address_register;
     67 
     68   cie->fde_augmentation_data_size = 0;
     69   cie->sized_augmentation_data = false;
     70   cie->signal_frame = false;
     71 
     72   cie->fde_encoding = DW_EH_PE_absptr;
     73   cie->lsda_encoding = DW_EH_PE_omit;
     74 
     75   /* Grok the augmentation string and its data.  */
     76   const uint8_t *data = info->augmentation_data;
     77   for (const char *ap = info->augmentation; *ap != '\0'; ++ap)
     78     {
     79       uint8_t encoding;
     80       switch (*ap)
     81 	{
     82 	case 'z':
     83 	  cie->sized_augmentation_data = true;
     84 	  continue;
     85 
     86 	case 'S':
     87 	  cie->signal_frame = true;
     88 	  continue;
     89 
     90 	case 'L':		/* LSDA pointer encoding byte.  */
     91 	  cie->lsda_encoding = *data++;
     92 	  if (!cie->sized_augmentation_data)
     93 	    cie->fde_augmentation_data_size
     94 	      += encoded_value_size (&cache->data->d, cache->e_ident,
     95 				     cie->lsda_encoding, NULL);
     96 	  continue;
     97 
     98 	case 'R':		/* FDE address encoding byte.  */
     99 	  cie->fde_encoding = *data++;
    100 	  continue;
    101 
    102 	case 'P':		/* Skip personality routine.  */
    103 	  encoding = *data++;
    104 	  data += encoded_value_size (&cache->data->d, cache->e_ident,
    105 				      encoding, data);
    106 	  continue;
    107 
    108 	default:
    109 	  /* Unknown augmentation string.  If we have 'z' we can ignore it,
    110 	     otherwise we must bail out.  */
    111 	  if (cie->sized_augmentation_data)
    112 	    continue;
    113 	}
    114       /* We only get here when we need to bail out.  */
    115       break;
    116     }
    117 
    118   if ((cie->fde_encoding & 0x0f) == DW_EH_PE_absptr)
    119     {
    120       /* Canonicalize encoding to a specific size.  */
    121       assert (DW_EH_PE_absptr == 0);
    122 
    123       /* XXX should get from dwarf_next_cfi with v4 header.  */
    124       uint_fast8_t address_size
    125 	= cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
    126       switch (address_size)
    127 	{
    128 	case 8:
    129 	  cie->fde_encoding |= DW_EH_PE_udata8;
    130 	  break;
    131 	case 4:
    132 	  cie->fde_encoding |= DW_EH_PE_udata4;
    133 	  break;
    134 	default:
    135 	  free (cie);
    136 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    137 	  return NULL;
    138 	}
    139     }
    140 
    141   /* Save the initial instructions to be played out into initial state.  */
    142   cie->initial_instructions = info->initial_instructions;
    143   cie->initial_instructions_end = info->initial_instructions_end;
    144   cie->initial_state = NULL;
    145 
    146   /* Add the new entry to the search tree.  */
    147   if (tsearch (cie, &cache->cie_tree, &compare_cie) == NULL)
    148     {
    149       free (cie);
    150       __libdw_seterrno (DWARF_E_NOMEM);
    151       return NULL;
    152     }
    153 
    154   return cie;
    155 }
    156 
    157 /* Look up a CIE_pointer for random access.  */
    158 struct dwarf_cie *
    159 internal_function
    160 __libdw_find_cie (Dwarf_CFI *cache, Dwarf_Off offset)
    161 {
    162   const struct dwarf_cie cie_key = { .offset = offset };
    163   struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
    164   if (found != NULL)
    165     return *found;
    166 
    167   /* We have not read this CIE yet.  Go find it.  */
    168   Dwarf_Off next_offset = offset;
    169   Dwarf_CFI_Entry entry;
    170   int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
    171 				       &cache->data->d, CFI_IS_EH (cache),
    172 				       offset, &next_offset, &entry);
    173   if (result != 0 || entry.cie.CIE_id != DW_CIE_ID_64)
    174     {
    175       __libdw_seterrno (DWARF_E_INVALID_DWARF);
    176       return NULL;
    177     }
    178 
    179   /* If this happened to be what we would have read next, notice it.  */
    180   if (cache->next_offset == offset)
    181     cache->next_offset = next_offset;
    182 
    183   return intern_new_cie (cache, offset, &entry.cie);
    184 }
    185 
    186 /* Enter a CIE encountered while reading through for FDEs.  */
    187 void
    188 internal_function
    189 __libdw_intern_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
    190 {
    191   const struct dwarf_cie cie_key = { .offset = offset };
    192   struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
    193   if (found == NULL)
    194     /* We have not read this CIE yet.  Enter it.  */
    195     (void) intern_new_cie (cache, offset, info);
    196 }
    197