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