OSDN Git Service

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