1/* Look up a symbol in a shared object loaded by `dlopen'.
2 Copyright (C) 1999-2020 Free Software Foundation, Inc.
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
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.
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
13 Lesser General Public License for more details.
14
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 <https://www.gnu.org/licenses/>. */
18
19#include <assert.h>
20#include <stddef.h>
21#include <setjmp.h>
22#include <stdlib.h>
23#include <libintl.h>
24
25#include <dlfcn.h>
26#include <ldsodefs.h>
27#include <dl-hash.h>
28#include <sysdep-cancel.h>
29#include <dl-tls.h>
30#include <dl-irel.h>
31
32
33#ifdef SHARED
34/* Systems which do not have tls_index also probably have to define
35 DONT_USE_TLS_INDEX. */
36
37# ifndef __TLS_GET_ADDR
38# define __TLS_GET_ADDR __tls_get_addr
39# endif
40
41/* Return the symbol address given the map of the module it is in and
42 the symbol record. This is used in dl-sym.c. */
43static void *
44_dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
45{
46# ifndef DONT_USE_TLS_INDEX
47 tls_index tmp =
48 {
49 .ti_module = map->l_tls_modid,
50 .ti_offset = ref->st_value
51 };
52
53 return __TLS_GET_ADDR (&tmp);
54# else
55 return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
56# endif
57}
58#endif
59
60
61struct call_dl_lookup_args
62{
63 /* Arguments to do_dlsym. */
64 struct link_map *map;
65 const char *name;
66 struct r_found_version *vers;
67 int flags;
68
69 /* Return values of do_dlsym. */
70 lookup_t loadbase;
71 const ElfW(Sym) **refp;
72};
73
74static void
75call_dl_lookup (void *ptr)
76{
77 struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr;
78 args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp,
79 args->map->l_scope, args->vers, 0,
80 args->flags, NULL);
81}
82
83/* Return the link map containing the caller address. */
84static inline struct link_map *
85find_caller_link_map (ElfW(Addr) caller)
86{
87 struct link_map *l = _dl_find_dso_for_object (caller);
88 if (l != NULL)
89 return l;
90 else
91 /* If the address is not recognized the call comes from the main
92 program (we hope). */
93 return GL(dl_ns)[LM_ID_BASE]._ns_loaded;
94}
95
96static void *
97do_sym (void *handle, const char *name, void *who,
98 struct r_found_version *vers, int flags)
99{
100 const ElfW(Sym) *ref = NULL;
101 lookup_t result;
102 ElfW(Addr) caller = (ElfW(Addr)) who;
103
104 /* Link map of the caller if needed. */
105 struct link_map *match = NULL;
106
107 if (handle == RTLD_DEFAULT)
108 {
109 match = find_caller_link_map (caller);
110
111 /* Search the global scope. We have the simple case where
112 we look up in the scope of an object which was part of
113 the initial binary. And then the more complex part
114 where the object is dynamically loaded and the scope
115 array can change. */
116 if (RTLD_SINGLE_THREAD_P)
117 result = GLRO(dl_lookup_symbol_x) (name, match, &ref,
118 match->l_scope, vers, 0,
119 flags | DL_LOOKUP_ADD_DEPENDENCY,
120 NULL);
121 else
122 {
123 struct call_dl_lookup_args args;
124 args.name = name;
125 args.map = match;
126 args.vers = vers;
127 args.flags
128 = flags | DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK;
129 args.refp = &ref;
130
131 THREAD_GSCOPE_SET_FLAG ();
132 struct dl_exception exception;
133 int err = _dl_catch_exception (&exception, call_dl_lookup, &args);
134 THREAD_GSCOPE_RESET_FLAG ();
135 if (__glibc_unlikely (exception.errstring != NULL))
136 _dl_signal_exception (err, &exception, NULL);
137
138 result = args.map;
139 }
140 }
141 else if (handle == RTLD_NEXT)
142 {
143 match = find_caller_link_map (caller);
144
145 if (__glibc_unlikely (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded))
146 {
147 if (match == NULL
148 || caller < match->l_map_start
149 || caller >= match->l_map_end)
150 _dl_signal_error (0, NULL, NULL, N_("\
151RTLD_NEXT used in code not dynamically loaded"));
152 }
153
154 struct link_map *l = match;
155 while (l->l_loader != NULL)
156 l = l->l_loader;
157
158 result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
159 vers, 0, 0, match);
160 }
161 else
162 {
163 /* Search the scope of the given object. */
164 struct link_map *map = handle;
165 result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
166 vers, 0, flags, NULL);
167 }
168
169 if (ref != NULL)
170 {
171 void *value;
172
173#ifdef SHARED
174 if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
175 /* The found symbol is a thread-local storage variable.
176 Return the address for to the current thread. */
177 value = _dl_tls_symaddr (result, ref);
178 else
179#endif
180 value = DL_SYMBOL_ADDRESS (result, ref);
181
182 /* Resolve indirect function address. */
183 if (__glibc_unlikely (ELFW(ST_TYPE) (ref->st_info) == STT_GNU_IFUNC))
184 {
185 DL_FIXUP_VALUE_TYPE fixup
186 = DL_FIXUP_MAKE_VALUE (result, (ElfW(Addr)) value);
187 fixup = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (fixup));
188 value = (void *) DL_FIXUP_VALUE_CODE_ADDR (fixup);
189 }
190
191#ifdef SHARED
192 /* Auditing checkpoint: we have a new binding. Provide the
193 auditing libraries the possibility to change the value and
194 tell us whether further auditing is wanted. */
195 if (__glibc_unlikely (GLRO(dl_naudit) > 0))
196 {
197 const char *strtab = (const char *) D_PTR (result,
198 l_info[DT_STRTAB]);
199 /* Compute index of the symbol entry in the symbol table of
200 the DSO with the definition. */
201 unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
202 l_info[DT_SYMTAB]));
203
204 if (match == NULL)
205 match = find_caller_link_map (caller);
206
207 if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
208 {
209 unsigned int altvalue = 0;
210 struct audit_ifaces *afct = GLRO(dl_audit);
211 /* Synthesize a symbol record where the st_value field is
212 the result. */
213 ElfW(Sym) sym = *ref;
214 sym.st_value = (ElfW(Addr)) value;
215
216 for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
217 {
218 struct auditstate *match_audit
219 = link_map_audit_state (match, cnt);
220 struct auditstate *result_audit
221 = link_map_audit_state (result, cnt);
222 if (afct->symbind != NULL
223 && ((match_audit->bindflags & LA_FLG_BINDFROM) != 0
224 || ((result_audit->bindflags & LA_FLG_BINDTO)
225 != 0)))
226 {
227 unsigned int flags = altvalue | LA_SYMB_DLSYM;
228 uintptr_t new_value
229 = afct->symbind (&sym, ndx,
230 &match_audit->cookie,
231 &result_audit->cookie,
232 &flags, strtab + ref->st_name);
233 if (new_value != (uintptr_t) sym.st_value)
234 {
235 altvalue = LA_SYMB_ALTVALUE;
236 sym.st_value = new_value;
237 }
238 }
239
240 afct = afct->next;
241 }
242
243 value = (void *) sym.st_value;
244 }
245 }
246#endif
247
248 return value;
249 }
250
251 return NULL;
252}
253
254
255void *
256_dl_vsym (void *handle, const char *name, const char *version, void *who)
257{
258 struct r_found_version vers;
259
260 /* Compute hash value to the version string. */
261 vers.name = version;
262 vers.hidden = 1;
263 vers.hash = _dl_elf_hash (version);
264 /* We don't have a specific file where the symbol can be found. */
265 vers.filename = NULL;
266
267 return do_sym (handle, name, who, &vers, 0);
268}
269
270
271void *
272_dl_sym (void *handle, const char *name, void *who)
273{
274 return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);
275}
276