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
40static int
41compare_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.  */
53static struct dwarf_cie *
54intern_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.  */
158struct dwarf_cie *
159internal_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.  */
187void
188internal_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