aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/libgcc/config/ia64/fde-glibc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.9/libgcc/config/ia64/fde-glibc.c')
-rw-r--r--gcc-4.9/libgcc/config/ia64/fde-glibc.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/gcc-4.9/libgcc/config/ia64/fde-glibc.c b/gcc-4.9/libgcc/config/ia64/fde-glibc.c
new file mode 100644
index 000000000..8643b86ba
--- /dev/null
+++ b/gcc-4.9/libgcc/config/ia64/fde-glibc.c
@@ -0,0 +1,161 @@
+/* Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ Contributed by Richard Henderson <rth@cygnus.com>.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* Locate the FDE entry for a given address, using glibc ld.so routines
+ to avoid register/deregister calls at DSO load/unload. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include "config.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <link.h>
+#include "unwind-ia64.h"
+
+#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \
+ || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG))
+# error You need GLIBC 2.2.4 or later on IA-64 Linux
+#endif
+
+struct unw_ia64_callback_data
+{
+ Elf64_Addr pc;
+ unsigned long *segment_base;
+ unsigned long *gp;
+ struct unw_table_entry *ret;
+};
+
+static int
+_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
+{
+ struct unw_ia64_callback_data *data = (struct unw_ia64_callback_data *) ptr;
+ const Elf64_Phdr *phdr, *p_unwind, *p_dynamic;
+ long n, match;
+ Elf64_Addr load_base, seg_base;
+ struct unw_table_entry *f_base, *f;
+ size_t lo, hi;
+
+ /* Make sure struct dl_phdr_info is at least as big as we need. */
+ if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
+ + sizeof (info->dlpi_phnum))
+ return -1;
+
+ match = 0;
+ phdr = info->dlpi_phdr;
+ load_base = info->dlpi_addr;
+ p_unwind = NULL;
+ p_dynamic = NULL;
+ seg_base = ~(Elf64_Addr) 0;
+
+ /* See if PC falls into one of the loaded segments. Find the unwind
+ segment at the same time. */
+ for (n = info->dlpi_phnum; --n >= 0; phdr++)
+ {
+ if (phdr->p_type == PT_LOAD)
+ {
+ Elf64_Addr vaddr = phdr->p_vaddr + load_base;
+ if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
+ match = 1;
+ if (vaddr < seg_base)
+ seg_base = vaddr;
+ }
+ else if (phdr->p_type == PT_IA_64_UNWIND)
+ p_unwind = phdr;
+ else if (phdr->p_type == PT_DYNAMIC)
+ p_dynamic = phdr;
+ }
+ if (!match || !p_unwind)
+ return 0;
+
+ /* Search for the FDE within the unwind segment. */
+
+ f_base = (struct unw_table_entry *) (p_unwind->p_vaddr + load_base);
+ lo = 0;
+ hi = p_unwind->p_memsz / sizeof (struct unw_table_entry);
+
+ while (lo < hi)
+ {
+ size_t mid = (lo + hi) / 2;
+
+ f = f_base + mid;
+ if (data->pc < f->start_offset + seg_base)
+ hi = mid;
+ else if (data->pc >= f->end_offset + seg_base)
+ lo = mid + 1;
+ else
+ goto found;
+ }
+ /* No need to search for further libraries when we know pc is contained
+ in this library. */
+ return 1;
+
+ found:
+ *data->segment_base = seg_base;
+ *data->gp = 0;
+ data->ret = f;
+
+ if (p_dynamic)
+ {
+ /* For dynamically linked executables and shared libraries,
+ DT_PLTGOT is the gp value for that object. */
+ Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base);
+ for (; dyn->d_tag != DT_NULL ; dyn++)
+ if (dyn->d_tag == DT_PLTGOT)
+ {
+ /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */
+ *data->gp = dyn->d_un.d_ptr;
+ break;
+ }
+ }
+ else
+ {
+ /* Otherwise this is a static executable with no _DYNAMIC.
+ The gp is constant program-wide. */
+ register unsigned long gp __asm__("gp");
+ *data->gp = gp;
+ }
+
+ return 1;
+}
+
+/* Return a pointer to the unwind table entry for the function
+ containing PC. */
+
+struct unw_table_entry *
+_Unwind_FindTableEntry (void *pc, unw_word *segment_base, unw_word *gp,
+ struct unw_table_entry *ent ATTRIBUTE_UNUSED)
+{
+ struct unw_ia64_callback_data data;
+
+ data.pc = (Elf64_Addr) pc;
+ data.segment_base = segment_base;
+ data.gp = gp;
+ data.ret = NULL;
+
+ if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
+ return NULL;
+
+ return data.ret;
+}