1 /* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2009, 2010, 2011
2    Free Software Foundation, Inc.
3    Contributed by Jakub Jelinek <jakub@redhat.com>.
4 
5    This file is part of GCC.
6 
7    GCC is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11 
12    GCC is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    Under Section 7 of GPL version 3, you are granted additional
18    permissions described in the GCC Runtime Library Exception, version
19    3.1, as published by the Free Software Foundation.
20 
21    You should have received a copy of the GNU General Public License and
22    a copy of the GCC Runtime Library Exception along with this program;
23    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24    <http://www.gnu.org/licenses/>.  */
25 
26 /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
27    segment and dl_iterate_phdr to avoid register/deregister calls at
28    DSO load/unload.  */
29 
30 #ifndef _GNU_SOURCE
31 #define _GNU_SOURCE 1
32 #endif
33 
34 #include "tconfig.h"
35 #include "tsystem.h"
36 #ifndef inhibit_libc
37 #include <elf.h>		/* Get DT_CONFIG.  */
38 #endif
39 #include "coretypes.h"
40 #include "tm.h"
41 #include "libgcc_tm.h"
42 #include "dwarf2.h"
43 #include "unwind.h"
44 #define NO_BASE_OF_ENCODED_VALUE
45 #include "unwind-pe.h"
46 #include "unwind-dw2-fde.h"
47 #include "unwind-compat.h"
48 #include "gthr.h"
49 
50 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
51     && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
52 	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
53 # define USE_PT_GNU_EH_FRAME
54 #endif
55 
56 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
57     && defined(TARGET_DL_ITERATE_PHDR) \
58     && (defined(__FreeBSD__) || defined(__DragonFly__))
59 # define ElfW __ElfN
60 # define USE_PT_GNU_EH_FRAME
61 #endif
62 
63 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
64     && defined(TARGET_DL_ITERATE_PHDR) \
65     && (defined(__OpenBSD__) || defined(__NetBSD__))
66 # define ElfW(n) Elf_##n
67 # define USE_PT_GNU_EH_FRAME
68 #endif
69 
70 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
71     && defined(TARGET_DL_ITERATE_PHDR) \
72     && defined(__sun__) && defined(__svr4__)
73 # define USE_PT_GNU_EH_FRAME
74 #endif
75 
76 #if defined(USE_PT_GNU_EH_FRAME)
77 
78 #include <link.h>
79 
80 #ifndef __RELOC_POINTER
81 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
82 #endif
83 
84 static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
85 
86 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
87 #include "unwind-dw2-fde.c"
88 #undef _Unwind_Find_FDE
89 
90 #ifndef PT_GNU_EH_FRAME
91 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
92 #endif
93 
94 struct unw_eh_callback_data
95 {
96   _Unwind_Ptr pc;
97   void *tbase;
98   void *dbase;
99   void *func;
100   const fde *ret;
101   int check_cache;
102 };
103 
104 struct unw_eh_frame_hdr
105 {
106   unsigned char version;
107   unsigned char eh_frame_ptr_enc;
108   unsigned char fde_count_enc;
109   unsigned char table_enc;
110 };
111 
112 #define FRAME_HDR_CACHE_SIZE 8
113 
114 static struct frame_hdr_cache_element
115 {
116   _Unwind_Ptr pc_low;
117   _Unwind_Ptr pc_high;
118   _Unwind_Ptr load_base;
119   const ElfW(Phdr) *p_eh_frame_hdr;
120   const ElfW(Phdr) *p_dynamic;
121   struct frame_hdr_cache_element *link;
122 } frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
123 
124 static struct frame_hdr_cache_element *frame_hdr_cache_head;
125 
126 /* Like base_of_encoded_value, but take the base from a struct
127    unw_eh_callback_data instead of an _Unwind_Context.  */
128 
129 static _Unwind_Ptr
130 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
131 {
132   if (encoding == DW_EH_PE_omit)
133     return 0;
134 
135   switch (encoding & 0x70)
136     {
137     case DW_EH_PE_absptr:
138     case DW_EH_PE_pcrel:
139     case DW_EH_PE_aligned:
140       return 0;
141 
142     case DW_EH_PE_textrel:
143       return (_Unwind_Ptr) data->tbase;
144     case DW_EH_PE_datarel:
145       return (_Unwind_Ptr) data->dbase;
146     default:
147       gcc_unreachable ();
148     }
149 }
150 
151 static int
152 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
153 {
154   struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
155   const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
156   long n, match;
157 #ifdef __FRV_FDPIC__
158   struct elf32_fdpic_loadaddr load_base;
159 #else
160   _Unwind_Ptr load_base;
161 #endif
162   const unsigned char *p;
163   const struct unw_eh_frame_hdr *hdr;
164   _Unwind_Ptr eh_frame;
165   struct object ob;
166   _Unwind_Ptr pc_low = 0, pc_high = 0;
167 
168   struct ext_dl_phdr_info
169     {
170       ElfW(Addr) dlpi_addr;
171       const char *dlpi_name;
172       const ElfW(Phdr) *dlpi_phdr;
173       ElfW(Half) dlpi_phnum;
174       unsigned long long int dlpi_adds;
175       unsigned long long int dlpi_subs;
176     };
177 
178   match = 0;
179   phdr = info->dlpi_phdr;
180   load_base = info->dlpi_addr;
181   p_eh_frame_hdr = NULL;
182   p_dynamic = NULL;
183 
184   struct frame_hdr_cache_element *prev_cache_entry = NULL,
185     *last_cache_entry = NULL;
186 
187   if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
188     {
189       static unsigned long long adds = -1ULL, subs;
190       struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
191 
192       /* We use a least recently used cache replacement policy.  Also,
193 	 the most recently used cache entries are placed at the head
194 	 of the search chain.  */
195 
196       if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
197 	{
198 	  /* Find data->pc in shared library cache.
199 	     Set load_base, p_eh_frame_hdr and p_dynamic
200 	     plus match from the cache and goto
201 	     "Read .eh_frame_hdr header." below.  */
202 
203 	  struct frame_hdr_cache_element *cache_entry;
204 
205 	  for (cache_entry = frame_hdr_cache_head;
206 	       cache_entry;
207 	       cache_entry = cache_entry->link)
208 	    {
209 	      if (data->pc >= cache_entry->pc_low
210 		  && data->pc < cache_entry->pc_high)
211 		{
212 		  load_base = cache_entry->load_base;
213 		  p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
214 		  p_dynamic = cache_entry->p_dynamic;
215 
216 		  /* And move the entry we're using to the head.  */
217 		  if (cache_entry != frame_hdr_cache_head)
218 		    {
219 		      prev_cache_entry->link = cache_entry->link;
220 		      cache_entry->link = frame_hdr_cache_head;
221 		      frame_hdr_cache_head = cache_entry;
222 		    }
223 		  goto found;
224 		}
225 
226 	      last_cache_entry = cache_entry;
227 	      /* Exit early if we found an unused entry.  */
228 	      if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
229 		break;
230 	      if (cache_entry->link != NULL)
231 		prev_cache_entry = cache_entry;
232 	    }
233 	}
234       else
235 	{
236 	  adds = einfo->dlpi_adds;
237 	  subs = einfo->dlpi_subs;
238 	  /* Initialize the cache.  Create a chain of cache entries,
239 	     with the final one terminated by a NULL link.  */
240 	  int i;
241 	  for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
242 	    {
243 	      frame_hdr_cache[i].pc_low = 0;
244 	      frame_hdr_cache[i].pc_high = 0;
245 	      frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
246 	    }
247 	  frame_hdr_cache[i-1].link = NULL;
248 	  frame_hdr_cache_head = &frame_hdr_cache[0];
249 	  data->check_cache = 0;
250 	}
251     }
252 
253   /* Make sure struct dl_phdr_info is at least as big as we need.  */
254   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
255 	     + sizeof (info->dlpi_phnum))
256     return -1;
257 
258   /* See if PC falls into one of the loaded segments.  Find the eh_frame
259      segment at the same time.  */
260   for (n = info->dlpi_phnum; --n >= 0; phdr++)
261     {
262       if (phdr->p_type == PT_LOAD)
263 	{
264 	  _Unwind_Ptr vaddr = (_Unwind_Ptr)
265 	    __RELOC_POINTER (phdr->p_vaddr, load_base);
266 	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
267 	    {
268 	      match = 1;
269 	      pc_low = vaddr;
270 	      pc_high =  vaddr + phdr->p_memsz;
271 	    }
272 	}
273       else if (phdr->p_type == PT_GNU_EH_FRAME)
274 	p_eh_frame_hdr = phdr;
275 #ifdef PT_SUNW_UNWIND
276       /* Sun ld emits PT_SUNW_UNWIND .eh_frame_hdr sections instead of
277 	 PT_SUNW_EH_FRAME/PT_GNU_EH_FRAME, so accept them as well.  */
278       else if (phdr->p_type == PT_SUNW_UNWIND)
279 	p_eh_frame_hdr = phdr;
280 #endif
281       else if (phdr->p_type == PT_DYNAMIC)
282 	p_dynamic = phdr;
283     }
284 
285   if (!match)
286     return 0;
287 
288   if (size >= sizeof (struct ext_dl_phdr_info))
289     {
290       /* Move the cache entry we're about to overwrite to the head of
291 	 the list.  If either last_cache_entry or prev_cache_entry are
292 	 NULL, that cache entry is already at the head.  */
293       if (last_cache_entry != NULL && prev_cache_entry != NULL)
294 	{
295 	  prev_cache_entry->link = last_cache_entry->link;
296 	  last_cache_entry->link = frame_hdr_cache_head;
297 	  frame_hdr_cache_head = last_cache_entry;
298 	}
299 
300       frame_hdr_cache_head->load_base = load_base;
301       frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
302       frame_hdr_cache_head->p_dynamic = p_dynamic;
303       frame_hdr_cache_head->pc_low = pc_low;
304       frame_hdr_cache_head->pc_high = pc_high;
305     }
306 
307  found:
308 
309   if (!p_eh_frame_hdr)
310     return 0;
311 
312   /* Read .eh_frame_hdr header.  */
313   hdr = (const struct unw_eh_frame_hdr *)
314     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
315   if (hdr->version != 1)
316     return 1;
317 
318 #ifdef CRT_GET_RFIB_DATA
319 # ifdef __i386__
320   data->dbase = NULL;
321   if (p_dynamic)
322     {
323       /* For dynamically linked executables and shared libraries,
324 	 DT_PLTGOT is the gp value for that object.  */
325       ElfW(Dyn) *dyn = (ElfW(Dyn) *)
326 	__RELOC_POINTER (p_dynamic->p_vaddr, load_base);
327       for (; dyn->d_tag != DT_NULL ; dyn++)
328 	if (dyn->d_tag == DT_PLTGOT)
329 	  {
330 	    data->dbase = (void *) dyn->d_un.d_ptr;
331 #if defined __linux__
332 	    /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
333 	       relocated it.  */
334 #elif defined __sun__ && defined __svr4__
335 	    /* On Solaris 2/x86, we need to do this ourselves.  */
336 	    data->dbase += load_base;
337 #endif
338 	    break;
339 	  }
340     }
341 # elif defined __FRV_FDPIC__ && defined __linux__
342   data->dbase = load_base.got_value;
343 # elif defined __x86_64__ && defined __sun__ && defined __svr4__
344   /* While CRT_GET_RFIB_DATA is also defined for 64-bit Solaris 10+/x86, it
345      doesn't apply since it uses DW_EH_PE_pcrel encoding.  */
346 # else
347 #  error What is DW_EH_PE_datarel base on this platform?
348 # endif
349 #endif
350 
351   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
352 				    base_from_cb_data (hdr->eh_frame_ptr_enc,
353 						       data),
354 				    (const unsigned char *) (hdr + 1),
355 				    &eh_frame);
356 
357   /* We require here specific table encoding to speed things up.
358      Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
359      as base, not the processor specific DW_EH_PE_datarel.  */
360   if (hdr->fde_count_enc != DW_EH_PE_omit
361       && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
362     {
363       _Unwind_Ptr fde_count;
364 
365       p = read_encoded_value_with_base (hdr->fde_count_enc,
366 					base_from_cb_data (hdr->fde_count_enc,
367 							   data),
368 					p, &fde_count);
369       /* Shouldn't happen.  */
370       if (fde_count == 0)
371 	return 1;
372       if ((((_Unwind_Ptr) p) & 3) == 0)
373 	{
374 	  struct fde_table {
375 	    signed initial_loc __attribute__ ((mode (SI)));
376 	    signed fde __attribute__ ((mode (SI)));
377 	  };
378 	  const struct fde_table *table = (const struct fde_table *) p;
379 	  size_t lo, hi, mid;
380 	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
381 	  fde *f;
382 	  unsigned int f_enc, f_enc_size;
383 	  _Unwind_Ptr range;
384 
385 	  mid = fde_count - 1;
386 	  if (data->pc < table[0].initial_loc + data_base)
387 	    return 1;
388 	  else if (data->pc < table[mid].initial_loc + data_base)
389 	    {
390 	      lo = 0;
391 	      hi = mid;
392 
393 	      while (lo < hi)
394 		{
395 		  mid = (lo + hi) / 2;
396 		  if (data->pc < table[mid].initial_loc + data_base)
397 		    hi = mid;
398 		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
399 		    lo = mid + 1;
400 		  else
401 		    break;
402 		}
403 
404 	      gcc_assert (lo < hi);
405 	    }
406 
407 	  f = (fde *) (table[mid].fde + data_base);
408 	  f_enc = get_fde_encoding (f);
409 	  f_enc_size = size_of_encoded_value (f_enc);
410 	  read_encoded_value_with_base (f_enc & 0x0f, 0,
411 					&f->pc_begin[f_enc_size], &range);
412 	  if (data->pc < table[mid].initial_loc + data_base + range)
413 	    data->ret = f;
414 	  data->func = (void *) (table[mid].initial_loc + data_base);
415 	  return 1;
416 	}
417     }
418 
419   /* We have no sorted search table, so need to go the slow way.
420      As soon as GLIBC will provide API so to notify that a library has been
421      removed, we could cache this (and thus use search_object).  */
422   ob.pc_begin = NULL;
423   ob.tbase = data->tbase;
424   ob.dbase = data->dbase;
425   ob.u.single = (fde *) eh_frame;
426   ob.s.i = 0;
427   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
428   data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
429   if (data->ret != NULL)
430     {
431       _Unwind_Ptr func;
432       unsigned int encoding = get_fde_encoding (data->ret);
433 
434       read_encoded_value_with_base (encoding,
435 				    base_from_cb_data (encoding, data),
436 				    data->ret->pc_begin, &func);
437       data->func = (void *) func;
438     }
439   return 1;
440 }
441 
442 const fde *
443 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
444 {
445   struct unw_eh_callback_data data;
446   const fde *ret;
447 
448   ret = _Unwind_Find_registered_FDE (pc, bases);
449   if (ret != NULL)
450     return ret;
451 
452   data.pc = (_Unwind_Ptr) pc;
453   data.tbase = NULL;
454   data.dbase = NULL;
455   data.func = NULL;
456   data.ret = NULL;
457   data.check_cache = 1;
458 
459   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
460     return NULL;
461 
462   if (data.ret)
463     {
464       bases->tbase = data.tbase;
465       bases->dbase = data.dbase;
466       bases->func = data.func;
467     }
468   return data.ret;
469 }
470 
471 #else
472 /* Prevent multiple include of header files.  */
473 #define _Unwind_Find_FDE _Unwind_Find_FDE
474 #include "unwind-dw2-fde.c"
475 #endif
476 
477 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
478 alias (_Unwind_Find_FDE);
479 #endif
480