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