(root)/
glibc-2.38/
sysdeps/
generic/
unwind-dw2-fde-glibc.c
       1  /* Copyright (C) 2001-2023 Free Software Foundation, Inc.
       2  
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
      20     segment and dl_iterate_phdr to avoid register/deregister calls at
      21     DSO load/unload.  */
      22  
      23  #ifdef _LIBC
      24  # include <shlib-compat.h>
      25  #endif
      26  
      27  #if !defined _LIBC || SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2_5)
      28  
      29  #include <link.h>
      30  #include <stddef.h>
      31  
      32  #define _Unwind_Find_FDE _Unwind_Find_registered_FDE
      33  
      34  #include <unwind-dw2-fde.c>
      35  
      36  #undef _Unwind_Find_FDE
      37  
      38  extern fde * _Unwind_Find_registered_FDE (void *pc,
      39  					  struct dwarf_eh_bases *bases);
      40  extern fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
      41  
      42  struct unw_eh_callback_data
      43  {
      44    _Unwind_Ptr pc;
      45    void *tbase;
      46    void *dbase;
      47    void *func;
      48    fde *ret;
      49  };
      50  
      51  struct unw_eh_frame_hdr
      52  {
      53    unsigned char version;
      54    unsigned char eh_frame_ptr_enc;
      55    unsigned char fde_count_enc;
      56    unsigned char table_enc;
      57  };
      58  
      59  /* Like base_of_encoded_value, but take the base from a struct object
      60     instead of an _Unwind_Context.  */
      61  
      62  static _Unwind_Ptr
      63  base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
      64  {
      65    if (encoding == DW_EH_PE_omit)
      66      return 0;
      67  
      68    switch (encoding & 0x70)
      69      {
      70      case DW_EH_PE_absptr:
      71      case DW_EH_PE_pcrel:
      72      case DW_EH_PE_aligned:
      73        return 0;
      74  
      75      case DW_EH_PE_textrel:
      76        return (_Unwind_Ptr) data->tbase;
      77      case DW_EH_PE_datarel:
      78        return (_Unwind_Ptr) data->dbase;
      79      }
      80    abort ();
      81  }
      82  
      83  static int
      84  _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
      85  {
      86    struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
      87    const ElfW(Phdr) *phdr, *p_eh_frame_hdr;
      88    const ElfW(Phdr) *p_dynamic __attribute__ ((unused));
      89    long n, match;
      90    _Unwind_Ptr load_base;
      91    const unsigned char *p;
      92    const struct unw_eh_frame_hdr *hdr;
      93    _Unwind_Ptr eh_frame;
      94    struct object ob;
      95  
      96    /* Make sure struct dl_phdr_info is at least as big as we need.  */
      97    if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
      98  	     + sizeof (info->dlpi_phnum))
      99      return -1;
     100  
     101    match = 0;
     102    phdr = info->dlpi_phdr;
     103    load_base = info->dlpi_addr;
     104    p_eh_frame_hdr = NULL;
     105    p_dynamic = NULL;
     106  
     107    /* See if PC falls into one of the loaded segments.  Find the eh_frame
     108       segment at the same time.  */
     109    for (n = info->dlpi_phnum; --n >= 0; phdr++)
     110      {
     111        if (phdr->p_type == PT_LOAD)
     112  	{
     113  	  _Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
     114  	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
     115  	    match = 1;
     116  	}
     117        else if (phdr->p_type == PT_GNU_EH_FRAME)
     118  	p_eh_frame_hdr = phdr;
     119        else if (phdr->p_type == PT_DYNAMIC)
     120  	p_dynamic = phdr;
     121      }
     122    if (!match || !p_eh_frame_hdr)
     123      return 0;
     124  
     125    /* Read .eh_frame_hdr header.  */
     126    hdr = (const struct unw_eh_frame_hdr *)
     127  	(p_eh_frame_hdr->p_vaddr + load_base);
     128    if (hdr->version != 1)
     129      return 1;
     130  
     131  #ifdef CRT_GET_RFIB_DATA
     132  # ifdef __i386__
     133    data->dbase = NULL;
     134    if (p_dynamic)
     135      {
     136        /* For dynamically linked executables and shared libraries,
     137  	 DT_PLTGOT is the gp value for that object.  */
     138        ElfW(Dyn) *dyn = (ElfW(Dyn) *)(p_dynamic->p_vaddr + load_base);
     139        for (; dyn->d_tag != DT_NULL ; dyn++)
     140  	if (dyn->d_tag == DT_PLTGOT)
     141  	  {
     142  	    /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it.  */
     143  	    data->dbase = (void *) dyn->d_un.d_ptr;
     144  	    break;
     145  	  }
     146      }
     147  # else
     148  #  error What is DW_EH_PE_datarel base on this platform?
     149  # endif
     150  #endif
     151  #ifdef CRT_GET_RFIB_TEXT
     152  # error What is DW_EH_PE_textrel base on this platform?
     153  #endif
     154  
     155    p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
     156  				    base_from_cb_data (hdr->eh_frame_ptr_enc,
     157  						       data),
     158  				    (const unsigned char *) (hdr + 1),
     159  				    &eh_frame);
     160  
     161    /* We require here specific table encoding to speed things up.
     162       Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
     163       as base, not the processor specific DW_EH_PE_datarel.  */
     164    if (hdr->fde_count_enc != DW_EH_PE_omit
     165        && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
     166      {
     167        _Unwind_Ptr fde_count;
     168  
     169        p = read_encoded_value_with_base (hdr->fde_count_enc,
     170  					base_from_cb_data (hdr->fde_count_enc,
     171  							   data),
     172  					p, &fde_count);
     173        /* Shouldn't happen.  */
     174        if (fde_count == 0)
     175  	return 1;
     176        if ((((_Unwind_Ptr) p) & 3) == 0)
     177  	{
     178  	  struct fde_table {
     179  	    signed initial_loc __attribute__ ((mode (SI)));
     180  	    signed fde __attribute__ ((mode (SI)));
     181  	  };
     182  	  const struct fde_table *table = (const struct fde_table *) p;
     183  	  size_t lo, hi, mid;
     184  	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
     185  	  fde *f;
     186  	  unsigned int f_enc, f_enc_size;
     187  	  _Unwind_Ptr range;
     188  
     189  	  mid = fde_count - 1;
     190  	  if (data->pc < table[0].initial_loc + data_base)
     191  	    return 1;
     192  	  else if (data->pc < table[mid].initial_loc + data_base)
     193  	    {
     194  	      lo = 0;
     195  	      hi = mid;
     196  
     197  	      while (lo < hi)
     198  		{
     199  		  mid = (lo + hi) / 2;
     200  		  if (data->pc < table[mid].initial_loc + data_base)
     201  		    hi = mid;
     202  		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
     203  		    lo = mid + 1;
     204  		  else
     205  		    break;
     206  		}
     207  
     208  	      if (lo >= hi)
     209  		__gxx_abort ();
     210  	    }
     211  
     212  	  f = (fde *) (table[mid].fde + data_base);
     213  	  f_enc = get_fde_encoding (f);
     214  	  f_enc_size = size_of_encoded_value (f_enc);
     215  	  read_encoded_value_with_base (f_enc & 0x0f, 0,
     216  					&f->pc_begin[f_enc_size], &range);
     217  	  if (data->pc < table[mid].initial_loc + data_base + range)
     218  	    data->ret = f;
     219  	  data->func = (void *) (table[mid].initial_loc + data_base);
     220  	  return 1;
     221  	}
     222      }
     223  
     224    /* We have no sorted search table, so need to go the slow way.
     225       As soon as GLIBC will provide API so to notify that a library has been
     226       removed, we could cache this (and thus use search_object).  */
     227    ob.pc_begin = NULL;
     228    ob.tbase = data->tbase;
     229    ob.dbase = data->dbase;
     230    ob.u.single = (fde *) eh_frame;
     231    ob.s.i = 0;
     232    ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
     233    data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
     234    if (data->ret != NULL)
     235      {
     236        unsigned int encoding = get_fde_encoding (data->ret);
     237        _Unwind_Ptr func;
     238        read_encoded_value_with_base (encoding,
     239  				    base_from_cb_data (encoding, data),
     240  				    data->ret->pc_begin, &func);
     241        data->func = (void *) func;
     242      }
     243    return 1;
     244  }
     245  
     246  # ifdef _LIBC
     247  # define dl_iterate_phdr __dl_iterate_phdr
     248  # endif
     249  
     250  fde *
     251  _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
     252  {
     253    struct unw_eh_callback_data data;
     254    fde *ret;
     255  
     256    ret = _Unwind_Find_registered_FDE (pc, bases);
     257    if (ret != NULL)
     258      return ret;
     259  
     260    data.pc = (_Unwind_Ptr) pc;
     261    data.tbase = NULL;
     262    data.dbase = NULL;
     263    data.func = NULL;
     264    data.ret = NULL;
     265  
     266    if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
     267      return NULL;
     268  
     269    if (data.ret)
     270      {
     271        bases->tbase = data.tbase;
     272        bases->dbase = data.dbase;
     273        bases->func = data.func;
     274      }
     275    return data.ret;
     276  }
     277  
     278  #endif