1169689Skan/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
290075Sobrien   Contributed by Jakub Jelinek <jakub@redhat.com>.
390075Sobrien
4132718Skan   This file is part of GCC.
590075Sobrien
6132718Skan   GCC is free software; you can redistribute it and/or modify
790075Sobrien   it under the terms of the GNU General Public License as published by
890075Sobrien   the Free Software Foundation; either version 2, or (at your option)
990075Sobrien   any later version.
1090075Sobrien
11132718Skan   GCC is distributed in the hope that it will be useful,
1290075Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1390075Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1490075Sobrien   GNU General Public License for more details.
1590075Sobrien
1690075Sobrien   You should have received a copy of the GNU General Public License
17132718Skan   along with GCC; see the file COPYING.  If not, write to
18169689Skan   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
19169689Skan   Boston, MA 02110-1301, USA.  */
2090075Sobrien
2190075Sobrien/* As a special exception, if you link this library with other files,
2290075Sobrien   some of which are compiled with GCC, to produce an executable,
2390075Sobrien   this library does not by itself cause the resulting executable
2490075Sobrien   to be covered by the GNU General Public License.
2590075Sobrien   This exception does not however invalidate any other reasons why
2690075Sobrien   the executable file might be covered by the GNU General Public License.  */
2790075Sobrien
2890075Sobrien/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
2990075Sobrien   segment and dl_iterate_phdr to avoid register/deregister calls at
3090075Sobrien   DSO load/unload.  */
3190075Sobrien
3296263Sobrien#ifndef _GNU_SOURCE
3396263Sobrien#define _GNU_SOURCE 1
3496263Sobrien#endif
3596263Sobrien
3690075Sobrien#include "tconfig.h"
37132718Skan#include "tsystem.h"
3890075Sobrien#ifndef inhibit_libc
3990075Sobrien#include <link.h>
4090075Sobrien#endif
41132718Skan#include "coretypes.h"
42132718Skan#include "tm.h"
4390075Sobrien#include "dwarf2.h"
4490075Sobrien#include "unwind.h"
4590075Sobrien#define NO_BASE_OF_ENCODED_VALUE
4690075Sobrien#include "unwind-pe.h"
4790075Sobrien#include "unwind-dw2-fde.h"
48146895Skan#include "unwind-compat.h"
4990075Sobrien#include "gthr.h"
5090075Sobrien
5190075Sobrien#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
5290075Sobrien    && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
5390075Sobrien	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
5490075Sobrien
55169689Skan#ifndef __RELOC_POINTER
56169689Skan# define __RELOC_POINTER(ptr, base) ((ptr) + (base))
57169689Skan#endif
58169689Skan
59132718Skanstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
6090075Sobrien
6190075Sobrien#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
6290075Sobrien#include "unwind-dw2-fde.c"
6390075Sobrien#undef _Unwind_Find_FDE
6490075Sobrien
6590075Sobrien#ifndef PT_GNU_EH_FRAME
6690075Sobrien#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
6790075Sobrien#endif
6890075Sobrien
6990075Sobrienstruct unw_eh_callback_data
7090075Sobrien{
7190075Sobrien  _Unwind_Ptr pc;
7290075Sobrien  void *tbase;
7390075Sobrien  void *dbase;
7490075Sobrien  void *func;
75132718Skan  const fde *ret;
76169689Skan  int check_cache;
7790075Sobrien};
7890075Sobrien
7990075Sobrienstruct unw_eh_frame_hdr
8090075Sobrien{
8190075Sobrien  unsigned char version;
8290075Sobrien  unsigned char eh_frame_ptr_enc;
8390075Sobrien  unsigned char fde_count_enc;
8490075Sobrien  unsigned char table_enc;
8590075Sobrien};
8690075Sobrien
87169689Skan#define FRAME_HDR_CACHE_SIZE 8
88169689Skan
89169689Skanstatic struct frame_hdr_cache_element
90169689Skan{
91169689Skan  _Unwind_Ptr pc_low;
92169689Skan  _Unwind_Ptr pc_high;
93169689Skan  _Unwind_Ptr load_base;
94169689Skan  const ElfW(Phdr) *p_eh_frame_hdr;
95169689Skan  const ElfW(Phdr) *p_dynamic;
96169689Skan  struct frame_hdr_cache_element *link;
97169689Skan} frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
98169689Skan
99169689Skanstatic struct frame_hdr_cache_element *frame_hdr_cache_head;
100169689Skan
101117395Skan/* Like base_of_encoded_value, but take the base from a struct
102117395Skan   unw_eh_callback_data instead of an _Unwind_Context.  */
103117395Skan
10490075Sobrienstatic _Unwind_Ptr
10590075Sobrienbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
10690075Sobrien{
10790075Sobrien  if (encoding == DW_EH_PE_omit)
10890075Sobrien    return 0;
10990075Sobrien
11090075Sobrien  switch (encoding & 0x70)
11190075Sobrien    {
11290075Sobrien    case DW_EH_PE_absptr:
11390075Sobrien    case DW_EH_PE_pcrel:
11490075Sobrien    case DW_EH_PE_aligned:
11590075Sobrien      return 0;
116117395Skan
11790075Sobrien    case DW_EH_PE_textrel:
11890075Sobrien      return (_Unwind_Ptr) data->tbase;
11990075Sobrien    case DW_EH_PE_datarel:
12090075Sobrien      return (_Unwind_Ptr) data->dbase;
121169689Skan    default:
122169689Skan      gcc_unreachable ();
12390075Sobrien    }
12490075Sobrien}
12590075Sobrien
12690075Sobrienstatic int
12790075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
12890075Sobrien{
12990075Sobrien  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
13090075Sobrien  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
13190075Sobrien  long n, match;
132169689Skan#ifdef __FRV_FDPIC__
133169689Skan  struct elf32_fdpic_loadaddr load_base;
134169689Skan#else
13590075Sobrien  _Unwind_Ptr load_base;
136169689Skan#endif
13790075Sobrien  const unsigned char *p;
13890075Sobrien  const struct unw_eh_frame_hdr *hdr;
13990075Sobrien  _Unwind_Ptr eh_frame;
14090075Sobrien  struct object ob;
141169689Skan
142169689Skan  struct ext_dl_phdr_info
143169689Skan    {
144169689Skan      ElfW(Addr) dlpi_addr;
145169689Skan      const char *dlpi_name;
146169689Skan      const ElfW(Phdr) *dlpi_phdr;
147169689Skan      ElfW(Half) dlpi_phnum;
148169689Skan      unsigned long long int dlpi_adds;
149169689Skan      unsigned long long int dlpi_subs;
150169689Skan    };
15190075Sobrien
15290075Sobrien  match = 0;
15390075Sobrien  phdr = info->dlpi_phdr;
15490075Sobrien  load_base = info->dlpi_addr;
15590075Sobrien  p_eh_frame_hdr = NULL;
15690075Sobrien  p_dynamic = NULL;
15790075Sobrien
158169689Skan  struct frame_hdr_cache_element *prev_cache_entry = NULL,
159169689Skan    *last_cache_entry = NULL;
160169689Skan
161169689Skan  if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
162169689Skan    {
163169689Skan      static unsigned long long adds = -1ULL, subs;
164169689Skan      struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
165169689Skan
166169689Skan      /* We use a least recently used cache replacement policy.  Also,
167169689Skan	 the most recently used cache entries are placed at the head
168169689Skan	 of the search chain.  */
169169689Skan
170169689Skan      if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
171169689Skan	{
172169689Skan	  /* Find data->pc in shared library cache.
173169689Skan	     Set load_base, p_eh_frame_hdr and p_dynamic
174169689Skan	     plus match from the cache and goto
175169689Skan	     "Read .eh_frame_hdr header." below.  */
176169689Skan
177169689Skan	  struct frame_hdr_cache_element *cache_entry;
178169689Skan
179169689Skan	  for (cache_entry = frame_hdr_cache_head;
180169689Skan	       cache_entry;
181169689Skan	       cache_entry = cache_entry->link)
182169689Skan	    {
183169689Skan	      if (data->pc >= cache_entry->pc_low
184169689Skan		  && data->pc < cache_entry->pc_high)
185169689Skan		{
186169689Skan		  load_base = cache_entry->load_base;
187169689Skan		  p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
188169689Skan		  p_dynamic = cache_entry->p_dynamic;
189169689Skan
190169689Skan		  /* And move the entry we're using to the head.  */
191169689Skan		  if (cache_entry != frame_hdr_cache_head)
192169689Skan		    {
193169689Skan		      prev_cache_entry->link = cache_entry->link;
194169689Skan		      cache_entry->link = frame_hdr_cache_head;
195169689Skan		      frame_hdr_cache_head = cache_entry;
196169689Skan		    }
197169689Skan		  goto found;
198169689Skan		}
199169689Skan
200169689Skan	      last_cache_entry = cache_entry;
201169689Skan	      /* Exit early if we found an unused entry.  */
202169689Skan	      if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
203169689Skan		break;
204169689Skan	      if (cache_entry->link != NULL)
205169689Skan		prev_cache_entry = cache_entry;
206169689Skan	    }
207169689Skan	}
208169689Skan      else
209169689Skan	{
210169689Skan	  adds = einfo->dlpi_adds;
211169689Skan	  subs = einfo->dlpi_subs;
212169689Skan	  /* Initialize the cache.  Create a chain of cache entries,
213169689Skan	     with the final one terminated by a NULL link.  */
214169689Skan	  int i;
215169689Skan	  for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
216169689Skan	    {
217169689Skan	      frame_hdr_cache[i].pc_low = 0;
218169689Skan	      frame_hdr_cache[i].pc_high = 0;
219169689Skan	      frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
220169689Skan	    }
221169689Skan	  frame_hdr_cache[i-1].link = NULL;
222169689Skan	  frame_hdr_cache_head = &frame_hdr_cache[0];
223169689Skan	  data->check_cache = 0;
224169689Skan	}
225169689Skan    }
226169689Skan
227169689Skan  /* Make sure struct dl_phdr_info is at least as big as we need.  */
228169689Skan  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
229169689Skan	     + sizeof (info->dlpi_phnum))
230169689Skan    return -1;
231169689Skan
232169689Skan  _Unwind_Ptr pc_low = 0, pc_high = 0;
233169689Skan
23490075Sobrien  /* See if PC falls into one of the loaded segments.  Find the eh_frame
23590075Sobrien     segment at the same time.  */
23690075Sobrien  for (n = info->dlpi_phnum; --n >= 0; phdr++)
23790075Sobrien    {
23890075Sobrien      if (phdr->p_type == PT_LOAD)
23990075Sobrien	{
240169689Skan	  _Unwind_Ptr vaddr = (_Unwind_Ptr)
241169689Skan	    __RELOC_POINTER (phdr->p_vaddr, load_base);
24290075Sobrien	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
243169689Skan	    {
244169689Skan	      match = 1;
245169689Skan	      pc_low = vaddr;
246169689Skan	      pc_high =  vaddr + phdr->p_memsz;
247169689Skan	    }
24890075Sobrien	}
24990075Sobrien      else if (phdr->p_type == PT_GNU_EH_FRAME)
25090075Sobrien	p_eh_frame_hdr = phdr;
25190075Sobrien      else if (phdr->p_type == PT_DYNAMIC)
25290075Sobrien	p_dynamic = phdr;
25390075Sobrien    }
254169689Skan
255169689Skan  if (!match)
25690075Sobrien    return 0;
25790075Sobrien
258169689Skan  if (size >= sizeof (struct ext_dl_phdr_info))
259169689Skan    {
260169689Skan      /* Move the cache entry we're about to overwrite to the head of
261169689Skan	 the list.  If either last_cache_entry or prev_cache_entry are
262169689Skan	 NULL, that cache entry is already at the head.  */
263169689Skan      if (last_cache_entry != NULL && prev_cache_entry != NULL)
264169689Skan	{
265169689Skan	  prev_cache_entry->link = last_cache_entry->link;
266169689Skan	  last_cache_entry->link = frame_hdr_cache_head;
267169689Skan	  frame_hdr_cache_head = last_cache_entry;
268169689Skan	}
269169689Skan
270169689Skan      frame_hdr_cache_head->load_base = load_base;
271169689Skan      frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
272169689Skan      frame_hdr_cache_head->p_dynamic = p_dynamic;
273169689Skan      frame_hdr_cache_head->pc_low = pc_low;
274169689Skan      frame_hdr_cache_head->pc_high = pc_high;
275169689Skan    }
276169689Skan
277169689Skan found:
278169689Skan
279169689Skan  if (!p_eh_frame_hdr)
280169689Skan    return 0;
281169689Skan
28290075Sobrien  /* Read .eh_frame_hdr header.  */
28390075Sobrien  hdr = (const struct unw_eh_frame_hdr *)
284169689Skan    __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
28590075Sobrien  if (hdr->version != 1)
28690075Sobrien    return 1;
28790075Sobrien
28890075Sobrien#ifdef CRT_GET_RFIB_DATA
28990075Sobrien# ifdef __i386__
29090075Sobrien  data->dbase = NULL;
29190075Sobrien  if (p_dynamic)
29290075Sobrien    {
293132718Skan      /* For dynamically linked executables and shared libraries,
29490075Sobrien	 DT_PLTGOT is the gp value for that object.  */
295169689Skan      ElfW(Dyn) *dyn = (ElfW(Dyn) *)
296169689Skan	__RELOC_POINTER (p_dynamic->p_vaddr, load_base);
29790075Sobrien      for (; dyn->d_tag != DT_NULL ; dyn++)
29890075Sobrien	if (dyn->d_tag == DT_PLTGOT)
29990075Sobrien	  {
30090075Sobrien	    /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
30190075Sobrien	    data->dbase = (void *) dyn->d_un.d_ptr;
30290075Sobrien	    break;
30390075Sobrien	  }
30490075Sobrien    }
305169689Skan# elif defined __FRV_FDPIC__ && defined __linux__
306169689Skan  data->dbase = load_base.got_value;
30790075Sobrien# else
30890075Sobrien#  error What is DW_EH_PE_datarel base on this platform?
30990075Sobrien# endif
31090075Sobrien#endif
31190075Sobrien
31290075Sobrien  p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
31390075Sobrien				    base_from_cb_data (hdr->eh_frame_ptr_enc,
31490075Sobrien						       data),
31590075Sobrien				    (const unsigned char *) (hdr + 1),
31690075Sobrien				    &eh_frame);
31790075Sobrien
31890075Sobrien  /* We require here specific table encoding to speed things up.
31990075Sobrien     Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
32090075Sobrien     as base, not the processor specific DW_EH_PE_datarel.  */
32190075Sobrien  if (hdr->fde_count_enc != DW_EH_PE_omit
32290075Sobrien      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
32390075Sobrien    {
32490075Sobrien      _Unwind_Ptr fde_count;
32590075Sobrien
32690075Sobrien      p = read_encoded_value_with_base (hdr->fde_count_enc,
32790075Sobrien					base_from_cb_data (hdr->fde_count_enc,
32890075Sobrien							   data),
32990075Sobrien					p, &fde_count);
33090075Sobrien      /* Shouldn't happen.  */
33190075Sobrien      if (fde_count == 0)
33290075Sobrien	return 1;
33390075Sobrien      if ((((_Unwind_Ptr) p) & 3) == 0)
33490075Sobrien	{
33590075Sobrien	  struct fde_table {
33690075Sobrien	    signed initial_loc __attribute__ ((mode (SI)));
33790075Sobrien	    signed fde __attribute__ ((mode (SI)));
33890075Sobrien	  };
33990075Sobrien	  const struct fde_table *table = (const struct fde_table *) p;
34090075Sobrien	  size_t lo, hi, mid;
34190075Sobrien	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
34290075Sobrien	  fde *f;
34390075Sobrien	  unsigned int f_enc, f_enc_size;
34490075Sobrien	  _Unwind_Ptr range;
34590075Sobrien
34690075Sobrien	  mid = fde_count - 1;
34790075Sobrien	  if (data->pc < table[0].initial_loc + data_base)
34890075Sobrien	    return 1;
34990075Sobrien	  else if (data->pc < table[mid].initial_loc + data_base)
35090075Sobrien	    {
35190075Sobrien	      lo = 0;
35290075Sobrien	      hi = mid;
35390075Sobrien
35490075Sobrien	      while (lo < hi)
35590075Sobrien		{
35690075Sobrien		  mid = (lo + hi) / 2;
35790075Sobrien		  if (data->pc < table[mid].initial_loc + data_base)
35890075Sobrien		    hi = mid;
35990075Sobrien		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
36090075Sobrien		    lo = mid + 1;
36190075Sobrien		  else
36290075Sobrien		    break;
36390075Sobrien		}
36490075Sobrien
365169689Skan	      gcc_assert (lo < hi);
36690075Sobrien	    }
36790075Sobrien
36890075Sobrien	  f = (fde *) (table[mid].fde + data_base);
36990075Sobrien	  f_enc = get_fde_encoding (f);
37090075Sobrien	  f_enc_size = size_of_encoded_value (f_enc);
37190075Sobrien	  read_encoded_value_with_base (f_enc & 0x0f, 0,
37290075Sobrien					&f->pc_begin[f_enc_size], &range);
37390075Sobrien	  if (data->pc < table[mid].initial_loc + data_base + range)
37490075Sobrien	    data->ret = f;
37590075Sobrien	  data->func = (void *) (table[mid].initial_loc + data_base);
37690075Sobrien	  return 1;
37790075Sobrien	}
37890075Sobrien    }
37990075Sobrien
38090075Sobrien  /* We have no sorted search table, so need to go the slow way.
38190075Sobrien     As soon as GLIBC will provide API so to notify that a library has been
38290075Sobrien     removed, we could cache this (and thus use search_object).  */
38390075Sobrien  ob.pc_begin = NULL;
38490075Sobrien  ob.tbase = data->tbase;
38590075Sobrien  ob.dbase = data->dbase;
38690075Sobrien  ob.u.single = (fde *) eh_frame;
38790075Sobrien  ob.s.i = 0;
38890075Sobrien  ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
38990075Sobrien  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
39090075Sobrien  if (data->ret != NULL)
39190075Sobrien    {
392169689Skan      _Unwind_Ptr func;
39390075Sobrien      unsigned int encoding = get_fde_encoding (data->ret);
394169689Skan
39590075Sobrien      read_encoded_value_with_base (encoding,
39690075Sobrien				    base_from_cb_data (encoding, data),
397169689Skan				    data->ret->pc_begin, &func);
398169689Skan      data->func = (void *) func;
39990075Sobrien    }
40090075Sobrien  return 1;
40190075Sobrien}
40290075Sobrien
403132718Skanconst fde *
40490075Sobrien_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
40590075Sobrien{
40690075Sobrien  struct unw_eh_callback_data data;
407132718Skan  const fde *ret;
40890075Sobrien
40990075Sobrien  ret = _Unwind_Find_registered_FDE (pc, bases);
41090075Sobrien  if (ret != NULL)
41190075Sobrien    return ret;
41290075Sobrien
41390075Sobrien  data.pc = (_Unwind_Ptr) pc;
41490075Sobrien  data.tbase = NULL;
41590075Sobrien  data.dbase = NULL;
41690075Sobrien  data.func = NULL;
41790075Sobrien  data.ret = NULL;
418169689Skan  data.check_cache = 1;
41990075Sobrien
42090075Sobrien  if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
42190075Sobrien    return NULL;
42290075Sobrien
42390075Sobrien  if (data.ret)
42490075Sobrien    {
42590075Sobrien      bases->tbase = data.tbase;
42690075Sobrien      bases->dbase = data.dbase;
42790075Sobrien      bases->func = data.func;
42890075Sobrien    }
42990075Sobrien  return data.ret;
43090075Sobrien}
43190075Sobrien
43290075Sobrien#else
43390075Sobrien/* Prevent multiple include of header files.  */
43490075Sobrien#define _Unwind_Find_FDE _Unwind_Find_FDE
43590075Sobrien#include "unwind-dw2-fde.c"
43690075Sobrien#endif
437146895Skan
438146895Skan#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
439146895Skanalias (_Unwind_Find_FDE);
440146895Skan#endif
441