1 /* Look up a symbol in a shared object loaded by `dlopen'.
2    Copyright (C) 1999-2022 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 #include <dl-sym-post.h>
32 
33 
34 #ifdef SHARED
35 /* Systems which do not have tls_index also probably have to define
36    DONT_USE_TLS_INDEX.  */
37 
38 # ifndef __TLS_GET_ADDR
39 #  define __TLS_GET_ADDR __tls_get_addr
40 # endif
41 
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 *
_dl_tls_symaddr(struct link_map * map,const ElfW (Sym)* ref)45 _dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
46 {
47 # ifndef DONT_USE_TLS_INDEX
48   tls_index tmp =
49     {
50       .ti_module = map->l_tls_modid,
51       .ti_offset = ref->st_value
52     };
53 
54   return __TLS_GET_ADDR (&tmp);
55 # else
56   return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
57 # endif
58 }
59 #endif
60 
61 
62 struct call_dl_lookup_args
63 {
64   /* Arguments to do_dlsym.  */
65   struct link_map *map;
66   const char *name;
67   struct r_found_version *vers;
68   int flags;
69 
70   /* Return values of do_dlsym.  */
71   lookup_t loadbase;
72   const ElfW(Sym) **refp;
73 };
74 
75 static void
call_dl_lookup(void * ptr)76 call_dl_lookup (void *ptr)
77 {
78   struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr;
79   args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp,
80 					args->map->l_scope, args->vers, 0,
81 					args->flags, NULL);
82 }
83 
84 static void *
do_sym(void * handle,const char * name,void * who,struct r_found_version * vers,int flags)85 do_sym (void *handle, const char *name, void *who,
86 	struct r_found_version *vers, int flags)
87 {
88   const ElfW(Sym) *ref = NULL;
89   lookup_t result;
90   ElfW(Addr) caller = (ElfW(Addr)) who;
91 
92   /* Link map of the caller if needed.  */
93   struct link_map *match = NULL;
94 
95   if (handle == RTLD_DEFAULT)
96     {
97       match = _dl_sym_find_caller_link_map (caller);
98 
99       /* Search the global scope.  We have the simple case where
100 	 we look up in the scope of an object which was part of
101 	 the initial binary.  And then the more complex part
102 	 where the object is dynamically loaded and the scope
103 	 array can change.  */
104       if (RTLD_SINGLE_THREAD_P)
105 	result = GLRO(dl_lookup_symbol_x) (name, match, &ref,
106 					   match->l_scope, vers, 0,
107 					   flags | DL_LOOKUP_ADD_DEPENDENCY,
108 					   NULL);
109       else
110 	{
111 	  struct call_dl_lookup_args args;
112 	  args.name = name;
113 	  args.map = match;
114 	  args.vers = vers;
115 	  args.flags
116 	    = flags | DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK;
117 	  args.refp = &ref;
118 
119 	  THREAD_GSCOPE_SET_FLAG ();
120 	  struct dl_exception exception;
121 	  int err = _dl_catch_exception (&exception, call_dl_lookup, &args);
122 	  THREAD_GSCOPE_RESET_FLAG ();
123 	  if (__glibc_unlikely (exception.errstring != NULL))
124 	    _dl_signal_exception (err, &exception, NULL);
125 
126 	  result = args.map;
127 	}
128     }
129   else if (handle == RTLD_NEXT)
130     {
131       match = _dl_sym_find_caller_link_map (caller);
132 
133       if (__glibc_unlikely (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded))
134 	{
135 	  if (match == NULL
136 	      || caller < match->l_map_start
137 	      || caller >= match->l_map_end)
138 	    _dl_signal_error (0, NULL, NULL, N_("\
139 RTLD_NEXT used in code not dynamically loaded"));
140 	}
141 
142       struct link_map *l = match;
143       while (l->l_loader != NULL)
144 	l = l->l_loader;
145 
146       result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
147 					 vers, 0, flags, match);
148     }
149   else
150     {
151       /* Search the scope of the given object.  */
152       struct link_map *map = handle;
153       result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
154 					 vers, 0, flags, NULL);
155     }
156 
157   if (ref != NULL)
158     {
159       void *value;
160 
161 #ifdef SHARED
162       if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
163 	/* The found symbol is a thread-local storage variable.
164 	   Return the address for to the current thread.  */
165 	value = _dl_tls_symaddr (result, ref);
166       else
167 #endif
168 	value = DL_SYMBOL_ADDRESS (result, ref);
169 
170       return _dl_sym_post (result, ref, value, caller, match);
171     }
172 
173   return NULL;
174 }
175 
176 
177 void *
_dl_vsym(void * handle,const char * name,const char * version,void * who)178 _dl_vsym (void *handle, const char *name, const char *version, void *who)
179 {
180   struct r_found_version vers;
181 
182   /* Compute hash value to the version string.  */
183   vers.name = version;
184   vers.hidden = 1;
185   vers.hash = _dl_elf_hash (version);
186   /* We don't have a specific file where the symbol can be found.  */
187   vers.filename = NULL;
188 
189   return do_sym (handle, name, who, &vers, 0);
190 }
191 
192 void *
_dl_sym(void * handle,const char * name,void * who)193 _dl_sym (void *handle, const char *name, void *who)
194 {
195   return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);
196 }
197