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