• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Get CFI from ELF file's exception-handling info.
2    Copyright (C) 2009-2010, 2014 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 *
allocate_cfi(Elf * elf,GElf_Addr vaddr)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 *
parse_eh_frame_hdr(const uint8_t * hdr,size_t hdr_size,GElf_Addr hdr_vaddr,const GElf_Ehdr * ehdr,GElf_Addr * eh_frame_vaddr,size_t * table_entries,uint8_t * table_encoding)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 *
getcfi_gnu_eh_frame(Elf * elf,const GElf_Ehdr * ehdr,const GElf_Phdr * phdr)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 = 0;
144   uint8_t search_table_encoding = 0;
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 *
getcfi_phdr(Elf * elf,const GElf_Ehdr * ehdr)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 *
getcfi_scn_eh_frame(Elf * elf,const GElf_Ehdr * ehdr,Elf_Scn * scn,GElf_Shdr * shdr,Elf_Scn * hdr_scn,GElf_Addr hdr_vaddr)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 *
getcfi_shdr(Elf * elf,const GElf_Ehdr * ehdr)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 	    {
284 	      if (shdr->sh_type == SHT_PROGBITS)
285 		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
286 					    hdr_scn, hdr_vaddr);
287 	      else
288 		return NULL;
289 	    }
290 	}
291     }
292 
293   return (void *) -1l;
294 }
295 
296 Dwarf_CFI *
dwarf_getcfi_elf(elf)297 dwarf_getcfi_elf (elf)
298      Elf *elf;
299 {
300   if (elf_kind (elf) != ELF_K_ELF)
301     {
302       __libdw_seterrno (DWARF_E_NOELF);
303       return NULL;
304     }
305 
306   GElf_Ehdr ehdr_mem;
307   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
308   if (unlikely (ehdr == NULL))
309     {
310       __libdw_seterrno (DWARF_E_INVALID_ELF);
311       return NULL;
312     }
313 
314   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
315   if (result == (void *) -1l)
316     result = getcfi_phdr (elf, ehdr);
317 
318   return result;
319 }
320 INTDEF (dwarf_getcfi_elf)
321