1/* FDE reading.
2   Copyright (C) 2009-2010 Red Hat, Inc.
3   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 "cfi.h"
55#include <search.h>
56#include <stdlib.h>
57
58#include "encoded-value.h"
59
60static int
61compare_fde (const void *a, const void *b)
62{
63  const struct dwarf_fde *fde1 = a;
64  const struct dwarf_fde *fde2 = b;
65
66  /* Find out which of the two arguments is the search value.
67     It has end offset 0.  */
68  if (fde1->end == 0)
69    {
70      if (fde1->start < fde2->start)
71	return -1;
72      if (fde1->start >= fde2->end)
73	return 1;
74    }
75  else
76    {
77      if (fde2->start < fde1->start)
78	return 1;
79      if (fde2->start >= fde1->end)
80	return -1;
81    }
82
83  return 0;
84}
85
86static struct dwarf_fde *
87intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
88{
89  /* Look up the new entry's CIE.  */
90  struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
91  if (cie == NULL)
92    return (void *) -1l;
93
94  struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
95  if (fde == NULL)
96    {
97      __libdw_seterrno (DWARF_E_NOMEM);
98      return NULL;
99    }
100
101  fde->instructions = entry->start;
102  fde->instructions_end = entry->end;
103  if (unlikely (read_encoded_value (cache, cie->fde_encoding,
104				    &fde->instructions, &fde->start))
105      || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
106				       &fde->instructions, &fde->end)))
107    return NULL;
108  fde->end += fde->start;
109
110  fde->cie = cie;
111
112  if (cie->sized_augmentation_data)
113    {
114      /* The CIE augmentation says the FDE has a DW_FORM_block
115	 before its actual instruction stream.  */
116      Dwarf_Word len;
117      get_uleb128 (len, fde->instructions);
118      if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
119	{
120	  free (fde);
121	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
122	  return NULL;
123	}
124      fde->instructions += len;
125    }
126  else
127    /* We had to understand all of the CIE augmentation string.
128       We've recorded the number of data bytes in FDEs.  */
129    fde->instructions += cie->fde_augmentation_data_size;
130
131  /* Add the new entry to the search tree.  */
132  if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
133    {
134      free (fde);
135      __libdw_seterrno (DWARF_E_NOMEM);
136      return NULL;
137    }
138
139  return fde;
140}
141
142struct dwarf_fde *
143internal_function
144__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
145{
146  Dwarf_CFI_Entry entry;
147  Dwarf_Off next_offset;
148  int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
149				       &cache->data->d, CFI_IS_EH (cache),
150				       offset, &next_offset, &entry);
151  if (result != 0)
152    {
153      if (result > 0)
154      invalid:
155	__libdw_seterrno (DWARF_E_INVALID_DWARF);
156      return NULL;
157    }
158
159  if (unlikely (dwarf_cfi_cie_p (&entry)))
160    goto invalid;
161
162  /* We have a new FDE to consider.  */
163  struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
164  if (fde == (void *) -1l || fde == NULL)
165    return NULL;
166
167  /* If this happened to be what we would have read next, notice it.  */
168  if (cache->next_offset == offset)
169    cache->next_offset = next_offset;
170
171  return fde;
172}
173
174/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
175static Dwarf_Off
176binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
177{
178  const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
179					      cache->search_table_encoding,
180					      NULL);
181
182  /* Dummy used by read_encoded_value.  */
183  Dwarf_CFI dummy_cfi =
184    {
185      .e_ident = cache->e_ident,
186      .datarel = cache->search_table_vaddr,
187      .frame_vaddr = cache->search_table_vaddr,
188    };
189
190  size_t l = 0, u = cache->search_table_entries;
191  while (l < u)
192    {
193      size_t idx = (l + u) / 2;
194
195      const uint8_t *p = &cache->search_table[idx * size];
196      Dwarf_Addr start;
197      if (unlikely (read_encoded_value (&dummy_cfi,
198					cache->search_table_encoding, &p,
199					&start)))
200	break;
201      if (address < start)
202	u = idx;
203      else
204	{
205	  Dwarf_Addr fde;
206	  if (unlikely (read_encoded_value (&dummy_cfi,
207					    cache->search_table_encoding, &p,
208					    &fde)))
209	    break;
210	  if (address >= start)
211	    {
212	      l = idx + 1;
213
214	      /* If this is the last entry, its upper bound is assumed to be
215		 the end of the module.
216		 XXX really should be end of containing PT_LOAD segment */
217	      if (l < cache->search_table_entries)
218		{
219		  /* Look at the start address in the following entry.  */
220		  Dwarf_Addr end;
221		  if (unlikely (read_encoded_value
222				(&dummy_cfi, cache->search_table_encoding, &p,
223				 &end)))
224		    break;
225		  if (address >= end)
226		    continue;
227		}
228
229	      return fde - cache->frame_vaddr;
230	    }
231	}
232    }
233
234  return (Dwarf_Off) -1l;
235}
236
237struct dwarf_fde *
238internal_function
239__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
240{
241  /* Look for a cached FDE covering this address.  */
242
243  const struct dwarf_fde fde_key = { .start = address, .end = 0 };
244  struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
245  if (found != NULL)
246    return *found;
247
248  /* Use .eh_frame_hdr binary search table if possible.  */
249  if (cache->search_table != NULL)
250    {
251      Dwarf_Off offset = binary_search_fde (cache, address);
252      if (offset == (Dwarf_Off) -1l)
253	goto no_match;
254      struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
255      if (unlikely (fde != NULL)
256	  /* Sanity check the address range.  */
257	  && unlikely (address < fde->start || address >= fde->end))
258	{
259	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
260	  return NULL;
261	}
262      return fde;
263    }
264
265  /* It's not there.  Read more CFI entries until we find it.  */
266  while (1)
267    {
268      Dwarf_Off last_offset = cache->next_offset;
269      Dwarf_CFI_Entry entry;
270      int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
271					   &cache->data->d, CFI_IS_EH (cache),
272					   last_offset, &cache->next_offset,
273					   &entry);
274      if (result > 0)
275	break;
276      if (result < 0)
277	{
278	  if (cache->next_offset == last_offset)
279	    /* We couldn't progress past the bogus FDE.  */
280	    break;
281	  /* Skip the loser and look at the next entry.  */
282	  continue;
283	}
284
285      if (dwarf_cfi_cie_p (&entry))
286	{
287	  /* This is a CIE, not an FDE.  We eagerly intern these
288	     because the next FDE will usually refer to this CIE.  */
289	  __libdw_intern_cie (cache, last_offset, &entry.cie);
290	  continue;
291	}
292
293      /* We have a new FDE to consider.  */
294      struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
295
296      if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
297	continue;
298
299      if (fde == NULL)		/* Bad data.  */
300	return NULL;
301
302      /* Is this the one we're looking for?  */
303      if (fde->start <= address && fde->end > address)
304	return fde;
305    }
306
307 no_match:
308  /* We found no FDE covering this address.  */
309  __libdw_seterrno (DWARF_E_NO_MATCH);
310  return NULL;
311}
312