[BZ #3313]
[glibc.git] / elf / dl-sym.c
blob34d75a1a67081400602e8440725190ef57b0195c
1 /* Look up a symbol in a shared object loaded by `dlopen'.
2 Copyright (C) 1999,2000,2001,2002,2004,2006 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, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <assert.h>
21 #include <stddef.h>
22 #include <setjmp.h>
23 #include <libintl.h>
25 #include <dlfcn.h>
26 #include <ldsodefs.h>
27 #include <dl-hash.h>
28 #include <sysdep-cancel.h>
29 #ifdef USE_TLS
30 # include <dl-tls.h>
31 #endif
34 #if defined USE_TLS && defined SHARED
35 /* Systems which do not have tls_index also probably have to define
36 DONT_USE_TLS_INDEX. */
38 # ifndef __TLS_GET_ADDR
39 # define __TLS_GET_ADDR __tls_get_addr
40 # endif
42 /* Return the symbol address given the map of the module it is in and
43 the symbol record. This is used in dl-sym.c. */
44 static void *
45 internal_function
46 _dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
48 # ifndef DONT_USE_TLS_INDEX
49 tls_index tmp =
51 .ti_module = map->l_tls_modid,
52 .ti_offset = ref->st_value
55 return __TLS_GET_ADDR (&tmp);
56 # else
57 return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
58 # endif
60 #endif
63 struct call_dl_lookup_args
65 /* Arguments to do_dlsym. */
66 struct link_map *map;
67 const char *name;
68 struct r_scope_elem **scope;
69 struct r_found_version *vers;
70 int flags;
72 /* Return values of do_dlsym. */
73 lookup_t loadbase;
74 const ElfW(Sym) **refp;
77 static void
78 call_dl_lookup (void *ptr)
80 struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr;
81 args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp,
82 args->scope, args->vers, 0,
83 args->flags, NULL);
87 static void *
88 internal_function
89 do_sym (void *handle, const char *name, void *who,
90 struct r_found_version *vers, int flags)
92 const ElfW(Sym) *ref = NULL;
93 lookup_t result;
94 ElfW(Addr) caller = (ElfW(Addr)) who;
96 /* If the address is not recognized the call comes from the main
97 program (we hope). */
98 struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
100 /* Find the highest-addressed object that CALLER is not below. */
101 for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
102 for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
103 l = l->l_next)
104 if (caller >= l->l_map_start && caller < l->l_map_end)
106 /* There must be exactly one DSO for the range of the virtual
107 memory. Otherwise something is really broken. */
108 match = l;
109 break;
112 if (handle == RTLD_DEFAULT)
114 /* Search the global scope. We have the simple case where
115 we look up in the scope of an object which was part of
116 the initial binary. And then the more complex part
117 where the object is dynamically loaded and the scope
118 array can change. */
119 if (match->l_type != lt_loaded || SINGLE_THREAD_P)
120 result = GLRO(dl_lookup_symbol_x) (name, match, &ref,
121 match->l_scoperec->scope, vers, 0,
122 flags | DL_LOOKUP_ADD_DEPENDENCY,
123 NULL);
124 else
126 __rtld_mrlock_lock (match->l_scoperec_lock);
127 struct r_scoperec *scoperec = match->l_scoperec;
128 atomic_increment (&scoperec->nusers);
129 __rtld_mrlock_unlock (match->l_scoperec_lock);
131 struct call_dl_lookup_args args;
132 args.name = name;
133 args.map = match;
134 args.scope = scoperec->scope;
135 args.vers = vers;
136 args.flags = flags | DL_LOOKUP_ADD_DEPENDENCY;
137 args.refp = &ref;
139 const char *objname;
140 const char *errstring = NULL;
141 bool malloced;
142 int err = GLRO(dl_catch_error) (&objname, &errstring, &malloced,
143 call_dl_lookup, &args);
145 if (atomic_decrement_val (&scoperec->nusers) == 0
146 && __builtin_expect (scoperec->remove_after_use, 0))
148 if (scoperec->notify)
149 __rtld_notify (scoperec->nusers);
150 else
151 free (scoperec);
154 if (__builtin_expect (errstring != NULL, 0))
156 /* The lookup was unsuccessful. Rethrow the error. */
157 char *errstring_dup = strdupa (errstring);
158 char *objname_dup = strdupa (objname);
159 if (malloced)
160 free ((char *) errstring);
162 GLRO(dl_signal_error) (err, objname_dup, NULL, errstring_dup);
163 /* NOTREACHED */
166 result = args.map;
169 else if (handle == RTLD_NEXT)
171 if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0))
173 if (match == NULL
174 || caller < match->l_map_start
175 || caller >= match->l_map_end)
176 GLRO(dl_signal_error) (0, NULL, NULL, N_("\
177 RTLD_NEXT used in code not dynamically loaded"));
180 struct link_map *l = match;
181 while (l->l_loader != NULL)
182 l = l->l_loader;
184 result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
185 vers, 0, 0, match);
187 else
189 /* Search the scope of the given object. */
190 struct link_map *map = handle;
191 result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
192 vers, 0, flags, NULL);
195 if (ref != NULL)
197 void *value;
199 #if defined USE_TLS && defined SHARED
200 if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
201 /* The found symbol is a thread-local storage variable.
202 Return the address for to the current thread. */
203 value = _dl_tls_symaddr (result, ref);
204 else
205 #endif
206 value = DL_SYMBOL_ADDRESS (result, ref);
208 #ifdef SHARED
209 /* Auditing checkpoint: we have a new binding. Provide the
210 auditing libraries the possibility to change the value and
211 tell us whether further auditing is wanted. */
212 if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
214 const char *strtab = (const char *) D_PTR (result,
215 l_info[DT_STRTAB]);
216 /* Compute index of the symbol entry in the symbol table of
217 the DSO with the definition. */
218 unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
219 l_info[DT_SYMTAB]));
221 if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
223 unsigned int altvalue = 0;
224 struct audit_ifaces *afct = GLRO(dl_audit);
225 /* Synthesize a symbol record where the st_value field is
226 the result. */
227 ElfW(Sym) sym = *ref;
228 sym.st_value = (ElfW(Addr)) value;
230 for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
232 if (afct->symbind != NULL
233 && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM)
234 != 0
235 || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO)
236 != 0)))
238 unsigned int flags = altvalue | LA_SYMB_DLSYM;
239 uintptr_t new_value
240 = afct->symbind (&sym, ndx,
241 &match->l_audit[cnt].cookie,
242 &result->l_audit[cnt].cookie,
243 &flags, strtab + ref->st_name);
244 if (new_value != (uintptr_t) sym.st_value)
246 altvalue = LA_SYMB_ALTVALUE;
247 sym.st_value = new_value;
251 afct = afct->next;
254 value = (void *) sym.st_value;
257 #endif
259 return value;
262 return NULL;
266 void *
267 internal_function
268 _dl_vsym (void *handle, const char *name, const char *version, void *who)
270 struct r_found_version vers;
272 /* Compute hash value to the version string. */
273 vers.name = version;
274 vers.hidden = 1;
275 vers.hash = _dl_elf_hash (version);
276 /* We don't have a specific file where the symbol can be found. */
277 vers.filename = NULL;
279 return do_sym (handle, name, who, &vers, 0);
283 void *
284 internal_function
285 _dl_sym (void *handle, const char *name, void *who)
287 return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);