Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / mono-dl.c
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*
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*
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*
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*
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
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*
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 *
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
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*
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*
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 }