1 /* Get CFI from ELF file's exception-handling info.
2 Copyright (C) 2009-2010, 2014 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 (*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 if (unlikely (phdr->p_filesz < 4))
129 goto invalid;
130
131 Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
132 ELF_T_BYTE);
133 if (data == NULL)
134 {
135 invalid_hdr:
136 invalid:
137 /* XXX might be read error or corrupt phdr */
138 __libdw_seterrno (DWARF_E_INVALID_CFI);
139 return NULL;
140 }
141
142 Dwarf_Addr eh_frame_ptr;
143 size_t search_table_entries = 0;
144 uint8_t search_table_encoding = 0;
145 const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
146 phdr->p_vaddr, ehdr,
147 &eh_frame_ptr,
148 &search_table_entries,
149 &search_table_encoding);
150 if (search_table == (void *) -1l)
151 goto invalid_hdr;
152
153 Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
154 Dwarf_Word eh_frame_size = 0;
155
156 /* XXX we have no way without section headers to know the size
157 of the .eh_frame data. Calculate the largest it might possibly be.
158 This won't be wasteful if the file is already mmap'd, but if it isn't
159 it might be quite excessive. */
160 size_t filesize;
161 if (elf_rawfile (elf, &filesize) != NULL)
162 eh_frame_size = filesize - eh_frame_offset;
163
164 data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
165 if (data == NULL)
166 {
167 __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
168 return NULL;
169 }
170 Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
171 if (cfi != NULL)
172 {
173 cfi->data = (Elf_Data_Scn *) data;
174
175 if (search_table != NULL)
176 {
177 cfi->search_table = search_table;
178 cfi->search_table_vaddr = phdr->p_vaddr;
179 cfi->search_table_encoding = search_table_encoding;
180 cfi->search_table_entries = search_table_entries;
181 }
182 }
183 return cfi;
184 }
185
186 /* Search the phdrs for PT_GNU_EH_FRAME. */
187 static Dwarf_CFI *
getcfi_phdr(Elf * elf,const GElf_Ehdr * ehdr)188 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
189 {
190 size_t phnum;
191 if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
192 return NULL;
193
194 for (size_t i = 0; i < phnum; ++i)
195 {
196 GElf_Phdr phdr_mem;
197 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
198 if (unlikely (phdr == NULL))
199 return NULL;
200 if (phdr->p_type == PT_GNU_EH_FRAME)
201 return getcfi_gnu_eh_frame (elf, ehdr, phdr);
202 }
203
204 __libdw_seterrno (DWARF_E_NO_DWARF);
205 return NULL;
206 }
207
208 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)209 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
210 Elf_Scn *scn, GElf_Shdr *shdr,
211 Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
212 {
213 Elf_Data *data = elf_rawdata (scn, NULL);
214 if (data == NULL)
215 {
216 __libdw_seterrno (DWARF_E_INVALID_ELF);
217 return NULL;
218 }
219 Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
220 if (cfi != NULL)
221 {
222 cfi->data = (Elf_Data_Scn *) data;
223 if (hdr_scn != NULL)
224 {
225 Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
226 if (hdr_data != NULL)
227 {
228 GElf_Addr eh_frame_vaddr;
229 cfi->search_table_vaddr = hdr_vaddr;
230 cfi->search_table
231 = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
232 hdr_vaddr, ehdr, &eh_frame_vaddr,
233 &cfi->search_table_entries,
234 &cfi->search_table_encoding);
235 if (cfi->search_table == (void *) -1l)
236 {
237 free (cfi);
238 /* XXX might be read error or corrupt phdr */
239 __libdw_seterrno (DWARF_E_INVALID_CFI);
240 return NULL;
241 }
242
243 /* Sanity check. */
244 if (unlikely (eh_frame_vaddr != shdr->sh_addr))
245 cfi->search_table = NULL;
246 }
247 }
248 }
249 return cfi;
250 }
251
252 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr". */
253 static Dwarf_CFI *
getcfi_shdr(Elf * elf,const GElf_Ehdr * ehdr)254 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
255 {
256 size_t shstrndx;
257 if (elf_getshdrstrndx (elf, &shstrndx) != 0)
258 {
259 __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
260 return NULL;
261 }
262
263 if (shstrndx != 0)
264 {
265 Elf_Scn *hdr_scn = NULL;
266 GElf_Addr hdr_vaddr = 0;
267 Elf_Scn *scn = NULL;
268 while ((scn = elf_nextscn (elf, scn)) != NULL)
269 {
270 GElf_Shdr shdr_mem;
271 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
272 if (shdr == NULL)
273 continue;
274 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
275 if (name == NULL)
276 continue;
277 if (!strcmp (name, ".eh_frame_hdr"))
278 {
279 hdr_scn = scn;
280 hdr_vaddr = shdr->sh_addr;
281 }
282 else if (!strcmp (name, ".eh_frame"))
283 {
284 if (shdr->sh_type == SHT_PROGBITS)
285 return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
286 hdr_scn, hdr_vaddr);
287 else
288 return NULL;
289 }
290 }
291 }
292
293 return (void *) -1l;
294 }
295
296 Dwarf_CFI *
dwarf_getcfi_elf(elf)297 dwarf_getcfi_elf (elf)
298 Elf *elf;
299 {
300 if (elf_kind (elf) != ELF_K_ELF)
301 {
302 __libdw_seterrno (DWARF_E_NOELF);
303 return NULL;
304 }
305
306 GElf_Ehdr ehdr_mem;
307 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
308 if (unlikely (ehdr == NULL))
309 {
310 __libdw_seterrno (DWARF_E_INVALID_ELF);
311 return NULL;
312 }
313
314 Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
315 if (result == (void *) -1l)
316 result = getcfi_phdr (elf, ehdr);
317
318 return result;
319 }
320 INTDEF (dwarf_getcfi_elf)
321