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