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