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