1da0c48c4Sopenharmony_ci/* FDE reading.
2da0c48c4Sopenharmony_ci   Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include "cfi.h"
34da0c48c4Sopenharmony_ci#include <search.h>
35da0c48c4Sopenharmony_ci#include <stdlib.h>
36da0c48c4Sopenharmony_ci
37da0c48c4Sopenharmony_ci#include "encoded-value.h"
38da0c48c4Sopenharmony_ci
39da0c48c4Sopenharmony_cistatic int
40da0c48c4Sopenharmony_cicompare_fde (const void *a, const void *b)
41da0c48c4Sopenharmony_ci{
42da0c48c4Sopenharmony_ci  const struct dwarf_fde *fde1 = a;
43da0c48c4Sopenharmony_ci  const struct dwarf_fde *fde2 = b;
44da0c48c4Sopenharmony_ci
45da0c48c4Sopenharmony_ci  /* Find out which of the two arguments is the search value.
46da0c48c4Sopenharmony_ci     It has end offset 0.  */
47da0c48c4Sopenharmony_ci  if (fde1->end == 0)
48da0c48c4Sopenharmony_ci    {
49da0c48c4Sopenharmony_ci      if (fde1->start < fde2->start)
50da0c48c4Sopenharmony_ci	return -1;
51da0c48c4Sopenharmony_ci      if (fde1->start >= fde2->end)
52da0c48c4Sopenharmony_ci	return 1;
53da0c48c4Sopenharmony_ci    }
54da0c48c4Sopenharmony_ci  else
55da0c48c4Sopenharmony_ci    {
56da0c48c4Sopenharmony_ci      if (fde2->start < fde1->start)
57da0c48c4Sopenharmony_ci	return 1;
58da0c48c4Sopenharmony_ci      if (fde2->start >= fde1->end)
59da0c48c4Sopenharmony_ci	return -1;
60da0c48c4Sopenharmony_ci    }
61da0c48c4Sopenharmony_ci
62da0c48c4Sopenharmony_ci  return 0;
63da0c48c4Sopenharmony_ci}
64da0c48c4Sopenharmony_ci
65da0c48c4Sopenharmony_cistatic struct dwarf_fde *
66da0c48c4Sopenharmony_ciintern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
67da0c48c4Sopenharmony_ci{
68da0c48c4Sopenharmony_ci  /* Look up the new entry's CIE.  */
69da0c48c4Sopenharmony_ci  struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
70da0c48c4Sopenharmony_ci  if (cie == NULL)
71da0c48c4Sopenharmony_ci    return (void *) -1l;
72da0c48c4Sopenharmony_ci
73da0c48c4Sopenharmony_ci  struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
74da0c48c4Sopenharmony_ci  if (fde == NULL)
75da0c48c4Sopenharmony_ci    {
76da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_NOMEM);
77da0c48c4Sopenharmony_ci      return NULL;
78da0c48c4Sopenharmony_ci    }
79da0c48c4Sopenharmony_ci
80da0c48c4Sopenharmony_ci  fde->instructions = entry->start;
81da0c48c4Sopenharmony_ci  fde->instructions_end = entry->end;
82da0c48c4Sopenharmony_ci  if (unlikely (read_encoded_value (cache, cie->fde_encoding,
83da0c48c4Sopenharmony_ci				    &fde->instructions, &fde->start))
84da0c48c4Sopenharmony_ci      || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
85da0c48c4Sopenharmony_ci				       &fde->instructions, &fde->end)))
86da0c48c4Sopenharmony_ci    {
87da0c48c4Sopenharmony_ci      free (fde);
88da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_INVALID_DWARF);
89da0c48c4Sopenharmony_ci      return NULL;
90da0c48c4Sopenharmony_ci    }
91da0c48c4Sopenharmony_ci  fde->end += fde->start;
92da0c48c4Sopenharmony_ci
93da0c48c4Sopenharmony_ci  /* Make sure the fde actually covers a real code range.  */
94da0c48c4Sopenharmony_ci  if (fde->start >= fde->end)
95da0c48c4Sopenharmony_ci    {
96da0c48c4Sopenharmony_ci      free (fde);
97da0c48c4Sopenharmony_ci      return (void *) -1;
98da0c48c4Sopenharmony_ci    }
99da0c48c4Sopenharmony_ci
100da0c48c4Sopenharmony_ci  fde->cie = cie;
101da0c48c4Sopenharmony_ci
102da0c48c4Sopenharmony_ci  if (cie->sized_augmentation_data)
103da0c48c4Sopenharmony_ci    {
104da0c48c4Sopenharmony_ci      /* The CIE augmentation says the FDE has a DW_FORM_block
105da0c48c4Sopenharmony_ci	 before its actual instruction stream.  */
106da0c48c4Sopenharmony_ci      Dwarf_Word len;
107da0c48c4Sopenharmony_ci      get_uleb128 (len, fde->instructions, fde->instructions_end);
108da0c48c4Sopenharmony_ci      if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
109da0c48c4Sopenharmony_ci	{
110da0c48c4Sopenharmony_ci	  free (fde);
111da0c48c4Sopenharmony_ci	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
112da0c48c4Sopenharmony_ci	  return NULL;
113da0c48c4Sopenharmony_ci	}
114da0c48c4Sopenharmony_ci      fde->instructions += len;
115da0c48c4Sopenharmony_ci    }
116da0c48c4Sopenharmony_ci  else
117da0c48c4Sopenharmony_ci    /* We had to understand all of the CIE augmentation string.
118da0c48c4Sopenharmony_ci       We've recorded the number of data bytes in FDEs.  */
119da0c48c4Sopenharmony_ci    fde->instructions += cie->fde_augmentation_data_size;
120da0c48c4Sopenharmony_ci
121da0c48c4Sopenharmony_ci  /* Add the new entry to the search tree.  */
122da0c48c4Sopenharmony_ci  struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
123da0c48c4Sopenharmony_ci  if (tres == NULL)
124da0c48c4Sopenharmony_ci    {
125da0c48c4Sopenharmony_ci      free (fde);
126da0c48c4Sopenharmony_ci      __libdw_seterrno (DWARF_E_NOMEM);
127da0c48c4Sopenharmony_ci      return NULL;
128da0c48c4Sopenharmony_ci    }
129da0c48c4Sopenharmony_ci  else if (*tres != fde)
130da0c48c4Sopenharmony_ci    {
131da0c48c4Sopenharmony_ci      /* There is already an FDE in the cache that covers the same
132da0c48c4Sopenharmony_ci	 address range.  That is odd.  Ignore this FDE.  And just use
133da0c48c4Sopenharmony_ci	 the one in the cache for consistency.  */
134da0c48c4Sopenharmony_ci      free (fde);
135da0c48c4Sopenharmony_ci      return *tres;
136da0c48c4Sopenharmony_ci    }
137da0c48c4Sopenharmony_ci
138da0c48c4Sopenharmony_ci  return fde;
139da0c48c4Sopenharmony_ci}
140da0c48c4Sopenharmony_ci
141da0c48c4Sopenharmony_cistruct dwarf_fde *
142da0c48c4Sopenharmony_ciinternal_function
143da0c48c4Sopenharmony_ci__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
144da0c48c4Sopenharmony_ci{
145da0c48c4Sopenharmony_ci  Dwarf_CFI_Entry entry;
146da0c48c4Sopenharmony_ci  Dwarf_Off next_offset;
147da0c48c4Sopenharmony_ci  int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
148da0c48c4Sopenharmony_ci				       &cache->data->d, CFI_IS_EH (cache),
149da0c48c4Sopenharmony_ci				       offset, &next_offset, &entry);
150da0c48c4Sopenharmony_ci  if (result != 0)
151da0c48c4Sopenharmony_ci    {
152da0c48c4Sopenharmony_ci      if (result > 0)
153da0c48c4Sopenharmony_ci      invalid:
154da0c48c4Sopenharmony_ci	__libdw_seterrno (DWARF_E_INVALID_DWARF);
155da0c48c4Sopenharmony_ci      return NULL;
156da0c48c4Sopenharmony_ci    }
157da0c48c4Sopenharmony_ci
158da0c48c4Sopenharmony_ci  if (unlikely (dwarf_cfi_cie_p (&entry)))
159da0c48c4Sopenharmony_ci    goto invalid;
160da0c48c4Sopenharmony_ci
161da0c48c4Sopenharmony_ci  /* We have a new FDE to consider.  */
162da0c48c4Sopenharmony_ci  struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
163da0c48c4Sopenharmony_ci  if (fde == (void *) -1l || fde == NULL)
164da0c48c4Sopenharmony_ci    return NULL;
165da0c48c4Sopenharmony_ci
166da0c48c4Sopenharmony_ci  /* If this happened to be what we would have read next, notice it.  */
167da0c48c4Sopenharmony_ci  if (cache->next_offset == offset)
168da0c48c4Sopenharmony_ci    cache->next_offset = next_offset;
169da0c48c4Sopenharmony_ci
170da0c48c4Sopenharmony_ci  return fde;
171da0c48c4Sopenharmony_ci}
172da0c48c4Sopenharmony_ci
173da0c48c4Sopenharmony_ci/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
174da0c48c4Sopenharmony_cistatic Dwarf_Off
175da0c48c4Sopenharmony_cibinary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
176da0c48c4Sopenharmony_ci{
177da0c48c4Sopenharmony_ci  const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
178da0c48c4Sopenharmony_ci					      cache->search_table_encoding,
179da0c48c4Sopenharmony_ci					      NULL);
180da0c48c4Sopenharmony_ci  if (unlikely (size == 0))
181da0c48c4Sopenharmony_ci    return (Dwarf_Off) -1l;
182da0c48c4Sopenharmony_ci
183da0c48c4Sopenharmony_ci  /* Dummy used by read_encoded_value.  */
184da0c48c4Sopenharmony_ci  Elf_Data_Scn dummy_cfi_hdr_data =
185da0c48c4Sopenharmony_ci    {
186da0c48c4Sopenharmony_ci      .d = { .d_buf = (void *) cache->search_table,
187da0c48c4Sopenharmony_ci	     .d_size = cache->search_table_len }
188da0c48c4Sopenharmony_ci    };
189da0c48c4Sopenharmony_ci
190da0c48c4Sopenharmony_ci  Dwarf_CFI dummy_cfi =
191da0c48c4Sopenharmony_ci    {
192da0c48c4Sopenharmony_ci      .e_ident = cache->e_ident,
193da0c48c4Sopenharmony_ci      .datarel = cache->search_table_vaddr,
194da0c48c4Sopenharmony_ci      .frame_vaddr = cache->search_table_vaddr,
195da0c48c4Sopenharmony_ci      .data = &dummy_cfi_hdr_data
196da0c48c4Sopenharmony_ci    };
197da0c48c4Sopenharmony_ci
198da0c48c4Sopenharmony_ci  size_t l = 0, u = cache->search_table_entries;
199da0c48c4Sopenharmony_ci  while (l < u)
200da0c48c4Sopenharmony_ci    {
201da0c48c4Sopenharmony_ci      size_t idx = (l + u) / 2;
202da0c48c4Sopenharmony_ci
203da0c48c4Sopenharmony_ci      /* Max idx * size is checked against search_table len when
204da0c48c4Sopenharmony_ci	 loading eh_frame_hdr.  */
205da0c48c4Sopenharmony_ci      const uint8_t *p = &cache->search_table[idx * size];
206da0c48c4Sopenharmony_ci      Dwarf_Addr start;
207da0c48c4Sopenharmony_ci      if (unlikely (read_encoded_value (&dummy_cfi,
208da0c48c4Sopenharmony_ci					cache->search_table_encoding, &p,
209da0c48c4Sopenharmony_ci					&start)))
210da0c48c4Sopenharmony_ci	break;
211da0c48c4Sopenharmony_ci      if (address < start)
212da0c48c4Sopenharmony_ci	u = idx;
213da0c48c4Sopenharmony_ci      else
214da0c48c4Sopenharmony_ci	{
215da0c48c4Sopenharmony_ci	  l = idx + 1;
216da0c48c4Sopenharmony_ci
217da0c48c4Sopenharmony_ci	  Dwarf_Addr fde;
218da0c48c4Sopenharmony_ci	  if (unlikely (read_encoded_value (&dummy_cfi,
219da0c48c4Sopenharmony_ci					    cache->search_table_encoding, &p,
220da0c48c4Sopenharmony_ci					    &fde)))
221da0c48c4Sopenharmony_ci	    break;
222da0c48c4Sopenharmony_ci
223da0c48c4Sopenharmony_ci	  /* If this is the last entry, its upper bound is assumed to be
224da0c48c4Sopenharmony_ci	     the end of the module.
225da0c48c4Sopenharmony_ci	     XXX really should be end of containing PT_LOAD segment */
226da0c48c4Sopenharmony_ci	  if (l < cache->search_table_entries)
227da0c48c4Sopenharmony_ci	    {
228da0c48c4Sopenharmony_ci	      /* Look at the start address in the following entry.  */
229da0c48c4Sopenharmony_ci	      Dwarf_Addr end;
230da0c48c4Sopenharmony_ci	      if (unlikely (read_encoded_value
231da0c48c4Sopenharmony_ci			    (&dummy_cfi, cache->search_table_encoding, &p,
232da0c48c4Sopenharmony_ci			     &end)))
233da0c48c4Sopenharmony_ci		break;
234da0c48c4Sopenharmony_ci	      if (address >= end)
235da0c48c4Sopenharmony_ci		continue;
236da0c48c4Sopenharmony_ci	    }
237da0c48c4Sopenharmony_ci
238da0c48c4Sopenharmony_ci	  return fde - cache->frame_vaddr;
239da0c48c4Sopenharmony_ci	}
240da0c48c4Sopenharmony_ci    }
241da0c48c4Sopenharmony_ci
242da0c48c4Sopenharmony_ci  return (Dwarf_Off) -1l;
243da0c48c4Sopenharmony_ci}
244da0c48c4Sopenharmony_ci
245da0c48c4Sopenharmony_cistruct dwarf_fde *
246da0c48c4Sopenharmony_ciinternal_function
247da0c48c4Sopenharmony_ci__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
248da0c48c4Sopenharmony_ci{
249da0c48c4Sopenharmony_ci  /* Look for a cached FDE covering this address.  */
250da0c48c4Sopenharmony_ci
251da0c48c4Sopenharmony_ci  const struct dwarf_fde fde_key = { .start = address, .end = 0 };
252da0c48c4Sopenharmony_ci  struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
253da0c48c4Sopenharmony_ci  if (found != NULL)
254da0c48c4Sopenharmony_ci    return *found;
255da0c48c4Sopenharmony_ci
256da0c48c4Sopenharmony_ci  /* Use .eh_frame_hdr binary search table if possible.  */
257da0c48c4Sopenharmony_ci  if (cache->search_table != NULL)
258da0c48c4Sopenharmony_ci    {
259da0c48c4Sopenharmony_ci      Dwarf_Off offset = binary_search_fde (cache, address);
260da0c48c4Sopenharmony_ci      if (offset == (Dwarf_Off) -1l)
261da0c48c4Sopenharmony_ci	goto no_match;
262da0c48c4Sopenharmony_ci      struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
263da0c48c4Sopenharmony_ci      if (likely (fde != NULL))
264da0c48c4Sopenharmony_ci	{
265da0c48c4Sopenharmony_ci	  /* Sanity check the address range.  */
266da0c48c4Sopenharmony_ci	  if (unlikely (address < fde->start))
267da0c48c4Sopenharmony_ci	    {
268da0c48c4Sopenharmony_ci	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
269da0c48c4Sopenharmony_ci	      return NULL;
270da0c48c4Sopenharmony_ci	    }
271da0c48c4Sopenharmony_ci	  /* .eh_frame_hdr does not indicate length covered by FDE.  */
272da0c48c4Sopenharmony_ci	  if (unlikely (address >= fde->end))
273da0c48c4Sopenharmony_ci	    goto no_match;
274da0c48c4Sopenharmony_ci	}
275da0c48c4Sopenharmony_ci      return fde;
276da0c48c4Sopenharmony_ci    }
277da0c48c4Sopenharmony_ci
278da0c48c4Sopenharmony_ci  /* It's not there.  Read more CFI entries until we find it.  */
279da0c48c4Sopenharmony_ci  while (1)
280da0c48c4Sopenharmony_ci    {
281da0c48c4Sopenharmony_ci      Dwarf_Off last_offset = cache->next_offset;
282da0c48c4Sopenharmony_ci      Dwarf_CFI_Entry entry;
283da0c48c4Sopenharmony_ci      int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
284da0c48c4Sopenharmony_ci					   &cache->data->d, CFI_IS_EH (cache),
285da0c48c4Sopenharmony_ci					   last_offset, &cache->next_offset,
286da0c48c4Sopenharmony_ci					   &entry);
287da0c48c4Sopenharmony_ci      if (result > 0)
288da0c48c4Sopenharmony_ci	break;
289da0c48c4Sopenharmony_ci      if (result < 0)
290da0c48c4Sopenharmony_ci	{
291da0c48c4Sopenharmony_ci	  if (cache->next_offset == last_offset)
292da0c48c4Sopenharmony_ci	    /* We couldn't progress past the bogus FDE.  */
293da0c48c4Sopenharmony_ci	    break;
294da0c48c4Sopenharmony_ci	  /* Skip the loser and look at the next entry.  */
295da0c48c4Sopenharmony_ci	  continue;
296da0c48c4Sopenharmony_ci	}
297da0c48c4Sopenharmony_ci
298da0c48c4Sopenharmony_ci      if (dwarf_cfi_cie_p (&entry))
299da0c48c4Sopenharmony_ci	{
300da0c48c4Sopenharmony_ci	  /* This is a CIE, not an FDE.  We eagerly intern these
301da0c48c4Sopenharmony_ci	     because the next FDE will usually refer to this CIE.  */
302da0c48c4Sopenharmony_ci	  __libdw_intern_cie (cache, last_offset, &entry.cie);
303da0c48c4Sopenharmony_ci	  continue;
304da0c48c4Sopenharmony_ci	}
305da0c48c4Sopenharmony_ci
306da0c48c4Sopenharmony_ci      /* We have a new FDE to consider.  */
307da0c48c4Sopenharmony_ci      struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
308da0c48c4Sopenharmony_ci
309da0c48c4Sopenharmony_ci      if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
310da0c48c4Sopenharmony_ci	continue;
311da0c48c4Sopenharmony_ci
312da0c48c4Sopenharmony_ci      if (fde == NULL)		/* Bad data.  */
313da0c48c4Sopenharmony_ci	return NULL;
314da0c48c4Sopenharmony_ci
315da0c48c4Sopenharmony_ci      /* Is this the one we're looking for?  */
316da0c48c4Sopenharmony_ci      if (fde->start <= address && fde->end > address)
317da0c48c4Sopenharmony_ci	return fde;
318da0c48c4Sopenharmony_ci    }
319da0c48c4Sopenharmony_ci
320da0c48c4Sopenharmony_ci no_match:
321da0c48c4Sopenharmony_ci  /* We found no FDE covering this address.  */
322da0c48c4Sopenharmony_ci  __libdw_seterrno (DWARF_E_NO_MATCH);
323da0c48c4Sopenharmony_ci  return NULL;
324da0c48c4Sopenharmony_ci}
325