blob: 64a2a885525237507bd544db6ce4660d553738d9 [file] [log] [blame]
Roland McGrath3c84db32009-06-24 17:41:40 -07001/* Get CFI from ELF file's exception-handling info.
Roland McGrathf95760a2010-01-07 20:24:34 -08002 Copyright (C) 2009-2010 Red Hat, Inc.
Roland McGrath3c84db32009-06-24 17:41:40 -07003 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
64static Dwarf_CFI *
65allocate_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
93static const uint8_t *
94parse_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
146static Dwarf_CFI *
147getcfi_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. */
208static Dwarf_CFI *
209getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
210{
Roland McGrathf95760a2010-01-07 20:24:34 -0800211 size_t phnum;
212 if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
213 return NULL;
Roland McGrath3c84db32009-06-24 17:41:40 -0700214
Roland McGrathf95760a2010-01-07 20:24:34 -0800215 for (size_t i = 0; i < phnum; ++i)
Roland McGrath3c84db32009-06-24 17:41:40 -0700216 {
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
229static Dwarf_CFI *
230getcfi_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". */
274static Dwarf_CFI *
275getcfi_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
312Dwarf_CFI *
313dwarf_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}
336INTDEF (dwarf_getcfi_elf)