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