/src/elfutils/libdw/dwarf_getcfi_elf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Get CFI from ELF file's exception-handling info. |
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 <stdlib.h> |
34 | | #include <string.h> |
35 | | #include <assert.h> |
36 | | |
37 | | #include "libdwP.h" |
38 | | #include "cfi.h" |
39 | | #include "encoded-value.h" |
40 | | #include <dwarf.h> |
41 | | |
42 | | |
43 | | static Dwarf_CFI * |
44 | | allocate_cfi (Elf *elf, const GElf_Ehdr *ehdr, GElf_Addr vaddr) |
45 | 0 | { |
46 | 0 | Dwarf_CFI *cfi = calloc (1, sizeof *cfi); |
47 | 0 | if (cfi == NULL) |
48 | 0 | { |
49 | 0 | __libdw_seterrno (DWARF_E_NOMEM); |
50 | 0 | return NULL; |
51 | 0 | } |
52 | | |
53 | 0 | cfi->e_ident = (unsigned char *) elf_getident (elf, NULL); |
54 | 0 | if (cfi->e_ident == NULL) |
55 | 0 | { |
56 | 0 | free (cfi); |
57 | 0 | __libdw_seterrno (DWARF_E_GETEHDR_ERROR); |
58 | 0 | return NULL; |
59 | 0 | } |
60 | | |
61 | 0 | cfi->e_machine = ehdr->e_machine; |
62 | |
|
63 | 0 | if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB) |
64 | 0 | || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB)) |
65 | 0 | cfi->other_byte_order = true; |
66 | |
|
67 | 0 | cfi->frame_vaddr = vaddr; |
68 | 0 | cfi->textrel = 0; /* XXX ? */ |
69 | 0 | cfi->datarel = 0; /* XXX ? */ |
70 | |
|
71 | 0 | return cfi; |
72 | 0 | } |
73 | | |
74 | | static const uint8_t * |
75 | | parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr, |
76 | | const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr, |
77 | | size_t *table_entries, uint8_t *table_encoding) |
78 | 0 | { |
79 | 0 | const uint8_t *h = hdr; |
80 | |
|
81 | 0 | if (hdr_size < 4 || *h++ != 1) /* version */ |
82 | 0 | return (void *) -1l; |
83 | | |
84 | 0 | uint8_t eh_frame_ptr_encoding = *h++; |
85 | 0 | uint8_t fde_count_encoding = *h++; |
86 | 0 | uint8_t fde_table_encoding = *h++; |
87 | |
|
88 | 0 | if (eh_frame_ptr_encoding == DW_EH_PE_omit) |
89 | 0 | return (void *) -1l; |
90 | | |
91 | | /* Dummy used by read_encoded_value. */ |
92 | 0 | Elf_Data_Scn dummy_cfi_hdr_data = |
93 | 0 | { |
94 | 0 | .d = { .d_buf = (void *) hdr, .d_size = hdr_size } |
95 | 0 | }; |
96 | 0 | Dwarf_CFI dummy_cfi = |
97 | 0 | { |
98 | 0 | .e_ident = ehdr->e_ident, |
99 | 0 | .datarel = hdr_vaddr, |
100 | 0 | .frame_vaddr = hdr_vaddr, |
101 | 0 | .data = &dummy_cfi_hdr_data, |
102 | 0 | }; |
103 | |
|
104 | 0 | if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h, |
105 | 0 | eh_frame_vaddr))) |
106 | 0 | return (void *) -1l; |
107 | | |
108 | 0 | if (fde_count_encoding != DW_EH_PE_omit) |
109 | 0 | { |
110 | 0 | Dwarf_Word fde_count; |
111 | 0 | if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h, |
112 | 0 | &fde_count))) |
113 | 0 | return (void *) -1l; |
114 | 0 | if (fde_count != 0 && (size_t) fde_count == fde_count |
115 | 0 | && fde_table_encoding != DW_EH_PE_omit |
116 | 0 | && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128) |
117 | 0 | { |
118 | 0 | *table_entries = fde_count; |
119 | 0 | *table_encoding = fde_table_encoding; |
120 | 0 | return h; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 0 | return NULL; |
125 | 0 | } |
126 | | |
127 | | static Dwarf_CFI * |
128 | | getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr) |
129 | 0 | { |
130 | 0 | Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, |
131 | 0 | ELF_T_BYTE); |
132 | 0 | if (data == NULL || data->d_buf == NULL) |
133 | 0 | { |
134 | 0 | invalid_hdr: |
135 | | /* XXX might be read error or corrupt phdr */ |
136 | 0 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | 0 | size_t vsize, dmax; |
141 | 0 | Dwarf_Addr eh_frame_ptr; |
142 | 0 | size_t search_table_entries = 0; |
143 | 0 | uint8_t search_table_encoding = 0; |
144 | 0 | const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz, |
145 | 0 | phdr->p_vaddr, ehdr, |
146 | 0 | &eh_frame_ptr, |
147 | 0 | &search_table_entries, |
148 | 0 | &search_table_encoding); |
149 | | |
150 | | /* Make sure there is enough room for the entries in the table, |
151 | | each entry consists of 2 encoded values. */ |
152 | 0 | vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding, |
153 | 0 | NULL); |
154 | 0 | dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf); |
155 | 0 | if (unlikely (search_table == (void *) -1l |
156 | 0 | || vsize == 0 |
157 | 0 | || search_table_entries > (dmax / vsize) / 2)) |
158 | 0 | goto invalid_hdr; |
159 | | |
160 | 0 | Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset; |
161 | 0 | Dwarf_Word eh_frame_size = 0; |
162 | | |
163 | | /* XXX we have no way without section headers to know the size |
164 | | of the .eh_frame data. Calculate the largest it might possibly be. |
165 | | This won't be wasteful if the file is already mmap'd, but if it isn't |
166 | | it might be quite excessive. */ |
167 | 0 | size_t filesize; |
168 | 0 | if (elf_rawfile (elf, &filesize) != NULL) |
169 | 0 | eh_frame_size = filesize - eh_frame_offset; |
170 | |
|
171 | 0 | data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE); |
172 | 0 | if (data == NULL) |
173 | 0 | { |
174 | 0 | __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */ |
175 | 0 | return NULL; |
176 | 0 | } |
177 | 0 | Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, eh_frame_ptr); |
178 | 0 | if (cfi != NULL) |
179 | 0 | { |
180 | 0 | cfi->data = (Elf_Data_Scn *) data; |
181 | |
|
182 | 0 | if (search_table != NULL) |
183 | 0 | { |
184 | 0 | cfi->search_table = search_table; |
185 | 0 | cfi->search_table_len = phdr->p_filesz; |
186 | 0 | cfi->search_table_vaddr = phdr->p_vaddr; |
187 | 0 | cfi->search_table_encoding = search_table_encoding; |
188 | 0 | cfi->search_table_entries = search_table_entries; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | return cfi; |
192 | 0 | } |
193 | | |
194 | | /* Search the phdrs for PT_GNU_EH_FRAME. */ |
195 | | static Dwarf_CFI * |
196 | | getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr) |
197 | 0 | { |
198 | 0 | size_t phnum; |
199 | 0 | if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) |
200 | 0 | return NULL; |
201 | | |
202 | 0 | for (size_t i = 0; i < phnum; ++i) |
203 | 0 | { |
204 | 0 | GElf_Phdr phdr_mem; |
205 | 0 | GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); |
206 | 0 | if (unlikely (phdr == NULL)) |
207 | 0 | return NULL; |
208 | 0 | if (phdr->p_type == PT_GNU_EH_FRAME) |
209 | 0 | return getcfi_gnu_eh_frame (elf, ehdr, phdr); |
210 | 0 | } |
211 | | |
212 | 0 | __libdw_seterrno (DWARF_E_NO_DWARF); |
213 | 0 | return NULL; |
214 | 0 | } |
215 | | |
216 | | static Dwarf_CFI * |
217 | | getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, |
218 | | Elf_Scn *scn, GElf_Shdr *shdr, |
219 | | Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr) |
220 | 0 | { |
221 | 0 | Elf_Data *data = elf_rawdata (scn, NULL); |
222 | 0 | if (data == NULL || data->d_buf == NULL) |
223 | 0 | { |
224 | 0 | __libdw_seterrno (DWARF_E_INVALID_ELF); |
225 | 0 | return NULL; |
226 | 0 | } |
227 | 0 | Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, shdr->sh_addr); |
228 | 0 | if (cfi != NULL) |
229 | 0 | { |
230 | 0 | cfi->data = (Elf_Data_Scn *) data; |
231 | 0 | if (hdr_scn != NULL) |
232 | 0 | { |
233 | 0 | Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL); |
234 | 0 | if (hdr_data != NULL && hdr_data->d_buf != NULL) |
235 | 0 | { |
236 | 0 | size_t vsize, dmax; |
237 | 0 | GElf_Addr eh_frame_vaddr; |
238 | 0 | cfi->search_table_vaddr = hdr_vaddr; |
239 | 0 | cfi->search_table |
240 | 0 | = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size, |
241 | 0 | hdr_vaddr, ehdr, &eh_frame_vaddr, |
242 | 0 | &cfi->search_table_entries, |
243 | 0 | &cfi->search_table_encoding); |
244 | 0 | cfi->search_table_len = hdr_data->d_size; |
245 | | |
246 | | /* Make sure there is enough room for the entries in the table, |
247 | | each entry consists of 2 encoded values. */ |
248 | 0 | vsize = encoded_value_size (hdr_data, ehdr->e_ident, |
249 | 0 | cfi->search_table_encoding, NULL); |
250 | 0 | dmax = hdr_data->d_size - (cfi->search_table |
251 | 0 | - (const uint8_t *) hdr_data->d_buf); |
252 | 0 | if (unlikely (cfi->search_table == (void *) -1l |
253 | 0 | || vsize == 0 |
254 | 0 | || cfi->search_table_entries > (dmax / vsize) / 2)) |
255 | 0 | { |
256 | 0 | free (cfi); |
257 | | /* XXX might be read error or corrupt phdr */ |
258 | 0 | __libdw_seterrno (DWARF_E_INVALID_CFI); |
259 | 0 | return NULL; |
260 | 0 | } |
261 | | |
262 | | /* Sanity check. */ |
263 | 0 | if (unlikely (eh_frame_vaddr != shdr->sh_addr)) |
264 | 0 | cfi->search_table = NULL; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | return cfi; |
269 | 0 | } |
270 | | |
271 | | /* Search for the sections named ".eh_frame" and ".eh_frame_hdr". */ |
272 | | static Dwarf_CFI * |
273 | | getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr) |
274 | 0 | { |
275 | 0 | size_t shstrndx; |
276 | 0 | if (elf_getshdrstrndx (elf, &shstrndx) != 0) |
277 | 0 | { |
278 | 0 | __libdw_seterrno (DWARF_E_GETEHDR_ERROR); |
279 | 0 | return NULL; |
280 | 0 | } |
281 | | |
282 | 0 | if (shstrndx != 0) |
283 | 0 | { |
284 | 0 | Elf_Scn *hdr_scn = NULL; |
285 | 0 | GElf_Addr hdr_vaddr = 0; |
286 | 0 | Elf_Scn *scn = NULL; |
287 | 0 | while ((scn = elf_nextscn (elf, scn)) != NULL) |
288 | 0 | { |
289 | 0 | GElf_Shdr shdr_mem; |
290 | 0 | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
291 | 0 | if (shdr == NULL) |
292 | 0 | continue; |
293 | 0 | const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); |
294 | 0 | if (name == NULL) |
295 | 0 | continue; |
296 | 0 | if (!strcmp (name, ".eh_frame_hdr")) |
297 | 0 | { |
298 | 0 | hdr_scn = scn; |
299 | 0 | hdr_vaddr = shdr->sh_addr; |
300 | 0 | } |
301 | 0 | else if (!strcmp (name, ".eh_frame")) |
302 | 0 | { |
303 | 0 | if (shdr->sh_type != SHT_NOBITS) |
304 | 0 | return getcfi_scn_eh_frame (elf, ehdr, scn, shdr, |
305 | 0 | hdr_scn, hdr_vaddr); |
306 | 0 | else |
307 | 0 | return NULL; |
308 | 0 | } |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | return (void *) -1l; |
313 | 0 | } |
314 | | |
315 | | Dwarf_CFI * |
316 | | dwarf_getcfi_elf (Elf *elf) |
317 | 0 | { |
318 | 0 | if (elf_kind (elf) != ELF_K_ELF) |
319 | 0 | { |
320 | 0 | __libdw_seterrno (DWARF_E_NOELF); |
321 | 0 | return NULL; |
322 | 0 | } |
323 | | |
324 | 0 | GElf_Ehdr ehdr_mem; |
325 | 0 | GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); |
326 | 0 | if (unlikely (ehdr == NULL)) |
327 | 0 | { |
328 | 0 | __libdw_seterrno (DWARF_E_INVALID_ELF); |
329 | 0 | return NULL; |
330 | 0 | } |
331 | | |
332 | 0 | Dwarf_CFI *result = getcfi_shdr (elf, ehdr); |
333 | 0 | if (result == (void *) -1l) |
334 | 0 | result = getcfi_phdr (elf, ehdr); |
335 | |
|
336 | 0 | return result; |
337 | 0 | } |
338 | | INTDEF (dwarf_getcfi_elf) |