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