Merge branch 'vendor/FILE'
[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 #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