unwind-dw2-fde-glibc.c revision 225736
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, 51 Franklin Street, Fifth Floor,
19   Boston, MA 02110-1301, 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
59static 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
69struct 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
79struct 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
89static 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
99static 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
104static _Unwind_Ptr
105base_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    default:
122      gcc_unreachable ();
123    }
124}
125
126static int
127_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
128{
129  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
130  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
131  long n, match;
132#ifdef __FRV_FDPIC__
133  struct elf32_fdpic_loadaddr load_base;
134#else
135  _Unwind_Ptr load_base;
136#endif
137  const unsigned char *p;
138  const struct unw_eh_frame_hdr *hdr;
139  _Unwind_Ptr eh_frame;
140  struct object ob;
141
142  struct ext_dl_phdr_info
143    {
144      ElfW(Addr) dlpi_addr;
145      const char *dlpi_name;
146      const ElfW(Phdr) *dlpi_phdr;
147      ElfW(Half) dlpi_phnum;
148      unsigned long long int dlpi_adds;
149      unsigned long long int dlpi_subs;
150    };
151
152  match = 0;
153  phdr = info->dlpi_phdr;
154  load_base = info->dlpi_addr;
155  p_eh_frame_hdr = NULL;
156  p_dynamic = NULL;
157
158  struct frame_hdr_cache_element *prev_cache_entry = NULL,
159    *last_cache_entry = NULL;
160
161  if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
162    {
163      static unsigned long long adds = -1ULL, subs;
164      struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
165
166      /* We use a least recently used cache replacement policy.  Also,
167	 the most recently used cache entries are placed at the head
168	 of the search chain.  */
169
170      if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
171	{
172	  /* Find data->pc in shared library cache.
173	     Set load_base, p_eh_frame_hdr and p_dynamic
174	     plus match from the cache and goto
175	     "Read .eh_frame_hdr header." below.  */
176
177	  struct frame_hdr_cache_element *cache_entry;
178
179	  for (cache_entry = frame_hdr_cache_head;
180	       cache_entry;
181	       cache_entry = cache_entry->link)
182	    {
183	      if (data->pc >= cache_entry->pc_low
184		  && data->pc < cache_entry->pc_high)
185		{
186		  load_base = cache_entry->load_base;
187		  p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
188		  p_dynamic = cache_entry->p_dynamic;
189
190		  /* And move the entry we're using to the head.  */
191		  if (cache_entry != frame_hdr_cache_head)
192		    {
193		      prev_cache_entry->link = cache_entry->link;
194		      cache_entry->link = frame_hdr_cache_head;
195		      frame_hdr_cache_head = cache_entry;
196		    }
197		  goto found;
198		}
199
200	      last_cache_entry = cache_entry;
201	      /* Exit early if we found an unused entry.  */
202	      if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
203		break;
204	      if (cache_entry->link != NULL)
205		prev_cache_entry = cache_entry;
206	    }
207	}
208      else
209	{
210	  adds = einfo->dlpi_adds;
211	  subs = einfo->dlpi_subs;
212	  /* Initialize the cache.  Create a chain of cache entries,
213	     with the final one terminated by a NULL link.  */
214	  int i;
215	  for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
216	    {
217	      frame_hdr_cache[i].pc_low = 0;
218	      frame_hdr_cache[i].pc_high = 0;
219	      frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
220	    }
221	  frame_hdr_cache[i-1].link = NULL;
222	  frame_hdr_cache_head = &frame_hdr_cache[0];
223	  data->check_cache = 0;
224	}
225    }
226
227  /* Make sure struct dl_phdr_info is at least as big as we need.  */
228  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
229	     + sizeof (info->dlpi_phnum))
230    return -1;
231
232  _Unwind_Ptr pc_low = 0, pc_high = 0;
233
234  /* See if PC falls into one of the loaded segments.  Find the eh_frame
235     segment at the same time.  */
236  for (n = info->dlpi_phnum; --n >= 0; phdr++)
237    {
238      if (phdr->p_type == PT_LOAD)
239	{
240	  _Unwind_Ptr vaddr = (_Unwind_Ptr)
241	    __RELOC_POINTER (phdr->p_vaddr, load_base);
242	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
243	    {
244	      match = 1;
245	      pc_low = vaddr;
246	      pc_high =  vaddr + phdr->p_memsz;
247	    }
248	}
249      else if (phdr->p_type == PT_GNU_EH_FRAME)
250	p_eh_frame_hdr = phdr;
251      else if (phdr->p_type == PT_DYNAMIC)
252	p_dynamic = phdr;
253    }
254
255  if (!match)
256    return 0;
257
258  if (size >= sizeof (struct ext_dl_phdr_info))
259    {
260      /* Move the cache entry we're about to overwrite to the head of
261	 the list.  If either last_cache_entry or prev_cache_entry are
262	 NULL, that cache entry is already at the head.  */
263      if (last_cache_entry != NULL && prev_cache_entry != NULL)
264	{
265	  prev_cache_entry->link = last_cache_entry->link;
266	  last_cache_entry->link = frame_hdr_cache_head;
267	  frame_hdr_cache_head = last_cache_entry;
268	}
269
270      frame_hdr_cache_head->load_base = load_base;
271      frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
272      frame_hdr_cache_head->p_dynamic = p_dynamic;
273      frame_hdr_cache_head->pc_low = pc_low;
274      frame_hdr_cache_head->pc_high = pc_high;
275    }
276
277 found:
278
279  if (!p_eh_frame_hdr)
280    return 0;
281
282  /* Read .eh_frame_hdr header.  */
283  hdr = (const struct unw_eh_frame_hdr *)
284    __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
285  if (hdr->version != 1)
286    return 1;
287
288#ifdef CRT_GET_RFIB_DATA
289# ifdef __i386__
290  data->dbase = NULL;
291  if (p_dynamic)
292    {
293      /* For dynamically linked executables and shared libraries,
294	 DT_PLTGOT is the gp value for that object.  */
295      ElfW(Dyn) *dyn = (ElfW(Dyn) *)
296	__RELOC_POINTER (p_dynamic->p_vaddr, load_base);
297      for (; dyn->d_tag != DT_NULL ; dyn++)
298	if (dyn->d_tag == DT_PLTGOT)
299	  {
300	    /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
301	    data->dbase = (void *) dyn->d_un.d_ptr;
302	    break;
303	  }
304    }
305# elif defined __FRV_FDPIC__ && defined __linux__
306  data->dbase = load_base.got_value;
307# else
308#  error What is DW_EH_PE_datarel base on this platform?
309# endif
310#endif
311
312  p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
313				    base_from_cb_data (hdr->eh_frame_ptr_enc,
314						       data),
315				    (const unsigned char *) (hdr + 1),
316				    &eh_frame);
317
318  /* We require here specific table encoding to speed things up.
319     Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
320     as base, not the processor specific DW_EH_PE_datarel.  */
321  if (hdr->fde_count_enc != DW_EH_PE_omit
322      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
323    {
324      _Unwind_Ptr fde_count;
325
326      p = read_encoded_value_with_base (hdr->fde_count_enc,
327					base_from_cb_data (hdr->fde_count_enc,
328							   data),
329					p, &fde_count);
330      /* Shouldn't happen.  */
331      if (fde_count == 0)
332	return 1;
333      if ((((_Unwind_Ptr) p) & 3) == 0)
334	{
335	  struct fde_table {
336	    signed initial_loc __attribute__ ((mode (SI)));
337	    signed fde __attribute__ ((mode (SI)));
338	  };
339	  const struct fde_table *table = (const struct fde_table *) p;
340	  size_t lo, hi, mid;
341	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
342	  fde *f;
343	  unsigned int f_enc, f_enc_size;
344	  _Unwind_Ptr range;
345
346	  mid = fde_count - 1;
347	  if (data->pc < table[0].initial_loc + data_base)
348	    return 1;
349	  else if (data->pc < table[mid].initial_loc + data_base)
350	    {
351	      lo = 0;
352	      hi = mid;
353
354	      while (lo < hi)
355		{
356		  mid = (lo + hi) / 2;
357		  if (data->pc < table[mid].initial_loc + data_base)
358		    hi = mid;
359		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
360		    lo = mid + 1;
361		  else
362		    break;
363		}
364
365	      gcc_assert (lo < hi);
366	    }
367
368	  f = (fde *) (table[mid].fde + data_base);
369	  f_enc = get_fde_encoding (f);
370	  f_enc_size = size_of_encoded_value (f_enc);
371	  read_encoded_value_with_base (f_enc & 0x0f, 0,
372					&f->pc_begin[f_enc_size], &range);
373	  if (data->pc < table[mid].initial_loc + data_base + range)
374	    data->ret = f;
375	  data->func = (void *) (table[mid].initial_loc + data_base);
376	  return 1;
377	}
378    }
379
380  /* We have no sorted search table, so need to go the slow way.
381     As soon as GLIBC will provide API so to notify that a library has been
382     removed, we could cache this (and thus use search_object).  */
383  ob.pc_begin = NULL;
384  ob.tbase = data->tbase;
385  ob.dbase = data->dbase;
386  ob.u.single = (fde *) eh_frame;
387  ob.s.i = 0;
388  ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
389  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
390  if (data->ret != NULL)
391    {
392      _Unwind_Ptr func;
393      unsigned int encoding = get_fde_encoding (data->ret);
394
395      read_encoded_value_with_base (encoding,
396				    base_from_cb_data (encoding, data),
397				    data->ret->pc_begin, &func);
398      data->func = (void *) func;
399    }
400  return 1;
401}
402
403const fde *
404_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
405{
406  struct unw_eh_callback_data data;
407  const fde *ret;
408
409  ret = _Unwind_Find_registered_FDE (pc, bases);
410  if (ret != NULL)
411    return ret;
412
413  data.pc = (_Unwind_Ptr) pc;
414  data.tbase = NULL;
415  data.dbase = NULL;
416  data.func = NULL;
417  data.ret = NULL;
418  data.check_cache = 1;
419
420  if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
421    return NULL;
422
423  if (data.ret)
424    {
425      bases->tbase = data.tbase;
426      bases->dbase = data.dbase;
427      bases->func = data.func;
428    }
429  return data.ret;
430}
431
432#else
433/* Prevent multiple include of header files.  */
434#define _Unwind_Find_FDE _Unwind_Find_FDE
435#include "unwind-dw2-fde.c"
436#endif
437
438#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
439alias (_Unwind_Find_FDE);
440#endif
441