aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/ia64/dl-fptr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/ia64/dl-fptr.c')
-rw-r--r--sysdeps/ia64/dl-fptr.c357
1 files changed, 216 insertions, 141 deletions
diff --git a/sysdeps/ia64/dl-fptr.c b/sysdeps/ia64/dl-fptr.c
index 588bb9d599..c31de86079 100644
--- a/sysdeps/ia64/dl-fptr.c
+++ b/sysdeps/ia64/dl-fptr.c
@@ -1,5 +1,5 @@
-/* Unmap a loaded object. IA-64 version.
- Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+/* Manage function descriptors. IA-64 version.
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -17,6 +17,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <ia64intrin.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
@@ -27,186 +28,260 @@
#include <dl-machine.h>
#ifdef _LIBC_REENTRANT
# include <pt-machine.h>
-
-static int __ia64_fptr_lock = 0;
+# include <signal.h>
+# include <time.h>
#endif
-/* Because ld.so is now versioned, these functions can be in their own
- file; no relocations need to be done to call them. Of course, if
- ld.so is not versioned... */
-#if 0
-#ifndef DO_VERSIONING
-# error "This will not work with versioning turned off, sorry."
-#endif
+Elf64_Addr __ia64_boot_fptr_table[IA64_BOOT_FPTR_TABLE_LEN];
+
+static struct local
+ {
+ struct ia64_fdesc_table *root;
+ struct ia64_fdesc *free_list;
+ unsigned int npages; /* # of pages to allocate */
+#ifdef _LIBC_REENTRANT
+ volatile int lock;
+ sigset_t full_sigset;
#endif
+ /* the next to members MUST be consecutive! */
+ struct ia64_fdesc_table boot_table;
+ struct ia64_fdesc boot_fdescs[1024];
+ }
+local =
+ {
+ root: &local.boot_table,
+ npages: 2,
+ boot_table:
+ {
+ len: sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]),
+ first_unused: 0
+ }
+ };
-#ifdef MAP_ANON
-/* The fd is not examined when using MAP_ANON. */
-#define ANONFD -1
+/* Locking is tricky: we may get a signal while holding the lock and
+ the signal handler may end up calling into the dynamic loader
+ again. Also, if a real-time process spins on the lock, a
+ non-realtime process may never get the chance to release it's lock,
+ unless the realtime process relinquishes the CPU from time to time.
+ Hence we (a) block signals before acquiring the lock and (b) do a
+ nanosleep() when we detect prolongued contention. */
+#ifdef _LIBC_REENTRANT
+# define lock(l) \
+{ \
+ sigset_t _saved_set; \
+ int i = 10000; \
+ if (!__sigismember (&(l)->full_sigset, SIGINT)) \
+ __sigfillset (&(l)->full_sigset); \
+ \
+ while (testandset ((int *) &(l)->lock)) \
+ { \
+ struct timespec ts; \
+ if (i > 0) \
+ { \
+ --i; \
+ continue; \
+ } \
+ ts.tv_sec = 0; \
+ ts.tv_nsec = 1*1000*1000; \
+ __nanosleep (&ts, NULL); \
+ } \
+ __sigprocmask (SIG_BLOCK, &(l)->full_sigset, &_saved_set);
+# define unlock(l) \
+ __sigprocmask (SIG_SETMASK, &_saved_set, NULL); \
+ (l)->lock = 0; \
+}
#else
-extern int _dl_zerofd;
-#define ANONFD _dl_zerofd
+# define lock(l)
+# define unlock(l)
#endif
+/* Create a new fdesc table and return a pointer to the first fdesc
+ entry. The fdesc lock must have been acquired already. */
-/* ld.so currently has 14 FPTR relocs, we take 256 and use them for
- the relocs for the dynamic app itself. */
-struct ia64_fptr __boot_ldso_fptr[IA64_BOOT_FPTR_SIZE];
-struct ia64_fptr *__fptr_root = NULL;
-struct ia64_fptr *__fptr_next = __boot_ldso_fptr;
-static struct ia64_fptr *__fptr_free = NULL;
-int __fptr_count = IA64_BOOT_FPTR_SIZE;
-
-Elf64_Addr
-__ia64_make_fptr (const struct link_map *sym_map, Elf64_Addr value,
- struct ia64_fptr **root, struct ia64_fptr *mem)
+static struct ia64_fdesc *
+new_fdesc_table (struct local *l)
{
- struct ia64_fptr **loc;
- struct ia64_fptr *f;
+ size_t size = l->npages * _dl_pagesize;
+ struct ia64_fdesc_table *new_table;
+ struct ia64_fdesc *fdesc;
-#ifdef _LIBC_REENTRANT
- /* Make sure we are alone. We don't need a lock during bootstrap. */
- if (mem == NULL)
- while (testandset (&__ia64_fptr_lock));
-#endif
+ l->npages += l->npages;
+ new_table = __mmap (0, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (new_table == MAP_FAILED)
+ _dl_signal_error (errno, NULL, "cannot map pages for fdesc table");
- /* Search the sorted linked list for an existing entry for this
- symbol. */
- loc = root;
- f = *loc;
- while (f != NULL && f->func <= value)
- {
- if (f->func == value)
- goto found;
- loc = &f->next;
- f = *loc;
- }
+ new_table->len = (size - sizeof (*new_table)) / sizeof (struct ia64_fdesc);
+ fdesc = &new_table->fdesc[0];
+ new_table->first_unused = 1;
+ new_table->next = l->root;
+ l->root = new_table;
+ return fdesc;
+}
- /* Not found. Create a new one. */
- if (mem != NULL)
- f = mem;
- else if (__fptr_free != NULL)
- {
- f = __fptr_free;
- __fptr_free = f->next;
- }
- else
+static Elf64_Addr
+make_fdesc (Elf64_Addr ip, Elf64_Addr gp)
+{
+ struct ia64_fdesc *fdesc = NULL;
+ struct ia64_fdesc_table *t;
+ unsigned int old;
+ struct local *l;
+
+ asm ("addl %0 = @gprel (local), gp" : "=r" (l));
+
+ t = l->root;
+ while (1)
{
- if (__fptr_count == 0)
+ old = t->first_unused;
+ if (old >= t->len)
+ break;
+ else if (__sync_bool_compare_and_swap (&t->first_unused, old, old + 1))
{
-#ifndef MAP_ANON
-# define MAP_ANON 0
- if (_dl_zerofd == -1)
- {
- _dl_zerofd = _dl_sysdep_open_zero_fill ();
- if (_dl_zerofd == -1)
- {
- __close (fd);
- _dl_signal_error (errno, NULL,
- "cannot open zero fill device");
- }
- }
-#endif
-
- __fptr_next = __mmap (0, _dl_pagesize, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_PRIVATE, ANONFD, 0);
- if (__fptr_next == MAP_FAILED)
- _dl_signal_error(errno, NULL, "cannot map page for fptr");
- __fptr_count = _dl_pagesize / sizeof (struct ia64_fptr);
+ fdesc = &t->fdesc[old];
+ goto install;
}
- f = __fptr_next++;
- __fptr_count--;
}
- f->func = value;
- /* GOT has already been relocated in elf_get_dynamic_info - don't
- try to relocate it again. */
- f->gp = sym_map->l_info[DT_PLTGOT]->d_un.d_ptr;
- f->next = *loc;
- *loc = f;
+ lock (l);
+ {
+ if (l->free_list)
+ {
+ fdesc = l->free_list; /* get it from free-list */
+ l->free_list = (struct ia64_fdesc *) fdesc->ip;
+ }
+ else
+ fdesc = new_fdesc_table (l); /* create new fdesc table */
+ }
+ unlock (l);
-found:
-#ifdef _LIBC_REENTRANT
- /* Release the lock. */
- if (mem == NULL)
- __ia64_fptr_lock = 0;
+ install:
+ fdesc->ip = ip;
+ fdesc->gp = gp;
+
+ return (Elf64_Addr) fdesc;
+}
+
+static inline Elf64_Addr *
+make_fptr_table (struct link_map *map)
+{
+ const Elf64_Sym *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ Elf64_Addr *fptr_table;
+ size_t size;
+ size_t len;
+
+ /* XXX Apparently the only way to find out the size of the dynamic
+ symbol section is to assume that the string table follows right
+ afterwards... */
+ len = ((strtab - (char *) symtab) / map->l_info[DT_SYMENT]->d_un.d_val);
+ size = ((len * sizeof (fptr_table[0]) + _dl_pagesize - 1) & -_dl_pagesize);
+ /* XXX We don't support here in the moment systems without MAP_ANON.
+ There probably are none for IA-64. In case this is proven wrong
+ we will have to open /dev/null here and use the file descriptor
+ instead of the hard-coded -1. */
+ fptr_table = __mmap (NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (fptr_table == MAP_FAILED)
+ _dl_signal_error (errno, NULL, "cannot map pages for fptr table");
+
+ map->l_mach.fptr_table_len = len;
+ map->l_mach.fptr_table = fptr_table;
+ return fptr_table;
+}
+
+Elf64_Addr
+__ia64_make_fptr (struct link_map *map, const Elf64_Sym *sym, Elf64_Addr ip)
+{
+ Elf64_Addr *ftab = map->l_mach.fptr_table;
+ const Elf64_Sym *symtab;
+ Elf_Symndx symidx;
+
+ if (__builtin_expect (!map->l_mach.fptr_table, 0))
+ ftab = make_fptr_table (map);
+
+ symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ symidx = sym - symtab;
+
+ if (symidx >= map->l_mach.fptr_table_len)
+ _dl_signal_error (0, NULL,
+ "internal error: symidx out of range of fptr table");
+
+ if (!ftab[symidx])
+ {
+ /* GOT has already been relocated in elf_get_dynamic_info -
+ don't try to relocate it again. */
+ ftab[symidx] = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr);
+#if 0
+ {
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ struct local *l;
+
+ asm ("addl %0 = @gprel (local), gp" : "=r" (l));
+ if (l->root != &l->boot_table || l->boot_table.first_unused > 20)
+ _dl_debug_printf ("created fdesc symbol `%s' at %lx\n",
+ strtab + sym->st_name, ftab[symidx]);
+ }
#endif
+ }
- return (Elf64_Addr) f;
+ return ftab[symidx];
}
void
_dl_unmap (struct link_map *map)
{
- struct ia64_fptr **floc;
- struct ia64_fptr *f;
- struct ia64_fptr **lloc;
- struct ia64_fptr *l;
+ Elf64_Addr *ftab = map->l_mach.fptr_table;
+ struct ia64_fdesc *head = NULL, *tail = NULL;
+ size_t i;
__munmap ((void *) map->l_map_start, map->l_map_end - map->l_map_start);
-#ifdef _LIBC_REENTRANT
- /* Make sure we are alone. */
- while (testandset (&__ia64_fptr_lock));
-#endif
+ if (!ftab)
+ return;
- /* Search the sorted linked list for the first entry for this object. */
- floc = &__fptr_root;
- f = *floc;
- while (f != NULL && f->func < map->l_map_start)
+ /* String together the fdesc structures that are being freed. */
+ for (i = 0; i < map->l_mach.fptr_table_len; ++i)
{
- floc = &f->next;
- f = *floc;
- }
-
- /* We found one. */
- if (f != NULL && f->func < map->l_map_end)
- {
- /* Get the last entry. */
- lloc = floc;
- l = f;
- while (l && l->func < map->l_map_end)
+ if (ftab[i])
{
- lloc = &l->next;
- l = *lloc;
+ *(struct ia64_fdesc **) ftab[i] = head;
+ head = (struct ia64_fdesc *) ftab[i];
+ if (!tail)
+ tail = head;
}
+ }
- /* Updated FPTR. */
- *floc = l;
-
- /* Prepend them to the free list. */
- *lloc = __fptr_free;
- __fptr_free = f;
+ /* Prepend the new list to the free_list: */
+ if (tail)
+ {
+ lock (&local);
+ {
+ *(struct ia64_fdesc **) tail = local.free_list;
+ local.free_list = head;
+ }
+ unlock (&local);
}
-#ifdef _LIBC_REENTRANT
- /* Release the lock. */
- __ia64_fptr_lock = 0;
-#endif
+ __munmap (ftab,
+ map->l_mach.fptr_table_len * sizeof (map->l_mach.fptr_table[0]));
+ map->l_mach.fptr_table = NULL;
}
Elf64_Addr
_dl_lookup_address (const void *address)
{
Elf64_Addr addr = (Elf64_Addr) address;
- struct ia64_fptr *f;
-
-#ifdef _LIBC_REENTRANT
- /* Make sure we are alone. */
- while (testandset (&__ia64_fptr_lock));
-#endif
-
- for (f = __fptr_root; f != NULL; f = f->next)
- if (f == address)
- {
- addr = f->func;
- break;
- }
-
-#ifdef _LIBC_REENTRANT
- /* Release the lock. */
- __ia64_fptr_lock = 0;
-#endif
+ struct ia64_fdesc_table *t;
+ unsigned long int i;
+ for (t = local.root; t != NULL; t = t->next)
+ {
+ i = (struct ia64_fdesc *) addr - &t->fdesc[0];
+ if (i < t->first_unused && addr == (Elf64_Addr) &t->fdesc[i])
+ {
+ addr = t->fdesc[i].ip;
+ break;
+ }
+ }
return addr;
}