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