Back to index

glibc  2.9
tlsdesc.c
Go to the documentation of this file.
00001 /* Manage TLS descriptors.  i386 version.
00002    Copyright (C) 2005, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <link.h>
00021 #include <ldsodefs.h>
00022 #include <elf/dynamic-link.h>
00023 #include <tls.h>
00024 #include <dl-tlsdesc.h>
00025 #include <tlsdeschtab.h>
00026 
00027 /* The following 4 functions take an entry_check_offset argument.
00028    It's computed by the caller as an offset between its entry point
00029    and the call site, such that by adding the built-in return address
00030    that is implicitly passed to the function with this offset, we can
00031    easily obtain the caller's entry point to compare with the entry
00032    point given in the TLS descriptor.  If it's changed, we want to
00033    return immediately.  */
00034 
00035 /* This function is used to lazily resolve TLS_DESC REL relocations
00036    that reference the *ABS* segment in their own link maps.  The
00037    argument is the addend originally stored there.  */
00038 
00039 void
00040 __attribute__ ((regparm (3))) attribute_hidden
00041 _dl_tlsdesc_resolve_abs_plus_addend_fixup (struct tlsdesc volatile *td,
00042                                       struct link_map *l,
00043                                       ptrdiff_t entry_check_offset)
00044 {
00045   ptrdiff_t addend = (ptrdiff_t) td->arg;
00046 
00047   if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
00048                                      - entry_check_offset))
00049     return;
00050 
00051 #ifndef SHARED
00052   CHECK_STATIC_TLS (l, l);
00053 #else
00054   if (!TRY_STATIC_TLS (l, l))
00055     {
00056       td->arg = _dl_make_tlsdesc_dynamic (l, addend);
00057       td->entry = _dl_tlsdesc_dynamic;
00058     }
00059   else
00060 #endif
00061     {
00062       td->arg = (void*) (addend - l->l_tls_offset);
00063       td->entry = _dl_tlsdesc_return;
00064     }
00065 
00066   _dl_tlsdesc_wake_up_held_fixups ();
00067 }
00068 
00069 /* This function is used to lazily resolve TLS_DESC REL relocations
00070    that originally had zero addends.  The argument location, that
00071    originally held the addend, is used to hold a pointer to the
00072    relocation, but it has to be restored before we call the function
00073    that applies relocations.  */
00074 
00075 void
00076 __attribute__ ((regparm (3))) attribute_hidden
00077 _dl_tlsdesc_resolve_rel_fixup (struct tlsdesc volatile *td,
00078                             struct link_map *l,
00079                             ptrdiff_t entry_check_offset)
00080 {
00081   const ElfW(Rel) *reloc = td->arg;
00082 
00083   if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
00084                                      - entry_check_offset))
00085     return;
00086 
00087   /* The code below was borrowed from _dl_fixup(),
00088      except for checking for STB_LOCAL.  */
00089   const ElfW(Sym) *const symtab
00090     = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
00091   const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
00092   const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
00093   lookup_t result;
00094 
00095    /* Look up the target symbol.  If the normal lookup rules are not
00096       used don't look in the global scope.  */
00097   if (ELFW(ST_BIND) (sym->st_info) != STB_LOCAL
00098       && __builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
00099     {
00100       const struct r_found_version *version = NULL;
00101 
00102       if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
00103        {
00104          const ElfW(Half) *vernum =
00105            (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
00106          ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
00107          version = &l->l_versions[ndx];
00108          if (version->hash == 0)
00109            version = NULL;
00110        }
00111 
00112       result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
00113                                 l->l_scope, version, ELF_RTYPE_CLASS_PLT,
00114                                 DL_LOOKUP_ADD_DEPENDENCY, NULL);
00115     }
00116   else
00117     {
00118       /* We already found the symbol.  The module (and therefore its load
00119         address) is also known.  */
00120       result = l;
00121     }
00122 
00123   if (!sym)
00124     {
00125       td->arg = 0;
00126       td->entry = _dl_tlsdesc_undefweak;
00127     }
00128   else
00129     {
00130 #  ifndef SHARED
00131       CHECK_STATIC_TLS (l, result);
00132 #  else
00133       if (!TRY_STATIC_TLS (l, result))
00134        {
00135          td->arg = _dl_make_tlsdesc_dynamic (result, sym->st_value);
00136          td->entry = _dl_tlsdesc_dynamic;
00137        }
00138       else
00139 #  endif
00140        {
00141          td->arg = (void*)(sym->st_value - result->l_tls_offset);
00142          td->entry = _dl_tlsdesc_return;
00143        }
00144     }
00145 
00146   _dl_tlsdesc_wake_up_held_fixups ();
00147 }
00148 
00149 /* This function is used to lazily resolve TLS_DESC RELA relocations.
00150    The argument location is used to hold a pointer to the relocation.  */
00151 
00152 void
00153 __attribute__ ((regparm (3))) attribute_hidden
00154 _dl_tlsdesc_resolve_rela_fixup (struct tlsdesc volatile *td,
00155                             struct link_map *l,
00156                             ptrdiff_t entry_check_offset)
00157 {
00158   const ElfW(Rela) *reloc = td->arg;
00159 
00160   if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
00161                                      - entry_check_offset))
00162     return;
00163 
00164   /* The code below was borrowed from _dl_fixup(),
00165      except for checking for STB_LOCAL.  */
00166   const ElfW(Sym) *const symtab
00167     = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
00168   const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
00169   const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
00170   lookup_t result;
00171 
00172    /* Look up the target symbol.  If the normal lookup rules are not
00173       used don't look in the global scope.  */
00174   if (ELFW(ST_BIND) (sym->st_info) != STB_LOCAL
00175       && __builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
00176     {
00177       const struct r_found_version *version = NULL;
00178 
00179       if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
00180        {
00181          const ElfW(Half) *vernum =
00182            (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
00183          ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
00184          version = &l->l_versions[ndx];
00185          if (version->hash == 0)
00186            version = NULL;
00187        }
00188 
00189       result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
00190                                 l->l_scope, version, ELF_RTYPE_CLASS_PLT,
00191                                 DL_LOOKUP_ADD_DEPENDENCY, NULL);
00192     }
00193   else
00194     {
00195       /* We already found the symbol.  The module (and therefore its load
00196         address) is also known.  */
00197       result = l;
00198     }
00199 
00200   if (!sym)
00201     {
00202       td->arg = (void*) reloc->r_addend;
00203       td->entry = _dl_tlsdesc_undefweak;
00204     }
00205   else
00206     {
00207 #  ifndef SHARED
00208       CHECK_STATIC_TLS (l, result);
00209 #  else
00210       if (!TRY_STATIC_TLS (l, result))
00211        {
00212          td->arg = _dl_make_tlsdesc_dynamic (result, sym->st_value
00213                                          + reloc->r_addend);
00214          td->entry = _dl_tlsdesc_dynamic;
00215        }
00216       else
00217 #  endif
00218        {
00219          td->arg = (void*) (sym->st_value - result->l_tls_offset
00220                           + reloc->r_addend);
00221          td->entry = _dl_tlsdesc_return;
00222        }
00223     }
00224 
00225   _dl_tlsdesc_wake_up_held_fixups ();
00226 }
00227 
00228 /* This function is used to avoid busy waiting for other threads to
00229    complete the lazy relocation.  Once another thread wins the race to
00230    relocate a TLS descriptor, it sets the descriptor up such that this
00231    function is called to wait until the resolver releases the
00232    lock.  */
00233 
00234 void
00235 __attribute__ ((regparm (3))) attribute_hidden
00236 _dl_tlsdesc_resolve_hold_fixup (struct tlsdesc volatile *td,
00237                             struct link_map *l __attribute__((__unused__)),
00238                             ptrdiff_t entry_check_offset)
00239 {
00240   /* Maybe we're lucky and can return early.  */
00241   if (__builtin_return_address (0) - entry_check_offset != td->entry)
00242     return;
00243 
00244   /* Locking here will stop execution until the running resolver runs
00245      _dl_tlsdesc_wake_up_held_fixups(), releasing the lock.
00246 
00247      FIXME: We'd be better off waiting on a condition variable, such
00248      that we didn't have to hold the lock throughout the relocation
00249      processing.  */
00250   __rtld_lock_lock_recursive (GL(dl_load_lock));
00251   __rtld_lock_unlock_recursive (GL(dl_load_lock));
00252 }
00253 
00254 
00255 /* Unmap the dynamic object, but also release its TLS descriptor table
00256    if there is one.  */
00257 
00258 void
00259 internal_function
00260 _dl_unmap (struct link_map *map)
00261 {
00262   __munmap ((void *) (map)->l_map_start,
00263            (map)->l_map_end - (map)->l_map_start);
00264 
00265 #if SHARED
00266   if (map->l_mach.tlsdesc_table)
00267     htab_delete (map->l_mach.tlsdesc_table);
00268 #endif
00269 }