In libobjc/: 2011-06-03 Nicola Pero <nicola.pero@meta-innovation.com>
[official-gcc.git] / gcc / unwind-dw2-fde-glibc.c
blobd8e3c0e934b047ddf104c86af09bc3d580440f20
1 /* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2009, 2010
2 Free Software Foundation, Inc.
3 Contributed by Jakub Jelinek <jakub@redhat.com>.
5 This file is part of GCC.
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.
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.
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.
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/>. */
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. */
30 #ifndef _GNU_SOURCE
31 #define _GNU_SOURCE 1
32 #endif
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"
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
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
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
67 #if defined(USE_PT_GNU_EH_FRAME)
69 #include <link.h>
71 #ifndef __RELOC_POINTER
72 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
73 #endif
75 static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
77 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
78 #include "unwind-dw2-fde.c"
79 #undef _Unwind_Find_FDE
81 #ifndef PT_GNU_EH_FRAME
82 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
83 #endif
85 struct unw_eh_callback_data
87 _Unwind_Ptr pc;
88 void *tbase;
89 void *dbase;
90 void *func;
91 const fde *ret;
92 int check_cache;
95 struct unw_eh_frame_hdr
97 unsigned char version;
98 unsigned char eh_frame_ptr_enc;
99 unsigned char fde_count_enc;
100 unsigned char table_enc;
103 #define FRAME_HDR_CACHE_SIZE 8
105 static struct frame_hdr_cache_element
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];
115 static struct frame_hdr_cache_element *frame_hdr_cache_head;
117 /* Like base_of_encoded_value, but take the base from a struct
118 unw_eh_callback_data instead of an _Unwind_Context. */
120 static _Unwind_Ptr
121 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
123 if (encoding == DW_EH_PE_omit)
124 return 0;
126 switch (encoding & 0x70)
128 case DW_EH_PE_absptr:
129 case DW_EH_PE_pcrel:
130 case DW_EH_PE_aligned:
131 return 0;
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 ();
142 static int
143 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
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;
159 struct ext_dl_phdr_info
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;
169 match = 0;
170 phdr = info->dlpi_phdr;
171 load_base = info->dlpi_addr;
172 p_eh_frame_hdr = NULL;
173 p_dynamic = NULL;
175 struct frame_hdr_cache_element *prev_cache_entry = NULL,
176 *last_cache_entry = NULL;
178 if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
180 static unsigned long long adds = -1ULL, subs;
181 struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
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. */
187 if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
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. */
194 struct frame_hdr_cache_element *cache_entry;
196 for (cache_entry = frame_hdr_cache_head;
197 cache_entry;
198 cache_entry = cache_entry->link)
200 if (data->pc >= cache_entry->pc_low
201 && data->pc < cache_entry->pc_high)
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;
207 /* And move the entry we're using to the head. */
208 if (cache_entry != frame_hdr_cache_head)
210 prev_cache_entry->link = cache_entry->link;
211 cache_entry->link = frame_hdr_cache_head;
212 frame_hdr_cache_head = cache_entry;
214 goto found;
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;
225 else
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++)
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];
238 frame_hdr_cache[i-1].link = NULL;
239 frame_hdr_cache_head = &frame_hdr_cache[0];
240 data->check_cache = 0;
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;
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++)
253 if (phdr->p_type == PT_LOAD)
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)
259 match = 1;
260 pc_low = vaddr;
261 pc_high = vaddr + phdr->p_memsz;
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;
276 if (!match)
277 return 0;
279 if (size >= sizeof (struct ext_dl_phdr_info))
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)
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;
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;
298 found:
300 if (!p_eh_frame_hdr)
301 return 0;
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;
309 #ifdef CRT_GET_RFIB_DATA
310 # ifdef __i386__
311 data->dbase = NULL;
312 if (p_dynamic)
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)
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;
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
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);
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))
354 _Unwind_Ptr fde_count;
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)
365 struct fde_table {
366 signed initial_loc __attribute__ ((mode (SI)));
367 signed fde __attribute__ ((mode (SI)));
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;
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)
381 lo = 0;
382 hi = mid;
384 while (lo < hi)
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;
395 gcc_assert (lo < hi);
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;
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)
422 _Unwind_Ptr func;
423 unsigned int encoding = get_fde_encoding (data->ret);
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;
430 return 1;
433 const fde *
434 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
436 struct unw_eh_callback_data data;
437 const fde *ret;
439 ret = _Unwind_Find_registered_FDE (pc, bases);
440 if (ret != NULL)
441 return ret;
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;
450 if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
451 return NULL;
453 if (data.ret)
455 bases->tbase = data.tbase;
456 bases->dbase = data.dbase;
457 bases->func = data.func;
459 return data.ret;
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
468 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
469 alias (_Unwind_Find_FDE);
470 #endif