unwind-dw2-fde-glibc.c revision 90075
1130917Sru/* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
2126826Sru   Contributed by Jakub Jelinek <jakub@redhat.com>.
3126826Sru
4126826Sru   This file is part of GNU CC.
5126826Sru
6126826Sru   GNU CC is free software; you can redistribute it and/or modify
7126826Sru   it under the terms of the GNU General Public License as published by
8130917Sru   the Free Software Foundation; either version 2, or (at your option)
9126826Sru   any later version.
10126826Sru
11126826Sru   GNU CC is distributed in the hope that it will be useful,
12126826Sru   but WITHOUT ANY WARRANTY; without even the implied warranty of
13126826Sru   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14126826Sru   GNU General Public License for more details.
15126826Sru
16126826Sru   You should have received a copy of the GNU General Public License
17126826Sru   along with GNU CC; see the file COPYING.  If not, write to
18126826Sru   the Free Software Foundation, 59 Temple Place - Suite 330,
19126826Sru   Boston, MA 02111-1307, USA.  */
20126826Sru
21126826Sru/* As a special exception, if you link this library with other files,
22126826Sru   some of which are compiled with GCC, to produce an executable,
23126826Sru   this library does not by itself cause the resulting executable
24126826Sru   to be covered by the GNU General Public License.
25126826Sru   This exception does not however invalidate any other reasons why
26126826Sru   the executable file might be covered by the GNU General Public License.  */
27141193Sru
28126826Sru/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
29126826Sru   segment and dl_iterate_phdr to avoid register/deregister calls at
30126826Sru   DSO load/unload.  */
31126826Sru
32126826Sru#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR.  */
33126826Sru#include "tconfig.h"
34126826Sru#ifndef inhibit_libc
35126826Sru#include <stddef.h>
36126826Sru#include <stdlib.h>
37126826Sru#include <link.h>
38126826Sru#endif
39126826Sru#include "tsystem.h"
40126826Sru#include "dwarf2.h"
41126826Sru#include "unwind.h"
42126826Sru#define NO_BASE_OF_ENCODED_VALUE
43126826Sru#include "unwind-pe.h"
44126826Sru#include "unwind-dw2-fde.h"
45126826Sru#include "gthr.h"
46126826Sru
47126826Sru#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
48126826Sru    && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
49126826Sru	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
50126826Sru
51126826Srustatic fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
52126826Sru
53126826Sru#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
54126826Sru#include "unwind-dw2-fde.c"
55126826Sru#undef _Unwind_Find_FDE
56126826Sru
57126826Sru#ifndef PT_GNU_EH_FRAME
58126826Sru#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
59126826Sru#endif
60126826Sru
61126826Srustruct unw_eh_callback_data
62126826Sru{
63126826Sru  _Unwind_Ptr pc;
64126826Sru  void *tbase;
65126826Sru  void *dbase;
66242997Sjoel  void *func;
67242997Sjoel  fde *ret;
68126826Sru};
69126826Sru
70126826Srustruct unw_eh_frame_hdr
71126826Sru{
72126826Sru  unsigned char version;
73126826Sru  unsigned char eh_frame_ptr_enc;
74126826Sru  unsigned char fde_count_enc;
75126826Sru  unsigned char table_enc;
76126826Sru};
77126826Sru
78126826Sru/* Like base_of_encoded_value, but take the base from a struct object
79242997Sjoel   instead of an _Unwind_Context.  */
80126826Sru
81126826Srustatic _Unwind_Ptr
82126826Srubase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
83126826Sru{
84126826Sru  if (encoding == DW_EH_PE_omit)
85126826Sru    return 0;
86126826Sru
87126826Sru  switch (encoding & 0x70)
88126826Sru    {
89126826Sru    case DW_EH_PE_absptr:
90126826Sru    case DW_EH_PE_pcrel:
91126826Sru    case DW_EH_PE_aligned:
92126826Sru      return 0;
93126826Sru
94126826Sru    case DW_EH_PE_textrel:
95126826Sru      return (_Unwind_Ptr) data->tbase;
96126826Sru    case DW_EH_PE_datarel:
97126826Sru      return (_Unwind_Ptr) data->dbase;
98126826Sru    }
99126826Sru  abort ();
100126826Sru}
101126826Sru
102126826Srustatic int
103126826Sru_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
104126826Sru{
105126826Sru  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
106126826Sru  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
107126826Sru  long n, match;
108126826Sru  _Unwind_Ptr load_base;
109126826Sru  const unsigned char *p;
110126826Sru  const struct unw_eh_frame_hdr *hdr;
111126826Sru  _Unwind_Ptr eh_frame;
112126826Sru  struct object ob;
113126826Sru
114126826Sru  /* Make sure struct dl_phdr_info is at least as big as we need.  */
115126826Sru  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
116126826Sru	     + sizeof (info->dlpi_phnum))
117126826Sru    return -1;
118126826Sru
119126826Sru  match = 0;
120126826Sru  phdr = info->dlpi_phdr;
121126826Sru  load_base = info->dlpi_addr;
122126826Sru  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