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