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