• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
60 static int
compare_fde(const void * a,const void * b)61 compare_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 
86 static struct dwarf_fde *
intern_fde(Dwarf_CFI * cache,const Dwarf_FDE * entry)87 intern_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 
142 struct dwarf_fde *
143 internal_function
__libdw_fde_by_offset(Dwarf_CFI * cache,Dwarf_Off offset)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.  */
175 static Dwarf_Off
binary_search_fde(Dwarf_CFI * cache,Dwarf_Addr address)176 binary_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 
237 struct dwarf_fde *
238 internal_function
__libdw_find_fde(Dwarf_CFI * cache,Dwarf_Addr address)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