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 0 : allocate_cfi (Elf *elf, const GElf_Ehdr *ehdr, GElf_Addr vaddr)
45 : {
46 0 : Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
47 0 : if (cfi == NULL)
48 : {
49 0 : __libdw_seterrno (DWARF_E_NOMEM);
50 0 : return NULL;
51 : }
52 :
53 0 : cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
54 0 : 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 0 : cfi->e_machine = ehdr->e_machine;
62 :
63 0 : if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
64 : || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
65 0 : cfi->other_byte_order = true;
66 :
67 0 : cfi->frame_vaddr = vaddr;
68 0 : cfi->textrel = 0; /* XXX ? */
69 0 : cfi->datarel = 0; /* XXX ? */
70 :
71 0 : return cfi;
72 : }
73 :
74 : static const uint8_t *
75 67 : 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 67 : const uint8_t *h = hdr;
80 :
81 67 : if (hdr_size < 4 || *h++ != 1) /* version */
82 0 : return (void *) -1l;
83 :
84 67 : uint8_t eh_frame_ptr_encoding = *h++;
85 67 : uint8_t fde_count_encoding = *h++;
86 67 : uint8_t fde_table_encoding = *h++;
87 :
88 67 : if (eh_frame_ptr_encoding == DW_EH_PE_omit)
89 : return (void *) -1l;
90 :
91 : /* Dummy used by read_encoded_value. */
92 67 : Elf_Data_Scn dummy_cfi_hdr_data =
93 : {
94 : .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
95 : };
96 134 : Dwarf_CFI dummy_cfi =
97 : {
98 67 : .e_ident = ehdr->e_ident,
99 : .datarel = hdr_vaddr,
100 : .frame_vaddr = hdr_vaddr,
101 : .data = &dummy_cfi_hdr_data,
102 : };
103 :
104 67 : if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
105 : eh_frame_vaddr)))
106 : return (void *) -1l;
107 :
108 67 : if (fde_count_encoding != DW_EH_PE_omit)
109 : {
110 67 : Dwarf_Word fde_count;
111 67 : if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
112 : &fde_count)))
113 67 : return (void *) -1l;
114 67 : if (fde_count != 0 && (size_t) fde_count == fde_count
115 67 : && fde_table_encoding != DW_EH_PE_omit
116 67 : && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
117 : {
118 67 : *table_entries = fde_count;
119 67 : *table_encoding = fde_table_encoding;
120 67 : 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 : 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 1 : getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
197 : {
198 1 : size_t phnum;
199 1 : if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
200 : return NULL;
201 :
202 7 : 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 0 : __libdw_seterrno (DWARF_E_NO_DWARF);
213 0 : return NULL;
214 : }
215 :
216 : static Dwarf_CFI *
217 0 : 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 0 : Elf_Data *data = elf_rawdata (scn, NULL);
222 0 : if (data == NULL || data->d_buf == NULL)
223 : {
224 0 : __libdw_seterrno (DWARF_E_INVALID_ELF);
225 0 : return NULL;
226 : }
227 0 : Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, shdr->sh_addr);
228 0 : if (cfi != NULL)
229 : {
230 0 : cfi->data = (Elf_Data_Scn *) data;
231 0 : if (hdr_scn != NULL)
232 : {
233 0 : Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
234 0 : if (hdr_data != NULL && hdr_data->d_buf != NULL)
235 : {
236 0 : size_t vsize, dmax;
237 0 : GElf_Addr eh_frame_vaddr;
238 0 : cfi->search_table_vaddr = hdr_vaddr;
239 0 : cfi->search_table
240 0 : = 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 0 : 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 0 : vsize = encoded_value_size (hdr_data, ehdr->e_ident,
249 0 : cfi->search_table_encoding, NULL);
250 0 : dmax = hdr_data->d_size - (cfi->search_table
251 0 : - (const uint8_t *) hdr_data->d_buf);
252 0 : 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 0 : 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 92 : getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
274 : {
275 92 : size_t shstrndx;
276 92 : if (elf_getshdrstrndx (elf, &shstrndx) != 0)
277 : {
278 0 : __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
279 0 : return NULL;
280 : }
281 :
282 92 : if (shstrndx != 0)
283 : {
284 : Elf_Scn *hdr_scn = NULL;
285 : GElf_Addr hdr_vaddr = 0;
286 : Elf_Scn *scn = NULL;
287 1600 : while ((scn = elf_nextscn (elf, scn)) != NULL)
288 : {
289 1600 : GElf_Shdr shdr_mem;
290 1600 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
291 1600 : if (shdr == NULL)
292 0 : continue;
293 1600 : const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
294 1600 : if (name == NULL)
295 : continue;
296 1600 : if (!strcmp (name, ".eh_frame_hdr"))
297 : {
298 74 : hdr_scn = scn;
299 74 : hdr_vaddr = shdr->sh_addr;
300 : }
301 1526 : else if (!strcmp (name, ".eh_frame"))
302 : {
303 91 : if (shdr->sh_type != SHT_NOBITS)
304 91 : 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 92 : dwarf_getcfi_elf (Elf *elf)
317 : {
318 92 : if (elf_kind (elf) != ELF_K_ELF)
319 : {
320 0 : __libdw_seterrno (DWARF_E_NOELF);
321 0 : return NULL;
322 : }
323 :
324 92 : GElf_Ehdr ehdr_mem;
325 92 : GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
326 92 : if (unlikely (ehdr == NULL))
327 : {
328 0 : __libdw_seterrno (DWARF_E_INVALID_ELF);
329 0 : return NULL;
330 : }
331 :
332 92 : Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
333 92 : if (result == (void *) -1l)
334 1 : result = getcfi_phdr (elf, ehdr);
335 :
336 : return result;
337 : }
338 : INTDEF (dwarf_getcfi_elf)
|