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.
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.
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.
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.
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.
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>. */
60 #include "encoded-value.h"
65 allocate_cfi (Elf *elf, GElf_Addr vaddr)
67 Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
70 __libdw_seterrno (DWARF_E_NOMEM);
74 cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
75 if (cfi->e_ident == NULL)
78 __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
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;
86 cfi->frame_vaddr = vaddr;
87 cfi->textrel = 0; /* XXX ? */
88 cfi->datarel = 0; /* XXX ? */
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)
98 const uint8_t *h = hdr;
100 if (*h++ != 1) /* version */
103 uint8_t eh_frame_ptr_encoding = *h++;
104 uint8_t fde_count_encoding = *h++;
105 uint8_t fde_table_encoding = *h++;
107 if (eh_frame_ptr_encoding == DW_EH_PE_omit)
110 /* Dummy used by read_encoded_value. */
111 Elf_Data_Scn dummy_cfi_hdr_data =
113 .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
115 Dwarf_CFI dummy_cfi =
117 .e_ident = ehdr->e_ident,
118 .datarel = hdr_vaddr,
119 .frame_vaddr = hdr_vaddr,
120 .data = &dummy_cfi_hdr_data,
123 if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
127 if (fde_count_encoding != DW_EH_PE_omit)
129 Dwarf_Word fde_count;
130 if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
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)
137 *table_entries = fde_count;
138 *table_encoding = fde_table_encoding;
147 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
149 if (unlikely (phdr->p_filesz < 4))
152 Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
158 /* XXX might be read error or corrupt phdr */
159 __libdw_seterrno (DWARF_E_INVALID_CFI);
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,
169 &search_table_entries,
170 &search_table_encoding);
171 if (search_table == (void *) -1l)
174 Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
175 Dwarf_Word eh_frame_size = 0;
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. */
182 if (elf_rawfile (elf, &filesize) != NULL)
183 eh_frame_size = filesize - eh_frame_offset;
185 data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
188 __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
191 Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
194 cfi->data = (Elf_Data_Scn *) data;
196 if (search_table != NULL)
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;
207 /* Search the phdrs for PT_GNU_EH_FRAME. */
209 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
212 if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
215 for (size_t i = 0; i < phnum; ++i)
218 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
219 if (unlikely (phdr == NULL))
221 if (phdr->p_type == PT_GNU_EH_FRAME)
222 return getcfi_gnu_eh_frame (elf, ehdr, phdr);
225 __libdw_seterrno (DWARF_E_NO_DWARF);
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)
234 Elf_Data *data = elf_rawdata (scn, NULL);
237 __libdw_seterrno (DWARF_E_INVALID_ELF);
240 Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
243 cfi->data = (Elf_Data_Scn *) data;
246 Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
247 if (hdr_data != NULL)
249 GElf_Addr eh_frame_vaddr;
250 cfi->search_table_vaddr = hdr_vaddr;
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)
259 /* XXX might be read error or corrupt phdr */
260 __libdw_seterrno (DWARF_E_INVALID_CFI);
265 if (unlikely (eh_frame_vaddr != shdr->sh_addr))
266 cfi->search_table = NULL;
273 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr". */
275 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
278 if (elf_getshdrstrndx (elf, &shstrndx) != 0)
280 __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
286 Elf_Scn *hdr_scn = NULL;
287 GElf_Addr hdr_vaddr = 0;
289 while ((scn = elf_nextscn (elf, scn)) != NULL)
292 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
295 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
298 if (!strcmp (name, ".eh_frame_hdr"))
301 hdr_vaddr = shdr->sh_addr;
303 else if (!strcmp (name, ".eh_frame"))
304 return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
313 dwarf_getcfi_elf (elf)
316 if (elf_kind (elf) != ELF_K_ELF)
318 __libdw_seterrno (DWARF_E_NOELF);
323 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
324 if (unlikely (ehdr == NULL))
326 __libdw_seterrno (DWARF_E_INVALID_ELF);
330 Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
331 if (result == (void *) -1l)
332 result = getcfi_phdr (elf, ehdr);
336 INTDEF (dwarf_getcfi_elf)