Imported Upstream version 0.153
[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 Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif
53
54 #include <stdlib.h>
55 #include <string.h>
56 #include <assert.h>
57
58 #include "libdwP.h"
59 #include "cfi.h"
60 #include "encoded-value.h"
61 #include <dwarf.h>
62
63
64 static Dwarf_CFI *
65 allocate_cfi (Elf *elf, GElf_Addr vaddr)
66 {
67   Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
68   if (cfi == NULL)
69     {
70       __libdw_seterrno (DWARF_E_NOMEM);
71       return NULL;
72     }
73
74   cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
75   if (cfi->e_ident == NULL)
76     {
77       free (cfi);
78       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
79       return NULL;
80     }
81
82   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
83       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
84     cfi->other_byte_order = true;
85
86   cfi->frame_vaddr = vaddr;
87   cfi->textrel = 0;             /* XXX ? */
88   cfi->datarel = 0;             /* XXX ? */
89
90   return cfi;
91 }
92
93 static const uint8_t *
94 parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
95                     const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
96                     size_t *table_entries, uint8_t *table_encoding)
97 {
98   const uint8_t *h = hdr;
99
100   if (*h++ != 1)                /* version */
101     return (void *) -1l;
102
103   uint8_t eh_frame_ptr_encoding = *h++;
104   uint8_t fde_count_encoding = *h++;
105   uint8_t fde_table_encoding = *h++;
106
107   if (eh_frame_ptr_encoding == DW_EH_PE_omit)
108     return (void *) -1l;
109
110   /* Dummy used by read_encoded_value.  */
111   Elf_Data_Scn dummy_cfi_hdr_data =
112     {
113       .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
114     };
115   Dwarf_CFI dummy_cfi =
116     {
117       .e_ident = ehdr->e_ident,
118       .datarel = hdr_vaddr,
119       .frame_vaddr = hdr_vaddr,
120       .data = &dummy_cfi_hdr_data,
121     };
122
123   if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
124                                     eh_frame_vaddr)))
125     return (void *) -1l;
126
127   if (fde_count_encoding != DW_EH_PE_omit)
128     {
129       Dwarf_Word fde_count;
130       if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
131                                         &fde_count)))
132         return (void *) -1l;
133       if (fde_count != 0 && (size_t) fde_count == fde_count
134           && fde_table_encoding != DW_EH_PE_omit
135           && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
136         {
137           *table_entries = fde_count;
138           *table_encoding = fde_table_encoding;
139           return h;
140         }
141     }
142
143   return NULL;
144 }
145
146 static Dwarf_CFI *
147 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
148 {
149   if (unlikely (phdr->p_filesz < 4))
150     goto invalid;
151
152   Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
153                                          ELF_T_BYTE);
154   if (data == NULL)
155     {
156     invalid_hdr:
157     invalid:
158       /* XXX might be read error or corrupt phdr */
159       __libdw_seterrno (DWARF_E_INVALID_CFI);
160       return NULL;
161     }
162
163   Dwarf_Addr eh_frame_ptr;
164   size_t search_table_entries;
165   uint8_t search_table_encoding;
166   const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
167                                                     phdr->p_vaddr, ehdr,
168                                                     &eh_frame_ptr,
169                                                     &search_table_entries,
170                                                     &search_table_encoding);
171   if (search_table == (void *) -1l)
172     goto invalid_hdr;
173
174   Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
175   Dwarf_Word eh_frame_size = 0;
176
177   /* XXX we have no way without section headers to know the size
178      of the .eh_frame data.  Calculate the largest it might possibly be.
179      This won't be wasteful if the file is already mmap'd, but if it isn't
180      it might be quite excessive.  */
181   size_t filesize;
182   if (elf_rawfile (elf, &filesize) != NULL)
183     eh_frame_size = filesize - eh_frame_offset;
184
185   data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
186   if (data == NULL)
187     {
188       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
189       return NULL;
190     }
191   Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
192   if (cfi != NULL)
193     {
194       cfi->data = (Elf_Data_Scn *) data;
195
196       if (search_table != NULL)
197         {
198           cfi->search_table = search_table;
199           cfi->search_table_vaddr = phdr->p_vaddr;
200           cfi->search_table_encoding = search_table_encoding;
201           cfi->search_table_entries = search_table_entries;
202         }
203     }
204   return cfi;
205 }
206
207 /* Search the phdrs for PT_GNU_EH_FRAME.  */
208 static Dwarf_CFI *
209 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
210 {
211   size_t phnum;
212   if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
213     return NULL;
214
215   for (size_t i = 0; i < phnum; ++i)
216     {
217       GElf_Phdr phdr_mem;
218       GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
219       if (unlikely (phdr == NULL))
220         return NULL;
221       if (phdr->p_type == PT_GNU_EH_FRAME)
222         return getcfi_gnu_eh_frame (elf, ehdr, phdr);
223     }
224
225   __libdw_seterrno (DWARF_E_NO_DWARF);
226   return NULL;
227 }
228
229 static Dwarf_CFI *
230 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
231                      Elf_Scn *scn, GElf_Shdr *shdr,
232                      Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
233 {
234   Elf_Data *data = elf_rawdata (scn, NULL);
235   if (data == NULL)
236     {
237       __libdw_seterrno (DWARF_E_INVALID_ELF);
238       return NULL;
239     }
240   Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
241   if (cfi != NULL)
242     {
243       cfi->data = (Elf_Data_Scn *) data;
244       if (hdr_scn != NULL)
245         {
246           Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
247           if (hdr_data != NULL)
248             {
249               GElf_Addr eh_frame_vaddr;
250               cfi->search_table_vaddr = hdr_vaddr;
251               cfi->search_table
252                 = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
253                                       hdr_vaddr, ehdr, &eh_frame_vaddr,
254                                       &cfi->search_table_entries,
255                                       &cfi->search_table_encoding);
256               if (cfi->search_table == (void *) -1l)
257                 {
258                   free (cfi);
259                   /* XXX might be read error or corrupt phdr */
260                   __libdw_seterrno (DWARF_E_INVALID_CFI);
261                   return NULL;
262                 }
263
264               /* Sanity check.  */
265               if (unlikely (eh_frame_vaddr != shdr->sh_addr))
266                 cfi->search_table = NULL;
267             }
268         }
269     }
270   return cfi;
271 }
272
273 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
274 static Dwarf_CFI *
275 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
276 {
277   size_t shstrndx;
278   if (elf_getshdrstrndx (elf, &shstrndx) != 0)
279     {
280       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
281       return NULL;
282     }
283
284   if (shstrndx != 0)
285     {
286       Elf_Scn *hdr_scn = NULL;
287       GElf_Addr hdr_vaddr = 0;
288       Elf_Scn *scn = NULL;
289       while ((scn = elf_nextscn (elf, scn)) != NULL)
290         {
291           GElf_Shdr shdr_mem;
292           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
293           if (shdr == NULL)
294             continue;
295           const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
296           if (name == NULL)
297             continue;
298           if (!strcmp (name, ".eh_frame_hdr"))
299             {
300               hdr_scn = scn;
301               hdr_vaddr = shdr->sh_addr;
302             }
303           else if (!strcmp (name, ".eh_frame"))
304             return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
305                                         hdr_scn, hdr_vaddr);
306         }
307     }
308
309   return (void *) -1l;
310 }
311
312 Dwarf_CFI *
313 dwarf_getcfi_elf (elf)
314      Elf *elf;
315 {
316   if (elf_kind (elf) != ELF_K_ELF)
317     {
318       __libdw_seterrno (DWARF_E_NOELF);
319       return NULL;
320     }
321
322   GElf_Ehdr ehdr_mem;
323   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
324   if (unlikely (ehdr == NULL))
325     {
326       __libdw_seterrno (DWARF_E_INVALID_ELF);
327       return NULL;
328     }
329
330   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
331   if (result == (void *) -1l)
332     result = getcfi_phdr (elf, ehdr);
333
334   return result;
335 }
336 INTDEF (dwarf_getcfi_elf)