1/* Handle loading and unloading shared objects for internal libc purposes.
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Zack Weinberg <zack@rabi.columbia.edu>, 1999.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20#include <dlfcn.h>
21#include <stdlib.h>
22#include <ldsodefs.h>
23#include <dl-hash.h>
24
25extern int __libc_argc attribute_hidden;
26extern char **__libc_argv attribute_hidden;
27
28extern char **__environ;
29
30/* The purpose of this file is to provide wrappers around the dynamic
31 linker error mechanism (similar to dlopen() et al in libdl) which
32 are usable from within libc. Generally we want to throw away the
33 string that dlerror() would return and just pass back a null pointer
34 for errors. This also lets the rest of libc not know about the error
35 handling mechanism.
36
37 Much of this code came from gconv_dl.c with slight modifications. */
38
39static int
40dlerror_run (void (*operate) (void *), void *args)
41{
42 const char *objname;
43 const char *last_errstring = NULL;
44 bool malloced;
45
46 int result = (GLRO (dl_catch_error) (&objname, &last_errstring, &malloced,
47 operate, args)
48 ?: last_errstring != NULL);
49
50 if (result && malloced)
51 GLRO (dl_error_free) ((char *) last_errstring);
52
53 return result;
54}
55
56/* These functions are called by dlerror_run... */
57
58struct do_dlopen_args
59{
60 /* Argument to do_dlopen. */
61 const char *name;
62 /* Opening mode. */
63 int mode;
64 /* This is the caller of the dlopen() function. */
65 const void *caller_dlopen;
66
67 /* Return from do_dlopen. */
68 struct link_map *map;
69};
70
71struct do_dlsym_args
72{
73 /* Arguments to do_dlsym. */
74 struct link_map *map;
75 const char *name;
76
77 /* Return values of do_dlsym. */
78 lookup_t loadbase;
79 const ElfW(Sym) *ref;
80};
81
82struct do_dlvsym_args
83{
84 /* dlvsym is like dlsym. */
85 struct do_dlsym_args dlsym;
86
87 /* But dlvsym needs a version as well. */
88 struct r_found_version version;
89};
90
91static void
92do_dlopen (void *ptr)
93{
94 struct do_dlopen_args *args = (struct do_dlopen_args *) ptr;
95 /* Open and relocate the shared object. */
96 args->map = GLRO(dl_open) (args->name, args->mode, args->caller_dlopen,
97 __LM_ID_CALLER, __libc_argc, __libc_argv,
98 __environ);
99}
100
101static void
102do_dlsym (void *ptr)
103{
104 struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
105 args->ref = NULL;
106 args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
107 args->map->l_local_scope, NULL, 0,
108 DL_LOOKUP_RETURN_NEWEST, NULL);
109}
110
111static void
112do_dlvsym (void *ptr)
113{
114 struct do_dlvsym_args *args = ptr;
115 args->dlsym.ref = NULL;
116 args->dlsym.loadbase
117 = GLRO(dl_lookup_symbol_x) (args->dlsym.name, args->dlsym.map,
118 &args->dlsym.ref,
119 args->dlsym.map->l_local_scope,
120 &args->version, 0, 0, NULL);
121}
122
123static void
124do_dlclose (void *ptr)
125{
126 GLRO(dl_close) ((struct link_map *) ptr);
127}
128
129#ifndef SHARED
130static void
131do_dlsym_private (void *ptr)
132{
133 lookup_t l;
134 struct r_found_version vers;
135 vers.name = "GLIBC_PRIVATE";
136 vers.hidden = 1;
137 /* vers.hash = _dl_elf_hash (vers.name); */
138 vers.hash = 0x0963cf85;
139 vers.filename = NULL;
140
141 struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
142 args->ref = NULL;
143 l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
144 args->map->l_scope, &vers, 0, 0, NULL);
145 args->loadbase = l;
146}
147#endif
148
149/* ... and these functions call dlerror_run. */
150
151void *
152__libc_dlopen_mode (const char *name, int mode)
153{
154 struct do_dlopen_args args;
155 args.name = name;
156 args.mode = mode;
157 args.caller_dlopen = RETURN_ADDRESS (0);
158
159#ifdef SHARED
160 if (!rtld_active ())
161 return GLRO (dl_dlfcn_hook)->libc_dlopen_mode (name, mode);
162#endif
163 return dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map;
164}
165
166#ifndef SHARED
167void *
168__libc_dlsym_private (struct link_map *map, const char *name)
169{
170 struct do_dlsym_args sargs;
171 sargs.map = map;
172 sargs.name = name;
173
174 if (! dlerror_run (do_dlsym_private, &sargs))
175 return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref);
176 return NULL;
177}
178#endif
179
180void *
181__libc_dlsym (void *map, const char *name)
182{
183 struct do_dlsym_args args;
184 args.map = map;
185 args.name = name;
186
187#ifdef SHARED
188 if (!rtld_active ())
189 return GLRO (dl_dlfcn_hook)->libc_dlsym (map, name);
190#endif
191 return (dlerror_run (do_dlsym, &args) ? NULL
192 : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref)));
193}
194
195/* Replacement for dlvsym. MAP must be a real map. This function
196 returns NULL without setting the dlerror value in case of static
197 dlopen from an old binary. */
198void *
199__libc_dlvsym (void *map, const char *name, const char *version)
200{
201#ifdef SHARED
202 if (!rtld_active ())
203 return GLRO (dl_dlfcn_hook)->libc_dlvsym (map, name, version);
204#endif
205
206 struct do_dlvsym_args args;
207 args.dlsym.map = map;
208 args.dlsym.name = name;
209
210 /* See _dl_vsym in dl-sym.c. */
211 args.version.name = version;
212 args.version.hidden = 1;
213 args.version.hash = _dl_elf_hash (version);
214 args.version.filename = NULL;
215
216 return (dlerror_run (do_dlvsym, &args) ? NULL
217 : (void *) (DL_SYMBOL_ADDRESS (args.dlsym.loadbase,
218 args.dlsym.ref)));
219}
220
221int
222__libc_dlclose (void *map)
223{
224#ifdef SHARED
225 if (!rtld_active ())
226 return GLRO (dl_dlfcn_hook)->libc_dlclose (map);
227#endif
228 return dlerror_run (do_dlclose, map);
229}
230
231
232static bool __libc_freeres_fn_section
233free_slotinfo (struct dtv_slotinfo_list **elemp)
234{
235 size_t cnt;
236
237 if (*elemp == NULL)
238 /* Nothing here, all is removed (or there never was anything). */
239 return true;
240
241 if (!free_slotinfo (&(*elemp)->next))
242 /* We cannot free the entry. */
243 return false;
244
245 /* That cleared our next pointer for us. */
246
247 for (cnt = 0; cnt < (*elemp)->len; ++cnt)
248 if ((*elemp)->slotinfo[cnt].map != NULL)
249 /* Still used. */
250 return false;
251
252 /* We can remove the list element. */
253 free (*elemp);
254 *elemp = NULL;
255
256 return true;
257}
258
259
260libc_freeres_fn (free_mem)
261{
262 struct link_map *l;
263 struct r_search_path_elem *d;
264
265 /* Remove all search directories. */
266 d = GL(dl_all_dirs);
267 while (d != GLRO(dl_init_all_dirs))
268 {
269 struct r_search_path_elem *old = d;
270 d = d->next;
271 free (old);
272 }
273
274 for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
275 {
276 for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
277 {
278 struct libname_list *lnp = l->l_libname->next;
279
280 l->l_libname->next = NULL;
281
282 /* Remove all additional names added to the objects. */
283 while (lnp != NULL)
284 {
285 struct libname_list *old = lnp;
286 lnp = lnp->next;
287 if (! old->dont_free)
288 free (old);
289 }
290
291 /* Free the initfini dependency list. */
292 if (l->l_free_initfini)
293 free (l->l_initfini);
294 l->l_initfini = NULL;
295 }
296
297 if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0
298 && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist
299 // XXX Check whether we need NS-specific initial_searchlist
300 == GLRO(dl_initial_searchlist).r_nlist))
301 {
302 /* All object dynamically loaded by the program are unloaded. Free
303 the memory allocated for the global scope variable. */
304 struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list;
305
306 /* Put the old map in. */
307 GL(dl_ns)[ns]._ns_main_searchlist->r_list
308 // XXX Check whether we need NS-specific initial_searchlist
309 = GLRO(dl_initial_searchlist).r_list;
310 /* Signal that the original map is used. */
311 GL(dl_ns)[ns]._ns_global_scope_alloc = 0;
312
313 /* Now free the old map. */
314 free (old);
315 }
316 }
317
318 /* Free the memory allocated for the dtv slotinfo array. We can do
319 this only if all modules which used this memory are unloaded. */
320#ifdef SHARED
321 if (GL(dl_initial_dtv) == NULL)
322 /* There was no initial TLS setup, it was set up later when
323 it used the normal malloc. */
324 free_slotinfo (&GL(dl_tls_dtv_slotinfo_list));
325 else
326#endif
327 /* The first element of the list does not have to be deallocated.
328 It was allocated in the dynamic linker (i.e., with a different
329 malloc), and in the static library it's in .bss space. */
330 free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next);
331
332 void *scope_free_list = GL(dl_scope_free_list);
333 GL(dl_scope_free_list) = NULL;
334 free (scope_free_list);
335}
336