1 | /* loader-dlopen.c -- dynamic linking with dlopen/dlsym |
2 | |
3 | Copyright (C) 1998-2000, 2004, 2006-2008, 2011-2015 Free Software |
4 | Foundation, Inc. |
5 | Written by Thomas Tanner, 1998 |
6 | |
7 | NOTE: The canonical source of this file is maintained with the |
8 | GNU Libtool package. Report bugs to bug-libtool@gnu.org. |
9 | |
10 | GNU Libltdl is free software; you can redistribute it and/or |
11 | modify it under the terms of the GNU Lesser General Public |
12 | License as published by the Free Software Foundation; either |
13 | version 2 of the License, or (at your option) any later version. |
14 | |
15 | As a special exception to the GNU Lesser General Public License, |
16 | if you distribute this file as part of a program or library that |
17 | is built using GNU Libtool, you may include this file under the |
18 | same distribution terms that you use for the rest of that program. |
19 | |
20 | GNU Libltdl is distributed in the hope that it will be useful, |
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | GNU Lesser General Public License for more details. |
24 | |
25 | You should have received a copy of the GNU Lesser General Public |
26 | License along with GNU Libltdl; see the file COPYING.LIB. If not, a |
27 | copy can be downloaded from http://www.gnu.org/licenses/lgpl.html, |
28 | or obtained by writing to the Free Software Foundation, Inc., |
29 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
30 | */ |
31 | |
32 | #include "lt__private.h" |
33 | #include "lt_dlloader.h" |
34 | |
35 | /* Use the preprocessor to rename non-static symbols to avoid namespace |
36 | collisions when the loader code is statically linked into libltdl. |
37 | Use the "<module_name>_LTX_" prefix so that the symbol addresses can |
38 | be fetched from the preloaded symbol list by lt_dlsym(): */ |
39 | #define get_vtable dlopen_LTX_get_vtable |
40 | |
41 | LT_BEGIN_C_DECLS |
42 | LT_SCOPE lt_dlvtable *get_vtable (lt_user_data loader_data); |
43 | LT_END_C_DECLS |
44 | |
45 | |
46 | /* Boilerplate code to set up the vtable for hooking this loader into |
47 | libltdl's loader list: */ |
48 | static int vl_exit (lt_user_data loader_data); |
49 | static lt_module vm_open (lt_user_data loader_data, const char *filename, |
50 | lt_dladvise advise); |
51 | static int vm_close (lt_user_data loader_data, lt_module module); |
52 | static void * vm_sym (lt_user_data loader_data, lt_module module, |
53 | const char *symbolname); |
54 | |
55 | static lt_dlvtable *vtable = 0; |
56 | |
57 | /* Return the vtable for this loader, only the name and sym_prefix |
58 | attributes (plus the virtual function implementations, obviously) |
59 | change between loaders. */ |
60 | lt_dlvtable * |
61 | get_vtable (lt_user_data loader_data) |
62 | { |
63 | if (!vtable) |
64 | { |
65 | vtable = (lt_dlvtable *) lt__zalloc (sizeof *vtable); |
66 | } |
67 | |
68 | if (vtable && !vtable->name) |
69 | { |
70 | vtable->name = "lt_dlopen" ; |
71 | #if defined DLSYM_USCORE |
72 | vtable->sym_prefix = "_" ; |
73 | #endif |
74 | vtable->module_open = vm_open; |
75 | vtable->module_close = vm_close; |
76 | vtable->find_sym = vm_sym; |
77 | vtable->dlloader_exit = vl_exit; |
78 | vtable->dlloader_data = loader_data; |
79 | vtable->priority = LT_DLLOADER_PREPEND; |
80 | } |
81 | |
82 | if (vtable && (vtable->dlloader_data != loader_data)) |
83 | { |
84 | LT__SETERROR (INIT_LOADER); |
85 | return 0; |
86 | } |
87 | |
88 | return vtable; |
89 | } |
90 | |
91 | |
92 | |
93 | /* --- IMPLEMENTATION --- */ |
94 | |
95 | |
96 | #if defined HAVE_DLFCN_H |
97 | # include <dlfcn.h> |
98 | #endif |
99 | |
100 | #if defined HAVE_SYS_DL_H |
101 | # include <sys/dl.h> |
102 | #endif |
103 | |
104 | |
105 | /* We may have to define LT_LAZY_OR_NOW in the command line if we |
106 | find out it does not work in some platform. */ |
107 | #if !defined LT_LAZY_OR_NOW |
108 | # if defined RTLD_LAZY |
109 | # define LT_LAZY_OR_NOW RTLD_LAZY |
110 | # else |
111 | # if defined DL_LAZY |
112 | # define LT_LAZY_OR_NOW DL_LAZY |
113 | # endif |
114 | # endif /* !RTLD_LAZY */ |
115 | #endif |
116 | #if !defined LT_LAZY_OR_NOW |
117 | # if defined RTLD_NOW |
118 | # define LT_LAZY_OR_NOW RTLD_NOW |
119 | # else |
120 | # if defined DL_NOW |
121 | # define LT_LAZY_OR_NOW DL_NOW |
122 | # endif |
123 | # endif /* !RTLD_NOW */ |
124 | #endif |
125 | #if !defined LT_LAZY_OR_NOW |
126 | # define LT_LAZY_OR_NOW 0 |
127 | #endif /* !LT_LAZY_OR_NOW */ |
128 | |
129 | /* We only support local and global symbols from modules for loaders |
130 | that provide such a thing, otherwise the system default is used. */ |
131 | #if !defined RTLD_GLOBAL |
132 | # if defined DL_GLOBAL |
133 | # define RTLD_GLOBAL DL_GLOBAL |
134 | # endif |
135 | #endif /* !RTLD_GLOBAL */ |
136 | #if !defined RTLD_LOCAL |
137 | # if defined DL_LOCAL |
138 | # define RTLD_LOCAL DL_LOCAL |
139 | # endif |
140 | #endif /* !RTLD_LOCAL */ |
141 | |
142 | #if defined HAVE_DLERROR |
143 | # define DLERROR(arg) dlerror () |
144 | #else |
145 | # define DLERROR(arg) LT__STRERROR (arg) |
146 | #endif |
147 | |
148 | #define DL__SETERROR(errorcode) \ |
149 | LT__SETERRORSTR (DLERROR (errorcode)) |
150 | |
151 | |
152 | /* A function called through the vtable when this loader is no |
153 | longer needed by the application. */ |
154 | static int |
155 | vl_exit (lt_user_data loader_data LT__UNUSED) |
156 | { |
157 | vtable = NULL; |
158 | return 0; |
159 | } |
160 | |
161 | |
162 | /* A function called through the vtable to open a module with this |
163 | loader. Returns an opaque representation of the newly opened |
164 | module for processing with this loader's other vtable functions. */ |
165 | static lt_module |
166 | vm_open (lt_user_data loader_data LT__UNUSED, const char *filename, |
167 | lt_dladvise advise) |
168 | { |
169 | int module_flags = LT_LAZY_OR_NOW; |
170 | lt_module module; |
171 | #ifdef RTLD_MEMBER |
172 | int len = LT_STRLEN (filename); |
173 | #endif |
174 | |
175 | if (advise) |
176 | { |
177 | #ifdef RTLD_GLOBAL |
178 | /* If there is some means of asking for global symbol resolution, |
179 | do so. */ |
180 | if (advise->is_symglobal) |
181 | module_flags |= RTLD_GLOBAL; |
182 | #else |
183 | /* Otherwise, reset that bit so the caller can tell it wasn't |
184 | acted on. */ |
185 | advise->is_symglobal = 0; |
186 | #endif |
187 | |
188 | /* And similarly for local only symbol resolution. */ |
189 | #ifdef RTLD_LOCAL |
190 | if (advise->is_symlocal) |
191 | module_flags |= RTLD_LOCAL; |
192 | #else |
193 | advise->is_symlocal = 0; |
194 | #endif |
195 | } |
196 | |
197 | #ifdef RTLD_MEMBER /* AIX */ |
198 | if (len >= 4) /* at least "l(m)" */ |
199 | { |
200 | /* Advise loading an archive member only if the filename really |
201 | contains both the opening and closing parent, and a member. */ |
202 | if (filename[len-1] == ')') |
203 | { |
204 | const char *opening = strrchr(filename, '('); |
205 | if (opening && opening < (filename+len-2) && strchr(opening+1, '/') == NULL) |
206 | module_flags |= RTLD_MEMBER; |
207 | } |
208 | } |
209 | #endif |
210 | |
211 | module = dlopen (filename, module_flags); |
212 | |
213 | #if defined RTLD_MEMBER && defined LT_SHARED_LIB_MEMBER |
214 | if (!module && len && !(module_flags & RTLD_MEMBER) && errno == ENOEXEC) |
215 | { |
216 | /* Loading without a member specified failed with "Exec format error". |
217 | So the file is there, but either has wrong bitwidth, or is an |
218 | archive eventually containing the default shared archive member. |
219 | Retry with default member, getting same error in worst case. */ |
220 | const char *member = LT_SHARED_LIB_MEMBER; |
221 | |
222 | char *attempt = MALLOC (char, len + strlen (member) + 1); |
223 | if (!attempt) |
224 | { |
225 | LT__SETERROR (NO_MEMORY); |
226 | return module; |
227 | } |
228 | |
229 | sprintf (attempt, "%s%s" , filename, member); |
230 | module = vm_open (loader_data, attempt, advise); |
231 | FREE (attempt); |
232 | return module; |
233 | } |
234 | #endif |
235 | |
236 | if (!module) |
237 | { |
238 | DL__SETERROR (CANNOT_OPEN); |
239 | } |
240 | |
241 | return module; |
242 | } |
243 | |
244 | |
245 | /* A function called through the vtable when a particular module |
246 | should be unloaded. */ |
247 | static int |
248 | vm_close (lt_user_data loader_data LT__UNUSED, lt_module module) |
249 | { |
250 | int errors = 0; |
251 | |
252 | if (dlclose (module) != 0) |
253 | { |
254 | DL__SETERROR (CANNOT_CLOSE); |
255 | ++errors; |
256 | } |
257 | |
258 | return errors; |
259 | } |
260 | |
261 | |
262 | /* A function called through the vtable to get the address of |
263 | a symbol loaded from a particular module. */ |
264 | static void * |
265 | vm_sym (lt_user_data loader_data LT__UNUSED, lt_module module, const char *name) |
266 | { |
267 | void *address = dlsym (module, name); |
268 | |
269 | if (!address) |
270 | { |
271 | DL__SETERROR (SYMBOL_NOT_FOUND); |
272 | } |
273 | |
274 | return address; |
275 | } |
276 | |