/src/elfutils/libdw/fde.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* FDE reading. |
2 | | Copyright (C) 2009-2010, 2014, 2015 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 "eu-search.h" |
35 | | #include <stdlib.h> |
36 | | |
37 | | #include "encoded-value.h" |
38 | | |
39 | | static int |
40 | | compare_fde (const void *a, const void *b) |
41 | 0 | { |
42 | 0 | const struct dwarf_fde *fde1 = a; |
43 | 0 | const struct dwarf_fde *fde2 = b; |
44 | | |
45 | | /* Find out which of the two arguments is the search value. |
46 | | It has end offset 0. */ |
47 | 0 | if (fde1->end == 0) |
48 | 0 | { |
49 | 0 | if (fde1->start < fde2->start) |
50 | 0 | return -1; |
51 | 0 | if (fde1->start >= fde2->end) |
52 | 0 | return 1; |
53 | 0 | } |
54 | 0 | else |
55 | 0 | { |
56 | 0 | if (fde2->start < fde1->start) |
57 | 0 | return 1; |
58 | 0 | if (fde2->start >= fde1->end) |
59 | 0 | return -1; |
60 | 0 | } |
61 | | |
62 | 0 | return 0; |
63 | 0 | } |
64 | | |
65 | | static struct dwarf_fde * |
66 | | intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) |
67 | 0 | { |
68 | | /* Look up the new entry's CIE. */ |
69 | 0 | struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer); |
70 | 0 | if (cie == NULL) |
71 | 0 | return (void *) -1l; |
72 | | |
73 | 0 | struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde)); |
74 | 0 | if (fde == NULL) |
75 | 0 | { |
76 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
77 | 0 | return NULL; |
78 | 0 | } |
79 | | |
80 | 0 | fde->instructions = entry->start; |
81 | 0 | fde->instructions_end = entry->end; |
82 | 0 | if (unlikely (read_encoded_value (cache, cie->fde_encoding, |
83 | 0 | &fde->instructions, &fde->start)) |
84 | 0 | || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f, |
85 | 0 | &fde->instructions, &fde->end))) |
86 | 0 | { |
87 | 0 | free (fde); |
88 | 0 | __libdw_seterrno (DWARF_E_INVALID_DWARF); |
89 | 0 | return NULL; |
90 | 0 | } |
91 | 0 | fde->end += fde->start; |
92 | | |
93 | | /* Make sure the fde actually covers a real code range. */ |
94 | 0 | if (fde->start >= fde->end) |
95 | 0 | { |
96 | 0 | free (fde); |
97 | 0 | return (void *) -1; |
98 | 0 | } |
99 | | |
100 | 0 | fde->cie = cie; |
101 | |
|
102 | 0 | if (cie->sized_augmentation_data) |
103 | 0 | { |
104 | | /* The CIE augmentation says the FDE has a DW_FORM_block |
105 | | before its actual instruction stream. */ |
106 | 0 | Dwarf_Word len; |
107 | 0 | if (fde->instructions >= fde->instructions_end) |
108 | 0 | goto invalid; |
109 | 0 | get_uleb128 (len, fde->instructions, fde->instructions_end); |
110 | 0 | if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len) |
111 | 0 | { |
112 | 0 | invalid: |
113 | 0 | free (fde); |
114 | 0 | __libdw_seterrno (DWARF_E_INVALID_DWARF); |
115 | 0 | return NULL; |
116 | 0 | } |
117 | 0 | fde->instructions += len; |
118 | 0 | } |
119 | 0 | else |
120 | | /* We had to understand all of the CIE augmentation string. |
121 | | We've recorded the number of data bytes in FDEs. */ |
122 | 0 | fde->instructions += cie->fde_augmentation_data_size; |
123 | | |
124 | | /* Add the new entry to the search tree. */ |
125 | 0 | struct dwarf_fde **tres = eu_tsearch_nolock (fde, &cache->fde_tree, |
126 | 0 | &compare_fde); |
127 | 0 | if (tres == NULL) |
128 | 0 | { |
129 | 0 | free (fde); |
130 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
131 | 0 | return NULL; |
132 | 0 | } |
133 | 0 | else if (*tres != fde) |
134 | 0 | { |
135 | | /* There is already an FDE in the cache that covers the same |
136 | | address range. That is odd. Ignore this FDE. And just use |
137 | | the one in the cache for consistency. */ |
138 | 0 | free (fde); |
139 | 0 | return *tres; |
140 | 0 | } |
141 | | |
142 | 0 | return fde; |
143 | 0 | } |
144 | | |
145 | | /* Look for an FDE by its offset in the section. |
146 | | Should be called with cache->lock held. */ |
147 | | static struct dwarf_fde * |
148 | | __libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset) |
149 | 0 | { |
150 | 0 | Dwarf_CFI_Entry entry; |
151 | 0 | Dwarf_Off next_offset; |
152 | 0 | int result = INTUSE(dwarf_next_cfi) (cache->e_ident, |
153 | 0 | &cache->data->d, CFI_IS_EH (cache), |
154 | 0 | offset, &next_offset, &entry); |
155 | 0 | if (result != 0) |
156 | 0 | { |
157 | 0 | if (result > 0) |
158 | 0 | invalid: |
159 | 0 | __libdw_seterrno (DWARF_E_INVALID_DWARF); |
160 | 0 | return NULL; |
161 | 0 | } |
162 | | |
163 | 0 | if (unlikely (dwarf_cfi_cie_p (&entry))) |
164 | 0 | goto invalid; |
165 | | |
166 | | /* We have a new FDE to consider. */ |
167 | 0 | struct dwarf_fde *fde = intern_fde (cache, &entry.fde); |
168 | 0 | if (fde == (void *) -1l || fde == NULL) |
169 | 0 | return NULL; |
170 | | |
171 | | /* If this happened to be what we would have read next, notice it. */ |
172 | 0 | if (cache->next_offset == offset) |
173 | 0 | cache->next_offset = next_offset; |
174 | |
|
175 | 0 | return fde; |
176 | 0 | } |
177 | | |
178 | | /* Use a binary search table in .eh_frame_hdr format, yield an FDE offset. */ |
179 | | static Dwarf_Off |
180 | | binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address) |
181 | 0 | { |
182 | 0 | const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident, |
183 | 0 | cache->search_table_encoding, |
184 | 0 | NULL); |
185 | 0 | if (unlikely (size == 0)) |
186 | 0 | return (Dwarf_Off) -1l; |
187 | | |
188 | | /* Dummy used by read_encoded_value. */ |
189 | 0 | Elf_Data_Scn dummy_cfi_hdr_data = |
190 | 0 | { |
191 | 0 | .d = { .d_buf = (void *) cache->search_table, |
192 | 0 | .d_size = cache->search_table_len } |
193 | 0 | }; |
194 | |
|
195 | 0 | Dwarf_CFI dummy_cfi = |
196 | 0 | { |
197 | 0 | .e_ident = cache->e_ident, |
198 | 0 | .datarel = cache->search_table_vaddr, |
199 | 0 | .frame_vaddr = cache->search_table_vaddr, |
200 | 0 | .data = &dummy_cfi_hdr_data |
201 | 0 | }; |
202 | |
|
203 | 0 | size_t l = 0, u = cache->search_table_entries; |
204 | 0 | while (l < u) |
205 | 0 | { |
206 | 0 | size_t idx = (l + u) / 2; |
207 | | |
208 | | /* Max idx * size is checked against search_table len when |
209 | | loading eh_frame_hdr. */ |
210 | 0 | const uint8_t *p = &cache->search_table[idx * size]; |
211 | 0 | Dwarf_Addr start; |
212 | 0 | if (unlikely (read_encoded_value (&dummy_cfi, |
213 | 0 | cache->search_table_encoding, &p, |
214 | 0 | &start))) |
215 | 0 | break; |
216 | 0 | if (address < start) |
217 | 0 | u = idx; |
218 | 0 | else |
219 | 0 | { |
220 | 0 | l = idx + 1; |
221 | |
|
222 | 0 | Dwarf_Addr fde; |
223 | 0 | if (unlikely (read_encoded_value (&dummy_cfi, |
224 | 0 | cache->search_table_encoding, &p, |
225 | 0 | &fde))) |
226 | 0 | break; |
227 | | |
228 | | /* If this is the last entry, its upper bound is assumed to be |
229 | | the end of the module. |
230 | | XXX really should be end of containing PT_LOAD segment */ |
231 | 0 | if (l < cache->search_table_entries) |
232 | 0 | { |
233 | | /* Look at the start address in the following entry. */ |
234 | 0 | Dwarf_Addr end; |
235 | 0 | if (unlikely (read_encoded_value |
236 | 0 | (&dummy_cfi, cache->search_table_encoding, &p, |
237 | 0 | &end))) |
238 | 0 | break; |
239 | 0 | if (address >= end) |
240 | 0 | continue; |
241 | 0 | } |
242 | | |
243 | 0 | return fde - cache->frame_vaddr; |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | 0 | return (Dwarf_Off) -1l; |
248 | 0 | } |
249 | | |
250 | | struct dwarf_fde * |
251 | | internal_function |
252 | | __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address) |
253 | 0 | { |
254 | | /* Look for a cached FDE covering this address. */ |
255 | |
|
256 | 0 | const struct dwarf_fde fde_key = { .start = address, .end = 0 }; |
257 | 0 | struct dwarf_fde **found = eu_tfind_nolock (&fde_key, &cache->fde_tree, |
258 | 0 | &compare_fde); |
259 | 0 | if (found != NULL) |
260 | 0 | return *found; |
261 | | |
262 | | /* Use .eh_frame_hdr binary search table if possible. */ |
263 | 0 | if (cache->search_table != NULL) |
264 | 0 | { |
265 | 0 | Dwarf_Off offset = binary_search_fde (cache, address); |
266 | 0 | if (offset == (Dwarf_Off) -1l) |
267 | 0 | goto no_match; |
268 | 0 | struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset); |
269 | 0 | if (likely (fde != NULL)) |
270 | 0 | { |
271 | | /* Sanity check the address range. */ |
272 | 0 | if (unlikely (address < fde->start)) |
273 | 0 | { |
274 | 0 | __libdw_seterrno (DWARF_E_INVALID_DWARF); |
275 | 0 | return NULL; |
276 | 0 | } |
277 | | /* .eh_frame_hdr does not indicate length covered by FDE. */ |
278 | 0 | if (unlikely (address >= fde->end)) |
279 | 0 | goto no_match; |
280 | 0 | } |
281 | 0 | return fde; |
282 | 0 | } |
283 | | |
284 | | /* It's not there. Read more CFI entries until we find it. */ |
285 | 0 | while (1) |
286 | 0 | { |
287 | 0 | Dwarf_Off last_offset = cache->next_offset; |
288 | 0 | Dwarf_CFI_Entry entry; |
289 | 0 | int result = INTUSE(dwarf_next_cfi) (cache->e_ident, |
290 | 0 | &cache->data->d, CFI_IS_EH (cache), |
291 | 0 | last_offset, &cache->next_offset, |
292 | 0 | &entry); |
293 | 0 | if (result > 0) |
294 | 0 | break; |
295 | 0 | if (result < 0) |
296 | 0 | { |
297 | 0 | if (cache->next_offset == last_offset) |
298 | | /* We couldn't progress past the bogus FDE. */ |
299 | 0 | break; |
300 | | /* Skip the loser and look at the next entry. */ |
301 | 0 | continue; |
302 | 0 | } |
303 | | |
304 | 0 | if (dwarf_cfi_cie_p (&entry)) |
305 | 0 | { |
306 | | /* This is a CIE, not an FDE. We eagerly intern these |
307 | | because the next FDE will usually refer to this CIE. */ |
308 | 0 | __libdw_intern_cie (cache, last_offset, &entry.cie); |
309 | 0 | continue; |
310 | 0 | } |
311 | | |
312 | | /* We have a new FDE to consider. */ |
313 | 0 | struct dwarf_fde *fde = intern_fde (cache, &entry.fde); |
314 | |
|
315 | 0 | if (fde == (void *) -1l) /* Bad FDE, but we can keep looking. */ |
316 | 0 | continue; |
317 | | |
318 | 0 | if (fde == NULL) /* Bad data. */ |
319 | 0 | return NULL; |
320 | | |
321 | | /* Is this the one we're looking for? */ |
322 | 0 | if (fde->start <= address && fde->end > address) |
323 | 0 | return fde; |
324 | 0 | } |
325 | | |
326 | 0 | no_match: |
327 | | /* We found no FDE covering this address. */ |
328 | 0 | __libdw_seterrno (DWARF_E_NO_MATCH); |
329 | 0 | return NULL; |
330 | 0 | } |