unwind-dw2-fde-glibc.c revision 146895
1132718Skan/* Copyright (C) 2001, 2002, 2003 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
1890075Sobrien   the Free Software Foundation, 59 Temple Place - Suite 330,
1990075Sobrien   Boston, MA 02111-1307, 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 "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR.  */
3790075Sobrien#include "tconfig.h"
38132718Skan#include "tsystem.h"
3990075Sobrien#ifndef inhibit_libc
4090075Sobrien#include <link.h>
4190075Sobrien#endif
42132718Skan#include "coretypes.h"
43132718Skan#include "tm.h"
4490075Sobrien#include "dwarf2.h"
4590075Sobrien#include "unwind.h"
4690075Sobrien#define NO_BASE_OF_ENCODED_VALUE
4790075Sobrien#include "unwind-pe.h"
4890075Sobrien#include "unwind-dw2-fde.h"
49146895Skan#include "unwind-compat.h"
5090075Sobrien#include "gthr.h"
5190075Sobrien
5290075Sobrien#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
5390075Sobrien    && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
5490075Sobrien	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
5590075Sobrien
56132718Skanstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
5790075Sobrien
5890075Sobrien#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
5990075Sobrien#include "unwind-dw2-fde.c"
6090075Sobrien#undef _Unwind_Find_FDE
6190075Sobrien
6290075Sobrien#ifndef PT_GNU_EH_FRAME
6390075Sobrien#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
6490075Sobrien#endif
6590075Sobrien
6690075Sobrienstruct unw_eh_callback_data
6790075Sobrien{
6890075Sobrien  _Unwind_Ptr pc;
6990075Sobrien  void *tbase;
7090075Sobrien  void *dbase;
7190075Sobrien  void *func;
72132718Skan  const fde *ret;
7390075Sobrien};
7490075Sobrien
7590075Sobrienstruct unw_eh_frame_hdr
7690075Sobrien{
7790075Sobrien  unsigned char version;
7890075Sobrien  unsigned char eh_frame_ptr_enc;
7990075Sobrien  unsigned char fde_count_enc;
8090075Sobrien  unsigned char table_enc;
8190075Sobrien};
8290075Sobrien
83117395Skan/* Like base_of_encoded_value, but take the base from a struct
84117395Skan   unw_eh_callback_data instead of an _Unwind_Context.  */
85117395Skan
8690075Sobrienstatic _Unwind_Ptr
8790075Sobrienbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
8890075Sobrien{
8990075Sobrien  if (encoding == DW_EH_PE_omit)
9090075Sobrien    return 0;
9190075Sobrien
9290075Sobrien  switch (encoding & 0x70)
9390075Sobrien    {
9490075Sobrien    case DW_EH_PE_absptr:
9590075Sobrien    case DW_EH_PE_pcrel:
9690075Sobrien    case DW_EH_PE_aligned:
9790075Sobrien      return 0;
98117395Skan
9990075Sobrien    case DW_EH_PE_textrel:
10090075Sobrien      return (_Unwind_Ptr) data->tbase;
10190075Sobrien    case DW_EH_PE_datarel:
10290075Sobrien      return (_Unwind_Ptr) data->dbase;
10390075Sobrien    }
10490075Sobrien  abort ();
10590075Sobrien}
10690075Sobrien
10790075Sobrienstatic int
10890075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
10990075Sobrien{
11090075Sobrien  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
11190075Sobrien  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
11290075Sobrien  long n, match;
11390075Sobrien  _Unwind_Ptr load_base;
11490075Sobrien  const unsigned char *p;
11590075Sobrien  const struct unw_eh_frame_hdr *hdr;
11690075Sobrien  _Unwind_Ptr eh_frame;
11790075Sobrien  struct object ob;
11890075Sobrien
11990075Sobrien  /* Make sure struct dl_phdr_info is at least as big as we need.  */
12090075Sobrien  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
12190075Sobrien	     + sizeof (info->dlpi_phnum))
12290075Sobrien    return -1;
12390075Sobrien
12490075Sobrien  match = 0;
12590075Sobrien  phdr = info->dlpi_phdr;
12690075Sobrien  load_base = info->dlpi_addr;
12790075Sobrien  p_eh_frame_hdr = NULL;
12890075Sobrien  p_dynamic = NULL;
12990075Sobrien
13090075Sobrien  /* See if PC falls into one of the loaded segments.  Find the eh_frame
13190075Sobrien     segment at the same time.  */
13290075Sobrien  for (n = info->dlpi_phnum; --n >= 0; phdr++)
13390075Sobrien    {
13490075Sobrien      if (phdr->p_type == PT_LOAD)
13590075Sobrien	{
13690075Sobrien	  _Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
13790075Sobrien	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
13890075Sobrien	    match = 1;
13990075Sobrien	}
14090075Sobrien      else if (phdr->p_type == PT_GNU_EH_FRAME)
14190075Sobrien	p_eh_frame_hdr = phdr;
14290075Sobrien      else if (phdr->p_type == PT_DYNAMIC)
14390075Sobrien	p_dynamic = phdr;
14490075Sobrien    }
14590075Sobrien  if (!match || !p_eh_frame_hdr)
14690075Sobrien    return 0;
14790075Sobrien
14890075Sobrien  /* Read .eh_frame_hdr header.  */
14990075Sobrien  hdr = (const struct unw_eh_frame_hdr *)
15090075Sobrien	(p_eh_frame_hdr->p_vaddr + load_base);
15190075Sobrien  if (hdr->version != 1)
15290075Sobrien    return 1;
15390075Sobrien
15490075Sobrien#ifdef CRT_GET_RFIB_DATA
15590075Sobrien# ifdef __i386__
15690075Sobrien  data->dbase = NULL;
15790075Sobrien  if (p_dynamic)
15890075Sobrien    {
159132718Skan      /* For dynamically linked executables and shared libraries,
16090075Sobrien	 DT_PLTGOT is the gp value for that object.  */
16190075Sobrien      ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base);
16290075Sobrien      for (; dyn->d_tag != DT_NULL ; dyn++)
16390075Sobrien	if (dyn->d_tag == DT_PLTGOT)
16490075Sobrien	  {
16590075Sobrien	    /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
16690075Sobrien	    data->dbase = (void *) dyn->d_un.d_ptr;
16790075Sobrien	    break;
16890075Sobrien	  }
16990075Sobrien    }
17090075Sobrien# else
17190075Sobrien#  error What is DW_EH_PE_datarel base on this platform?
17290075Sobrien# endif
17390075Sobrien#endif
17490075Sobrien
17590075Sobrien  p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
17690075Sobrien				    base_from_cb_data (hdr->eh_frame_ptr_enc,
17790075Sobrien						       data),
17890075Sobrien				    (const unsigned char *) (hdr + 1),
17990075Sobrien				    &eh_frame);
18090075Sobrien
18190075Sobrien  /* We require here specific table encoding to speed things up.
18290075Sobrien     Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
18390075Sobrien     as base, not the processor specific DW_EH_PE_datarel.  */
18490075Sobrien  if (hdr->fde_count_enc != DW_EH_PE_omit
18590075Sobrien      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
18690075Sobrien    {
18790075Sobrien      _Unwind_Ptr fde_count;
18890075Sobrien
18990075Sobrien      p = read_encoded_value_with_base (hdr->fde_count_enc,
19090075Sobrien					base_from_cb_data (hdr->fde_count_enc,
19190075Sobrien							   data),
19290075Sobrien					p, &fde_count);
19390075Sobrien      /* Shouldn't happen.  */
19490075Sobrien      if (fde_count == 0)
19590075Sobrien	return 1;
19690075Sobrien      if ((((_Unwind_Ptr) p) & 3) == 0)
19790075Sobrien	{
19890075Sobrien	  struct fde_table {
19990075Sobrien	    signed initial_loc __attribute__ ((mode (SI)));
20090075Sobrien	    signed fde __attribute__ ((mode (SI)));
20190075Sobrien	  };
20290075Sobrien	  const struct fde_table *table = (const struct fde_table *) p;
20390075Sobrien	  size_t lo, hi, mid;
20490075Sobrien	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
20590075Sobrien	  fde *f;
20690075Sobrien	  unsigned int f_enc, f_enc_size;
20790075Sobrien	  _Unwind_Ptr range;
20890075Sobrien
20990075Sobrien	  mid = fde_count - 1;
21090075Sobrien	  if (data->pc < table[0].initial_loc + data_base)
21190075Sobrien	    return 1;
21290075Sobrien	  else if (data->pc < table[mid].initial_loc + data_base)
21390075Sobrien	    {
21490075Sobrien	      lo = 0;
21590075Sobrien	      hi = mid;
21690075Sobrien
21790075Sobrien	      while (lo < hi)
21890075Sobrien		{
21990075Sobrien		  mid = (lo + hi) / 2;
22090075Sobrien		  if (data->pc < table[mid].initial_loc + data_base)
22190075Sobrien		    hi = mid;
22290075Sobrien		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
22390075Sobrien		    lo = mid + 1;
22490075Sobrien		  else
22590075Sobrien		    break;
22690075Sobrien		}
22790075Sobrien
22890075Sobrien	      if (lo >= hi)
22990075Sobrien		__gxx_abort ();
23090075Sobrien	    }
23190075Sobrien
23290075Sobrien	  f = (fde *) (table[mid].fde + data_base);
23390075Sobrien	  f_enc = get_fde_encoding (f);
23490075Sobrien	  f_enc_size = size_of_encoded_value (f_enc);
23590075Sobrien	  read_encoded_value_with_base (f_enc & 0x0f, 0,
23690075Sobrien					&f->pc_begin[f_enc_size], &range);
23790075Sobrien	  if (data->pc < table[mid].initial_loc + data_base + range)
23890075Sobrien	    data->ret = f;
23990075Sobrien	  data->func = (void *) (table[mid].initial_loc + data_base);
24090075Sobrien	  return 1;
24190075Sobrien	}
24290075Sobrien    }
24390075Sobrien
24490075Sobrien  /* We have no sorted search table, so need to go the slow way.
24590075Sobrien     As soon as GLIBC will provide API so to notify that a library has been
24690075Sobrien     removed, we could cache this (and thus use search_object).  */
24790075Sobrien  ob.pc_begin = NULL;
24890075Sobrien  ob.tbase = data->tbase;
24990075Sobrien  ob.dbase = data->dbase;
25090075Sobrien  ob.u.single = (fde *) eh_frame;
25190075Sobrien  ob.s.i = 0;
25290075Sobrien  ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
25390075Sobrien  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
25490075Sobrien  if (data->ret != NULL)
25590075Sobrien    {
25690075Sobrien      unsigned int encoding = get_fde_encoding (data->ret);
25790075Sobrien      read_encoded_value_with_base (encoding,
25890075Sobrien				    base_from_cb_data (encoding, data),
25990075Sobrien				    data->ret->pc_begin,
26090075Sobrien				    (_Unwind_Ptr *)&data->func);
26190075Sobrien    }
26290075Sobrien  return 1;
26390075Sobrien}
26490075Sobrien
265132718Skanconst fde *
26690075Sobrien_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
26790075Sobrien{
26890075Sobrien  struct unw_eh_callback_data data;
269132718Skan  const fde *ret;
27090075Sobrien
27190075Sobrien  ret = _Unwind_Find_registered_FDE (pc, bases);
27290075Sobrien  if (ret != NULL)
27390075Sobrien    return ret;
27490075Sobrien
27590075Sobrien  data.pc = (_Unwind_Ptr) pc;
27690075Sobrien  data.tbase = NULL;
27790075Sobrien  data.dbase = NULL;
27890075Sobrien  data.func = NULL;
27990075Sobrien  data.ret = NULL;
28090075Sobrien
28190075Sobrien  if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
28290075Sobrien    return NULL;
28390075Sobrien
28490075Sobrien  if (data.ret)
28590075Sobrien    {
28690075Sobrien      bases->tbase = data.tbase;
28790075Sobrien      bases->dbase = data.dbase;
28890075Sobrien      bases->func = data.func;
28990075Sobrien    }
29090075Sobrien  return data.ret;
29190075Sobrien}
29290075Sobrien
29390075Sobrien#else
29490075Sobrien/* Prevent multiple include of header files.  */
29590075Sobrien#define _Unwind_Find_FDE _Unwind_Find_FDE
29690075Sobrien#include "unwind-dw2-fde.c"
29790075Sobrien#endif
298146895Skan
299146895Skan#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
300146895Skanalias (_Unwind_Find_FDE);
301146895Skan#endif
302