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