Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libdw / dwarf_getcfi_elf.c
1 /* Get CFI from ELF file's exception-handling info.
2    Copyright (C) 2009-2010 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, 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 *
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 *
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;
144   uint8_t search_table_encoding;
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 *
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 *
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 *
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             return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
284                                         hdr_scn, hdr_vaddr);
285         }
286     }
287
288   return (void *) -1l;
289 }
290
291 Dwarf_CFI *
292 dwarf_getcfi_elf (elf)
293      Elf *elf;
294 {
295   if (elf_kind (elf) != ELF_K_ELF)
296     {
297       __libdw_seterrno (DWARF_E_NOELF);
298       return NULL;
299     }
300
301   GElf_Ehdr ehdr_mem;
302   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
303   if (unlikely (ehdr == NULL))
304     {
305       __libdw_seterrno (DWARF_E_INVALID_ELF);
306       return NULL;
307     }
308
309   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
310   if (result == (void *) -1l)
311     result = getcfi_phdr (elf, ehdr);
312
313   return result;
314 }
315 INTDEF (dwarf_getcfi_elf)