OSDN Git Service

Fix linux make profiledbootstrap.
[pf3gnuchains/gcc-fork.git] / gcc / unwind-dw2-fde-glibc.c
1 /* Copyright (C) 2001, 2002, 2003, 2004 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 "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR.  */
37 #include "tconfig.h"
38 #include "tsystem.h"
39 #ifndef inhibit_libc
40 #include <link.h>
41 #endif
42 #include "coretypes.h"
43 #include "tm.h"
44 #include "dwarf2.h"
45 #include "unwind.h"
46 #define NO_BASE_OF_ENCODED_VALUE
47 #include "unwind-pe.h"
48 #include "unwind-dw2-fde.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 };
77
78 struct unw_eh_frame_hdr
79 {
80   unsigned char version;
81   unsigned char eh_frame_ptr_enc;
82   unsigned char fde_count_enc;
83   unsigned char table_enc;
84 };
85
86 /* Like base_of_encoded_value, but take the base from a struct
87    unw_eh_callback_data instead of an _Unwind_Context.  */
88
89 static _Unwind_Ptr
90 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
91 {
92   if (encoding == DW_EH_PE_omit)
93     return 0;
94
95   switch (encoding & 0x70)
96     {
97     case DW_EH_PE_absptr:
98     case DW_EH_PE_pcrel:
99     case DW_EH_PE_aligned:
100       return 0;
101
102     case DW_EH_PE_textrel:
103       return (_Unwind_Ptr) data->tbase;
104     case DW_EH_PE_datarel:
105       return (_Unwind_Ptr) data->dbase;
106     }
107   abort ();
108 }
109
110 static int
111 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
112 {
113   struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
114   const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
115   long n, match;
116 #ifdef __FRV_FDPIC__
117   struct elf32_fdpic_loadaddr load_base;
118 #else
119   _Unwind_Ptr load_base;
120 #endif
121   const unsigned char *p;
122   const struct unw_eh_frame_hdr *hdr;
123   _Unwind_Ptr eh_frame;
124   struct object ob;
125
126   /* Make sure struct dl_phdr_info is at least as big as we need.  */
127   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
128              + sizeof (info->dlpi_phnum))
129     return -1;
130
131   match = 0;
132   phdr = info->dlpi_phdr;
133   load_base = info->dlpi_addr;
134   p_eh_frame_hdr = NULL;
135   p_dynamic = NULL;
136
137   /* See if PC falls into one of the loaded segments.  Find the eh_frame
138      segment at the same time.  */
139   for (n = info->dlpi_phnum; --n >= 0; phdr++)
140     {
141       if (phdr->p_type == PT_LOAD)
142         {
143           _Unwind_Ptr vaddr = (_Unwind_Ptr)
144             __RELOC_POINTER (phdr->p_vaddr, load_base);
145           if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
146             match = 1;
147         }
148       else if (phdr->p_type == PT_GNU_EH_FRAME)
149         p_eh_frame_hdr = phdr;
150       else if (phdr->p_type == PT_DYNAMIC)
151         p_dynamic = phdr;
152     }
153   if (!match || !p_eh_frame_hdr)
154     return 0;
155
156   /* Read .eh_frame_hdr header.  */
157   hdr = (const struct unw_eh_frame_hdr *)
158     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
159   if (hdr->version != 1)
160     return 1;
161
162 #ifdef CRT_GET_RFIB_DATA
163 # ifdef __i386__
164   data->dbase = NULL;
165   if (p_dynamic)
166     {
167       /* For dynamically linked executables and shared libraries,
168          DT_PLTGOT is the gp value for that object.  */
169       ElfW(Dyn) *dyn = (ElfW(Dyn) *)
170         __RELOC_POINTER (p_dynamic->p_vaddr, load_base);
171       for (; dyn->d_tag != DT_NULL ; dyn++)
172         if (dyn->d_tag == DT_PLTGOT)
173           {
174             /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
175             data->dbase = (void *) dyn->d_un.d_ptr;
176             break;
177           }
178     }
179 # elif defined __FRV_FDPIC__ && defined __linux__
180   data->dbase = load_base.got_value;
181 # else
182 #  error What is DW_EH_PE_datarel base on this platform?
183 # endif
184 #endif
185
186   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
187                                     base_from_cb_data (hdr->eh_frame_ptr_enc,
188                                                        data),
189                                     (const unsigned char *) (hdr + 1),
190                                     &eh_frame);
191
192   /* We require here specific table encoding to speed things up.
193      Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
194      as base, not the processor specific DW_EH_PE_datarel.  */
195   if (hdr->fde_count_enc != DW_EH_PE_omit
196       && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
197     {
198       _Unwind_Ptr fde_count;
199
200       p = read_encoded_value_with_base (hdr->fde_count_enc,
201                                         base_from_cb_data (hdr->fde_count_enc,
202                                                            data),
203                                         p, &fde_count);
204       /* Shouldn't happen.  */
205       if (fde_count == 0)
206         return 1;
207       if ((((_Unwind_Ptr) p) & 3) == 0)
208         {
209           struct fde_table {
210             signed initial_loc __attribute__ ((mode (SI)));
211             signed fde __attribute__ ((mode (SI)));
212           };
213           const struct fde_table *table = (const struct fde_table *) p;
214           size_t lo, hi, mid;
215           _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
216           fde *f;
217           unsigned int f_enc, f_enc_size;
218           _Unwind_Ptr range;
219
220           mid = fde_count - 1;
221           if (data->pc < table[0].initial_loc + data_base)
222             return 1;
223           else if (data->pc < table[mid].initial_loc + data_base)
224             {
225               lo = 0;
226               hi = mid;
227
228               while (lo < hi)
229                 {
230                   mid = (lo + hi) / 2;
231                   if (data->pc < table[mid].initial_loc + data_base)
232                     hi = mid;
233                   else if (data->pc >= table[mid + 1].initial_loc + data_base)
234                     lo = mid + 1;
235                   else
236                     break;
237                 }
238
239               if (lo >= hi)
240                 __gxx_abort ();
241             }
242
243           f = (fde *) (table[mid].fde + data_base);
244           f_enc = get_fde_encoding (f);
245           f_enc_size = size_of_encoded_value (f_enc);
246           read_encoded_value_with_base (f_enc & 0x0f, 0,
247                                         &f->pc_begin[f_enc_size], &range);
248           if (data->pc < table[mid].initial_loc + data_base + range)
249             data->ret = f;
250           data->func = (void *) (table[mid].initial_loc + data_base);
251           return 1;
252         }
253     }
254
255   /* We have no sorted search table, so need to go the slow way.
256      As soon as GLIBC will provide API so to notify that a library has been
257      removed, we could cache this (and thus use search_object).  */
258   ob.pc_begin = NULL;
259   ob.tbase = data->tbase;
260   ob.dbase = data->dbase;
261   ob.u.single = (fde *) eh_frame;
262   ob.s.i = 0;
263   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
264   data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
265   if (data->ret != NULL)
266     {
267       unsigned int encoding = get_fde_encoding (data->ret);
268       read_encoded_value_with_base (encoding,
269                                     base_from_cb_data (encoding, data),
270                                     data->ret->pc_begin,
271                                     (_Unwind_Ptr *)&data->func);
272     }
273   return 1;
274 }
275
276 const fde *
277 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
278 {
279   struct unw_eh_callback_data data;
280   const fde *ret;
281
282   ret = _Unwind_Find_registered_FDE (pc, bases);
283   if (ret != NULL)
284     return ret;
285
286   data.pc = (_Unwind_Ptr) pc;
287   data.tbase = NULL;
288   data.dbase = NULL;
289   data.func = NULL;
290   data.ret = NULL;
291
292   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
293     return NULL;
294
295   if (data.ret)
296     {
297       bases->tbase = data.tbase;
298       bases->dbase = data.dbase;
299       bases->func = data.func;
300     }
301   return data.ret;
302 }
303
304 #else
305 /* Prevent multiple include of header files.  */
306 #define _Unwind_Find_FDE _Unwind_Find_FDE
307 #include "unwind-dw2-fde.c"
308 #endif