Back to index

glibc  2.9
dl-sym.c
Go to the documentation of this file.
00001 /* Look up a symbol in a shared object loaded by `dlopen'.
00002    Copyright (C) 1999-2002,2004,2006,2007 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 <assert.h>
00021 #include <stddef.h>
00022 #include <setjmp.h>
00023 #include <libintl.h>
00024 
00025 #include <dlfcn.h>
00026 #include <ldsodefs.h>
00027 #include <dl-hash.h>
00028 #include <sysdep-cancel.h>
00029 #include <dl-tls.h>
00030 
00031 
00032 #ifdef SHARED
00033 /* Systems which do not have tls_index also probably have to define
00034    DONT_USE_TLS_INDEX.  */
00035 
00036 # ifndef __TLS_GET_ADDR
00037 #  define __TLS_GET_ADDR __tls_get_addr
00038 # endif
00039 
00040 /* Return the symbol address given the map of the module it is in and
00041    the symbol record.  This is used in dl-sym.c.  */
00042 static void *
00043 internal_function
00044 _dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
00045 {
00046 # ifndef DONT_USE_TLS_INDEX
00047   tls_index tmp =
00048     {
00049       .ti_module = map->l_tls_modid,
00050       .ti_offset = ref->st_value
00051     };
00052 
00053   return __TLS_GET_ADDR (&tmp);
00054 # else
00055   return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
00056 # endif
00057 }
00058 #endif
00059 
00060 
00061 struct call_dl_lookup_args
00062 {
00063   /* Arguments to do_dlsym.  */
00064   struct link_map *map;
00065   const char *name;
00066   struct r_found_version *vers;
00067   int flags;
00068 
00069   /* Return values of do_dlsym.  */
00070   lookup_t loadbase;
00071   const ElfW(Sym) **refp;
00072 };
00073 
00074 static void
00075 call_dl_lookup (void *ptr)
00076 {
00077   struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr;
00078   args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp,
00079                                    args->map->l_scope, args->vers, 0,
00080                                    args->flags, NULL);
00081 }
00082 
00083 
00084 static void *
00085 internal_function
00086 do_sym (void *handle, const char *name, void *who,
00087        struct r_found_version *vers, int flags)
00088 {
00089   const ElfW(Sym) *ref = NULL;
00090   lookup_t result;
00091   ElfW(Addr) caller = (ElfW(Addr)) who;
00092 
00093   /* If the address is not recognized the call comes from the main
00094      program (we hope).  */
00095   struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
00096 
00097   /* Find the highest-addressed object that CALLER is not below.  */
00098   for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
00099     for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
00100         l = l->l_next)
00101       if (caller >= l->l_map_start && caller < l->l_map_end
00102          && (l->l_contiguous || _dl_addr_inside_object (l, caller)))
00103        {
00104          match = l;
00105          break;
00106        }
00107 
00108   if (handle == RTLD_DEFAULT)
00109     {
00110       /* Search the global scope.  We have the simple case where
00111         we look up in the scope of an object which was part of
00112         the initial binary.  And then the more complex part
00113         where the object is dynamically loaded and the scope
00114         array can change.  */
00115       if (RTLD_SINGLE_THREAD_P)
00116        result = GLRO(dl_lookup_symbol_x) (name, match, &ref,
00117                                       match->l_scope, vers, 0,
00118                                       flags | DL_LOOKUP_ADD_DEPENDENCY,
00119                                       NULL);
00120       else
00121        {
00122          struct call_dl_lookup_args args;
00123          args.name = name;
00124          args.map = match;
00125          args.vers = vers;
00126          args.flags
00127            = flags | DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK;
00128          args.refp = &ref;
00129 
00130          THREAD_GSCOPE_SET_FLAG ();
00131 
00132          const char *objname;
00133          const char *errstring = NULL;
00134          bool malloced;
00135          int err = GLRO(dl_catch_error) (&objname, &errstring, &malloced,
00136                                      call_dl_lookup, &args);
00137 
00138          THREAD_GSCOPE_RESET_FLAG ();
00139 
00140          if (__builtin_expect (errstring != NULL, 0))
00141            {
00142              /* The lookup was unsuccessful.  Rethrow the error.  */
00143              char *errstring_dup = strdupa (errstring);
00144              char *objname_dup = strdupa (objname);
00145              if (malloced)
00146               free ((char *) errstring);
00147 
00148              GLRO(dl_signal_error) (err, objname_dup, NULL, errstring_dup);
00149              /* NOTREACHED */
00150            }
00151 
00152          result = args.map;
00153        }
00154     }
00155   else if (handle == RTLD_NEXT)
00156     {
00157       if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0))
00158        {
00159          if (match == NULL
00160              || caller < match->l_map_start
00161              || caller >= match->l_map_end)
00162            GLRO(dl_signal_error) (0, NULL, NULL, N_("\
00163 RTLD_NEXT used in code not dynamically loaded"));
00164        }
00165 
00166       struct link_map *l = match;
00167       while (l->l_loader != NULL)
00168        l = l->l_loader;
00169 
00170       result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
00171                                     vers, 0, 0, match);
00172     }
00173   else
00174     {
00175       /* Search the scope of the given object.  */
00176       struct link_map *map = handle;
00177       result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
00178                                     vers, 0, flags, NULL);
00179     }
00180 
00181   if (ref != NULL)
00182     {
00183       void *value;
00184 
00185 #ifdef SHARED
00186       if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
00187        /* The found symbol is a thread-local storage variable.
00188           Return the address for to the current thread.  */
00189        value = _dl_tls_symaddr (result, ref);
00190       else
00191 #endif
00192        value = DL_SYMBOL_ADDRESS (result, ref);
00193 
00194 #ifdef SHARED
00195       /* Auditing checkpoint: we have a new binding.  Provide the
00196         auditing libraries the possibility to change the value and
00197         tell us whether further auditing is wanted.  */
00198       if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
00199        {
00200          const char *strtab = (const char *) D_PTR (result,
00201                                                l_info[DT_STRTAB]);
00202          /* Compute index of the symbol entry in the symbol table of
00203             the DSO with the definition.  */
00204          unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
00205                                                   l_info[DT_SYMTAB]));
00206 
00207          if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
00208            {
00209              unsigned int altvalue = 0;
00210              struct audit_ifaces *afct = GLRO(dl_audit);
00211              /* Synthesize a symbol record where the st_value field is
00212                the result.  */
00213              ElfW(Sym) sym = *ref;
00214              sym.st_value = (ElfW(Addr)) value;
00215 
00216              for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
00217               {
00218                 if (afct->symbind != NULL
00219                     && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM)
00220                        != 0
00221                        || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO)
00222                            != 0)))
00223                   {
00224                     unsigned int flags = altvalue | LA_SYMB_DLSYM;
00225                     uintptr_t new_value
00226                      = afct->symbind (&sym, ndx,
00227                                     &match->l_audit[cnt].cookie,
00228                                     &result->l_audit[cnt].cookie,
00229                                     &flags, strtab + ref->st_name);
00230                     if (new_value != (uintptr_t) sym.st_value)
00231                      {
00232                        altvalue = LA_SYMB_ALTVALUE;
00233                        sym.st_value = new_value;
00234                      }
00235                   }
00236 
00237                 afct = afct->next;
00238               }
00239 
00240              value = (void *) sym.st_value;
00241            }
00242        }
00243 #endif
00244 
00245       return value;
00246     }
00247 
00248   return NULL;
00249 }
00250 
00251 
00252 void *
00253 internal_function
00254 _dl_vsym (void *handle, const char *name, const char *version, void *who)
00255 {
00256   struct r_found_version vers;
00257 
00258   /* Compute hash value to the version string.  */
00259   vers.name = version;
00260   vers.hidden = 1;
00261   vers.hash = _dl_elf_hash (version);
00262   /* We don't have a specific file where the symbol can be found.  */
00263   vers.filename = NULL;
00264 
00265   return do_sym (handle, name, who, &vers, 0);
00266 }
00267 
00268 
00269 void *
00270 internal_function
00271 _dl_sym (void *handle, const char *name, void *who)
00272 {
00273   return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);
00274 }