• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Get CFI from ELF file's exception-handling info.
2    Copyright (C) 2009-2010, 2014, 2015 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,const GElf_Ehdr * ehdr,GElf_Addr vaddr)44 allocate_cfi (Elf *elf, const GElf_Ehdr *ehdr, 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   cfi->e_machine = ehdr->e_machine;
62 
63   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
64       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
65     cfi->other_byte_order = true;
66 
67   cfi->frame_vaddr = vaddr;
68   cfi->textrel = 0;		/* XXX ? */
69   cfi->datarel = 0;		/* XXX ? */
70 
71   return cfi;
72 }
73 
74 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)75 parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
76 		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
77 		    size_t *table_entries, uint8_t *table_encoding)
78 {
79   const uint8_t *h = hdr;
80 
81   if (hdr_size < 4 || *h++ != 1)		/* version */
82     return (void *) -1l;
83 
84   uint8_t eh_frame_ptr_encoding = *h++;
85   uint8_t fde_count_encoding = *h++;
86   uint8_t fde_table_encoding = *h++;
87 
88   if (eh_frame_ptr_encoding == DW_EH_PE_omit)
89     return (void *) -1l;
90 
91   /* Dummy used by read_encoded_value.  */
92   Elf_Data_Scn dummy_cfi_hdr_data =
93     {
94       .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
95     };
96   Dwarf_CFI dummy_cfi =
97     {
98       .e_ident = ehdr->e_ident,
99       .datarel = hdr_vaddr,
100       .frame_vaddr = hdr_vaddr,
101       .data = &dummy_cfi_hdr_data,
102     };
103 
104   if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
105 				    eh_frame_vaddr)))
106     return (void *) -1l;
107 
108   if (fde_count_encoding != DW_EH_PE_omit)
109     {
110       Dwarf_Word fde_count;
111       if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
112 					&fde_count)))
113 	return (void *) -1l;
114       if (fde_count != 0 && (size_t) fde_count == fde_count
115 	  && fde_table_encoding != DW_EH_PE_omit
116 	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
117 	{
118 	  *table_entries = fde_count;
119 	  *table_encoding = fde_table_encoding;
120 	  return h;
121 	}
122     }
123 
124   return NULL;
125 }
126 
127 static Dwarf_CFI *
getcfi_gnu_eh_frame(Elf * elf,const GElf_Ehdr * ehdr,const GElf_Phdr * phdr)128 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
129 {
130   Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
131 					 ELF_T_BYTE);
132   if (data == NULL || data->d_buf == NULL)
133     {
134     invalid_hdr:
135       /* XXX might be read error or corrupt phdr */
136       __libdw_seterrno (DWARF_E_INVALID_CFI);
137       return NULL;
138     }
139 
140   size_t vsize, dmax;
141   Dwarf_Addr eh_frame_ptr;
142   size_t search_table_entries = 0;
143   uint8_t search_table_encoding = 0;
144   const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
145 						    phdr->p_vaddr, ehdr,
146 						    &eh_frame_ptr,
147 						    &search_table_entries,
148 						    &search_table_encoding);
149 
150   /* Make sure there is enough room for the entries in the table,
151      each entry consists of 2 encoded values.  */
152   vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
153 			      NULL);
154   dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
155   if (unlikely (search_table == (void *) -1l
156 		|| vsize == 0
157 		|| search_table_entries > (dmax / vsize) / 2))
158     goto invalid_hdr;
159 
160   Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
161   Dwarf_Word eh_frame_size = 0;
162 
163   /* XXX we have no way without section headers to know the size
164      of the .eh_frame data.  Calculate the largest it might possibly be.
165      This won't be wasteful if the file is already mmap'd, but if it isn't
166      it might be quite excessive.  */
167   size_t filesize;
168   if (elf_rawfile (elf, &filesize) != NULL)
169     eh_frame_size = filesize - eh_frame_offset;
170 
171   data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
172   if (data == NULL)
173     {
174       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
175       return NULL;
176     }
177   Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, eh_frame_ptr);
178   if (cfi != NULL)
179     {
180       cfi->data = (Elf_Data_Scn *) data;
181 
182       if (search_table != NULL)
183 	{
184 	  cfi->search_table = search_table;
185 	  cfi->search_table_len = phdr->p_filesz;
186 	  cfi->search_table_vaddr = phdr->p_vaddr;
187 	  cfi->search_table_encoding = search_table_encoding;
188 	  cfi->search_table_entries = search_table_entries;
189 	}
190     }
191   return cfi;
192 }
193 
194 /* Search the phdrs for PT_GNU_EH_FRAME.  */
195 static Dwarf_CFI *
getcfi_phdr(Elf * elf,const GElf_Ehdr * ehdr)196 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
197 {
198   size_t phnum;
199   if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
200     return NULL;
201 
202   for (size_t i = 0; i < phnum; ++i)
203     {
204       GElf_Phdr phdr_mem;
205       GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
206       if (unlikely (phdr == NULL))
207 	return NULL;
208       if (phdr->p_type == PT_GNU_EH_FRAME)
209 	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
210     }
211 
212   __libdw_seterrno (DWARF_E_NO_DWARF);
213   return NULL;
214 }
215 
216 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)217 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
218 		     Elf_Scn *scn, GElf_Shdr *shdr,
219 		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
220 {
221   Elf_Data *data = elf_rawdata (scn, NULL);
222   if (data == NULL || data->d_buf == NULL)
223     {
224       __libdw_seterrno (DWARF_E_INVALID_ELF);
225       return NULL;
226     }
227   Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, shdr->sh_addr);
228   if (cfi != NULL)
229     {
230       cfi->data = (Elf_Data_Scn *) data;
231       if (hdr_scn != NULL)
232 	{
233 	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
234 	  if (hdr_data != NULL && hdr_data->d_buf != NULL)
235 	    {
236 	      size_t vsize, dmax;
237 	      GElf_Addr eh_frame_vaddr;
238 	      cfi->search_table_vaddr = hdr_vaddr;
239 	      cfi->search_table
240 		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
241 				      hdr_vaddr, ehdr, &eh_frame_vaddr,
242 				      &cfi->search_table_entries,
243 				      &cfi->search_table_encoding);
244 	      cfi->search_table_len = hdr_data->d_size;
245 
246 	      /* Make sure there is enough room for the entries in the table,
247 		 each entry consists of 2 encoded values.  */
248 	      vsize = encoded_value_size (hdr_data, ehdr->e_ident,
249 					  cfi->search_table_encoding, NULL);
250 	      dmax = hdr_data->d_size - (cfi->search_table
251 					 - (const uint8_t *) hdr_data->d_buf);
252 	      if (unlikely (cfi->search_table == (void *) -1l
253 			    || vsize == 0
254 			    || cfi->search_table_entries > (dmax / vsize) / 2))
255 		{
256 		  free (cfi);
257 		  /* XXX might be read error or corrupt phdr */
258 		  __libdw_seterrno (DWARF_E_INVALID_CFI);
259 		  return NULL;
260 		}
261 
262 	      /* Sanity check.  */
263 	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
264 		cfi->search_table = NULL;
265 	    }
266 	}
267     }
268   return cfi;
269 }
270 
271 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
272 static Dwarf_CFI *
getcfi_shdr(Elf * elf,const GElf_Ehdr * ehdr)273 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
274 {
275   size_t shstrndx;
276   if (elf_getshdrstrndx (elf, &shstrndx) != 0)
277     {
278       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
279       return NULL;
280     }
281 
282   if (shstrndx != 0)
283     {
284       Elf_Scn *hdr_scn = NULL;
285       GElf_Addr hdr_vaddr = 0;
286       Elf_Scn *scn = NULL;
287       while ((scn = elf_nextscn (elf, scn)) != NULL)
288 	{
289 	  GElf_Shdr shdr_mem;
290 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
291 	  if (shdr == NULL)
292 	    continue;
293 	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
294 	  if (name == NULL)
295 	    continue;
296 	  if (!strcmp (name, ".eh_frame_hdr"))
297 	    {
298 	      hdr_scn = scn;
299 	      hdr_vaddr = shdr->sh_addr;
300 	    }
301 	  else if (!strcmp (name, ".eh_frame"))
302 	    {
303 	      if (shdr->sh_type != SHT_NOBITS)
304 		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
305 					    hdr_scn, hdr_vaddr);
306 	      else
307 		return NULL;
308 	    }
309 	}
310     }
311 
312   return (void *) -1l;
313 }
314 
315 Dwarf_CFI *
dwarf_getcfi_elf(Elf * elf)316 dwarf_getcfi_elf (Elf *elf)
317 {
318   if (elf_kind (elf) != ELF_K_ELF)
319     {
320       __libdw_seterrno (DWARF_E_NOELF);
321       return NULL;
322     }
323 
324   GElf_Ehdr ehdr_mem;
325   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
326   if (unlikely (ehdr == NULL))
327     {
328       __libdw_seterrno (DWARF_E_INVALID_ELF);
329       return NULL;
330     }
331 
332   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
333   if (result == (void *) -1l)
334     result = getcfi_phdr (elf, ehdr);
335 
336   return result;
337 }
338 INTDEF (dwarf_getcfi_elf)
339