Merge branch 'vendor/GCC47'
[dragonfly.git] / contrib / gcc-4.7 / libgcc / unwind-dw2-fde-dip.c
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 # 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(__OpenBSD__)
65 # define ElfW(type) Elf_##type
66 # define USE_PT_GNU_EH_FRAME
67 #endif
68
69 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
70     && defined(TARGET_DL_ITERATE_PHDR) \
71     && (defined(__OpenBSD__) || defined(__NetBSD__))
72 # define ElfW(n) Elf_##n
73 # define USE_PT_GNU_EH_FRAME
74 #endif
75
76 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
77     && defined(TARGET_DL_ITERATE_PHDR) \
78     && defined(__sun__) && defined(__svr4__)
79 # define USE_PT_GNU_EH_FRAME
80 #endif
81
82 #if defined(USE_PT_GNU_EH_FRAME)
83
84 #include <link.h>
85
86 #ifndef __RELOC_POINTER
87 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
88 #endif
89
90 static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
91
92 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
93 #include "unwind-dw2-fde.c"
94 #undef _Unwind_Find_FDE
95
96 #ifndef PT_GNU_EH_FRAME
97 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
98 #endif
99
100 struct unw_eh_callback_data
101 {
102   _Unwind_Ptr pc;
103   void *tbase;
104   void *dbase;
105   void *func;
106   const fde *ret;
107   int check_cache;
108 };
109
110 struct unw_eh_frame_hdr
111 {
112   unsigned char version;
113   unsigned char eh_frame_ptr_enc;
114   unsigned char fde_count_enc;
115   unsigned char table_enc;
116 };
117
118 #define FRAME_HDR_CACHE_SIZE 8
119
120 static struct frame_hdr_cache_element
121 {
122   _Unwind_Ptr pc_low;
123   _Unwind_Ptr pc_high;
124   _Unwind_Ptr load_base;
125   const ElfW(Phdr) *p_eh_frame_hdr;
126   const ElfW(Phdr) *p_dynamic;
127   struct frame_hdr_cache_element *link;
128 } frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
129
130 static struct frame_hdr_cache_element *frame_hdr_cache_head;
131
132 /* Like base_of_encoded_value, but take the base from a struct
133    unw_eh_callback_data instead of an _Unwind_Context.  */
134
135 static _Unwind_Ptr
136 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
137 {
138   if (encoding == DW_EH_PE_omit)
139     return 0;
140
141   switch (encoding & 0x70)
142     {
143     case DW_EH_PE_absptr:
144     case DW_EH_PE_pcrel:
145     case DW_EH_PE_aligned:
146       return 0;
147
148     case DW_EH_PE_textrel:
149       return (_Unwind_Ptr) data->tbase;
150     case DW_EH_PE_datarel:
151       return (_Unwind_Ptr) data->dbase;
152     default:
153       gcc_unreachable ();
154     }
155 }
156
157 static int
158 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
159 {
160   struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
161   const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
162   long n, match;
163 #ifdef __FRV_FDPIC__
164   struct elf32_fdpic_loadaddr load_base;
165 #else
166   _Unwind_Ptr load_base;
167 #endif
168   const unsigned char *p;
169   const struct unw_eh_frame_hdr *hdr;
170   _Unwind_Ptr eh_frame;
171   struct object ob;
172   _Unwind_Ptr pc_low = 0, pc_high = 0;
173
174   struct ext_dl_phdr_info
175     {
176       ElfW(Addr) dlpi_addr;
177       const char *dlpi_name;
178       const ElfW(Phdr) *dlpi_phdr;
179       ElfW(Half) dlpi_phnum;
180       unsigned long long int dlpi_adds;
181       unsigned long long int dlpi_subs;
182     };
183
184   match = 0;
185   phdr = info->dlpi_phdr;
186   load_base = info->dlpi_addr;
187   p_eh_frame_hdr = NULL;
188   p_dynamic = NULL;
189
190   struct frame_hdr_cache_element *prev_cache_entry = NULL,
191     *last_cache_entry = NULL;
192
193   if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
194     {
195       static unsigned long long adds = -1ULL, subs;
196       struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
197
198       /* We use a least recently used cache replacement policy.  Also,
199          the most recently used cache entries are placed at the head
200          of the search chain.  */
201
202       if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
203         {
204           /* Find data->pc in shared library cache.
205              Set load_base, p_eh_frame_hdr and p_dynamic
206              plus match from the cache and goto
207              "Read .eh_frame_hdr header." below.  */
208
209           struct frame_hdr_cache_element *cache_entry;
210
211           for (cache_entry = frame_hdr_cache_head;
212                cache_entry;
213                cache_entry = cache_entry->link)
214             {
215               if (data->pc >= cache_entry->pc_low
216                   && data->pc < cache_entry->pc_high)
217                 {
218                   load_base = cache_entry->load_base;
219                   p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
220                   p_dynamic = cache_entry->p_dynamic;
221
222                   /* And move the entry we're using to the head.  */
223                   if (cache_entry != frame_hdr_cache_head)
224                     {
225                       prev_cache_entry->link = cache_entry->link;
226                       cache_entry->link = frame_hdr_cache_head;
227                       frame_hdr_cache_head = cache_entry;
228                     }
229                   goto found;
230                 }
231
232               last_cache_entry = cache_entry;
233               /* Exit early if we found an unused entry.  */
234               if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
235                 break;
236               if (cache_entry->link != NULL)
237                 prev_cache_entry = cache_entry;
238             }
239         }
240       else
241         {
242           adds = einfo->dlpi_adds;
243           subs = einfo->dlpi_subs;
244           /* Initialize the cache.  Create a chain of cache entries,
245              with the final one terminated by a NULL link.  */
246           int i;
247           for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
248             {
249               frame_hdr_cache[i].pc_low = 0;
250               frame_hdr_cache[i].pc_high = 0;
251               frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
252             }
253           frame_hdr_cache[i-1].link = NULL;
254           frame_hdr_cache_head = &frame_hdr_cache[0];
255           data->check_cache = 0;
256         }
257     }
258
259   /* Make sure struct dl_phdr_info is at least as big as we need.  */
260   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
261              + sizeof (info->dlpi_phnum))
262     return -1;
263
264   /* See if PC falls into one of the loaded segments.  Find the eh_frame
265      segment at the same time.  */
266   for (n = info->dlpi_phnum; --n >= 0; phdr++)
267     {
268       if (phdr->p_type == PT_LOAD)
269         {
270           _Unwind_Ptr vaddr = (_Unwind_Ptr)
271             __RELOC_POINTER (phdr->p_vaddr, load_base);
272           if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
273             {
274               match = 1;
275               pc_low = vaddr;
276               pc_high =  vaddr + phdr->p_memsz;
277             }
278         }
279       else if (phdr->p_type == PT_GNU_EH_FRAME)
280         p_eh_frame_hdr = phdr;
281 #ifdef PT_SUNW_UNWIND
282       /* Sun ld emits PT_SUNW_UNWIND .eh_frame_hdr sections instead of
283          PT_SUNW_EH_FRAME/PT_GNU_EH_FRAME, so accept them as well.  */
284       else if (phdr->p_type == PT_SUNW_UNWIND)
285         p_eh_frame_hdr = phdr;
286 #endif
287       else if (phdr->p_type == PT_DYNAMIC)
288         p_dynamic = phdr;
289     }
290
291   if (!match)
292     return 0;
293
294   if (size >= sizeof (struct ext_dl_phdr_info))
295     {
296       /* Move the cache entry we're about to overwrite to the head of
297          the list.  If either last_cache_entry or prev_cache_entry are
298          NULL, that cache entry is already at the head.  */
299       if (last_cache_entry != NULL && prev_cache_entry != NULL)
300         {
301           prev_cache_entry->link = last_cache_entry->link;
302           last_cache_entry->link = frame_hdr_cache_head;
303           frame_hdr_cache_head = last_cache_entry;
304         }
305
306       frame_hdr_cache_head->load_base = load_base;
307       frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
308       frame_hdr_cache_head->p_dynamic = p_dynamic;
309       frame_hdr_cache_head->pc_low = pc_low;
310       frame_hdr_cache_head->pc_high = pc_high;
311     }
312
313  found:
314
315   if (!p_eh_frame_hdr)
316     return 0;
317
318   /* Read .eh_frame_hdr header.  */
319   hdr = (const struct unw_eh_frame_hdr *)
320     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
321   if (hdr->version != 1)
322     return 1;
323
324 #ifdef CRT_GET_RFIB_DATA
325 # ifdef __i386__
326   data->dbase = NULL;
327   if (p_dynamic)
328     {
329       /* For dynamically linked executables and shared libraries,
330          DT_PLTGOT is the gp value for that object.  */
331       ElfW(Dyn) *dyn = (ElfW(Dyn) *)
332         __RELOC_POINTER (p_dynamic->p_vaddr, load_base);
333       for (; dyn->d_tag != DT_NULL ; dyn++)
334         if (dyn->d_tag == DT_PLTGOT)
335           {
336             data->dbase = (void *) dyn->d_un.d_ptr;
337 #if defined __linux__
338             /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
339                relocated it.  */
340 #elif defined __sun__ && defined __svr4__
341             /* On Solaris 2/x86, we need to do this ourselves.  */
342             data->dbase += load_base;
343 #endif
344             break;
345           }
346     }
347 # elif defined __FRV_FDPIC__ && defined __linux__
348   data->dbase = load_base.got_value;
349 # elif defined __x86_64__ && defined __sun__ && defined __svr4__
350   /* While CRT_GET_RFIB_DATA is also defined for 64-bit Solaris 10+/x86, it
351      doesn't apply since it uses DW_EH_PE_pcrel encoding.  */
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