Import GCC-8 to a new vendor branch
[dragonfly.git] / contrib / gcc-8.0 / libgcc / unwind-dw2-fde-dip.c
1 /* Copyright (C) 2001-2018 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(__OpenBSD__)
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
93 static 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
103 struct 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
113 struct 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
123 static 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
137 static 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
142 static _Unwind_Ptr
143 base_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
164 static 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 # ifdef __i386__
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
452 const 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)
488 alias (_Unwind_Find_FDE);
489 #endif