unwind-dw2-fde-glibc.c revision 132718
1279377Simp/* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
2279377Simp   Contributed by Jakub Jelinek <jakub@redhat.com>.
3279377Simp
4279377Simp   This file is part of GCC.
5279377Simp
6279377Simp   GCC is free software; you can redistribute it and/or modify
7279377Simp   it under the terms of the GNU General Public License as published by
8279377Simp   the Free Software Foundation; either version 2, or (at your option)
9279377Simp   any later version.
10279377Simp
11279377Simp   GCC is distributed in the hope that it will be useful,
12279377Simp   but WITHOUT ANY WARRANTY; without even the implied warranty of
13279377Simp   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14279377Simp   GNU General Public License for more details.
15279377Simp
16279377Simp   You should have received a copy of the GNU General Public License
17279377Simp   along with GCC; see the file COPYING.  If not, write to
18279377Simp   the Free Software Foundation, 59 Temple Place - Suite 330,
19279377Simp   Boston, MA 02111-1307, USA.  */
20279377Simp
21279377Simp/* As a special exception, if you link this library with other files,
22279377Simp   some of which are compiled with GCC, to produce an executable,
23279377Simp   this library does not by itself cause the resulting executable
24279377Simp   to be covered by the GNU General Public License.
25279377Simp   This exception does not however invalidate any other reasons why
26279377Simp   the executable file might be covered by the GNU General Public License.  */
27279377Simp
28279377Simp/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
29279377Simp   segment and dl_iterate_phdr to avoid register/deregister calls at
30279377Simp   DSO load/unload.  */
31279377Simp
32279377Simp#ifndef _GNU_SOURCE
33279377Simp#define _GNU_SOURCE 1
34279377Simp#endif
35279377Simp
36279377Simp#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR.  */
37279377Simp#include "tconfig.h"
38279377Simp#include "tsystem.h"
39279377Simp#ifndef inhibit_libc
40279377Simp#include <link.h>
41279377Simp#endif
42279377Simp#include "coretypes.h"
43279377Simp#include "tm.h"
44279377Simp#include "dwarf2.h"
45279377Simp#include "unwind.h"
46279377Simp#define NO_BASE_OF_ENCODED_VALUE
47279377Simp#include "unwind-pe.h"
48279377Simp#include "unwind-dw2-fde.h"
49279377Simp#include "gthr.h"
50279377Simp
51279377Simp#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
52279377Simp    && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
53279377Simp	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
54279377Simp
55279377Simpstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
56279377Simp
57279377Simp#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
58279377Simp#include "unwind-dw2-fde.c"
59279377Simp#undef _Unwind_Find_FDE
60279377Simp
61279377Simp#ifndef PT_GNU_EH_FRAME
62279377Simp#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
63279377Simp#endif
64279377Simp
65279377Simpstruct unw_eh_callback_data
66279377Simp{
67279377Simp  _Unwind_Ptr pc;
68279377Simp  void *tbase;
69279377Simp  void *dbase;
70279377Simp  void *func;
71279377Simp  const fde *ret;
72279377Simp};
73279377Simp
74279377Simpstruct unw_eh_frame_hdr
75279377Simp{
76279377Simp  unsigned char version;
77279377Simp  unsigned char eh_frame_ptr_enc;
78279377Simp  unsigned char fde_count_enc;
79279377Simp  unsigned char table_enc;
80279377Simp};
81279377Simp
82279377Simp/* Like base_of_encoded_value, but take the base from a struct
83279377Simp   unw_eh_callback_data instead of an _Unwind_Context.  */
84279377Simp
85279377Simpstatic _Unwind_Ptr
86279377Simpbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
87279377Simp{
88279377Simp  if (encoding == DW_EH_PE_omit)
89279377Simp    return 0;
90279377Simp
91279377Simp  switch (encoding & 0x70)
92279377Simp    {
93279377Simp    case DW_EH_PE_absptr:
94279377Simp    case DW_EH_PE_pcrel:
95279377Simp    case DW_EH_PE_aligned:
96279377Simp      return 0;
97279377Simp
98279377Simp    case DW_EH_PE_textrel:
99279377Simp      return (_Unwind_Ptr) data->tbase;
100279377Simp    case DW_EH_PE_datarel:
101279377Simp      return (_Unwind_Ptr) data->dbase;
102279377Simp    }
103279377Simp  abort ();
104279377Simp}
105279377Simp
106279377Simpstatic int
107279377Simp_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
108279377Simp{
109279377Simp  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
110279377Simp  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
111279377Simp  long n, match;
112279377Simp  _Unwind_Ptr load_base;
113279377Simp  const unsigned char *p;
114279377Simp  const struct unw_eh_frame_hdr *hdr;
115279377Simp  _Unwind_Ptr eh_frame;
116279377Simp  struct object ob;
117279377Simp
118279377Simp  /* Make sure struct dl_phdr_info is at least as big as we need.  */
119279377Simp  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
120279377Simp	     + sizeof (info->dlpi_phnum))
121279377Simp    return -1;
122279377Simp
123279377Simp  match = 0;
124279377Simp  phdr = info->dlpi_phdr;
125279377Simp  load_base = info->dlpi_addr;
126279377Simp  p_eh_frame_hdr = NULL;
127279377Simp  p_dynamic = NULL;
128279377Simp
129279377Simp  /* See if PC falls into one of the loaded segments.  Find the eh_frame
130279377Simp     segment at the same time.  */
131279377Simp  for (n = info->dlpi_phnum; --n >= 0; phdr++)
132279377Simp    {
133279377Simp      if (phdr->p_type == PT_LOAD)
134279377Simp	{
135279377Simp	  _Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
136279377Simp	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
137279377Simp	    match = 1;
138279377Simp	}
139279377Simp      else if (phdr->p_type == PT_GNU_EH_FRAME)
140279377Simp	p_eh_frame_hdr = phdr;
141279377Simp      else if (phdr->p_type == PT_DYNAMIC)
142279377Simp	p_dynamic = phdr;
143279377Simp    }
144279377Simp  if (!match || !p_eh_frame_hdr)
145279377Simp    return 0;
146279377Simp
147279377Simp  /* Read .eh_frame_hdr header.  */
148279377Simp  hdr = (const struct unw_eh_frame_hdr *)
149279377Simp	(p_eh_frame_hdr->p_vaddr + load_base);
150279377Simp  if (hdr->version != 1)
151279377Simp    return 1;
152279377Simp
153279377Simp#ifdef CRT_GET_RFIB_DATA
154279377Simp# ifdef __i386__
155279377Simp  data->dbase = NULL;
156279377Simp  if (p_dynamic)
157279377Simp    {
158279377Simp      /* For dynamically linked executables and shared libraries,
159279377Simp	 DT_PLTGOT is the gp value for that object.  */
160279377Simp      ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base);
161279377Simp      for (; dyn->d_tag != DT_NULL ; dyn++)
162279377Simp	if (dyn->d_tag == DT_PLTGOT)
163279377Simp	  {
164279377Simp	    /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
165279377Simp	    data->dbase = (void *) dyn->d_un.d_ptr;
166279377Simp	    break;
167279377Simp	  }
168279377Simp    }
169279377Simp# else
170279377Simp#  error What is DW_EH_PE_datarel base on this platform?
171279377Simp# endif
172279377Simp#endif
173279377Simp
174279377Simp  p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
175279377Simp				    base_from_cb_data (hdr->eh_frame_ptr_enc,
176279377Simp						       data),
177279377Simp				    (const unsigned char *) (hdr + 1),
178279377Simp				    &eh_frame);
179279377Simp
180279377Simp  /* We require here specific table encoding to speed things up.
181279377Simp     Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
182279377Simp     as base, not the processor specific DW_EH_PE_datarel.  */
183279377Simp  if (hdr->fde_count_enc != DW_EH_PE_omit
184279377Simp      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
185279377Simp    {
186279377Simp      _Unwind_Ptr fde_count;
187279377Simp
188279377Simp      p = read_encoded_value_with_base (hdr->fde_count_enc,
189279377Simp					base_from_cb_data (hdr->fde_count_enc,
190279377Simp							   data),
191279377Simp					p, &fde_count);
192279377Simp      /* Shouldn't happen.  */
193279377Simp      if (fde_count == 0)
194279377Simp	return 1;
195279377Simp      if ((((_Unwind_Ptr) p) & 3) == 0)
196279377Simp	{
197279377Simp	  struct fde_table {
198279377Simp	    signed initial_loc __attribute__ ((mode (SI)));
199279377Simp	    signed fde __attribute__ ((mode (SI)));
200279377Simp	  };
201279377Simp	  const struct fde_table *table = (const struct fde_table *) p;
202279377Simp	  size_t lo, hi, mid;
203279377Simp	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
204279377Simp	  fde *f;
205279377Simp	  unsigned int f_enc, f_enc_size;
206279377Simp	  _Unwind_Ptr range;
207279377Simp
208279377Simp	  mid = fde_count - 1;
209279377Simp	  if (data->pc < table[0].initial_loc + data_base)
210279377Simp	    return 1;
211279377Simp	  else if (data->pc < table[mid].initial_loc + data_base)
212279377Simp	    {
213279377Simp	      lo = 0;
214279377Simp	      hi = mid;
215279377Simp
216279377Simp	      while (lo < hi)
217279377Simp		{
218279377Simp		  mid = (lo + hi) / 2;
219279377Simp		  if (data->pc < table[mid].initial_loc + data_base)
220279377Simp		    hi = mid;
221279377Simp		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
222279377Simp		    lo = mid + 1;
223279377Simp		  else
224279377Simp		    break;
225279377Simp		}
226279377Simp
227279377Simp	      if (lo >= hi)
228279377Simp		__gxx_abort ();
229279377Simp	    }
230279377Simp
231279377Simp	  f = (fde *) (table[mid].fde + data_base);
232279377Simp	  f_enc = get_fde_encoding (f);
233279377Simp	  f_enc_size = size_of_encoded_value (f_enc);
234279377Simp	  read_encoded_value_with_base (f_enc & 0x0f, 0,
235279377Simp					&f->pc_begin[f_enc_size], &range);
236279377Simp	  if (data->pc < table[mid].initial_loc + data_base + range)
237279377Simp	    data->ret = f;
238279377Simp	  data->func = (void *) (table[mid].initial_loc + data_base);
239279377Simp	  return 1;
240279377Simp	}
241279377Simp    }
242279377Simp
243279377Simp  /* We have no sorted search table, so need to go the slow way.
244279377Simp     As soon as GLIBC will provide API so to notify that a library has been
245279377Simp     removed, we could cache this (and thus use search_object).  */
246279377Simp  ob.pc_begin = NULL;
247279377Simp  ob.tbase = data->tbase;
248279377Simp  ob.dbase = data->dbase;
249279377Simp  ob.u.single = (fde *) eh_frame;
250279377Simp  ob.s.i = 0;
251279377Simp  ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
252279377Simp  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
253279377Simp  if (data->ret != NULL)
254279377Simp    {
255279377Simp      unsigned int encoding = get_fde_encoding (data->ret);
256279377Simp      read_encoded_value_with_base (encoding,
257279377Simp				    base_from_cb_data (encoding, data),
258279377Simp				    data->ret->pc_begin,
259279377Simp				    (_Unwind_Ptr *)&data->func);
260279377Simp    }
261279377Simp  return 1;
262279377Simp}
263279377Simp
264279377Simpconst fde *
265279377Simp_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
266279377Simp{
267279377Simp  struct unw_eh_callback_data data;
268279377Simp  const fde *ret;
269279377Simp
270279377Simp  ret = _Unwind_Find_registered_FDE (pc, bases);
271279377Simp  if (ret != NULL)
272279377Simp    return ret;
273279377Simp
274279377Simp  data.pc = (_Unwind_Ptr) pc;
275279377Simp  data.tbase = NULL;
276279377Simp  data.dbase = NULL;
277279377Simp  data.func = NULL;
278279377Simp  data.ret = NULL;
279279377Simp
280279377Simp  if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
281279377Simp    return NULL;
282279377Simp
283279377Simp  if (data.ret)
284279377Simp    {
285279377Simp      bases->tbase = data.tbase;
286279377Simp      bases->dbase = data.dbase;
287279377Simp      bases->func = data.func;
288279377Simp    }
289279377Simp  return data.ret;
290279377Simp}
291279377Simp
292279377Simp#else
293279377Simp/* Prevent multiple include of header files.  */
294279377Simp#define _Unwind_Find_FDE _Unwind_Find_FDE
295279377Simp#include "unwind-dw2-fde.c"
296279377Simp#endif
297279377Simp