Back to index

glibc  2.9
unwind-dw2-fde-glibc.c
Go to the documentation of this file.
00001 /* Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
00002    Contributed by Jakub Jelinek <jakub@redhat.com>.
00003 
00004    This file is part of the GNU C Library.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
00022    segment and dl_iterate_phdr to avoid register/deregister calls at
00023    DSO load/unload.  */
00024 
00025 #ifdef _LIBC
00026 # include <shlib-compat.h>
00027 #endif
00028 
00029 #if !defined _LIBC || SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2_5)
00030 
00031 #include <link.h>
00032 #include <stddef.h>
00033 
00034 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
00035 
00036 #include <unwind-dw2-fde.c>
00037 
00038 #undef _Unwind_Find_FDE
00039 
00040 extern fde * _Unwind_Find_registered_FDE (void *pc,
00041                                      struct dwarf_eh_bases *bases);
00042 extern fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
00043 
00044 struct unw_eh_callback_data
00045 {
00046   _Unwind_Ptr pc;
00047   void *tbase;
00048   void *dbase;
00049   void *func;
00050   fde *ret;
00051 };
00052 
00053 struct unw_eh_frame_hdr
00054 {
00055   unsigned char version;
00056   unsigned char eh_frame_ptr_enc;
00057   unsigned char fde_count_enc;
00058   unsigned char table_enc;
00059 };
00060 
00061 /* Like base_of_encoded_value, but take the base from a struct object
00062    instead of an _Unwind_Context.  */
00063 
00064 static _Unwind_Ptr
00065 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
00066 {
00067   if (encoding == DW_EH_PE_omit)
00068     return 0;
00069 
00070   switch (encoding & 0x70)
00071     {
00072     case DW_EH_PE_absptr:
00073     case DW_EH_PE_pcrel:
00074     case DW_EH_PE_aligned:
00075       return 0;
00076 
00077     case DW_EH_PE_textrel:
00078       return (_Unwind_Ptr) data->tbase;
00079     case DW_EH_PE_datarel:
00080       return (_Unwind_Ptr) data->dbase;
00081     }
00082   abort ();
00083 }
00084 
00085 static int
00086 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
00087 {
00088   struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
00089   const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
00090   long n, match;
00091   _Unwind_Ptr load_base;
00092   const unsigned char *p;
00093   const struct unw_eh_frame_hdr *hdr;
00094   _Unwind_Ptr eh_frame;
00095   struct object ob;
00096 
00097   /* Make sure struct dl_phdr_info is at least as big as we need.  */
00098   if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
00099             + sizeof (info->dlpi_phnum))
00100     return -1;
00101 
00102   match = 0;
00103   phdr = info->dlpi_phdr;
00104   load_base = info->dlpi_addr;
00105   p_eh_frame_hdr = NULL;
00106   p_dynamic = NULL;
00107 
00108   /* See if PC falls into one of the loaded segments.  Find the eh_frame
00109      segment at the same time.  */
00110   for (n = info->dlpi_phnum; --n >= 0; phdr++)
00111     {
00112       if (phdr->p_type == PT_LOAD)
00113        {
00114          _Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
00115          if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
00116            match = 1;
00117        }
00118       else if (phdr->p_type == PT_GNU_EH_FRAME)
00119        p_eh_frame_hdr = phdr;
00120       else if (phdr->p_type == PT_DYNAMIC)
00121        p_dynamic = phdr;
00122     }
00123   if (!match || !p_eh_frame_hdr)
00124     return 0;
00125 
00126   /* Read .eh_frame_hdr header.  */
00127   hdr = (const struct unw_eh_frame_hdr *)
00128        (p_eh_frame_hdr->p_vaddr + load_base);
00129   if (hdr->version != 1)
00130     return 1;
00131 
00132 #ifdef CRT_GET_RFIB_DATA
00133 # ifdef __i386__
00134   data->dbase = NULL;
00135   if (p_dynamic)
00136     {
00137       /* For dynamicly linked executables and shared libraries,
00138         DT_PLTGOT is the gp value for that object.  */
00139       ElfW(Dyn) *dyn = (ElfW(Dyn) *)(p_dynamic->p_vaddr + load_base);
00140       for (; dyn->d_tag != DT_NULL ; dyn++)
00141        if (dyn->d_tag == DT_PLTGOT)
00142          {
00143            /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
00144            data->dbase = (void *) dyn->d_un.d_ptr;
00145            break;
00146          }
00147     }
00148 # else
00149 #  error What is DW_EH_PE_datarel base on this platform?
00150 # endif
00151 #endif
00152 #ifdef CRT_GET_RFIB_TEXT
00153 # error What is DW_EH_PE_textrel base on this platform?
00154 #endif
00155 
00156   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
00157                                 base_from_cb_data (hdr->eh_frame_ptr_enc,
00158                                                  data),
00159                                 (const unsigned char *) (hdr + 1),
00160                                 &eh_frame);
00161 
00162   /* We require here specific table encoding to speed things up.
00163      Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
00164      as base, not the processor specific DW_EH_PE_datarel.  */
00165   if (hdr->fde_count_enc != DW_EH_PE_omit
00166       && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
00167     {
00168       _Unwind_Ptr fde_count;
00169 
00170       p = read_encoded_value_with_base (hdr->fde_count_enc,
00171                                    base_from_cb_data (hdr->fde_count_enc,
00172                                                     data),
00173                                    p, &fde_count);
00174       /* Shouldn't happen.  */
00175       if (fde_count == 0)
00176        return 1;
00177       if ((((_Unwind_Ptr) p) & 3) == 0)
00178        {
00179          struct fde_table {
00180            signed initial_loc __attribute__ ((mode (SI)));
00181            signed fde __attribute__ ((mode (SI)));
00182          };
00183          const struct fde_table *table = (const struct fde_table *) p;
00184          size_t lo, hi, mid;
00185          _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
00186          fde *f;
00187          unsigned int f_enc, f_enc_size;
00188          _Unwind_Ptr range;
00189 
00190          mid = fde_count - 1;
00191          if (data->pc < table[0].initial_loc + data_base)
00192            return 1;
00193          else if (data->pc < table[mid].initial_loc + data_base)
00194            {
00195              lo = 0;
00196              hi = mid;
00197 
00198              while (lo < hi)
00199               {
00200                 mid = (lo + hi) / 2;
00201                 if (data->pc < table[mid].initial_loc + data_base)
00202                   hi = mid;
00203                 else if (data->pc >= table[mid + 1].initial_loc + data_base)
00204                   lo = mid + 1;
00205                 else
00206                   break;
00207               }
00208 
00209              if (lo >= hi)
00210               __gxx_abort ();
00211            }
00212 
00213          f = (fde *) (table[mid].fde + data_base);
00214          f_enc = get_fde_encoding (f);
00215          f_enc_size = size_of_encoded_value (f_enc);
00216          read_encoded_value_with_base (f_enc & 0x0f, 0,
00217                                    &f->pc_begin[f_enc_size], &range);
00218          if (data->pc < table[mid].initial_loc + data_base + range)
00219            data->ret = f;
00220          data->func = (void *) (table[mid].initial_loc + data_base);
00221          return 1;
00222        }
00223     }
00224 
00225   /* We have no sorted search table, so need to go the slow way.
00226      As soon as GLIBC will provide API so to notify that a library has been
00227      removed, we could cache this (and thus use search_object).  */
00228   ob.pc_begin = NULL;
00229   ob.tbase = data->tbase;
00230   ob.dbase = data->dbase;
00231   ob.u.single = (fde *) eh_frame;
00232   ob.s.i = 0;
00233   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
00234   data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
00235   if (data->ret != NULL)
00236     {
00237       unsigned int encoding = get_fde_encoding (data->ret);
00238       _Unwind_Ptr func;
00239       read_encoded_value_with_base (encoding,
00240                                 base_from_cb_data (encoding, data),
00241                                 data->ret->pc_begin, &func);
00242       data->func = (void *) func;
00243     }
00244   return 1;
00245 }
00246 
00247 # ifdef _LIBC
00248 # define dl_iterate_phdr __dl_iterate_phdr
00249 # endif
00250 
00251 fde *
00252 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
00253 {
00254   struct unw_eh_callback_data data;
00255   fde *ret;
00256 
00257   ret = _Unwind_Find_registered_FDE (pc, bases);
00258   if (ret != NULL)
00259     return ret;
00260 
00261   data.pc = (_Unwind_Ptr) pc;
00262   data.tbase = NULL;
00263   data.dbase = NULL;
00264   data.func = NULL;
00265   data.ret = NULL;
00266 
00267   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
00268     return NULL;
00269 
00270   if (data.ret)
00271     {
00272       bases->tbase = data.tbase;
00273       bases->dbase = data.dbase;
00274       bases->func = data.func;
00275     }
00276   return data.ret;
00277 }
00278 
00279 #endif