1/* Copyright (C) 2001-2020 Free Software Foundation, Inc.
2   Contributed by Jakub Jelinek <jakub@redhat.com>.
3
4   This file is part of GCC.
5
6   GCC is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3, or (at your option)
9   any later version.
10
11   GCC is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   Under Section 7 of GPL version 3, you are granted additional
17   permissions described in the GCC Runtime Library Exception, version
18   3.1, as published by the Free Software Foundation.
19
20   You should have received a copy of the GNU General Public License and
21   a copy of the GCC Runtime Library Exception along with this program;
22   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23   <http://www.gnu.org/licenses/>.  */
24
25/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF
26   segment and dl_iterate_phdr to avoid register/deregister calls at
27   DSO load/unload.  */
28
29#ifndef _GNU_SOURCE
30#define _GNU_SOURCE 1
31#endif
32
33#include "tconfig.h"
34#include "tsystem.h"
35#if !defined(inhibit_libc) && defined(__GLIBC__)
36#include <elf.h>		/* Get DT_CONFIG.  */
37#endif
38#include "coretypes.h"
39#include "tm.h"
40#include "libgcc_tm.h"
41#include "dwarf2.h"
42#include "unwind.h"
43#define NO_BASE_OF_ENCODED_VALUE
44#include "unwind-pe.h"
45#include "unwind-dw2-fde.h"
46#include "unwind-compat.h"
47#include "gthr.h"
48
49#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
50    && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
51	|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
52# define USE_PT_GNU_EH_FRAME
53#endif
54
55#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
56    && defined(__BIONIC__)
57# define USE_PT_GNU_EH_FRAME
58#endif
59
60#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
61    && defined(TARGET_DL_ITERATE_PHDR) \
62    && defined(__linux__)
63# define USE_PT_GNU_EH_FRAME
64#endif
65
66#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
67    && defined(TARGET_DL_ITERATE_PHDR) \
68    && (defined(__DragonFly__) || defined(__FreeBSD__))
69# define ElfW __ElfN
70# define USE_PT_GNU_EH_FRAME
71#endif
72
73#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
74    && (defined(__OpenBSD__) || defined(__NetBSD__))
75# define ElfW(type) Elf_##type
76# define USE_PT_GNU_EH_FRAME
77#endif
78
79#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
80    && defined(TARGET_DL_ITERATE_PHDR) \
81    && defined(__sun__) && defined(__svr4__)
82# define USE_PT_GNU_EH_FRAME
83#endif
84
85#if defined(USE_PT_GNU_EH_FRAME)
86
87#include <link.h>
88
89#ifndef __RELOC_POINTER
90# define __RELOC_POINTER(ptr, base) ((ptr) + (base))
91#endif
92
93static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
94
95#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
96#include "unwind-dw2-fde.c"
97#undef _Unwind_Find_FDE
98
99#ifndef PT_GNU_EH_FRAME
100#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
101#endif
102
103struct unw_eh_callback_data
104{
105  _Unwind_Ptr pc;
106  void *tbase;
107  void *dbase;
108  void *func;
109  const fde *ret;
110  int check_cache;
111};
112
113struct unw_eh_frame_hdr
114{
115  unsigned char version;
116  unsigned char eh_frame_ptr_enc;
117  unsigned char fde_count_enc;
118  unsigned char table_enc;
119};
120
121#define FRAME_HDR_CACHE_SIZE 8
122
123static struct frame_hdr_cache_element
124{
125  _Unwind_Ptr pc_low;
126  _Unwind_Ptr pc_high;
127#if defined __FRV_FDPIC__ || defined __BFIN_FDPIC__
128  struct elf32_fdpic_loadaddr load_base;
129#else
130  _Unwind_Ptr load_base;
131#endif
132  const ElfW(Phdr) *p_eh_frame_hdr;
133  const ElfW(Phdr) *p_dynamic;
134  struct frame_hdr_cache_element *link;
135} frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
136
137static struct frame_hdr_cache_element *frame_hdr_cache_head;
138
139/* Like base_of_encoded_value, but take the base from a struct
140   unw_eh_callback_data instead of an _Unwind_Context.  */
141
142static _Unwind_Ptr
143base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
144{
145  if (encoding == DW_EH_PE_omit)
146    return 0;
147
148  switch (encoding & 0x70)
149    {
150    case DW_EH_PE_absptr:
151    case DW_EH_PE_pcrel:
152    case DW_EH_PE_aligned:
153      return 0;
154
155    case DW_EH_PE_textrel:
156      return (_Unwind_Ptr) data->tbase;
157    case DW_EH_PE_datarel:
158      return (_Unwind_Ptr) data->dbase;
159    default:
160      gcc_unreachable ();
161    }
162}
163
164static int
165_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
166{
167  struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
168  const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
169  long n, match;
170#if defined __FRV_FDPIC__ || defined __BFIN_FDPIC__
171  struct elf32_fdpic_loadaddr load_base;
172#else
173  _Unwind_Ptr load_base;
174#endif
175  const unsigned char *p;
176  const struct unw_eh_frame_hdr *hdr;
177  _Unwind_Ptr eh_frame;
178  struct object ob;
179  _Unwind_Ptr pc_low = 0, pc_high = 0;
180
181  struct ext_dl_phdr_info
182    {
183      ElfW(Addr) dlpi_addr;
184      const char *dlpi_name;
185      const ElfW(Phdr) *dlpi_phdr;
186      ElfW(Half) dlpi_phnum;
187      unsigned long long int dlpi_adds;
188      unsigned long long int dlpi_subs;
189    };
190
191  match = 0;
192  phdr = info->dlpi_phdr;
193  load_base = info->dlpi_addr;
194  p_eh_frame_hdr = NULL;
195  p_dynamic = NULL;
196
197  struct frame_hdr_cache_element *prev_cache_entry = NULL,
198    *last_cache_entry = NULL;
199
200  if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
201    {
202      static unsigned long long adds = -1ULL, subs;
203      struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
204
205      /* We use a least recently used cache replacement policy.  Also,
206	 the most recently used cache entries are placed at the head
207	 of the search chain.  */
208
209      if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
210	{
211	  /* Find data->pc in shared library cache.
212	     Set load_base, p_eh_frame_hdr and p_dynamic
213	     plus match from the cache and goto
214	     "Read .eh_frame_hdr header." below.  */
215
216	  struct frame_hdr_cache_element *cache_entry;
217
218	  for (cache_entry = frame_hdr_cache_head;
219	       cache_entry;
220	       cache_entry = cache_entry->link)
221	    {
222	      if (data->pc >= cache_entry->pc_low
223		  && data->pc < cache_entry->pc_high)
224		{
225		  load_base = cache_entry->load_base;
226		  p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
227		  p_dynamic = cache_entry->p_dynamic;
228
229		  /* And move the entry we're using to the head.  */
230		  if (cache_entry != frame_hdr_cache_head)
231		    {
232		      prev_cache_entry->link = cache_entry->link;
233		      cache_entry->link = frame_hdr_cache_head;
234		      frame_hdr_cache_head = cache_entry;
235		    }
236		  goto found;
237		}
238
239	      last_cache_entry = cache_entry;
240	      /* Exit early if we found an unused entry.  */
241	      if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
242		break;
243	      if (cache_entry->link != NULL)
244		prev_cache_entry = cache_entry;
245	    }
246	}
247      else
248	{
249	  adds = einfo->dlpi_adds;
250	  subs = einfo->dlpi_subs;
251	  /* Initialize the cache.  Create a chain of cache entries,
252	     with the final one terminated by a NULL link.  */
253	  int i;
254	  for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
255	    {
256	      frame_hdr_cache[i].pc_low = 0;
257	      frame_hdr_cache[i].pc_high = 0;
258	      frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
259	    }
260	  frame_hdr_cache[i-1].link = NULL;
261	  frame_hdr_cache_head = &frame_hdr_cache[0];
262	  data->check_cache = 0;
263	}
264    }
265
266  /* Make sure struct dl_phdr_info is at least as big as we need.  */
267  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
268	     + sizeof (info->dlpi_phnum))
269    return -1;
270
271  /* See if PC falls into one of the loaded segments.  Find the eh_frame
272     segment at the same time.  */
273  for (n = info->dlpi_phnum; --n >= 0; phdr++)
274    {
275      if (phdr->p_type == PT_LOAD)
276	{
277	  _Unwind_Ptr vaddr = (_Unwind_Ptr)
278	    __RELOC_POINTER (phdr->p_vaddr, load_base);
279	  if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
280	    {
281	      match = 1;
282	      pc_low = vaddr;
283	      pc_high =  vaddr + phdr->p_memsz;
284	    }
285	}
286      else if (phdr->p_type == PT_GNU_EH_FRAME)
287	p_eh_frame_hdr = phdr;
288#ifdef PT_SUNW_UNWIND
289      /* Sun ld emits PT_SUNW_UNWIND .eh_frame_hdr sections instead of
290	 PT_SUNW_EH_FRAME/PT_GNU_EH_FRAME, so accept them as well.  */
291      else if (phdr->p_type == PT_SUNW_UNWIND)
292	p_eh_frame_hdr = phdr;
293#endif
294      else if (phdr->p_type == PT_DYNAMIC)
295	p_dynamic = phdr;
296    }
297
298  if (!match)
299    return 0;
300
301  if (size >= sizeof (struct ext_dl_phdr_info))
302    {
303      /* Move the cache entry we're about to overwrite to the head of
304	 the list.  If either last_cache_entry or prev_cache_entry are
305	 NULL, that cache entry is already at the head.  */
306      if (last_cache_entry != NULL && prev_cache_entry != NULL)
307	{
308	  prev_cache_entry->link = last_cache_entry->link;
309	  last_cache_entry->link = frame_hdr_cache_head;
310	  frame_hdr_cache_head = last_cache_entry;
311	}
312
313      frame_hdr_cache_head->load_base = load_base;
314      frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
315      frame_hdr_cache_head->p_dynamic = p_dynamic;
316      frame_hdr_cache_head->pc_low = pc_low;
317      frame_hdr_cache_head->pc_high = pc_high;
318    }
319
320 found:
321
322  if (!p_eh_frame_hdr)
323    return 0;
324
325  /* Read .eh_frame_hdr header.  */
326  hdr = (const struct unw_eh_frame_hdr *)
327    __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
328  if (hdr->version != 1)
329    return 1;
330
331#ifdef CRT_GET_RFIB_DATA
332# if defined __i386__ || defined __nios2__
333  data->dbase = NULL;
334  if (p_dynamic)
335    {
336      /* For dynamically linked executables and shared libraries,
337	 DT_PLTGOT is the gp value for that object.  */
338      ElfW(Dyn) *dyn = (ElfW(Dyn) *)
339	__RELOC_POINTER (p_dynamic->p_vaddr, load_base);
340      for (; dyn->d_tag != DT_NULL ; dyn++)
341	if (dyn->d_tag == DT_PLTGOT)
342	  {
343	    data->dbase = (void *) dyn->d_un.d_ptr;
344#if defined __linux__
345	    /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
346	       relocated it.  */
347#elif defined __sun__ && defined __svr4__
348	    /* On Solaris 2/x86, we need to do this ourselves.  */
349	    data->dbase += load_base;
350#endif
351	    break;
352	  }
353    }
354# elif (defined __FRV_FDPIC__ || defined __BFIN_FDPIC__) && defined __linux__
355  data->dbase = load_base.got_value;
356# else
357#  error What is DW_EH_PE_datarel base on this platform?
358# endif
359#endif
360
361  p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
362				    base_from_cb_data (hdr->eh_frame_ptr_enc,
363						       data),
364				    (const unsigned char *) (hdr + 1),
365				    &eh_frame);
366
367  /* We require here specific table encoding to speed things up.
368     Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start
369     as base, not the processor specific DW_EH_PE_datarel.  */
370  if (hdr->fde_count_enc != DW_EH_PE_omit
371      && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
372    {
373      _Unwind_Ptr fde_count;
374
375      p = read_encoded_value_with_base (hdr->fde_count_enc,
376					base_from_cb_data (hdr->fde_count_enc,
377							   data),
378					p, &fde_count);
379      /* Shouldn't happen.  */
380      if (fde_count == 0)
381	return 1;
382      if ((((_Unwind_Ptr) p) & 3) == 0)
383	{
384	  struct fde_table {
385	    signed initial_loc __attribute__ ((mode (SI)));
386	    signed fde __attribute__ ((mode (SI)));
387	  };
388	  const struct fde_table *table = (const struct fde_table *) p;
389	  size_t lo, hi, mid;
390	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
391	  fde *f;
392	  unsigned int f_enc, f_enc_size;
393	  _Unwind_Ptr range;
394
395	  mid = fde_count - 1;
396	  if (data->pc < table[0].initial_loc + data_base)
397	    return 1;
398	  else if (data->pc < table[mid].initial_loc + data_base)
399	    {
400	      lo = 0;
401	      hi = mid;
402
403	      while (lo < hi)
404		{
405		  mid = (lo + hi) / 2;
406		  if (data->pc < table[mid].initial_loc + data_base)
407		    hi = mid;
408		  else if (data->pc >= table[mid + 1].initial_loc + data_base)
409		    lo = mid + 1;
410		  else
411		    break;
412		}
413
414	      gcc_assert (lo < hi);
415	    }
416
417	  f = (fde *) (table[mid].fde + data_base);
418	  f_enc = get_fde_encoding (f);
419	  f_enc_size = size_of_encoded_value (f_enc);
420	  read_encoded_value_with_base (f_enc & 0x0f, 0,
421					&f->pc_begin[f_enc_size], &range);
422	  if (data->pc < table[mid].initial_loc + data_base + range)
423	    data->ret = f;
424	  data->func = (void *) (table[mid].initial_loc + data_base);
425	  return 1;
426	}
427    }
428
429  /* We have no sorted search table, so need to go the slow way.
430     As soon as GLIBC will provide API so to notify that a library has been
431     removed, we could cache this (and thus use search_object).  */
432  ob.pc_begin = NULL;
433  ob.tbase = data->tbase;
434  ob.dbase = data->dbase;
435  ob.u.single = (fde *) eh_frame;
436  ob.s.i = 0;
437  ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
438  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
439  if (data->ret != NULL)
440    {
441      _Unwind_Ptr func;
442      unsigned int encoding = get_fde_encoding (data->ret);
443
444      read_encoded_value_with_base (encoding,
445				    base_from_cb_data (encoding, data),
446				    data->ret->pc_begin, &func);
447      data->func = (void *) func;
448    }
449  return 1;
450}
451
452const fde *
453_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
454{
455  struct unw_eh_callback_data data;
456  const fde *ret;
457
458  ret = _Unwind_Find_registered_FDE (pc, bases);
459  if (ret != NULL)
460    return ret;
461
462  data.pc = (_Unwind_Ptr) pc;
463  data.tbase = NULL;
464  data.dbase = NULL;
465  data.func = NULL;
466  data.ret = NULL;
467  data.check_cache = 1;
468
469  if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
470    return NULL;
471
472  if (data.ret)
473    {
474      bases->tbase = data.tbase;
475      bases->dbase = data.dbase;
476      bases->func = data.func;
477    }
478  return data.ret;
479}
480
481#else
482/* Prevent multiple include of header files.  */
483#define _Unwind_Find_FDE _Unwind_Find_FDE
484#include "unwind-dw2-fde.c"
485#endif
486
487#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
488alias (_Unwind_Find_FDE);
489#endif
490