1 /**
2 * \file
3 * Interface to the dynamic linker
4 *
5 * Author:
6 * Mono Team (http://www.mono-project.com)
7 *
8 * Copyright 2001-2004 Ximian, Inc.
9 * Copyright 2004-2009 Novell, Inc.
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 */
12 #include "config.h"
13 #include "mono/utils/mono-dl.h"
14 #include "mono/utils/mono-embed.h"
15 #include "mono/utils/mono-path.h"
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <glib.h>
22
23 struct MonoDlFallbackHandler {
24 MonoDlFallbackLoad load_func;
25 MonoDlFallbackSymbol symbol_func;
26 MonoDlFallbackClose close_func;
27 void *user_data;
28 };
29
30 static GSList *fallback_handlers;
31
32 /*
33 * read a value string from line with any of the following formats:
34 * \s*=\s*'string'
35 * \s*=\s*"string"
36 * \s*=\s*non_white_space_string
37 */
38 static char*
read_string(char * p,FILE * file)39 read_string (char *p, FILE *file)
40 {
41 char *endp;
42 char *startp;
43 while (*p && isspace (*p))
44 ++p;
45 if (*p == 0)
46 return NULL;
47 if (*p == '=')
48 p++;
49 while (*p && isspace (*p))
50 ++p;
51 if (*p == '\'' || *p == '"') {
52 char t = *p;
53 p++;
54 startp = p;
55 endp = strchr (p, t);
56 /* FIXME: may need to read more from file... */
57 if (!endp)
58 return NULL;
59 *endp = 0;
60 return (char *) g_memdup (startp, (endp - startp) + 1);
61 }
62 if (*p == 0)
63 return NULL;
64 startp = p;
65 while (*p && !isspace (*p))
66 ++p;
67 *p = 0;
68 return (char *) g_memdup (startp, (p - startp) + 1);
69 }
70
71 /*
72 * parse a libtool .la file and return the path of the file to dlopen ()
73 * handling both the installed and uninstalled cases
74 */
75 static char*
get_dl_name_from_libtool(const char * libtool_file)76 get_dl_name_from_libtool (const char *libtool_file)
77 {
78 FILE* file;
79 char buf [512];
80 char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
81 if (!(file = fopen (libtool_file, "r")))
82 return NULL;
83 while ((line = fgets (buf, 512, file))) {
84 while (*line && isspace (*line))
85 ++line;
86 if (*line == '#' || *line == 0)
87 continue;
88 if (strncmp ("dlname", line, 6) == 0) {
89 g_free (dlname);
90 dlname = read_string (line + 6, file);
91 } else if (strncmp ("libdir", line, 6) == 0) {
92 g_free (libdir);
93 libdir = read_string (line + 6, file);
94 } else if (strncmp ("installed", line, 9) == 0) {
95 g_free (installed);
96 installed = read_string (line + 9, file);
97 }
98 }
99 fclose (file);
100 line = NULL;
101 if (installed && strcmp (installed, "no") == 0) {
102 char *dir = g_path_get_dirname (libtool_file);
103 if (dlname)
104 line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
105 g_free (dir);
106 } else {
107 if (libdir && dlname)
108 line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
109 }
110 g_free (dlname);
111 g_free (libdir);
112 g_free (installed);
113 return line;
114 }
115
116 /**
117 * mono_dl_open:
118 * \param name name of file containing shared module
119 * \param flags flags
120 * \param error_msg pointer for error message on failure
121 *
122 * Load the given file \p name as a shared library or dynamically loadable
123 * module. \p name can be NULL to indicate loading the currently executing
124 * binary image.
125 * \p flags can have the \c MONO_DL_LOCAL bit set to avoid exporting symbols
126 * from the module to the shared namespace. The \c MONO_DL_LAZY bit can be set
127 * to lazily load the symbols instead of resolving everithing at load time.
128 * \p error_msg points to a string where an error message will be stored in
129 * case of failure. The error must be released with \c g_free.
130 * \returns a \c MonoDl pointer on success, NULL on failure.
131 */
132 MonoDl*
mono_dl_open(const char * name,int flags,char ** error_msg)133 mono_dl_open (const char *name, int flags, char **error_msg)
134 {
135 MonoDl *module;
136 void *lib;
137 MonoDlFallbackHandler *dl_fallback = NULL;
138 int lflags = mono_dl_convert_flags (flags);
139
140 if (error_msg)
141 *error_msg = NULL;
142
143 module = (MonoDl *) g_malloc (sizeof (MonoDl));
144 if (!module) {
145 if (error_msg)
146 *error_msg = g_strdup ("Out of memory");
147 return NULL;
148 }
149 module->main_module = name == NULL? TRUE: FALSE;
150
151 lib = mono_dl_open_file (name, lflags);
152
153 if (!lib) {
154 GSList *node;
155 for (node = fallback_handlers; node != NULL; node = node->next){
156 MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
157 if (error_msg)
158 *error_msg = NULL;
159
160 lib = handler->load_func (name, lflags, error_msg, handler->user_data);
161 if (error_msg && *error_msg != NULL)
162 g_free (*error_msg);
163
164 if (lib != NULL){
165 dl_fallback = handler;
166 break;
167 }
168 }
169 }
170 if (!lib && !dl_fallback) {
171 char *lname;
172 char *llname;
173 const char *suff;
174 const char *ext;
175 /* This platform does not support dlopen */
176 if (name == NULL) {
177 g_free (module);
178 return NULL;
179 }
180
181 suff = ".la";
182 ext = strrchr (name, '.');
183 if (ext && strcmp (ext, ".la") == 0)
184 suff = "";
185 lname = g_strconcat (name, suff, NULL);
186 llname = get_dl_name_from_libtool (lname);
187 g_free (lname);
188 if (llname) {
189 lib = mono_dl_open_file (llname, lflags);
190 g_free (llname);
191 }
192 if (!lib) {
193 if (error_msg) {
194 *error_msg = mono_dl_current_error_string ();
195 }
196 g_free (module);
197 return NULL;
198 }
199 }
200 module->handle = lib;
201 module->dl_fallback = dl_fallback;
202 return module;
203 }
204
205 /**
206 * mono_dl_symbol:
207 * \param module a MonoDl pointer
208 * \param name symbol name
209 * \param symbol pointer for the result value
210 * Load the address of symbol \p name from the given \p module.
211 * The address is stored in the pointer pointed to by \p symbol.
212 * \returns NULL on success, an error message on failure
213 */
214 char*
mono_dl_symbol(MonoDl * module,const char * name,void ** symbol)215 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
216 {
217 void *sym;
218 char *err = NULL;
219
220 if (module->dl_fallback) {
221 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
222 } else {
223 #if MONO_DL_NEED_USCORE
224 {
225 char *usname = g_malloc (strlen (name) + 2);
226 *usname = '_';
227 strcpy (usname + 1, name);
228 sym = mono_dl_lookup_symbol (module, usname);
229 g_free (usname);
230 }
231 #else
232 sym = mono_dl_lookup_symbol (module, name);
233 #endif
234 }
235
236 if (sym) {
237 if (symbol)
238 *symbol = sym;
239 return NULL;
240 }
241 if (symbol)
242 *symbol = NULL;
243 return (module->dl_fallback != NULL) ? err : mono_dl_current_error_string ();
244 }
245
246 /**
247 * mono_dl_close:
248 * \param module a \c MonoDl pointer
249 * Unload the given module and free the module memory.
250 * \returns \c 0 on success.
251 */
252 void
mono_dl_close(MonoDl * module)253 mono_dl_close (MonoDl *module)
254 {
255 MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
256
257 if (dl_fallback){
258 if (dl_fallback->close_func != NULL)
259 dl_fallback->close_func (module->handle, dl_fallback->user_data);
260 } else
261 mono_dl_close_handle (module);
262
263 g_free (module);
264 }
265
266 /**
267 * mono_dl_build_path:
268 * \param directory optional directory
269 * \param name base name of the library
270 * \param iter iterator token
271 * Given a directory name and the base name of a library, iterate
272 * over the possible file names of the library, taking into account
273 * the possible different suffixes and prefixes on the host platform.
274 *
275 * The returned file name must be freed by the caller.
276 * \p iter must point to a NULL pointer the first time the function is called
277 * and then passed unchanged to the following calls.
278 * \returns the filename or NULL at the end of the iteration
279 */
280 char*
mono_dl_build_path(const char * directory,const char * name,void ** iter)281 mono_dl_build_path (const char *directory, const char *name, void **iter)
282 {
283 int idx;
284 const char *prefix;
285 const char *suffix;
286 gboolean first_call;
287 int prlen;
288 int suffixlen;
289 char *res;
290
291 if (!iter)
292 return NULL;
293
294 /*
295 The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
296 "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
297 the dll thus named, we start appending suffixes, each time increasing idx twice (since now
298 the 0 value became special and we need to offset idx to a 0-based array index). This is
299 done to handle situations when mapped dll name is specified as libsomething.so.1 or
300 libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
301 here.
302 */
303 idx = GPOINTER_TO_UINT (*iter);
304 if (idx == 0) {
305 first_call = TRUE;
306 suffix = "";
307 suffixlen = 0;
308 } else {
309 idx--;
310 if (mono_dl_get_so_suffixes () [idx][0] == '\0')
311 return NULL;
312 first_call = FALSE;
313 suffix = mono_dl_get_so_suffixes () [idx];
314 suffixlen = strlen (suffix);
315 }
316
317 prlen = strlen (mono_dl_get_so_prefix ());
318 if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
319 prefix = mono_dl_get_so_prefix ();
320 else
321 prefix = "";
322
323 if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
324 suffix = "";
325
326 if (directory && *directory)
327 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
328 else
329 res = g_strconcat (prefix, name, suffix, NULL);
330 ++idx;
331 if (!first_call)
332 idx++;
333 *iter = GUINT_TO_POINTER (idx);
334 return res;
335 }
336
337 MonoDlFallbackHandler *
mono_dl_fallback_register(MonoDlFallbackLoad load_func,MonoDlFallbackSymbol symbol_func,MonoDlFallbackClose close_func,void * user_data)338 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
339 {
340 MonoDlFallbackHandler *handler;
341
342 g_return_val_if_fail (load_func != NULL, NULL);
343 g_return_val_if_fail (symbol_func != NULL, NULL);
344
345 handler = g_new (MonoDlFallbackHandler, 1);
346 handler->load_func = load_func;
347 handler->symbol_func = symbol_func;
348 handler->close_func = close_func;
349 handler->user_data = user_data;
350
351 fallback_handlers = g_slist_prepend (fallback_handlers, handler);
352
353 return handler;
354 }
355
356 void
mono_dl_fallback_unregister(MonoDlFallbackHandler * handler)357 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
358 {
359 GSList *found;
360
361 found = g_slist_find (fallback_handlers, handler);
362 if (found == NULL)
363 return;
364
365 g_slist_remove (fallback_handlers, handler);
366 g_free (handler);
367 }
368
369 static MonoDl*
try_load(const char * lib_name,char * dir,int flags,char ** err)370 try_load (const char *lib_name, char *dir, int flags, char **err)
371 {
372 gpointer iter;
373 MonoDl *runtime_lib;
374 char *path;
375 iter = NULL;
376 *err = NULL;
377 while ((path = mono_dl_build_path (dir, lib_name, &iter))) {
378 g_free (*err);
379 runtime_lib = mono_dl_open (path, flags, err);
380 g_free (path);
381 if (runtime_lib)
382 return runtime_lib;
383 }
384 return NULL;
385 }
386
387 MonoDl*
mono_dl_open_runtime_lib(const char * lib_name,int flags,char ** error_msg)388 mono_dl_open_runtime_lib (const char* lib_name, int flags, char **error_msg)
389 {
390 MonoDl *runtime_lib = NULL;
391 char buf [4096];
392 int binl;
393 *error_msg = NULL;
394
395 binl = mono_dl_get_executable_path (buf, sizeof (buf));
396
397 if (binl != -1) {
398 char *base;
399 char *resolvedname, *name;
400 char *baseparent = NULL;
401 buf [binl] = 0;
402 resolvedname = mono_path_resolve_symlinks (buf);
403 base = g_path_get_dirname (resolvedname);
404 name = g_strdup_printf ("%s/.libs", base);
405 runtime_lib = try_load (lib_name, name, flags, error_msg);
406 g_free (name);
407 if (!runtime_lib)
408 baseparent = g_path_get_dirname (base);
409 if (!runtime_lib) {
410 name = g_strdup_printf ("%s/lib", baseparent);
411 runtime_lib = try_load (lib_name, name, flags, error_msg);
412 g_free (name);
413 }
414 #ifdef __MACH__
415 if (!runtime_lib) {
416 name = g_strdup_printf ("%s/Libraries", baseparent);
417 runtime_lib = try_load (lib_name, name, flags, error_msg);
418 g_free (name);
419 }
420 #endif
421 if (!runtime_lib) {
422 name = g_strdup_printf ("%s/profiler/.libs", baseparent);
423 runtime_lib = try_load (lib_name, name, flags, error_msg);
424 g_free (name);
425 }
426 g_free (base);
427 g_free (resolvedname);
428 g_free (baseparent);
429 }
430 if (!runtime_lib)
431 runtime_lib = try_load (lib_name, NULL, flags, error_msg);
432
433 return runtime_lib;
434 }
435