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