rtld: properly handle root directory in load path (bug 30435)
[platform/upstream/glibc.git] / elf / dl-libc.c
1 /* Handle loading and unloading shared objects for internal libc purposes.
2    Copyright (C) 1999-2023 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 <dlfcn.h>
20 #include <stdlib.h>
21 #include <ldsodefs.h>
22 #include <dl-hash.h>
23
24 extern int __libc_argc attribute_hidden;
25 extern char **__libc_argv attribute_hidden;
26
27 extern char **__environ;
28
29 /* The purpose of this file is to provide wrappers around the dynamic
30    linker error mechanism (similar to dlopen() et al in libdl) which
31    are usable from within libc.  Generally we want to throw away the
32    string that dlerror() would return and just pass back a null pointer
33    for errors.  This also lets the rest of libc not know about the error
34    handling mechanism.
35
36    Much of this code came from gconv_dl.c with slight modifications. */
37
38 static int
39 dlerror_run (void (*operate) (void *), void *args)
40 {
41   const char *objname;
42   const char *last_errstring = NULL;
43   bool malloced;
44
45   int result = (GLRO (dl_catch_error) (&objname, &last_errstring, &malloced,
46                                        operate, args)
47                 ?: last_errstring != NULL);
48
49   if (result && malloced)
50     GLRO (dl_error_free) ((char *) last_errstring);
51
52   return result;
53 }
54
55 /* These functions are called by dlerror_run... */
56
57 struct do_dlopen_args
58 {
59   /* Argument to do_dlopen.  */
60   const char *name;
61   /* Opening mode.  */
62   int mode;
63   /* This is the caller of the dlopen() function.  */
64   const void *caller_dlopen;
65
66   /* Return from do_dlopen.  */
67   struct link_map *map;
68 };
69
70 struct do_dlsym_args
71 {
72   /* Arguments to do_dlsym.  */
73   struct link_map *map;
74   const char *name;
75
76   /* Return values of do_dlsym.  */
77   lookup_t loadbase;
78   const ElfW(Sym) *ref;
79 };
80
81 struct do_dlvsym_args
82 {
83   /* dlvsym is like dlsym.  */
84   struct do_dlsym_args dlsym;
85
86   /* But dlvsym needs a version  as well.  */
87   struct r_found_version version;
88 };
89
90 static void
91 do_dlopen (void *ptr)
92 {
93   struct do_dlopen_args *args = (struct do_dlopen_args *) ptr;
94   /* Open and relocate the shared object.  */
95   args->map = GLRO(dl_open) (args->name, args->mode, args->caller_dlopen,
96                              __LM_ID_CALLER, __libc_argc, __libc_argv,
97                              __environ);
98 }
99
100 static void
101 do_dlsym (void *ptr)
102 {
103   struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
104   args->ref = NULL;
105   args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
106                                              args->map->l_local_scope, NULL, 0,
107                                              DL_LOOKUP_RETURN_NEWEST, NULL);
108 }
109
110 static void
111 do_dlvsym (void *ptr)
112 {
113   struct do_dlvsym_args *args = ptr;
114   args->dlsym.ref = NULL;
115   args->dlsym.loadbase
116     = GLRO(dl_lookup_symbol_x) (args->dlsym.name, args->dlsym.map,
117                                 &args->dlsym.ref,
118                                 args->dlsym.map->l_local_scope,
119                                 &args->version, 0, 0, NULL);
120 }
121
122 static void
123 do_dlclose (void *ptr)
124 {
125   GLRO(dl_close) ((struct link_map *) ptr);
126 }
127
128 #ifndef SHARED
129 static void
130 do_dlsym_private (void *ptr)
131 {
132   lookup_t l;
133   struct r_found_version vers;
134   vers.name = "GLIBC_PRIVATE";
135   vers.hidden = 1;
136   /* vers.hash = _dl_elf_hash (vers.name);  */
137   vers.hash = 0x0963cf85;
138   vers.filename = NULL;
139
140   struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
141   args->ref = NULL;
142   l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
143                                 args->map->l_scope, &vers, 0, 0, NULL);
144   args->loadbase = l;
145 }
146 #endif
147
148 /* ... and these functions call dlerror_run. */
149
150 void *
151 __libc_dlopen_mode (const char *name, int mode)
152 {
153   struct do_dlopen_args args;
154   args.name = name;
155   args.mode = mode;
156   args.caller_dlopen = RETURN_ADDRESS (0);
157
158 #ifdef SHARED
159   if (GLRO (dl_dlfcn_hook) != NULL)
160     return GLRO (dl_dlfcn_hook)->libc_dlopen_mode (name, mode);
161 #endif
162   return dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map;
163 }
164
165 #ifndef SHARED
166 void *
167 __libc_dlsym_private (struct link_map *map, const char *name)
168 {
169   struct do_dlsym_args sargs;
170   sargs.map = map;
171   sargs.name = name;
172
173   if (! dlerror_run (do_dlsym_private, &sargs))
174     return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref);
175   return NULL;
176 }
177 #endif
178
179 void *
180 __libc_dlsym (void *map, const char *name)
181 {
182   struct do_dlsym_args args;
183   args.map = map;
184   args.name = name;
185
186 #ifdef SHARED
187   if (GLRO (dl_dlfcn_hook) != NULL)
188     return GLRO (dl_dlfcn_hook)->libc_dlsym (map, name);
189 #endif
190   return (dlerror_run (do_dlsym, &args) ? NULL
191           : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref)));
192 }
193
194 /* Replacement for dlvsym.  MAP must be a real map.  This function
195    returns NULL without setting the dlerror value in case of static
196    dlopen from an old binary.  */
197 void *
198 __libc_dlvsym (void *map, const char *name, const char *version)
199 {
200 #ifdef SHARED
201   if (GLRO (dl_dlfcn_hook) != NULL)
202     return GLRO (dl_dlfcn_hook)->libc_dlvsym (map, name, version);
203 #endif
204
205   struct do_dlvsym_args args;
206   args.dlsym.map = map;
207   args.dlsym.name = name;
208
209   /* See _dl_vsym in dl-sym.c.  */
210   args.version.name = version;
211   args.version.hidden = 1;
212   args.version.hash = _dl_elf_hash (version);
213   args.version.filename = NULL;
214
215   return (dlerror_run (do_dlvsym, &args) ? NULL
216           : (void *) (DL_SYMBOL_ADDRESS (args.dlsym.loadbase,
217                                          args.dlsym.ref)));
218 }
219
220 int
221 __libc_dlclose (void *map)
222 {
223 #ifdef SHARED
224   if (GLRO (dl_dlfcn_hook) != NULL)
225     return GLRO (dl_dlfcn_hook)->libc_dlclose (map);
226 #endif
227   return dlerror_run (do_dlclose, map);
228 }
229
230
231 static bool
232 free_slotinfo (struct dtv_slotinfo_list **elemp)
233 {
234   size_t cnt;
235
236   if (*elemp == NULL)
237     /* Nothing here, all is removed (or there never was anything).  */
238     return true;
239
240   if (!free_slotinfo (&(*elemp)->next))
241     /* We cannot free the entry.  */
242     return false;
243
244   /* That cleared our next pointer for us.  */
245
246   for (cnt = 0; cnt < (*elemp)->len; ++cnt)
247     if ((*elemp)->slotinfo[cnt].map != NULL)
248       /* Still used.  */
249       return false;
250
251   /* We can remove the list element.  */
252   free (*elemp);
253   *elemp = NULL;
254
255   return true;
256 }
257
258
259 void
260 __dl_libc_freemem (void)
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 }