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