]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/ia64/dl-fptr.c
Update.
[thirdparty/glibc.git] / sysdeps / ia64 / dl-fptr.c
CommitLineData
370f00c3
UD
1/* Manage function descriptors. IA-64 version.
2 Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
c0282c06
UD
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
c0282c06
UD
9
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
41bdb6e2 13 Lesser General Public License for more details.
c0282c06 14
41bdb6e2
AJ
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. */
c0282c06 19
370f00c3 20#include <ia64intrin.h>
c0282c06
UD
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>
370f00c3
UD
31# include <signal.h>
32# include <time.h>
c0282c06
UD
33#endif
34
370f00c3
UD
35Elf64_Addr __ia64_boot_fptr_table[IA64_BOOT_FPTR_TABLE_LEN];
36
37static struct local
38 {
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;
c0282c06 45#endif
370f00c3
UD
46 /* the next to members MUST be consecutive! */
47 struct ia64_fdesc_table boot_table;
48 struct ia64_fdesc boot_fdescs[1024];
49 }
50local =
51 {
52 root: &local.boot_table,
53 npages: 2,
54 boot_table:
55 {
56 len: sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]),
57 first_unused: 0
58 }
59 };
c0282c06 60
370f00c3
UD
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); \
75 \
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; \
92}
c0282c06 93#else
370f00c3
UD
94# define lock(l)
95# define unlock(l)
c0282c06
UD
96#endif
97
370f00c3
UD
98/* Create a new fdesc table and return a pointer to the first fdesc
99 entry. The fdesc lock must have been acquired already. */
c0282c06 100
370f00c3
UD
101static struct ia64_fdesc *
102new_fdesc_table (struct local *l)
c0282c06 103{
d6b5d570 104 size_t size = l->npages * GL(dl_pagesize);
370f00c3
UD
105 struct ia64_fdesc_table *new_table;
106 struct ia64_fdesc *fdesc;
c0282c06 107
370f00c3
UD
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)
84aafa91 112 _dl_signal_error (errno, NULL, NULL, "cannot map pages for fdesc table");
c0282c06 113
370f00c3
UD
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;
120}
c0282c06 121
370f00c3
UD
122static Elf64_Addr
123make_fdesc (Elf64_Addr ip, Elf64_Addr gp)
124{
125 struct ia64_fdesc *fdesc = NULL;
126 struct ia64_fdesc_table *t;
127 unsigned int old;
128 struct local *l;
129
130 asm ("addl %0 = @gprel (local), gp" : "=r" (l));
131
132 t = l->root;
133 while (1)
c0282c06 134 {
370f00c3
UD
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))
c0282c06 139 {
370f00c3
UD
140 fdesc = &t->fdesc[old];
141 goto install;
c0282c06 142 }
c0282c06
UD
143 }
144
370f00c3
UD
145 lock (l);
146 {
147 if (l->free_list)
148 {
149 fdesc = l->free_list; /* get it from free-list */
150 l->free_list = (struct ia64_fdesc *) fdesc->ip;
151 }
152 else
153 fdesc = new_fdesc_table (l); /* create new fdesc table */
154 }
155 unlock (l);
c0282c06 156
370f00c3
UD
157 install:
158 fdesc->ip = ip;
159 fdesc->gp = gp;
160
161 return (Elf64_Addr) fdesc;
162}
163
164static inline Elf64_Addr *
165make_fptr_table (struct link_map *map)
166{
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;
172
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);
d6b5d570
UD
177 size = ((len * sizeof (fptr_table[0]) + GL(dl_pagesize) - 1)
178 & -GL(dl_pagesize));
370f00c3
UD
179 /* XXX We don't support here in the moment systems without MAP_ANON.
180 There probably are none for IA-64. In case this is proven wrong
181 we will have to open /dev/null here and use the file descriptor
182 instead of the hard-coded -1. */
183 fptr_table = __mmap (NULL, size, PROT_READ | PROT_WRITE,
184 MAP_ANON | MAP_PRIVATE, -1, 0);
185 if (fptr_table == MAP_FAILED)
84aafa91 186 _dl_signal_error (errno, NULL, NULL, "cannot map pages for fptr table");
370f00c3
UD
187
188 map->l_mach.fptr_table_len = len;
189 map->l_mach.fptr_table = fptr_table;
190 return fptr_table;
191}
192
193Elf64_Addr
194__ia64_make_fptr (struct link_map *map, const Elf64_Sym *sym, Elf64_Addr ip)
195{
196 Elf64_Addr *ftab = map->l_mach.fptr_table;
197 const Elf64_Sym *symtab;
198 Elf_Symndx symidx;
199
200 if (__builtin_expect (!map->l_mach.fptr_table, 0))
201 ftab = make_fptr_table (map);
202
203 symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
204 symidx = sym - symtab;
205
206 if (symidx >= map->l_mach.fptr_table_len)
84aafa91 207 _dl_signal_error (0, NULL, NULL,
370f00c3
UD
208 "internal error: symidx out of range of fptr table");
209
210 if (!ftab[symidx])
211 {
212 /* GOT has already been relocated in elf_get_dynamic_info -
213 don't try to relocate it again. */
214 ftab[symidx] = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr);
215#if 0
216 {
217 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
218 struct local *l;
219
220 asm ("addl %0 = @gprel (local), gp" : "=r" (l));
221 if (l->root != &l->boot_table || l->boot_table.first_unused > 20)
222 _dl_debug_printf ("created fdesc symbol `%s' at %lx\n",
223 strtab + sym->st_name, ftab[symidx]);
224 }
c0282c06 225#endif
370f00c3 226 }
c0282c06 227
370f00c3 228 return ftab[symidx];
c0282c06
UD
229}
230
231void
232_dl_unmap (struct link_map *map)
233{
370f00c3
UD
234 Elf64_Addr *ftab = map->l_mach.fptr_table;
235 struct ia64_fdesc *head = NULL, *tail = NULL;
236 size_t i;
c0282c06
UD
237
238 __munmap ((void *) map->l_map_start, map->l_map_end - map->l_map_start);
239
370f00c3
UD
240 if (!ftab)
241 return;
c0282c06 242
370f00c3
UD
243 /* String together the fdesc structures that are being freed. */
244 for (i = 0; i < map->l_mach.fptr_table_len; ++i)
c0282c06 245 {
370f00c3 246 if (ftab[i])
c0282c06 247 {
370f00c3
UD
248 *(struct ia64_fdesc **) ftab[i] = head;
249 head = (struct ia64_fdesc *) ftab[i];
250 if (!tail)
251 tail = head;
c0282c06 252 }
370f00c3 253 }
c0282c06 254
370f00c3
UD
255 /* Prepend the new list to the free_list: */
256 if (tail)
257 {
258 lock (&local);
259 {
260 *(struct ia64_fdesc **) tail = local.free_list;
261 local.free_list = head;
262 }
263 unlock (&local);
c0282c06
UD
264 }
265
370f00c3
UD
266 __munmap (ftab,
267 map->l_mach.fptr_table_len * sizeof (map->l_mach.fptr_table[0]));
268 map->l_mach.fptr_table = NULL;
c0282c06 269}
09bf6406
UD
270
271Elf64_Addr
272_dl_lookup_address (const void *address)
273{
274 Elf64_Addr addr = (Elf64_Addr) address;
370f00c3
UD
275 struct ia64_fdesc_table *t;
276 unsigned long int i;
09bf6406 277
370f00c3
UD
278 for (t = local.root; t != NULL; t = t->next)
279 {
280 i = (struct ia64_fdesc *) addr - &t->fdesc[0];
281 if (i < t->first_unused && addr == (Elf64_Addr) &t->fdesc[i])
282 {
283 addr = t->fdesc[i].ip;
284 break;
285 }
286 }
09bf6406
UD
287 return addr;
288}