alpha: Purge __ASSUME_STAT64_SYSCALL
[glibc.git] / sysdeps / i386 / tlsdesc.c
blob2d55c63cdb976d79577ea35a739f1b109f8bdb8c
1 /* Manage TLS descriptors. i386 version.
2 Copyright (C) 2005, 2008 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, see
17 <http://www.gnu.org/licenses/>. */
19 #include <link.h>
20 #include <ldsodefs.h>
21 #include <elf/dynamic-link.h>
22 #include <tls.h>
23 #include <dl-tlsdesc.h>
24 #include <tlsdeschtab.h>
26 /* The following 4 functions take an entry_check_offset argument.
27 It's computed by the caller as an offset between its entry point
28 and the call site, such that by adding the built-in return address
29 that is implicitly passed to the function with this offset, we can
30 easily obtain the caller's entry point to compare with the entry
31 point given in the TLS descriptor. If it's changed, we want to
32 return immediately. */
34 /* This function is used to lazily resolve TLS_DESC REL relocations
35 that reference the *ABS* segment in their own link maps. The
36 argument is the addend originally stored there. */
38 void
39 __attribute__ ((regparm (3))) attribute_hidden
40 _dl_tlsdesc_resolve_abs_plus_addend_fixup (struct tlsdesc volatile *td,
41 struct link_map *l,
42 ptrdiff_t entry_check_offset)
44 ptrdiff_t addend = (ptrdiff_t) td->arg;
46 if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
47 - entry_check_offset))
48 return;
50 #ifndef SHARED
51 CHECK_STATIC_TLS (l, l);
52 #else
53 if (!TRY_STATIC_TLS (l, l))
55 td->arg = _dl_make_tlsdesc_dynamic (l, addend);
56 td->entry = _dl_tlsdesc_dynamic;
58 else
59 #endif
61 td->arg = (void*) (addend - l->l_tls_offset);
62 td->entry = _dl_tlsdesc_return;
65 _dl_tlsdesc_wake_up_held_fixups ();
68 /* This function is used to lazily resolve TLS_DESC REL relocations
69 that originally had zero addends. The argument location, that
70 originally held the addend, is used to hold a pointer to the
71 relocation, but it has to be restored before we call the function
72 that applies relocations. */
74 void
75 __attribute__ ((regparm (3))) attribute_hidden
76 _dl_tlsdesc_resolve_rel_fixup (struct tlsdesc volatile *td,
77 struct link_map *l,
78 ptrdiff_t entry_check_offset)
80 const ElfW(Rel) *reloc = td->arg;
82 if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
83 - entry_check_offset))
84 return;
86 /* The code below was borrowed from _dl_fixup(),
87 except for checking for STB_LOCAL. */
88 const ElfW(Sym) *const symtab
89 = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
90 const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
91 const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
92 lookup_t result;
94 /* Look up the target symbol. If the normal lookup rules are not
95 used don't look in the global scope. */
96 if (ELFW(ST_BIND) (sym->st_info) != STB_LOCAL
97 && __builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
99 const struct r_found_version *version = NULL;
101 if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
103 const ElfW(Half) *vernum =
104 (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
105 ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
106 version = &l->l_versions[ndx];
107 if (version->hash == 0)
108 version = NULL;
111 result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
112 l->l_scope, version, ELF_RTYPE_CLASS_PLT,
113 DL_LOOKUP_ADD_DEPENDENCY, NULL);
115 else
117 /* We already found the symbol. The module (and therefore its load
118 address) is also known. */
119 result = l;
122 if (!sym)
124 td->arg = 0;
125 td->entry = _dl_tlsdesc_undefweak;
127 else
129 # ifndef SHARED
130 CHECK_STATIC_TLS (l, result);
131 # else
132 if (!TRY_STATIC_TLS (l, result))
134 td->arg = _dl_make_tlsdesc_dynamic (result, sym->st_value);
135 td->entry = _dl_tlsdesc_dynamic;
137 else
138 # endif
140 td->arg = (void*)(sym->st_value - result->l_tls_offset);
141 td->entry = _dl_tlsdesc_return;
145 _dl_tlsdesc_wake_up_held_fixups ();
148 /* This function is used to lazily resolve TLS_DESC RELA relocations.
149 The argument location is used to hold a pointer to the relocation. */
151 void
152 __attribute__ ((regparm (3))) attribute_hidden
153 _dl_tlsdesc_resolve_rela_fixup (struct tlsdesc volatile *td,
154 struct link_map *l,
155 ptrdiff_t entry_check_offset)
157 const ElfW(Rela) *reloc = td->arg;
159 if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
160 - entry_check_offset))
161 return;
163 /* The code below was borrowed from _dl_fixup(),
164 except for checking for STB_LOCAL. */
165 const ElfW(Sym) *const symtab
166 = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
167 const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
168 const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
169 lookup_t result;
171 /* Look up the target symbol. If the normal lookup rules are not
172 used don't look in the global scope. */
173 if (ELFW(ST_BIND) (sym->st_info) != STB_LOCAL
174 && __builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
176 const struct r_found_version *version = NULL;
178 if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
180 const ElfW(Half) *vernum =
181 (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
182 ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
183 version = &l->l_versions[ndx];
184 if (version->hash == 0)
185 version = NULL;
188 result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
189 l->l_scope, version, ELF_RTYPE_CLASS_PLT,
190 DL_LOOKUP_ADD_DEPENDENCY, NULL);
192 else
194 /* We already found the symbol. The module (and therefore its load
195 address) is also known. */
196 result = l;
199 if (!sym)
201 td->arg = (void*) reloc->r_addend;
202 td->entry = _dl_tlsdesc_undefweak;
204 else
206 # ifndef SHARED
207 CHECK_STATIC_TLS (l, result);
208 # else
209 if (!TRY_STATIC_TLS (l, result))
211 td->arg = _dl_make_tlsdesc_dynamic (result, sym->st_value
212 + reloc->r_addend);
213 td->entry = _dl_tlsdesc_dynamic;
215 else
216 # endif
218 td->arg = (void*) (sym->st_value - result->l_tls_offset
219 + reloc->r_addend);
220 td->entry = _dl_tlsdesc_return;
224 _dl_tlsdesc_wake_up_held_fixups ();
227 /* This function is used to avoid busy waiting for other threads to
228 complete the lazy relocation. Once another thread wins the race to
229 relocate a TLS descriptor, it sets the descriptor up such that this
230 function is called to wait until the resolver releases the
231 lock. */
233 void
234 __attribute__ ((regparm (3))) attribute_hidden
235 _dl_tlsdesc_resolve_hold_fixup (struct tlsdesc volatile *td,
236 struct link_map *l __attribute__((__unused__)),
237 ptrdiff_t entry_check_offset)
239 /* Maybe we're lucky and can return early. */
240 if (__builtin_return_address (0) - entry_check_offset != td->entry)
241 return;
243 /* Locking here will stop execution until the running resolver runs
244 _dl_tlsdesc_wake_up_held_fixups(), releasing the lock.
246 FIXME: We'd be better off waiting on a condition variable, such
247 that we didn't have to hold the lock throughout the relocation
248 processing. */
249 __rtld_lock_lock_recursive (GL(dl_load_lock));
250 __rtld_lock_unlock_recursive (GL(dl_load_lock));
254 /* Unmap the dynamic object, but also release its TLS descriptor table
255 if there is one. */
257 void
258 internal_function
259 _dl_unmap (struct link_map *map)
261 __munmap ((void *) (map)->l_map_start,
262 (map)->l_map_end - (map)->l_map_start);
264 #if SHARED
265 if (map->l_mach.tlsdesc_table)
266 htab_delete (map->l_mach.tlsdesc_table);
267 #endif