Merge pull request #487 from mayerwin/patch-1
[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
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <glib.h>
19
20 #ifdef TARGET_WIN32
21 #define SOPREFIX ""
22 static const char suffixes [][5] = {
23         ".dll"
24 };
25 #elif defined(__APPLE__)
26 #define SOPREFIX "lib"
27 static const char suffixes [][8] = {
28         ".dylib",
29         ".so",
30         ".bundle"
31 };
32 #else
33 #define SOPREFIX "lib"
34 static const char suffixes [][4] = {
35         ".so"
36 };
37 #endif
38
39 #ifdef TARGET_WIN32
40
41 #include <windows.h>
42 #include <psapi.h>
43
44 #define SO_HANDLE_TYPE HMODULE
45 #define LL_SO_OPEN(file,flags) w32_load_module ((file), (flags))
46 #define LL_SO_CLOSE(module) do { if (!(module)->main_module) FreeLibrary ((module)->handle); } while (0)
47 #define LL_SO_SYMBOL(module, name) w32_find_symbol ((module), (name))
48 #define LL_SO_TRFLAGS(flags) 0
49 #define LL_SO_ERROR() w32_dlerror ()
50
51 #elif defined (HAVE_DL_LOADER)
52
53 #include <dlfcn.h>
54
55 #ifndef RTLD_LAZY
56 #define RTLD_LAZY       1
57 #endif  /* RTLD_LAZY */
58
59 #define SO_HANDLE_TYPE void*
60 #ifdef PLATFORM_ANDROID
61 /* Bionic doesn't support NULL filenames */
62 #  define LL_SO_OPEN(file,flags) ((file) ? dlopen ((file), (flags)) : NULL)
63 #else
64 #  define LL_SO_OPEN(file,flags) dlopen ((file), (flags))
65 #endif
66 #define LL_SO_CLOSE(module) dlclose ((module)->handle)
67 #define LL_SO_SYMBOL(module, name) dlsym ((module)->handle, (name))
68 #define LL_SO_TRFLAGS(flags) convert_flags ((flags))
69 #define LL_SO_ERROR() g_strdup (dlerror ())
70
71 static int
72 convert_flags (int flags)
73 {
74         int lflags = flags & MONO_DL_LOCAL? 0: RTLD_GLOBAL;
75
76         if (flags & MONO_DL_LAZY)
77                 lflags |= RTLD_LAZY;
78         else
79                 lflags |= RTLD_NOW;
80         return lflags;
81 }
82
83 #else
84 /* no dynamic loader supported */
85 #define SO_HANDLE_TYPE void*
86 #define LL_SO_OPEN(file,flags) NULL
87 #define LL_SO_CLOSE(module) 
88 #define LL_SO_SYMBOL(module, name) NULL
89 #define LL_SO_TRFLAGS(flags) (flags)
90 #define LL_SO_ERROR() g_strdup ("No support for dynamic loader")
91
92 #endif
93
94 static GSList *fallback_handlers;
95
96 struct MonoDlFallbackHandler {
97         MonoDlFallbackLoad load_func;
98         MonoDlFallbackSymbol symbol_func;
99         MonoDlFallbackClose close_func;
100         void *user_data;
101 };
102         
103 struct _MonoDl {
104         SO_HANDLE_TYPE handle;
105         int main_module;
106
107         /* If not NULL, use the methods in MonoDlFallbackHandler instead of the LL_* methods */
108         MonoDlFallbackHandler *dl_fallback;
109 };
110
111 #ifdef TARGET_WIN32
112
113 static char*
114 w32_dlerror (void)
115 {
116         char* ret = NULL;
117         wchar_t* buf = NULL;
118         DWORD code = GetLastError ();
119
120         if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,
121                 code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL))
122         {
123                 ret = g_utf16_to_utf8 (buf, wcslen(buf), NULL, NULL, NULL);
124                 LocalFree (buf);
125         } else {
126                 g_assert_not_reached ();
127         }
128         return ret;
129 }
130
131 static gpointer
132 w32_find_symbol (MonoDl *module, const gchar *symbol_name)
133 {
134         HMODULE *modules;
135         DWORD buffer_size = sizeof (HMODULE) * 1024;
136         DWORD needed, i;
137         gpointer proc = NULL;
138
139         /* get the symbol directly from the specified module */
140         if (!module->main_module)
141                 return GetProcAddress (module->handle, symbol_name);
142
143         /* get the symbol from the main module */
144         proc = GetProcAddress (module->handle, symbol_name);
145         if (proc != NULL)
146                 return proc;
147
148         /* get the symbol from the loaded DLLs */
149         modules = (HMODULE *) g_malloc (buffer_size);
150         if (modules == NULL)
151                 return NULL;
152
153         if (!EnumProcessModules (GetCurrentProcess (), modules,
154                                  buffer_size, &needed)) {
155                 g_free (modules);
156                 return NULL;
157         }
158
159         /* check whether the supplied buffer was too small, realloc, retry */
160         if (needed > buffer_size) {
161                 g_free (modules);
162
163                 buffer_size = needed;
164                 modules = (HMODULE *) g_malloc (buffer_size);
165
166                 if (modules == NULL)
167                         return NULL;
168
169                 if (!EnumProcessModules (GetCurrentProcess (), modules,
170                                          buffer_size, &needed)) {
171                         g_free (modules);
172                         return NULL;
173                 }
174         }
175
176         for (i = 0; i < needed / sizeof (HANDLE); i++) {
177                 proc = GetProcAddress (modules [i], symbol_name);
178                 if (proc != NULL) {
179                         g_free (modules);
180                         return proc;
181                 }
182         }
183
184         g_free (modules);
185         return NULL;
186 }
187
188
189 static gpointer
190 w32_load_module (const char* file, int flags)
191 {
192         gpointer hModule = NULL;
193         if (file) {
194                 gunichar2* file_utf16 = g_utf8_to_utf16 (file, strlen (file), NULL, NULL, NULL);
195                 guint last_sem = SetErrorMode (SEM_FAILCRITICALERRORS);
196                 guint32 last_error = 0;
197
198                 hModule = LoadLibrary (file_utf16);
199                 if (!hModule)
200                         last_error = GetLastError ();
201
202                 SetErrorMode (last_sem);
203                 g_free (file_utf16);
204
205                 if (!hModule)
206                         SetLastError (last_error);
207         } else {
208                 hModule = GetModuleHandle (NULL);
209         }
210         return hModule;
211 }
212 #endif
213
214 /*
215  * read a value string from line with any of the following formats:
216  * \s*=\s*'string'
217  * \s*=\s*"string"
218  * \s*=\s*non_white_space_string
219  */
220 static char*
221 read_string (char *p, FILE *file)
222 {
223         char *endp;
224         char *startp;
225         while (*p && isspace (*p))
226                 ++p;
227         if (*p == 0)
228                 return NULL;
229         if (*p == '=')
230                 p++;
231         while (*p && isspace (*p))
232                 ++p;
233         if (*p == '\'' || *p == '"') {
234                 char t = *p;
235                 p++;
236                 startp = p;
237                 endp = strchr (p, t);
238                 /* FIXME: may need to read more from file... */
239                 if (!endp)
240                         return NULL;
241                 *endp = 0;
242                 return g_memdup (startp, (endp - startp) + 1);
243         }
244         if (*p == 0)
245                 return NULL;
246         startp = p;
247         while (*p && !isspace (*p))
248                 ++p;
249         *p = 0;
250         return g_memdup (startp, (p - startp) + 1);
251 }
252
253 /*
254  * parse a libtool .la file and return the path of the file to dlopen ()
255  * handling both the installed and uninstalled cases
256  */
257 static char*
258 get_dl_name_from_libtool (const char *libtool_file)
259 {
260         FILE* file;
261         char buf [512];
262         char *line, *dlname = NULL, *libdir = NULL, *installed = NULL;
263         if (!(file = fopen (libtool_file, "r")))
264                 return NULL;
265         while ((line = fgets (buf, 512, file))) {
266                 while (*line && isspace (*line))
267                         ++line;
268                 if (*line == '#' || *line == 0)
269                         continue;
270                 if (strncmp ("dlname", line, 6) == 0) {
271                         g_free (dlname);
272                         dlname = read_string (line + 6, file);
273                 } else if (strncmp ("libdir", line, 6) == 0) {
274                         g_free (libdir);
275                         libdir = read_string (line + 6, file);
276                 } else if (strncmp ("installed", line, 9) == 0) {
277                         g_free (installed);
278                         installed = read_string (line + 9, file);
279                 }
280         }
281         fclose (file);
282         line = NULL;
283         if (installed && strcmp (installed, "no") == 0) {
284                 char *dir = g_path_get_dirname (libtool_file);
285                 if (dlname)
286                         line = g_strconcat (dir, G_DIR_SEPARATOR_S ".libs" G_DIR_SEPARATOR_S, dlname, NULL);
287                 g_free (dir);
288         } else {
289                 if (libdir && dlname)
290                         line = g_strconcat (libdir, G_DIR_SEPARATOR_S, dlname, NULL);
291         }
292         g_free (dlname);
293         g_free (libdir);
294         g_free (installed);
295         return line;
296 }
297
298 /**
299  * mono_dl_open:
300  * @name: name of file containing shared module
301  * @flags: flags
302  * @error_msg: pointer for error message on failure
303  *
304  * Load the given file @name as a shared library or dynamically loadable
305  * module. @name can be NULL to indicate loading the currently executing
306  * binary image.
307  * @flags can have the MONO_DL_LOCAL bit set to avoid exporting symbols
308  * from the module to the shared namespace. The MONO_DL_LAZY bit can be set
309  * to lazily load the symbols instead of resolving everithing at load time.
310  * @error_msg points to a string where an error message will be stored in
311  * case of failure.   The error must be released with g_free.
312  *
313  * Returns: a MonoDl pointer on success, NULL on failure.
314  */
315 MonoDl*
316 mono_dl_open (const char *name, int flags, char **error_msg)
317 {
318         MonoDl *module;
319         void *lib;
320         MonoDlFallbackHandler *dl_fallback = NULL;
321         int lflags = LL_SO_TRFLAGS (flags);
322
323         if (error_msg)
324                 *error_msg = NULL;
325
326         module = malloc (sizeof (MonoDl));
327         if (!module) {
328                 if (error_msg)
329                         *error_msg = g_strdup ("Out of memory");
330                 return NULL;
331         }
332         module->main_module = name == NULL? TRUE: FALSE;
333         lib = LL_SO_OPEN (name, lflags);
334         if (!lib) {
335                 GSList *node;
336                 for (node = fallback_handlers; node != NULL; node = node->next){
337                         MonoDlFallbackHandler *handler = (MonoDlFallbackHandler *) node->data;
338                         if (error_msg)
339                                 *error_msg = NULL;
340                         
341                         lib = handler->load_func (name, lflags, error_msg, handler->user_data);
342                         if (error_msg && *error_msg != NULL)
343                                 g_free (*error_msg);
344                         
345                         if (lib != NULL){
346                                 dl_fallback = handler;
347                                 break;
348                         }
349                 }
350         }
351         if (!lib && !dl_fallback) {
352                 char *lname;
353                 char *llname;
354                 const char *suff;
355                 const char *ext;
356                 /* This platform does not support dlopen */
357                 if (name == NULL) {
358                         free (module);
359                         return NULL;
360                 }
361                 
362                 suff = ".la";
363                 ext = strrchr (name, '.');
364                 if (ext && strcmp (ext, ".la") == 0)
365                         suff = "";
366                 lname = g_strconcat (name, suff, NULL);
367                 llname = get_dl_name_from_libtool (lname);
368                 g_free (lname);
369                 if (llname) {
370                         lib = LL_SO_OPEN (llname, lflags);
371                         g_free (llname);
372                 }
373                 if (!lib) {
374                         if (error_msg) {
375                                 *error_msg = LL_SO_ERROR ();
376                         }
377                         free (module);
378                         return NULL;
379                 }
380         }
381         module->handle = lib;
382         module->dl_fallback = dl_fallback;
383         return module;
384 }
385
386 /**
387  * mono_dl_symbol:
388  * @module: a MonoDl pointer
389  * @name: symbol name
390  * @symbol: pointer for the result value
391  *
392  * Load the address of symbol @name from the given @module.
393  * The address is stored in the pointer pointed to by @symbol.
394  *
395  * Returns: NULL on success, an error message on failure
396  */
397 char*
398 mono_dl_symbol (MonoDl *module, const char *name, void **symbol)
399 {
400         void *sym;
401         char *err = NULL;
402
403         if (module->dl_fallback) {
404                 sym = module->dl_fallback->symbol_func (module->handle, name, &err, module->dl_fallback->user_data);
405         } else {
406 #if MONO_DL_NEED_USCORE
407                 {
408                         char *usname = malloc (strlen (name) + 2);
409                         *usname = '_';
410                         strcpy (usname + 1, name);
411                         sym = LL_SO_SYMBOL (module, usname);
412                         free (usname);
413                 }
414 #else
415                 sym = LL_SO_SYMBOL (module, name);
416 #endif
417         }
418
419         if (sym) {
420                 if (symbol)
421                         *symbol = sym;
422                 return NULL;
423         }
424         if (symbol)
425                 *symbol = NULL;
426         return (module->dl_fallback != NULL) ? err :  LL_SO_ERROR ();
427 }
428
429 /**
430  * mono_dl_close:
431  * @module: a MonoDl pointer
432  *
433  * Unload the given module and free the module memory.
434  *
435  * Returns: 0 on success.
436  */
437 void
438 mono_dl_close (MonoDl *module)
439 {
440         MonoDlFallbackHandler *dl_fallback = module->dl_fallback;
441         
442         if (dl_fallback){
443                 if (dl_fallback->close_func != NULL)
444                         dl_fallback->close_func (module->handle, dl_fallback->user_data);
445         } else
446                 LL_SO_CLOSE (module);
447         
448         free (module);
449 }
450
451 /**
452  * mono_dl_build_path:
453  * @directory: optional directory
454  * @name: base name of the library
455  * @iter: iterator token
456  *
457  * Given a directory name and the base name of a library, iterate
458  * over the possible file names of the library, taking into account
459  * the possible different suffixes and prefixes on the host platform.
460  *
461  * The returned file name must be freed by the caller.
462  * @iter must point to a NULL pointer the first time the function is called
463  * and then passed unchanged to the following calls.
464  * Returns: the filename or NULL at the end of the iteration
465  */
466 char*
467 mono_dl_build_path (const char *directory, const char *name, void **iter)
468 {
469         int idx;
470         const char *prefix;
471         const char *suffix;
472         gboolean first_call;
473         int prlen;
474         int suffixlen;
475         char *res;
476
477         if (!iter)
478                 return NULL;
479
480         /*
481           The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
482           "bootstrap" phase in which we check the passed name verbatim and only if we fail to find
483           the dll thus named, we start appending suffixes, each time increasing idx twice (since now
484           the 0 value became special and we need to offset idx to a 0-based array index). This is
485           done to handle situations when mapped dll name is specified as libsomething.so.1 or
486           libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
487           here.
488          */
489         idx = GPOINTER_TO_UINT (*iter);
490         if (idx == 0) {
491                 first_call = TRUE;
492                 suffix = "";
493                 suffixlen = 0;
494         } else {
495                 idx--;
496                 if (idx >= G_N_ELEMENTS (suffixes))
497                         return NULL;
498                 first_call = FALSE;
499                 suffix = suffixes [idx];
500                 suffixlen = strlen (suffix);
501         }
502
503         prlen = strlen (SOPREFIX);
504         if (prlen && strncmp (name, SOPREFIX, prlen) != 0)
505                 prefix = SOPREFIX;
506         else
507                 prefix = "";
508
509         if (first_call || (suffixlen && strstr (name, suffix) == (name + strlen (name) - suffixlen)))
510                 suffix = "";
511
512         if (directory && *directory)
513                 res = g_strconcat (directory, G_DIR_SEPARATOR_S, prefix, name, suffix, NULL);
514         else
515                 res = g_strconcat (prefix, name, suffix, NULL);
516         ++idx;
517         if (!first_call)
518                 idx++;
519         *iter = GUINT_TO_POINTER (idx);
520         return res;
521 }
522
523 MonoDlFallbackHandler *
524 mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
525 {
526         MonoDlFallbackHandler *handler;
527         
528         g_return_val_if_fail (load_func != NULL, NULL);
529         g_return_val_if_fail (symbol_func != NULL, NULL);
530
531         handler = g_new (MonoDlFallbackHandler, 1);
532         handler->load_func = load_func;
533         handler->symbol_func = symbol_func;
534         handler->close_func = close_func;
535         handler->user_data = user_data;
536
537         fallback_handlers = g_slist_prepend (fallback_handlers, handler);
538         
539         return handler;
540 }
541
542 void
543 mono_dl_fallback_unregister (MonoDlFallbackHandler *handler)
544 {
545         GSList *found;
546
547         found = g_slist_find (fallback_handlers, handler);
548         if (found == NULL)
549                 return;
550
551         g_slist_remove (fallback_handlers, handler);
552         g_free (handler);
553 }