summaryrefslogtreecommitdiffstats
path: root/contrib/gcc/unwind-dw2-fde-glibc.c
diff options
context:
space:
mode:
authorkan <kan@FreeBSD.org>2007-05-19 01:19:51 +0000
committerkan <kan@FreeBSD.org>2007-05-19 01:19:51 +0000
commit1f9ea4d0a40cca64d60cf4dab152349da7b9dddf (patch)
tree0cb530c9c38af219e6dda2994c078b6b2b9ad853 /contrib/gcc/unwind-dw2-fde-glibc.c
parent4895159b2b4f648051c1f139faa7b6dc50c2bfcb (diff)
downloadFreeBSD-src-1f9ea4d0a40cca64d60cf4dab152349da7b9dddf.zip
FreeBSD-src-1f9ea4d0a40cca64d60cf4dab152349da7b9dddf.tar.gz
GCC 4.2.0 release.
Diffstat (limited to 'contrib/gcc/unwind-dw2-fde-glibc.c')
-rw-r--r--contrib/gcc/unwind-dw2-fde-glibc.c177
1 files changed, 158 insertions, 19 deletions
diff --git a/contrib/gcc/unwind-dw2-fde-glibc.c b/contrib/gcc/unwind-dw2-fde-glibc.c
index e7d09cf..25bf2bb 100644
--- a/contrib/gcc/unwind-dw2-fde-glibc.c
+++ b/contrib/gcc/unwind-dw2-fde-glibc.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>.
This file is part of GCC.
@@ -15,8 +15,8 @@
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
@@ -33,7 +33,6 @@
#define _GNU_SOURCE 1
#endif
-#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR. */
#include "tconfig.h"
#include "tsystem.h"
#ifndef inhibit_libc
@@ -53,6 +52,10 @@
&& (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
+#ifndef __RELOC_POINTER
+# define __RELOC_POINTER(ptr, base) ((ptr) + (base))
+#endif
+
static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
@@ -70,6 +73,7 @@ struct unw_eh_callback_data
void *dbase;
void *func;
const fde *ret;
+ int check_cache;
};
struct unw_eh_frame_hdr
@@ -80,6 +84,20 @@ struct unw_eh_frame_hdr
unsigned char table_enc;
};
+#define FRAME_HDR_CACHE_SIZE 8
+
+static struct frame_hdr_cache_element
+{
+ _Unwind_Ptr pc_low;
+ _Unwind_Ptr pc_high;
+ _Unwind_Ptr load_base;
+ const ElfW(Phdr) *p_eh_frame_hdr;
+ const ElfW(Phdr) *p_dynamic;
+ struct frame_hdr_cache_element *link;
+} frame_hdr_cache[FRAME_HDR_CACHE_SIZE];
+
+static struct frame_hdr_cache_element *frame_hdr_cache_head;
+
/* Like base_of_encoded_value, but take the base from a struct
unw_eh_callback_data instead of an _Unwind_Context. */
@@ -100,8 +118,9 @@ base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data)
return (_Unwind_Ptr) data->tbase;
case DW_EH_PE_datarel:
return (_Unwind_Ptr) data->dbase;
+ default:
+ gcc_unreachable ();
}
- abort ();
}
static int
@@ -110,16 +129,25 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr;
const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic;
long n, match;
+#ifdef __FRV_FDPIC__
+ struct elf32_fdpic_loadaddr load_base;
+#else
_Unwind_Ptr load_base;
+#endif
const unsigned char *p;
const struct unw_eh_frame_hdr *hdr;
_Unwind_Ptr eh_frame;
struct object ob;
-
- /* 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;
+
+ struct ext_dl_phdr_info
+ {
+ ElfW(Addr) dlpi_addr;
+ const char *dlpi_name;
+ const ElfW(Phdr) *dlpi_phdr;
+ ElfW(Half) dlpi_phnum;
+ unsigned long long int dlpi_adds;
+ unsigned long long int dlpi_subs;
+ };
match = 0;
phdr = info->dlpi_phdr;
@@ -127,27 +155,133 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
p_eh_frame_hdr = NULL;
p_dynamic = NULL;
+ struct frame_hdr_cache_element *prev_cache_entry = NULL,
+ *last_cache_entry = NULL;
+
+ if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info))
+ {
+ static unsigned long long adds = -1ULL, subs;
+ struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info;
+
+ /* We use a least recently used cache replacement policy. Also,
+ the most recently used cache entries are placed at the head
+ of the search chain. */
+
+ if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs)
+ {
+ /* Find data->pc in shared library cache.
+ Set load_base, p_eh_frame_hdr and p_dynamic
+ plus match from the cache and goto
+ "Read .eh_frame_hdr header." below. */
+
+ struct frame_hdr_cache_element *cache_entry;
+
+ for (cache_entry = frame_hdr_cache_head;
+ cache_entry;
+ cache_entry = cache_entry->link)
+ {
+ if (data->pc >= cache_entry->pc_low
+ && data->pc < cache_entry->pc_high)
+ {
+ load_base = cache_entry->load_base;
+ p_eh_frame_hdr = cache_entry->p_eh_frame_hdr;
+ p_dynamic = cache_entry->p_dynamic;
+
+ /* And move the entry we're using to the head. */
+ if (cache_entry != frame_hdr_cache_head)
+ {
+ prev_cache_entry->link = cache_entry->link;
+ cache_entry->link = frame_hdr_cache_head;
+ frame_hdr_cache_head = cache_entry;
+ }
+ goto found;
+ }
+
+ last_cache_entry = cache_entry;
+ /* Exit early if we found an unused entry. */
+ if ((cache_entry->pc_low | cache_entry->pc_high) == 0)
+ break;
+ if (cache_entry->link != NULL)
+ prev_cache_entry = cache_entry;
+ }
+ }
+ else
+ {
+ adds = einfo->dlpi_adds;
+ subs = einfo->dlpi_subs;
+ /* Initialize the cache. Create a chain of cache entries,
+ with the final one terminated by a NULL link. */
+ int i;
+ for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++)
+ {
+ frame_hdr_cache[i].pc_low = 0;
+ frame_hdr_cache[i].pc_high = 0;
+ frame_hdr_cache[i].link = &frame_hdr_cache[i+1];
+ }
+ frame_hdr_cache[i-1].link = NULL;
+ frame_hdr_cache_head = &frame_hdr_cache[0];
+ data->check_cache = 0;
+ }
+ }
+
+ /* 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;
+
+ _Unwind_Ptr pc_low = 0, pc_high = 0;
+
/* See if PC falls into one of the loaded segments. Find the eh_frame
segment at the same time. */
for (n = info->dlpi_phnum; --n >= 0; phdr++)
{
if (phdr->p_type == PT_LOAD)
{
- _Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
+ _Unwind_Ptr vaddr = (_Unwind_Ptr)
+ __RELOC_POINTER (phdr->p_vaddr, load_base);
if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
- match = 1;
+ {
+ match = 1;
+ pc_low = vaddr;
+ pc_high = vaddr + phdr->p_memsz;
+ }
}
else if (phdr->p_type == PT_GNU_EH_FRAME)
p_eh_frame_hdr = phdr;
else if (phdr->p_type == PT_DYNAMIC)
p_dynamic = phdr;
}
- if (!match || !p_eh_frame_hdr)
+
+ if (!match)
+ return 0;
+
+ if (size >= sizeof (struct ext_dl_phdr_info))
+ {
+ /* Move the cache entry we're about to overwrite to the head of
+ the list. If either last_cache_entry or prev_cache_entry are
+ NULL, that cache entry is already at the head. */
+ if (last_cache_entry != NULL && prev_cache_entry != NULL)
+ {
+ prev_cache_entry->link = last_cache_entry->link;
+ last_cache_entry->link = frame_hdr_cache_head;
+ frame_hdr_cache_head = last_cache_entry;
+ }
+
+ frame_hdr_cache_head->load_base = load_base;
+ frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr;
+ frame_hdr_cache_head->p_dynamic = p_dynamic;
+ frame_hdr_cache_head->pc_low = pc_low;
+ frame_hdr_cache_head->pc_high = pc_high;
+ }
+
+ found:
+
+ if (!p_eh_frame_hdr)
return 0;
/* Read .eh_frame_hdr header. */
hdr = (const struct unw_eh_frame_hdr *)
- (p_eh_frame_hdr->p_vaddr + load_base);
+ __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
if (hdr->version != 1)
return 1;
@@ -158,7 +292,8 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
{
/* For dynamically linked executables and shared libraries,
DT_PLTGOT is the gp value for that object. */
- ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base);
+ ElfW(Dyn) *dyn = (ElfW(Dyn) *)
+ __RELOC_POINTER (p_dynamic->p_vaddr, load_base);
for (; dyn->d_tag != DT_NULL ; dyn++)
if (dyn->d_tag == DT_PLTGOT)
{
@@ -167,6 +302,8 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
break;
}
}
+# elif defined __FRV_FDPIC__ && defined __linux__
+ data->dbase = load_base.got_value;
# else
# error What is DW_EH_PE_datarel base on this platform?
# endif
@@ -225,8 +362,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
break;
}
- if (lo >= hi)
- __gxx_abort ();
+ gcc_assert (lo < hi);
}
f = (fde *) (table[mid].fde + data_base);
@@ -253,11 +389,13 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr)
data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
if (data->ret != NULL)
{
+ _Unwind_Ptr func;
unsigned int encoding = get_fde_encoding (data->ret);
+
read_encoded_value_with_base (encoding,
base_from_cb_data (encoding, data),
- data->ret->pc_begin,
- (_Unwind_Ptr *)&data->func);
+ data->ret->pc_begin, &func);
+ data->func = (void *) func;
}
return 1;
}
@@ -277,6 +415,7 @@ _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
data.dbase = NULL;
data.func = NULL;
data.ret = NULL;
+ data.check_cache = 1;
if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
return NULL;
OpenPOWER on IntegriCloud