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