OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / unwind-dw2-fde-glibc.c
1 /* Copyright (C) 2001, 2002, 2003, 2004, 2005 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 2, 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    You should have received a copy of the GNU General Public License
17    along with GCC; see the file COPYING.  If not, write to
18    the Free Software Foundation, 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* As a special exception, if you link this library with other files,
22    some of which are compiled with GCC, to produce an executable,
23    this library does not by itself cause the resulting executable
24    to be covered by the GNU General Public License.
25    This exception does not however invalidate any other reasons why
26    the executable file might be covered by the GNU General Public License.  */
27
28 /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
29    segment and dl_iterate_phdr to avoid register/deregister calls at
30    DSO load/unload.  */
31
32 #ifndef _GNU_SOURCE
33 #define _GNU_SOURCE 1
34 #endif
35
36 #include "tconfig.h"
37 #include "tsystem.h"
38 #ifndef inhibit_libc
39 #include <link.h>
40 #endif
41 #include "coretypes.h"
42 #include "tm.h"
43 #include "dwarf2.h"
44 #include "unwind.h"
45 #define NO_BASE_OF_ENCODED_VALUE
46 #include "unwind-pe.h"
47 #include "unwind-dw2-fde.h"
48 #include "unwind-compat.h"
49 #include "gthr.h"
50
51 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
52     && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
53         || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
54
55 #ifndef __RELOC_POINTER
56 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
57 #endif
58
59 static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
60
61 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
62 #include "unwind-dw2-fde.c"
63 #undef _Unwind_Find_FDE
64
65 #ifndef PT_GNU_EH_FRAME
66 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
67 #endif
68
69 struct unw_eh_callback_data
70 {
71   _Unwind_Ptr pc;
72   void *tbase;
73   void *dbase;
74   void *func;
75   const fde *ret;
76   int check_cache;
77 };
78
79 struct unw_eh_frame_hdr
80 {
81   unsigned char version;
82   unsigned char eh_frame_ptr_enc;
83   unsigned char fde_count_enc;
84   unsigned char table_enc;
85 };
86
87 #define FRAME_HDR_CACHE_SIZE 8
88
89 static struct frame_hdr_cache_element
90 {
91   _Unwind_Ptr pc_low;
92   _Unwind_Ptr pc_high;
93   _Unwind_Ptr load_base;
94   const ElfW(Phdr) *p_eh_frame_hdr;
95   const ElfW(Phdr) *p_dynamic;
96   struct frame_hdr_cache_element *link;
97 } frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
98
99 static struct frame_hdr_cache_element *frame_hdr_cache_head;
100
101 /* Like base_of_encoded_value, but take the base from a struct
102    unw_eh_callback_data instead of an _Unwind_Context.  */
103
104 static _Unwind_Ptr
105 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
106 {
107   if (encoding == DW_EH_PE_omit)
108     return 0;
109
110   switch (encoding & 0x70)
111     {
112     case DW_EH_PE_absptr:
113     case DW_EH_PE_pcrel:
114     case DW_EH_PE_aligned:
115       return 0;
116
117     case DW_EH_PE_textrel:
118       return (_Unwind_Ptr) data->tbase;
119     case DW_EH_PE_datarel:
120       return (_Unwind_Ptr) data->dbase;
121     }
122   abort ();
123 }
124
125 static int
126 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
127 {
128   struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
129   const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
130   long n, match;
131 #ifdef __FRV_FDPIC__
132   struct elf32_fdpic_loadaddr load_base;
133 #else
134   _Unwind_Ptr load_base;
135 #endif
136   const unsigned char *p;
137   const struct unw_eh_frame_hdr *hdr;
138   _Unwind_Ptr eh_frame;
139   struct object ob;
140   
141   struct ext_dl_phdr_info
142     {
143       ElfW(Addr) dlpi_addr;
144       const char *dlpi_name;
145       const ElfW(Phdr) *dlpi_phdr;
146       ElfW(Half) dlpi_phnum;
147       unsigned long long int dlpi_adds;
148       unsigned long long int dlpi_subs;
149     };
150
151   match = 0;
152   phdr = info->dlpi_phdr;
153   load_base = info->dlpi_addr;
154   p_eh_frame_hdr = NULL;
155   p_dynamic = NULL;
156
157   struct frame_hdr_cache_element *prev_cache_entry = NULL,
158     *last_cache_entry = NULL;
159
160   if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
161     {
162       static unsigned long long adds = -1ULL, subs;
163       struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
164
165       /* We use a least recently used cache replacement policy.  Also,
166          the most recently used cache entries are placed at the head
167          of the search chain.  */
168
169       if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
170         {
171           /* Find data->pc in shared library cache.
172              Set load_base, p_eh_frame_hdr and p_dynamic
173              plus match from the cache and goto
174              "Read .eh_frame_hdr header." below.  */
175
176           struct frame_hdr_cache_element *cache_entry;
177
178           for (cache_entry = frame_hdr_cache_head;
179                cache_entry;
180                cache_entry = cache_entry->link)
181             {
182               if (data->pc >= cache_entry->pc_low
183                   && data->pc < cache_entry->pc_high)
184                 {
185                   load_base = cache_entry->load_base;
186                   p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
187                   p_dynamic = cache_entry->p_dynamic;
188
189                   /* And move the entry we're using to the head.  */
190                   if (cache_entry != frame_hdr_cache_head)
191                     {
192                       prev_cache_entry->link = cache_entry->link;
193                       cache_entry->link = frame_hdr_cache_head;
194                       frame_hdr_cache_head = cache_entry;
195                     }
196                   goto found;
197                 }
198                   
199               last_cache_entry = cache_entry;
200               /* Exit early if we found an unused entry.  */
201               if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
202                 break;
203               if (cache_entry->link != NULL)
204                 prev_cache_entry = cache_entry;           
205             }
206         }
207       else
208         {
209           adds = einfo->dlpi_adds;
210           subs = einfo->dlpi_subs;
211           /* Initialize the cache.  Create a chain of cache entries,
212              with the final one terminated by a NULL link.  */
213           int i;
214           for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
215             {
216               frame_hdr_cache[i].pc_low = 0;
217               frame_hdr_cache[i].pc_high = 0;
218               frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
219             }
220           frame_hdr_cache[i-1].link = NULL;
221           frame_hdr_cache_head = &frame_hdr_cache[0];
222           data->check_cache = 0;
223         }
224     }
225
226   /* Make sure struct dl_phdr_info is at least as big as we need.  */
227   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
228              + sizeof (info->dlpi_phnum))
229     return -1;
230  
231   _Unwind_Ptr pc_low = 0, pc_high = 0;
232
233   /* See if PC falls into one of the loaded segments.  Find the eh_frame
234      segment at the same time.  */
235   for (n = info->dlpi_phnum; --n >= 0; phdr++)
236     {
237       if (phdr->p_type == PT_LOAD)
238         {
239           _Unwind_Ptr vaddr = (_Unwind_Ptr)
240             __RELOC_POINTER (phdr->p_vaddr, load_base);
241           if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
242             {
243               match = 1;
244               pc_low = vaddr;
245               pc_high =  vaddr + phdr->p_memsz;
246             }
247         }
248       else if (phdr->p_type == PT_GNU_EH_FRAME)
249         p_eh_frame_hdr = phdr;
250       else if (phdr->p_type == PT_DYNAMIC)
251         p_dynamic = phdr;
252     }
253   
254   if (!match)
255     return 0;
256
257   if (size >= sizeof (struct ext_dl_phdr_info))
258     {
259       if (last_cache_entry != NULL)
260         {
261           prev_cache_entry->link = last_cache_entry->link;
262           last_cache_entry->link = frame_hdr_cache_head;
263           frame_hdr_cache_head = last_cache_entry;
264         }
265
266       frame_hdr_cache_head->load_base = load_base;
267       frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
268       frame_hdr_cache_head->p_dynamic = p_dynamic;
269       frame_hdr_cache_head->pc_low = pc_low;
270       frame_hdr_cache_head->pc_high = pc_high;
271     }
272
273  found:
274
275   if (!p_eh_frame_hdr)
276     return 0;
277
278   /* Read .eh_frame_hdr header.  */
279   hdr = (const struct unw_eh_frame_hdr *)
280     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
281   if (hdr->version != 1)
282     return 1;
283
284 #ifdef CRT_GET_RFIB_DATA
285 # ifdef __i386__
286   data->dbase = NULL;
287   if (p_dynamic)
288     {
289       /* For dynamically linked executables and shared libraries,
290          DT_PLTGOT is the gp value for that object.  */
291       ElfW(Dyn) *dyn = (ElfW(Dyn) *)
292         __RELOC_POINTER (p_dynamic->p_vaddr, load_base);
293       for (; dyn->d_tag != DT_NULL ; dyn++)
294         if (dyn->d_tag == DT_PLTGOT)
295           {
296             /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
297             data->dbase = (void *) dyn->d_un.d_ptr;
298             break;
299           }
300     }
301 # elif defined __FRV_FDPIC__ && defined __linux__
302   data->dbase = load_base.got_value;
303 # else
304 #  error What is DW_EH_PE_datarel base on this platform?
305 # endif
306 #endif
307
308   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
309                                     base_from_cb_data (hdr->eh_frame_ptr_enc,
310                                                        data),
311                                     (const unsigned char *) (hdr + 1),
312                                     &eh_frame);
313
314   /* We require here specific table encoding to speed things up.
315      Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
316      as base, not the processor specific DW_EH_PE_datarel.  */
317   if (hdr->fde_count_enc != DW_EH_PE_omit
318       && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
319     {
320       _Unwind_Ptr fde_count;
321
322       p = read_encoded_value_with_base (hdr->fde_count_enc,
323                                         base_from_cb_data (hdr->fde_count_enc,
324                                                            data),
325                                         p, &fde_count);
326       /* Shouldn't happen.  */
327       if (fde_count == 0)
328         return 1;
329       if ((((_Unwind_Ptr) p) & 3) == 0)
330         {
331           struct fde_table {
332             signed initial_loc __attribute__ ((mode (SI)));
333             signed fde __attribute__ ((mode (SI)));
334           };
335           const struct fde_table *table = (const struct fde_table *) p;
336           size_t lo, hi, mid;
337           _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
338           fde *f;
339           unsigned int f_enc, f_enc_size;
340           _Unwind_Ptr range;
341
342           mid = fde_count - 1;
343           if (data->pc < table[0].initial_loc + data_base)
344             return 1;
345           else if (data->pc < table[mid].initial_loc + data_base)
346             {
347               lo = 0;
348               hi = mid;
349
350               while (lo < hi)
351                 {
352                   mid = (lo + hi) / 2;
353                   if (data->pc < table[mid].initial_loc + data_base)
354                     hi = mid;
355                   else if (data->pc >= table[mid + 1].initial_loc + data_base)
356                     lo = mid + 1;
357                   else
358                     break;
359                 }
360
361               if (lo >= hi)
362                 __gxx_abort ();
363             }
364
365           f = (fde *) (table[mid].fde + data_base);
366           f_enc = get_fde_encoding (f);
367           f_enc_size = size_of_encoded_value (f_enc);
368           read_encoded_value_with_base (f_enc & 0x0f, 0,
369                                         &f->pc_begin[f_enc_size], &range);
370           if (data->pc < table[mid].initial_loc + data_base + range)
371             data->ret = f;
372           data->func = (void *) (table[mid].initial_loc + data_base);
373           return 1;
374         }
375     }
376
377   /* We have no sorted search table, so need to go the slow way.
378      As soon as GLIBC will provide API so to notify that a library has been
379      removed, we could cache this (and thus use search_object).  */
380   ob.pc_begin = NULL;
381   ob.tbase = data->tbase;
382   ob.dbase = data->dbase;
383   ob.u.single = (fde *) eh_frame;
384   ob.s.i = 0;
385   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
386   data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
387   if (data->ret != NULL)
388     {
389       unsigned int encoding = get_fde_encoding (data->ret);
390       read_encoded_value_with_base (encoding,
391                                     base_from_cb_data (encoding, data),
392                                     data->ret->pc_begin,
393                                     (_Unwind_Ptr *)&data->func);
394     }
395   return 1;
396 }
397
398 const fde *
399 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
400 {
401   struct unw_eh_callback_data data;
402   const fde *ret;
403
404   ret = _Unwind_Find_registered_FDE (pc, bases);
405   if (ret != NULL)
406     return ret;
407
408   data.pc = (_Unwind_Ptr) pc;
409   data.tbase = NULL;
410   data.dbase = NULL;
411   data.func = NULL;
412   data.ret = NULL;
413   data.check_cache = 1;
414
415   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
416     return NULL;
417
418   if (data.ret)
419     {
420       bases->tbase = data.tbase;
421       bases->dbase = data.dbase;
422       bases->func = data.func;
423     }
424   return data.ret;
425 }
426
427 #else
428 /* Prevent multiple include of header files.  */
429 #define _Unwind_Find_FDE _Unwind_Find_FDE
430 #include "unwind-dw2-fde.c"
431 #endif
432
433 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
434 alias (_Unwind_Find_FDE);
435 #endif