]>
Commit | Line | Data |
---|---|---|
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 |
35 | Elf64_Addr __ia64_boot_fptr_table[IA64_BOOT_FPTR_TABLE_LEN]; |
36 | ||
37 | static 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 | } | |
50 | local = | |
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 |
101 | static struct ia64_fdesc * |
102 | new_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 |
122 | static Elf64_Addr |
123 | make_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 | ||
164 | static inline Elf64_Addr * | |
165 | make_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 | ||
193 | Elf64_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 | ||
231 | void | |
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 | |
271 | Elf64_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 | } |