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