1da0c48c4Sopenharmony_ci/* CIE reading.
2da0c48c4Sopenharmony_ci   Copyright (C) 2009-2010 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include "cfi.h"
34da0c48c4Sopenharmony_ci#include "encoded-value.h"
35da0c48c4Sopenharmony_ci#include <assert.h>
36da0c48c4Sopenharmony_ci#include <search.h>
37da0c48c4Sopenharmony_ci#include <stdlib.h>
38da0c48c4Sopenharmony_ci
39da0c48c4Sopenharmony_ci
40da0c48c4Sopenharmony_cistatic int
41da0c48c4Sopenharmony_cicompare_cie (const void *a, const void *b)
42da0c48c4Sopenharmony_ci{
43da0c48c4Sopenharmony_ci  const struct dwarf_cie *cie1 = a;
44da0c48c4Sopenharmony_ci  const struct dwarf_cie *cie2 = b;
45da0c48c4Sopenharmony_ci  if (cie1->offset < cie2->offset)
46da0c48c4Sopenharmony_ci    return -1;
47da0c48c4Sopenharmony_ci  if (cie1->offset > cie2->offset)
48da0c48c4Sopenharmony_ci    return 1;
49da0c48c4Sopenharmony_ci  return 0;
50da0c48c4Sopenharmony_ci}
51da0c48c4Sopenharmony_ci
52da0c48c4Sopenharmony_ci/* There is no CIE at OFFSET in the tree.  Add it.  */
53da0c48c4Sopenharmony_cistatic struct dwarf_cie *
54da0c48c4Sopenharmony_ciintern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
55da0c48c4Sopenharmony_ci{
56da0c48c4Sopenharmony_ci  struct dwarf_cie *cie = malloc (sizeof (struct dwarf_cie));
57da0c48c4Sopenharmony_ci  if (cie == NULL)
58da0c48c4Sopenharmony_ci    {
59da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_NOMEM);
60da0c48c4Sopenharmony_ci      return NULL;
61da0c48c4Sopenharmony_ci    }
62da0c48c4Sopenharmony_ci
63da0c48c4Sopenharmony_ci  cie->offset = offset;
64da0c48c4Sopenharmony_ci  cie->code_alignment_factor = info->code_alignment_factor;
65da0c48c4Sopenharmony_ci  cie->data_alignment_factor = info->data_alignment_factor;
66da0c48c4Sopenharmony_ci  cie->return_address_register = info->return_address_register;
67da0c48c4Sopenharmony_ci
68da0c48c4Sopenharmony_ci  cie->fde_augmentation_data_size = 0;
69da0c48c4Sopenharmony_ci  cie->sized_augmentation_data = false;
70da0c48c4Sopenharmony_ci  cie->signal_frame = false;
71da0c48c4Sopenharmony_ci
72da0c48c4Sopenharmony_ci  cie->fde_encoding = DW_EH_PE_absptr;
73da0c48c4Sopenharmony_ci  cie->lsda_encoding = DW_EH_PE_omit;
74da0c48c4Sopenharmony_ci
75da0c48c4Sopenharmony_ci  /* Grok the augmentation string and its data.  */
76da0c48c4Sopenharmony_ci  const uint8_t *data = info->augmentation_data;
77da0c48c4Sopenharmony_ci  for (const char *ap = info->augmentation; *ap != '\0'; ++ap)
78da0c48c4Sopenharmony_ci    {
79da0c48c4Sopenharmony_ci      uint8_t encoding;
80da0c48c4Sopenharmony_ci      switch (*ap)
81da0c48c4Sopenharmony_ci	{
82da0c48c4Sopenharmony_ci	case 'z':
83da0c48c4Sopenharmony_ci	  cie->sized_augmentation_data = true;
84da0c48c4Sopenharmony_ci	  continue;
85da0c48c4Sopenharmony_ci
86da0c48c4Sopenharmony_ci	case 'S':
87da0c48c4Sopenharmony_ci	  cie->signal_frame = true;
88da0c48c4Sopenharmony_ci	  continue;
89da0c48c4Sopenharmony_ci
90da0c48c4Sopenharmony_ci	case 'L':		/* LSDA pointer encoding byte.  */
91da0c48c4Sopenharmony_ci	  cie->lsda_encoding = *data++;
92da0c48c4Sopenharmony_ci	  if (!cie->sized_augmentation_data)
93da0c48c4Sopenharmony_ci	    cie->fde_augmentation_data_size
94da0c48c4Sopenharmony_ci	      += encoded_value_size (&cache->data->d, cache->e_ident,
95da0c48c4Sopenharmony_ci				     cie->lsda_encoding, NULL);
96da0c48c4Sopenharmony_ci	  continue;
97da0c48c4Sopenharmony_ci
98da0c48c4Sopenharmony_ci	case 'R':		/* FDE address encoding byte.  */
99da0c48c4Sopenharmony_ci	  cie->fde_encoding = *data++;
100da0c48c4Sopenharmony_ci	  continue;
101da0c48c4Sopenharmony_ci
102da0c48c4Sopenharmony_ci	case 'P':		/* Skip personality routine.  */
103da0c48c4Sopenharmony_ci	  encoding = *data++;
104da0c48c4Sopenharmony_ci	  data += encoded_value_size (&cache->data->d, cache->e_ident,
105da0c48c4Sopenharmony_ci				      encoding, data);
106da0c48c4Sopenharmony_ci	  continue;
107da0c48c4Sopenharmony_ci
108da0c48c4Sopenharmony_ci	default:
109da0c48c4Sopenharmony_ci	  /* Unknown augmentation string.  If we have 'z' we can ignore it,
110da0c48c4Sopenharmony_ci	     otherwise we must bail out.  */
111da0c48c4Sopenharmony_ci	  if (cie->sized_augmentation_data)
112da0c48c4Sopenharmony_ci	    continue;
113da0c48c4Sopenharmony_ci	}
114da0c48c4Sopenharmony_ci      /* We only get here when we need to bail out.  */
115da0c48c4Sopenharmony_ci      break;
116da0c48c4Sopenharmony_ci    }
117da0c48c4Sopenharmony_ci
118da0c48c4Sopenharmony_ci  if ((cie->fde_encoding & 0x0f) == DW_EH_PE_absptr)
119da0c48c4Sopenharmony_ci    {
120da0c48c4Sopenharmony_ci      /* Canonicalize encoding to a specific size.  */
121da0c48c4Sopenharmony_ci      assert (DW_EH_PE_absptr == 0);
122da0c48c4Sopenharmony_ci
123da0c48c4Sopenharmony_ci      /* XXX should get from dwarf_next_cfi with v4 header.  */
124da0c48c4Sopenharmony_ci      uint_fast8_t address_size
125da0c48c4Sopenharmony_ci	= cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
126da0c48c4Sopenharmony_ci      switch (address_size)
127da0c48c4Sopenharmony_ci	{
128da0c48c4Sopenharmony_ci	case 8:
129da0c48c4Sopenharmony_ci	  cie->fde_encoding |= DW_EH_PE_udata8;
130da0c48c4Sopenharmony_ci	  break;
131da0c48c4Sopenharmony_ci	case 4:
132da0c48c4Sopenharmony_ci	  cie->fde_encoding |= DW_EH_PE_udata4;
133da0c48c4Sopenharmony_ci	  break;
134da0c48c4Sopenharmony_ci	default:
135da0c48c4Sopenharmony_ci	  free (cie);
136da0c48c4Sopenharmony_ci	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
137da0c48c4Sopenharmony_ci	  return NULL;
138da0c48c4Sopenharmony_ci	}
139da0c48c4Sopenharmony_ci    }
140da0c48c4Sopenharmony_ci
141da0c48c4Sopenharmony_ci  /* Save the initial instructions to be played out into initial state.  */
142da0c48c4Sopenharmony_ci  cie->initial_instructions = info->initial_instructions;
143da0c48c4Sopenharmony_ci  cie->initial_instructions_end = info->initial_instructions_end;
144da0c48c4Sopenharmony_ci  cie->initial_state = NULL;
145da0c48c4Sopenharmony_ci
146da0c48c4Sopenharmony_ci  /* Add the new entry to the search tree.  */
147da0c48c4Sopenharmony_ci  if (tsearch (cie, &cache->cie_tree, &compare_cie) == NULL)
148da0c48c4Sopenharmony_ci    {
149da0c48c4Sopenharmony_ci      free (cie);
150da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_NOMEM);
151da0c48c4Sopenharmony_ci      return NULL;
152da0c48c4Sopenharmony_ci    }
153da0c48c4Sopenharmony_ci
154da0c48c4Sopenharmony_ci  return cie;
155da0c48c4Sopenharmony_ci}
156da0c48c4Sopenharmony_ci
157da0c48c4Sopenharmony_ci/* Look up a CIE_pointer for random access.  */
158da0c48c4Sopenharmony_cistruct dwarf_cie *
159da0c48c4Sopenharmony_ciinternal_function
160da0c48c4Sopenharmony_ci__libdw_find_cie (Dwarf_CFI *cache, Dwarf_Off offset)
161da0c48c4Sopenharmony_ci{
162da0c48c4Sopenharmony_ci  const struct dwarf_cie cie_key = { .offset = offset };
163da0c48c4Sopenharmony_ci  struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
164da0c48c4Sopenharmony_ci  if (found != NULL)
165da0c48c4Sopenharmony_ci    return *found;
166da0c48c4Sopenharmony_ci
167da0c48c4Sopenharmony_ci  /* We have not read this CIE yet.  Go find it.  */
168da0c48c4Sopenharmony_ci  Dwarf_Off next_offset = offset;
169da0c48c4Sopenharmony_ci  Dwarf_CFI_Entry entry;
170da0c48c4Sopenharmony_ci  int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
171da0c48c4Sopenharmony_ci				       &cache->data->d, CFI_IS_EH (cache),
172da0c48c4Sopenharmony_ci				       offset, &next_offset, &entry);
173da0c48c4Sopenharmony_ci  if (result != 0 || entry.cie.CIE_id != DW_CIE_ID_64)
174da0c48c4Sopenharmony_ci    {
175da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_INVALID_DWARF);
176da0c48c4Sopenharmony_ci      return NULL;
177da0c48c4Sopenharmony_ci    }
178da0c48c4Sopenharmony_ci
179da0c48c4Sopenharmony_ci  /* If this happened to be what we would have read next, notice it.  */
180da0c48c4Sopenharmony_ci  if (cache->next_offset == offset)
181da0c48c4Sopenharmony_ci    cache->next_offset = next_offset;
182da0c48c4Sopenharmony_ci
183da0c48c4Sopenharmony_ci  return intern_new_cie (cache, offset, &entry.cie);
184da0c48c4Sopenharmony_ci}
185da0c48c4Sopenharmony_ci
186da0c48c4Sopenharmony_ci/* Enter a CIE encountered while reading through for FDEs.  */
187da0c48c4Sopenharmony_civoid
188da0c48c4Sopenharmony_ciinternal_function
189da0c48c4Sopenharmony_ci__libdw_intern_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
190da0c48c4Sopenharmony_ci{
191da0c48c4Sopenharmony_ci  const struct dwarf_cie cie_key = { .offset = offset };
192da0c48c4Sopenharmony_ci  struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
193da0c48c4Sopenharmony_ci  if (found == NULL)
194da0c48c4Sopenharmony_ci    /* We have not read this CIE yet.  Enter it.  */
195da0c48c4Sopenharmony_ci    (void) intern_new_cie (cache, offset, info);
196da0c48c4Sopenharmony_ci}
197