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