unwind-dw2-fde-glibc.c revision 146895
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
56static 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
66struct unw_eh_callback_data
67{
68  _Unwind_Ptr pc;
69  void *tbase;
70  void *dbase;
71  void *func;
72  const fde *ret;
73};
74
75struct 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
86static _Unwind_Ptr
87base_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
107static 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
265const 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)
300alias (_Unwind_Find_FDE);
301#endif
302