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