Line data Source code
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 25996 : compare_cie (const void *a, const void *b)
42 : {
43 25996 : const struct dwarf_cie *cie1 = a;
44 25996 : const struct dwarf_cie *cie2 = b;
45 25996 : if (cie1->offset < cie2->offset)
46 : return -1;
47 19638 : if (cie1->offset > cie2->offset)
48 : return 1;
49 17295 : return 0;
50 : }
51 :
52 : /* There is no CIE at OFFSET in the tree. Add it. */
53 : static struct dwarf_cie *
54 160 : intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
55 : {
56 160 : struct dwarf_cie *cie = malloc (sizeof (struct dwarf_cie));
57 160 : if (cie == NULL)
58 : {
59 0 : __libdw_seterrno (DWARF_E_NOMEM);
60 0 : return NULL;
61 : }
62 :
63 160 : cie->offset = offset;
64 160 : cie->code_alignment_factor = info->code_alignment_factor;
65 160 : cie->data_alignment_factor = info->data_alignment_factor;
66 160 : cie->return_address_register = info->return_address_register;
67 :
68 160 : cie->fde_augmentation_data_size = 0;
69 160 : cie->sized_augmentation_data = false;
70 160 : cie->signal_frame = false;
71 :
72 160 : cie->fde_encoding = DW_EH_PE_absptr;
73 160 : cie->lsda_encoding = DW_EH_PE_omit;
74 :
75 : /* Grok the augmentation string and its data. */
76 160 : const uint8_t *data = info->augmentation_data;
77 456 : for (const char *ap = info->augmentation; *ap != '\0'; ++ap)
78 : {
79 : uint8_t encoding;
80 296 : switch (*ap)
81 : {
82 129 : case 'z':
83 129 : cie->sized_augmentation_data = true;
84 129 : continue;
85 :
86 3 : case 'S':
87 3 : cie->signal_frame = true;
88 3 : continue;
89 :
90 17 : case 'L': /* LSDA pointer encoding byte. */
91 17 : cie->lsda_encoding = *data++;
92 17 : if (!cie->sized_augmentation_data)
93 : cie->fde_augmentation_data_size
94 0 : += encoded_value_size (&cache->data->d, cache->e_ident,
95 : cie->lsda_encoding, NULL);
96 17 : continue;
97 :
98 126 : case 'R': /* FDE address encoding byte. */
99 126 : cie->fde_encoding = *data++;
100 126 : continue;
101 :
102 21 : case 'P': /* Skip personality routine. */
103 21 : encoding = *data++;
104 21 : data += encoded_value_size (&cache->data->d, cache->e_ident,
105 : encoding, data);
106 21 : continue;
107 :
108 0 : default:
109 : /* Unknown augmentation string. If we have 'z' we can ignore it,
110 : otherwise we must bail out. */
111 0 : if (cie->sized_augmentation_data)
112 0 : continue;
113 : }
114 : /* We only get here when we need to bail out. */
115 : break;
116 : }
117 :
118 160 : 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 57 : uint_fast8_t address_size
125 34 : = cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
126 : switch (address_size)
127 : {
128 : case 8:
129 23 : cie->fde_encoding |= DW_EH_PE_udata8;
130 23 : break;
131 : case 4:
132 11 : cie->fde_encoding |= DW_EH_PE_udata4;
133 11 : break;
134 : default:
135 : free (cie);
136 : __libdw_seterrno (DWARF_E_INVALID_DWARF);
137 : return NULL;
138 : }
139 126 : }
140 :
141 : /* Save the initial instructions to be played out into initial state. */
142 160 : cie->initial_instructions = info->initial_instructions;
143 160 : cie->initial_instructions_end = info->initial_instructions_end;
144 160 : cie->initial_state = NULL;
145 :
146 : /* Add the new entry to the search tree. */
147 160 : if (tsearch (cie, &cache->cie_tree, &compare_cie) == NULL)
148 : {
149 0 : free (cie);
150 0 : __libdw_seterrno (DWARF_E_NOMEM);
151 0 : 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 17369 : __libdw_find_cie (Dwarf_CFI *cache, Dwarf_Off offset)
161 : {
162 17369 : const struct dwarf_cie cie_key = { .offset = offset };
163 17369 : struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
164 17369 : if (found != NULL)
165 17295 : return *found;
166 :
167 : /* We have not read this CIE yet. Go find it. */
168 74 : Dwarf_Off next_offset = offset;
169 : Dwarf_CFI_Entry entry;
170 148 : int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
171 148 : &cache->data->d, CFI_IS_EH (cache),
172 : offset, &next_offset, &entry);
173 74 : if (result != 0 || entry.cie.CIE_id != DW_CIE_ID_64)
174 : {
175 0 : __libdw_seterrno (DWARF_E_INVALID_DWARF);
176 0 : return NULL;
177 : }
178 :
179 : /* If this happened to be what we would have read next, notice it. */
180 74 : if (cache->next_offset == offset)
181 35 : cache->next_offset = next_offset;
182 :
183 74 : 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 86 : __libdw_intern_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info)
190 : {
191 86 : const struct dwarf_cie cie_key = { .offset = offset };
192 86 : struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie);
193 86 : if (found == NULL)
194 : /* We have not read this CIE yet. Enter it. */
195 86 : (void) intern_new_cie (cache, offset, info);
196 86 : }
|