diff options
-rw-r--r-- | ChangeLog | 26 | ||||
-rw-r--r-- | sysdeps/generic/dl-fptr.c | 312 | ||||
-rw-r--r-- | sysdeps/generic/dl-fptr.h | 44 | ||||
-rw-r--r-- | sysdeps/ia64/dl-fptr.h (renamed from sysdeps/ia64/dl-symaddr.c) | 31 | ||||
-rw-r--r-- | sysdeps/ia64/dl-machine.h | 34 |
5 files changed, 405 insertions, 42 deletions
diff --git a/ChangeLog b/ChangeLog index c6332049d2..dec9c8ab3a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2003-04-07 H.J. Lu <hjl@gnu.org> + + * sysdeps/generic/dl-fptr.c: Modify to remove the lock. + +2003-04-03 H.J. Lu <hjl@gnu.org> + + * sysdeps/ia64/dl-fptr.c: Moved to ... + * sysdeps/generic/dl-fptr.c: Here. + + * sysdeps/generic/dl-fptr.h: New. + * sysdeps/ia64/dl-fptr.h: New. + + * sysdeps/ia64/dl-symaddr.c: Moved to ... + * sysdeps/generic/dl-symaddr.c: here. + + * sysdeps/ia64/dl-machine.h: Include <dl-fptr.h>. + (IA64_BOOT_FPTR_TABLE_LEN): Removed. + (ia64_fdesc): Likewise. + (ia64_fdesc_table): Likewise. + (__ia64_make_fptr): Likewise. + (__ia64_init_bootstrap_fdesc_table): Replace __ia64_boot_fptr_table + with _dl_boot_fptr_table. + (elf_machine_runtime_setup): Replace `struct ia64_fdesc' with + `struct fdesc'. + (elf_machine_rela): Replace __ia64_make_fptr with _dl_make_fptr. + 2003-05-01 Roland McGrath <roland@redhat.com> * sysdeps/generic/bp-thunks.h: Protect includes with [!__ASSEMBLER__]. diff --git a/sysdeps/generic/dl-fptr.c b/sysdeps/generic/dl-fptr.c new file mode 100644 index 0000000000..d8dbefa95f --- /dev/null +++ b/sysdeps/generic/dl-fptr.c @@ -0,0 +1,312 @@ +/* Manage function descriptors. Generic version. + Copyright (C) 1999,2000,2001,2002,2003 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 + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <unistd.h> +#include <string.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <link.h> +#include <ldsodefs.h> +#include <elf/dynamic-link.h> +#include <dl-fptr.h> +#include <atomic.h> + +#ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN +/* ELF_MACHINE_BOOT_FPTR_TABLE_LEN should be greater than the number of + dynamic symbols in ld.so. */ +#define ELF_MACHINE_BOOT_FPTR_TABLE_LEN 256 +#endif + +#ifndef ELF_MACHINE_LOAD_ADDRESS +# error "ELF_MACHINE_LOAD_ADDRESS is not defined." +#endif + +#ifndef COMPARE_AND_SWAP +#define COMPARE_AND_SWAP(ptr,old,new) \ + atomic_compare_and_exchange_bool_acq ((ptr), (old), (new)) +#endif + +ElfW(Addr) _dl_boot_fptr_table [ELF_MACHINE_BOOT_FPTR_TABLE_LEN]; + +static struct local + { + struct fdesc_table *root; + struct fdesc *free_list; + unsigned int npages; /* # of pages to allocate */ + /* the next to members MUST be consecutive! */ + struct fdesc_table boot_table; + struct 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 + } + }; + +/* Create a new fdesc table and return a pointer to the first fdesc + entry. The fdesc lock must have been acquired already. */ + +static struct fdesc_table * +new_fdesc_table (struct local *l, size_t *size) +{ + size_t old_npages = l->npages; + size_t new_npages = old_npages + old_npages; + struct fdesc_table *new_table; + + /* If someone has just created a new table, we return NULL to tell + the caller to use the new table. */ + if (! COMPARE_AND_SWAP (&l->npages, old_npages, new_npages)) + return (struct fdesc_table *) NULL; + + *size = old_npages * GL(dl_pagesize); + 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, NULL, + "cannot map pages for fdesc table"); + + new_table->len + = (*size - sizeof (*new_table)) / sizeof (struct fdesc); + new_table->first_unused = 1; + return new_table; +} + +static ElfW(Addr) +make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp) +{ + struct fdesc *fdesc = NULL; + struct fdesc_table *root; + unsigned int old; + struct local *l; + + ELF_MACHINE_LOAD_ADDRESS (l, local); + + retry: + root = l->root; + while (1) + { + old = root->first_unused; + if (old >= root->len) + break; + else if (COMPARE_AND_SWAP (&root->first_unused, old, old + 1)) + { + fdesc = &root->fdesc[old]; + goto install; + } + } + + if (l->free_list) + { + /* Get it from free-list */ + do + { + fdesc = l->free_list; + if (fdesc == NULL) + goto retry; + } + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list, + (ElfW(Addr)) fdesc, fdesc->ip)); + } + else + { + /* Create a new fdesc table */ + size_t size; + struct fdesc_table *new_table = new_fdesc_table (l, &size); + + if (new_table == NULL) + goto retry; + + new_table->next = root; + if (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->root, + (ElfW(Addr)) root, + (ElfW(Addr)) new_table)) + /* Someone has just installed a new table. Return NULL to + tell the caller to use the new table. */ + __munmap (new_table, size); + + goto retry; + } + + install: + fdesc->ip = ip; + fdesc->gp = gp; + + return (ElfW(Addr)) fdesc; +} + +static inline ElfW(Addr) * +make_fptr_table (struct link_map *map) +{ + const ElfW(Sym) *symtab + = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ElfW(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]) + GL(dl_pagesize) - 1) + & -GL(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, NULL, + "cannot map pages for fptr table"); + + if (COMPARE_AND_SWAP ((ElfW(Addr) *) &map->l_mach.fptr_table, + (ElfW(Addr)) NULL, (ElfW(Addr)) fptr_table)) + map->l_mach.fptr_table_len = len; + else + __munmap (fptr_table, len * sizeof (fptr_table[0])); + + return map->l_mach.fptr_table; +} + +ElfW(Addr) +_dl_make_fptr (struct link_map *map, const ElfW(Sym) *sym, + ElfW(Addr) ip) +{ + ElfW(Addr) *ftab = map->l_mach.fptr_table; + const ElfW(Sym) *symtab; + Elf_Symndx symidx; + struct local *l; + + if (__builtin_expect (ftab == NULL, 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, NULL, + "internal error: symidx out of range of fptr table"); + + while (ftab[symidx] == NULL) + { + /* GOT has already been relocated in elf_get_dynamic_info - + don't try to relocate it again. */ + ElfW(Addr) fdesc + = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr); + + if (__builtin_expect (COMPARE_AND_SWAP (&ftab[symidx], (ElfW(Addr)) NULL, + fdesc), 1)) + { + /* Noone has updated the entry and the new function + descriptor has been installed. */ +#if 0 + const char *strtab + = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + ELF_MACHINE_LOAD_ADDRESS (l, local); + 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 + break; + } + else + { + /* We created a duplicated function descriptor. We put it on + free-list. */ + struct fdesc *f = (struct fdesc *) fdesc; + + ELF_MACHINE_LOAD_ADDRESS (l, local); + + do + f->ip = (ElfW(Addr)) l->free_list; + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list, + f->ip, fdesc)); + } + } + + return ftab[symidx]; +} + +void +_dl_unmap (struct link_map *map) +{ + ElfW(Addr) *ftab = map->l_mach.fptr_table; + struct fdesc *head = NULL, *tail = NULL; + size_t i; + + __munmap ((void *) map->l_map_start, + map->l_map_end - map->l_map_start); + + if (ftab == NULL) + return; + + /* String together the fdesc structures that are being freed. */ + for (i = 0; i < map->l_mach.fptr_table_len; ++i) + { + if (ftab[i]) + { + *(struct fdesc **) ftab[i] = head; + head = (struct fdesc *) ftab[i]; + if (tail = NULL) + tail = head; + } + } + + /* Prepend the new list to the free_list: */ + if (tail) + do + tail->ip = (ElfW(Addr)) local.free_list; + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &local.free_list, + tail->ip, (ElfW(Addr)) head)); + + __munmap (ftab, (map->l_mach.fptr_table_len + * sizeof (map->l_mach.fptr_table[0]))); + + map->l_mach.fptr_table = NULL; +} + +ElfW(Addr) +_dl_lookup_address (const void *address) +{ + ElfW(Addr) addr = (ElfW(Addr)) address; + struct fdesc_table *t; + unsigned long int i; + + for (t = local.root; t != NULL; t = t->next) + { + i = (struct fdesc *) addr - &t->fdesc[0]; + if (i < t->first_unused && addr == (ElfW(Addr)) &t->fdesc[i]) + { + addr = t->fdesc[i].ip; + break; + } + } + return addr; +} diff --git a/sysdeps/generic/dl-fptr.h b/sysdeps/generic/dl-fptr.h new file mode 100644 index 0000000000..8156981e6e --- /dev/null +++ b/sysdeps/generic/dl-fptr.h @@ -0,0 +1,44 @@ +/* Function descriptors. Generic version. + Copyright (C) 1995, 1996, 1997, 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 + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef dl_fptr_h +#define dl_fptr_h 1 + +/* An FDESC is a function descriptor. */ + +struct fdesc + { + ElfW(Addr) ip; /* code entry point */ + ElfW(Addr) gp; /* global pointer */ + }; + +struct fdesc_table + { + struct fdesc_table *next; + unsigned int len; /* # of entries in fdesc table */ + volatile unsigned int first_unused; /* index of first available entry */ + struct fdesc fdesc[0]; + }; + +extern ElfW(Addr) _dl_boot_fptr_table []; + +extern ElfW(Addr) _dl_make_fptr (struct link_map *, const ElfW(Sym) *, + ElfW(Addr)); + +#endif /* !dl_fptr_h */ diff --git a/sysdeps/ia64/dl-symaddr.c b/sysdeps/ia64/dl-fptr.h index c1d6f7295e..de6771305e 100644 --- a/sysdeps/ia64/dl-symaddr.c +++ b/sysdeps/ia64/dl-fptr.h @@ -1,5 +1,5 @@ -/* Get the symbol address. IA-64 version. - Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. +/* Function descriptors. IA64 version. + Copyright (C) 2003 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,17 +17,20 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <ldsodefs.h> -#include <dl-machine.h> +#ifndef dl_ia64_fptr_h +#define dl_ia64_fptr_h 1 -void * -_dl_symbol_address (struct link_map *map, const Elf64_Sym *ref) -{ - Elf64_Addr value = (map ? map->l_addr : 0) + ref->st_value; +#include <ia64intrin.h> +#include <sysdeps/generic/dl-fptr.h> - /* On ia64, we have to return the pointer to function descriptor. */ - if (ELFW(ST_TYPE) (ref->st_info) == STT_FUNC) - return (void *) __ia64_make_fptr (map, ref, value); - else - return (void *) value; -} +#define COMPARE_AND_SWAP(ptr,old,new) \ + __sync_bool_compare_and_swap ((ptr), (old), (new)) + +/* There are currently 123 dynamic symbols in ld.so. + ELF_MACHINE_BOOT_FPTR_TABLE_LEN needs to be at least that big. */ +#define ELF_MACHINE_BOOT_FPTR_TABLE_LEN 200 + +#define ELF_MACHINE_LOAD_ADDRESS(var,symbol) \ + asm ("addl %0 = @gprel (" #symbol "), gp" : "=r" (var)); + +#endif /* !dl_ia64_fptr_h */ diff --git a/sysdeps/ia64/dl-machine.h b/sysdeps/ia64/dl-machine.h index d3cc0ca210..7aaf0848e3 100644 --- a/sysdeps/ia64/dl-machine.h +++ b/sysdeps/ia64/dl-machine.h @@ -26,44 +26,22 @@ #include <string.h> #include <link.h> #include <errno.h> +#include <dl-fptr.h> #include <tls.h> /* Translate a processor specific dynamic tag to the index in l_info array. */ #define DT_IA_64(x) (DT_IA_64_##x - DT_LOPROC + DT_NUM) -/* There are currently 123 dynamic symbols in ld.so. - IA64_BOOT_FPTR_TABLE_LEN needs to be at least that big. */ -#define IA64_BOOT_FPTR_TABLE_LEN 200 - -/* An FDESC is a function descriptor. */ - -struct ia64_fdesc - { - Elf64_Addr ip; /* code entry point */ - Elf64_Addr gp; /* global pointer */ - }; - -struct ia64_fdesc_table - { - struct ia64_fdesc_table *next; - unsigned int len; /* # of entries in fdesc table */ - volatile unsigned int first_unused; /* index of first available entry */ - struct ia64_fdesc fdesc[0]; - }; - -extern Elf64_Addr __ia64_make_fptr (struct link_map *, const Elf64_Sym *, - Elf64_Addr); - static inline void __ia64_init_bootstrap_fdesc_table (struct link_map *map) { Elf64_Addr *boot_table; /* careful: this will be called before got has been relocated... */ - asm (";; addl %0 = @gprel (__ia64_boot_fptr_table), gp" : "=r"(boot_table)); + asm (";; addl %0 = @gprel (_dl_boot_fptr_table), gp" : "=r"(boot_table)); - map->l_mach.fptr_table_len = IA64_BOOT_FPTR_TABLE_LEN; + map->l_mach.fptr_table_len = ELF_MACHINE_BOOT_FPTR_TABLE_LEN; map->l_mach.fptr_table = boot_table; } @@ -142,7 +120,7 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) /* This function will be called to perform the relocation. */ if (!profile) - doit = (Elf64_Addr) ((struct ia64_fdesc *) &_dl_runtime_resolve)->ip; + doit = (Elf64_Addr) ((struct fdesc *) &_dl_runtime_resolve)->ip; else { if (_dl_name_match_p (GL(dl_profile), l)) @@ -151,7 +129,7 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) want profiling and the timers are started. */ GL(dl_profile_map) = l; } - doit = (Elf64_Addr) ((struct ia64_fdesc *) &_dl_runtime_profile)->ip; + doit = (Elf64_Addr) ((struct fdesc *) &_dl_runtime_profile)->ip; } reserve[1] = doit; @@ -579,7 +557,7 @@ elf_machine_rela (struct link_map *map, return; } else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB)) - value = __ia64_make_fptr (sym_map, sym, value); + value = _dl_make_fptr (sym_map, sym, value); else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB)) value -= (Elf64_Addr) reloc_addr & -16; #if defined USE_TLS && (!defined RTLD_BOOTSTRAP || defined USE___THREAD) |