Catalon message catalog for glibc.
[glibc.git] / sysdeps / ia64 / dl-fptr.c
blob69166352814754f5a1cfb4948d0c352c36ff0d68
1 /* Manage function descriptors. IA-64 version.
2 Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <ia64intrin.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/param.h>
24 #include <sys/mman.h>
25 #include <link.h>
26 #include <ldsodefs.h>
27 #include <elf/dynamic-link.h>
28 #include <dl-machine.h>
29 #ifdef _LIBC_REENTRANT
30 # include <pt-machine.h>
31 # include <signal.h>
32 # include <time.h>
33 #endif
35 Elf64_Addr __ia64_boot_fptr_table[IA64_BOOT_FPTR_TABLE_LEN];
37 static struct local
39 struct ia64_fdesc_table *root;
40 struct ia64_fdesc *free_list;
41 unsigned int npages; /* # of pages to allocate */
42 #ifdef _LIBC_REENTRANT
43 volatile int lock;
44 sigset_t full_sigset;
45 #endif
46 /* the next to members MUST be consecutive! */
47 struct ia64_fdesc_table boot_table;
48 struct ia64_fdesc boot_fdescs[1024];
50 local =
52 root: &local.boot_table,
53 npages: 2,
54 boot_table:
56 len: sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]),
57 first_unused: 0
61 /* Locking is tricky: we may get a signal while holding the lock and
62 the signal handler may end up calling into the dynamic loader
63 again. Also, if a real-time process spins on the lock, a
64 non-realtime process may never get the chance to release it's lock,
65 unless the realtime process relinquishes the CPU from time to time.
66 Hence we (a) block signals before acquiring the lock and (b) do a
67 nanosleep() when we detect prolongued contention. */
68 #ifdef _LIBC_REENTRANT
69 # define lock(l) \
70 { \
71 sigset_t _saved_set; \
72 int i = 10000; \
73 if (!__sigismember (&(l)->full_sigset, SIGINT)) \
74 __sigfillset (&(l)->full_sigset); \
76 while (testandset ((int *) &(l)->lock)) \
77 { \
78 struct timespec ts; \
79 if (i > 0) \
80 { \
81 --i; \
82 continue; \
83 } \
84 ts.tv_sec = 0; \
85 ts.tv_nsec = 1*1000*1000; \
86 __nanosleep (&ts, NULL); \
87 } \
88 __sigprocmask (SIG_BLOCK, &(l)->full_sigset, &_saved_set);
89 # define unlock(l) \
90 __sigprocmask (SIG_SETMASK, &_saved_set, NULL); \
91 (l)->lock = 0; \
93 #else
94 # define lock(l)
95 # define unlock(l)
96 #endif
98 /* Create a new fdesc table and return a pointer to the first fdesc
99 entry. The fdesc lock must have been acquired already. */
101 static struct ia64_fdesc *
102 new_fdesc_table (struct local *l)
104 size_t size = l->npages * _dl_pagesize;
105 struct ia64_fdesc_table *new_table;
106 struct ia64_fdesc *fdesc;
108 l->npages += l->npages;
109 new_table = __mmap (0, size, PROT_READ | PROT_WRITE,
110 MAP_ANON | MAP_PRIVATE, -1, 0);
111 if (new_table == MAP_FAILED)
112 _dl_signal_error (errno, NULL, NULL, "cannot map pages for fdesc table");
114 new_table->len = (size - sizeof (*new_table)) / sizeof (struct ia64_fdesc);
115 fdesc = &new_table->fdesc[0];
116 new_table->first_unused = 1;
117 new_table->next = l->root;
118 l->root = new_table;
119 return fdesc;
122 static Elf64_Addr
123 make_fdesc (Elf64_Addr ip, Elf64_Addr gp)
125 struct ia64_fdesc *fdesc = NULL;
126 struct ia64_fdesc_table *t;
127 unsigned int old;
128 struct local *l;
130 asm ("addl %0 = @gprel (local), gp" : "=r" (l));
132 t = l->root;
133 while (1)
135 old = t->first_unused;
136 if (old >= t->len)
137 break;
138 else if (__sync_bool_compare_and_swap (&t->first_unused, old, old + 1))
140 fdesc = &t->fdesc[old];
141 goto install;
145 lock (l);
147 if (l->free_list)
149 fdesc = l->free_list; /* get it from free-list */
150 l->free_list = (struct ia64_fdesc *) fdesc->ip;
152 else
153 fdesc = new_fdesc_table (l); /* create new fdesc table */
155 unlock (l);
157 install:
158 fdesc->ip = ip;
159 fdesc->gp = gp;
161 return (Elf64_Addr) fdesc;
164 static inline Elf64_Addr *
165 make_fptr_table (struct link_map *map)
167 const Elf64_Sym *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
168 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
169 Elf64_Addr *fptr_table;
170 size_t size;
171 size_t len;
173 /* XXX Apparently the only way to find out the size of the dynamic
174 symbol section is to assume that the string table follows right
175 afterwards... */
176 len = ((strtab - (char *) symtab) / map->l_info[DT_SYMENT]->d_un.d_val);
177 size = ((len * sizeof (fptr_table[0]) + _dl_pagesize - 1) & -_dl_pagesize);
178 /* XXX We don't support here in the moment systems without MAP_ANON.
179 There probably are none for IA-64. In case this is proven wrong
180 we will have to open /dev/null here and use the file descriptor
181 instead of the hard-coded -1. */
182 fptr_table = __mmap (NULL, size, PROT_READ | PROT_WRITE,
183 MAP_ANON | MAP_PRIVATE, -1, 0);
184 if (fptr_table == MAP_FAILED)
185 _dl_signal_error (errno, NULL, NULL, "cannot map pages for fptr table");
187 map->l_mach.fptr_table_len = len;
188 map->l_mach.fptr_table = fptr_table;
189 return fptr_table;
192 Elf64_Addr
193 __ia64_make_fptr (struct link_map *map, const Elf64_Sym *sym, Elf64_Addr ip)
195 Elf64_Addr *ftab = map->l_mach.fptr_table;
196 const Elf64_Sym *symtab;
197 Elf_Symndx symidx;
199 if (__builtin_expect (!map->l_mach.fptr_table, 0))
200 ftab = make_fptr_table (map);
202 symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
203 symidx = sym - symtab;
205 if (symidx >= map->l_mach.fptr_table_len)
206 _dl_signal_error (0, NULL, NULL,
207 "internal error: symidx out of range of fptr table");
209 if (!ftab[symidx])
211 /* GOT has already been relocated in elf_get_dynamic_info -
212 don't try to relocate it again. */
213 ftab[symidx] = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr);
214 #if 0
216 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
217 struct local *l;
219 asm ("addl %0 = @gprel (local), gp" : "=r" (l));
220 if (l->root != &l->boot_table || l->boot_table.first_unused > 20)
221 _dl_debug_printf ("created fdesc symbol `%s' at %lx\n",
222 strtab + sym->st_name, ftab[symidx]);
224 #endif
227 return ftab[symidx];
230 void
231 _dl_unmap (struct link_map *map)
233 Elf64_Addr *ftab = map->l_mach.fptr_table;
234 struct ia64_fdesc *head = NULL, *tail = NULL;
235 size_t i;
237 __munmap ((void *) map->l_map_start, map->l_map_end - map->l_map_start);
239 if (!ftab)
240 return;
242 /* String together the fdesc structures that are being freed. */
243 for (i = 0; i < map->l_mach.fptr_table_len; ++i)
245 if (ftab[i])
247 *(struct ia64_fdesc **) ftab[i] = head;
248 head = (struct ia64_fdesc *) ftab[i];
249 if (!tail)
250 tail = head;
254 /* Prepend the new list to the free_list: */
255 if (tail)
257 lock (&local);
259 *(struct ia64_fdesc **) tail = local.free_list;
260 local.free_list = head;
262 unlock (&local);
265 __munmap (ftab,
266 map->l_mach.fptr_table_len * sizeof (map->l_mach.fptr_table[0]));
267 map->l_mach.fptr_table = NULL;
270 Elf64_Addr
271 _dl_lookup_address (const void *address)
273 Elf64_Addr addr = (Elf64_Addr) address;
274 struct ia64_fdesc_table *t;
275 unsigned long int i;
277 for (t = local.root; t != NULL; t = t->next)
279 i = (struct ia64_fdesc *) addr - &t->fdesc[0];
280 if (i < t->first_unused && addr == (Elf64_Addr) &t->fdesc[i])
282 addr = t->fdesc[i].ip;
283 break;
286 return addr;