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
43static Dwarf_CFI *
44allocate_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
72static const uint8_t *
73parse_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 (hdr_size < 4 || *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
125static Dwarf_CFI *
126getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
127{
128  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
129					 ELF_T_BYTE);
130  if (data == NULL || data->d_buf == NULL)
131    {
132    invalid_hdr:
133      /* XXX might be read error or corrupt phdr */
134      __libdw_seterrno (DWARF_E_INVALID_CFI);
135      return NULL;
136    }
137
138  size_t vsize, dmax;
139  Dwarf_Addr eh_frame_ptr;
140  size_t search_table_entries = 0;
141  uint8_t search_table_encoding = 0;
142  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
143						    phdr->p_vaddr, ehdr,
144						    &eh_frame_ptr,
145						    &search_table_entries,
146						    &search_table_encoding);
147
148  /* Make sure there is enough room for the entries in the table,
149     each entry consists of 2 encoded values.  */
150  vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
151			      NULL);
152  dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
153  if (unlikely (search_table == (void *) -1l
154		|| vsize == 0
155		|| search_table_entries > (dmax / vsize) / 2))
156    goto invalid_hdr;
157
158  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
159  Dwarf_Word eh_frame_size = 0;
160
161  /* XXX we have no way without section headers to know the size
162     of the .eh_frame data.  Calculate the largest it might possibly be.
163     This won't be wasteful if the file is already mmap'd, but if it isn't
164     it might be quite excessive.  */
165  size_t filesize;
166  if (elf_rawfile (elf, &filesize) != NULL)
167    eh_frame_size = filesize - eh_frame_offset;
168
169  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
170  if (data == NULL)
171    {
172      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
173      return NULL;
174    }
175  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
176  if (cfi != NULL)
177    {
178      cfi->data = (Elf_Data_Scn *) data;
179
180      if (search_table != NULL)
181	{
182	  cfi->search_table = search_table;
183	  cfi->search_table_len = phdr->p_filesz;
184	  cfi->search_table_vaddr = phdr->p_vaddr;
185	  cfi->search_table_encoding = search_table_encoding;
186	  cfi->search_table_entries = search_table_entries;
187	}
188    }
189  return cfi;
190}
191
192/* Search the phdrs for PT_GNU_EH_FRAME.  */
193static Dwarf_CFI *
194getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
195{
196  size_t phnum;
197  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
198    return NULL;
199
200  for (size_t i = 0; i < phnum; ++i)
201    {
202      GElf_Phdr phdr_mem;
203      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
204      if (unlikely (phdr == NULL))
205	return NULL;
206      if (phdr->p_type == PT_GNU_EH_FRAME)
207	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
208    }
209
210  __libdw_seterrno (DWARF_E_NO_DWARF);
211  return NULL;
212}
213
214static Dwarf_CFI *
215getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
216		     Elf_Scn *scn, GElf_Shdr *shdr,
217		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
218{
219  Elf_Data *data = elf_rawdata (scn, NULL);
220  if (data == NULL || data->d_buf == NULL)
221    {
222      __libdw_seterrno (DWARF_E_INVALID_ELF);
223      return NULL;
224    }
225  Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
226  if (cfi != NULL)
227    {
228      cfi->data = (Elf_Data_Scn *) data;
229      if (hdr_scn != NULL)
230	{
231	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
232	  if (hdr_data != NULL && hdr_data->d_buf != NULL)
233	    {
234	      size_t vsize, dmax;
235	      GElf_Addr eh_frame_vaddr;
236	      cfi->search_table_vaddr = hdr_vaddr;
237	      cfi->search_table
238		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
239				      hdr_vaddr, ehdr, &eh_frame_vaddr,
240				      &cfi->search_table_entries,
241				      &cfi->search_table_encoding);
242	      cfi->search_table_len = hdr_data->d_size;
243
244	      /* Make sure there is enough room for the entries in the table,
245		 each entry consists of 2 encoded values.  */
246	      vsize = encoded_value_size (hdr_data, ehdr->e_ident,
247					  cfi->search_table_encoding, NULL);
248	      dmax = hdr_data->d_size - (cfi->search_table
249					 - (const uint8_t *) hdr_data->d_buf);
250	      if (unlikely (cfi->search_table == (void *) -1l
251			    || vsize == 0
252			    || cfi->search_table_entries > (dmax / vsize) / 2))
253		{
254		  free (cfi);
255		  /* XXX might be read error or corrupt phdr */
256		  __libdw_seterrno (DWARF_E_INVALID_CFI);
257		  return NULL;
258		}
259
260	      /* Sanity check.  */
261	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
262		cfi->search_table = NULL;
263	    }
264	}
265    }
266  return cfi;
267}
268
269/* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
270static Dwarf_CFI *
271getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
272{
273  size_t shstrndx;
274  if (elf_getshdrstrndx (elf, &shstrndx) != 0)
275    {
276      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
277      return NULL;
278    }
279
280  if (shstrndx != 0)
281    {
282      Elf_Scn *hdr_scn = NULL;
283      GElf_Addr hdr_vaddr = 0;
284      Elf_Scn *scn = NULL;
285      while ((scn = elf_nextscn (elf, scn)) != NULL)
286	{
287	  GElf_Shdr shdr_mem;
288	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
289	  if (shdr == NULL)
290	    continue;
291	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
292	  if (name == NULL)
293	    continue;
294	  if (!strcmp (name, ".eh_frame_hdr"))
295	    {
296	      hdr_scn = scn;
297	      hdr_vaddr = shdr->sh_addr;
298	    }
299	  else if (!strcmp (name, ".eh_frame"))
300	    {
301	      if (shdr->sh_type == SHT_PROGBITS)
302		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
303					    hdr_scn, hdr_vaddr);
304	      else
305		return NULL;
306	    }
307	}
308    }
309
310  return (void *) -1l;
311}
312
313Dwarf_CFI *
314dwarf_getcfi_elf (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)
337